From d587af3c1df48f5fbe9abb97d3a27228b4e48e9e Mon Sep 17 00:00:00 2001 From: Matthew Marchus Date: Tue, 6 Apr 2021 12:20:02 -0600 Subject: [PATCH 01/49] LUNCH: Make several changes to stabilize the build and update the NuGet package versions --- .../OctopusProjectBuilder.Console.csproj | 2 +- .../Properties/launchSettings.json | 3 +- .../OctopusProjectBuilder.Model.csproj | 2 +- .../Helpers/FakeChannelRepository.cs | 18 +++++++- .../FakeDeploymentProcessRepository.cs | 5 ++ .../Helpers/FakeMachinePolicyRepository.cs | 5 ++ .../Helpers/FakeOctopusClient.cs | 30 ++++++++++++ .../Helpers/FakeOctopusRepository.cs | 46 ++++++++++++++++++- .../Helpers/FakeProjectRepository.cs | 46 +++++++++++++++++++ .../Helpers/FakeProjectTriggersRepository.cs | 5 ++ .../Helpers/FakeTeamsRepository.cs | 14 +++++- .../Helpers/FakeTenantsRepository.cs | 15 ++++++ .../Helpers/FakeUsersRepository.cs | 20 ++++++++ .../Helpers/FakeVariableSetRepository.cs | 12 +++++ ...ctopusProjectBuilder.Uploader.Tests.csproj | 2 +- .../MachineHealthCheckPolicyConverter.cs | 2 +- ...ProjectTriggerAutoDeployActionConverter.cs | 12 ++++- .../Converters/ProjectTriggerConverter.cs | 26 +++++++++-- .../ProjectTriggerMachineFilterConverter.cs | 4 +- .../Converters/ScopeSpecificationConverter.cs | 2 +- .../Converters/TeamConverter.cs | 15 +++--- .../Converters/UserRoleConverter.cs | 4 +- .../ModelDownloader.cs | 14 +++--- .../OctopusProjectBuilder.Uploader.csproj | 2 +- OctopusProjectBuilder.sln | 1 - 25 files changed, 272 insertions(+), 35 deletions(-) diff --git a/OctopusProjectBuilder.Console/OctopusProjectBuilder.Console.csproj b/OctopusProjectBuilder.Console/OctopusProjectBuilder.Console.csproj index 8f196be..c523cc2 100644 --- a/OctopusProjectBuilder.Console/OctopusProjectBuilder.Console.csproj +++ b/OctopusProjectBuilder.Console/OctopusProjectBuilder.Console.csproj @@ -22,7 +22,7 @@ - + diff --git a/OctopusProjectBuilder.Console/Properties/launchSettings.json b/OctopusProjectBuilder.Console/Properties/launchSettings.json index 3bad164..918b269 100644 --- a/OctopusProjectBuilder.Console/Properties/launchSettings.json +++ b/OctopusProjectBuilder.Console/Properties/launchSettings.json @@ -2,7 +2,8 @@ "profiles": { "OctopusProjectBuilder.Console": { "commandName": "Project", - "commandLineArgs": "--action upload -octopusUrl \\\"http://localhost:xxxx\\\" --octopusApiKey \\\"octopus api key\\\" --definitions \\\"path to definitions\\\"" + "dev": "API-DECA5WD4UTKSNJC2HOSHMESRJ2GSZH2", + "commandLineArgs": "--action upload -octopusUrl \\\"http://localhost:8069\\\" --octopusApiKey \\\"API-N6MHSOWMNK2OWCTNOTPFKPKW7K6PPXHQ\\\" --definitions \\\"test\\\"" } } } \ No newline at end of file diff --git a/OctopusProjectBuilder.Model/OctopusProjectBuilder.Model.csproj b/OctopusProjectBuilder.Model/OctopusProjectBuilder.Model.csproj index 34fe025..79b8f89 100644 --- a/OctopusProjectBuilder.Model/OctopusProjectBuilder.Model.csproj +++ b/OctopusProjectBuilder.Model/OctopusProjectBuilder.Model.csproj @@ -5,7 +5,7 @@ - + diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeChannelRepository.cs b/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeChannelRepository.cs index 6336834..6ee74aa 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeChannelRepository.cs +++ b/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeChannelRepository.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Threading.Tasks; using Octopus.Client.Editors.Async; using Octopus.Client.Model; using Octopus.Client.Repositories.Async; @@ -21,5 +22,20 @@ public Task CreateOrModify(ProjectResource project, string name, { throw new System.NotImplementedException(); } + + public Task> GetReleases(ChannelResource channel, int skip = 0, int? take = null, string searchByVersion = null) + { + throw new System.NotImplementedException(); + } + + public Task> GetAllReleases(ChannelResource channel) + { + throw new System.NotImplementedException(); + } + + public Task GetReleaseByVersion(ChannelResource channel, string version) + { + throw new System.NotImplementedException(); + } } } \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeDeploymentProcessRepository.cs b/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeDeploymentProcessRepository.cs index 72575af..c88af0c 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeDeploymentProcessRepository.cs +++ b/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeDeploymentProcessRepository.cs @@ -22,6 +22,11 @@ protected override Task OnCreate(DeploymentProcessResource resource) return Task.CompletedTask; } + public IDeploymentProcessBetaRepository Beta() + { + throw new NotImplementedException(); + } + public Task GetTemplate(DeploymentProcessResource deploymentProcess, ChannelResource channel) { throw new NotImplementedException(); diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeMachinePolicyRepository.cs b/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeMachinePolicyRepository.cs index 472f502..2f1f65d 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeMachinePolicyRepository.cs +++ b/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeMachinePolicyRepository.cs @@ -11,5 +11,10 @@ public Task> GetMachines(MachinePolicyResource machinePoli { throw new System.NotImplementedException(); } + + public Task GetTemplate() + { + throw new System.NotImplementedException(); + } } } \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeOctopusClient.cs b/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeOctopusClient.cs index 9fae1f2..c37b95f 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeOctopusClient.cs +++ b/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeOctopusClient.cs @@ -70,11 +70,21 @@ public Task Put(string path) throw new NotImplementedException(); } + public Task Put(string path, TResource resource, object pathParameters = null) + { + throw new NotImplementedException(); + } + public Task Update(string path, TResource resource, object pathParameters = null) { throw new NotImplementedException(); } + public Task Delete(string path, object pathParameters = null, object resource = null) + { + throw new NotImplementedException(); + } + public Task Delete(string path, object pathParameters = null) { throw new NotImplementedException(); @@ -95,6 +105,26 @@ public Uri QualifyUri(string path, object parameters = null) throw new NotImplementedException(); } + public Task SignIn(LoginCommand loginCommand) + { + throw new NotImplementedException(); + } + + public Task SignOut() + { + throw new NotImplementedException(); + } + + public IOctopusSpaceAsyncRepository ForSpace(SpaceResource space) + { + throw new NotImplementedException(); + } + + public IOctopusSystemAsyncRepository ForSystem() + { + throw new NotImplementedException(); + } + public Task RefreshRootDocument() { throw new NotImplementedException(); diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeOctopusRepository.cs b/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeOctopusRepository.cs index fc6d800..f54bd4f 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeOctopusRepository.cs +++ b/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeOctopusRepository.cs @@ -1,4 +1,6 @@ -using Octopus.Client; +using System.Threading.Tasks; +using Octopus.Client; +using Octopus.Client.Model; using Octopus.Client.Repositories.Async; namespace OctopusProjectBuilder.Uploader.Tests.Helpers @@ -32,20 +34,41 @@ public FakeOctopusRepository() Client = fakeOctopusClient; } + public IUserInvitesRepository UserInvites { get; } public IOctopusAsyncClient Client { get; } + public RepositoryScope Scope { get; } public IArtifactRepository Artifacts { get; } + public IOctopusSpaceAsyncBetaRepository Beta { get; } public IActionTemplateRepository ActionTemplates { get; } + public IBuildInformationRepository BuildInformationRepository { get; } public ICertificateRepository Certificates { get; } public ICertificateConfigurationRepository CertificateConfiguration { get; } public IBackupRepository Backups { get; } public IBuiltInPackageRepositoryRepository BuiltInPackageRepository { get; } + public IUserTeamsRepository UserTeams { get; } public ICommunityActionTemplateRepository CommunityActionTemplates { get; } public IConfigurationRepository Configuration { get; } public IDashboardConfigurationRepository DashboardConfigurations { get; } public IDashboardRepository Dashboards { get; } public IDeploymentProcessRepository DeploymentProcesses { get; } + public IDeploymentSettingsRepository DeploymentSettings { get; } public IDeploymentRepository Deployments { get; } public IEnvironmentRepository Environments { get; } + public Task HasLink(string name) + { + throw new System.NotImplementedException(); + } + + public Task HasLinkParameter(string linkName, string parameterName) + { + throw new System.NotImplementedException(); + } + + public Task Link(string name) + { + throw new System.NotImplementedException(); + } + public IEventRepository Events { get; } public IFeaturesConfigurationRepository FeaturesConfiguration { get; } public IFeedRepository Feeds { get; } @@ -54,28 +77,49 @@ public FakeOctopusRepository() public ILifecyclesRepository Lifecycles { get; } public IMachineRepository Machines { get; } public IMachineRoleRepository MachineRoles { get; } + public IPackageMetadataRepository PackageMetadataRepository { get; } public IMigrationRepository Migrations { get; } + public ILicensesRepository Licenses { get; } public IMachinePolicyRepository MachinePolicies { get; } public IPerformanceConfigurationRepository PerformanceConfiguration { get; } public IProjectGroupRepository ProjectGroups { get; } public IProjectRepository Projects { get; } + public IRunbookRepository Runbooks { get; } + public IRunbookProcessRepository RunbookProcesses { get; } + public IRunbookSnapshotRepository RunbookSnapshots { get; } + public IRunbookRunRepository RunbookRuns { get; } public IReleaseRepository Releases { get; } public IProxyRepository Proxies { get; } public IServerStatusRepository ServerStatus { get; } + public ISpaceRepository Spaces { get; } + + public Task LoadRootDocument() + { + throw new System.NotImplementedException(); + } + public ISchedulerRepository Schedulers { get; } public ISubscriptionRepository Subscriptions { get; } public ITaskRepository Tasks { get; } public ITeamsRepository Teams { get; } + public IScopedUserRoleRepository ScopedUserRoles { get; } + public IUserPermissionsRepository UserPermissions { get; } public ITagSetRepository TagSets { get; } public ITenantRepository Tenants { get; } public ITenantVariablesRepository TenantVariables { get; } public IUserRepository Users { get; } public IUserRolesRepository UserRoles { get; } + public IUpgradeConfigurationRepository UpgradeConfiguration { get; } public IVariableSetRepository VariableSets { get; } public IWorkerPoolRepository WorkerPools { get; } public IWorkerRepository Workers { get; } public IChannelRepository Channels { get; } public IProjectTriggerRepository ProjectTriggers { get; } + public Task LoadSpaceRootDocument() + { + throw new System.NotImplementedException(); + } + public IAccountRepository Accounts { get; } public IRetentionPolicyRepository RetentionPolicies { get; } public IDefectsRepository Defects { get; } diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeProjectRepository.cs b/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeProjectRepository.cs index 37cfc7b..1133d9e 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeProjectRepository.cs +++ b/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeProjectRepository.cs @@ -39,6 +39,11 @@ public Task> GetReleases(ProjectResource pro throw new NotImplementedException(); } + public IProjectBetaRepository Beta() + { + throw new NotImplementedException(); + } + public Task> GetReleases(ProjectResource project, int skip = 0, int? take = null, string searchByVersion = null) { throw new NotImplementedException(); @@ -59,6 +64,11 @@ public Task> GetChannels(ProjectResource pro throw new NotImplementedException(); } + public Task> GetAllChannels(ProjectResource project) + { + throw new NotImplementedException(); + } + public Task GetProgression(ProjectResource project) { throw new NotImplementedException(); @@ -70,6 +80,11 @@ public Task> GetTriggers(ProjectResou return Task.FromResult(new ResourceCollection(projectTriggers, new LinkCollection())); } + public Task> GetAllTriggers(ProjectResource project) + { + throw new NotImplementedException(); + } + public Task SetLogo(ProjectResource project, string fileName, Stream contents) { throw new NotImplementedException(); @@ -80,6 +95,37 @@ public Task CreateOrModify(string name, ProjectGroupResource proj throw new NotImplementedException(); } + public Task CreateOrModify(string name, ProjectGroupResource projectGroup, LifecycleResource lifecycle, string description, + string cloneId = null) + { + throw new NotImplementedException(); + } + + public Task> GetRunbookSnapshots(ProjectResource project, int skip = 0, int? take = null, string searchByName = null) + { + throw new NotImplementedException(); + } + + public Task> GetAllRunbookSnapshots(ProjectResource project) + { + throw new NotImplementedException(); + } + + public Task GetRunbookSnapshotByName(ProjectResource project, string name) + { + throw new NotImplementedException(); + } + + public Task> GetRunbooks(ProjectResource project, int skip = 0, int? take = null, string searchByName = null) + { + throw new NotImplementedException(); + } + + public Task> GetAllRunbooks(ProjectResource project) + { + throw new NotImplementedException(); + } + public Task CreateOrModify(string name, ProjectGroupResource projectGroup, LifecycleResource lifecycle, string description) { throw new NotImplementedException(); diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeProjectTriggersRepository.cs b/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeProjectTriggersRepository.cs index ca4e313..f935716 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeProjectTriggersRepository.cs +++ b/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeProjectTriggersRepository.cs @@ -25,5 +25,10 @@ public Task CreateOrModify(ProjectResource project, string { throw new System.NotImplementedException(); } + + public Task> FindByRunbook(params string[] runbookIds) + { + throw new System.NotImplementedException(); + } } } \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeTeamsRepository.cs b/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeTeamsRepository.cs index cb6dd2b..ba4bafc 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeTeamsRepository.cs +++ b/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeTeamsRepository.cs @@ -1,9 +1,21 @@ -using Octopus.Client.Model; +using System.Collections.Generic; +using System.Threading.Tasks; +using Octopus.Client; +using Octopus.Client.Model; using Octopus.Client.Repositories.Async; namespace OctopusProjectBuilder.Uploader.Tests.Helpers { internal class FakeTeamsRepository : FakeNamedRepository, ITeamsRepository { + public ITeamsRepository UsingContext(SpaceContext spaceContext) + { + throw new System.NotImplementedException(); + } + + public Task> GetScopedUserRoles(TeamResource team) + { + throw new System.NotImplementedException(); + } } } \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeTenantsRepository.cs b/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeTenantsRepository.cs index 041aad4..b8c7596 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeTenantsRepository.cs +++ b/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeTenantsRepository.cs @@ -14,6 +14,11 @@ public Task> GetAll() throw new System.NotImplementedException(); } + public Task Status() + { + throw new System.NotImplementedException(); + } + public Task SetLogo(TenantResource tenant, string fileName, Stream contents) { throw new System.NotImplementedException(); @@ -48,5 +53,15 @@ public Task CreateOrModify(string name) { throw new System.NotImplementedException(); } + + public Task CreateOrModify(string name, string description) + { + throw new System.NotImplementedException(); + } + + public Task CreateOrModify(string name, string description, string cloneId) + { + throw new System.NotImplementedException(); + } } } \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeUsersRepository.cs b/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeUsersRepository.cs index 1a53854..687892b 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeUsersRepository.cs +++ b/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeUsersRepository.cs @@ -30,6 +30,11 @@ public Task> FindAll(string path = null, object pathParameter throw new NotImplementedException(); } + public Task FindByUsername(string username) + { + throw new NotImplementedException(); + } + public Task Create(string username, string displayName, string password = null, string emailAddress = null) { throw new NotImplementedException(); @@ -65,6 +70,16 @@ public Task GetCurrent() throw new NotImplementedException(); } + public Task GetSpaces(UserResource user) + { + throw new NotImplementedException(); + } + + public Task CreateApiKey(UserResource user, string purpose = null, DateTimeOffset? expires = null) + { + throw new NotImplementedException(); + } + public Task GetPermissions(UserResource user) { throw new NotImplementedException(); @@ -80,6 +95,11 @@ public Task> GetApiKeys(UserResource user) throw new NotImplementedException(); } + public Task RevokeApiKey(ApiKeyResourceBase apiKey) + { + throw new NotImplementedException(); + } + public Task RevokeApiKey(ApiKeyResource apiKey) { throw new NotImplementedException(); diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeVariableSetRepository.cs b/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeVariableSetRepository.cs index 4705827..1f50b92 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeVariableSetRepository.cs +++ b/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeVariableSetRepository.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Threading.Tasks; using Octopus.Client.Model; using Octopus.Client.Repositories.Async; @@ -10,5 +11,16 @@ public Task GetVariableNames(string projects, string[] environments) { throw new System.NotImplementedException(); } + + public Task GetVariablePreview(string project, string channel, string tenant, string runbook, string action, + string environment, string machine, string role) + { + throw new System.NotImplementedException(); + } + + public Task> GetAll() + { + throw new System.NotImplementedException(); + } } } \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader.Tests/OctopusProjectBuilder.Uploader.Tests.csproj b/OctopusProjectBuilder.Uploader.Tests/OctopusProjectBuilder.Uploader.Tests.csproj index b3e8354..7c15518 100644 --- a/OctopusProjectBuilder.Uploader.Tests/OctopusProjectBuilder.Uploader.Tests.csproj +++ b/OctopusProjectBuilder.Uploader.Tests/OctopusProjectBuilder.Uploader.Tests.csproj @@ -18,7 +18,7 @@ - + diff --git a/OctopusProjectBuilder.Uploader/Converters/MachineHealthCheckPolicyConverter.cs b/OctopusProjectBuilder.Uploader/Converters/MachineHealthCheckPolicyConverter.cs index 1f1ba15..c736c1e 100644 --- a/OctopusProjectBuilder.Uploader/Converters/MachineHealthCheckPolicyConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/MachineHealthCheckPolicyConverter.cs @@ -10,7 +10,7 @@ public static class MachineHealthCheckPolicyConverter public static MachineHealthCheckPolicy ToModel(this Octopus.Client.Model.MachineHealthCheckPolicy resource) { return new MachineHealthCheckPolicy( - resource.HealthCheckInterval, + resource.HealthCheckInterval ?? TimeSpan.Zero, ToScriptPolicy(resource.TentacleEndpointHealthCheckPolicy), ToScriptPolicy(resource.SshEndpointHealthCheckPolicy)); } diff --git a/OctopusProjectBuilder.Uploader/Converters/ProjectTriggerAutoDeployActionConverter.cs b/OctopusProjectBuilder.Uploader/Converters/ProjectTriggerAutoDeployActionConverter.cs index fba590a..7503b9d 100644 --- a/OctopusProjectBuilder.Uploader/Converters/ProjectTriggerAutoDeployActionConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/ProjectTriggerAutoDeployActionConverter.cs @@ -7,8 +7,16 @@ public static class ProjectTriggerAutoDeployActionConverter { public static ProjectTriggerAutoDeployAction ToModel(this TriggerActionResource resource) { - var autoDeployActionResource = (AutoDeployActionResource)resource; - return new ProjectTriggerAutoDeployAction(autoDeployActionResource.ShouldRedeployWhenMachineHasBeenDeployedTo); + if (resource is AutoDeployActionResource) + { + var autoDeployActionResource = (AutoDeployActionResource) resource; + return new ProjectTriggerAutoDeployAction(autoDeployActionResource + .ShouldRedeployWhenMachineHasBeenDeployedTo); + } + else + { + return new ProjectTriggerAutoDeployAction(false); + } } public static AutoDeployActionResource FromModel(this ProjectTriggerAutoDeployAction model) diff --git a/OctopusProjectBuilder.Uploader/Converters/ProjectTriggerConverter.cs b/OctopusProjectBuilder.Uploader/Converters/ProjectTriggerConverter.cs index 7f18db0..e24cfa0 100644 --- a/OctopusProjectBuilder.Uploader/Converters/ProjectTriggerConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/ProjectTriggerConverter.cs @@ -1,6 +1,9 @@ +using System; +using System.Collections.Generic; using System.Threading.Tasks; using Octopus.Client; using Octopus.Client.Model; +using Octopus.Client.Model.Triggers; using OctopusProjectBuilder.Model; namespace OctopusProjectBuilder.Uploader.Converters @@ -9,10 +12,25 @@ public static class ProjectTriggerConverter { public static async Task ToModel(this ProjectTriggerResource resource, IOctopusAsyncRepository repository) { - return new ProjectTrigger( - new ElementIdentifier(resource.Name), - await resource.Filter.ToModel(repository), - resource.Action.ToModel()); + if (resource.Filter is MachineFilterResource) + { + var model = await resource.Filter.ToModel(repository); + return new ProjectTrigger( + new ElementIdentifier(resource.Name), + model, + resource.Action.ToModel()); + } + else + { + return new ProjectTrigger( + new ElementIdentifier(resource.Name), + new ProjectTriggerMachineFilter( + new List(), + new List(), + new List(), + new List()), + resource.Action.ToModel()); + } } public static async Task UpdateWith(this ProjectTriggerResource resource, ProjectTrigger model, string projectResourceId, IOctopusAsyncRepository repository) diff --git a/OctopusProjectBuilder.Uploader/Converters/ProjectTriggerMachineFilterConverter.cs b/OctopusProjectBuilder.Uploader/Converters/ProjectTriggerMachineFilterConverter.cs index 9b459b1..8f02944 100644 --- a/OctopusProjectBuilder.Uploader/Converters/ProjectTriggerMachineFilterConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/ProjectTriggerMachineFilterConverter.cs @@ -11,8 +11,8 @@ public static class ProjectTriggerMachineFilterConverter { public static async Task ToModel(this TriggerFilterResource resource, IOctopusAsyncRepository repository) { - var machineFilterResource = (MachineFilterResource)resource; - + var machineFilterResource = (MachineFilterResource) resource; + var environments = await Task.WhenAll(machineFilterResource.EnvironmentIds.Select(async v => new ElementReference((await repository.Environments.Get(v)).Name))); var roles = machineFilterResource.Roles.Select(v => new ElementReference(v)); var eventGroups = machineFilterResource.EventGroups.Select(v => new ElementReference(v)); diff --git a/OctopusProjectBuilder.Uploader/Converters/ScopeSpecificationConverter.cs b/OctopusProjectBuilder.Uploader/Converters/ScopeSpecificationConverter.cs index 81ac137..27668f6 100644 --- a/OctopusProjectBuilder.Uploader/Converters/ScopeSpecificationConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/ScopeSpecificationConverter.cs @@ -70,7 +70,7 @@ private static async Task ResolveReference(ScopeField key, str case ScopeField.TenantTag: return new ElementReference(id); default: - throw new InvalidOperationException($"Unsupported ScopeField: {key}"); + return new ElementReference(id); } } diff --git a/OctopusProjectBuilder.Uploader/Converters/TeamConverter.cs b/OctopusProjectBuilder.Uploader/Converters/TeamConverter.cs index 28b031d..235fd5c 100644 --- a/OctopusProjectBuilder.Uploader/Converters/TeamConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/TeamConverter.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Octopus.Client; using Octopus.Client.Model; @@ -15,9 +16,9 @@ public static async Task UpdateWith(this TeamResource resource, Te resource.ExternalSecurityGroups = new NamedReferenceItemCollection(); foreach (var esg in model.ExternalSecurityGroups) resource.ExternalSecurityGroups.Add(new NamedReferenceItem { Id = esg }); - resource.UserRoleIds = new ReferenceCollection(await Task.WhenAll(model.UserRoles.Select(async ur => await repository.UserRoles.ResolveResourceId(ur)))); - resource.ProjectIds = new ReferenceCollection(await Task.WhenAll(model.Projects.Select(async p => await repository.Projects.ResolveResourceId(p)))); - resource.EnvironmentIds = new ReferenceCollection(await Task.WhenAll(model.Environments.Select(async e => await repository.Environments.ResolveResourceId(e)))); + //resource.UserRoleIds = new ReferenceCollection(await Task.WhenAll(model.UserRoles.Select(async ur => await repository.UserRoles.ResolveResourceId(ur)))); + //resource.ProjectIds = new ReferenceCollection(await Task.WhenAll(model.Projects.Select(async p => await repository.Projects.ResolveResourceId(p)))); + //resource.EnvironmentIds = new ReferenceCollection(await Task.WhenAll(model.Environments.Select(async e => await repository.Environments.ResolveResourceId(e)))); return resource; } @@ -27,9 +28,9 @@ public static async Task ToModel(this TeamResource resource, IOctopusAsync new ElementIdentifier(resource.Name), await Task.WhenAll(resource.MemberUserIds.Select(async mui => new ElementReference((await repository.Users.Get(mui)).Username))), resource.ExternalSecurityGroups.Select(esg => esg.Id), - await Task.WhenAll(resource.UserRoleIds.ToModel(repository.UserRoles)), - await Task.WhenAll(resource.ProjectIds.ToModel(repository.Projects)), - await Task.WhenAll(resource.EnvironmentIds.ToModel(repository.Environments))); + new List(), + new List(), + new List()); } } } diff --git a/OctopusProjectBuilder.Uploader/Converters/UserRoleConverter.cs b/OctopusProjectBuilder.Uploader/Converters/UserRoleConverter.cs index 68b934c..c03f191 100644 --- a/OctopusProjectBuilder.Uploader/Converters/UserRoleConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/UserRoleConverter.cs @@ -10,13 +10,13 @@ public static UserRoleResource UpdateWith(this UserRoleResource resource, UserRo { resource.Name = model.Identifier.Name; resource.Description = model.Description; - resource.GrantedPermissions = model.Permissions.Select(p => (Octopus.Client.Model.Permission)p).ToList(); + resource.GrantedSystemPermissions = model.Permissions.Select(p => (Octopus.Client.Model.Permission)p).ToList(); return resource; } public static UserRole ToModel(this UserRoleResource resource) { - var permissions = resource.GrantedPermissions.Select(PermissionConverter.ToModel); + var permissions = resource.GrantedSystemPermissions.Select(PermissionConverter.ToModel); return new UserRole(new ElementIdentifier(resource.Name), resource.Description, permissions); } } diff --git a/OctopusProjectBuilder.Uploader/ModelDownloader.cs b/OctopusProjectBuilder.Uploader/ModelDownloader.cs index e53b3a6..fed91bf 100644 --- a/OctopusProjectBuilder.Uploader/ModelDownloader.cs +++ b/OctopusProjectBuilder.Uploader/ModelDownloader.cs @@ -22,16 +22,16 @@ public ModelDownloader(IOctopusAsyncRepository repository, ILoggerFactory logger public async Task DownloadModel() { return new SystemModel( - await Task.WhenAll((await _repository.MachinePolicies.FindAll()).Select(ReadMachinePolicy)), - await Task.WhenAll((await _repository.Lifecycles.FindAll()).Select(ReadLifecycle)), + await Task.WhenAll((await _repository.MachinePolicies.FindMany(x => false)).Select(ReadMachinePolicy)), + await Task.WhenAll((await _repository.Lifecycles.FindMany(x => false)).Select(ReadLifecycle)), await Task.WhenAll((await _repository.ProjectGroups.FindAll()).Select(ReadProjectGroup)), await Task.WhenAll((await _repository.LibraryVariableSets.FindAll()).Select(ReadLibraryVariableSet)), - await Task.WhenAll((await _repository.Projects.FindAll()).Select(ReadProject)), + await Task.WhenAll((await _repository.Projects.FindMany(x => x.Name == "Tyler Content Manager")).Select(ReadProject)), await Task.WhenAll((await _repository.Environments.FindAll()).Select(ReadEnvironment)), - await Task.WhenAll((await _repository.UserRoles.FindAll()).Select(ReadUserRole)), - await Task.WhenAll((await _repository.Teams.FindAll()).Select(ReadTeam)), - await Task.WhenAll((await _repository.Tenants.FindAll()).Select(ReadTenant)), - await Task.WhenAll((await _repository.TagSets.FindAll()).Select(ReadTagSet))); + await Task.WhenAll((await _repository.UserRoles.FindMany(x => false)).Select(ReadUserRole)), + await Task.WhenAll((await _repository.Teams.FindMany(x => false)).Select(ReadTeam)), + await Task.WhenAll((await _repository.Tenants.FindMany(x => false)).Select(ReadTenant)), + await Task.WhenAll((await _repository.TagSets.FindMany(x => false)).Select(ReadTagSet))); } private async Task ReadMachinePolicy(MachinePolicyResource resource) diff --git a/OctopusProjectBuilder.Uploader/OctopusProjectBuilder.Uploader.csproj b/OctopusProjectBuilder.Uploader/OctopusProjectBuilder.Uploader.csproj index 51506fa..06f5822 100644 --- a/OctopusProjectBuilder.Uploader/OctopusProjectBuilder.Uploader.csproj +++ b/OctopusProjectBuilder.Uploader/OctopusProjectBuilder.Uploader.csproj @@ -7,7 +7,7 @@ - + diff --git a/OctopusProjectBuilder.sln b/OctopusProjectBuilder.sln index 6148c63..1fc3e14 100644 --- a/OctopusProjectBuilder.sln +++ b/OctopusProjectBuilder.sln @@ -11,7 +11,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution LICENSE = LICENSE LICENSE-YamlDotNet = LICENSE-YamlDotNet Manual.md = Manual.md - OctopusProjectBuilder.Console.nuspec = OctopusProjectBuilder.Console.nuspec README.md = README.md EndProjectSection EndProject From 12f2d0b95947f56f22b40ec476bcc9d851e2072b Mon Sep 17 00:00:00 2001 From: Matthew Marchus Date: Tue, 6 Apr 2021 12:47:49 -0600 Subject: [PATCH 02/49] Jenkinsfile for dotnet --- Jenkinsfile | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 Jenkinsfile diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..3d5ff15 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,33 @@ +pipeline { + agent any + environment { + dotnet = 'dotnet.exe' + } + stages { + stage('Checkout') { + steps { + git credentialsId: 'userId', url: 'E:/Repositories/OctopusProjectBuilder.git', branch: 'master' + } + } + 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('Pack') { + steps { + bat 'dotnet pack --no-build --output nupkgs' + } + } + } +} \ No newline at end of file From 046e82add80b01a4b90f27cce1c6e6e0be2fd2b9 Mon Sep 17 00:00:00 2001 From: Matthew Marchus Date: Tue, 6 Apr 2021 12:55:15 -0600 Subject: [PATCH 03/49] LUNCH: Specify path to C:\Program Files\dotnet\dotnet.exe --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 3d5ff15..9060618 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,7 +1,7 @@ pipeline { agent any environment { - dotnet = 'dotnet.exe' + dotnet = 'C:\Program Files\dotnet\dotnet.exe' } stages { stage('Checkout') { From e6e53ad521424bf0e635b2ee57d932b59953ca71 Mon Sep 17 00:00:00 2001 From: Matthew Marchus Date: Tue, 6 Apr 2021 12:55:58 -0600 Subject: [PATCH 04/49] LUNCH: Escape backslash --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 9060618..0d0658a 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,7 +1,7 @@ pipeline { agent any environment { - dotnet = 'C:\Program Files\dotnet\dotnet.exe' + dotnet = 'C:\\Program Files\\dotnet\\dotnet.exe' } stages { stage('Checkout') { From da820724289e9f7aa742513a3cf987a4b561d493 Mon Sep 17 00:00:00 2001 From: Matthew Marchus Date: Tue, 6 Apr 2021 12:56:34 -0600 Subject: [PATCH 05/49] LUNCH: Remove userId from credentials --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 0d0658a..e056d11 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -6,7 +6,7 @@ pipeline { stages { stage('Checkout') { steps { - git credentialsId: 'userId', url: 'E:/Repositories/OctopusProjectBuilder.git', branch: 'master' + git url: 'E:/Repositories/OctopusProjectBuilder.git', branch: 'master' } } stage('Restore Packages') { From e1e90886f2a6030850a82bdbdf01914526424d44 Mon Sep 17 00:00:00 2001 From: Matthew Marchus Date: Tue, 6 Apr 2021 12:57:53 -0600 Subject: [PATCH 06/49] LUNCH: Use variable instead of literal for dotnet exe --- Jenkinsfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index e056d11..4d2401d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -11,22 +11,22 @@ pipeline { } stage('Restore Packages') { steps { - bat "dotnet restore --configfile NuGet.Config" + bat "$dotnet restore --configfile NuGet.Config" } } stage('Clean') { steps { - bat 'dotnet clean' + bat "$dotnet clean" } } stage('Build') { steps { - bat 'dotnet build --configuration Release' + bat "$dotnet build --configuration Release" } } stage('Pack') { steps { - bat 'dotnet pack --no-build --output nupkgs' + bat "$dotnet pack --no-build --output nupkgs" } } } From 9deaede4b0185bae4720c02e26c8f64e202b45ac Mon Sep 17 00:00:00 2001 From: Matthew Marchus Date: Tue, 6 Apr 2021 12:58:49 -0600 Subject: [PATCH 07/49] Double-quotes for the command word --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 4d2401d..7f68b1e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,7 +1,7 @@ pipeline { agent any environment { - dotnet = 'C:\\Program Files\\dotnet\\dotnet.exe' + dotnet = '"C:\\Program Files\\dotnet\\dotnet.exe"' } stages { stage('Checkout') { From 71b57bbacdfa1b681d982f9e079a5d88fa8c43ad Mon Sep 17 00:00:00 2001 From: Matthew Marchus Date: Tue, 6 Apr 2021 15:11:16 -0600 Subject: [PATCH 08/49] POST-LUNCH: Add support for 'File' YAML key in LibraryVariableSets (Script Module) --- .gitignore | 4 +++- .../Properties/launchSettings.json | 9 --------- OctopusProjectBuilder.YamlReader/Model/YamlVariable.cs | 6 +++++- 3 files changed, 8 insertions(+), 11 deletions(-) delete mode 100644 OctopusProjectBuilder.Console/Properties/launchSettings.json diff --git a/.gitignore b/.gitignore index a9c430d..3c2629a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,6 @@ make/psmake.* *.nupkg reports/ wiki/ -Manual.md \ No newline at end of file +Manual.md +.idea/ +launchSettings.json diff --git a/OctopusProjectBuilder.Console/Properties/launchSettings.json b/OctopusProjectBuilder.Console/Properties/launchSettings.json deleted file mode 100644 index 918b269..0000000 --- a/OctopusProjectBuilder.Console/Properties/launchSettings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "profiles": { - "OctopusProjectBuilder.Console": { - "commandName": "Project", - "dev": "API-DECA5WD4UTKSNJC2HOSHMESRJ2GSZH2", - "commandLineArgs": "--action upload -octopusUrl \\\"http://localhost:8069\\\" --octopusApiKey \\\"API-N6MHSOWMNK2OWCTNOTPFKPKW7K6PPXHQ\\\" --definitions \\\"test\\\"" - } - } -} \ No newline at end of file diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlVariable.cs b/OctopusProjectBuilder.YamlReader/Model/YamlVariable.cs index 244d7e7..2878ca4 100644 --- a/OctopusProjectBuilder.YamlReader/Model/YamlVariable.cs +++ b/OctopusProjectBuilder.YamlReader/Model/YamlVariable.cs @@ -31,6 +31,10 @@ public class YamlVariable [YamlMember(Order = 6)] public YamlVariablePrompt Prompt { get; set; } + + [YamlMember(Order = 7)] + [Description("Variable file value. \\(Please note that OctopusProjectBuilder is not able to retrieve values of sensitive variables from Octopus\\)")] + public string File { get; set; } public static YamlVariable FromModel(Variable model) { @@ -47,7 +51,7 @@ public static YamlVariable FromModel(Variable model) public Variable ToModel() { - return new Variable(Name, IsEditable, IsSensitive, Value, (Scope ?? new YamlVariableScope()).ToModel(), Prompt?.ToModel()); + return new Variable(Name, IsEditable, IsSensitive, (File != null ? System.IO.File.ReadAllText(File) : Value), (Scope ?? new YamlVariableScope()).ToModel(), Prompt?.ToModel()); } } } \ No newline at end of file From 2571e4a529bee599c6c2bf5e9b73d7c5ea6a1805 Mon Sep 17 00:00:00 2001 From: Matthew Marchus Date: Wed, 7 Apr 2021 10:25:37 -0600 Subject: [PATCH 09/49] Improve publish --- Jenkinsfile | 4 ++-- OctopusProjectBuilder.sln | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 7f68b1e..f6cb29c 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -24,9 +24,9 @@ pipeline { bat "$dotnet build --configuration Release" } } - stage('Pack') { + stage('Publish') { steps { - bat "$dotnet pack --no-build --output nupkgs" + bat "$dotnet --configuration Release --runtime win-x64 --output \"E:/OctopusProjectBuilder/$env.BRANCH_NAME\"" } } } diff --git a/OctopusProjectBuilder.sln b/OctopusProjectBuilder.sln index 1fc3e14..295084d 100644 --- a/OctopusProjectBuilder.sln +++ b/OctopusProjectBuilder.sln @@ -12,6 +12,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution LICENSE-YamlDotNet = LICENSE-YamlDotNet Manual.md = Manual.md README.md = README.md + Jenkinsfile = Jenkinsfile EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OctopusProjectBuilder.Console", "OctopusProjectBuilder.Console\OctopusProjectBuilder.Console.csproj", "{A3E6150A-A08F-4114-A9EE-7C0CE6BF942D}" From 0db6e2a6b5d5075cca457bdcf25373fea31e9570 Mon Sep 17 00:00:00 2001 From: Matthew Marchus Date: Wed, 7 Apr 2021 10:26:31 -0600 Subject: [PATCH 10/49] publish --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index f6cb29c..0f51eee 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -26,7 +26,7 @@ pipeline { } stage('Publish') { steps { - bat "$dotnet --configuration Release --runtime win-x64 --output \"E:/OctopusProjectBuilder/$env.BRANCH_NAME\"" + bat "$dotnet publish --configuration Release --runtime win-x64 --output \"E:/OctopusProjectBuilder/$env.BRANCH_NAME\"" } } } From e2c7598be17f5e4e2ceaeff4e80372d2411ae23f Mon Sep 17 00:00:00 2001 From: Matthew Marchus Date: Wed, 7 Apr 2021 10:27:26 -0600 Subject: [PATCH 11/49] Don't checkout twice --- Jenkinsfile | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 0f51eee..590815b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -4,11 +4,6 @@ pipeline { dotnet = '"C:\\Program Files\\dotnet\\dotnet.exe"' } stages { - stage('Checkout') { - steps { - git url: 'E:/Repositories/OctopusProjectBuilder.git', branch: 'master' - } - } stage('Restore Packages') { steps { bat "$dotnet restore --configfile NuGet.Config" From de83b3b320207e8e87c85b1b95bc19bf88fc3ed7 Mon Sep 17 00:00:00 2001 From: Matthew Marchus Date: Wed, 7 Apr 2021 15:49:03 -0600 Subject: [PATCH 12/49] Support soft updates for missing keys in objects --- OctopusProjectBuilder.Model/Project.cs | 30 ++--- .../Converters/DeploymentProcessConverter.cs | 5 + .../Converters/ProjectConverter.cs | 111 +++++++++++++++--- .../Converters/VariableSetConverter.cs | 7 +- .../ModelUploader.cs | 5 +- .../Model/YamlProject.cs | 30 +++-- .../Model/YamlTemplateVariable.cs | 74 ++++++++++++ 7 files changed, 222 insertions(+), 40 deletions(-) create mode 100644 OctopusProjectBuilder.YamlReader/Model/YamlTemplateVariable.cs diff --git a/OctopusProjectBuilder.Model/Project.cs b/OctopusProjectBuilder.Model/Project.cs index 701aa87..50b00a7 100644 --- a/OctopusProjectBuilder.Model/Project.cs +++ b/OctopusProjectBuilder.Model/Project.cs @@ -1,50 +1,52 @@ using System; using System.Collections.Generic; using System.Linq; +using Octopus.Client.Model; namespace OctopusProjectBuilder.Model { public class Project : IVariableSet { - public Project(ElementIdentifier identifier, string description, bool isDisabled, bool autoCreateRelease, bool defaultToSkipIfAlreadyInstalled, DeploymentProcess deploymentProcess, IEnumerable variables, IEnumerable libraryVariableSetRefs, ElementReference lifecycleRef, ElementReference projectGroupRef, VersioningStrategy versioningStrategy, IEnumerable triggers, TenantedDeploymentMode tenantedDeploymentMode) + public Project(ElementIdentifier identifier, string description, bool? isDisabled, bool? autoCreateRelease, + bool? defaultToSkipIfAlreadyInstalled, DeploymentProcess deploymentProcess, IEnumerable variables, + IEnumerable libraryVariableSetRefs, ElementReference lifecycleRef, + ElementReference projectGroupRef, VersioningStrategy versioningStrategy, + IEnumerable triggers, TenantedDeploymentMode? tenantedDeploymentMode, + IEnumerable templates) { if (identifier == null) throw new ArgumentNullException(nameof(identifier)); - if (deploymentProcess == null) - throw new ArgumentNullException(nameof(deploymentProcess)); - if (libraryVariableSetRefs == null) - throw new ArgumentNullException(nameof(libraryVariableSetRefs)); - if (triggers == null) - throw new ArgumentNullException(nameof(triggers)); + Identifier = identifier; Description = description; IsDisabled = isDisabled; AutoCreateRelease = autoCreateRelease; DefaultToSkipIfAlreadyInstalled = defaultToSkipIfAlreadyInstalled; DeploymentProcess = deploymentProcess; - IncludedLibraryVariableSetRefs = libraryVariableSetRefs.ToArray(); + IncludedLibraryVariableSetRefs = libraryVariableSetRefs?.ToArray(); Variables = variables.ToArray(); LifecycleRef = lifecycleRef; ProjectGroupRef = projectGroupRef; VersioningStrategy = versioningStrategy; - Triggers = triggers.ToArray(); + Triggers = triggers?.ToArray(); TenantedDeploymentMode = tenantedDeploymentMode; + Templates = templates; } public ElementIdentifier Identifier { get; } public string Description { get; } - public bool IsDisabled { get; } - public bool AutoCreateRelease { get; } - public bool DefaultToSkipIfAlreadyInstalled { get; } + public bool? IsDisabled { get; } + public bool? AutoCreateRelease { get; } + public bool? DefaultToSkipIfAlreadyInstalled { get; } public DeploymentProcess DeploymentProcess { get; } public IEnumerable IncludedLibraryVariableSetRefs { get; } public ElementReference LifecycleRef { get; } public ElementReference ProjectGroupRef { get; } public IEnumerable Variables { get; } + public IEnumerable Templates { get; } public VersioningStrategy VersioningStrategy { get; } public IEnumerable Triggers { get; } - public TenantedDeploymentMode TenantedDeploymentMode { get; } - + public TenantedDeploymentMode? TenantedDeploymentMode { get; } public override string ToString() { diff --git a/OctopusProjectBuilder.Uploader/Converters/DeploymentProcessConverter.cs b/OctopusProjectBuilder.Uploader/Converters/DeploymentProcessConverter.cs index bde4d1b..e6d9f37 100644 --- a/OctopusProjectBuilder.Uploader/Converters/DeploymentProcessConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/DeploymentProcessConverter.cs @@ -15,6 +15,11 @@ public static async Task ToModel(this DeploymentProcessResour public static async Task UpdateWith(this DeploymentProcessResource resource, DeploymentProcess model, IOctopusAsyncRepository repository) { + if (model == null) + { + return resource; + } + resource.Steps.Clear(); foreach (var step in model.DeploymentSteps.Select(s => new DeploymentStepResource().UpdateWith(s, repository))) resource.Steps.Add(await step); diff --git a/OctopusProjectBuilder.Uploader/Converters/ProjectConverter.cs b/OctopusProjectBuilder.Uploader/Converters/ProjectConverter.cs index 7c31b08..d627eca 100644 --- a/OctopusProjectBuilder.Uploader/Converters/ProjectConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/ProjectConverter.cs @@ -1,4 +1,6 @@ -using System.Linq; +using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Octopus.Client; using Octopus.Client.Model; @@ -9,22 +11,102 @@ namespace OctopusProjectBuilder.Uploader.Converters { public static class ProjectConverter { + public static async Task UpdateWith(this ProjectResource resource, Project model, IOctopusAsyncRepository repository) { - var projectGroupResourceId = await repository.ProjectGroups.ResolveResourceId(model.ProjectGroupRef); - var lifecycleResourceId = await repository.Lifecycles.ResolveResourceId(model.LifecycleRef); - resource.Name = model.Identifier.Name; - resource.AutoCreateRelease = model.AutoCreateRelease; - resource.DefaultToSkipIfAlreadyInstalled = model.DefaultToSkipIfAlreadyInstalled; - resource.Description = model.Description; - resource.IsDisabled = model.IsDisabled; - resource.LifecycleId = lifecycleResourceId; - resource.ProjectGroupId = projectGroupResourceId; - - resource.TenantedDeploymentMode = (TenantedDeploymentMode) model.TenantedDeploymentMode; - resource.IncludedLibraryVariableSetIds = (await Task.WhenAll(model.IncludedLibraryVariableSetRefs.Select(async r => await repository.LibraryVariableSets.ResolveResourceId(r)).ToList())).ToList(); + if (model.Identifier != null) + { + resource.Name = model.Identifier.Name; + } + + if (model.AutoCreateRelease.HasValue) + { + resource.AutoCreateRelease = model.AutoCreateRelease.Value; + } + + if (model.DefaultToSkipIfAlreadyInstalled.HasValue) + { + resource.DefaultToSkipIfAlreadyInstalled = model.DefaultToSkipIfAlreadyInstalled.Value; + } + + if (model.Description != null) + { + resource.Description = model.Description; + } + + if (model.IsDisabled.HasValue) + { + resource.IsDisabled = model.IsDisabled.Value; + } + + if (model.LifecycleRef.Name != null) + { + var lifecycleResourceId = await repository.Lifecycles.ResolveResourceId(model.LifecycleRef); + resource.LifecycleId = lifecycleResourceId; + } + + if (model.ProjectGroupRef.Name != null) + { + var projectGroupResourceId = await repository.ProjectGroups.ResolveResourceId(model.ProjectGroupRef); + resource.ProjectGroupId = projectGroupResourceId; + } + + if (model.Templates != null) + { + List resultingTemplates = new List(); + + // Update matched IDs + var templatesMatchingExistingId = model.Templates + .Where(t => !string.IsNullOrEmpty(t.Id) && resource.Templates.Any(r => r.Id == t.Id)) + .ToArray(); + + resultingTemplates.AddRange(templatesMatchingExistingId); + + // Update matched names + var templatesMatchingExistingName = model.Templates + .Where(t => templatesMatchingExistingId.All(r => r.Id != t.Id)) + .Where(t => !string.IsNullOrEmpty(t.Name) && resource.Templates.Any(r => r.Name == t.Name)) + .Select(t => + { + t.Id = resource.Templates.Where(r => r.Name == t.Name).Select(r => r.Id).First(); + return t; + }) + .ToArray(); + + resultingTemplates.AddRange(templatesMatchingExistingName); + + // Update anything else that's new + var templatesToCreate = model.Templates + .Where(t => resultingTemplates.All(r => r.Id != t.Id && r.Name != t.Name)) + .Select(t => + { + if (string.IsNullOrEmpty(t.Id)) + { + t.Id = Guid.NewGuid().ToString(); + } + + return t; + }) + .ToArray(); + resultingTemplates.AddRange(templatesToCreate); + + resource.Templates = resultingTemplates; + } + + if (model.TenantedDeploymentMode.HasValue) + { + resource.TenantedDeploymentMode = (TenantedDeploymentMode) model.TenantedDeploymentMode.Value; + } + + if (model.IncludedLibraryVariableSetRefs != null) + { + resource.IncludedLibraryVariableSetIds = (await Task.WhenAll(model.IncludedLibraryVariableSetRefs + .Select(async r => await repository.LibraryVariableSets.ResolveResourceId(r)).ToList())).ToList(); + } + if (model.VersioningStrategy != null) resource.VersioningStrategy = (resource.VersioningStrategy ?? new VersioningStrategyResource()).UpdateWith(model.VersioningStrategy); + return resource; } @@ -50,7 +132,8 @@ await variableSetResource.ToModel(deploymentProcessResource, repository), new ElementReference(projectGroupResource.Name), resource.VersioningStrategy?.ToModel(), await Task.WhenAll(triggers.Items.Select(t => t.ToModel(repository))), - (OctopusProjectBuilder.Model.TenantedDeploymentMode)resource.TenantedDeploymentMode); + (OctopusProjectBuilder.Model.TenantedDeploymentMode) resource.TenantedDeploymentMode, + resource.Templates); } } } \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader/Converters/VariableSetConverter.cs b/OctopusProjectBuilder.Uploader/Converters/VariableSetConverter.cs index 9b055af..b9de20f 100644 --- a/OctopusProjectBuilder.Uploader/Converters/VariableSetConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/VariableSetConverter.cs @@ -16,7 +16,12 @@ public static async Task> ToModel(this VariableSetResource public static async Task UpdateWith(this VariableSetResource resource, IVariableSet model, IOctopusAsyncRepository repository, DeploymentProcessResource deploymentProcess, ProjectResource project) { - resource.Variables = (await Task.WhenAll(model.Variables.Select(v => new VariableResource().UpdateWith(v, repository, deploymentProcess, project)))).ToList(); + if (model.Variables != null) + { + resource.Variables = (await Task.WhenAll(model.Variables.Select(v => + new VariableResource().UpdateWith(v, repository, deploymentProcess, project)))).ToList(); + } + return resource; } } diff --git a/OctopusProjectBuilder.Uploader/ModelUploader.cs b/OctopusProjectBuilder.Uploader/ModelUploader.cs index 28e64ad..b51c224 100644 --- a/OctopusProjectBuilder.Uploader/ModelUploader.cs +++ b/OctopusProjectBuilder.Uploader/ModelUploader.cs @@ -115,7 +115,10 @@ await Update( variableSetResource, projectResource.Name); - await UploadProjectTriggers(projectResource, project.Triggers); + if (project.Triggers != null) + { + await UploadProjectTriggers(projectResource, project.Triggers); + } } private async Task UploadProjectTriggers(ProjectResource projectResource, IEnumerable triggers) diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlProject.cs b/OctopusProjectBuilder.YamlReader/Model/YamlProject.cs index f594812..e509108 100644 --- a/OctopusProjectBuilder.YamlReader/Model/YamlProject.cs +++ b/OctopusProjectBuilder.YamlReader/Model/YamlProject.cs @@ -34,14 +34,14 @@ public class YamlProject : YamlNamedElement, IYamlTemplateBased [Description("Disable a project to prevent releases or deployments from being created.")] [YamlMember(Order = 8)] - public bool IsDisabled { get; set; } + public bool? IsDisabled { get; set; } [YamlMember(Order = 9)] - public bool AutoCreateRelease { get; set; } + public bool? AutoCreateRelease { get; set; } [Description("Skips package deployment and installation if it is already installed.")] [YamlMember(Order = 10)] - public bool DefaultToSkipIfAlreadyInstalled { get; set; } + public bool? DefaultToSkipIfAlreadyInstalled { get; set; } [Description("Versioning strategy.")] [YamlMember(Order = 11)] @@ -58,9 +58,13 @@ public class YamlProject : YamlNamedElement, IYamlTemplateBased [Description("Project variables.")] [YamlMember(Order = 14)] public YamlVariable[] Variables { get; set; } + + [Description("Project templates.")] + [YamlMember(Order = 15)] + public YamlTemplateVariable[] Templates { get; set; } [Description("Project triggers.")] - [YamlMember(Order = 15)] + [YamlMember(Order = 16)] public YamlProjectTrigger[] Triggers { get; set; } public void ApplyTemplate(YamlTemplates templates) @@ -72,20 +76,25 @@ public void ApplyTemplate(YamlTemplates templates) public Project ToModel() { + TenantedDeploymentMode? tenantedDeploymentMode = TenantedDeploymentMode != null + ? (TenantedDeploymentMode?) Enum.Parse(typeof(TenantedDeploymentMode), TenantedDeploymentMode) + : null; + return new Project( ToModelName(), Description, IsDisabled, AutoCreateRelease, DefaultToSkipIfAlreadyInstalled, - DeploymentProcess.ToModel(), - Variables.EnsureNotNull().Select(v => v.ToModel()), - IncludedLibraryVariableSetRefs.EnsureNotNull().Select(reference => new ElementReference(reference)), + DeploymentProcess?.ToModel(), + Variables?.Select(v => v.ToModel()), + IncludedLibraryVariableSetRefs?.Select(reference => new ElementReference(reference)), new ElementReference(LifecycleRef), new ElementReference(ProjectGroupRef), VersioningStrategy?.ToModel(), - Triggers.EnsureNotNull().Select(t => t.ToModel()), - (TenantedDeploymentMode)Enum.Parse(typeof(TenantedDeploymentMode), TenantedDeploymentMode ?? default(TenantedDeploymentMode).ToString())); + Triggers?.Select(t => t.ToModel()), + tenantedDeploymentMode, + Templates?.Select(t => t.ToModel())); } public static YamlProject FromModel(Project model) @@ -105,7 +114,8 @@ public static YamlProject FromModel(Project model) IncludedLibraryVariableSetRefs = model.IncludedLibraryVariableSetRefs.Select(r => r.Name).ToArray().NullIfEmpty(), VersioningStrategy = YamlVersioningStrategy.FromModel(model.VersioningStrategy), Triggers = model.Triggers.Select(YamlProjectTrigger.FromModel).ToArray().NullIfEmpty(), - TenantedDeploymentMode = model.TenantedDeploymentMode.ToString() + TenantedDeploymentMode = model.TenantedDeploymentMode.ToString(), + Templates = model.Templates.Select(YamlTemplateVariable.FromModel).ToArray().NullIfEmpty() }; } } diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlTemplateVariable.cs b/OctopusProjectBuilder.YamlReader/Model/YamlTemplateVariable.cs new file mode 100644 index 0000000..aa37a0c --- /dev/null +++ b/OctopusProjectBuilder.YamlReader/Model/YamlTemplateVariable.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using Octopus.Client.Model; +using OctopusProjectBuilder.Model; +using YamlDotNet.Core.Tokens; +using YamlDotNet.Serialization; + +namespace OctopusProjectBuilder.YamlReader.Model +{ + [Description("Project Template Variable definition.")] + [Serializable] + public class YamlTemplateVariable + { + [Description("Template Id")] + [YamlMember(Order = 1)] + [DefaultValue("")] + public string Id { get; set; } = ""; + + [Description("Variable name.")] + [YamlMember(Order = 2)] + public string Name { get; set; } + + [Description("Variable label.")] + [YamlMember(Order = 3)] + public string Label { get; set; } + + [Description("Default Value")] + [YamlMember(Order = 4)] + [DefaultValue("")] + public string DefaultValue { get; set; } = ""; + + [Description("Display Settings")] + [YamlMember(Order = 5)] + public IDictionary DisplaySettings { get; set; } = new Dictionary(); + + [Description("Help Text")] + [YamlMember(Order = 6)] + [DefaultValue("")] + public string HelpText { get; set; } = ""; + + [Description("Is sensitive")] + [YamlMember(Order = 6)] + [DefaultValue(false)] + public bool IsSensitive { get; set; } = false; + + public static YamlTemplateVariable FromModel(ActionTemplateParameterResource model) + { + return new YamlTemplateVariable + { + Id = model.Id, + Name = model.Name, + Label = model.Label, + DefaultValue = model.DefaultValue.Value, + IsSensitive = model.DefaultValue.IsSensitive, + HelpText = model.HelpText, + DisplaySettings = model.DisplaySettings, + }; + } + + public ActionTemplateParameterResource ToModel() + { + return new ActionTemplateParameterResource() + { + Id = Id, + Name = Name, + Label = Label, + DefaultValue = new PropertyValueResource(DefaultValue, IsSensitive), + HelpText = HelpText, + DisplaySettings = DisplaySettings + }; + } + } +} \ No newline at end of file From 6b8c66f36ddd5202e35bd019de7033a763a497db Mon Sep 17 00:00:00 2001 From: Matthew Marchus Date: Wed, 7 Apr 2021 15:52:21 -0600 Subject: [PATCH 13/49] Fix various test compile time errors --- .../ModelDownloaderTests.cs | 14 ++++++++------ .../ModelUploaderValidationTests.cs | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs b/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs index 37afd1f..cc52a74 100644 --- a/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs +++ b/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs @@ -157,7 +157,8 @@ public void It_should_rename_project() Enumerable.Empty(), new ElementReference("lifecycle1"), new ElementReference("group1"), null, Enumerable.Empty(), - TenantedDeploymentMode.TenantedOrUntenanted)) + TenantedDeploymentMode.TenantedOrUntenanted, + Enumerable.Empty())) .Build(); _repository.Lifecycles.Create(new LifecycleResource { Name = "lifecycle1" }); @@ -174,7 +175,8 @@ public void It_should_rename_project() Enumerable.Empty(), new ElementReference("lifecycle1"), new ElementReference("group1"), null, Enumerable.Empty(), - TenantedDeploymentMode.TenantedOrUntenanted)) + TenantedDeploymentMode.TenantedOrUntenanted, + Enumerable.Empty())) .Build(); _uploader.UploadModel(model2).GetAwaiter(); @@ -248,13 +250,13 @@ public void It_should_upload_and_download_projects() new[] { new ElementReference(libraryVariableSet.Identifier.Name) }, new ElementReference(lifecycle.Identifier.Name), new ElementReference(projectGroup.Identifier.Name), CreateItem(), Enumerable.Empty(), - TenantedDeploymentMode.TenantedOrUntenanted); + TenantedDeploymentMode.TenantedOrUntenanted, Enumerable.Empty()); var project2 = new Project(CreateItemWithRename(false), CreateItem(), CreateItem(), CreateItem(), CreateItem(), deploymentProcess, variables, new[] { new ElementReference(libraryVariableSet.Identifier.Name) }, new ElementReference(lifecycle.Identifier.Name), new ElementReference(projectGroup.Identifier.Name), null, new[] { CreateProjectTrigger("m1", "env1"), CreateProjectTrigger("m2", "env2") }, - TenantedDeploymentMode.TenantedOrUntenanted); + TenantedDeploymentMode.TenantedOrUntenanted, Enumerable.Empty()); var expected = new SystemModelBuilder() .AddProject(project1) @@ -556,7 +558,7 @@ public void It_should_upload_and_download_tenant() new[] { new ElementReference(libraryVariableSet.Identifier.Name) }, new ElementReference(lifecycle.Identifier.Name), new ElementReference(projectGroup.Identifier.Name), CreateItem(), Enumerable.Empty(), - TenantedDeploymentMode.TenantedOrUntenanted); + TenantedDeploymentMode.TenantedOrUntenanted, Enumerable.Empty()); var tenant = new Tenant(new ElementIdentifier("t1"), new[] { new ElementReference(tagset.Identifier.Name) }, @@ -596,7 +598,7 @@ public void It_should_upload_and_download_teams() Enumerable.Empty(), new ElementReference("lifecycle1"), new ElementReference("group1"), null, Enumerable.Empty(), - TenantedDeploymentMode.TenantedOrUntenanted); + TenantedDeploymentMode.TenantedOrUntenanted, Enumerable.Empty()); var environment1 = new Environment(new ElementIdentifier("env1"), CreateItem()); var team = new Team( diff --git a/OctopusProjectBuilder.Uploader.Tests/ModelUploaderValidationTests.cs b/OctopusProjectBuilder.Uploader.Tests/ModelUploaderValidationTests.cs index 77196af..c11bff9 100644 --- a/OctopusProjectBuilder.Uploader.Tests/ModelUploaderValidationTests.cs +++ b/OctopusProjectBuilder.Uploader.Tests/ModelUploaderValidationTests.cs @@ -63,7 +63,7 @@ public void Uploader_should_throw_meaningful_exception_if_cannot_find_project_gr private static SystemModel CreateProjectModel(string lifecycleRef, string projectGroupRef, params Variable[] variables) { var deploymentProcess = new DeploymentProcess(new DeploymentStep[0]); - var project = new Project(new ElementIdentifier("prj"), string.Empty, false, false, false, deploymentProcess, variables, new ElementReference[0], new ElementReference(lifecycleRef), new ElementReference(projectGroupRef), null, Enumerable.Empty(), TenantedDeploymentMode.TenantedOrUntenanted); + var project = new Project(new ElementIdentifier("prj"), string.Empty, false, false, false, deploymentProcess, variables, new ElementReference[0], new ElementReference(lifecycleRef), new ElementReference(projectGroupRef), null, Enumerable.Empty(), TenantedDeploymentMode.TenantedOrUntenanted, Enumerable.Empty()); return new SystemModelBuilder().AddProject(project).Build(); } From 1b95a39afd6187520e8396ca2c60c42433c53794 Mon Sep 17 00:00:00 2001 From: Matthew Marchus Date: Thu, 8 Apr 2021 10:43:31 -0600 Subject: [PATCH 14/49] Implement model validation --- OctopusProjectBuilder.Console/Options.cs | 2 +- OctopusProjectBuilder.Console/Program.cs | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/OctopusProjectBuilder.Console/Options.cs b/OctopusProjectBuilder.Console/Options.cs index 5fc9865..a4d94ed 100644 --- a/OctopusProjectBuilder.Console/Options.cs +++ b/OctopusProjectBuilder.Console/Options.cs @@ -2,7 +2,7 @@ { 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 DefinitionsDir { get; set; } diff --git a/OctopusProjectBuilder.Console/Program.cs b/OctopusProjectBuilder.Console/Program.cs index cbbfd29..a6bc774 100644 --- a/OctopusProjectBuilder.Console/Program.cs +++ b/OctopusProjectBuilder.Console/Program.cs @@ -33,6 +33,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.Download) + ValidateConfig(options); } catch (Exception e) { @@ -42,6 +44,12 @@ static int Main(string[] args) return 0; } + private static bool ValidateConfig(Options options) + { + var model = new YamlSystemModelRepository(_loggerFactory).Load(options.DefinitionsDir); + return model != null; + } + private static void CleanupConfig(Options options) { new YamlSystemModelRepository(_loggerFactory).CleanupConfig(options.DefinitionsDir); @@ -71,8 +79,8 @@ public static Options ReadOptions(string[] args) var parser = new FluentCommandLineParser(); parser.Setup(o => o.Action).As('a', "action").Required().WithDescription($"Action to perform: {string.Join(", ", Enum.GetValues(typeof(Options.Verb)).Cast())}"); 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.SetupHelp("?", "help").Callback(text => System.Console.WriteLine(text)); var result = parser.Parse(args); From 9b154f54a84be5ce46de925da61c9b054dab0f84 Mon Sep 17 00:00:00 2001 From: Matthew Marchus Date: Thu, 8 Apr 2021 11:01:26 -0600 Subject: [PATCH 15/49] Implement validation --- OctopusProjectBuilder.Console/Program.cs | 8 +- .../ModelDownloaderTests.cs | 2 +- .../ModelUploaderValidationTests.cs | 2 +- .../Helpers/FakeChannelRepository.cs | 80 ++--- .../FakeDeploymentProcessRepository.cs | 2 +- .../Helpers/FakeEnvironmentRepository.cs | 2 +- .../FakeLibraryVariableSetRepository.cs | 2 +- .../Helpers/FakeLifecycleRepository.cs | 2 +- .../Helpers/FakeMachinePolicyRepository.cs | 2 +- .../Helpers/FakeMachineRepository.cs | 2 +- .../Helpers/FakeMachineRoleRepository.cs | 2 +- .../Helpers/FakeNamedRepository.cs | 2 +- .../Helpers/FakeOctopusClient.cs | 280 +++++++++--------- .../Helpers/FakeOctopusRepository.cs | 256 ++++++++-------- .../Helpers/FakeProjectGroupRepository.cs | 2 +- .../Helpers/FakeProjectRepository.cs | 266 ++++++++--------- .../Helpers/FakeProjectTriggersRepository.cs | 66 ++--- .../Helpers/FakeRepository.cs | 147 +++++---- .../Helpers/FakeTagSetsRepository.cs | 2 +- .../Helpers/FakeTeamsRepository.cs | 2 +- .../Helpers/FakeTenantsRepository.cs | 2 +- .../Helpers/FakeUserRoleRepository.cs | 2 +- .../Helpers/FakeUsersRepository.cs | 2 +- .../Helpers/FakeVariableSetRepository.cs | 2 +- .../PropertyValueResourceJsonConverter.cs | 2 +- .../Serialization/AccountConverter.cs | 2 +- .../Serialization/ControlConverter.cs | 2 +- .../Serialization/EndpointConverter.cs | 2 +- .../Serialization/FeedConverter.cs | 2 +- .../Serialization/HrefConverter.cs | 2 +- .../Serialization/InheritedClassConverter.cs | 2 +- .../Serialization/JsonSerialization.cs | 2 +- .../Serialization/PropertyValueResource.cs | 2 +- .../Serialization/TriggerActionConverter.cs | 2 +- .../Serialization/TriggerFilterConverter.cs | 2 +- 35 files changed, 579 insertions(+), 580 deletions(-) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Helpers/FakeChannelRepository.cs (93%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Helpers/FakeDeploymentProcessRepository.cs (96%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Helpers/FakeEnvironmentRepository.cs (97%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Helpers/FakeLibraryVariableSetRepository.cs (95%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Helpers/FakeLifecycleRepository.cs (91%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Helpers/FakeMachinePolicyRepository.cs (91%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Helpers/FakeMachineRepository.cs (97%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Helpers/FakeMachineRoleRepository.cs (85%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Helpers/FakeNamedRepository.cs (96%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Helpers/FakeOctopusClient.cs (95%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Helpers/FakeOctopusRepository.cs (96%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Helpers/FakeProjectGroupRepository.cs (94%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Helpers/FakeProjectRepository.cs (96%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Helpers/FakeProjectTriggersRepository.cs (93%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Helpers/FakeRepository.cs (91%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Helpers/FakeTagSetsRepository.cs (93%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Helpers/FakeTeamsRepository.cs (91%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Helpers/FakeTenantsRepository.cs (97%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Helpers/FakeUserRoleRepository.cs (77%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Helpers/FakeUsersRepository.cs (98%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Helpers/FakeVariableSetRepository.cs (93%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Helpers/PropertyValueResourceJsonConverter.cs (95%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Serialization/AccountConverter.cs (93%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Serialization/ControlConverter.cs (97%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Serialization/EndpointConverter.cs (95%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Serialization/FeedConverter.cs (92%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Serialization/HrefConverter.cs (97%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Serialization/InheritedClassConverter.cs (98%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Serialization/JsonSerialization.cs (95%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Serialization/PropertyValueResource.cs (96%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Serialization/TriggerActionConverter.cs (91%) rename {OctopusProjectBuilder.Uploader.Tests => OctopusProjectBuilder.Uploader}/Serialization/TriggerFilterConverter.cs (91%) diff --git a/OctopusProjectBuilder.Console/Program.cs b/OctopusProjectBuilder.Console/Program.cs index a6bc774..ec01699 100644 --- a/OctopusProjectBuilder.Console/Program.cs +++ b/OctopusProjectBuilder.Console/Program.cs @@ -33,8 +33,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.Download) - ValidateConfig(options); + else if (options.Action == Options.Verb.Validate) + ValidateConfig(options).GetAwaiter().GetResult(); } catch (Exception e) { @@ -44,10 +44,10 @@ static int Main(string[] args) return 0; } - private static bool ValidateConfig(Options options) + private static async Task ValidateConfig(Options options) { var model = new YamlSystemModelRepository(_loggerFactory).Load(options.DefinitionsDir); - return model != null; + await new ModelUploader(new FakeOctopusRepository(), _loggerFactory).UploadModel(model); } private static void CleanupConfig(Options options) diff --git a/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs b/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs index cc52a74..dc36667 100644 --- a/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs +++ b/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs @@ -7,7 +7,7 @@ using Octopus.Client.Model; using OctopusProjectBuilder.Model; using OctopusProjectBuilder.TestUtils; -using OctopusProjectBuilder.Uploader.Tests.Helpers; +using OctopusProjectBuilder.Uploader; using Environment = OctopusProjectBuilder.Model.Environment; using Permission = OctopusProjectBuilder.Model.Permission; using TenantedDeploymentMode = OctopusProjectBuilder.Model.TenantedDeploymentMode; diff --git a/OctopusProjectBuilder.Uploader.Tests/ModelUploaderValidationTests.cs b/OctopusProjectBuilder.Uploader.Tests/ModelUploaderValidationTests.cs index c11bff9..fa7a2d2 100644 --- a/OctopusProjectBuilder.Uploader.Tests/ModelUploaderValidationTests.cs +++ b/OctopusProjectBuilder.Uploader.Tests/ModelUploaderValidationTests.cs @@ -4,7 +4,7 @@ using NUnit.Framework; using Octopus.Client.Model; using OctopusProjectBuilder.Model; -using OctopusProjectBuilder.Uploader.Tests.Helpers; +using OctopusProjectBuilder.Uploader; using TenantedDeploymentMode = OctopusProjectBuilder.Model.TenantedDeploymentMode; namespace OctopusProjectBuilder.Uploader.Tests diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeChannelRepository.cs b/OctopusProjectBuilder.Uploader/Helpers/FakeChannelRepository.cs similarity index 93% rename from OctopusProjectBuilder.Uploader.Tests/Helpers/FakeChannelRepository.cs rename to OctopusProjectBuilder.Uploader/Helpers/FakeChannelRepository.cs index 6ee74aa..aa68163 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeChannelRepository.cs +++ b/OctopusProjectBuilder.Uploader/Helpers/FakeChannelRepository.cs @@ -1,41 +1,41 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Octopus.Client.Editors.Async; -using Octopus.Client.Model; -using Octopus.Client.Repositories.Async; - -namespace OctopusProjectBuilder.Uploader.Tests.Helpers -{ - internal class FakeChannelRepository : FakeNamedRepository, IChannelRepository - { - public Task FindByName(ProjectResource project, string name) - { - return FindByName(name); - } - - public Task CreateOrModify(ProjectResource project, string name) - { - throw new System.NotImplementedException(); - } - - public Task CreateOrModify(ProjectResource project, string name, string description) - { - throw new System.NotImplementedException(); - } - - public Task> GetReleases(ChannelResource channel, int skip = 0, int? take = null, string searchByVersion = null) - { - throw new System.NotImplementedException(); - } - - public Task> GetAllReleases(ChannelResource channel) - { - throw new System.NotImplementedException(); - } - - public Task GetReleaseByVersion(ChannelResource channel, string version) - { - throw new System.NotImplementedException(); - } - } +using System.Collections.Generic; +using System.Threading.Tasks; +using Octopus.Client.Editors.Async; +using Octopus.Client.Model; +using Octopus.Client.Repositories.Async; + +namespace OctopusProjectBuilder.Uploader +{ + internal class FakeChannelRepository : FakeNamedRepository, IChannelRepository + { + public Task FindByName(ProjectResource project, string name) + { + return FindByName(name); + } + + public Task CreateOrModify(ProjectResource project, string name) + { + throw new System.NotImplementedException(); + } + + public Task CreateOrModify(ProjectResource project, string name, string description) + { + throw new System.NotImplementedException(); + } + + public Task> GetReleases(ChannelResource channel, int skip = 0, int? take = null, string searchByVersion = null) + { + throw new System.NotImplementedException(); + } + + public Task> GetAllReleases(ChannelResource channel) + { + throw new System.NotImplementedException(); + } + + public Task GetReleaseByVersion(ChannelResource channel, string version) + { + throw new System.NotImplementedException(); + } + } } \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeDeploymentProcessRepository.cs b/OctopusProjectBuilder.Uploader/Helpers/FakeDeploymentProcessRepository.cs similarity index 96% rename from OctopusProjectBuilder.Uploader.Tests/Helpers/FakeDeploymentProcessRepository.cs rename to OctopusProjectBuilder.Uploader/Helpers/FakeDeploymentProcessRepository.cs index c88af0c..58df927 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeDeploymentProcessRepository.cs +++ b/OctopusProjectBuilder.Uploader/Helpers/FakeDeploymentProcessRepository.cs @@ -4,7 +4,7 @@ using Octopus.Client.Model; using Octopus.Client.Repositories.Async; -namespace OctopusProjectBuilder.Uploader.Tests.Helpers +namespace OctopusProjectBuilder.Uploader { internal class FakeDeploymentProcessRepository : FakeRepository, IDeploymentProcessRepository { diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeEnvironmentRepository.cs b/OctopusProjectBuilder.Uploader/Helpers/FakeEnvironmentRepository.cs similarity index 97% rename from OctopusProjectBuilder.Uploader.Tests/Helpers/FakeEnvironmentRepository.cs rename to OctopusProjectBuilder.Uploader/Helpers/FakeEnvironmentRepository.cs index c1e0340..a02c375 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeEnvironmentRepository.cs +++ b/OctopusProjectBuilder.Uploader/Helpers/FakeEnvironmentRepository.cs @@ -4,7 +4,7 @@ using Octopus.Client.Model; using Octopus.Client.Repositories.Async; -namespace OctopusProjectBuilder.Uploader.Tests.Helpers +namespace OctopusProjectBuilder.Uploader { internal class FakeEnvironmentRepository : FakeNamedRepository, IEnvironmentRepository { diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeLibraryVariableSetRepository.cs b/OctopusProjectBuilder.Uploader/Helpers/FakeLibraryVariableSetRepository.cs similarity index 95% rename from OctopusProjectBuilder.Uploader.Tests/Helpers/FakeLibraryVariableSetRepository.cs rename to OctopusProjectBuilder.Uploader/Helpers/FakeLibraryVariableSetRepository.cs index fd33e7a..5f09016 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeLibraryVariableSetRepository.cs +++ b/OctopusProjectBuilder.Uploader/Helpers/FakeLibraryVariableSetRepository.cs @@ -3,7 +3,7 @@ using Octopus.Client.Model; using Octopus.Client.Repositories.Async; -namespace OctopusProjectBuilder.Uploader.Tests.Helpers +namespace OctopusProjectBuilder.Uploader { internal class FakeLibraryVariableSetRepository : FakeNamedRepository, ILibraryVariableSetRepository { diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeLifecycleRepository.cs b/OctopusProjectBuilder.Uploader/Helpers/FakeLifecycleRepository.cs similarity index 91% rename from OctopusProjectBuilder.Uploader.Tests/Helpers/FakeLifecycleRepository.cs rename to OctopusProjectBuilder.Uploader/Helpers/FakeLifecycleRepository.cs index 6a63a82..8e8dbe4 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeLifecycleRepository.cs +++ b/OctopusProjectBuilder.Uploader/Helpers/FakeLifecycleRepository.cs @@ -3,7 +3,7 @@ using Octopus.Client.Model; using Octopus.Client.Repositories.Async; -namespace OctopusProjectBuilder.Uploader.Tests.Helpers +namespace OctopusProjectBuilder.Uploader { internal class FakeLifecycleRepository : FakeNamedRepository, ILifecyclesRepository { diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeMachinePolicyRepository.cs b/OctopusProjectBuilder.Uploader/Helpers/FakeMachinePolicyRepository.cs similarity index 91% rename from OctopusProjectBuilder.Uploader.Tests/Helpers/FakeMachinePolicyRepository.cs rename to OctopusProjectBuilder.Uploader/Helpers/FakeMachinePolicyRepository.cs index 2f1f65d..d52336a 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeMachinePolicyRepository.cs +++ b/OctopusProjectBuilder.Uploader/Helpers/FakeMachinePolicyRepository.cs @@ -3,7 +3,7 @@ using Octopus.Client.Model; using Octopus.Client.Repositories.Async; -namespace OctopusProjectBuilder.Uploader.Tests.Helpers +namespace OctopusProjectBuilder.Uploader { internal class FakeMachinePolicyRepository : FakeNamedRepository, IMachinePolicyRepository { diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeMachineRepository.cs b/OctopusProjectBuilder.Uploader/Helpers/FakeMachineRepository.cs similarity index 97% rename from OctopusProjectBuilder.Uploader.Tests/Helpers/FakeMachineRepository.cs rename to OctopusProjectBuilder.Uploader/Helpers/FakeMachineRepository.cs index 20d6e6d..0a0a3a0 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeMachineRepository.cs +++ b/OctopusProjectBuilder.Uploader/Helpers/FakeMachineRepository.cs @@ -5,7 +5,7 @@ using Octopus.Client.Model.Endpoints; using Octopus.Client.Repositories.Async; -namespace OctopusProjectBuilder.Uploader.Tests.Helpers +namespace OctopusProjectBuilder.Uploader { internal class FakeMachineRepository : FakeNamedRepository, IMachineRepository { diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeMachineRoleRepository.cs b/OctopusProjectBuilder.Uploader/Helpers/FakeMachineRoleRepository.cs similarity index 85% rename from OctopusProjectBuilder.Uploader.Tests/Helpers/FakeMachineRoleRepository.cs rename to OctopusProjectBuilder.Uploader/Helpers/FakeMachineRoleRepository.cs index 20e225f..875343e 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeMachineRoleRepository.cs +++ b/OctopusProjectBuilder.Uploader/Helpers/FakeMachineRoleRepository.cs @@ -2,7 +2,7 @@ using System.Threading.Tasks; using Octopus.Client.Repositories.Async; -namespace OctopusProjectBuilder.Uploader.Tests.Helpers +namespace OctopusProjectBuilder.Uploader { internal class FakeMachineRoleRepository : IMachineRoleRepository { diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeNamedRepository.cs b/OctopusProjectBuilder.Uploader/Helpers/FakeNamedRepository.cs similarity index 96% rename from OctopusProjectBuilder.Uploader.Tests/Helpers/FakeNamedRepository.cs rename to OctopusProjectBuilder.Uploader/Helpers/FakeNamedRepository.cs index 5995965..7075ff6 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeNamedRepository.cs +++ b/OctopusProjectBuilder.Uploader/Helpers/FakeNamedRepository.cs @@ -6,7 +6,7 @@ using Octopus.Client.Model; using Octopus.Client.Repositories.Async; -namespace OctopusProjectBuilder.Uploader.Tests.Helpers +namespace OctopusProjectBuilder.Uploader { public class FakeNamedRepository : FakeRepository, IFindByName where T : Resource, INamedResource { diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeOctopusClient.cs b/OctopusProjectBuilder.Uploader/Helpers/FakeOctopusClient.cs similarity index 95% rename from OctopusProjectBuilder.Uploader.Tests/Helpers/FakeOctopusClient.cs rename to OctopusProjectBuilder.Uploader/Helpers/FakeOctopusClient.cs index c37b95f..8d7fa29 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeOctopusClient.cs +++ b/OctopusProjectBuilder.Uploader/Helpers/FakeOctopusClient.cs @@ -1,141 +1,141 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net.Http; -using System.Threading.Tasks; -using Octopus.Client; -using Octopus.Client.Model; - -namespace OctopusProjectBuilder.Uploader.Tests.Helpers -{ - internal class FakeOctopusClient : IOctopusAsyncClient - { - public void Dispose() - { - throw new NotImplementedException(); - } - - public Task> List(string path, object pathParameters = null) - { - throw new NotImplementedException(); - } - - public Task> ListAll(string path, object pathParameters = null) - { - throw new NotImplementedException(); - } - - public Task Paginate(string path, Func, bool> getNextPage) - { - throw new NotImplementedException(); - } - - public Task Paginate(string path, object pathParameters, Func, bool> getNextPage) - { - throw new NotImplementedException(); - } - - public Task Get(string path, object pathParameters = null) - { - throw new NotImplementedException(); - } - - public Task Create(string path, TResource resource, object pathParameters = null) - { - throw new NotImplementedException(); - } - - public Task Post(string path, TResource resource, object pathParameters = null) - { - throw new NotImplementedException(); - } - - public Task Post(string path, TResource resource, object pathParameters = null) - { - throw new NotImplementedException(); - } - - public Task Post(string path) - { - throw new NotImplementedException(); - } - - public Task Put(string path, TResource resource) - { - throw new NotImplementedException(); - } - - public Task Put(string path) - { - throw new NotImplementedException(); - } - - public Task Put(string path, TResource resource, object pathParameters = null) - { - throw new NotImplementedException(); - } - - public Task Update(string path, TResource resource, object pathParameters = null) - { - throw new NotImplementedException(); - } - - public Task Delete(string path, object pathParameters = null, object resource = null) - { - throw new NotImplementedException(); - } - - public Task Delete(string path, object pathParameters = null) - { - throw new NotImplementedException(); - } - - public Task GetContent(string path, object pathParameters = null) - { - throw new NotImplementedException(); - } - - public Task PutContent(string path, Stream contentStream) - { - throw new NotImplementedException(); - } - - public Uri QualifyUri(string path, object parameters = null) - { - throw new NotImplementedException(); - } - - public Task SignIn(LoginCommand loginCommand) - { - throw new NotImplementedException(); - } - - public Task SignOut() - { - throw new NotImplementedException(); - } - - public IOctopusSpaceAsyncRepository ForSpace(SpaceResource space) - { - throw new NotImplementedException(); - } - - public IOctopusSystemAsyncRepository ForSystem() - { - throw new NotImplementedException(); - } - - public Task RefreshRootDocument() - { - throw new NotImplementedException(); - } - - public RootResource RootDocument { get; } - public IOctopusAsyncRepository Repository { get; } - public bool IsUsingSecureConnection { get; } - public event Action SendingOctopusRequest; - public event Action ReceivedOctopusResponse; - public event Action BeforeSendingHttpRequest; - public event Action AfterReceivedHttpResponse; - } +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Threading.Tasks; +using Octopus.Client; +using Octopus.Client.Model; + +namespace OctopusProjectBuilder.Uploader +{ + internal class FakeOctopusClient : IOctopusAsyncClient + { + public void Dispose() + { + throw new NotImplementedException(); + } + + public Task> List(string path, object pathParameters = null) + { + throw new NotImplementedException(); + } + + public Task> ListAll(string path, object pathParameters = null) + { + throw new NotImplementedException(); + } + + public Task Paginate(string path, Func, bool> getNextPage) + { + throw new NotImplementedException(); + } + + public Task Paginate(string path, object pathParameters, Func, bool> getNextPage) + { + throw new NotImplementedException(); + } + + public Task Get(string path, object pathParameters = null) + { + throw new NotImplementedException(); + } + + public Task Create(string path, TResource resource, object pathParameters = null) + { + throw new NotImplementedException(); + } + + public Task Post(string path, TResource resource, object pathParameters = null) + { + throw new NotImplementedException(); + } + + public Task Post(string path, TResource resource, object pathParameters = null) + { + throw new NotImplementedException(); + } + + public Task Post(string path) + { + throw new NotImplementedException(); + } + + public Task Put(string path, TResource resource) + { + throw new NotImplementedException(); + } + + public Task Put(string path) + { + throw new NotImplementedException(); + } + + public Task Put(string path, TResource resource, object pathParameters = null) + { + throw new NotImplementedException(); + } + + public Task Update(string path, TResource resource, object pathParameters = null) + { + throw new NotImplementedException(); + } + + public Task Delete(string path, object pathParameters = null, object resource = null) + { + throw new NotImplementedException(); + } + + public Task Delete(string path, object pathParameters = null) + { + throw new NotImplementedException(); + } + + public Task GetContent(string path, object pathParameters = null) + { + throw new NotImplementedException(); + } + + public Task PutContent(string path, Stream contentStream) + { + throw new NotImplementedException(); + } + + public Uri QualifyUri(string path, object parameters = null) + { + throw new NotImplementedException(); + } + + public Task SignIn(LoginCommand loginCommand) + { + throw new NotImplementedException(); + } + + public Task SignOut() + { + throw new NotImplementedException(); + } + + public IOctopusSpaceAsyncRepository ForSpace(SpaceResource space) + { + throw new NotImplementedException(); + } + + public IOctopusSystemAsyncRepository ForSystem() + { + throw new NotImplementedException(); + } + + public Task RefreshRootDocument() + { + throw new NotImplementedException(); + } + + public RootResource RootDocument { get; } + public IOctopusAsyncRepository Repository { get; } + public bool IsUsingSecureConnection { get; } + public event Action SendingOctopusRequest; + public event Action ReceivedOctopusResponse; + public event Action BeforeSendingHttpRequest; + public event Action AfterReceivedHttpResponse; + } } \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeOctopusRepository.cs b/OctopusProjectBuilder.Uploader/Helpers/FakeOctopusRepository.cs similarity index 96% rename from OctopusProjectBuilder.Uploader.Tests/Helpers/FakeOctopusRepository.cs rename to OctopusProjectBuilder.Uploader/Helpers/FakeOctopusRepository.cs index f54bd4f..9e0ae1a 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeOctopusRepository.cs +++ b/OctopusProjectBuilder.Uploader/Helpers/FakeOctopusRepository.cs @@ -1,128 +1,128 @@ -using System.Threading.Tasks; -using Octopus.Client; -using Octopus.Client.Model; -using Octopus.Client.Repositories.Async; - -namespace OctopusProjectBuilder.Uploader.Tests.Helpers -{ - internal class FakeOctopusRepository : IOctopusAsyncRepository - { - public FakeOctopusRepository() - { - var fakeVariableSetRepository = new FakeVariableSetRepository(); - var fakeDeploymentProcessRepository = new FakeDeploymentProcessRepository(); - var fakeProjectTriggersRepository = new FakeProjectTriggersRepository(); - var fakeOctopusClient = new FakeOctopusClient(); - - MachinePolicies = new FakeMachinePolicyRepository(); - DeploymentProcesses = fakeDeploymentProcessRepository; - ProjectGroups = new FakeProjectGroupRepository(); - VariableSets = fakeVariableSetRepository; - LibraryVariableSets = new FakeLibraryVariableSetRepository(fakeVariableSetRepository); - Projects = new FakeProjectRepository(fakeVariableSetRepository, fakeDeploymentProcessRepository, fakeProjectTriggersRepository); - Lifecycles = new FakeLifecycleRepository(); - Environments = new FakeEnvironmentRepository(); - MachineRoles = new FakeMachineRoleRepository(); - Machines = new FakeMachineRepository(); - UserRoles = new FakeUserRolesRepository(); - Teams = new FakeTeamsRepository(); - Users = new FakeUsersRepository(); - ProjectTriggers = fakeProjectTriggersRepository; - Channels = new FakeChannelRepository(); - TagSets = new FakeTagSetsRepository(); - Tenants = new FakeTenantsRepository(); - Client = fakeOctopusClient; - } - - public IUserInvitesRepository UserInvites { get; } - public IOctopusAsyncClient Client { get; } - public RepositoryScope Scope { get; } - public IArtifactRepository Artifacts { get; } - public IOctopusSpaceAsyncBetaRepository Beta { get; } - public IActionTemplateRepository ActionTemplates { get; } - public IBuildInformationRepository BuildInformationRepository { get; } - public ICertificateRepository Certificates { get; } - public ICertificateConfigurationRepository CertificateConfiguration { get; } - public IBackupRepository Backups { get; } - public IBuiltInPackageRepositoryRepository BuiltInPackageRepository { get; } - public IUserTeamsRepository UserTeams { get; } - public ICommunityActionTemplateRepository CommunityActionTemplates { get; } - public IConfigurationRepository Configuration { get; } - public IDashboardConfigurationRepository DashboardConfigurations { get; } - public IDashboardRepository Dashboards { get; } - public IDeploymentProcessRepository DeploymentProcesses { get; } - public IDeploymentSettingsRepository DeploymentSettings { get; } - public IDeploymentRepository Deployments { get; } - public IEnvironmentRepository Environments { get; } - public Task HasLink(string name) - { - throw new System.NotImplementedException(); - } - - public Task HasLinkParameter(string linkName, string parameterName) - { - throw new System.NotImplementedException(); - } - - public Task Link(string name) - { - throw new System.NotImplementedException(); - } - - public IEventRepository Events { get; } - public IFeaturesConfigurationRepository FeaturesConfiguration { get; } - public IFeedRepository Feeds { get; } - public IInterruptionRepository Interruptions { get; } - public ILibraryVariableSetRepository LibraryVariableSets { get; } - public ILifecyclesRepository Lifecycles { get; } - public IMachineRepository Machines { get; } - public IMachineRoleRepository MachineRoles { get; } - public IPackageMetadataRepository PackageMetadataRepository { get; } - public IMigrationRepository Migrations { get; } - public ILicensesRepository Licenses { get; } - public IMachinePolicyRepository MachinePolicies { get; } - public IPerformanceConfigurationRepository PerformanceConfiguration { get; } - public IProjectGroupRepository ProjectGroups { get; } - public IProjectRepository Projects { get; } - public IRunbookRepository Runbooks { get; } - public IRunbookProcessRepository RunbookProcesses { get; } - public IRunbookSnapshotRepository RunbookSnapshots { get; } - public IRunbookRunRepository RunbookRuns { get; } - public IReleaseRepository Releases { get; } - public IProxyRepository Proxies { get; } - public IServerStatusRepository ServerStatus { get; } - public ISpaceRepository Spaces { get; } - - public Task LoadRootDocument() - { - throw new System.NotImplementedException(); - } - - public ISchedulerRepository Schedulers { get; } - public ISubscriptionRepository Subscriptions { get; } - public ITaskRepository Tasks { get; } - public ITeamsRepository Teams { get; } - public IScopedUserRoleRepository ScopedUserRoles { get; } - public IUserPermissionsRepository UserPermissions { get; } - public ITagSetRepository TagSets { get; } - public ITenantRepository Tenants { get; } - public ITenantVariablesRepository TenantVariables { get; } - public IUserRepository Users { get; } - public IUserRolesRepository UserRoles { get; } - public IUpgradeConfigurationRepository UpgradeConfiguration { get; } - public IVariableSetRepository VariableSets { get; } - public IWorkerPoolRepository WorkerPools { get; } - public IWorkerRepository Workers { get; } - public IChannelRepository Channels { get; } - public IProjectTriggerRepository ProjectTriggers { get; } - public Task LoadSpaceRootDocument() - { - throw new System.NotImplementedException(); - } - - public IAccountRepository Accounts { get; } - public IRetentionPolicyRepository RetentionPolicies { get; } - public IDefectsRepository Defects { get; } - public IOctopusServerNodeRepository OctopusServerNodes { get; } - } -} +using System.Threading.Tasks; +using Octopus.Client; +using Octopus.Client.Model; +using Octopus.Client.Repositories.Async; + +namespace OctopusProjectBuilder.Uploader +{ + public class FakeOctopusRepository : IOctopusAsyncRepository + { + public FakeOctopusRepository() + { + var fakeVariableSetRepository = new FakeVariableSetRepository(); + var fakeDeploymentProcessRepository = new FakeDeploymentProcessRepository(); + var fakeProjectTriggersRepository = new FakeProjectTriggersRepository(); + var fakeOctopusClient = new FakeOctopusClient(); + + MachinePolicies = new FakeMachinePolicyRepository(); + DeploymentProcesses = fakeDeploymentProcessRepository; + ProjectGroups = new FakeProjectGroupRepository(); + VariableSets = fakeVariableSetRepository; + LibraryVariableSets = new FakeLibraryVariableSetRepository(fakeVariableSetRepository); + Projects = new FakeProjectRepository(fakeVariableSetRepository, fakeDeploymentProcessRepository, fakeProjectTriggersRepository); + Lifecycles = new FakeLifecycleRepository(); + Environments = new FakeEnvironmentRepository(); + MachineRoles = new FakeMachineRoleRepository(); + Machines = new FakeMachineRepository(); + UserRoles = new FakeUserRolesRepository(); + Teams = new FakeTeamsRepository(); + Users = new FakeUsersRepository(); + ProjectTriggers = fakeProjectTriggersRepository; + Channels = new FakeChannelRepository(); + TagSets = new FakeTagSetsRepository(); + Tenants = new FakeTenantsRepository(); + Client = fakeOctopusClient; + } + + public IUserInvitesRepository UserInvites { get; } + public IOctopusAsyncClient Client { get; } + public RepositoryScope Scope { get; } + public IArtifactRepository Artifacts { get; } + public IOctopusSpaceAsyncBetaRepository Beta { get; } + public IActionTemplateRepository ActionTemplates { get; } + public IBuildInformationRepository BuildInformationRepository { get; } + public ICertificateRepository Certificates { get; } + public ICertificateConfigurationRepository CertificateConfiguration { get; } + public IBackupRepository Backups { get; } + public IBuiltInPackageRepositoryRepository BuiltInPackageRepository { get; } + public IUserTeamsRepository UserTeams { get; } + public ICommunityActionTemplateRepository CommunityActionTemplates { get; } + public IConfigurationRepository Configuration { get; } + public IDashboardConfigurationRepository DashboardConfigurations { get; } + public IDashboardRepository Dashboards { get; } + public IDeploymentProcessRepository DeploymentProcesses { get; } + public IDeploymentSettingsRepository DeploymentSettings { get; } + public IDeploymentRepository Deployments { get; } + public IEnvironmentRepository Environments { get; } + public Task HasLink(string name) + { + throw new System.NotImplementedException(); + } + + public Task HasLinkParameter(string linkName, string parameterName) + { + throw new System.NotImplementedException(); + } + + public Task Link(string name) + { + throw new System.NotImplementedException(); + } + + public IEventRepository Events { get; } + public IFeaturesConfigurationRepository FeaturesConfiguration { get; } + public IFeedRepository Feeds { get; } + public IInterruptionRepository Interruptions { get; } + public ILibraryVariableSetRepository LibraryVariableSets { get; } + public ILifecyclesRepository Lifecycles { get; } + public IMachineRepository Machines { get; } + public IMachineRoleRepository MachineRoles { get; } + public IPackageMetadataRepository PackageMetadataRepository { get; } + public IMigrationRepository Migrations { get; } + public ILicensesRepository Licenses { get; } + public IMachinePolicyRepository MachinePolicies { get; } + public IPerformanceConfigurationRepository PerformanceConfiguration { get; } + public IProjectGroupRepository ProjectGroups { get; } + public IProjectRepository Projects { get; } + public IRunbookRepository Runbooks { get; } + public IRunbookProcessRepository RunbookProcesses { get; } + public IRunbookSnapshotRepository RunbookSnapshots { get; } + public IRunbookRunRepository RunbookRuns { get; } + public IReleaseRepository Releases { get; } + public IProxyRepository Proxies { get; } + public IServerStatusRepository ServerStatus { get; } + public ISpaceRepository Spaces { get; } + + public Task LoadRootDocument() + { + throw new System.NotImplementedException(); + } + + public ISchedulerRepository Schedulers { get; } + public ISubscriptionRepository Subscriptions { get; } + public ITaskRepository Tasks { get; } + public ITeamsRepository Teams { get; } + public IScopedUserRoleRepository ScopedUserRoles { get; } + public IUserPermissionsRepository UserPermissions { get; } + public ITagSetRepository TagSets { get; } + public ITenantRepository Tenants { get; } + public ITenantVariablesRepository TenantVariables { get; } + public IUserRepository Users { get; } + public IUserRolesRepository UserRoles { get; } + public IUpgradeConfigurationRepository UpgradeConfiguration { get; } + public IVariableSetRepository VariableSets { get; } + public IWorkerPoolRepository WorkerPools { get; } + public IWorkerRepository Workers { get; } + public IChannelRepository Channels { get; } + public IProjectTriggerRepository ProjectTriggers { get; } + public Task LoadSpaceRootDocument() + { + throw new System.NotImplementedException(); + } + + public IAccountRepository Accounts { get; } + public IRetentionPolicyRepository RetentionPolicies { get; } + public IDefectsRepository Defects { get; } + public IOctopusServerNodeRepository OctopusServerNodes { get; } + } +} diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeProjectGroupRepository.cs b/OctopusProjectBuilder.Uploader/Helpers/FakeProjectGroupRepository.cs similarity index 94% rename from OctopusProjectBuilder.Uploader.Tests/Helpers/FakeProjectGroupRepository.cs rename to OctopusProjectBuilder.Uploader/Helpers/FakeProjectGroupRepository.cs index 3fe2fe3..5dfe8c1 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeProjectGroupRepository.cs +++ b/OctopusProjectBuilder.Uploader/Helpers/FakeProjectGroupRepository.cs @@ -5,7 +5,7 @@ using Octopus.Client.Model; using Octopus.Client.Repositories.Async; -namespace OctopusProjectBuilder.Uploader.Tests.Helpers +namespace OctopusProjectBuilder.Uploader { internal class FakeProjectGroupRepository : FakeNamedRepository, IProjectGroupRepository { diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeProjectRepository.cs b/OctopusProjectBuilder.Uploader/Helpers/FakeProjectRepository.cs similarity index 96% rename from OctopusProjectBuilder.Uploader.Tests/Helpers/FakeProjectRepository.cs rename to OctopusProjectBuilder.Uploader/Helpers/FakeProjectRepository.cs index 1133d9e..df43186 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeProjectRepository.cs +++ b/OctopusProjectBuilder.Uploader/Helpers/FakeProjectRepository.cs @@ -1,134 +1,134 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Octopus.Client.Editors.Async; -using Octopus.Client.Extensibility; -using Octopus.Client.Model; -using Octopus.Client.Repositories.Async; - -namespace OctopusProjectBuilder.Uploader.Tests.Helpers -{ - internal class FakeProjectRepository : FakeNamedRepository, IProjectRepository - { - private readonly FakeVariableSetRepository _variableSetRepository; - private readonly FakeDeploymentProcessRepository _deploymentProcessRepository; - private readonly FakeProjectTriggersRepository _projectTriggersRepository; - - public FakeProjectRepository(FakeVariableSetRepository variableSetRepository, FakeDeploymentProcessRepository deploymentProcessRepository, FakeProjectTriggersRepository projectTriggersRepository) - { - _variableSetRepository = variableSetRepository; - _deploymentProcessRepository = deploymentProcessRepository; - _projectTriggersRepository = projectTriggersRepository; - } - - protected override async Task OnCreate(ProjectResource resource) - { - resource.VariableSetId = (await _variableSetRepository.Create(new VariableSetResource())).Id; - resource.DeploymentProcessId = (await _deploymentProcessRepository.Create(new DeploymentProcessResource())).Id; - } - - public Task> GetAll() - { - throw new NotImplementedException(); - } - - public Task> GetReleases(ProjectResource project, int skip = 0) - { - throw new NotImplementedException(); - } - - public IProjectBetaRepository Beta() - { - throw new NotImplementedException(); - } - - public Task> GetReleases(ProjectResource project, int skip = 0, int? take = null, string searchByVersion = null) - { - throw new NotImplementedException(); - } - - public Task> GetAllReleases(ProjectResource project) - { - throw new NotImplementedException(); - } - - public Task GetReleaseByVersion(ProjectResource project, string version) - { - throw new NotImplementedException(); - } - - public Task> GetChannels(ProjectResource project) - { - throw new NotImplementedException(); - } - - public Task> GetAllChannels(ProjectResource project) - { - throw new NotImplementedException(); - } - - public Task GetProgression(ProjectResource project) - { - throw new NotImplementedException(); - } - - public Task> GetTriggers(ProjectResource project) - { - var projectTriggers = _projectTriggersRepository.FindAll().GetAwaiter().GetResult().Where(pt => pt.ProjectId == project.Id); - return Task.FromResult(new ResourceCollection(projectTriggers, new LinkCollection())); - } - - public Task> GetAllTriggers(ProjectResource project) - { - throw new NotImplementedException(); - } - - public Task SetLogo(ProjectResource project, string fileName, Stream contents) - { - throw new NotImplementedException(); - } - - public Task CreateOrModify(string name, ProjectGroupResource projectGroup, LifecycleResource lifecycle) - { - throw new NotImplementedException(); - } - - public Task CreateOrModify(string name, ProjectGroupResource projectGroup, LifecycleResource lifecycle, string description, - string cloneId = null) - { - throw new NotImplementedException(); - } - - public Task> GetRunbookSnapshots(ProjectResource project, int skip = 0, int? take = null, string searchByName = null) - { - throw new NotImplementedException(); - } - - public Task> GetAllRunbookSnapshots(ProjectResource project) - { - throw new NotImplementedException(); - } - - public Task GetRunbookSnapshotByName(ProjectResource project, string name) - { - throw new NotImplementedException(); - } - - public Task> GetRunbooks(ProjectResource project, int skip = 0, int? take = null, string searchByName = null) - { - throw new NotImplementedException(); - } - - public Task> GetAllRunbooks(ProjectResource project) - { - throw new NotImplementedException(); - } - - public Task CreateOrModify(string name, ProjectGroupResource projectGroup, LifecycleResource lifecycle, string description) - { - throw new NotImplementedException(); - } - } +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Octopus.Client.Editors.Async; +using Octopus.Client.Extensibility; +using Octopus.Client.Model; +using Octopus.Client.Repositories.Async; + +namespace OctopusProjectBuilder.Uploader +{ + internal class FakeProjectRepository : FakeNamedRepository, IProjectRepository + { + private readonly FakeVariableSetRepository _variableSetRepository; + private readonly FakeDeploymentProcessRepository _deploymentProcessRepository; + private readonly FakeProjectTriggersRepository _projectTriggersRepository; + + public FakeProjectRepository(FakeVariableSetRepository variableSetRepository, FakeDeploymentProcessRepository deploymentProcessRepository, FakeProjectTriggersRepository projectTriggersRepository) + { + _variableSetRepository = variableSetRepository; + _deploymentProcessRepository = deploymentProcessRepository; + _projectTriggersRepository = projectTriggersRepository; + } + + protected override async Task OnCreate(ProjectResource resource) + { + resource.VariableSetId = (await _variableSetRepository.Create(new VariableSetResource())).Id; + resource.DeploymentProcessId = (await _deploymentProcessRepository.Create(new DeploymentProcessResource())).Id; + } + + public Task> GetAll() + { + throw new NotImplementedException(); + } + + public Task> GetReleases(ProjectResource project, int skip = 0) + { + throw new NotImplementedException(); + } + + public IProjectBetaRepository Beta() + { + throw new NotImplementedException(); + } + + public Task> GetReleases(ProjectResource project, int skip = 0, int? take = null, string searchByVersion = null) + { + throw new NotImplementedException(); + } + + public Task> GetAllReleases(ProjectResource project) + { + throw new NotImplementedException(); + } + + public Task GetReleaseByVersion(ProjectResource project, string version) + { + throw new NotImplementedException(); + } + + public Task> GetChannels(ProjectResource project) + { + throw new NotImplementedException(); + } + + public Task> GetAllChannels(ProjectResource project) + { + throw new NotImplementedException(); + } + + public Task GetProgression(ProjectResource project) + { + throw new NotImplementedException(); + } + + public Task> GetTriggers(ProjectResource project) + { + var projectTriggers = _projectTriggersRepository.FindAll().GetAwaiter().GetResult().Where(pt => pt.ProjectId == project.Id); + return Task.FromResult(new ResourceCollection(projectTriggers, new LinkCollection())); + } + + public Task> GetAllTriggers(ProjectResource project) + { + throw new NotImplementedException(); + } + + public Task SetLogo(ProjectResource project, string fileName, Stream contents) + { + throw new NotImplementedException(); + } + + public Task CreateOrModify(string name, ProjectGroupResource projectGroup, LifecycleResource lifecycle) + { + throw new NotImplementedException(); + } + + public Task CreateOrModify(string name, ProjectGroupResource projectGroup, LifecycleResource lifecycle, string description, + string cloneId = null) + { + throw new NotImplementedException(); + } + + public Task> GetRunbookSnapshots(ProjectResource project, int skip = 0, int? take = null, string searchByName = null) + { + throw new NotImplementedException(); + } + + public Task> GetAllRunbookSnapshots(ProjectResource project) + { + throw new NotImplementedException(); + } + + public Task GetRunbookSnapshotByName(ProjectResource project, string name) + { + throw new NotImplementedException(); + } + + public Task> GetRunbooks(ProjectResource project, int skip = 0, int? take = null, string searchByName = null) + { + throw new NotImplementedException(); + } + + public Task> GetAllRunbooks(ProjectResource project) + { + throw new NotImplementedException(); + } + + public Task CreateOrModify(string name, ProjectGroupResource projectGroup, LifecycleResource lifecycle, string description) + { + throw new NotImplementedException(); + } + } } \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeProjectTriggersRepository.cs b/OctopusProjectBuilder.Uploader/Helpers/FakeProjectTriggersRepository.cs similarity index 93% rename from OctopusProjectBuilder.Uploader.Tests/Helpers/FakeProjectTriggersRepository.cs rename to OctopusProjectBuilder.Uploader/Helpers/FakeProjectTriggersRepository.cs index f935716..77ddcf5 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeProjectTriggersRepository.cs +++ b/OctopusProjectBuilder.Uploader/Helpers/FakeProjectTriggersRepository.cs @@ -1,34 +1,34 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Octopus.Client.Editors.Async; -using Octopus.Client.Model; -using Octopus.Client.Model.Triggers; -using Octopus.Client.Repositories.Async; - -namespace OctopusProjectBuilder.Uploader.Tests.Helpers -{ - internal class FakeProjectTriggersRepository : FakeRepository, IProjectTriggerRepository - { - public Task> FindAll() - { - return Task.FromResult(_items); - } - - public Task FindByName(ProjectResource project, string name) - { - var trigger = _items.FirstOrDefault(m => string.Equals(m.Name, name, System.StringComparison.OrdinalIgnoreCase)); - return Task.FromResult(trigger); - } - - public Task CreateOrModify(ProjectResource project, string name, TriggerFilterResource filter, TriggerActionResource action) - { - throw new System.NotImplementedException(); - } - - public Task> FindByRunbook(params string[] runbookIds) - { - throw new System.NotImplementedException(); - } - } +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Octopus.Client.Editors.Async; +using Octopus.Client.Model; +using Octopus.Client.Model.Triggers; +using Octopus.Client.Repositories.Async; + +namespace OctopusProjectBuilder.Uploader +{ + internal class FakeProjectTriggersRepository : FakeRepository, IProjectTriggerRepository + { + public Task> FindAll() + { + return Task.FromResult(_items); + } + + public Task FindByName(ProjectResource project, string name) + { + var trigger = _items.FirstOrDefault(m => string.Equals(m.Name, name, System.StringComparison.OrdinalIgnoreCase)); + return Task.FromResult(trigger); + } + + public Task CreateOrModify(ProjectResource project, string name, TriggerFilterResource filter, TriggerActionResource action) + { + throw new System.NotImplementedException(); + } + + public Task> FindByRunbook(params string[] runbookIds) + { + throw new System.NotImplementedException(); + } + } } \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeRepository.cs b/OctopusProjectBuilder.Uploader/Helpers/FakeRepository.cs similarity index 91% rename from OctopusProjectBuilder.Uploader.Tests/Helpers/FakeRepository.cs rename to OctopusProjectBuilder.Uploader/Helpers/FakeRepository.cs index b43d8c1..8d43396 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeRepository.cs +++ b/OctopusProjectBuilder.Uploader/Helpers/FakeRepository.cs @@ -1,75 +1,74 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Newtonsoft.Json; -using Octopus.Client.Model; -using Octopus.Client.Repositories.Async; -using OctopusProjectBuilder.Uploader.Tests.Serialization; - -namespace OctopusProjectBuilder.Uploader.Tests.Helpers -{ - public class FakeRepository : ICreate, IGet, IModify, IDelete where T : Resource - { - protected readonly List _items = new List(); - - public Task Create(T resource, object pathParameters = null) - { - resource.Id = Guid.NewGuid().ToString(); - OnCreate(resource); - _items.Add(Clone(resource)); - return Task.FromResult(resource); - } - - public Task Get(string idOrHref) - { - return Task.FromResult(Clone(_items.Single(t => t.Id == idOrHref))); - } - - public Task> Get(params string[] ids) - { - return Task.FromResult(_items.Where(t => ids.Contains(t.Id)).Select(Clone).ToList()); - } - - public Task Refresh(T resource) - { - throw new NotImplementedException(); - } - - public Task Modify(T resource) - { - var index = _items.FindIndex(t => t.Id == resource.Id); - if (index < 0) - throw new KeyNotFoundException(resource.Id); - OnModify(_items[index], resource); - resource.Id = _items[index].Id; - _items[index] = Clone(resource); - return Task.FromResult(resource); - } - - public Task Delete(T resource) - { - throw new NotImplementedException(); - } - - protected virtual Task OnCreate(T resource) - { - return Task.CompletedTask; - } - - protected virtual Task OnModify(T currentItem, T newItem) - { - return Task.CompletedTask; - } - - protected static T Clone(T resource) - { - if (ReferenceEquals(resource, null)) - { - return default(T); - } - - return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(resource), JsonSerialization.GetDefaultSerializerSettings()); - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Octopus.Client.Model; +using Octopus.Client.Repositories.Async; + +namespace OctopusProjectBuilder.Uploader +{ + public class FakeRepository : ICreate, IGet, IModify, IDelete where T : Resource + { + protected readonly List _items = new List(); + + public Task Create(T resource, object pathParameters = null) + { + resource.Id = Guid.NewGuid().ToString(); + OnCreate(resource); + _items.Add(Clone(resource)); + return Task.FromResult(resource); + } + + public Task Get(string idOrHref) + { + return Task.FromResult(Clone(_items.Single(t => t.Id == idOrHref))); + } + + public Task> Get(params string[] ids) + { + return Task.FromResult(_items.Where(t => ids.Contains(t.Id)).Select(Clone).ToList()); + } + + public Task Refresh(T resource) + { + throw new NotImplementedException(); + } + + public Task Modify(T resource) + { + var index = _items.FindIndex(t => t.Id == resource.Id); + if (index < 0) + throw new KeyNotFoundException(resource.Id); + OnModify(_items[index], resource); + resource.Id = _items[index].Id; + _items[index] = Clone(resource); + return Task.FromResult(resource); + } + + public Task Delete(T resource) + { + throw new NotImplementedException(); + } + + protected virtual Task OnCreate(T resource) + { + return Task.CompletedTask; + } + + protected virtual Task OnModify(T currentItem, T newItem) + { + return Task.CompletedTask; + } + + protected static T Clone(T resource) + { + if (ReferenceEquals(resource, null)) + { + return default(T); + } + + return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(resource), JsonSerialization.GetDefaultSerializerSettings()); + } + } } \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeTagSetsRepository.cs b/OctopusProjectBuilder.Uploader/Helpers/FakeTagSetsRepository.cs similarity index 93% rename from OctopusProjectBuilder.Uploader.Tests/Helpers/FakeTagSetsRepository.cs rename to OctopusProjectBuilder.Uploader/Helpers/FakeTagSetsRepository.cs index 2cc2f3d..86f6227 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeTagSetsRepository.cs +++ b/OctopusProjectBuilder.Uploader/Helpers/FakeTagSetsRepository.cs @@ -4,7 +4,7 @@ using Octopus.Client.Model; using Octopus.Client.Repositories.Async; -namespace OctopusProjectBuilder.Uploader.Tests.Helpers +namespace OctopusProjectBuilder.Uploader { public class FakeTagSetsRepository : FakeNamedRepository, ITagSetRepository { diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeTeamsRepository.cs b/OctopusProjectBuilder.Uploader/Helpers/FakeTeamsRepository.cs similarity index 91% rename from OctopusProjectBuilder.Uploader.Tests/Helpers/FakeTeamsRepository.cs rename to OctopusProjectBuilder.Uploader/Helpers/FakeTeamsRepository.cs index ba4bafc..4ea3c7e 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeTeamsRepository.cs +++ b/OctopusProjectBuilder.Uploader/Helpers/FakeTeamsRepository.cs @@ -4,7 +4,7 @@ using Octopus.Client.Model; using Octopus.Client.Repositories.Async; -namespace OctopusProjectBuilder.Uploader.Tests.Helpers +namespace OctopusProjectBuilder.Uploader { internal class FakeTeamsRepository : FakeNamedRepository, ITeamsRepository { diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeTenantsRepository.cs b/OctopusProjectBuilder.Uploader/Helpers/FakeTenantsRepository.cs similarity index 97% rename from OctopusProjectBuilder.Uploader.Tests/Helpers/FakeTenantsRepository.cs rename to OctopusProjectBuilder.Uploader/Helpers/FakeTenantsRepository.cs index b8c7596..e50917c 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeTenantsRepository.cs +++ b/OctopusProjectBuilder.Uploader/Helpers/FakeTenantsRepository.cs @@ -5,7 +5,7 @@ using Octopus.Client.Model; using Octopus.Client.Repositories.Async; -namespace OctopusProjectBuilder.Uploader.Tests.Helpers +namespace OctopusProjectBuilder.Uploader { internal class FakeTenantsRepository : FakeNamedRepository, ITenantRepository { diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeUserRoleRepository.cs b/OctopusProjectBuilder.Uploader/Helpers/FakeUserRoleRepository.cs similarity index 77% rename from OctopusProjectBuilder.Uploader.Tests/Helpers/FakeUserRoleRepository.cs rename to OctopusProjectBuilder.Uploader/Helpers/FakeUserRoleRepository.cs index 7b2ee90..e7cdae3 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeUserRoleRepository.cs +++ b/OctopusProjectBuilder.Uploader/Helpers/FakeUserRoleRepository.cs @@ -1,7 +1,7 @@ using Octopus.Client.Model; using Octopus.Client.Repositories.Async; -namespace OctopusProjectBuilder.Uploader.Tests.Helpers +namespace OctopusProjectBuilder.Uploader { internal class FakeUserRolesRepository : FakeNamedRepository, IUserRolesRepository { diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeUsersRepository.cs b/OctopusProjectBuilder.Uploader/Helpers/FakeUsersRepository.cs similarity index 98% rename from OctopusProjectBuilder.Uploader.Tests/Helpers/FakeUsersRepository.cs rename to OctopusProjectBuilder.Uploader/Helpers/FakeUsersRepository.cs index 687892b..2167777 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeUsersRepository.cs +++ b/OctopusProjectBuilder.Uploader/Helpers/FakeUsersRepository.cs @@ -5,7 +5,7 @@ using Octopus.Client.Model; using Octopus.Client.Repositories.Async; -namespace OctopusProjectBuilder.Uploader.Tests.Helpers +namespace OctopusProjectBuilder.Uploader { internal class FakeUsersRepository : FakeRepository, IUserRepository { diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeVariableSetRepository.cs b/OctopusProjectBuilder.Uploader/Helpers/FakeVariableSetRepository.cs similarity index 93% rename from OctopusProjectBuilder.Uploader.Tests/Helpers/FakeVariableSetRepository.cs rename to OctopusProjectBuilder.Uploader/Helpers/FakeVariableSetRepository.cs index 1f50b92..ee83d96 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/FakeVariableSetRepository.cs +++ b/OctopusProjectBuilder.Uploader/Helpers/FakeVariableSetRepository.cs @@ -3,7 +3,7 @@ using Octopus.Client.Model; using Octopus.Client.Repositories.Async; -namespace OctopusProjectBuilder.Uploader.Tests.Helpers +namespace OctopusProjectBuilder.Uploader { internal class FakeVariableSetRepository : FakeRepository, IVariableSetRepository { diff --git a/OctopusProjectBuilder.Uploader.Tests/Helpers/PropertyValueResourceJsonConverter.cs b/OctopusProjectBuilder.Uploader/Helpers/PropertyValueResourceJsonConverter.cs similarity index 95% rename from OctopusProjectBuilder.Uploader.Tests/Helpers/PropertyValueResourceJsonConverter.cs rename to OctopusProjectBuilder.Uploader/Helpers/PropertyValueResourceJsonConverter.cs index 788a0ff..ab1e0ad 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Helpers/PropertyValueResourceJsonConverter.cs +++ b/OctopusProjectBuilder.Uploader/Helpers/PropertyValueResourceJsonConverter.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json.Linq; using Octopus.Client.Model; -namespace OctopusProjectBuilder.Uploader.Tests.Helpers +namespace OctopusProjectBuilder.Uploader { internal class PropertyValueResourceJsonConverter : JsonConverter { diff --git a/OctopusProjectBuilder.Uploader.Tests/Serialization/AccountConverter.cs b/OctopusProjectBuilder.Uploader/Serialization/AccountConverter.cs similarity index 93% rename from OctopusProjectBuilder.Uploader.Tests/Serialization/AccountConverter.cs rename to OctopusProjectBuilder.Uploader/Serialization/AccountConverter.cs index d5af773..a9bf6ad 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Serialization/AccountConverter.cs +++ b/OctopusProjectBuilder.Uploader/Serialization/AccountConverter.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Octopus.Client.Model.Accounts; -namespace OctopusProjectBuilder.Uploader.Tests.Serialization +namespace OctopusProjectBuilder.Uploader { public class AccountConverter : InheritedClassConverter { diff --git a/OctopusProjectBuilder.Uploader.Tests/Serialization/ControlConverter.cs b/OctopusProjectBuilder.Uploader/Serialization/ControlConverter.cs similarity index 97% rename from OctopusProjectBuilder.Uploader.Tests/Serialization/ControlConverter.cs rename to OctopusProjectBuilder.Uploader/Serialization/ControlConverter.cs index 26c4271..6e9a49c 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Serialization/ControlConverter.cs +++ b/OctopusProjectBuilder.Uploader/Serialization/ControlConverter.cs @@ -6,7 +6,7 @@ using Octopus.Client.Extensions; using Octopus.Client.Model.Forms; -namespace OctopusProjectBuilder.Uploader.Tests.Serialization +namespace OctopusProjectBuilder.Uploader { /// /// Serializes s by including and reading a custom Type property. diff --git a/OctopusProjectBuilder.Uploader.Tests/Serialization/EndpointConverter.cs b/OctopusProjectBuilder.Uploader/Serialization/EndpointConverter.cs similarity index 95% rename from OctopusProjectBuilder.Uploader.Tests/Serialization/EndpointConverter.cs rename to OctopusProjectBuilder.Uploader/Serialization/EndpointConverter.cs index 9deeb9c..aaa968e 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Serialization/EndpointConverter.cs +++ b/OctopusProjectBuilder.Uploader/Serialization/EndpointConverter.cs @@ -3,7 +3,7 @@ using Octopus.Client.Model; using Octopus.Client.Model.Endpoints; -namespace OctopusProjectBuilder.Uploader.Tests.Serialization +namespace OctopusProjectBuilder.Uploader { /// /// Serializes s by including and the CommunicationStyle property. diff --git a/OctopusProjectBuilder.Uploader.Tests/Serialization/FeedConverter.cs b/OctopusProjectBuilder.Uploader/Serialization/FeedConverter.cs similarity index 92% rename from OctopusProjectBuilder.Uploader.Tests/Serialization/FeedConverter.cs rename to OctopusProjectBuilder.Uploader/Serialization/FeedConverter.cs index cfac299..468dda9 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Serialization/FeedConverter.cs +++ b/OctopusProjectBuilder.Uploader/Serialization/FeedConverter.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Octopus.Client.Model; -namespace OctopusProjectBuilder.Uploader.Tests.Serialization +namespace OctopusProjectBuilder.Uploader { public class FeedConverter : InheritedClassConverter { diff --git a/OctopusProjectBuilder.Uploader.Tests/Serialization/HrefConverter.cs b/OctopusProjectBuilder.Uploader/Serialization/HrefConverter.cs similarity index 97% rename from OctopusProjectBuilder.Uploader.Tests/Serialization/HrefConverter.cs rename to OctopusProjectBuilder.Uploader/Serialization/HrefConverter.cs index eb1be7c..f0fd097 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Serialization/HrefConverter.cs +++ b/OctopusProjectBuilder.Uploader/Serialization/HrefConverter.cs @@ -3,7 +3,7 @@ using Octopus.Client.Extensibility; using Octopus.Client.Model; -namespace OctopusProjectBuilder.Uploader.Tests.Serialization +namespace OctopusProjectBuilder.Uploader { public class HrefConverter : JsonConverter { diff --git a/OctopusProjectBuilder.Uploader.Tests/Serialization/InheritedClassConverter.cs b/OctopusProjectBuilder.Uploader/Serialization/InheritedClassConverter.cs similarity index 98% rename from OctopusProjectBuilder.Uploader.Tests/Serialization/InheritedClassConverter.cs rename to OctopusProjectBuilder.Uploader/Serialization/InheritedClassConverter.cs index 5312c1f..bbba9aa 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Serialization/InheritedClassConverter.cs +++ b/OctopusProjectBuilder.Uploader/Serialization/InheritedClassConverter.cs @@ -6,7 +6,7 @@ using Newtonsoft.Json.Linq; using Octopus.Client.Model; -namespace OctopusProjectBuilder.Uploader.Tests.Serialization +namespace OctopusProjectBuilder.Uploader { public abstract class InheritedClassConverter : JsonConverter where TBaseResource : Resource { diff --git a/OctopusProjectBuilder.Uploader.Tests/Serialization/JsonSerialization.cs b/OctopusProjectBuilder.Uploader/Serialization/JsonSerialization.cs similarity index 95% rename from OctopusProjectBuilder.Uploader.Tests/Serialization/JsonSerialization.cs rename to OctopusProjectBuilder.Uploader/Serialization/JsonSerialization.cs index f7ecbba..a860015 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Serialization/JsonSerialization.cs +++ b/OctopusProjectBuilder.Uploader/Serialization/JsonSerialization.cs @@ -1,7 +1,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; -namespace OctopusProjectBuilder.Uploader.Tests.Serialization +namespace OctopusProjectBuilder.Uploader { /// /// Support for reading and writing JSON, exposed for convenience of those using JSON.NET. diff --git a/OctopusProjectBuilder.Uploader.Tests/Serialization/PropertyValueResource.cs b/OctopusProjectBuilder.Uploader/Serialization/PropertyValueResource.cs similarity index 96% rename from OctopusProjectBuilder.Uploader.Tests/Serialization/PropertyValueResource.cs rename to OctopusProjectBuilder.Uploader/Serialization/PropertyValueResource.cs index c650823..010f58a 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Serialization/PropertyValueResource.cs +++ b/OctopusProjectBuilder.Uploader/Serialization/PropertyValueResource.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json.Linq; using Octopus.Client.Model; -namespace OctopusProjectBuilder.Uploader.Tests.Serialization +namespace OctopusProjectBuilder.Uploader { public class PropertyValueJsonConverter : JsonConverter { diff --git a/OctopusProjectBuilder.Uploader.Tests/Serialization/TriggerActionConverter.cs b/OctopusProjectBuilder.Uploader/Serialization/TriggerActionConverter.cs similarity index 91% rename from OctopusProjectBuilder.Uploader.Tests/Serialization/TriggerActionConverter.cs rename to OctopusProjectBuilder.Uploader/Serialization/TriggerActionConverter.cs index 0589242..1434e4d 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Serialization/TriggerActionConverter.cs +++ b/OctopusProjectBuilder.Uploader/Serialization/TriggerActionConverter.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Octopus.Client.Model.Triggers; -namespace OctopusProjectBuilder.Uploader.Tests.Serialization +namespace OctopusProjectBuilder.Uploader { public class TriggerActionConverter : InheritedClassConverter { diff --git a/OctopusProjectBuilder.Uploader.Tests/Serialization/TriggerFilterConverter.cs b/OctopusProjectBuilder.Uploader/Serialization/TriggerFilterConverter.cs similarity index 91% rename from OctopusProjectBuilder.Uploader.Tests/Serialization/TriggerFilterConverter.cs rename to OctopusProjectBuilder.Uploader/Serialization/TriggerFilterConverter.cs index 0af84e1..53d2256 100644 --- a/OctopusProjectBuilder.Uploader.Tests/Serialization/TriggerFilterConverter.cs +++ b/OctopusProjectBuilder.Uploader/Serialization/TriggerFilterConverter.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Octopus.Client.Model.Triggers; -namespace OctopusProjectBuilder.Uploader.Tests.Serialization +namespace OctopusProjectBuilder.Uploader { public class TriggerFilterConverter : InheritedClassConverter { From 99521427772ab504c5ac946cbb4dbc8249b0d90d Mon Sep 17 00:00:00 2001 From: Matthew Marchus Date: Wed, 21 Apr 2021 12:12:35 -0600 Subject: [PATCH 16/49] SKUNK-4: Add a flag for project names during model download (-projectName); specifying no project gets all projects --- OctopusProjectBuilder.Console/Options.cs | 1 + OctopusProjectBuilder.Console/Program.cs | 1 + OctopusProjectBuilder.Uploader/ModelDownloader.cs | 5 +++-- .../OctopusProjectBuilder.Uploader.csproj | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/OctopusProjectBuilder.Console/Options.cs b/OctopusProjectBuilder.Console/Options.cs index a4d94ed..fe00ba7 100644 --- a/OctopusProjectBuilder.Console/Options.cs +++ b/OctopusProjectBuilder.Console/Options.cs @@ -5,6 +5,7 @@ internal class Options 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 Verb Action { get; set; } } diff --git a/OctopusProjectBuilder.Console/Program.cs b/OctopusProjectBuilder.Console/Program.cs index ec01699..b28d710 100644 --- a/OctopusProjectBuilder.Console/Program.cs +++ b/OctopusProjectBuilder.Console/Program.cs @@ -81,6 +81,7 @@ public static Options ReadOptions(string[] args) parser.Setup(o => o.DefinitionsDir).As('d', "definitions").Required().WithDescription("Definitions directory"); 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.SetupHelp("?", "help").Callback(text => System.Console.WriteLine(text)); var result = parser.Parse(args); diff --git a/OctopusProjectBuilder.Uploader/ModelDownloader.cs b/OctopusProjectBuilder.Uploader/ModelDownloader.cs index fed91bf..49318af 100644 --- a/OctopusProjectBuilder.Uploader/ModelDownloader.cs +++ b/OctopusProjectBuilder.Uploader/ModelDownloader.cs @@ -19,14 +19,15 @@ public ModelDownloader(IOctopusAsyncRepository repository, ILoggerFactory logger _logger = loggerFactory.CreateLogger(); } - public async Task DownloadModel() + public async Task DownloadModel(string projectName = null) { return new SystemModel( await Task.WhenAll((await _repository.MachinePolicies.FindMany(x => false)).Select(ReadMachinePolicy)), await Task.WhenAll((await _repository.Lifecycles.FindMany(x => false)).Select(ReadLifecycle)), await Task.WhenAll((await _repository.ProjectGroups.FindAll()).Select(ReadProjectGroup)), await Task.WhenAll((await _repository.LibraryVariableSets.FindAll()).Select(ReadLibraryVariableSet)), - await Task.WhenAll((await _repository.Projects.FindMany(x => x.Name == "Tyler Content Manager")).Select(ReadProject)), + await Task.WhenAll((await _repository.Projects + .FindMany(x => projectName == null || x.Name == projectName)).Select(ReadProject)), await Task.WhenAll((await _repository.Environments.FindAll()).Select(ReadEnvironment)), await Task.WhenAll((await _repository.UserRoles.FindMany(x => false)).Select(ReadUserRole)), await Task.WhenAll((await _repository.Teams.FindMany(x => false)).Select(ReadTeam)), diff --git a/OctopusProjectBuilder.Uploader/OctopusProjectBuilder.Uploader.csproj b/OctopusProjectBuilder.Uploader/OctopusProjectBuilder.Uploader.csproj index 06f5822..e7d45bd 100644 --- a/OctopusProjectBuilder.Uploader/OctopusProjectBuilder.Uploader.csproj +++ b/OctopusProjectBuilder.Uploader/OctopusProjectBuilder.Uploader.csproj @@ -7,7 +7,7 @@ - + From e6f7c9a95037ca51170296ff2981f60b6df0c885 Mon Sep 17 00:00:00 2001 From: Matthew Marchus Date: Wed, 21 Apr 2021 12:15:47 -0600 Subject: [PATCH 17/49] SKUNK-4: Upgrade Octopus.Client --- .../OctopusProjectBuilder.Console.csproj | 2 +- OctopusProjectBuilder.Model/OctopusProjectBuilder.Model.csproj | 2 +- .../OctopusProjectBuilder.Uploader.Tests.csproj | 2 +- .../OctopusProjectBuilder.Uploader.csproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/OctopusProjectBuilder.Console/OctopusProjectBuilder.Console.csproj b/OctopusProjectBuilder.Console/OctopusProjectBuilder.Console.csproj index c523cc2..28399fb 100644 --- a/OctopusProjectBuilder.Console/OctopusProjectBuilder.Console.csproj +++ b/OctopusProjectBuilder.Console/OctopusProjectBuilder.Console.csproj @@ -22,7 +22,7 @@ - + diff --git a/OctopusProjectBuilder.Model/OctopusProjectBuilder.Model.csproj b/OctopusProjectBuilder.Model/OctopusProjectBuilder.Model.csproj index 79b8f89..1d32083 100644 --- a/OctopusProjectBuilder.Model/OctopusProjectBuilder.Model.csproj +++ b/OctopusProjectBuilder.Model/OctopusProjectBuilder.Model.csproj @@ -5,7 +5,7 @@ - + diff --git a/OctopusProjectBuilder.Uploader.Tests/OctopusProjectBuilder.Uploader.Tests.csproj b/OctopusProjectBuilder.Uploader.Tests/OctopusProjectBuilder.Uploader.Tests.csproj index 7c15518..fac2779 100644 --- a/OctopusProjectBuilder.Uploader.Tests/OctopusProjectBuilder.Uploader.Tests.csproj +++ b/OctopusProjectBuilder.Uploader.Tests/OctopusProjectBuilder.Uploader.Tests.csproj @@ -18,7 +18,7 @@ - + diff --git a/OctopusProjectBuilder.Uploader/OctopusProjectBuilder.Uploader.csproj b/OctopusProjectBuilder.Uploader/OctopusProjectBuilder.Uploader.csproj index e7d45bd..967bd82 100644 --- a/OctopusProjectBuilder.Uploader/OctopusProjectBuilder.Uploader.csproj +++ b/OctopusProjectBuilder.Uploader/OctopusProjectBuilder.Uploader.csproj @@ -7,7 +7,7 @@ - + From 442d6bd564007b984d806fb03ec85fc6fb365d36 Mon Sep 17 00:00:00 2001 From: Matthew Marchus Date: Wed, 21 Apr 2021 13:06:02 -0600 Subject: [PATCH 18/49] SKUNK-4: Actually pass program argument to DownloadModel --- OctopusProjectBuilder.Console/Program.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OctopusProjectBuilder.Console/Program.cs b/OctopusProjectBuilder.Console/Program.cs index b28d710..b9186a4 100644 --- a/OctopusProjectBuilder.Console/Program.cs +++ b/OctopusProjectBuilder.Console/Program.cs @@ -63,7 +63,8 @@ private static async Task UploadDefinitions(Options options) private static async Task DownloadDefinitions(Options options) { - var model = await new ModelDownloader(await BuildRepository(options), _loggerFactory).DownloadModel(); + var model = await new ModelDownloader(await BuildRepository(options), _loggerFactory) + .DownloadModel(options.ProjectName); new YamlSystemModelRepository(_loggerFactory).Save(model, options.DefinitionsDir); } From 08330c8da4df29ec641b12a1458e4fa6452722c5 Mon Sep 17 00:00:00 2001 From: Matthew Marchus Date: Thu, 22 Apr 2021 14:59:24 -0600 Subject: [PATCH 19/49] SKUNK-2: Add project channels in YAML --- OctopusProjectBuilder.Model/Channel.cs | 38 +++++++++++ .../ChannelVersionRule.cs | 19 ++++++ .../DeploymentActionPackage.cs | 16 +++++ OctopusProjectBuilder.Model/SystemModel.cs | 25 ++++---- .../SystemModelBuilder.cs | 8 +++ .../Converters/ChannelConverter.cs | 57 +++++++++++++++++ .../Converters/ChannelVersionRuleConverter.cs | 32 ++++++++++ .../DeploymentActionPackageConveter.cs | 21 +++++++ .../ModelDownloader.cs | 23 ++++++- .../ModelUploader.cs | 11 ++++ .../Model/YamlChannel.cs | 63 +++++++++++++++++++ .../Model/YamlChannelVersionRule.cs | 41 ++++++++++++ .../Model/YamlDeploymentActionPackage.cs | 35 +++++++++++ .../Model/YamlOctopusModel.cs | 10 ++- .../YamlSystemModelRepository.cs | 1 + 15 files changed, 385 insertions(+), 15 deletions(-) create mode 100644 OctopusProjectBuilder.Model/Channel.cs create mode 100644 OctopusProjectBuilder.Model/ChannelVersionRule.cs create mode 100644 OctopusProjectBuilder.Model/DeploymentActionPackage.cs create mode 100644 OctopusProjectBuilder.Uploader/Converters/ChannelConverter.cs create mode 100644 OctopusProjectBuilder.Uploader/Converters/ChannelVersionRuleConverter.cs create mode 100644 OctopusProjectBuilder.Uploader/Converters/DeploymentActionPackageConveter.cs create mode 100644 OctopusProjectBuilder.YamlReader/Model/YamlChannel.cs create mode 100644 OctopusProjectBuilder.YamlReader/Model/YamlChannelVersionRule.cs create mode 100644 OctopusProjectBuilder.YamlReader/Model/YamlDeploymentActionPackage.cs diff --git a/OctopusProjectBuilder.Model/Channel.cs b/OctopusProjectBuilder.Model/Channel.cs new file mode 100644 index 0000000..ee45ec0 --- /dev/null +++ b/OctopusProjectBuilder.Model/Channel.cs @@ -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 versionRules, IEnumerable 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 VersionRules { get; set; } + public IEnumerable TenantTags { get; } + + public override string ToString() + { + return Identifier.ToString(); + } + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.Model/ChannelVersionRule.cs b/OctopusProjectBuilder.Model/ChannelVersionRule.cs new file mode 100644 index 0000000..32812ef --- /dev/null +++ b/OctopusProjectBuilder.Model/ChannelVersionRule.cs @@ -0,0 +1,19 @@ +using System.Collections; +using System.Collections.Generic; + +namespace OctopusProjectBuilder.Model +{ + public class ChannelVersionRule + { + public ChannelVersionRule(string tag, string versionRange, IEnumerable actionPackages) + { + Tag = tag; + VersionRange = versionRange; + ActionPackages = actionPackages; + } + + public string Tag { get; set; } + public string VersionRange { get; set; } + public IEnumerable ActionPackages { get; set; } + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.Model/DeploymentActionPackage.cs b/OctopusProjectBuilder.Model/DeploymentActionPackage.cs new file mode 100644 index 0000000..c459c1a --- /dev/null +++ b/OctopusProjectBuilder.Model/DeploymentActionPackage.cs @@ -0,0 +1,16 @@ +using Octopus.Client.Model; + +namespace OctopusProjectBuilder.Model +{ + public class DeploymentActionPackage + { + public string DeploymentAction { get; set; } + public string PackageReference { get; set; } + + public DeploymentActionPackage(string deploymentAction, string packageReference) + { + DeploymentAction = deploymentAction; + PackageReference = packageReference; + } + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.Model/SystemModel.cs b/OctopusProjectBuilder.Model/SystemModel.cs index 114e3bb..368e694 100644 --- a/OctopusProjectBuilder.Model/SystemModel.cs +++ b/OctopusProjectBuilder.Model/SystemModel.cs @@ -11,12 +11,13 @@ public class SystemModel public IEnumerable Lifecycles { get; } public IEnumerable ProjectGroups { get; } public IEnumerable Projects { get; } + public IEnumerable Channels { get; } public IEnumerable UserRoles { get; } public IEnumerable Teams { get; } public IEnumerable Tenants { get; } public IEnumerable TagSets { get; } - public SystemModel(IEnumerable machinePolicies, IEnumerable lifecycles, IEnumerable projectGroups, IEnumerable libraryVariableSets, IEnumerable projects, IEnumerable environments, IEnumerable userRoles, IEnumerable teams, IEnumerable tenants, IEnumerable tagSets) + public SystemModel(IEnumerable machinePolicies, IEnumerable lifecycles, IEnumerable projectGroups, IEnumerable libraryVariableSets, IEnumerable projects, IEnumerable channels, IEnumerable environments, IEnumerable userRoles, IEnumerable teams, IEnumerable tenants, IEnumerable tagSets) { MachinePolicies = machinePolicies.OrderBy(s => s.Identifier.Name).ToArray(); Environments = environments.OrderBy(s => s.Identifier.Name).ToArray(); @@ -24,6 +25,7 @@ public SystemModel(IEnumerable machinePolicies, IEnumerable s.Identifier.Name).ToArray(); ProjectGroups = projectGroups.OrderBy(s => s.Identifier.Name).ToArray(); Projects = projects.OrderBy(s => s.Identifier.Name).ToArray(); + Channels = channels.OrderBy(s => s.Identifier.Name).ToArray(); UserRoles = userRoles.OrderBy(s => s.Identifier.Name).ToArray(); Teams = teams.OrderBy(s => s.Identifier.Name).ToArray(); Tenants = tenants.OrderBy(s => s.Identifier.Name).ToArray(); @@ -32,16 +34,17 @@ public SystemModel(IEnumerable machinePolicies, IEnumerable SplitModel() { - return MachinePolicies.Select(t => new SystemModel(Enumerable.Repeat(t, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty())) - .Concat(Environments.Select(e => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(e, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) - .Concat(ProjectGroups.Select(grp => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(grp, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) - .Concat(Projects.Select(prj => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(prj, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) - .Concat(Lifecycles.Select(lf => new SystemModel(Enumerable.Empty(), Enumerable.Repeat(lf, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) - .Concat(LibraryVariableSets.Select(lvs => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(lvs, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) - .Concat(UserRoles.Select(ur => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(ur, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) - .Concat(Teams.Select(t => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(t, 1), Enumerable.Empty(), Enumerable.Empty()))) - .Concat(Tenants.Select(t => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(t, 1), Enumerable.Empty()))) - .Concat(TagSets.Select(t => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(t, 1)))); + return MachinePolicies.Select(t => new SystemModel(Enumerable.Repeat(t, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty())) + .Concat(Environments.Select(e => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(e, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) + .Concat(ProjectGroups.Select(grp => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(grp, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) + .Concat(Projects.Select(prj => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(prj, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) + .Concat(Channels.Select(ch => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(ch, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) + .Concat(Lifecycles.Select(lf => new SystemModel(Enumerable.Empty(), Enumerable.Repeat(lf, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) + .Concat(LibraryVariableSets.Select(lvs => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(lvs, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) + .Concat(UserRoles.Select(ur => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(ur, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) + .Concat(Teams.Select(t => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(t, 1), Enumerable.Empty(), Enumerable.Empty()))) + .Concat(Tenants.Select(t => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(t, 1), Enumerable.Empty()))) + .Concat(TagSets.Select(t => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(t, 1)))); } } } diff --git a/OctopusProjectBuilder.Model/SystemModelBuilder.cs b/OctopusProjectBuilder.Model/SystemModelBuilder.cs index 2b1d2e2..d4bc74a 100644 --- a/OctopusProjectBuilder.Model/SystemModelBuilder.cs +++ b/OctopusProjectBuilder.Model/SystemModelBuilder.cs @@ -8,6 +8,7 @@ public class SystemModelBuilder private readonly List _environments = new List(); private readonly List _projectGroups = new List(); private readonly List _projects = new List(); + private readonly List _channels = new List(); private readonly List _lifecycles = new List(); private readonly List _libraryVariableSets = new List(); private readonly List _userRoles = new List(); @@ -33,6 +34,12 @@ public SystemModelBuilder AddProject(Project project) return this; } + public SystemModelBuilder AddChannel(Channel channel) + { + _channels.Add(channel); + return this; + } + public SystemModelBuilder AddLifecycle(Lifecycle lifecycle) { _lifecycles.Add(lifecycle); @@ -83,6 +90,7 @@ public SystemModel Build() _projectGroups, _libraryVariableSets, _projects, + _channels, _environments, _userRoles, _teams, diff --git a/OctopusProjectBuilder.Uploader/Converters/ChannelConverter.cs b/OctopusProjectBuilder.Uploader/Converters/ChannelConverter.cs new file mode 100644 index 0000000..2276227 --- /dev/null +++ b/OctopusProjectBuilder.Uploader/Converters/ChannelConverter.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Octopus.Client; +using Octopus.Client.Model; +using OctopusProjectBuilder.Model; +using TenantedDeploymentMode = Octopus.Client.Model.TenantedDeploymentMode; + +namespace OctopusProjectBuilder.Uploader.Converters +{ + public static class ChannelConverter + { + + public static async Task UpdateWith(this ChannelResource resource, Channel model, IOctopusAsyncRepository repository) + { + if (model.Identifier != null) + { + resource.Name = model.Identifier.Name; + } + + resource.IsDefault = model.IsDefault ?? false; + if (model.LifecycleName != null) + { + resource.LifecycleId = (await repository.Lifecycles.FindByName(model.LifecycleName)).Id; + } + else + { + resource.LifecycleId = String.Empty; + } + + resource.ProjectId = (await repository.Projects.FindByName(model.ProjectName)).Id; + + resource.Rules = (await Task.WhenAll(model.VersionRules.Select(rule => rule.ToResource()))).ToList(); + + resource.TenantTags = new ReferenceCollection(model.TenantTags.Select(x => x.Name)); + + return resource; + } + + public static async Task ToModel(this ChannelResource resource, IOctopusAsyncRepository repository) + { + var projectResource = await repository.Projects.Get(resource.ProjectId); + var lifecycleName = resource.LifecycleId != null ? + (await repository.Lifecycles.Get(resource.LifecycleId)).Name : null; + + return new Channel( + new ElementIdentifier(resource.Name), + resource.Description, + projectResource.Name, + resource.IsDefault, + lifecycleName, + await Task.WhenAll(resource.Rules.Select(rule => rule.ToModel(repository))), + resource.TenantTags.Select(x => new ElementReference(x)).ToArray()); + } + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader/Converters/ChannelVersionRuleConverter.cs b/OctopusProjectBuilder.Uploader/Converters/ChannelVersionRuleConverter.cs new file mode 100644 index 0000000..7a0719f --- /dev/null +++ b/OctopusProjectBuilder.Uploader/Converters/ChannelVersionRuleConverter.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Octopus.Client; +using Octopus.Client.Model; +using OctopusProjectBuilder.Model; +using TenantedDeploymentMode = Octopus.Client.Model.TenantedDeploymentMode; + +namespace OctopusProjectBuilder.Uploader.Converters +{ + public static class ChannelVersionRuleConverter + { + public static async Task ToResource(this ChannelVersionRule model) + { + return new ChannelVersionRuleResource() + { + Tag = model.Tag ?? "", + VersionRange = model.VersionRange ?? "", + ActionPackages = await Task.WhenAll(model.ActionPackages.Select(package => package.ToResource())) + }; + } + + public static async Task ToModel(this ChannelVersionRuleResource resource, IOctopusAsyncRepository repository) + { + return new ChannelVersionRule( + resource.Tag, + resource.VersionRange, + await Task.WhenAll(resource.ActionPackages.Select(package => package.ToModel(repository)))); + } + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionPackageConveter.cs b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionPackageConveter.cs new file mode 100644 index 0000000..d39e71d --- /dev/null +++ b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionPackageConveter.cs @@ -0,0 +1,21 @@ +using System.Linq; +using System.Threading.Tasks; +using Octopus.Client; +using Octopus.Client.Model; +using OctopusProjectBuilder.Model; + +namespace OctopusProjectBuilder.Uploader.Converters +{ + public static class DeploymentActionPackageConveter + { + public static async Task ToModel(this DeploymentActionPackageResource resource, IOctopusAsyncRepository repository) + { + return new DeploymentActionPackage(resource.DeploymentAction, resource.PackageReference); + } + + public static async Task ToResource(this DeploymentActionPackage model) + { + return new DeploymentActionPackageResource(model.DeploymentAction ?? "", model.PackageReference ?? ""); + } + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader/ModelDownloader.cs b/OctopusProjectBuilder.Uploader/ModelDownloader.cs index 49318af..9b02b2e 100644 --- a/OctopusProjectBuilder.Uploader/ModelDownloader.cs +++ b/OctopusProjectBuilder.Uploader/ModelDownloader.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Octopus.Client; @@ -21,13 +22,23 @@ public ModelDownloader(IOctopusAsyncRepository repository, ILoggerFactory logger public async Task DownloadModel(string projectName = null) { + List projects; + projects = projectName != null ? + Enumerable.Repeat(await _repository.Projects.FindByName(projectName), 1).ToList() : + (await _repository.Projects.FindAll()).ToList(); + + List channels; + channels = projectName != null ? + (await _repository.Channels.FindMany(c => projects.Any(p => p.Id == c.ProjectId))).ToList() : + (await _repository.Channels.FindAll()).ToList(); + return new SystemModel( await Task.WhenAll((await _repository.MachinePolicies.FindMany(x => false)).Select(ReadMachinePolicy)), await Task.WhenAll((await _repository.Lifecycles.FindMany(x => false)).Select(ReadLifecycle)), await Task.WhenAll((await _repository.ProjectGroups.FindAll()).Select(ReadProjectGroup)), await Task.WhenAll((await _repository.LibraryVariableSets.FindAll()).Select(ReadLibraryVariableSet)), - await Task.WhenAll((await _repository.Projects - .FindMany(x => projectName == null || x.Name == projectName)).Select(ReadProject)), + await Task.WhenAll(projects.Select(ReadProject)), + await Task.WhenAll(channels.Select(ReadChannel)), await Task.WhenAll((await _repository.Environments.FindAll()).Select(ReadEnvironment)), await Task.WhenAll((await _repository.UserRoles.FindMany(x => false)).Select(ReadUserRole)), await Task.WhenAll((await _repository.Teams.FindMany(x => false)).Select(ReadTeam)), @@ -58,6 +69,12 @@ private async Task ReadProject(ProjectResource resource) _logger.LogInformation($"Downloading {nameof(ProjectResource)}: {resource.Name}"); return await resource.ToModel(_repository); } + + private async Task ReadChannel(ChannelResource resource) + { + _logger.LogInformation($"Downloading {nameof(ChannelResource)}: {resource.Name}"); + return await resource.ToModel(_repository); + } private async Task ReadProjectGroup(ProjectGroupResource resource) { diff --git a/OctopusProjectBuilder.Uploader/ModelUploader.cs b/OctopusProjectBuilder.Uploader/ModelUploader.cs index b51c224..56eb2aa 100644 --- a/OctopusProjectBuilder.Uploader/ModelUploader.cs +++ b/OctopusProjectBuilder.Uploader/ModelUploader.cs @@ -45,6 +45,9 @@ public async Task UploadModel(SystemModel model) foreach (var project in model.Projects) await UploadProject(project); + + foreach (var channel in model.Channels) + await UploadChannel(channel); foreach (var tenant in model.Tenants) await UploadTenant(tenant); @@ -120,6 +123,14 @@ await Update( await UploadProjectTriggers(projectResource, project.Triggers); } } + + private async Task UploadChannel(Channel channel) + { + var projectResource = await LoadResource(_repository.Projects, new ElementIdentifier(channel.ProjectName)); + var resource = await LoadResource(name => _repository.Channels.FindByName(projectResource, name), channel.Identifier); + await resource.UpdateWith(channel, _repository); + await Upsert(_repository.Channels, resource); + } private async Task UploadProjectTriggers(ProjectResource projectResource, IEnumerable triggers) { diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlChannel.cs b/OctopusProjectBuilder.YamlReader/Model/YamlChannel.cs new file mode 100644 index 0000000..97eb08e --- /dev/null +++ b/OctopusProjectBuilder.YamlReader/Model/YamlChannel.cs @@ -0,0 +1,63 @@ +using System; +using System.ComponentModel; +using System.Linq; +using Octopus.Client.Model; +using OctopusProjectBuilder.Model; +using OctopusProjectBuilder.YamlReader.Helpers; +using YamlDotNet.Serialization; + +namespace OctopusProjectBuilder.YamlReader.Model +{ + [Description("Octopus Channel model.")] + [Serializable] + public class YamlChannel : YamlNamedElement + { + [Description("Description.")] + [YamlMember(Order = 3)] + public string Description { get; set; } + + [Description("Project name.")] + [YamlMember(Order = 4)] + public string ProjectName { get; set; } + + [Description("Channel default flag.")] + [YamlMember(Order = 5)] + public bool? IsDefault { get; set; } = false; + + [Description("Channel lifecycle name.")] + [YamlMember(Order = 6)] + public string LifecycleName { get; set; } + + [Description("Channel version rules.")] + [YamlMember(Order = 7)] + public YamlChannelVersionRule[] VersionRules { get; set; } + + [Description("List of TenantTag references")] + [YamlMember(Order = 8)] + public string[] TenantTagRefs { get; set; } + + public Channel ToModel() + { + return new Channel(ToModelName(), + Description, ProjectName, IsDefault, LifecycleName, + VersionRules.Select(rule => rule.ToModel()), + TenantTagRefs.EnsureNotNull().Select(t => new ElementReference(t)).ToArray()); + } + + public static YamlChannel FromModel(Channel model) + { + return new YamlChannel + { + Name = model.Identifier.Name, + RenamedFrom = model.Identifier.RenamedFrom, + Description = string.IsNullOrEmpty(model.Description) ? null : model.Description, + ProjectName = model.ProjectName, + IsDefault = model.IsDefault, + LifecycleName = string.IsNullOrEmpty(model.LifecycleName) ? null : model.LifecycleName, + VersionRules = model.VersionRules.Select(rule => YamlChannelVersionRule.FromModel(rule)).ToArray(), + TenantTagRefs = model.TenantTags.Select(t => t.Name).ToArray().NullIfEmpty(), + }; + } + + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlChannelVersionRule.cs b/OctopusProjectBuilder.YamlReader/Model/YamlChannelVersionRule.cs new file mode 100644 index 0000000..344446c --- /dev/null +++ b/OctopusProjectBuilder.YamlReader/Model/YamlChannelVersionRule.cs @@ -0,0 +1,41 @@ +using System; +using System.ComponentModel; +using System.Linq; +using OctopusProjectBuilder.Model; +using YamlDotNet.Serialization; + +namespace OctopusProjectBuilder.YamlReader.Model +{ + [Description("Octopus Verison Rule model.")] + [Serializable] + public class YamlChannelVersionRule + { + [Description("Channel version range.")] + [YamlMember(Order = 1)] + public string VersionRange { get; set; } + + [Description("Release tag.")] + [YamlMember(Order = 2)] + public string Tag { get; set; } + + [Description("Action package names.")] + [YamlMember(Order = 3)] + public YamlDeploymentActionPackage[] ActionPackages { get; set; } + + public ChannelVersionRule ToModel() + { + return new ChannelVersionRule(Tag, VersionRange, ActionPackages.Select(package => package.ToModel()).ToList()); + } + + public static YamlChannelVersionRule FromModel(ChannelVersionRule model) + { + return new YamlChannelVersionRule + { + Tag = string.IsNullOrEmpty(model.Tag) ? null : model.Tag, + VersionRange = string.IsNullOrEmpty(model.VersionRange) ? null : model.VersionRange, + ActionPackages = model.ActionPackages.Select(package => YamlDeploymentActionPackage.FromModel(package)).ToArray() + }; + } + + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentActionPackage.cs b/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentActionPackage.cs new file mode 100644 index 0000000..0814956 --- /dev/null +++ b/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentActionPackage.cs @@ -0,0 +1,35 @@ +using System; +using System.ComponentModel; +using OctopusProjectBuilder.Model; +using YamlDotNet.Serialization; + +namespace OctopusProjectBuilder.YamlReader.Model +{ + [Description("Octopus Channel model.")] + [Serializable] + public class YamlDeploymentActionPackage + { + [Description("Deployment action name.")] + [YamlMember(Order = 1)] + public string DeploymentAction { get; set; } + + [Description("Package reference.")] + [YamlMember(Order = 2)] + public string PackageReference { get; set; } + + public static YamlDeploymentActionPackage FromModel(DeploymentActionPackage model) + { + return new YamlDeploymentActionPackage() + { + DeploymentAction = string.IsNullOrEmpty(model.DeploymentAction) ? null : model.DeploymentAction, + PackageReference = string.IsNullOrEmpty(model.PackageReference) ? null : model.PackageReference + }; + } + + public DeploymentActionPackage ToModel() + { + return new DeploymentActionPackage(DeploymentAction, PackageReference); + } + + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlOctopusModel.cs b/OctopusProjectBuilder.YamlReader/Model/YamlOctopusModel.cs index f908530..6bfa293 100644 --- a/OctopusProjectBuilder.YamlReader/Model/YamlOctopusModel.cs +++ b/OctopusProjectBuilder.YamlReader/Model/YamlOctopusModel.cs @@ -13,12 +13,13 @@ public class YamlOctopusModel { public YamlOctopusModel() { } - private YamlOctopusModel(YamlMachinePolicy[] machinePolicies, YamlEnvironment[] environments, YamlProjectGroup[] projectGroups, YamlProject[] projects, YamlLifecycle[] lifecycles, YamlLibraryVariableSet[] libraryVariableSets, YamlUserRole[] userRoles, YamlTeam[] teams, YamlTenant[] tenants, YamlTagSet[] tagsets) + private YamlOctopusModel(YamlMachinePolicy[] machinePolicies, YamlEnvironment[] environments, YamlProjectGroup[] projectGroups, YamlProject[] projects, YamlChannel[] channels, YamlLifecycle[] lifecycles, YamlLibraryVariableSet[] libraryVariableSets, YamlUserRole[] userRoles, YamlTeam[] teams, YamlTenant[] tenants, YamlTagSet[] tagsets) { MachinePolicies = machinePolicies; Environments = environments; ProjectGroups = projectGroups; Projects = projects; + Channels = channels; Lifecycles = lifecycles; LibraryVariableSets = libraryVariableSets; UserRoles = userRoles; @@ -35,6 +36,8 @@ private YamlOctopusModel(YamlMachinePolicy[] machinePolicies, YamlEnvironment[] public YamlProjectGroup[] ProjectGroups { get; set; } [Description("List of Projects.")] public YamlProject[] Projects { get; set; } + [Description("List of Channels.")] + public YamlChannel[] Channels { get; set; } [Description("List of Lifecycles.")] public YamlLifecycle[] Lifecycles { get; set; } [Description("List of Library Variable Sets (including Script modules).")] @@ -69,6 +72,9 @@ public SystemModelBuilder BuildWith(SystemModelBuilder builder) foreach (var project in Projects.EnsureNotNull()) builder.AddProject(project.ToModel()); + foreach (var channel in Channels.EnsureNotNull()) + builder.AddChannel(channel.ToModel()); + foreach (var lifecycle in Lifecycles.EnsureNotNull()) builder.AddLifecycle(lifecycle.ToModel()); @@ -100,6 +106,7 @@ public static YamlOctopusModel FromModel(SystemModel model) model.Environments.Select(YamlEnvironment.FromModel).ToArray().NullIfEmpty(), model.ProjectGroups.Select(YamlProjectGroup.FromModel).ToArray().NullIfEmpty(), model.Projects.Select(YamlProject.FromModel).ToArray().NullIfEmpty(), + model.Channels.Select(YamlChannel.FromModel).ToArray().NullIfEmpty(), model.Lifecycles.Select(YamlLifecycle.FromModel).ToArray().NullIfEmpty(), model.LibraryVariableSets.Select(YamlLibraryVariableSet.FromModel).ToArray().NullIfEmpty(), model.UserRoles.Select(YamlUserRole.FromModel).ToArray().NullIfEmpty(), @@ -116,6 +123,7 @@ public YamlOctopusModel MergeIn(YamlOctopusModel model) LibraryVariableSets = this.MergeItemsIn(model, x => x.LibraryVariableSets); Lifecycles = this.MergeItemsIn(model, x => x.Lifecycles); Projects = this.MergeItemsIn(model, x => x.Projects); + Channels = this.MergeItemsIn(model, x => x.Channels); UserRoles = this.MergeItemsIn(model, x => x.UserRoles); Teams = this.MergeItemsIn(model, x => x.Teams); Tenants = this.MergeItemsIn(model, x => x.Tenants); diff --git a/OctopusProjectBuilder.YamlReader/YamlSystemModelRepository.cs b/OctopusProjectBuilder.YamlReader/YamlSystemModelRepository.cs index 4451507..4fb47cf 100644 --- a/OctopusProjectBuilder.YamlReader/YamlSystemModelRepository.cs +++ b/OctopusProjectBuilder.YamlReader/YamlSystemModelRepository.cs @@ -76,6 +76,7 @@ private string GetModelPath(YamlOctopusModel splitModel, string modelDirectory) .Concat(splitModel.Environments.EnsureNotNull().Select(x => $"Environment_{x.Name.SanitiseNameIfNeeded()}.yml")) .Concat(splitModel.ProjectGroups.EnsureNotNull().Select(x => $"ProjectGroup_{x.Name.SanitiseNameIfNeeded()}.yml")) .Concat(splitModel.Projects.EnsureNotNull().Select(x => $"Project_{x.Name.SanitiseNameIfNeeded()}.yml")) + .Concat(splitModel.Channels.EnsureNotNull().Select(x => $"Channel_{x.ProjectName.SanitiseNameIfNeeded()}_{x.Name.SanitiseNameIfNeeded()}.yml")) .Concat(splitModel.Lifecycles.EnsureNotNull().Select(x => $"Lifecycle_{x.Name.SanitiseNameIfNeeded()}.yml")) .Concat(splitModel.LibraryVariableSets.EnsureNotNull().Select(x => $"LibraryVariableSet_{x.Name.SanitiseNameIfNeeded()}.yml")) .Concat(splitModel.UserRoles.EnsureNotNull().Select(x => $"UserRole_{x.Name.SanitiseNameIfNeeded()}.yml")) From 3f356cd1a227c7505ea38ddf019a56ee6c18d5b5 Mon Sep 17 00:00:00 2001 From: Matthew Marchus Date: Thu, 22 Apr 2021 15:16:13 -0600 Subject: [PATCH 20/49] NONE: Download lifecycles --- OctopusProjectBuilder.Uploader/ModelDownloader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OctopusProjectBuilder.Uploader/ModelDownloader.cs b/OctopusProjectBuilder.Uploader/ModelDownloader.cs index 9b02b2e..5fea32f 100644 --- a/OctopusProjectBuilder.Uploader/ModelDownloader.cs +++ b/OctopusProjectBuilder.Uploader/ModelDownloader.cs @@ -34,7 +34,7 @@ public async Task DownloadModel(string projectName = null) return new SystemModel( await Task.WhenAll((await _repository.MachinePolicies.FindMany(x => false)).Select(ReadMachinePolicy)), - await Task.WhenAll((await _repository.Lifecycles.FindMany(x => false)).Select(ReadLifecycle)), + await Task.WhenAll((await _repository.Lifecycles.FindAll()).Select(ReadLifecycle)), await Task.WhenAll((await _repository.ProjectGroups.FindAll()).Select(ReadProjectGroup)), await Task.WhenAll((await _repository.LibraryVariableSets.FindAll()).Select(ReadLibraryVariableSet)), await Task.WhenAll(projects.Select(ReadProject)), From 9b0a4dd04bae0aaf417b4920efce1f5501a82582 Mon Sep 17 00:00:00 2001 From: Matthew Marchus Date: Thu, 22 Apr 2021 15:21:46 -0600 Subject: [PATCH 21/49] NONE: Add Default Lifecycle to mock --- OctopusProjectBuilder.Console/Program.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/OctopusProjectBuilder.Console/Program.cs b/OctopusProjectBuilder.Console/Program.cs index b9186a4..64b9206 100644 --- a/OctopusProjectBuilder.Console/Program.cs +++ b/OctopusProjectBuilder.Console/Program.cs @@ -5,6 +5,7 @@ using Fclp; using Microsoft.Extensions.Logging; using Octopus.Client; +using Octopus.Client.Model; using OctopusProjectBuilder.Uploader; using OctopusProjectBuilder.YamlReader; @@ -47,7 +48,15 @@ static int Main(string[] args) private static async Task ValidateConfig(Options options) { var model = new YamlSystemModelRepository(_loggerFactory).Load(options.DefinitionsDir); - await new ModelUploader(new FakeOctopusRepository(), _loggerFactory).UploadModel(model); + var fakeRepository = new FakeOctopusRepository(); + await fakeRepository.Lifecycles.Create(new LifecycleResource() + { + Id = "default", + Name = "Default Lifecycle" + }); + + await new ModelUploader(fakeRepository, _loggerFactory).UploadModel(model); + } private static void CleanupConfig(Options options) From d0d4f15ab71c7aa37b95f59aba75b7342dc5bff0 Mon Sep 17 00:00:00 2001 From: Matthew Marchus Date: Thu, 22 Apr 2021 16:28:36 -0600 Subject: [PATCH 22/49] NONE: Fix doc generator --- OctopusProjectBuilder.DocGen/DocGenerator.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/OctopusProjectBuilder.DocGen/DocGenerator.cs b/OctopusProjectBuilder.DocGen/DocGenerator.cs index 218612a..90746a8 100644 --- a/OctopusProjectBuilder.DocGen/DocGenerator.cs +++ b/OctopusProjectBuilder.DocGen/DocGenerator.cs @@ -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) From 947b578706998d26ff928ce1aaf76905e9454519 Mon Sep 17 00:00:00 2001 From: "matthew.marchus" Date: Tue, 25 May 2021 12:23:45 -0600 Subject: [PATCH 23/49] SKUNK-5: Support YAML references to project names --- OctopusProjectBuilder.Model/PropertyValue.cs | 8 ++++++ .../Converters/DeploymentActionConverter.cs | 2 +- .../Converters/DeploymentStepConverter.cs | 2 +- .../Converters/PropertyValueConverter.cs | 26 +++++++++++++++++-- .../ReferenceCollectionConverter.cs | 2 +- .../Model/YamlPropertyValue.cs | 12 ++++++--- 6 files changed, 43 insertions(+), 9 deletions(-) diff --git a/OctopusProjectBuilder.Model/PropertyValue.cs b/OctopusProjectBuilder.Model/PropertyValue.cs index 637ba8c..ed7fbcf 100644 --- a/OctopusProjectBuilder.Model/PropertyValue.cs +++ b/OctopusProjectBuilder.Model/PropertyValue.cs @@ -3,6 +3,7 @@ namespace OctopusProjectBuilder.Model public class PropertyValue { public bool IsSensitive { get; } + public string ValueType { get; } public string Value { get; } public PropertyValue(bool isSensitive, string value) @@ -10,5 +11,12 @@ public PropertyValue(bool isSensitive, string value) IsSensitive = isSensitive; Value = value; } + + public PropertyValue(bool isSensitive, string value, string valueType) + { + IsSensitive = isSensitive; + Value = value; + ValueType = valueType; + } } } \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs index f0384ad..f2c14c4 100644 --- a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs @@ -21,7 +21,7 @@ public static async Task UpdateWith(this DeploymentAct { resource.Name = model.Name; resource.ActionType = model.ActionType; - resource.Properties.UpdateWith(model.Properties); + resource.Properties.UpdateWith(repository, model.Properties); resource.Environments.UpdateWith(await Task.WhenAll(model.EnvironmentRefs.Select(r => repository.Environments.ResolveResourceId(r)))); return resource; } diff --git a/OctopusProjectBuilder.Uploader/Converters/DeploymentStepConverter.cs b/OctopusProjectBuilder.Uploader/Converters/DeploymentStepConverter.cs index 8dc3430..94676f0 100644 --- a/OctopusProjectBuilder.Uploader/Converters/DeploymentStepConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/DeploymentStepConverter.cs @@ -25,7 +25,7 @@ public static async Task UpdateWith(this DeploymentStepR resource.Condition = (DeploymentStepCondition)model.Condition; resource.RequiresPackagesToBeAcquired = model.RequiresPackagesToBeAcquired; resource.StartTrigger = (DeploymentStepStartTrigger)model.StartTrigger; - resource.Properties.UpdateWith(model.Properties); + resource.Properties.UpdateWith(repository, model.Properties); resource.Actions.Clear(); foreach (var action in model.Actions.Select(a => new DeploymentActionResource().UpdateWith(a, repository))) resource.Actions.Add(await action); diff --git a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs index 76b5fd5..db2647d 100644 --- a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Octopus.Client; using Octopus.Client.Model; using OctopusProjectBuilder.Model; @@ -20,11 +21,32 @@ public static Dictionary ToModel(this IDictionary kv.Item1, kv => kv.Item2); } - public static void UpdateWith(this IDictionary resource, IReadOnlyDictionary model) + public static async void UpdateWith(this IDictionary resource, IOctopusAsyncRepository repository, + IReadOnlyDictionary model) { resource.Clear(); + foreach (var keyValuePair in model) - resource.Add(keyValuePair.Key, new PropertyValueResource(keyValuePair.Value.Value, keyValuePair.Value.IsSensitive)); + { + string value = keyValuePair.Value.Value; + + switch (keyValuePair.Value.ValueType) + { + case "Literal": + break; + case "ProjectNameToId": + value = (await repository.Projects.FindByName(value)).Id; + break; + case "EnvironmentNameToId": + value = (await repository.Environments.FindByName(value)).Id; + break; + default: + throw new ArgumentException("ValueType: " + keyValuePair.Value.ValueType); + } + + resource.Add(keyValuePair.Key, + new PropertyValueResource(value, keyValuePair.Value.IsSensitive)); + } } } } \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader/Converters/ReferenceCollectionConverter.cs b/OctopusProjectBuilder.Uploader/Converters/ReferenceCollectionConverter.cs index 7bded67..c04a4c3 100644 --- a/OctopusProjectBuilder.Uploader/Converters/ReferenceCollectionConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/ReferenceCollectionConverter.cs @@ -25,7 +25,7 @@ public static IEnumerable> ToModel(this Refere return new ElementReference(resource.Name); }); } - + public static void UpdateWith(this IDictionary resource, IReadOnlyDictionary> model) { resource.Clear(); diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlPropertyValue.cs b/OctopusProjectBuilder.YamlReader/Model/YamlPropertyValue.cs index a3df06a..4d9bec8 100644 --- a/OctopusProjectBuilder.YamlReader/Model/YamlPropertyValue.cs +++ b/OctopusProjectBuilder.YamlReader/Model/YamlPropertyValue.cs @@ -16,22 +16,26 @@ public class YamlPropertyValue [YamlMember(Order = 1)] public string Key { get; set; } - [Description("Property value.")] + [Description("Property value type.")] [YamlMember(Order = 2)] + public string ValueType { get; set; } = "Literal"; + + [Description("Property value.")] + [YamlMember(Order = 3)] public string Value { get; set; } [Description("Should Octopus store this property value in encrypted format? \\(Please note that at this moment the sensitive values have to be stored in plain text in yaml definition.\\)")] - [YamlMember(Order = 3)] + [YamlMember(Order = 4)] public bool IsSensitive { get; set; } public static IReadOnlyDictionary ToModel(YamlPropertyValue[] properties) { - return properties.EnsureNotNull().ToDictionary(kv => kv.Key, kv => new PropertyValue(kv.IsSensitive, kv.Value)); + return properties.EnsureNotNull().ToDictionary(kv => kv.Key, kv => new PropertyValue(kv.IsSensitive, kv.Value, kv.ValueType)); } public static YamlPropertyValue[] FromModel(IReadOnlyDictionary properties) { - return properties.EnsureNotNull().Select(kv => new YamlPropertyValue { IsSensitive = kv.Value.IsSensitive, Value = kv.Value.Value, Key = kv.Key }).ToArray().NullIfEmpty(); + return properties.EnsureNotNull().Select(kv => new YamlPropertyValue { IsSensitive = kv.Value.IsSensitive, Value = kv.Value.Value, ValueType = kv.Value.ValueType, Key = kv.Key }).ToArray().NullIfEmpty(); } } } \ No newline at end of file From 69f2ff9edd371abe08a3e14ab2c3948f004a8cc1 Mon Sep 17 00:00:00 2001 From: "matthew.marchus" Date: Wed, 23 Jun 2021 09:13:41 -0600 Subject: [PATCH 24/49] EAG-847: Support a wide variety of new "XNameToId" value conversion types (i.e. StepTemplateNameToId) --- .../Converters/PropertyValueConverter.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs index db2647d..991033f 100644 --- a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs @@ -40,6 +40,39 @@ public static async void UpdateWith(this IDictionary Date: Wed, 23 Jun 2021 09:26:04 -0600 Subject: [PATCH 25/49] EAG-847: Don't remove protected IDs from property values, but do set them if strongly typed --- .../Converters/PropertyValueConverter.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs index 991033f..dc80547 100644 --- a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs @@ -9,6 +9,14 @@ namespace OctopusProjectBuilder.Uploader.Converters { public static class PropertyValueConverter { + private static readonly List protectedIds = new List(); + + static PropertyValueConverter() + { + // DO NOT attempt to overwrite template version IDs if not specified. Leave this up to Octopus. + protectedIds.Add("Octopus.Action.Template.Version"); + } + public static PropertyValue ToModel(this PropertyValueResource resource) { return new PropertyValue(resource.IsSensitive,resource.Value); @@ -24,7 +32,10 @@ public static Dictionary ToModel(this IDictionary resource, IOctopusAsyncRepository repository, IReadOnlyDictionary model) { - resource.Clear(); + foreach (var s in resource.Where(kv => !protectedIds.Contains(kv.Key)).ToList()) + { + resource.Remove(s.Key); + } foreach (var keyValuePair in model) { From 48ab955ccd7cf615248e0f4ca440a6d5e6615f0b Mon Sep 17 00:00:00 2001 From: "matthew.marchus" Date: Wed, 23 Jun 2021 12:37:46 -0600 Subject: [PATCH 26/49] EAG-847: Add support for a wide variety of new keys across the YAML structure --- OctopusProjectBuilder.Console/Program.cs | 16 ++------ .../Converters/DeploymentActionConverter.cs | 23 ++++++++++- .../Converters/PropertyValueConverter.cs | 38 ++++++++++++++++++- .../Model/YamlPropertyValue.cs | 6 ++- 4 files changed, 68 insertions(+), 15 deletions(-) diff --git a/OctopusProjectBuilder.Console/Program.cs b/OctopusProjectBuilder.Console/Program.cs index 64b9206..0bb9eb2 100644 --- a/OctopusProjectBuilder.Console/Program.cs +++ b/OctopusProjectBuilder.Console/Program.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.Logging; using Octopus.Client; using Octopus.Client.Model; +using OctopusProjectBuilder.Model; using OctopusProjectBuilder.Uploader; using OctopusProjectBuilder.YamlReader; @@ -35,7 +36,7 @@ static int Main(string[] args) else if (options.Action == Options.Verb.CleanupConfig) CleanupConfig(options); else if (options.Action == Options.Verb.Validate) - ValidateConfig(options).GetAwaiter().GetResult(); + ValidateConfig(options); } catch (Exception e) { @@ -45,18 +46,9 @@ static int Main(string[] args) return 0; } - private static async Task ValidateConfig(Options options) + private static SystemModel ValidateConfig(Options options) { - var model = new YamlSystemModelRepository(_loggerFactory).Load(options.DefinitionsDir); - var fakeRepository = new FakeOctopusRepository(); - await fakeRepository.Lifecycles.Create(new LifecycleResource() - { - Id = "default", - Name = "Default Lifecycle" - }); - - await new ModelUploader(fakeRepository, _loggerFactory).UploadModel(model); - + return new YamlSystemModelRepository(_loggerFactory).Load(options.DefinitionsDir); } private static void CleanupConfig(Options options) diff --git a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs index f2c14c4..cd3ffdf 100644 --- a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs @@ -1,6 +1,8 @@ +using System.Data; using System.Linq; using System.Threading.Tasks; using Octopus.Client; +using Octopus.Client.Exceptions; using Octopus.Client.Model; using OctopusProjectBuilder.Model; @@ -21,8 +23,27 @@ public static async Task UpdateWith(this DeploymentAct { resource.Name = model.Name; resource.ActionType = model.ActionType; - resource.Properties.UpdateWith(repository, model.Properties); resource.Environments.UpdateWith(await Task.WhenAll(model.EnvironmentRefs.Select(r => repository.Environments.ResolveResourceId(r)))); + + await resource.Properties.UpdateWith(repository, model.Properties); + switch (resource.ActionType) + { + case "Octopus.TentaclePackage": + if (!resource.Properties.ContainsKey("Octopus.Action.Package.PackageId")) + { + throw new ConstraintException("No package ID specified for package action" + resource.Name); + } + + break; + case "Octopus.Script": + if (!resource.Properties.ContainsKey("Octopus.Action.Script.ScriptBody")) + { + throw new ConstraintException("No script body specified for script action in " + resource.Name); + } + + break; + } + return resource; } } diff --git a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs index dc80547..d8e2a5c 100644 --- a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Octopus.Client; using Octopus.Client.Model; using OctopusProjectBuilder.Model; @@ -29,7 +30,7 @@ public static Dictionary ToModel(this IDictionary kv.Item1, kv => kv.Item2); } - public static async void UpdateWith(this IDictionary resource, IOctopusAsyncRepository repository, + public static async Task UpdateWith(this IDictionary resource, IOctopusAsyncRepository repository, IReadOnlyDictionary model) { foreach (var s in resource.Where(kv => !protectedIds.Contains(kv.Key)).ToList()) @@ -91,6 +92,41 @@ public static async void UpdateWith(this IDictionary a.Key == "Octopus.Action.Template.Id") + .Select(a => a.Value) + .FirstOrDefault(); + + if (actionTemplateId != null && !string.IsNullOrEmpty(actionTemplateId.Value)) + { + ActionTemplateResource actionTemplate = await repository.ActionTemplates.Get(actionTemplateId.Value); + PropertyValueResource actionTemplateVersion = resource + .Where(a => a.Key == "Octopus.Action.Template.Version") + .Select(a => a.Value) + .FirstOrDefault(); + int versionNumber = actionTemplateVersion != null ? + int.Parse(actionTemplateVersion.Value) : actionTemplate.Version; + + if (versionNumber != actionTemplate.Version) + { + actionTemplate = await repository.ActionTemplates.GetVersion(actionTemplate, versionNumber); + } + + foreach (var keyValuePair in actionTemplate.Properties) + { + if (!resource.ContainsKey(keyValuePair.Key)) + { + resource.Add(keyValuePair); + } + } + + if (!resource.ContainsKey("Octopus.Action.Template.Version")) + { + resource.Add("Octopus.Action.Template.Version", + new PropertyValueResource(versionNumber.ToString())); + } + } } } } \ No newline at end of file diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlPropertyValue.cs b/OctopusProjectBuilder.YamlReader/Model/YamlPropertyValue.cs index 4d9bec8..2b5e94c 100644 --- a/OctopusProjectBuilder.YamlReader/Model/YamlPropertyValue.cs +++ b/OctopusProjectBuilder.YamlReader/Model/YamlPropertyValue.cs @@ -23,6 +23,10 @@ public class YamlPropertyValue [Description("Property value.")] [YamlMember(Order = 3)] public string Value { get; set; } + + [Description("Property value file content.")] + [YamlMember(Order = 4)] + public string File { get; set; } [Description("Should Octopus store this property value in encrypted format? \\(Please note that at this moment the sensitive values have to be stored in plain text in yaml definition.\\)")] [YamlMember(Order = 4)] @@ -30,7 +34,7 @@ public class YamlPropertyValue public static IReadOnlyDictionary ToModel(YamlPropertyValue[] properties) { - return properties.EnsureNotNull().ToDictionary(kv => kv.Key, kv => new PropertyValue(kv.IsSensitive, kv.Value, kv.ValueType)); + return properties.EnsureNotNull().ToDictionary(kv => kv.Key, kv => new PropertyValue(kv.IsSensitive, kv.File != null ? System.IO.File.ReadAllText(kv.File) : kv.Value, kv.ValueType)); } public static YamlPropertyValue[] FromModel(IReadOnlyDictionary properties) From 99315e2776a6a9704a9a9562a900017536ef78e8 Mon Sep 17 00:00:00 2001 From: "matthew.marchus" Date: Wed, 23 Jun 2021 12:43:24 -0600 Subject: [PATCH 27/49] EAG-847: Better error handling --- .../Converters/PropertyValueConverter.cs | 101 ++++++++++-------- 1 file changed, 55 insertions(+), 46 deletions(-) diff --git a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs index d8e2a5c..141034c 100644 --- a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs @@ -37,58 +37,67 @@ public static async Task UpdateWith(this IDictionary Date: Wed, 23 Jun 2021 12:44:55 -0600 Subject: [PATCH 28/49] EAG-847: Better error text --- .../Converters/PropertyValueConverter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs index 141034c..bd3b184 100644 --- a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs @@ -94,7 +94,8 @@ public static async Task UpdateWith(this IDictionary Date: Wed, 23 Jun 2021 13:21:55 -0600 Subject: [PATCH 29/49] EAG-847: Log a warning if you are using an old step template version --- .../Converters/PropertyValueConverter.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs index bd3b184..9f67fb9 100644 --- a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Octopus.Client; using Octopus.Client.Model; using OctopusProjectBuilder.Model; @@ -10,6 +11,7 @@ namespace OctopusProjectBuilder.Uploader.Converters { public static class PropertyValueConverter { + private static readonly ILogger _logger; private static readonly List protectedIds = new List(); static PropertyValueConverter() @@ -120,6 +122,15 @@ public static async Task UpdateWith(this IDictionary Date: Wed, 23 Jun 2021 14:00:11 -0600 Subject: [PATCH 30/49] EAG-847: Fix an issue where step templates were all upgraded automatically --- .../Converters/DeploymentActionConverter.cs | 8 ++- .../Converters/DeploymentProcessConverter.cs | 19 +++++-- .../Converters/DeploymentStepConverter.cs | 24 +++++++-- .../Converters/PropertyValueConverter.cs | 49 +++++++++++++++---- 4 files changed, 81 insertions(+), 19 deletions(-) diff --git a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs index cd3ffdf..fe84748 100644 --- a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Data; using System.Linq; using System.Threading.Tasks; @@ -19,13 +20,16 @@ public static async Task ToModel(this DeploymentActionResource await Task.WhenAll(resource.Environments.ToModel(repository.Environments))); } - public static async Task UpdateWith(this DeploymentActionResource resource, DeploymentAction model, IOctopusAsyncRepository repository) + public static async Task UpdateWith(this DeploymentActionResource resource, + DeploymentAction model, IOctopusAsyncRepository repository, + DeploymentActionResource oldAction) { resource.Name = model.Name; resource.ActionType = model.ActionType; resource.Environments.UpdateWith(await Task.WhenAll(model.EnvironmentRefs.Select(r => repository.Environments.ResolveResourceId(r)))); - await resource.Properties.UpdateWith(repository, model.Properties); + await resource.Properties.UpdateWith(repository, model.Properties, + oldAction != null ? oldAction.Properties : new Dictionary()); switch (resource.ActionType) { case "Octopus.TentaclePackage": diff --git a/OctopusProjectBuilder.Uploader/Converters/DeploymentProcessConverter.cs b/OctopusProjectBuilder.Uploader/Converters/DeploymentProcessConverter.cs index e6d9f37..dbbdc17 100644 --- a/OctopusProjectBuilder.Uploader/Converters/DeploymentProcessConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/DeploymentProcessConverter.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Octopus.Client; @@ -19,11 +20,21 @@ public static async Task UpdateWith(this DeploymentPr { return resource; } - - resource.Steps.Clear(); - foreach (var step in model.DeploymentSteps.Select(s => new DeploymentStepResource().UpdateWith(s, repository))) - resource.Steps.Add(await step); + List newSteps = new List(); + foreach (var step in model.DeploymentSteps) + { + DeploymentStepResource oldStep = resource.FindStep(step.Name); + newSteps.Add(await new DeploymentStepResource().UpdateWith(step, repository, oldStep)); + } + + // Replace deployment steps + resource.Steps.Clear(); + foreach (DeploymentStepResource deploymentStep in newSteps) + { + resource.Steps.Add(deploymentStep); + } + return resource; } } diff --git a/OctopusProjectBuilder.Uploader/Converters/DeploymentStepConverter.cs b/OctopusProjectBuilder.Uploader/Converters/DeploymentStepConverter.cs index 94676f0..88a101a 100644 --- a/OctopusProjectBuilder.Uploader/Converters/DeploymentStepConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/DeploymentStepConverter.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Octopus.Client; @@ -19,16 +20,31 @@ public static async Task ToModel(this DeploymentStepResource res await Task.WhenAll(resource.Actions.Select(a => a.ToModel(repository)))); } - public static async Task UpdateWith(this DeploymentStepResource resource, DeploymentStep model, IOctopusAsyncRepository repository) + public static async Task UpdateWith(this DeploymentStepResource resource, + DeploymentStep model, IOctopusAsyncRepository repository, DeploymentStepResource oldStep) { resource.Name = model.Name; resource.Condition = (DeploymentStepCondition)model.Condition; resource.RequiresPackagesToBeAcquired = model.RequiresPackagesToBeAcquired; resource.StartTrigger = (DeploymentStepStartTrigger)model.StartTrigger; - resource.Properties.UpdateWith(repository, model.Properties); + PropertyValueConverter.UpdateWith(resource.Properties, repository, model.Properties, + oldStep != null ? oldStep.Properties : new Dictionary()); + resource.Actions.Clear(); - foreach (var action in model.Actions.Select(a => new DeploymentActionResource().UpdateWith(a, repository))) - resource.Actions.Add(await action); + foreach (var action in model.Actions) + { + DeploymentActionResource oldAction; + if (oldStep != null) + { + oldAction = oldStep.FindAction(action.Name); + } + else + { + oldAction = null; + } + resource.Actions.Add(await new DeploymentActionResource().UpdateWith(action, repository, oldAction)); + } + return resource; } } diff --git a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs index 9f67fb9..864368f 100644 --- a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs @@ -11,13 +11,34 @@ namespace OctopusProjectBuilder.Uploader.Converters { public static class PropertyValueConverter { + private static readonly string octopusTemplateId = "Octopus.Action.Template.Id"; + private static readonly string octopusTemplateVersion = "Octopus.Action.Template.Version"; + + delegate bool TestProperty(IReadOnlyDictionary model, + IDictionary oldProperties); + private static readonly ILogger _logger; - private static readonly List protectedIds = new List(); + private static readonly IDictionary protectedIds; static PropertyValueConverter() { + protectedIds = new Dictionary(); + // DO NOT attempt to overwrite template version IDs if not specified. Leave this up to Octopus. - protectedIds.Add("Octopus.Action.Template.Version"); + protectedIds.Add("Octopus.Action.Template.Version", (model, oldProperties) => + { + if (oldProperties.ContainsKey(octopusTemplateId) && model.ContainsKey(octopusTemplateId)) + { + // We should only keep a template version if the model doesn't specify a changed + // template ID. If it changed, we shouldn't try to keep the old one. + return oldProperties[octopusTemplateId].Value == model[octopusTemplateId].Value; + } + else + { + // In any other case, there is no reason to preserve this old version value. + return false; + } + }); } public static PropertyValue ToModel(this PropertyValueResource resource) @@ -32,14 +53,11 @@ public static Dictionary ToModel(this IDictionary kv.Item1, kv => kv.Item2); } - public static async Task UpdateWith(this IDictionary resource, IOctopusAsyncRepository repository, - IReadOnlyDictionary model) + public static async Task UpdateWith(this IDictionary resource, + IOctopusAsyncRepository repository, + IReadOnlyDictionary model, + IDictionary oldProperties) { - foreach (var s in resource.Where(kv => !protectedIds.Contains(kv.Key)).ToList()) - { - resource.Remove(s.Key); - } - foreach (var keyValuePair in model) { string value = keyValuePair.Value.Value; @@ -104,6 +122,18 @@ public static async Task UpdateWith(this IDictionary protectedIds.ContainsKey(old.Key)) + .Where(old => !resource.ContainsKey(old.Key)) + .Where(old => protectedIds[old.Key].Invoke(model, oldProperties))) + { + resource.Add(propertyToKeep); + } + } PropertyValueResource actionTemplateId = resource .Where(a => a.Key == "Octopus.Action.Template.Id") @@ -124,6 +154,7 @@ public static async Task UpdateWith(this IDictionary Date: Wed, 23 Jun 2021 14:07:31 -0600 Subject: [PATCH 31/49] EAG-847: Fix another bug with step template versioning --- .../Converters/PropertyValueConverter.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs index 864368f..f73ab34 100644 --- a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs @@ -14,7 +14,7 @@ public static class PropertyValueConverter private static readonly string octopusTemplateId = "Octopus.Action.Template.Id"; private static readonly string octopusTemplateVersion = "Octopus.Action.Template.Version"; - delegate bool TestProperty(IReadOnlyDictionary model, + delegate bool TestProperty(IDictionary model, IDictionary oldProperties); private static readonly ILogger _logger; @@ -25,7 +25,7 @@ static PropertyValueConverter() protectedIds = new Dictionary(); // DO NOT attempt to overwrite template version IDs if not specified. Leave this up to Octopus. - protectedIds.Add("Octopus.Action.Template.Version", (model, oldProperties) => + protectedIds.Add(octopusTemplateVersion, (model, oldProperties) => { if (oldProperties.ContainsKey(octopusTemplateId) && model.ContainsKey(octopusTemplateId)) { @@ -129,14 +129,14 @@ public static async Task UpdateWith(this IDictionary protectedIds.ContainsKey(old.Key)) .Where(old => !resource.ContainsKey(old.Key)) - .Where(old => protectedIds[old.Key].Invoke(model, oldProperties))) + .Where(old => protectedIds[old.Key].Invoke(resource, oldProperties))) { resource.Add(propertyToKeep); } } PropertyValueResource actionTemplateId = resource - .Where(a => a.Key == "Octopus.Action.Template.Id") + .Where(a => a.Key == octopusTemplateId) .Select(a => a.Value) .FirstOrDefault(); @@ -144,7 +144,7 @@ public static async Task UpdateWith(this IDictionary a.Key == "Octopus.Action.Template.Version") + .Where(a => a.Key == octopusTemplateVersion) .Select(a => a.Value) .FirstOrDefault(); int versionNumber = actionTemplateVersion != null ? @@ -154,8 +154,7 @@ public static async Task UpdateWith(this IDictionary Date: Wed, 23 Jun 2021 14:43:59 -0600 Subject: [PATCH 32/49] EAG-847: Support disabling steps --- OctopusProjectBuilder.Model/DeploymentAction.cs | 4 +++- .../Converters/DeploymentActionConverter.cs | 2 ++ .../Model/YamlDeploymentAction.cs | 15 ++++++++++----- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/OctopusProjectBuilder.Model/DeploymentAction.cs b/OctopusProjectBuilder.Model/DeploymentAction.cs index 60710ca..7a8e303 100644 --- a/OctopusProjectBuilder.Model/DeploymentAction.cs +++ b/OctopusProjectBuilder.Model/DeploymentAction.cs @@ -6,13 +6,15 @@ namespace OctopusProjectBuilder.Model public class DeploymentAction { public string Name { get; } + public bool Disabled { get; } public string ActionType { get; } public IReadOnlyDictionary Properties { get; } public IEnumerable EnvironmentRefs { get; } - public DeploymentAction(string name, string actionType, IReadOnlyDictionary properties, IEnumerable environmentRefs) + public DeploymentAction(string name, bool disabled, string actionType, IReadOnlyDictionary properties, IEnumerable environmentRefs) { Name = name; + Disabled = disabled; ActionType = actionType; Properties = properties; EnvironmentRefs = environmentRefs.ToArray(); diff --git a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs index fe84748..9c660cc 100644 --- a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs @@ -15,6 +15,7 @@ public static async Task ToModel(this DeploymentActionResource { return new DeploymentAction( resource.Name, + resource.IsDisabled, resource.ActionType, resource.Properties.ToModel(), await Task.WhenAll(resource.Environments.ToModel(repository.Environments))); @@ -25,6 +26,7 @@ public static async Task UpdateWith(this DeploymentAct DeploymentActionResource oldAction) { resource.Name = model.Name; + resource.IsDisabled = model.Disabled; resource.ActionType = model.ActionType; resource.Environments.UpdateWith(await Task.WhenAll(model.EnvironmentRefs.Select(r => repository.Environments.ResolveResourceId(r)))); diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentAction.cs b/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentAction.cs index fe7b8ed..c32960e 100644 --- a/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentAction.cs +++ b/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentAction.cs @@ -16,21 +16,25 @@ public class YamlDeploymentAction : IYamlTemplateBased [Description("Unique name.")] [YamlMember(Order = 1)] public string Name { get; set; } + + [Description("Defines if this action is disabled.")] + [YamlMember(Order = 2)] + public bool Disabled { get; set; } [Description("Indicates that the resource is template based.")] - [YamlMember(Order = 2)] + [YamlMember(Order = 3)] public YamlTemplateReference UseTemplate { get; set; } [Description("Action type.")] - [YamlMember(Order = 3)] + [YamlMember(Order = 4)] public string ActionType { get; set; } [Description("List of Environment references (based on the name) where action would be performed on. If none are specified, then action would be performed on all environments.")] - [YamlMember(Order = 4)] + [YamlMember(Order = 5)] public string[] EnvironmentRefs { get; set; } [Description("Action properties.")] - [YamlMember(Order = 5)] + [YamlMember(Order = 6)] public YamlPropertyValue[] Properties { get; set; } public void ApplyTemplate(YamlTemplates templates) @@ -43,6 +47,7 @@ public static YamlDeploymentAction FromModel(DeploymentAction model) return new YamlDeploymentAction { Name = model.Name, + Disabled = model.Disabled, ActionType = model.ActionType, Properties = YamlPropertyValue.FromModel(model.Properties), EnvironmentRefs = model.EnvironmentRefs.Select(r => r.Name).ToArray().NullIfEmpty() @@ -51,7 +56,7 @@ public static YamlDeploymentAction FromModel(DeploymentAction model) public DeploymentAction ToModel() { - return new DeploymentAction(Name, ActionType, YamlPropertyValue.ToModel(Properties), EnvironmentRefs.EnsureNotNull().Select(name => new ElementReference(name))); + return new DeploymentAction(Name, Disabled, ActionType, YamlPropertyValue.ToModel(Properties), EnvironmentRefs.EnsureNotNull().Select(name => new ElementReference(name))); } } } \ No newline at end of file From 52881ecc992765c5570f033abbe2a4e5f9980904 Mon Sep 17 00:00:00 2001 From: "matthew.marchus" Date: Wed, 23 Jun 2021 14:46:20 -0600 Subject: [PATCH 33/49] EAG-847: Fix tests, Disabled->IsDisabled --- OctopusProjectBuilder.Model/DeploymentAction.cs | 6 +++--- .../ModelDownloaderTests.cs | 8 ++++---- .../Converters/DeploymentActionConverter.cs | 2 +- .../Model/YamlDeploymentAction.cs | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/OctopusProjectBuilder.Model/DeploymentAction.cs b/OctopusProjectBuilder.Model/DeploymentAction.cs index 7a8e303..57d4ff0 100644 --- a/OctopusProjectBuilder.Model/DeploymentAction.cs +++ b/OctopusProjectBuilder.Model/DeploymentAction.cs @@ -6,15 +6,15 @@ namespace OctopusProjectBuilder.Model public class DeploymentAction { public string Name { get; } - public bool Disabled { get; } + public bool IsDisabled { get; } public string ActionType { get; } public IReadOnlyDictionary Properties { get; } public IEnumerable EnvironmentRefs { get; } - public DeploymentAction(string name, bool disabled, string actionType, IReadOnlyDictionary properties, IEnumerable environmentRefs) + public DeploymentAction(string name, bool isDisabled, string actionType, IReadOnlyDictionary properties, IEnumerable environmentRefs) { Name = name; - Disabled = disabled; + IsDisabled = isDisabled; ActionType = actionType; Properties = properties; EnvironmentRefs = environmentRefs.ToArray(); diff --git a/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs b/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs index dc36667..21f42f9 100644 --- a/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs +++ b/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs @@ -208,10 +208,10 @@ public void It_should_upload_and_download_projects() CreateItem(), CreateItem>(), new[] { - new DeploymentAction(CreateItem(), CreateItem(), + new DeploymentAction(CreateItem(), CreateItem(), CreateItem(), CreateItem>(), new[] {new ElementReference("env1")}), - new DeploymentAction(CreateItem(), CreateItem(), + new DeploymentAction(CreateItem(), CreateItem(), CreateItem(), CreateItem>(), new[] {new ElementReference("env2")}) }), @@ -219,7 +219,7 @@ public void It_should_upload_and_download_projects() CreateItem(), CreateItem>(), new[] { - new DeploymentAction(CreateItem(), CreateItem(), + new DeploymentAction(CreateItem(), CreateItem(), CreateItem(), CreateItem>(), new[] {new ElementReference("env1")}) }) @@ -536,7 +536,7 @@ public void It_should_upload_and_download_tenant() CreateItem(), CreateItem>(), new[] { - new DeploymentAction(CreateItem(), CreateItem(), + new DeploymentAction(CreateItem(), CreateItem(), CreateItem(), CreateItem>(), new[] {new ElementReference("env1")}), }) diff --git a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs index 9c660cc..62e7e53 100644 --- a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs @@ -26,7 +26,7 @@ public static async Task UpdateWith(this DeploymentAct DeploymentActionResource oldAction) { resource.Name = model.Name; - resource.IsDisabled = model.Disabled; + resource.IsDisabled = model.IsDisabled; resource.ActionType = model.ActionType; resource.Environments.UpdateWith(await Task.WhenAll(model.EnvironmentRefs.Select(r => repository.Environments.ResolveResourceId(r)))); diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentAction.cs b/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentAction.cs index c32960e..c4b0f72 100644 --- a/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentAction.cs +++ b/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentAction.cs @@ -19,7 +19,7 @@ public class YamlDeploymentAction : IYamlTemplateBased [Description("Defines if this action is disabled.")] [YamlMember(Order = 2)] - public bool Disabled { get; set; } + public bool IsDisabled { get; set; } [Description("Indicates that the resource is template based.")] [YamlMember(Order = 3)] @@ -47,7 +47,7 @@ public static YamlDeploymentAction FromModel(DeploymentAction model) return new YamlDeploymentAction { Name = model.Name, - Disabled = model.Disabled, + IsDisabled = model.IsDisabled, ActionType = model.ActionType, Properties = YamlPropertyValue.FromModel(model.Properties), EnvironmentRefs = model.EnvironmentRefs.Select(r => r.Name).ToArray().NullIfEmpty() @@ -56,7 +56,7 @@ public static YamlDeploymentAction FromModel(DeploymentAction model) public DeploymentAction ToModel() { - return new DeploymentAction(Name, Disabled, ActionType, YamlPropertyValue.ToModel(Properties), EnvironmentRefs.EnsureNotNull().Select(name => new ElementReference(name))); + return new DeploymentAction(Name, IsDisabled, ActionType, YamlPropertyValue.ToModel(Properties), EnvironmentRefs.EnsureNotNull().Select(name => new ElementReference(name))); } } } \ No newline at end of file From 99b85cb0e41fd84fe49a86a02eaa2446586c196c Mon Sep 17 00:00:00 2001 From: "matthew.marchus" Date: Wed, 23 Jun 2021 15:05:37 -0600 Subject: [PATCH 34/49] EAG-847: Update documentation --- OctopusProjectBuilder.YamlReader/Model/YamlPropertyValue.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlPropertyValue.cs b/OctopusProjectBuilder.YamlReader/Model/YamlPropertyValue.cs index 2b5e94c..705edd3 100644 --- a/OctopusProjectBuilder.YamlReader/Model/YamlPropertyValue.cs +++ b/OctopusProjectBuilder.YamlReader/Model/YamlPropertyValue.cs @@ -16,7 +16,7 @@ public class YamlPropertyValue [YamlMember(Order = 1)] public string Key { get; set; } - [Description("Property value type.")] + [Description("Property value type. This describes the transformation that should be applied to the value of this. Options are: Literal (default), ProjectNameToId, EnvironmentNameToId, StepTemplateNameToId, FeedNameToId, MachineNameToId, SpaceNameToId, TenantNameToId, LifecycleNameToId, CertificateNameToId, ProxyNameToId, TagSetNameToId, UserRoleNameToId, RunbookNameToId")] [YamlMember(Order = 2)] public string ValueType { get; set; } = "Literal"; @@ -24,7 +24,7 @@ public class YamlPropertyValue [YamlMember(Order = 3)] public string Value { get; set; } - [Description("Property value file content.")] + [Description("An optionally provided path to a file to override Value with.")] [YamlMember(Order = 4)] public string File { get; set; } From 5a837074aace0e548842a90d97c6344b26c72672 Mon Sep 17 00:00:00 2001 From: "matthew.marchus" Date: Wed, 23 Jun 2021 15:21:03 -0600 Subject: [PATCH 35/49] EAG-847: Support Variable step conditions --- OctopusProjectBuilder.Model/DeploymentStep.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OctopusProjectBuilder.Model/DeploymentStep.cs b/OctopusProjectBuilder.Model/DeploymentStep.cs index b150ed7..386db49 100644 --- a/OctopusProjectBuilder.Model/DeploymentStep.cs +++ b/OctopusProjectBuilder.Model/DeploymentStep.cs @@ -17,7 +17,8 @@ public enum StepCondition { Success, Failure, - Always + Always, + Variable } public enum StepStartTrigger From 921b23973ac81c3fa2d91e3c3c445963d5a8e291 Mon Sep 17 00:00:00 2001 From: "matthew.marchus" Date: Wed, 23 Jun 2021 15:35:12 -0600 Subject: [PATCH 36/49] EAG-847: Support conditional actions (variable) --- OctopusProjectBuilder.Model/DeploymentAction.cs | 12 +++++++++++- .../Converters/DeploymentActionConverter.cs | 2 ++ .../Model/YamlDeploymentAction.cs | 13 ++++++++++--- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/OctopusProjectBuilder.Model/DeploymentAction.cs b/OctopusProjectBuilder.Model/DeploymentAction.cs index 57d4ff0..a0eec34 100644 --- a/OctopusProjectBuilder.Model/DeploymentAction.cs +++ b/OctopusProjectBuilder.Model/DeploymentAction.cs @@ -7,17 +7,27 @@ public class DeploymentAction { public string Name { get; } public bool IsDisabled { get; } + public ActionCondition Condition { get; } public string ActionType { get; } public IReadOnlyDictionary Properties { get; } public IEnumerable EnvironmentRefs { get; } - public DeploymentAction(string name, bool isDisabled, string actionType, IReadOnlyDictionary properties, IEnumerable environmentRefs) + public DeploymentAction(string name, bool isDisabled, ActionCondition condition, + string actionType, IReadOnlyDictionary properties, + IEnumerable environmentRefs) { Name = name; IsDisabled = isDisabled; + Condition = condition; ActionType = actionType; Properties = properties; EnvironmentRefs = environmentRefs.ToArray(); } + + public enum ActionCondition + { + Success, + Variable + } } } \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs index 62e7e53..26bcb91 100644 --- a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs @@ -16,6 +16,7 @@ public static async Task ToModel(this DeploymentActionResource return new DeploymentAction( resource.Name, resource.IsDisabled, + (DeploymentAction.ActionCondition)resource.Condition, resource.ActionType, resource.Properties.ToModel(), await Task.WhenAll(resource.Environments.ToModel(repository.Environments))); @@ -27,6 +28,7 @@ public static async Task UpdateWith(this DeploymentAct { resource.Name = model.Name; resource.IsDisabled = model.IsDisabled; + resource.Condition = (DeploymentActionCondition) model.Condition; resource.ActionType = model.ActionType; resource.Environments.UpdateWith(await Task.WhenAll(model.EnvironmentRefs.Select(r => repository.Environments.ResolveResourceId(r)))); diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentAction.cs b/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentAction.cs index c4b0f72..911015b 100644 --- a/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentAction.cs +++ b/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentAction.cs @@ -28,13 +28,17 @@ public class YamlDeploymentAction : IYamlTemplateBased [Description("Action type.")] [YamlMember(Order = 4)] public string ActionType { get; set; } + + [Description("Action run condition.")] + [YamlMember(Order = 5)] + public DeploymentAction.ActionCondition Condition { get; set; } [Description("List of Environment references (based on the name) where action would be performed on. If none are specified, then action would be performed on all environments.")] - [YamlMember(Order = 5)] + [YamlMember(Order = 6)] public string[] EnvironmentRefs { get; set; } [Description("Action properties.")] - [YamlMember(Order = 6)] + [YamlMember(Order = 7)] public YamlPropertyValue[] Properties { get; set; } public void ApplyTemplate(YamlTemplates templates) @@ -48,6 +52,7 @@ public static YamlDeploymentAction FromModel(DeploymentAction model) { Name = model.Name, IsDisabled = model.IsDisabled, + Condition = model.Condition, ActionType = model.ActionType, Properties = YamlPropertyValue.FromModel(model.Properties), EnvironmentRefs = model.EnvironmentRefs.Select(r => r.Name).ToArray().NullIfEmpty() @@ -56,7 +61,9 @@ public static YamlDeploymentAction FromModel(DeploymentAction model) public DeploymentAction ToModel() { - return new DeploymentAction(Name, IsDisabled, ActionType, YamlPropertyValue.ToModel(Properties), EnvironmentRefs.EnsureNotNull().Select(name => new ElementReference(name))); + return new DeploymentAction(Name, IsDisabled, Condition, ActionType, + YamlPropertyValue.ToModel(Properties), + EnvironmentRefs.EnsureNotNull().Select(name => new ElementReference(name))); } } } \ No newline at end of file From 2a0e1e8e1ba3cabf11c08f4080aeab5090f4f2fd Mon Sep 17 00:00:00 2001 From: "matthew.marchus" Date: Wed, 23 Jun 2021 15:37:59 -0600 Subject: [PATCH 37/49] EAG-847: Fix tests --- .../ModelDownloaderTests.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs b/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs index 21f42f9..853d9e9 100644 --- a/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs +++ b/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs @@ -208,10 +208,12 @@ public void It_should_upload_and_download_projects() CreateItem(), CreateItem>(), new[] { - new DeploymentAction(CreateItem(), CreateItem(), CreateItem(), + new DeploymentAction(CreateItem(), CreateItem(), + CreateItem(), CreateItem(), CreateItem>(), new[] {new ElementReference("env1")}), - new DeploymentAction(CreateItem(), CreateItem(), CreateItem(), + new DeploymentAction(CreateItem(), CreateItem(), + CreateItem(), CreateItem(), CreateItem>(), new[] {new ElementReference("env2")}) }), @@ -219,7 +221,8 @@ public void It_should_upload_and_download_projects() CreateItem(), CreateItem>(), new[] { - new DeploymentAction(CreateItem(), CreateItem(), CreateItem(), + new DeploymentAction(CreateItem(), CreateItem(), + CreateItem(), CreateItem(), CreateItem>(), new[] {new ElementReference("env1")}) }) @@ -536,7 +539,8 @@ public void It_should_upload_and_download_tenant() CreateItem(), CreateItem>(), new[] { - new DeploymentAction(CreateItem(), CreateItem(), CreateItem(), + new DeploymentAction(CreateItem(), CreateItem(), + CreateItem(), CreateItem(), CreateItem>(), new[] {new ElementReference("env1")}), }) From e9e142aa55209539d637d25b9698984a87fb341b Mon Sep 17 00:00:00 2001 From: "matthew.marchus" Date: Wed, 23 Jun 2021 15:44:36 -0600 Subject: [PATCH 38/49] EAG-847: Preserve identifiers --- .../Converters/DeploymentActionConverter.cs | 6 ++++++ .../Converters/DeploymentStepConverter.cs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs index 26bcb91..d0337bf 100644 --- a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs @@ -26,6 +26,12 @@ public static async Task UpdateWith(this DeploymentAct DeploymentAction model, IOctopusAsyncRepository repository, DeploymentActionResource oldAction) { + // Preserve the Id + if (oldAction != null) + { + resource.Id = oldAction.Id; + } + resource.Name = model.Name; resource.IsDisabled = model.IsDisabled; resource.Condition = (DeploymentActionCondition) model.Condition; diff --git a/OctopusProjectBuilder.Uploader/Converters/DeploymentStepConverter.cs b/OctopusProjectBuilder.Uploader/Converters/DeploymentStepConverter.cs index 88a101a..ba86128 100644 --- a/OctopusProjectBuilder.Uploader/Converters/DeploymentStepConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/DeploymentStepConverter.cs @@ -23,6 +23,12 @@ public static async Task ToModel(this DeploymentStepResource res public static async Task UpdateWith(this DeploymentStepResource resource, DeploymentStep model, IOctopusAsyncRepository repository, DeploymentStepResource oldStep) { + // Preserve the old Id + if (oldStep != null) + { + resource.Id = oldStep.Id; + } + resource.Name = model.Name; resource.Condition = (DeploymentStepCondition)model.Condition; resource.RequiresPackagesToBeAcquired = model.RequiresPackagesToBeAcquired; From a72f36e30c6b214156b0350d2698ebaa3b855374 Mon Sep 17 00:00:00 2001 From: "matthew.marchus" Date: Wed, 23 Jun 2021 15:53:21 -0600 Subject: [PATCH 39/49] EAG-847: Don't specify an ID for actions --- .../Converters/DeploymentActionConverter.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs index d0337bf..25b735e 100644 --- a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs @@ -26,12 +26,6 @@ public static async Task UpdateWith(this DeploymentAct DeploymentAction model, IOctopusAsyncRepository repository, DeploymentActionResource oldAction) { - // Preserve the Id - if (oldAction != null) - { - resource.Id = oldAction.Id; - } - resource.Name = model.Name; resource.IsDisabled = model.IsDisabled; resource.Condition = (DeploymentActionCondition) model.Condition; @@ -40,6 +34,7 @@ public static async Task UpdateWith(this DeploymentAct await resource.Properties.UpdateWith(repository, model.Properties, oldAction != null ? oldAction.Properties : new Dictionary()); + switch (resource.ActionType) { case "Octopus.TentaclePackage": @@ -47,14 +42,18 @@ await resource.Properties.UpdateWith(repository, model.Properties, { throw new ConstraintException("No package ID specified for package action" + resource.Name); } - break; case "Octopus.Script": if (!resource.Properties.ContainsKey("Octopus.Action.Script.ScriptBody")) { throw new ConstraintException("No script body specified for script action in " + resource.Name); } - + break; + case "Octopus.DeployRelease": + if (!resource.Properties.ContainsKey("Octopus.Action.DeployRelease.ProjectId")) + { + throw new ConstraintException("No project ID specified for release action in " + resource.Name); + } break; } From 63740c529587b33941cab11b3cf49bee1afcbce6 Mon Sep 17 00:00:00 2001 From: "matthew.marchus" Date: Wed, 7 Jul 2021 15:17:34 -0600 Subject: [PATCH 40/49] NONE: Add support for 3 new value transformations --- .../Converters/PropertyValueConverter.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs index f73ab34..268ba8e 100644 --- a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs @@ -3,9 +3,11 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Newtonsoft.Json; using Octopus.Client; using Octopus.Client.Model; using OctopusProjectBuilder.Model; +using Environment = System.Environment; namespace OctopusProjectBuilder.Uploader.Converters { @@ -68,6 +70,20 @@ public static async Task UpdateWith(this IDictionary Date: Tue, 20 Jul 2021 11:39:37 -0600 Subject: [PATCH 41/49] EAG-887: Support runbooks --- .../DeploymentConnectivityPolicy.cs | 18 ++++ .../GuidedFailureMode.cs | 9 ++ OctopusProjectBuilder.Model/Runbook.cs | 18 ++++ .../RunbookEnvironmentScope.cs | 9 ++ OctopusProjectBuilder.Model/RunbookProcess.cs | 15 +++ .../RunbookRetentionPeriod.cs | 9 ++ .../SkipMachineBehavior.cs | 8 ++ OctopusProjectBuilder.Model/SystemModel.cs | 27 +++--- .../SystemModelBuilder.cs | 10 +- .../DeploymentConnectivityPolicyConverter.cs | 39 ++++++++ .../Converters/RunbookConverter.cs | 69 +++++++++++++ .../Converters/RunbookProcessConverter.cs | 41 ++++++++ .../RunbookRetentionPeriodConverter.cs | 26 +++++ .../ModelDownloader.cs | 14 ++- .../ModelUploader.cs | 18 ++++ .../Model/Templates/YamlRunbookTemplate.cs | 19 ++++ .../Model/Templates/YamlTemplates.cs | 3 + .../Model/YamlDeploymentConnectivityPolicy.cs | 52 ++++++++++ .../Model/YamlOctopusModel.cs | 15 ++- .../Model/YamlRunbook.cs | 96 +++++++++++++++++++ .../Model/YamlRunbookProcess.cs | 28 ++++++ .../YamlSystemModelRepository.cs | 1 + 22 files changed, 528 insertions(+), 16 deletions(-) create mode 100644 OctopusProjectBuilder.Model/DeploymentConnectivityPolicy.cs create mode 100644 OctopusProjectBuilder.Model/GuidedFailureMode.cs create mode 100644 OctopusProjectBuilder.Model/Runbook.cs create mode 100644 OctopusProjectBuilder.Model/RunbookEnvironmentScope.cs create mode 100644 OctopusProjectBuilder.Model/RunbookProcess.cs create mode 100644 OctopusProjectBuilder.Model/RunbookRetentionPeriod.cs create mode 100644 OctopusProjectBuilder.Model/SkipMachineBehavior.cs create mode 100644 OctopusProjectBuilder.Uploader/Converters/DeploymentConnectivityPolicyConverter.cs create mode 100644 OctopusProjectBuilder.Uploader/Converters/RunbookConverter.cs create mode 100644 OctopusProjectBuilder.Uploader/Converters/RunbookProcessConverter.cs create mode 100644 OctopusProjectBuilder.Uploader/Converters/RunbookRetentionPeriodConverter.cs create mode 100644 OctopusProjectBuilder.YamlReader/Model/Templates/YamlRunbookTemplate.cs create mode 100644 OctopusProjectBuilder.YamlReader/Model/YamlDeploymentConnectivityPolicy.cs create mode 100644 OctopusProjectBuilder.YamlReader/Model/YamlRunbook.cs create mode 100644 OctopusProjectBuilder.YamlReader/Model/YamlRunbookProcess.cs diff --git a/OctopusProjectBuilder.Model/DeploymentConnectivityPolicy.cs b/OctopusProjectBuilder.Model/DeploymentConnectivityPolicy.cs new file mode 100644 index 0000000..c189c3d --- /dev/null +++ b/OctopusProjectBuilder.Model/DeploymentConnectivityPolicy.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using Octopus.Client.Model; + +namespace OctopusProjectBuilder.Model +{ + public class DeploymentConnectivityPolicy + { + public Octopus.Client.Model.SkipMachineBehavior SkipMachineBehavior { get; set; } + + public IEnumerable TargetRoles { get; set; } + + public bool AllowDeploymentsToNoTargets { get; set; } + + public bool ExcludeUnhealthyTargets { get; set; } + + public DeploymentConnectivityPolicy() => this.TargetRoles = new List(); + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.Model/GuidedFailureMode.cs b/OctopusProjectBuilder.Model/GuidedFailureMode.cs new file mode 100644 index 0000000..803a8ca --- /dev/null +++ b/OctopusProjectBuilder.Model/GuidedFailureMode.cs @@ -0,0 +1,9 @@ +namespace OctopusProjectBuilder.Model +{ + public enum GuidedFailureMode + { + EnvironmentDefault, + Off, + On, + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.Model/Runbook.cs b/OctopusProjectBuilder.Model/Runbook.cs new file mode 100644 index 0000000..c1cc1b1 --- /dev/null +++ b/OctopusProjectBuilder.Model/Runbook.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace OctopusProjectBuilder.Model +{ + public class Runbook + { + public ElementIdentifier Identifier { get; set; } + public string Description { get; set; } + public string ProjectName { get; set; } + public RunbookProcess Process { get; set; } + public Model.DeploymentConnectivityPolicy ConnectivityPolicy { get; set; } + public TenantedDeploymentMode? MultiTenancyMode { get; set; } + public RunbookEnvironmentScope? EnvironmentScope { get; set; } + public RunbookRetentionPeriod RunRetentionPolicy { get; set; } + public GuidedFailureMode? GuidedFailureMode { get; set; } + public IEnumerable EnvironmentRefs { get; set; } + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.Model/RunbookEnvironmentScope.cs b/OctopusProjectBuilder.Model/RunbookEnvironmentScope.cs new file mode 100644 index 0000000..2b75bf0 --- /dev/null +++ b/OctopusProjectBuilder.Model/RunbookEnvironmentScope.cs @@ -0,0 +1,9 @@ +namespace OctopusProjectBuilder.Model +{ + public enum RunbookEnvironmentScope + { + All, + Specified, + FromProjectLifecycles, + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.Model/RunbookProcess.cs b/OctopusProjectBuilder.Model/RunbookProcess.cs new file mode 100644 index 0000000..0d63309 --- /dev/null +++ b/OctopusProjectBuilder.Model/RunbookProcess.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Linq; + +namespace OctopusProjectBuilder.Model +{ + public class RunbookProcess + { + public IEnumerable DeploymentSteps { get; } + + public RunbookProcess(IEnumerable deploymentSteps) + { + DeploymentSteps = deploymentSteps.ToArray(); + } + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.Model/RunbookRetentionPeriod.cs b/OctopusProjectBuilder.Model/RunbookRetentionPeriod.cs new file mode 100644 index 0000000..1a85675 --- /dev/null +++ b/OctopusProjectBuilder.Model/RunbookRetentionPeriod.cs @@ -0,0 +1,9 @@ +namespace OctopusProjectBuilder.Model +{ + public class RunbookRetentionPeriod + { + public int QuantityToKeep { get; set; } + + public bool ShouldKeepForever { get; set; } + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.Model/SkipMachineBehavior.cs b/OctopusProjectBuilder.Model/SkipMachineBehavior.cs new file mode 100644 index 0000000..ac9b8cd --- /dev/null +++ b/OctopusProjectBuilder.Model/SkipMachineBehavior.cs @@ -0,0 +1,8 @@ +namespace OctopusProjectBuilder.Model +{ + public enum SkipMachineBehavior + { + None, + SkipUnavailableMachines, + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.Model/SystemModel.cs b/OctopusProjectBuilder.Model/SystemModel.cs index 368e694..ae4f4a4 100644 --- a/OctopusProjectBuilder.Model/SystemModel.cs +++ b/OctopusProjectBuilder.Model/SystemModel.cs @@ -16,8 +16,9 @@ public class SystemModel public IEnumerable Teams { get; } public IEnumerable Tenants { get; } public IEnumerable TagSets { get; } + public IEnumerable Runbooks { get; } - public SystemModel(IEnumerable machinePolicies, IEnumerable lifecycles, IEnumerable projectGroups, IEnumerable libraryVariableSets, IEnumerable projects, IEnumerable channels, IEnumerable environments, IEnumerable userRoles, IEnumerable teams, IEnumerable tenants, IEnumerable tagSets) + public SystemModel(IEnumerable machinePolicies, IEnumerable lifecycles, IEnumerable projectGroups, IEnumerable libraryVariableSets, IEnumerable projects, IEnumerable channels, IEnumerable environments, IEnumerable userRoles, IEnumerable teams, IEnumerable tenants, IEnumerable tagSets, IEnumerable runbooks) { MachinePolicies = machinePolicies.OrderBy(s => s.Identifier.Name).ToArray(); Environments = environments.OrderBy(s => s.Identifier.Name).ToArray(); @@ -30,21 +31,23 @@ public SystemModel(IEnumerable machinePolicies, IEnumerable s.Identifier.Name).ToArray(); Tenants = tenants.OrderBy(s => s.Identifier.Name).ToArray(); TagSets = tagSets.OrderBy(s => s.Identifier.Name).ToArray(); + Runbooks = runbooks.OrderBy(s => s.Identifier.Name).ToArray(); } public IEnumerable SplitModel() { - return MachinePolicies.Select(t => new SystemModel(Enumerable.Repeat(t, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty())) - .Concat(Environments.Select(e => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(e, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) - .Concat(ProjectGroups.Select(grp => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(grp, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) - .Concat(Projects.Select(prj => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(prj, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) - .Concat(Channels.Select(ch => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(ch, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) - .Concat(Lifecycles.Select(lf => new SystemModel(Enumerable.Empty(), Enumerable.Repeat(lf, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) - .Concat(LibraryVariableSets.Select(lvs => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(lvs, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) - .Concat(UserRoles.Select(ur => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(ur, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) - .Concat(Teams.Select(t => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(t, 1), Enumerable.Empty(), Enumerable.Empty()))) - .Concat(Tenants.Select(t => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(t, 1), Enumerable.Empty()))) - .Concat(TagSets.Select(t => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(t, 1)))); + return MachinePolicies.Select(t => new SystemModel(Enumerable.Repeat(t, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty())) + .Concat(Environments.Select(e => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(e, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) + .Concat(ProjectGroups.Select(grp => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(grp, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) + .Concat(Projects.Select(prj => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(prj, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) + .Concat(Channels.Select(ch => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(ch, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) + .Concat(Lifecycles.Select(lf => new SystemModel(Enumerable.Empty(), Enumerable.Repeat(lf, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) + .Concat(LibraryVariableSets.Select(lvs => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(lvs, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) + .Concat(UserRoles.Select(ur => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(ur, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) + .Concat(Teams.Select(t => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(t, 1), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()))) + .Concat(Tenants.Select(t => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(t, 1), Enumerable.Empty(), Enumerable.Empty()))) + .Concat(TagSets.Select(t => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(t, 1), Enumerable.Empty()))) + .Concat(Runbooks.Select(t => new SystemModel(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Repeat(t, 1)))); } } } diff --git a/OctopusProjectBuilder.Model/SystemModelBuilder.cs b/OctopusProjectBuilder.Model/SystemModelBuilder.cs index d4bc74a..dc594b9 100644 --- a/OctopusProjectBuilder.Model/SystemModelBuilder.cs +++ b/OctopusProjectBuilder.Model/SystemModelBuilder.cs @@ -15,6 +15,7 @@ public class SystemModelBuilder private readonly List _teams = new List(); private readonly List _tenants = new List(); private readonly List _tagSets = new List(); + private readonly List _runbooks = new List(); public SystemModelBuilder AddProjectGroup(ProjectGroup group) { @@ -82,6 +83,12 @@ public SystemModelBuilder AddTagSet(TagSet tagSet) return this; } + public SystemModelBuilder AddRunbook(Runbook runbook) + { + _runbooks.Add(runbook); + return this; + } + public SystemModel Build() { return new SystemModel( @@ -95,7 +102,8 @@ public SystemModel Build() _userRoles, _teams, _tenants, - _tagSets); + _tagSets, + _runbooks); } } } \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader/Converters/DeploymentConnectivityPolicyConverter.cs b/OctopusProjectBuilder.Uploader/Converters/DeploymentConnectivityPolicyConverter.cs new file mode 100644 index 0000000..683d2d7 --- /dev/null +++ b/OctopusProjectBuilder.Uploader/Converters/DeploymentConnectivityPolicyConverter.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Octopus.Client; +using Octopus.Client.Model; +using OctopusProjectBuilder.Model; +using DeploymentConnectivityPolicy = Octopus.Client.Model.DeploymentConnectivityPolicy; +using SkipMachineBehavior = Octopus.Client.Model.SkipMachineBehavior; + +namespace OctopusProjectBuilder.Uploader.Converters +{ + public static class DeploymentConnectivityPolicyConverter + { + + public static async Task UpdateWith(this DeploymentConnectivityPolicy resource, + Model.DeploymentConnectivityPolicy model, IOctopusAsyncRepository repository) + { + resource.TargetRoles = new ReferenceCollection(model.TargetRoles.Select(x => x.Name)); + resource.ExcludeUnhealthyTargets = model.ExcludeUnhealthyTargets; + resource.SkipMachineBehavior = (SkipMachineBehavior) model.SkipMachineBehavior; + resource.AllowDeploymentsToNoTargets = model.AllowDeploymentsToNoTargets; + + return resource; + } + + public static async Task ToModel + (this DeploymentConnectivityPolicy resource, IOctopusAsyncRepository repository) + { + return new Model.DeploymentConnectivityPolicy + { + TargetRoles = resource.TargetRoles.Select(x => new ElementReference(x)).ToArray(), + ExcludeUnhealthyTargets = resource.ExcludeUnhealthyTargets, + SkipMachineBehavior = resource.SkipMachineBehavior, + AllowDeploymentsToNoTargets = resource.AllowDeploymentsToNoTargets + }; + } + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader/Converters/RunbookConverter.cs b/OctopusProjectBuilder.Uploader/Converters/RunbookConverter.cs new file mode 100644 index 0000000..e8f2114 --- /dev/null +++ b/OctopusProjectBuilder.Uploader/Converters/RunbookConverter.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Octopus.Client; +using Octopus.Client.Model; +using OctopusProjectBuilder.Model; +using GuidedFailureMode = Octopus.Client.Model.GuidedFailureMode; +using RunbookEnvironmentScope = Octopus.Client.Model.RunbookEnvironmentScope; +using TenantedDeploymentMode = Octopus.Client.Model.TenantedDeploymentMode; + +namespace OctopusProjectBuilder.Uploader.Converters +{ + public static class RunbookConverter + { + + public static async Task UpdateWith(this RunbookResource resource, Runbook model, + IOctopusAsyncRepository repository) + { + if (model.Identifier != null) + { + resource.Name = model.Identifier.Name; + } + + resource.Description = model.Description; + + resource.ProjectId = (await repository.Projects.FindByName(model.ProjectName)).Id; + + resource.Environments.UpdateWith(await Task.WhenAll(model.EnvironmentRefs + .Select(r => repository.Environments.ResolveResourceId(r)))); + + if (model.EnvironmentScope.HasValue) + resource.EnvironmentScope = (RunbookEnvironmentScope) model.EnvironmentScope.Value; + + if (model.MultiTenancyMode.HasValue) + resource.MultiTenancyMode = (TenantedDeploymentMode) model.MultiTenancyMode.Value; + + if (model.RunRetentionPolicy != null) + resource.RunRetentionPolicy = model.RunRetentionPolicy.FromModel(); + + if (model.GuidedFailureMode != null) + resource.DefaultGuidedFailureMode = (GuidedFailureMode) model.GuidedFailureMode; + + if (model.ConnectivityPolicy != null) + await resource.ConnectivityPolicy.UpdateWith(model.ConnectivityPolicy, repository); + + return resource; + } + + public static async Task ToModel(this RunbookResource resource, IOctopusAsyncRepository repository) + { + var projectResource = await repository.Projects.Get(resource.ProjectId); + var runbookProcess = await repository.RunbookProcesses.Get(resource.RunbookProcessId); + + return new Runbook() + { + Description = resource.Description, + EnvironmentScope = (Model.RunbookEnvironmentScope?) resource.EnvironmentScope, + Identifier = new ElementIdentifier(resource.Name), + MultiTenancyMode = (Model.TenantedDeploymentMode?) resource.MultiTenancyMode, + ProjectName = projectResource.Name, + RunRetentionPolicy = resource.RunRetentionPolicy.ToModel(), + Process = await runbookProcess.ToModel(repository), + GuidedFailureMode = (Model.GuidedFailureMode?) resource.DefaultGuidedFailureMode, + EnvironmentRefs = await Task.WhenAll(resource.Environments.ToModel(repository.Environments)) + }; + } + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader/Converters/RunbookProcessConverter.cs b/OctopusProjectBuilder.Uploader/Converters/RunbookProcessConverter.cs new file mode 100644 index 0000000..f5c9687 --- /dev/null +++ b/OctopusProjectBuilder.Uploader/Converters/RunbookProcessConverter.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Octopus.Client; +using Octopus.Client.Model; +using OctopusProjectBuilder.Model; + +namespace OctopusProjectBuilder.Uploader.Converters +{ + public static class RunbookProcessConverter + { + public static async Task ToModel(this RunbookProcessResource resource, IOctopusAsyncRepository repository) + { + return new RunbookProcess(await Task.WhenAll(resource.Steps.Select(s => s.ToModel(repository)))); + } + + public static async Task UpdateWith(this RunbookProcessResource resource, RunbookProcess model, IOctopusAsyncRepository repository) + { + if (model == null) + { + return resource; + } + + List newSteps = new List(); + foreach (var step in model.DeploymentSteps) + { + DeploymentStepResource oldStep = resource.FindStep(step.Name); + newSteps.Add(await new DeploymentStepResource().UpdateWith(step, repository, oldStep)); + } + + // Replace deployment steps + resource.Steps.Clear(); + foreach (DeploymentStepResource deploymentStep in newSteps) + { + resource.Steps.Add(deploymentStep); + } + + return resource; + } + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader/Converters/RunbookRetentionPeriodConverter.cs b/OctopusProjectBuilder.Uploader/Converters/RunbookRetentionPeriodConverter.cs new file mode 100644 index 0000000..4da8538 --- /dev/null +++ b/OctopusProjectBuilder.Uploader/Converters/RunbookRetentionPeriodConverter.cs @@ -0,0 +1,26 @@ +using Octopus.Client.Model; +using OctopusProjectBuilder.Model; +using RunbookRetentionPeriod = OctopusProjectBuilder.Model.RunbookRetentionPeriod; + +namespace OctopusProjectBuilder.Uploader.Converters +{ + public static class RunbookRetentionPeriodConverter { + public static RunbookRetentionPeriod ToModel(this Octopus.Client.Model.RunbookRetentionPeriod period) + { + return new RunbookRetentionPeriod() + { + ShouldKeepForever = period.ShouldKeepForever, + QuantityToKeep = period.QuantityToKeep + }; + } + + public static Octopus.Client.Model.RunbookRetentionPeriod FromModel(this RunbookRetentionPeriod model) + { + return new Octopus.Client.Model.RunbookRetentionPeriod() + { + ShouldKeepForever = model.ShouldKeepForever, + QuantityToKeep = model.QuantityToKeep + }; + } + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader/ModelDownloader.cs b/OctopusProjectBuilder.Uploader/ModelDownloader.cs index 5fea32f..33a37f8 100644 --- a/OctopusProjectBuilder.Uploader/ModelDownloader.cs +++ b/OctopusProjectBuilder.Uploader/ModelDownloader.cs @@ -32,6 +32,11 @@ public async Task DownloadModel(string projectName = null) (await _repository.Channels.FindMany(c => projects.Any(p => p.Id == c.ProjectId))).ToList() : (await _repository.Channels.FindAll()).ToList(); + List runbooks; + runbooks = projectName != null ? + (await _repository.Runbooks.FindMany(c => projects.Any(p => p.Id == c.ProjectId))).ToList() : + (await _repository.Runbooks.FindAll()).ToList(); + return new SystemModel( await Task.WhenAll((await _repository.MachinePolicies.FindMany(x => false)).Select(ReadMachinePolicy)), await Task.WhenAll((await _repository.Lifecycles.FindAll()).Select(ReadLifecycle)), @@ -43,7 +48,8 @@ await Task.WhenAll((await _repository.Environments.FindAll()).Select(ReadEnviron await Task.WhenAll((await _repository.UserRoles.FindMany(x => false)).Select(ReadUserRole)), await Task.WhenAll((await _repository.Teams.FindMany(x => false)).Select(ReadTeam)), await Task.WhenAll((await _repository.Tenants.FindMany(x => false)).Select(ReadTenant)), - await Task.WhenAll((await _repository.TagSets.FindMany(x => false)).Select(ReadTagSet))); + await Task.WhenAll((await _repository.TagSets.FindMany(x => false)).Select(ReadTagSet)), + await Task.WhenAll(runbooks.Select(ReadRunbook))); } private async Task ReadMachinePolicy(MachinePolicyResource resource) @@ -111,5 +117,11 @@ private async Task ReadTagSet(TagSetResource resource) _logger.LogInformation($"Downloading {nameof(TagSetResource)}: {resource.Name}"); return await Task.FromResult(resource.ToModel(_repository)); } + + private async Task ReadRunbook(RunbookResource resource) + { + _logger.LogInformation($"Downloading {nameof(Runbook)}: {resource.Name}"); + return await resource.ToModel(_repository); + } } } diff --git a/OctopusProjectBuilder.Uploader/ModelUploader.cs b/OctopusProjectBuilder.Uploader/ModelUploader.cs index 56eb2aa..f412f13 100644 --- a/OctopusProjectBuilder.Uploader/ModelUploader.cs +++ b/OctopusProjectBuilder.Uploader/ModelUploader.cs @@ -46,6 +46,9 @@ public async Task UploadModel(SystemModel model) foreach (var project in model.Projects) await UploadProject(project); + foreach (var runbook in model.Runbooks) + await UploadRunbook(runbook); + foreach (var channel in model.Channels) await UploadChannel(channel); @@ -124,6 +127,21 @@ await Update( } } + private async Task UploadRunbook(Runbook runbook) + { + var runbookResource = await LoadResource(_repository.Runbooks, runbook.Identifier); + await runbookResource.UpdateWith(runbook, _repository); + var response = await Upsert(_repository.Runbooks, runbookResource); + runbookResource.RunbookProcessId = response.RunbookProcessId; + + var runbookProcessResource = await _repository.RunbookProcesses.Get(runbookResource.RunbookProcessId); + await runbookProcessResource.UpdateWith(runbook.Process, _repository); + + await Update(_repository.RunbookProcesses, + runbookProcessResource, + runbookResource.Name); + } + private async Task UploadChannel(Channel channel) { var projectResource = await LoadResource(_repository.Projects, new ElementIdentifier(channel.ProjectName)); diff --git a/OctopusProjectBuilder.YamlReader/Model/Templates/YamlRunbookTemplate.cs b/OctopusProjectBuilder.YamlReader/Model/Templates/YamlRunbookTemplate.cs new file mode 100644 index 0000000..9d80ec2 --- /dev/null +++ b/OctopusProjectBuilder.YamlReader/Model/Templates/YamlRunbookTemplate.cs @@ -0,0 +1,19 @@ +using System; +using System.ComponentModel; +using YamlDotNet.Serialization; + +namespace OctopusProjectBuilder.YamlReader.Model.Templates +{ + [Description("Runbook Template model definition.")] + [Serializable] + public class YamlRunbookTemplate : YamlRunbook, IYamlTemplate + { + [Description("Unique template name.")] + [YamlMember(Order = -2)] + public string TemplateName { get; set; } + + [Description("List of template parameters, where accepted names should consist of alphanumeric characters and/or underscores. If template is not parameterized, the list should be left empty or undefined.")] + [YamlMember(Order = -1)] + public string[] TemplateParameters { get; set; } + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.YamlReader/Model/Templates/YamlTemplates.cs b/OctopusProjectBuilder.YamlReader/Model/Templates/YamlTemplates.cs index 995d772..940fa5e 100644 --- a/OctopusProjectBuilder.YamlReader/Model/Templates/YamlTemplates.cs +++ b/OctopusProjectBuilder.YamlReader/Model/Templates/YamlTemplates.cs @@ -43,6 +43,8 @@ public class YamlTemplates public YamlDeploymentStepTemplate[] DeploymentSteps { get; set; } [Description("List of Project templates")] public YamlProjectTemplate[] Projects { get; set; } + [Description("List of Runbook templates")] + public YamlRunbookTemplate[] Runbooks { get; set; } public static YamlTemplates MergeIn(YamlTemplates dst, YamlTemplates src) { @@ -53,6 +55,7 @@ public static YamlTemplates MergeIn(YamlTemplates dst, YamlTemplates src) dst.DeploymentActions = dst.MergeItemsIn(src, x => x.DeploymentActions); dst.DeploymentSteps = dst.MergeItemsIn(src, x => x.DeploymentSteps); dst.Projects = dst.MergeItemsIn(src, x => x.Projects); + dst.Runbooks = dst.MergeItemsIn(src, x => x.Runbooks); return dst; } } diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentConnectivityPolicy.cs b/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentConnectivityPolicy.cs new file mode 100644 index 0000000..053ba63 --- /dev/null +++ b/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentConnectivityPolicy.cs @@ -0,0 +1,52 @@ +using System; +using System.ComponentModel; +using System.Linq; +using OctopusProjectBuilder.Model; +using OctopusProjectBuilder.YamlReader.Helpers; +using OctopusProjectBuilder.YamlReader.Model.Templates; +using YamlDotNet.Serialization; + +namespace OctopusProjectBuilder.YamlReader.Model +{ + [Serializable] + public class YamlDeploymentConnectivityPolicy + { + [Description("Skip machine behavior.")] + [YamlMember(Order = 1)] + public SkipMachineBehavior SkipMachineBehavior { get; set; } + + [Description("Target roles.")] + [YamlMember(Order = 2)] + public string[] TargetRoles { get; set; } + + [Description("Allow deployments to no targets.")] + [YamlMember(Order = 3)] + public bool AllowDeploymentsToNoTargets { get; set; } + + [Description("Exclude unhealthy targets.")] + [YamlMember(Order = 4)] + public bool ExcludeUnhealthyTargets { get; set; } + + public static YamlDeploymentConnectivityPolicy FromModel(DeploymentConnectivityPolicy model) + { + return new YamlDeploymentConnectivityPolicy + { + SkipMachineBehavior = (SkipMachineBehavior) model.SkipMachineBehavior, + TargetRoles = model.TargetRoles.Select(r => r.Name).ToArray().NullIfEmpty(), + AllowDeploymentsToNoTargets = model.AllowDeploymentsToNoTargets, + ExcludeUnhealthyTargets = model.ExcludeUnhealthyTargets + }; + } + + public DeploymentConnectivityPolicy ToModel() + { + return new DeploymentConnectivityPolicy + { + SkipMachineBehavior = (Octopus.Client.Model.SkipMachineBehavior) SkipMachineBehavior, + TargetRoles = TargetRoles?.Select(t => new ElementReference(t)).ToArray(), + AllowDeploymentsToNoTargets = AllowDeploymentsToNoTargets, + ExcludeUnhealthyTargets = ExcludeUnhealthyTargets + }; + } + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlOctopusModel.cs b/OctopusProjectBuilder.YamlReader/Model/YamlOctopusModel.cs index 6bfa293..1f75d3e 100644 --- a/OctopusProjectBuilder.YamlReader/Model/YamlOctopusModel.cs +++ b/OctopusProjectBuilder.YamlReader/Model/YamlOctopusModel.cs @@ -13,7 +13,10 @@ public class YamlOctopusModel { public YamlOctopusModel() { } - private YamlOctopusModel(YamlMachinePolicy[] machinePolicies, YamlEnvironment[] environments, YamlProjectGroup[] projectGroups, YamlProject[] projects, YamlChannel[] channels, YamlLifecycle[] lifecycles, YamlLibraryVariableSet[] libraryVariableSets, YamlUserRole[] userRoles, YamlTeam[] teams, YamlTenant[] tenants, YamlTagSet[] tagsets) + private YamlOctopusModel(YamlMachinePolicy[] machinePolicies, YamlEnvironment[] environments, + YamlProjectGroup[] projectGroups, YamlProject[] projects, YamlChannel[] channels, YamlLifecycle[] lifecycles, + YamlLibraryVariableSet[] libraryVariableSets, YamlUserRole[] userRoles, YamlTeam[] teams, YamlTenant[] tenants, + YamlTagSet[] tagsets, YamlRunbook[] runbooks) { MachinePolicies = machinePolicies; Environments = environments; @@ -26,6 +29,7 @@ private YamlOctopusModel(YamlMachinePolicy[] machinePolicies, YamlEnvironment[] Teams = teams; Tenants = tenants; TagSets = tagsets; + Runbooks = runbooks; } [Description("List of Machine Policies.")] @@ -50,6 +54,8 @@ private YamlOctopusModel(YamlMachinePolicy[] machinePolicies, YamlEnvironment[] public YamlTenant[] Tenants { get; set; } [Description("List of tagsets")] public YamlTagSet[] TagSets { get; set; } + [Description("List of runbooks")] + public YamlRunbook[] Runbooks { get; set; } [Description("Templates node allowing to define templates for other octopus model elements.")] public YamlTemplates Templates { get; set; } @@ -95,6 +101,9 @@ public SystemModelBuilder BuildWith(SystemModelBuilder builder) foreach (var tagset in TagSets.EnsureNotNull()) builder.AddTagSet(tagset.ToModel()); + + foreach (var runbook in Runbooks.EnsureNotNull()) + builder.AddRunbook(runbook.ToModel()); return builder; } @@ -112,7 +121,8 @@ public static YamlOctopusModel FromModel(SystemModel model) model.UserRoles.Select(YamlUserRole.FromModel).ToArray().NullIfEmpty(), model.Teams.Select(YamlTeam.FromModel).ToArray().NullIfEmpty(), model.Tenants.Select(YamlTenant.FromModel).ToArray().NullIfEmpty(), - model.TagSets.Select(YamlTagSet.FromModel).ToArray().NullIfEmpty()); + model.TagSets.Select(YamlTagSet.FromModel).ToArray().NullIfEmpty(), + model.Runbooks.Select(YamlRunbook.FromModel).ToArray().NullIfEmpty()); } public YamlOctopusModel MergeIn(YamlOctopusModel model) @@ -128,6 +138,7 @@ public YamlOctopusModel MergeIn(YamlOctopusModel model) Teams = this.MergeItemsIn(model, x => x.Teams); Tenants = this.MergeItemsIn(model, x => x.Tenants); TagSets = this.MergeItemsIn(model, x => x.TagSets); + Runbooks = this.MergeItemsIn(model, x => x.Runbooks); Templates = YamlTemplates.MergeIn(Templates, model.Templates); return this; } diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlRunbook.cs b/OctopusProjectBuilder.YamlReader/Model/YamlRunbook.cs new file mode 100644 index 0000000..f48d4af --- /dev/null +++ b/OctopusProjectBuilder.YamlReader/Model/YamlRunbook.cs @@ -0,0 +1,96 @@ +using System; +using System.ComponentModel; +using System.Linq; +using Octopus.Client.Model; +using OctopusProjectBuilder.Model; +using OctopusProjectBuilder.YamlReader.Helpers; +using OctopusProjectBuilder.YamlReader.Model.Templates; +using YamlDotNet.Serialization; +using GuidedFailureMode = OctopusProjectBuilder.Model.GuidedFailureMode; +using RunbookEnvironmentScope = OctopusProjectBuilder.Model.RunbookEnvironmentScope; +using RunbookRetentionPeriod = OctopusProjectBuilder.Model.RunbookRetentionPeriod; +using TenantedDeploymentMode = OctopusProjectBuilder.Model.TenantedDeploymentMode; + +namespace OctopusProjectBuilder.YamlReader.Model +{ + [Description("Octopus Runbook model.")] + [Serializable] + public class YamlRunbook : YamlNamedElement, IYamlTemplateBased + { + [Description("Indicates that the resource is template based.")] + [YamlMember(Order = 3)] + public YamlTemplateReference UseTemplate { get; set; } + + [Description("Description.")] + [YamlMember(Order = 4)] + public string Description { get; set; } + + [Description("Project name.")] + [YamlMember(Order = 5)] + public string ProjectName { get; set; } + + [Description("Runbook process definition.")] + [YamlMember(Order = 6)] + public YamlRunbookProcess Process { get; set; } + + [Description("Runbook environment scope.")] + [YamlMember(Order = 7)] + public RunbookEnvironmentScope? EnvironmentScope { get; set; } + + [Description("List of Environment references (based on the name) where runbook would be performed on.")] + [YamlMember(Order = 8)] + public string[] EnvironmentRefs { get; set; } + + [Description("Runbook tenanted deployment mode.")] + [YamlMember(Order = 9)] + public TenantedDeploymentMode? MultiTenancyMode { get; set; } + + [Description("Runbook retention policy.")] + [YamlMember(Order = 10)] + public RunbookRetentionPeriod RetentionPolicy { get; set; } + + [Description("Runbook guided failure mode.")] + [YamlMember(Order = 11)] + public GuidedFailureMode? GuidedFailureMode { get; set; } + + public void ApplyTemplate(YamlTemplates templates) + { + this.ApplyTemplate(templates?.Runbooks); + foreach (var step in (Process?.Steps).EnsureNotNull()) + step.ApplyTemplate(templates); + } + + public Runbook ToModel() + { + return new Runbook() + { + Identifier = ToModelName(), + ProjectName = ProjectName, + Description = Description, + Process = Process?.ToModel(), + MultiTenancyMode = MultiTenancyMode, + EnvironmentScope = EnvironmentScope, + RunRetentionPolicy = RetentionPolicy, + GuidedFailureMode = GuidedFailureMode, + EnvironmentRefs = EnvironmentRefs.EnsureNotNull().Select(name => new ElementReference(name)) + }; + } + + public static YamlRunbook FromModel(Runbook model) + { + return new YamlRunbook + { + Name = model.Identifier.Name, + RenamedFrom = model.Identifier.RenamedFrom, + ProjectName = model.ProjectName, + Description = model.Description, + Process = YamlRunbookProcess.FromModel(model.Process), + MultiTenancyMode = model.MultiTenancyMode, + EnvironmentScope = model.EnvironmentScope, + RetentionPolicy = model.RunRetentionPolicy, + GuidedFailureMode = model.GuidedFailureMode, + EnvironmentRefs = model.EnvironmentRefs.Select(r => r.Name).ToArray().NullIfEmpty() + }; + } + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlRunbookProcess.cs b/OctopusProjectBuilder.YamlReader/Model/YamlRunbookProcess.cs new file mode 100644 index 0000000..d90b0b7 --- /dev/null +++ b/OctopusProjectBuilder.YamlReader/Model/YamlRunbookProcess.cs @@ -0,0 +1,28 @@ +using System; +using System.ComponentModel; +using System.Linq; +using OctopusProjectBuilder.Model; + +namespace OctopusProjectBuilder.YamlReader.Model +{ + [Description("Runbook deployment process definition.")] + [Serializable] + public class YamlRunbookProcess + { + [Description("List of steps to execute.")] + public YamlDeploymentStep[] Steps { get; set; } + + public RunbookProcess ToModel() + { + return new RunbookProcess(Steps.Select(s => s.ToModel())); + } + + public static YamlRunbookProcess FromModel(RunbookProcess model) + { + return new YamlRunbookProcess + { + Steps = model.DeploymentSteps.Select(YamlDeploymentStep.FromModel).ToArray() + }; + } + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.YamlReader/YamlSystemModelRepository.cs b/OctopusProjectBuilder.YamlReader/YamlSystemModelRepository.cs index 4fb47cf..a2e35f8 100644 --- a/OctopusProjectBuilder.YamlReader/YamlSystemModelRepository.cs +++ b/OctopusProjectBuilder.YamlReader/YamlSystemModelRepository.cs @@ -83,6 +83,7 @@ private string GetModelPath(YamlOctopusModel splitModel, string modelDirectory) .Concat(splitModel.Teams.EnsureNotNull().Select(x => $"Team_{x.Name.SanitiseNameIfNeeded()}.yml")) .Concat(splitModel.Tenants.EnsureNotNull().Select(x => $"Tenant_{x.Name.SanitiseNameIfNeeded()}.yml")) .Concat(splitModel.TagSets.EnsureNotNull().Select(x => $"TagSet_{x.Name.SanitiseNameIfNeeded()}.yml")) + .Concat(splitModel.Runbooks.EnsureNotNull().Select(x => $"Runbook_{x.ProjectName.SanitiseNameIfNeeded()}_{x.Name.SanitiseNameIfNeeded()}.yml")) .Single(); return Path.Combine(modelDirectory, name); } From 902fdba6aaed1741a1fbbe2efe2dbf81cb27bfad Mon Sep 17 00:00:00 2001 From: "matthew.marchus" Date: Tue, 20 Jul 2021 11:44:34 -0600 Subject: [PATCH 42/49] EAG-887: Fix order of YAML definitions --- .../Model/YamlRunbook.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlRunbook.cs b/OctopusProjectBuilder.YamlReader/Model/YamlRunbook.cs index f48d4af..3e0536d 100644 --- a/OctopusProjectBuilder.YamlReader/Model/YamlRunbook.cs +++ b/OctopusProjectBuilder.YamlReader/Model/YamlRunbook.cs @@ -29,30 +29,30 @@ public class YamlRunbook : YamlNamedElement, IYamlTemplateBased [YamlMember(Order = 5)] public string ProjectName { get; set; } - [Description("Runbook process definition.")] - [YamlMember(Order = 6)] - public YamlRunbookProcess Process { get; set; } - [Description("Runbook environment scope.")] - [YamlMember(Order = 7)] + [YamlMember(Order = 6)] public RunbookEnvironmentScope? EnvironmentScope { get; set; } [Description("List of Environment references (based on the name) where runbook would be performed on.")] - [YamlMember(Order = 8)] + [YamlMember(Order = 7)] public string[] EnvironmentRefs { get; set; } [Description("Runbook tenanted deployment mode.")] - [YamlMember(Order = 9)] + [YamlMember(Order = 8)] public TenantedDeploymentMode? MultiTenancyMode { get; set; } [Description("Runbook retention policy.")] - [YamlMember(Order = 10)] + [YamlMember(Order = 9)] public RunbookRetentionPeriod RetentionPolicy { get; set; } [Description("Runbook guided failure mode.")] - [YamlMember(Order = 11)] + [YamlMember(Order = 10)] public GuidedFailureMode? GuidedFailureMode { get; set; } + [Description("Runbook process definition.")] + [YamlMember(Order = 11)] + public YamlRunbookProcess Process { get; set; } + public void ApplyTemplate(YamlTemplates templates) { this.ApplyTemplate(templates?.Runbooks); From c504934ebf0e2f5918d6c6dda41606663a37eeb7 Mon Sep 17 00:00:00 2001 From: "matthew.marchus" Date: Wed, 21 Jul 2021 11:44:46 -0600 Subject: [PATCH 43/49] EAG-919: Support package references! --- .../ChannelVersionRule.cs | 4 +- .../ChannelVersionRulePackage.cs | 16 +++++++ .../DeploymentAction.cs | 5 +- .../DeploymentActionPackage.cs | 15 +++--- ...s => ChannelVersionRulePackageConveter.cs} | 8 ++-- .../Converters/DeploymentActionConverter.cs | 39 +++++++++++++-- .../DeploymentActionPackageConverter.cs | 42 ++++++++++++++++ .../Converters/PropertyValueConverter.cs | 18 ++++++- .../Model/YamlChannelVersionRule.cs | 4 +- .../Model/YamlChannelVersionRulePackage.cs | 35 ++++++++++++++ .../Model/YamlDeploymentAction.cs | 9 +++- .../Model/YamlDeploymentActionPackage.cs | 48 ++++++++++++++----- 12 files changed, 206 insertions(+), 37 deletions(-) create mode 100644 OctopusProjectBuilder.Model/ChannelVersionRulePackage.cs rename OctopusProjectBuilder.Uploader/Converters/{DeploymentActionPackageConveter.cs => ChannelVersionRulePackageConveter.cs} (53%) create mode 100644 OctopusProjectBuilder.Uploader/Converters/DeploymentActionPackageConverter.cs create mode 100644 OctopusProjectBuilder.YamlReader/Model/YamlChannelVersionRulePackage.cs diff --git a/OctopusProjectBuilder.Model/ChannelVersionRule.cs b/OctopusProjectBuilder.Model/ChannelVersionRule.cs index 32812ef..4574993 100644 --- a/OctopusProjectBuilder.Model/ChannelVersionRule.cs +++ b/OctopusProjectBuilder.Model/ChannelVersionRule.cs @@ -5,7 +5,7 @@ namespace OctopusProjectBuilder.Model { public class ChannelVersionRule { - public ChannelVersionRule(string tag, string versionRange, IEnumerable actionPackages) + public ChannelVersionRule(string tag, string versionRange, IEnumerable actionPackages) { Tag = tag; VersionRange = versionRange; @@ -14,6 +14,6 @@ public ChannelVersionRule(string tag, string versionRange, IEnumerable ActionPackages { get; set; } + public IEnumerable ActionPackages { get; set; } } } \ No newline at end of file diff --git a/OctopusProjectBuilder.Model/ChannelVersionRulePackage.cs b/OctopusProjectBuilder.Model/ChannelVersionRulePackage.cs new file mode 100644 index 0000000..0bf7019 --- /dev/null +++ b/OctopusProjectBuilder.Model/ChannelVersionRulePackage.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.Model/DeploymentAction.cs b/OctopusProjectBuilder.Model/DeploymentAction.cs index a0eec34..3a7b9b9 100644 --- a/OctopusProjectBuilder.Model/DeploymentAction.cs +++ b/OctopusProjectBuilder.Model/DeploymentAction.cs @@ -11,10 +11,12 @@ public class DeploymentAction public string ActionType { get; } public IReadOnlyDictionary Properties { get; } public IEnumerable EnvironmentRefs { get; } + public IEnumerable Packages { get; } public DeploymentAction(string name, bool isDisabled, ActionCondition condition, string actionType, IReadOnlyDictionary properties, - IEnumerable environmentRefs) + IEnumerable environmentRefs, + IEnumerable packages) { Name = name; IsDisabled = isDisabled; @@ -22,6 +24,7 @@ public DeploymentAction(string name, bool isDisabled, ActionCondition condition, ActionType = actionType; Properties = properties; EnvironmentRefs = environmentRefs.ToArray(); + Packages = packages.ToArray(); } public enum ActionCondition diff --git a/OctopusProjectBuilder.Model/DeploymentActionPackage.cs b/OctopusProjectBuilder.Model/DeploymentActionPackage.cs index c459c1a..222bb12 100644 --- a/OctopusProjectBuilder.Model/DeploymentActionPackage.cs +++ b/OctopusProjectBuilder.Model/DeploymentActionPackage.cs @@ -1,16 +1,13 @@ -using Octopus.Client.Model; +using System.Collections.Generic; namespace OctopusProjectBuilder.Model { public class DeploymentActionPackage { - public string DeploymentAction { get; set; } - public string PackageReference { get; set; } - - public DeploymentActionPackage(string deploymentAction, string packageReference) - { - DeploymentAction = deploymentAction; - PackageReference = packageReference; - } + public string Name { get; set; } + public string PackageId { get; set; } + public string FeedId { get; set; } + public string AcquisitionLocation { get; set; } + public IDictionary Properties { get; set; } } } \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionPackageConveter.cs b/OctopusProjectBuilder.Uploader/Converters/ChannelVersionRulePackageConveter.cs similarity index 53% rename from OctopusProjectBuilder.Uploader/Converters/DeploymentActionPackageConveter.cs rename to OctopusProjectBuilder.Uploader/Converters/ChannelVersionRulePackageConveter.cs index d39e71d..7d14132 100644 --- a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionPackageConveter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/ChannelVersionRulePackageConveter.cs @@ -6,14 +6,14 @@ namespace OctopusProjectBuilder.Uploader.Converters { - public static class DeploymentActionPackageConveter + public static class ChannelVersionRulePackageConveter { - public static async Task ToModel(this DeploymentActionPackageResource resource, IOctopusAsyncRepository repository) + public static async Task ToModel(this DeploymentActionPackageResource resource, IOctopusAsyncRepository repository) { - return new DeploymentActionPackage(resource.DeploymentAction, resource.PackageReference); + return new ChannelVersionRulePackage(resource.DeploymentAction, resource.PackageReference); } - public static async Task ToResource(this DeploymentActionPackage model) + public static async Task ToResource(this ChannelVersionRulePackage model) { return new DeploymentActionPackageResource(model.DeploymentAction ?? "", model.PackageReference ?? ""); } diff --git a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs index 25b735e..a34a2e7 100644 --- a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Data; using System.Linq; using System.Threading.Tasks; +using Newtonsoft.Json; using Octopus.Client; using Octopus.Client.Exceptions; using Octopus.Client.Model; @@ -19,7 +21,8 @@ public static async Task ToModel(this DeploymentActionResource (DeploymentAction.ActionCondition)resource.Condition, resource.ActionType, resource.Properties.ToModel(), - await Task.WhenAll(resource.Environments.ToModel(repository.Environments))); + await Task.WhenAll(resource.Environments.ToModel(repository.Environments)), + await Task.WhenAll(resource.Packages.Select(reference => reference.ToModel()))); } public static async Task UpdateWith(this DeploymentActionResource resource, @@ -30,11 +33,39 @@ public static async Task UpdateWith(this DeploymentAct resource.IsDisabled = model.IsDisabled; resource.Condition = (DeploymentActionCondition) model.Condition; resource.ActionType = model.ActionType; - resource.Environments.UpdateWith(await Task.WhenAll(model.EnvironmentRefs.Select(r => repository.Environments.ResolveResourceId(r)))); + resource.Environments.UpdateWith(await Task.WhenAll(model.EnvironmentRefs + .Select(r => repository.Environments.ResolveResourceId(r)))); + + List newReferences = new List(); + foreach (var reference in model.Packages) + { + PackageReference oldReference = resource.Packages.FirstOrDefault(x => x.Name == model.Name); + newReferences.Add(await new PackageReference().UpdateWith(reference, repository, oldReference)); + } - await resource.Properties.UpdateWith(repository, model.Properties, + // Replace package references + IDictionary properties = + model.Properties.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + resource.Packages.Clear(); + foreach (PackageReference reference in newReferences) + { + resource.Packages.Add(reference); + + // Attempt to auto-generate the property for this if it's not supplied + if (reference.Properties.ContainsKey("PackageParameterName")) + { + string parameterName = reference.Properties["PackageParameterName"]; + if (!model.Properties.ContainsKey(parameterName)) + { + properties.Add(parameterName, new PropertyValue(false, JsonConvert.SerializeObject(new + {reference.PackageId, reference.FeedId}))); + } + } + } + + await resource.Properties.UpdateWith(repository, new ReadOnlyDictionary(properties), oldAction != null ? oldAction.Properties : new Dictionary()); - + switch (resource.ActionType) { case "Octopus.TentaclePackage": diff --git a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionPackageConverter.cs b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionPackageConverter.cs new file mode 100644 index 0000000..002eb84 --- /dev/null +++ b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionPackageConverter.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using Octopus.Client; +using Octopus.Client.Exceptions; +using Octopus.Client.Model; +using OctopusProjectBuilder.Model; + +namespace OctopusProjectBuilder.Uploader.Converters +{ + public static class DeploymentActionPackageConverter + { + public static async Task ToModel(this PackageReference resource) + { + return new DeploymentActionPackage() + { + Name = resource.Name, + PackageId = resource.PackageId, + FeedId = resource.FeedId, + AcquisitionLocation = resource.AcquisitionLocation, + Properties = resource.Properties + }; + } + + public static async Task UpdateWith(this PackageReference resource, + DeploymentActionPackage model, IOctopusAsyncRepository repository, + PackageReference oldReference) + { + resource.Name = model.Name; + resource.PackageId = model.PackageId; + resource.FeedId = model.FeedId; + resource.AcquisitionLocation = model.AcquisitionLocation; + resource.Properties.Clear(); + foreach (var kvp in model.Properties) + { + resource.Properties.Add(kvp.Key, kvp.Value); + } + return resource; + } + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs index 268ba8e..105bfca 100644 --- a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs @@ -47,6 +47,13 @@ public static PropertyValue ToModel(this PropertyValueResource resource) { return new PropertyValue(resource.IsSensitive,resource.Value); } + + public static Dictionary ToModel(this IDictionary properties) + { + return properties + .Select(kv => Tuple.Create(kv.Key, ToModel(kv.Value))) + .ToDictionary(kv => kv.Item1, kv => kv.Item2); + } public static Dictionary ToModel(this IDictionary properties) { @@ -55,6 +62,16 @@ public static Dictionary ToModel(this IDictionary kv.Item1, kv => kv.Item2); } + public static async Task UpdateWith(this IDictionary resource, + IReadOnlyDictionary model) + { + resource.Clear(); + foreach (var keyValuePair in model) + { + resource.Add(keyValuePair.Key, keyValuePair.Value); + } + } + public static async Task UpdateWith(this IDictionary resource, IOctopusAsyncRepository repository, IReadOnlyDictionary model, @@ -144,7 +161,6 @@ public static async Task UpdateWith(this IDictionary YamlDeploymentActionPackage.FromModel(package)).ToArray() + ActionPackages = model.ActionPackages.Select(package => YamlChannelVersionRulePackage.FromModel(package)).ToArray() }; } diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlChannelVersionRulePackage.cs b/OctopusProjectBuilder.YamlReader/Model/YamlChannelVersionRulePackage.cs new file mode 100644 index 0000000..134458a --- /dev/null +++ b/OctopusProjectBuilder.YamlReader/Model/YamlChannelVersionRulePackage.cs @@ -0,0 +1,35 @@ +using System; +using System.ComponentModel; +using OctopusProjectBuilder.Model; +using YamlDotNet.Serialization; + +namespace OctopusProjectBuilder.YamlReader.Model +{ + [Description("Octopus Channel model.")] + [Serializable] + public class YamlChannelVersionRulePackage + { + [Description("Deployment action name.")] + [YamlMember(Order = 1)] + public string DeploymentAction { get; set; } + + [Description("Package reference.")] + [YamlMember(Order = 2)] + public string PackageReference { get; set; } + + public static YamlChannelVersionRulePackage FromModel(ChannelVersionRulePackage model) + { + return new YamlChannelVersionRulePackage() + { + DeploymentAction = string.IsNullOrEmpty(model.DeploymentAction) ? null : model.DeploymentAction, + PackageReference = string.IsNullOrEmpty(model.PackageReference) ? null : model.PackageReference + }; + } + + public ChannelVersionRulePackage ToModel() + { + return new ChannelVersionRulePackage(DeploymentAction, PackageReference); + } + + } +} \ No newline at end of file diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentAction.cs b/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentAction.cs index 911015b..c8f9367 100644 --- a/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentAction.cs +++ b/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentAction.cs @@ -36,9 +36,13 @@ public class YamlDeploymentAction : IYamlTemplateBased [Description("List of Environment references (based on the name) where action would be performed on. If none are specified, then action would be performed on all environments.")] [YamlMember(Order = 6)] public string[] EnvironmentRefs { get; set; } + + [Description("Action package references.")] + [YamlMember(Order = 7)] + public YamlDeploymentActionPackage[] Packages { get; set; } [Description("Action properties.")] - [YamlMember(Order = 7)] + [YamlMember(Order = 8)] public YamlPropertyValue[] Properties { get; set; } public void ApplyTemplate(YamlTemplates templates) @@ -63,7 +67,8 @@ public DeploymentAction ToModel() { return new DeploymentAction(Name, IsDisabled, Condition, ActionType, YamlPropertyValue.ToModel(Properties), - EnvironmentRefs.EnsureNotNull().Select(name => new ElementReference(name))); + EnvironmentRefs.EnsureNotNull().Select(name => new ElementReference(name)), + Packages?.Select(x => x.ToModel())); } } } \ No newline at end of file diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentActionPackage.cs b/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentActionPackage.cs index 0814956..f65f1f1 100644 --- a/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentActionPackage.cs +++ b/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentActionPackage.cs @@ -1,35 +1,59 @@ -using System; +using System; +using System.Collections.Generic; using System.ComponentModel; +using System.Linq; using OctopusProjectBuilder.Model; +using OctopusProjectBuilder.YamlReader.Helpers; +using OctopusProjectBuilder.YamlReader.Model.Templates; using YamlDotNet.Serialization; namespace OctopusProjectBuilder.YamlReader.Model { - [Description("Octopus Channel model.")] [Serializable] public class YamlDeploymentActionPackage { - [Description("Deployment action name.")] + [Description("Unique name.")] [YamlMember(Order = 1)] - public string DeploymentAction { get; set; } + public string Name { get; set; } - [Description("Package reference.")] + [Description("Package ID.")] [YamlMember(Order = 2)] - public string PackageReference { get; set; } - + public string PackageId { get; set; } + + [Description("Feed ID.")] + [YamlMember(Order = 3)] + public string FeedId { get; set; } + + [Description("Acquisition location.")] + [YamlMember(Order = 4)] + public string AcquisitionLocation { get; set; } + + [Description("Properties")] + [YamlMember(Order = 5)] + public IDictionary Properties { get; set; } = new Dictionary(); + public static YamlDeploymentActionPackage FromModel(DeploymentActionPackage model) { - return new YamlDeploymentActionPackage() + return new YamlDeploymentActionPackage { - DeploymentAction = string.IsNullOrEmpty(model.DeploymentAction) ? null : model.DeploymentAction, - PackageReference = string.IsNullOrEmpty(model.PackageReference) ? null : model.PackageReference + Name = model.Name, + PackageId = model.PackageId, + FeedId = model.FeedId, + AcquisitionLocation = model.AcquisitionLocation, + Properties = model.Properties }; } public DeploymentActionPackage ToModel() { - return new DeploymentActionPackage(DeploymentAction, PackageReference); + return new DeploymentActionPackage() + { + Name = Name, + PackageId = PackageId, + FeedId = FeedId, + AcquisitionLocation = AcquisitionLocation, + Properties = Properties + }; } - } } \ No newline at end of file From 391b49d707c9e0d543d4a60ea745287573bbc7bc Mon Sep 17 00:00:00 2001 From: "matthew.marchus" Date: Wed, 21 Jul 2021 11:52:54 -0600 Subject: [PATCH 44/49] EAG-919: Fix tests --- .../ModelDownloaderTests.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs b/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs index 853d9e9..7e8ff26 100644 --- a/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs +++ b/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs @@ -211,11 +211,13 @@ public void It_should_upload_and_download_projects() new DeploymentAction(CreateItem(), CreateItem(), CreateItem(), CreateItem(), CreateItem>(), - new[] {new ElementReference("env1")}), + new[] {new ElementReference("env1")}, + Enumerable.Empty()), new DeploymentAction(CreateItem(), CreateItem(), CreateItem(), CreateItem(), CreateItem>(), - new[] {new ElementReference("env2")}) + new[] {new ElementReference("env2")}, + Enumerable.Empty()) }), new DeploymentStep(CreateItem(), CreateItem(), CreateItem(), CreateItem(), @@ -224,7 +226,8 @@ public void It_should_upload_and_download_projects() new DeploymentAction(CreateItem(), CreateItem(), CreateItem(), CreateItem(), CreateItem>(), - new[] {new ElementReference("env1")}) + new[] {new ElementReference("env1")}, + Enumerable.Empty()) }) }); var scope = new Dictionary> @@ -542,7 +545,8 @@ public void It_should_upload_and_download_tenant() new DeploymentAction(CreateItem(), CreateItem(), CreateItem(), CreateItem(), CreateItem>(), - new[] {new ElementReference("env1")}), + new[] {new ElementReference("env1")}, + Enumerable.Empty()), }) }); var scope = new Dictionary> From f1bdf36b3da362324cb4ce82d53f7e737b2df843 Mon Sep 17 00:00:00 2001 From: "matthew.marchus" Date: Wed, 21 Jul 2021 12:03:11 -0600 Subject: [PATCH 45/49] EAG-919: EnsureNotNull() --- OctopusProjectBuilder.YamlReader/Model/YamlDeploymentAction.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentAction.cs b/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentAction.cs index c8f9367..a57ddb3 100644 --- a/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentAction.cs +++ b/OctopusProjectBuilder.YamlReader/Model/YamlDeploymentAction.cs @@ -68,7 +68,7 @@ public DeploymentAction ToModel() return new DeploymentAction(Name, IsDisabled, Condition, ActionType, YamlPropertyValue.ToModel(Properties), EnvironmentRefs.EnsureNotNull().Select(name => new ElementReference(name)), - Packages?.Select(x => x.ToModel())); + Packages.EnsureNotNull().Select(x => x.ToModel())); } } } \ No newline at end of file From c6d86d5460d6f1cbfae74dc343ae4b2ce4188167 Mon Sep 17 00:00:00 2001 From: "matthew.marchus" Date: Wed, 21 Jul 2021 12:05:22 -0600 Subject: [PATCH 46/49] EAG-919: Fallbacks --- .../Converters/PropertyValueConverter.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs index 105bfca..c9421f3 100644 --- a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs @@ -86,6 +86,8 @@ public static async Task UpdateWith(this IDictionary Date: Wed, 21 Jul 2021 12:13:01 -0600 Subject: [PATCH 47/49] EAG-919: CanBeUsedForProjectVersioning --- .../Converters/DeploymentActionConverter.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs index a34a2e7..bfcffb7 100644 --- a/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/DeploymentActionConverter.cs @@ -63,6 +63,11 @@ public static async Task UpdateWith(this DeploymentAct } } + if (resource.Packages.Any()) + { + resource.CanBeUsedForProjectVersioning = true; + } + await resource.Properties.UpdateWith(repository, new ReadOnlyDictionary(properties), oldAction != null ? oldAction.Properties : new Dictionary()); From c5f75143dd701b5135f9da87320f56818167036a Mon Sep 17 00:00:00 2001 From: "matthew.marchus" Date: Thu, 22 Jul 2021 08:35:59 -0600 Subject: [PATCH 48/49] NONE: Vastly improve the downloader. Automatically normalize the downloaded YAML. --- OctopusProjectBuilder.Console/Options.cs | 1 + OctopusProjectBuilder.Console/Program.cs | 163 +++++++++++++++++- .../DeploymentAction.cs | 4 +- OctopusProjectBuilder.Model/DeploymentStep.cs | 6 +- .../ModelDownloaderTests.cs | 14 +- .../Converters/PropertyValueConverter.cs | 2 +- .../ModelDownloader.cs | 8 +- .../Model/YamlPropertyValue.cs | 4 +- .../Model/YamlTemplateVariable.cs | 1 - .../YamlSystemModelRepository.cs | 16 +- 10 files changed, 198 insertions(+), 21 deletions(-) diff --git a/OctopusProjectBuilder.Console/Options.cs b/OctopusProjectBuilder.Console/Options.cs index fe00ba7..1d28308 100644 --- a/OctopusProjectBuilder.Console/Options.cs +++ b/OctopusProjectBuilder.Console/Options.cs @@ -7,6 +7,7 @@ public enum Verb { Upload, Download, CleanupConfig, Validate } 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; } } } \ No newline at end of file diff --git a/OctopusProjectBuilder.Console/Program.cs b/OctopusProjectBuilder.Console/Program.cs index 0bb9eb2..e7cf95c 100644 --- a/OctopusProjectBuilder.Console/Program.cs +++ b/OctopusProjectBuilder.Console/Program.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Linq; using System.Net; using System.Threading.Tasks; @@ -9,6 +10,7 @@ using OctopusProjectBuilder.Model; using OctopusProjectBuilder.Uploader; using OctopusProjectBuilder.YamlReader; +using OctopusProjectBuilder.YamlReader.Model; namespace OctopusProjectBuilder.Console { @@ -64,11 +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) + var repository = await BuildRepository(options); + var model = await new ModelDownloader(repository, _loggerFactory) .DownloadModel(options.ProjectName); - new YamlSystemModelRepository(_loggerFactory).Save(model, options.DefinitionsDir); + + await new YamlSystemModelRepository(_loggerFactory).Save(model, options.DefinitionsDir, async yaml => + { + 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 BuildRepository(Options options) { return new OctopusAsyncRepository( @@ -84,6 +242,7 @@ public static Options ReadOptions(string[] args) 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); diff --git a/OctopusProjectBuilder.Model/DeploymentAction.cs b/OctopusProjectBuilder.Model/DeploymentAction.cs index 3a7b9b9..c4228d4 100644 --- a/OctopusProjectBuilder.Model/DeploymentAction.cs +++ b/OctopusProjectBuilder.Model/DeploymentAction.cs @@ -9,12 +9,12 @@ public class DeploymentAction public bool IsDisabled { get; } public ActionCondition Condition { get; } public string ActionType { get; } - public IReadOnlyDictionary Properties { get; } + public IDictionary Properties { get; } public IEnumerable EnvironmentRefs { get; } public IEnumerable Packages { get; } public DeploymentAction(string name, bool isDisabled, ActionCondition condition, - string actionType, IReadOnlyDictionary properties, + string actionType, IDictionary properties, IEnumerable environmentRefs, IEnumerable packages) { diff --git a/OctopusProjectBuilder.Model/DeploymentStep.cs b/OctopusProjectBuilder.Model/DeploymentStep.cs index 386db49..537b05c 100644 --- a/OctopusProjectBuilder.Model/DeploymentStep.cs +++ b/OctopusProjectBuilder.Model/DeploymentStep.cs @@ -10,7 +10,7 @@ public class DeploymentStep public StepCondition Condition { get; } public bool RequiresPackagesToBeAcquired { get; } public StepStartTrigger StartTrigger { get; } - public IReadOnlyDictionary Properties { get; } + public IDictionary Properties { get; } public IEnumerable Actions { get; } public enum StepCondition @@ -27,7 +27,9 @@ public enum StepStartTrigger StartWithPrevious } - public DeploymentStep(string name, StepCondition condition, bool requiresPackagesToBeAcquired, StepStartTrigger startTrigger, IReadOnlyDictionary properties, IEnumerable actions) + public DeploymentStep(string name, StepCondition condition, bool requiresPackagesToBeAcquired, + StepStartTrigger startTrigger, IDictionary properties, + IEnumerable actions) { Name = name; Condition = condition; diff --git a/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs b/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs index 7e8ff26..5b27fa8 100644 --- a/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs +++ b/OctopusProjectBuilder.Uploader.Tests/ModelDownloaderTests.cs @@ -206,26 +206,26 @@ public void It_should_upload_and_download_projects() { new DeploymentStep(CreateItem(), CreateItem(), CreateItem(), CreateItem(), - CreateItem>(), new[] + CreateItem>(), new[] { new DeploymentAction(CreateItem(), CreateItem(), CreateItem(), CreateItem(), - CreateItem>(), + CreateItem>(), new[] {new ElementReference("env1")}, Enumerable.Empty()), new DeploymentAction(CreateItem(), CreateItem(), CreateItem(), CreateItem(), - CreateItem>(), + CreateItem>(), new[] {new ElementReference("env2")}, Enumerable.Empty()) }), new DeploymentStep(CreateItem(), CreateItem(), CreateItem(), CreateItem(), - CreateItem>(), new[] + CreateItem>(), new[] { new DeploymentAction(CreateItem(), CreateItem(), CreateItem(), CreateItem(), - CreateItem>(), + CreateItem>(), new[] {new ElementReference("env1")}, Enumerable.Empty()) }) @@ -540,11 +540,11 @@ public void It_should_upload_and_download_tenant() { new DeploymentStep(CreateItem(), CreateItem(), CreateItem(), CreateItem(), - CreateItem>(), new[] + CreateItem>(), new[] { new DeploymentAction(CreateItem(), CreateItem(), CreateItem(), CreateItem(), - CreateItem>(), + CreateItem>(), new[] {new ElementReference("env1")}, Enumerable.Empty()), }) diff --git a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs index c9421f3..88c7f6c 100644 --- a/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs +++ b/OctopusProjectBuilder.Uploader/Converters/PropertyValueConverter.cs @@ -74,7 +74,7 @@ public static async Task UpdateWith(this IDictionary resource, IOctopusAsyncRepository repository, - IReadOnlyDictionary model, + IDictionary model, IDictionary oldProperties) { foreach (var keyValuePair in model) diff --git a/OctopusProjectBuilder.Uploader/ModelDownloader.cs b/OctopusProjectBuilder.Uploader/ModelDownloader.cs index 33a37f8..b30a5c9 100644 --- a/OctopusProjectBuilder.Uploader/ModelDownloader.cs +++ b/OctopusProjectBuilder.Uploader/ModelDownloader.cs @@ -40,8 +40,12 @@ public async Task DownloadModel(string projectName = null) return new SystemModel( await Task.WhenAll((await _repository.MachinePolicies.FindMany(x => false)).Select(ReadMachinePolicy)), await Task.WhenAll((await _repository.Lifecycles.FindAll()).Select(ReadLifecycle)), - await Task.WhenAll((await _repository.ProjectGroups.FindAll()).Select(ReadProjectGroup)), - await Task.WhenAll((await _repository.LibraryVariableSets.FindAll()).Select(ReadLibraryVariableSet)), + await Task.WhenAll((await _repository.ProjectGroups.FindAll()) + .Where(g => projectName == null || projects.Any(p => p.ProjectGroupId == g.Id)) + .Select(ReadProjectGroup)), + await Task.WhenAll((await _repository.LibraryVariableSets.FindAll()) + .Where(v => projectName == null || projects.Any(p => p.IncludedLibraryVariableSetIds.Contains(v.Id))) + .Select(ReadLibraryVariableSet)), await Task.WhenAll(projects.Select(ReadProject)), await Task.WhenAll(channels.Select(ReadChannel)), await Task.WhenAll((await _repository.Environments.FindAll()).Select(ReadEnvironment)), diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlPropertyValue.cs b/OctopusProjectBuilder.YamlReader/Model/YamlPropertyValue.cs index 705edd3..ac58ad9 100644 --- a/OctopusProjectBuilder.YamlReader/Model/YamlPropertyValue.cs +++ b/OctopusProjectBuilder.YamlReader/Model/YamlPropertyValue.cs @@ -32,12 +32,12 @@ public class YamlPropertyValue [YamlMember(Order = 4)] public bool IsSensitive { get; set; } - public static IReadOnlyDictionary ToModel(YamlPropertyValue[] properties) + public static IDictionary ToModel(YamlPropertyValue[] properties) { return properties.EnsureNotNull().ToDictionary(kv => kv.Key, kv => new PropertyValue(kv.IsSensitive, kv.File != null ? System.IO.File.ReadAllText(kv.File) : kv.Value, kv.ValueType)); } - public static YamlPropertyValue[] FromModel(IReadOnlyDictionary properties) + public static YamlPropertyValue[] FromModel(IDictionary properties) { return properties.EnsureNotNull().Select(kv => new YamlPropertyValue { IsSensitive = kv.Value.IsSensitive, Value = kv.Value.Value, ValueType = kv.Value.ValueType, Key = kv.Key }).ToArray().NullIfEmpty(); } diff --git a/OctopusProjectBuilder.YamlReader/Model/YamlTemplateVariable.cs b/OctopusProjectBuilder.YamlReader/Model/YamlTemplateVariable.cs index aa37a0c..c5c8de4 100644 --- a/OctopusProjectBuilder.YamlReader/Model/YamlTemplateVariable.cs +++ b/OctopusProjectBuilder.YamlReader/Model/YamlTemplateVariable.cs @@ -14,7 +14,6 @@ public class YamlTemplateVariable { [Description("Template Id")] [YamlMember(Order = 1)] - [DefaultValue("")] public string Id { get; set; } = ""; [Description("Variable name.")] diff --git a/OctopusProjectBuilder.YamlReader/YamlSystemModelRepository.cs b/OctopusProjectBuilder.YamlReader/YamlSystemModelRepository.cs index a2e35f8..c83b501 100644 --- a/OctopusProjectBuilder.YamlReader/YamlSystemModelRepository.cs +++ b/OctopusProjectBuilder.YamlReader/YamlSystemModelRepository.cs @@ -1,6 +1,8 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; using OctopusProjectBuilder.Model; using OctopusProjectBuilder.YamlReader.Helpers; @@ -28,6 +30,11 @@ public SystemModel Load(string modelDirectory) return model.ApplyTemplates().BuildWith(new SystemModelBuilder()).Build(); } + public void Save(SystemModel model, string modelDirectory) + { + Save(model, modelDirectory, async yaml => { }); + } + private YamlOctopusModel[] LoadModels(string path) { _logger.LogInformation($"Loading: {Path.GetFileName(path)}"); @@ -51,11 +58,16 @@ private YamlOctopusModel[] ReadFile(string file) return _reader.Read(stream); } - public void Save(SystemModel model, string modelDirectory) + public async Task Save(SystemModel model, string modelDirectory, Func modelManipulator) { Directory.CreateDirectory(modelDirectory); foreach (var splitModel in model.SplitModel().Select(YamlOctopusModel.FromModel)) + { + if (splitModel != null) + await modelManipulator(splitModel); + SaveModel(splitModel, GetModelPath(splitModel, modelDirectory)); + } } private void SaveModel(YamlOctopusModel model, string path) From 9b5eca5c128ec62c84f7f4971fe748c3ac0945d2 Mon Sep 17 00:00:00 2001 From: Matt Marchus <54286304+matthewmarchus@users.noreply.github.com> Date: Fri, 23 Jul 2021 10:57:53 -0600 Subject: [PATCH 49/49] Update README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 17f9eb3..45fc2c4 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,10 @@ To see the yaml configuration manual, please take a look at [Configuration Manua * [Project Groups](https://github.com/Suremaker/OctopusProjectBuilder/wiki/Manual#YamlProjectGroup) * [Projects](https://github.com/Suremaker/OctopusProjectBuilder/wiki/Manual#YamlProject) + * Variables + * Variable Templates + * Runbooks + * Release Channels * [Lifecycles](https://github.com/Suremaker/OctopusProjectBuilder/wiki/Manual#YamlLifecycle) * [Library Variable Sets](https://github.com/Suremaker/OctopusProjectBuilder/wiki/Manual#YamlLibraryVariableSet) \(including script modules\) * [Environments](https://github.com/Suremaker/OctopusProjectBuilder/wiki/Manual#YamlEnvironment) @@ -34,6 +38,7 @@ OctopusProjectBuilder allows template definition for most repetative configurati * Projects - see [Project Templates](https://github.com/Suremaker/OctopusProjectBuilder/wiki/Manual#YamlProjectTemplate) * Project Steps - see [Deployment Step Templates](https://github.com/Suremaker/OctopusProjectBuilder/wiki/Manual#YamlDeploymentStepTemplate) * Project Step Actions - see [Deployment Action Templates](https://github.com/Suremaker/OctopusProjectBuilder/wiki/Manual#YamlDeploymentActionTemplate) +* Project Runbooks It is possible to create parameterized templates with ability to parameterize any properties of string type. It is also possible to use templates for lower configuration sections in template of higher configuration sections. For example The Project template can use Step templates that can be composed from Action templates. @@ -49,8 +54,11 @@ The OctopusProjectBuilder.Console.exe requires following paramters: |d:definitions|Definitions directory| |k:octopusApiKey|Octopus API key| |u:octopusUrl|Octopus Url| +|p:projectName|Project name to restrict download for| +|n:normalize|Normalize downloaded YAML and split out scripts into unique files| The **download** action allows to download the current Octopus configuration to the target directory, +the **validate** action allows to validate (stage) the downloaded YAML configuration in an existing directory, the **upload** action allows to apply the yaml configuration on Octopus server, while the **cleanupConfig** action allows to rewrite the configuration, and it is helpful to reorder nodes, reformats parameters or remove the entries with default values.