Example #1
0
        async Task <IReadOnlyList <DeploymentResource> > DeployToEnvironments(ProjectResource project, ReleaseResource release)
        {
            if (DeployToEnvironmentNamesOrIds.Count == 0)
            {
                return(new List <DeploymentResource>());
            }

            var releaseTemplate = await Repository.Releases.GetTemplate(release).ConfigureAwait(false);

            var deployToEnvironments = await Repository.Environments
                                       .FindByNamesOrIdsOrFail(DeployToEnvironmentNamesOrIds.Distinct(StringComparer.OrdinalIgnoreCase))
                                       .ConfigureAwait(false);

            var promotingEnvironments = deployToEnvironments.Select(environment => new
            {
                environment.Name,
                Promotion = releaseTemplate.PromoteTo
                            .FirstOrDefault(p => string.Equals(p.Name, environment.Name, StringComparison.OrdinalIgnoreCase))
            })
                                        .ToList();

            var unknownEnvironments = promotingEnvironments.Where(p => p.Promotion == null).ToList();

            if (unknownEnvironments.Count > 0)
            {
                throw new CommandException(
                          string.Format(
                              "Release '{0}' of project '{1}' cannot be deployed to {2} not in the list of environments that this release can be deployed to. This may be because a) the environment does not exist or is misspelled, b) The lifecycle has not reached this phase, possibly due to previous deployment failure, c) you don't have permission to deploy to this environment, or d) the environment is not in the list of environments defined by the lifecycle.",
                              release.Version,
                              project.Name,
                              unknownEnvironments.Count == 1
                            ? "environment '" + unknownEnvironments[0].Name + "' because the environment is"
                            : "environments " + string.Join(", ", unknownEnvironments.Select(e => "'" + e.Name + "'")) +
                              " because the environments are"
                              ));
            }

            LogScheduledDeployment();
            var specificMachineIds = await GetSpecificMachines().ConfigureAwait(false);

            var excludedMachineIds = await GetExcludedMachines().ConfigureAwait(false);

            var createTasks = promotingEnvironments.Select(promotion => CreateDeploymentTask(project,
                                                                                             release,
                                                                                             promotion.Promotion,
                                                                                             specificMachineIds,
                                                                                             excludedMachineIds));

            return(await Task.WhenAll(createTasks).ConfigureAwait(false));
        }
        public PromoteReleaseCommand(IOctopusAsyncRepositoryFactory repositoryFactory, IOctopusFileSystem fileSystem, IOctopusClientFactory clientFactory, ICommandOutputProvider commandOutputProvider, ExecutionResourceWaiter.Factory executionResourceWaiterFactory)
            : base(repositoryFactory, fileSystem, clientFactory, commandOutputProvider, executionResourceWaiterFactory)
        {
            var options = Options.For("Release Promotion");

            options.Add <string>("project=", "Name or ID of the project.", v => ProjectNameOrId = v);
            options.Add <string>("from=", "Name or ID of the environment to get the current deployment from, e.g., 'Staging' or 'Environments-2'.", v => FromEnvironmentNameOrId = v);
            options.Add <string>("to=|deployTo=", "Name or ID of the environment to deploy to, e.g., 'Production' or 'Environments-1'.", v => DeployToEnvironmentNamesOrIds.Add(v), allowsMultiple: true);
            options.Add <bool>("updateVariables", "Overwrite the variable snapshot for the release by re-importing the variables from the project.", v => UpdateVariableSnapshot = true);
            options.Add <bool>("latestSuccessful", "Use the latest successful release to promote.", v => UseLatestSuccessfulRelease = true);
        }
Example #3
0
        public DeployReleaseCommand(IOctopusAsyncRepositoryFactory repositoryFactory, IOctopusFileSystem fileSystem, IOctopusClientFactory clientFactory, ICommandOutputProvider commandOutputProvider, ExecutionResourceWaiter.Factory executionResourceWaiterFactory)
            : base(repositoryFactory, fileSystem, clientFactory, commandOutputProvider, executionResourceWaiterFactory)
        {
            var options = Options.For("Deployment");

            options.Add <string>("project=", "Name or ID of the project.", v => ProjectNameOrId = v);
            options.Add <string>("deployTo=", "Name or ID of the environment to deploy to, e.g., 'Production' or 'Environments-1'; specify this argument multiple times to deploy to multiple environments.", v => DeployToEnvironmentNamesOrIds.Add(v), allowsMultiple: true);
            options.Add <string>("releaseNumber=|version=", "Version number of the release to deploy. Or specify --version=latest for the latest release.", v => VersionNumber = v);
            options.Add <string>("channel=", "[Optional] Name or ID of the channel to use when getting the release to deploy.", v => ChannelNameOrId = v);
            options.Add <bool>("updateVariables", "Overwrite the variable snapshot for the release by re-importing the variables from the project.", v => UpdateVariableSnapshot = true);
        }
        public async Task Request()
        {
            var serverSupportsChannels = await ServerSupportsChannels();

            commandOutputProvider.Debug(serverSupportsChannels ? "This Octopus Server supports channels" : "This Octopus Server does not support channels");

            project = await Repository.Projects.FindByNameOrIdOrFail(ProjectNameOrId).ConfigureAwait(false);

            plan = await BuildReleasePlan(project).ConfigureAwait(false);


            if (!string.IsNullOrWhiteSpace(VersionNumber))
            {
                versionNumber = VersionNumber;
                commandOutputProvider.Debug("Using version number provided on command-line: {Version:l}", versionNumber);
            }
            else if (!string.IsNullOrWhiteSpace(plan.ReleaseTemplate.NextVersionIncrement))
            {
                versionNumber = plan.ReleaseTemplate.NextVersionIncrement;
                commandOutputProvider.Debug("Using version number from release template: {Version:l}", versionNumber);
            }
            else if (!string.IsNullOrWhiteSpace(plan.ReleaseTemplate.VersioningPackageStepName))
            {
                versionNumber = plan.GetActionVersionNumber(plan.ReleaseTemplate.VersioningPackageStepName, plan.ReleaseTemplate.VersioningPackageReferenceName);
                commandOutputProvider.Debug("Using version number from package step: {Version:l}", versionNumber);
            }
            else
            {
                throw new CommandException(
                          "A version number was not specified and could not be automatically selected.");
            }

            commandOutputProvider.Write(
                plan.IsViableReleasePlan() ? LogEventLevel.Information : LogEventLevel.Warning,
                "Release plan for {Project:l} {Version:l}" + System.Environment.NewLine + "{Plan:l}",
                project.Name, versionNumber, plan.FormatAsTable()
                );
            if (plan.HasUnresolvedSteps())
            {
                throw new CommandException(
                          "Package versions could not be resolved for one or more of the package packageSteps in this release. See the errors above for details. Either ensure the latest version of the package can be automatically resolved, or set the version to use specifically by using the --package argument.");
            }
            if (plan.ChannelHasAnyEnabledSteps() == false)
            {
                if (serverSupportsChannels)
                {
                    throw new CommandException($"Channel {plan.Channel.Name} has no available steps");
                }
                else
                {
                    throw new CommandException($"Plan has no available steps");
                }
            }

            if (plan.HasStepsViolatingChannelVersionRules())
            {
                if (IgnoreChannelRules)
                {
                    commandOutputProvider.Warning("At least one step violates the package version rules for the Channel '{Channel:l}'. Forcing the release to be created ignoring these rules...", plan.Channel.Name);
                }
                else
                {
                    throw new CommandException(
                              $"At least one step violates the package version rules for the Channel '{plan.Channel.Name}'. Either correct the package versions for this release, let Octopus select the best channel by omitting the --channel argument, select a different channel using --channel=MyChannel argument, or ignore these version rules altogether by using the --ignoreChannelRules argument.");
                }
            }

            if (IgnoreIfAlreadyExists)
            {
                commandOutputProvider.Debug("Checking for existing release for {Project:l} {Version:l} because you specified --ignoreexisting...", project.Name, versionNumber);
                try
                {
                    var found = await Repository.Projects.GetReleaseByVersion(project, versionNumber)
                                .ConfigureAwait(false);

                    if (found != null)
                    {
                        commandOutputProvider.Information("A release of {Project:l} with the number {Version:l} already exists, and you specified --ignoreexisting, so we won't even attempt to create the release.", project.Name, versionNumber);
                        return;
                    }
                }
                catch (OctopusResourceNotFoundException)
                {
                    // Expected
                    commandOutputProvider.Debug("No release exists - the coast is clear!");
                }
            }

            if (WhatIf)
            {
                // We were just doing a dry run - bail out here
                if (DeployToEnvironmentNamesOrIds.Any())
                {
                    commandOutputProvider.Information("[WhatIf] This release would have been created using the release plan and deployed to {Environments:l}", DeployToEnvironmentNamesOrIds.CommaSeperate());
                }
                else
                {
                    commandOutputProvider.Information("[WhatIf] This release would have been created using the release plan");
                }
            }
            else
            {
                // Actually create the release!
                commandOutputProvider.Debug("Creating release...");

                // if no release notes were provided on the command line, but the project has a template, then use the template
                if (string.IsNullOrWhiteSpace(ReleaseNotes) && !string.IsNullOrWhiteSpace(project.ReleaseNotesTemplate))
                {
                    ReleaseNotes = project.ReleaseNotesTemplate;
                }

                release = await Repository.Releases.Create(new ReleaseResource(versionNumber, project.Id, plan.Channel?.Id)
                {
                    ReleaseNotes     = ReleaseNotes,
                    SelectedPackages = plan.GetSelections()
                }, ignoreChannelRules : IgnoreChannelRules)
                          .ConfigureAwait(false);

                commandOutputProvider.Information("Release {Version:l} created successfully!", release.Version);
                commandOutputProvider.ServiceMessage("setParameter", new { name = "octo.releaseNumber", value = release.Version });
                commandOutputProvider.TfsServiceMessage(ServerBaseUrl, project, release);

                await DeployRelease(project, release).ConfigureAwait(false);
            }
        }
        public CreateReleaseCommand(IOctopusAsyncRepositoryFactory repositoryFactory, IOctopusFileSystem fileSystem, IPackageVersionResolver versionResolver, IReleasePlanBuilder releasePlanBuilder, IOctopusClientFactory clientFactory, ICommandOutputProvider commandOutputProvider)
            : base(repositoryFactory, fileSystem, clientFactory, commandOutputProvider)
        {
            this.releasePlanBuilder = releasePlanBuilder;

            var options = Options.For("Release creation");

            options.Add("project=", "Name or ID of the project", v => ProjectNameOrId = v);
            options.Add("defaultpackageversion=|packageversion=", "Default version number of all packages to use for this release. Override per-package using --package.", versionResolver.Default);
            options.Add("version=|releaseNumber=", "[Optional] Release number to use for the new release.", v => VersionNumber = v);
            options.Add("channel=", "[Optional] Name or ID of the channel to use for the new release. Omit this argument to automatically select the best channel.", v => ChannelNameOrId = v);
            options.Add("package=", "[Optional] Version number to use for a package in the release. Format: StepName:Version or PackageID:Version or StepName:PackageName:Version. StepName, PackageID, and PackageName can be replaced with an asterisk. An asterisk will be assumed for StepName, PackageID, or PackageName if they are omitted.", v => versionResolver.Add(v));
            options.Add("packagesFolder=", "[Optional] A folder containing NuGet packages from which we should get versions.", v => { v.CheckForIllegalPathCharacters("packagesFolder"); versionResolver.AddFolder(v); });
            options.Add("releasenotes=", "[Optional] Release Notes for the new release. Styling with Markdown is supported.", v => ReleaseNotes = v);
            options.Add("releasenotesfile=", "[Optional] Path to a file that contains Release Notes for the new release. Supports Markdown files.", ReadReleaseNotesFromFile);
            options.Add("ignoreexisting", "[Optional, Flag] Don't create this release if there is already one with the same version number.", v => IgnoreIfAlreadyExists = true);
            options.Add("ignorechannelrules", "[Optional, Flag] Create the release ignoring any version rules specified by the channel.", v => IgnoreChannelRules        = true);
            options.Add("packageprerelease=", "[Optional] Pre-release for latest version of all packages to use for this release.", v => VersionPreReleaseTag            = v);
            options.Add("whatif", "[Optional, Flag] Perform a dry run but don't actually create/deploy release.", v => WhatIf = true);

            options = Options.For("Deployment");
            options.Add("deployto=", "[Optional] Name or ID of the environment to automatically deploy to, e.g., 'Production' or 'Environments-1'; specify this argument multiple times to deploy to multiple environments.", v => DeployToEnvironmentNamesOrIds.Add(v));
        }
Example #6
0
        public PromoteReleaseCommand(IOctopusAsyncRepositoryFactory repositoryFactory, IOctopusFileSystem fileSystem, IOctopusClientFactory clientFactory, ICommandOutputProvider commandOutputProvider)
            : base(repositoryFactory, fileSystem, clientFactory, commandOutputProvider)
        {
            var options = Options.For("Release Promotion");

            options.Add("project=", "Name or ID of the project", v => ProjectNameOrId = v);
            options.Add("from=", "Name or ID of the environment to get the current deployment from, e.g., 'Staging' or 'Environments-2'.", v => FromEnvironmentNameOrId = v);
            options.Add("to=|deployto=", "Name or ID of the environment to deploy to, e.g., 'Production' or 'Environments-1'.", v => DeployToEnvironmentNamesOrIds.Add(v));
            options.Add("updateVariables", "Overwrite the variable snapshot for the release by re-importing the variables from the project", v => UpdateVariableSnapshot = true);
        }