Skip to content

Support modern Octopus features added since repo last regularly maintained #25

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 49 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
d587af3
LUNCH: Make several changes to stabilize the build and update the NuG…
matthewmarchus Apr 6, 2021
12f2d0b
Jenkinsfile for dotnet
matthewmarchus Apr 6, 2021
046e82a
LUNCH: Specify path to C:\Program Files\dotnet\dotnet.exe
matthewmarchus Apr 6, 2021
e6e53ad
LUNCH: Escape backslash
matthewmarchus Apr 6, 2021
da82072
LUNCH: Remove userId from credentials
matthewmarchus Apr 6, 2021
e1e9088
LUNCH: Use variable instead of literal for dotnet exe
matthewmarchus Apr 6, 2021
9deaede
Double-quotes for the command word
matthewmarchus Apr 6, 2021
71b57bb
POST-LUNCH: Add support for 'File' YAML key in LibraryVariableSets (S…
matthewmarchus Apr 6, 2021
2571e4a
Improve publish
matthewmarchus Apr 7, 2021
0db6e2a
publish
matthewmarchus Apr 7, 2021
e2c7598
Don't checkout twice
matthewmarchus Apr 7, 2021
de83b3b
Support soft updates for missing keys in objects
matthewmarchus Apr 7, 2021
6b8c66f
Fix various test compile time errors
matthewmarchus Apr 7, 2021
1b95a39
Implement model validation
matthewmarchus Apr 8, 2021
9b154f5
Implement validation
matthewmarchus Apr 8, 2021
9952142
SKUNK-4: Add a flag for project names during model download (-project…
matthewmarchus Apr 21, 2021
e6f7c9a
SKUNK-4: Upgrade Octopus.Client
matthewmarchus Apr 21, 2021
442d6bd
SKUNK-4: Actually pass program argument to DownloadModel
matthewmarchus Apr 21, 2021
08330c8
SKUNK-2: Add project channels in YAML
matthewmarchus Apr 22, 2021
3f356cd
NONE: Download lifecycles
matthewmarchus Apr 22, 2021
9b0a4dd
NONE: Add Default Lifecycle to mock
matthewmarchus Apr 22, 2021
d0d4f15
NONE: Fix doc generator
matthewmarchus Apr 22, 2021
947b578
SKUNK-5: Support YAML references to project names
matthewmarchus May 25, 2021
69f2ff9
EAG-847: Support a wide variety of new "XNameToId" value conversion t…
matthewmarchus Jun 23, 2021
6f5915f
EAG-847: Don't remove protected IDs from property values, but do set …
matthewmarchus Jun 23, 2021
48ab955
EAG-847: Add support for a wide variety of new keys across the YAML s…
matthewmarchus Jun 23, 2021
99315e2
EAG-847: Better error handling
matthewmarchus Jun 23, 2021
9908d8e
EAG-847: Better error text
matthewmarchus Jun 23, 2021
5099476
EAG-847: Log a warning if you are using an old step template version
matthewmarchus Jun 23, 2021
c7a5802
EAG-847: Fix an issue where step templates were all upgraded automati…
matthewmarchus Jun 23, 2021
d32bcd3
EAG-847: Fix another bug with step template versioning
matthewmarchus Jun 23, 2021
9854556
EAG-847: Support disabling steps
matthewmarchus Jun 23, 2021
52881ec
EAG-847: Fix tests, Disabled->IsDisabled
matthewmarchus Jun 23, 2021
99b85cb
EAG-847: Update documentation
matthewmarchus Jun 23, 2021
5a83707
EAG-847: Support Variable step conditions
matthewmarchus Jun 23, 2021
921b239
EAG-847: Support conditional actions (variable)
matthewmarchus Jun 23, 2021
2a0e1e8
EAG-847: Fix tests
matthewmarchus Jun 23, 2021
e9e142a
EAG-847: Preserve identifiers
matthewmarchus Jun 23, 2021
a72f36e
EAG-847: Don't specify an ID for actions
matthewmarchus Jun 23, 2021
63740c5
NONE: Add support for 3 new value transformations
matthewmarchus Jul 7, 2021
f2510c4
EAG-887: Support runbooks
matthewmarchus Jul 20, 2021
902fdba
EAG-887: Fix order of YAML definitions
matthewmarchus Jul 20, 2021
c504934
EAG-919: Support package references!
matthewmarchus Jul 21, 2021
391b49d
EAG-919: Fix tests
matthewmarchus Jul 21, 2021
f1bdf36
EAG-919: EnsureNotNull()
matthewmarchus Jul 21, 2021
c6d86d5
EAG-919: Fallbacks
matthewmarchus Jul 21, 2021
5d18436
EAG-919: CanBeUsedForProjectVersioning
matthewmarchus Jul 21, 2021
c5f7514
NONE: Vastly improve the downloader. Automatically normalize the down…
matthewmarchus Jul 22, 2021
9b5eca5
Update README.md
matthewmarchus Jul 23, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ make/psmake.*
*.nupkg
reports/
wiki/
Manual.md
Manual.md
.idea/
launchSettings.json
28 changes: 28 additions & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
pipeline {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this file as it won't be used during the project build.

agent any
environment {
dotnet = '"C:\\Program Files\\dotnet\\dotnet.exe"'
}
stages {
stage('Restore Packages') {
steps {
bat "$dotnet restore --configfile NuGet.Config"
}
}
stage('Clean') {
steps {
bat "$dotnet clean"
}
}
stage('Build') {
steps {
bat "$dotnet build --configuration Release"
}
}
stage('Publish') {
steps {
bat "$dotnet publish --configuration Release --runtime win-x64 --output \"E:/OctopusProjectBuilder/$env.BRANCH_NAME\""
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<PackageReference Include="Common.Logging" Version="3.4.1" />
<PackageReference Include="FluentCommandLineParser" Version="1.4.3" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.2" />
<PackageReference Include="Octopus.Client" Version="4.39.4" />
<PackageReference Include="Octopus.Client" Version="11.2.3274" />
</ItemGroup>

<ItemGroup>
Expand Down
4 changes: 3 additions & 1 deletion OctopusProjectBuilder.Console/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
{
internal class Options
{
public enum Verb { Upload, Download, CleanupConfig }
public enum Verb { Upload, Download, CleanupConfig, Validate }
public string OctopusUrl { get; set; }
public string OctopusApiKey { get; set; }
public string ProjectName { get; set; }
public string DefinitionsDir { get; set; }
public bool Normalize { get; set; }
public Verb Action { get; set; }
}
}
178 changes: 174 additions & 4 deletions OctopusProjectBuilder.Console/Program.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Fclp;
using Microsoft.Extensions.Logging;
using Octopus.Client;
using Octopus.Client.Model;
using OctopusProjectBuilder.Model;
using OctopusProjectBuilder.Uploader;
using OctopusProjectBuilder.YamlReader;
using OctopusProjectBuilder.YamlReader.Model;

namespace OctopusProjectBuilder.Console
{
Expand All @@ -33,6 +37,8 @@ static int Main(string[] args)
DownloadDefinitions(options).GetAwaiter().GetResult();
else if (options.Action == Options.Verb.CleanupConfig)
CleanupConfig(options);
else if (options.Action == Options.Verb.Validate)
ValidateConfig(options);
}
catch (Exception e)
{
Expand All @@ -42,6 +48,11 @@ static int Main(string[] args)
return 0;
}

private static SystemModel ValidateConfig(Options options)
{
return new YamlSystemModelRepository(_loggerFactory).Load(options.DefinitionsDir);
}

private static void CleanupConfig(Options options)
{
new YamlSystemModelRepository(_loggerFactory).CleanupConfig(options.DefinitionsDir);
Expand All @@ -55,10 +66,167 @@ private static async Task UploadDefinitions(Options options)

private static async Task DownloadDefinitions(Options options)
{
var model = await new ModelDownloader(await BuildRepository(options), _loggerFactory).DownloadModel();
new YamlSystemModelRepository(_loggerFactory).Save(model, options.DefinitionsDir);
var repository = await BuildRepository(options);
var model = await new ModelDownloader(repository, _loggerFactory)
.DownloadModel(options.ProjectName);

await new YamlSystemModelRepository(_loggerFactory).Save(model, options.DefinitionsDir, async yaml =>
{
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please extract the Normalisation logic out of the Program class to make DownloadDefinitions() easy to follow.

if (options.Normalize && yaml.LibraryVariableSets != null)
{
foreach (var libraryVariableSet in yaml.LibraryVariableSets
.Where(x => model.Projects
.Any(p => p.IncludedLibraryVariableSetRefs.Any(r => r.Name == x.Name))))
{
if (libraryVariableSet.ContentType != LibraryVariableSet.VariableSetContentType.ScriptModule)
{
continue;
}

YamlVariable contentType = libraryVariableSet.Variables
.Where(v => v.Name == "Octopus.Script.Module.Language[" + libraryVariableSet.Name + "]")
.FirstOrDefault();
string extension;
if (contentType == null)
{
extension = "script";
}
else
{
switch (contentType.Value)
{
case "PowerShell":
extension = "ps1";
break;
default:
extension = "script";
break;
}
}

YamlVariable content = libraryVariableSet.Variables
.Where(v => v.Name == "Octopus.Script.Module[" + libraryVariableSet.Name + "]")
.FirstOrDefault();
if (contentType == null)
{
continue;
}

string path = Path.Combine(options.DefinitionsDir,
"LibraryVariableSet_" + libraryVariableSet.Name + "." + extension);
File.WriteAllText(path, content.Value);
content.Value = null;
content.File = path;
}
}

// Massage model: reduce identification
if (options.Normalize && yaml.Projects != null)
{
foreach (var project in yaml.Projects)
{
// Normalize IDs on project variable templates
foreach (var variable in project.Templates)
{
variable.Id = null;
}

// Normalize deployment step templates
foreach (var step in project.DeploymentProcess.Steps)
{
foreach (var action in step.Actions.Where(action =>
action.Properties.Any(x => x.Key == "Octopus.Action.Template.Id")))
{
var actionTemplateId = action.Properties
.FirstOrDefault(property => property.Key == "Octopus.Action.Template.Id");
var template =
await repository.ActionTemplates.Get(actionTemplateId.Value);
var actionTemplateVersion = action.Properties
.FirstOrDefault(property => property.Key == "Octopus.Action.Template.Version");

if (actionTemplateVersion != null)
{
var templateVersion =
int.Parse(actionTemplateVersion.Value);
var versionedTemplate =
await repository.ActionTemplates.GetVersion(template, templateVersion);

action.Properties = action.Properties.Where(property =>
versionedTemplate.Properties.All(property2 =>
property2.Key != property.Key) &&
property.Key != "Octopus.Action.Template.Version")
.ToArray();
}

actionTemplateId.ValueType = "StepTemplateNameToId";
actionTemplateId.Value = template.Name;
}

foreach (var action in step.Actions)
{
HandleSplitActionToFile(project.Name, action, options.DefinitionsDir);
}
}
}
}
});
}

private static void HandleSplitActionToFile(string projectName, YamlDeploymentAction action, string directory)
{
if (action.ActionType == "Octopus.Script")
{
var scriptSource = action.Properties
.FirstOrDefault(property => property.Key == "Octopus.Action.Script.ScriptSource");
if (scriptSource == null)
{
return;
}

var syntax = action.Properties
.FirstOrDefault(property => property.Key == "Octopus.Action.Script.Syntax");
if (syntax == null)
{
return;
}

string extension;
switch (syntax.Value)
{
case "PowerShell":
extension = "ps1";
break;
default:
extension = "script";
break;
}

var scriptBody = action.Properties
.FirstOrDefault(property => property.Key == "Octopus.Action.Script.ScriptBody");
if (scriptBody == null)
{
return;
}

string path = Path.Combine(directory, "Script_" + projectName + "_" + action.Name + "." + extension);
File.WriteAllText(path, scriptBody.Value);
scriptBody.Value = null;
scriptBody.File = path;
}
else if (action.ActionType == "Octopus.TentaclePackage")
{
foreach (var postDeploy in action.Properties
.Where(property => property.Key.StartsWith("Octopus.Action.CustomScripts.")))
{
string path = Path.Combine(directory, "Script_" + projectName + "_" + action.Name + "." +
postDeploy.Key.Substring("Octopus.Action.CustomScripts.".Length));
File.WriteAllText(path, postDeploy.Value);
postDeploy.Value = null;
postDeploy.File = path;
}
}
}

private static async Task<OctopusAsyncRepository> BuildRepository(Options options)
{
return new OctopusAsyncRepository(
Expand All @@ -71,8 +239,10 @@ public static Options ReadOptions(string[] args)
var parser = new FluentCommandLineParser<Options>();
parser.Setup(o => o.Action).As('a', "action").Required().WithDescription($"Action to perform: {string.Join(", ", Enum.GetValues(typeof(Options.Verb)).Cast<object>())}");
parser.Setup(o => o.DefinitionsDir).As('d', "definitions").Required().WithDescription("Definitions directory");
parser.Setup(o => o.OctopusUrl).As('u', "octopusUrl").Required().WithDescription("Octopus Url");
parser.Setup(o => o.OctopusApiKey).As('k', "octopusApiKey").Required().WithDescription("Octopus API key");
parser.Setup(o => o.OctopusUrl).As('u', "octopusUrl").WithDescription("Octopus Url");
parser.Setup(o => o.OctopusApiKey).As('k', "octopusApiKey").WithDescription("Octopus API key");
parser.Setup(o => o.ProjectName).As('p', "projectName").WithDescription("Project Name");
parser.Setup(o => o.Normalize).As('n', "normalize").SetDefault(true).WithDescription("Project Name");
parser.SetupHelp("?", "help").Callback(text => System.Console.WriteLine(text));

var result = parser.Parse(args);
Expand Down
8 changes: 0 additions & 8 deletions OctopusProjectBuilder.Console/Properties/launchSettings.json

This file was deleted.

9 changes: 8 additions & 1 deletion OctopusProjectBuilder.DocGen/DocGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,14 @@ private static string GetPropertyDefaultValueText(PropertyInfo propertyInfo)

private static string GetDefaultValueText(Type type)
{
return type.IsValueType ? Activator.CreateInstance(type).ToString() : "null";
if (Nullable.GetUnderlyingType(type) == null)
{
return type.IsValueType ? Activator.CreateInstance(type).ToString() : "null";
}
else
{
return "null";
}
}

private static string GetDescription(MemberInfo memberInfo)
Expand Down
38 changes: 38 additions & 0 deletions OctopusProjectBuilder.Model/Channel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace OctopusProjectBuilder.Model
{
public class Channel
{
public Channel(ElementIdentifier identifier, string description, string projectName, bool? isDefault,
string lifecycleName, IEnumerable<ChannelVersionRule> versionRules, IEnumerable<ElementReference> tenantTags)
{
if (identifier == null)
throw new ArgumentNullException(nameof(identifier));

Identifier = identifier;
Description = description;
ProjectName = projectName;
IsDefault = isDefault;
LifecycleName = lifecycleName;
VersionRules = versionRules;
TenantTags = tenantTags.ToArray();
}

public ElementIdentifier Identifier { get; }
public string Description { get; set; }
public string ProjectName { get; set; }
public bool? IsDefault { get; set; }
public string LifecycleName { get; set; }
public IEnumerable<ChannelVersionRule> VersionRules { get; set; }
public IEnumerable<ElementReference> TenantTags { get; }

public override string ToString()
{
return Identifier.ToString();
}
}
}
19 changes: 19 additions & 0 deletions OctopusProjectBuilder.Model/ChannelVersionRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Collections;
using System.Collections.Generic;

namespace OctopusProjectBuilder.Model
{
public class ChannelVersionRule
{
public ChannelVersionRule(string tag, string versionRange, IEnumerable<ChannelVersionRulePackage> actionPackages)
{
Tag = tag;
VersionRange = versionRange;
ActionPackages = actionPackages;
}

public string Tag { get; set; }
public string VersionRange { get; set; }
public IEnumerable<ChannelVersionRulePackage> ActionPackages { get; set; }
}
}
16 changes: 16 additions & 0 deletions OctopusProjectBuilder.Model/ChannelVersionRulePackage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Octopus.Client.Model;

namespace OctopusProjectBuilder.Model
{
public class ChannelVersionRulePackage
{
public string DeploymentAction { get; set; }
public string PackageReference { get; set; }

public ChannelVersionRulePackage(string deploymentAction, string packageReference)
{
DeploymentAction = deploymentAction;
PackageReference = packageReference;
}
}
}
Loading