Skip to content

Instantly share code, notes, and snippets.

@bitbonk
Last active March 9, 2022 23:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bitbonk/71bfe0e0501e01372e79fcc3e111eb71 to your computer and use it in GitHub Desktop.
Save bitbonk/71bfe0e0501e01372e79fcc3e111eb71 to your computer and use it in GitHub Desktop.
Nuke pipeline Yaml generation for Azure DevOps Server (on-premise version of Azure DevOps)
# ------------------------------------------------------------------------------
# <auto-generated>
#
# This code was generated.
#
# - To turn off auto-generation set:
#
# [AzurePipelinesServer (AutoGenerate = false)]
#
# - To trigger manual generation invoke:
#
# nuke --generate-configuration AzurePipelines --host AzurePipelines
#
# </auto-generated>
# ------------------------------------------------------------------------------
pool: 'MyPool'
jobs:
- job: Nuke
displayName: 'Nuke'
dependsOn: [ ]
steps:
- task: UseDotNet@2
displayName: 'Use .NET SDK from global.json'
inputs:
useGlobalJson: true
- task: CmdLine@2
inputs:
script: './build.cmd Restore Compile Test Pack --skip'
- task: PublishBuildArtifacts@1
inputs:
artifactName: artifacts
pathtoPublish: 'artifacts'
// <copyright>
// Copyright (c) Kontron AIS GmbH. All rights reserved.
// </copyright>
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Nuke.Common;
using Nuke.Common.CI;
using Nuke.Common.CI.AzurePipelines;
using Nuke.Common.CI.AzurePipelines.Configuration;
using Nuke.Common.Execution;
using Nuke.Common.IO;
using Nuke.Common.Utilities;
using Nuke.Common.Utilities.Collections;
public class AzurePipelinesServerAttribute : AzurePipelinesAttribute
{
/// <inheritdoc />
public AzurePipelinesServerAttribute()
: base(default)
{
}
/// <inheritdoc />
public AzurePipelinesServerAttribute([CanBeNull] string suffix)
: base(suffix, default)
{
}
public string Pool { get; set; }
/// <inheritdoc />
public override ConfigurationEntity GetConfiguration(NukeBuild build, IReadOnlyCollection<ExecutableTarget> relevantTargets)
{
return new AzurePipelinesServerConfiguration
{
VariableGroups = ImportVariableGroups,
VcsPushTrigger = GetVcsPushTrigger(),
VcsPullRequestTrigger = GetVcsPullRequestTrigger(),
Stages = new[] { GetStage(default, relevantTargets) },
Pool = Pool
};
}
/// <inheritdoc />
protected override AzurePipelinesStage GetStage(AzurePipelinesImage image, IReadOnlyCollection<ExecutableTarget> relevantTargets)
{
return new AzurePipelinesStage
{
Dependencies = new AzurePipelinesStage[] { },
Jobs = new[]
{
new AzurePipelinesJob
{
Dependencies = new AzurePipelinesJob[] { },
Name = "Nuke",
DisplayName = "Nuke",
Steps = new AzurePipelinesStep[]
{
new AzurePipelinesUseDotNetStep(),
new AzurePipelinesCmdStep
{
Imports = new Dictionary<string, string>(),
BuildCmdPath = "build.cmd",
InvokedTargets = relevantTargets.Select(t => t.Name).ToArray()
}
}.Concat(GetPublishSteps(relevantTargets)).ToArray()
}
}
};
}
IEnumerable<AzurePipelinesPublishStep> GetPublishSteps(IReadOnlyCollection<ExecutableTarget> relevantTargets)
{
static string GetArtifactPath(AbsolutePath path)
=> NukeBuild.RootDirectory.Contains(path)
? NukeBuild.RootDirectory.GetUnixRelativePathTo(path)
: (string)path;
foreach (var publishedArtifact in relevantTargets.Select(relevantTarget => relevantTarget.ArtifactProducts
.Select(x => (AbsolutePath)x)
.Select(x => x.DescendantsAndSelf(y => y.Parent).FirstOrDefault(y => !y.ToString().ContainsOrdinalIgnoreCase("*")))
.Distinct()
.Select(GetArtifactPath).ToArray()).SelectMany(publishedArtifacts => publishedArtifacts))
{
yield return new AzurePipelinesPublishStep
{
ArtifactName = publishedArtifact.Split('/').Last(),
PathToPublish = publishedArtifact
};
}
}
class AzurePipelinesServerConfiguration : ConfigurationEntity
{
[CanBeNull]
public string Pool { get; set; }
public string[] VariableGroups { get; set; }
[CanBeNull]
public AzurePipelinesVcsPushTrigger VcsPushTrigger { get; set; }
[CanBeNull]
public AzurePipelinesVcsPushTrigger VcsPullRequestTrigger { get; set; }
public AzurePipelinesStage[] Stages { get; set; }
public override void Write(CustomFileWriter writer)
{
if (VariableGroups.Length > 0)
{
using (writer.WriteBlock("variables:"))
{
VariableGroups.ForEach(x => writer.WriteLine($"- group: {x}"));
writer.WriteLine();
}
}
if (VcsPushTrigger != null)
{
using (writer.WriteBlock("trigger:"))
{
VcsPushTrigger.Write(writer);
writer.WriteLine();
}
}
if (VcsPullRequestTrigger != null)
{
using (writer.WriteBlock("pr:"))
{
VcsPullRequestTrigger.Write(writer);
writer.WriteLine();
}
}
if (Pool == null)
{
using (writer.WriteBlock("stages:"))
{
Stages.ForEach(x => x.Write(writer));
}
}
else
{
writer.WriteLine($"pool: {Pool.SingleQuote()}");
using (writer.WriteBlock("jobs:"))
{
Stages.ForEach(
x => x.Jobs
.ForEach(x => x
.Write(writer)));
}
}
}
}
class AzurePipelinesUseDotNetStep : AzurePipelinesStep
{
public AzurePipelinesUseDotNetStep()
{
}
public AzurePipelinesUseDotNetStep(string version) => this.Version = version;
[CanBeNull]
public string Version { get; }
public bool UseGlobalJson => Version == null;
public override void Write(CustomFileWriter writer)
{
using (writer.WriteBlock("- task: UseDotNet@2"))
{
writer.WriteLine($"displayName: 'Use .NET SDK {(UseGlobalJson ? "from global.json" : Version) }'");
using (writer.WriteBlock("inputs:"))
{
if (UseGlobalJson)
{
writer.WriteLine("useGlobalJson: true");
}
else
{
writer.WriteLine($"version: {Version}.x");
}
}
}
}
}
}
[AzurePipelinesServer(
Pool = "MyPool",
InvokedTargets = new[] { nameof(Test), nameof(Pack) })]
[ShutdownDotNetAfterServerBuild]
class Build : NukeBuild
{
// ...
}
@bitbonk
Copy link
Author

bitbonk commented Mar 9, 2022

  • AzurePipelinesServerAttribute.cs: implementation of the AzurePipelinesServerAttribute
  • Build.cs: sample usage of the AzurePipelinesServerAttribute
  • azure-pipleines.yml: sample yaml that gets generated by be the attribute

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment