cli/azd/cmd/pipeline.go (224 lines of code) (raw):

// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. package cmd import ( "context" "fmt" "github.com/MakeNowJust/heredoc/v2" "github.com/azure/azure-dev/cli/azd/cmd/actions" "github.com/azure/azure-dev/cli/azd/internal" "github.com/azure/azure-dev/cli/azd/pkg/auth" "github.com/azure/azure-dev/cli/azd/pkg/environment" "github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning" "github.com/azure/azure-dev/cli/azd/pkg/input" "github.com/azure/azure-dev/cli/azd/pkg/output" "github.com/azure/azure-dev/cli/azd/pkg/output/ux" "github.com/azure/azure-dev/cli/azd/pkg/pipeline" "github.com/azure/azure-dev/cli/azd/pkg/project" "github.com/azure/azure-dev/cli/azd/pkg/prompt" "github.com/spf13/cobra" "github.com/spf13/pflag" ) type pipelineConfigFlags struct { pipeline.PipelineManagerArgs global *internal.GlobalCommandOptions internal.EnvFlag } func (pc *pipelineConfigFlags) Bind(local *pflag.FlagSet, global *internal.GlobalCommandOptions) { local.StringVar( &pc.PipelineServicePrincipalId, "principal-id", "", "The client id of the service principal to use to grant access to Azure resources as part of the pipeline.", ) local.StringVar( &pc.PipelineServicePrincipalName, "principal-name", "", "The name of the service principal to use to grant access to Azure resources as part of the pipeline.", ) local.StringVar( &pc.PipelineRemoteName, "remote-name", "origin", "The name of the git remote to configure the pipeline to run on.", ) //nolint:lll local.StringVar( &pc.PipelineAuthTypeName, "auth-type", "", "The authentication type used between the pipeline provider and Azure for deployment (Only valid for GitHub provider). Valid values: federated, client-credentials.", ) //nolint:lll local.StringArrayVar( &pc.PipelineRoleNames, "principal-role", pipeline.DefaultRoleNames, "The roles to assign to the service principal. By default the service principal will be granted the Contributor and User Access Administrator roles.", ) // default provider is empty because it can be set from azure.yaml. By letting default here be empty, we know that // there no customer input using --provider local.StringVar(&pc.PipelineProvider, "provider", "", "The pipeline provider to use (github for Github Actions and azdo for Azure Pipelines).") local.StringVarP(&pc.ServiceManagementReference, "applicationServiceManagementReference", "m", "", "Service Management Reference. "+ "References application or service contact information from a Service or Asset Management database. "+ "This value must be a Universally Unique Identifier (UUID). "+ "You can set this value globally by running "+ "azd config set pipeline.config.applicationServiceManagementReference <UUID>.") pc.EnvFlag.Bind(local, global) pc.global = global } func pipelineActions(root *actions.ActionDescriptor) *actions.ActionDescriptor { group := root.Add("pipeline", &actions.ActionDescriptorOptions{ Command: &cobra.Command{ Use: "pipeline", Short: fmt.Sprintf("Manage and configure your deployment pipelines. %s", output.WithWarningFormat("(Beta)")), }, HelpOptions: actions.ActionHelpOptions{ Description: getCmdPipelineHelpDescription, Footer: getCmdPipelineHelpFooter, }, GroupingOptions: actions.CommandGroupOptions{ RootLevelHelp: actions.CmdGroupMonitor, }, }) group.Add("config", &actions.ActionDescriptorOptions{ Command: newPipelineConfigCmd(), FlagsResolver: newPipelineConfigFlags, ActionResolver: newPipelineConfigAction, HelpOptions: actions.ActionHelpOptions{ Description: getCmdPipelineConfigHelpDescription, Footer: getCmdPipelineConfigHelpFooter, }, }) return group } func newPipelineConfigFlags(cmd *cobra.Command, global *internal.GlobalCommandOptions) *pipelineConfigFlags { flags := &pipelineConfigFlags{} flags.Bind(cmd.Flags(), global) return flags } func newPipelineConfigCmd() *cobra.Command { return &cobra.Command{ Use: "config", Short: fmt.Sprintf( "Configure your deployment pipeline to connect securely to Azure. %s", output.WithWarningFormat("(Beta)")), } } // pipelineConfigAction defines the action for pipeline config command type pipelineConfigAction struct { flags *pipelineConfigFlags manager *pipeline.PipelineManager provisioningManager *provisioning.Manager env *environment.Environment console input.Console prompters prompt.Prompter projectConfig *project.ProjectConfig importManager *project.ImportManager } func newPipelineConfigAction( env *environment.Environment, _ auth.LoggedInGuard, console input.Console, flags *pipelineConfigFlags, prompters prompt.Prompter, manager *pipeline.PipelineManager, provisioningManager *provisioning.Manager, importManager *project.ImportManager, projectConfig *project.ProjectConfig, ) actions.Action { pca := &pipelineConfigAction{ flags: flags, manager: manager, env: env, console: console, prompters: prompters, provisioningManager: provisioningManager, importManager: importManager, projectConfig: projectConfig, } return pca } // Run implements action interface func (p *pipelineConfigAction) Run(ctx context.Context) (*actions.ActionResult, error) { infra, err := p.importManager.ProjectInfrastructure(ctx, p.projectConfig) if err != nil { return nil, err } defer func() { _ = infra.Cleanup() }() err = p.provisioningManager.Initialize(ctx, p.projectConfig.Path, infra.Options) if err != nil { return nil, err } pipelineProviderName := p.manager.CiProviderName() // Command title p.console.MessageUxItem(ctx, &ux.MessageTitle{ Title: fmt.Sprintf("Configure your %s pipeline", pipelineProviderName), }) pipelineResult, err := p.manager.Configure(ctx, p.projectConfig.Name) if err != nil { return nil, err } return &actions.ActionResult{ Message: &actions.ResultMessage{ Header: fmt.Sprintf("Your %s pipeline has been configured!", pipelineProviderName), FollowUp: heredoc.Docf(` Link to view your new repo: %s Link to view your pipeline status: %s`, output.WithLinkFormat("%s", pipelineResult.RepositoryLink), output.WithLinkFormat("%s", pipelineResult.PipelineLink)), }, }, nil } func getCmdPipelineHelpDescription(*cobra.Command) string { return generateCmdHelpDescription( fmt.Sprintf("Manage integrating your application with deployment pipelines. %s", output.WithWarningFormat("(Beta)")), []string{ formatHelpNote( "azd commands (e.g. " + output.WithHighLightFormat("provision") + ", " + output.WithHighLightFormat("deploy") + ") " + "can be used within GitHub Actions and Azure Pipelines to test your code against real Azure resources " + "and facilitate deployments."), formatHelpNote( "After creating a pipeline definition file, running " + output.WithHighLightFormat("pipeline config") + " will help configure your deployment pipeline to connect securely to Azure."), formatHelpNote(fmt.Sprintf("For more information on how to use azd in your pipeline, go to: %s.", output.WithLinkFormat("https://aka.ms/azure-dev/pipeline"))), }) } func getCmdPipelineHelpFooter(c *cobra.Command) string { return generateCmdHelpSamplesBlock(map[string]string{ "Walk through the steps required " + "to set up your deployment pipeline.": output.WithHighLightFormat("azd pipeline config"), }) } func getCmdPipelineConfigHelpDescription(*cobra.Command) string { return generateCmdHelpDescription( "Configure your deployment pipeline to connect securely to Azure", []string{ formatHelpNote( "Supports GitHub Actions and Azure Pipelines. To configure using a specific pipeline provider, " + "provide a value for the '--provider' flag."), formatHelpNote( output.WithHighLightFormat("pipeline config") + " creates or uses a service principal on the Azure subscription to create a secure connection between" + " your deployment pipeline and Azure."), formatHelpNote("By default, " + output.WithHighLightFormat("pipeline config") + " will set deployment pipeline variables and secrets using the current environment. " + "To configure for a new or an existing environment, provide a value for the '-e' flag."), }) } func getCmdPipelineConfigHelpFooter(c *cobra.Command) string { return generateCmdHelpSamplesBlock(map[string]string{ "Configure a deployment pipeline using an existing service principal": fmt.Sprintf("%s %s", output.WithHighLightFormat("azd pipeline config --principal-name"), output.WithWarningFormat("[Principal name]"), ), "Configure a deployment pipeline for 'app-test' environment": fmt.Sprintf("%s %s", output.WithHighLightFormat("azd pipeline config -e"), output.WithWarningFormat("app-test"), ), "Configure a deployment pipeline for 'app-test' environment on Azure Pipelines.": fmt.Sprintf("%s %s %s", output.WithHighLightFormat("azd pipeline config -e"), output.WithWarningFormat("app-test"), output.WithHighLightFormat("--provider azdo"), ), }) }