Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add support for .azuredevops folder #4190

Merged
merged 6 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 23 additions & 20 deletions cli/azd/pkg/pipeline/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ const (
azdoDisplayName string = "Azure DevOps"
azdoCode = "azdo"
azdoRoot string = ".azdo"
azdoRootAlt string = ".azuredevops"
azdoPipelines string = "pipelines"
envPersistedKey string = "AZD_PIPELINE_PROVIDER"
)
Expand All @@ -176,36 +177,38 @@ var (
)

var (
// Define a map to hold the directory and file names for each provider
pipelineProviderFiles = map[ciProviderType]struct {
RootDirectory string
PipelineDirectory string
Files []string
DefaultFile string
DisplayName string
Code string
RootDirectories []string
PipelineDirectories []string
Files []string
DefaultFile string
DisplayName string
Code string
}{
ciProviderGitHubActions: {
RootDirectory: gitHubRoot,
PipelineDirectory: filepath.Join(gitHubRoot, gitHubWorkflows),
Files: generateFilePaths(filepath.Join(gitHubRoot, gitHubWorkflows), pipelineFileNames),
DefaultFile: pipelineFileNames[0],
DisplayName: gitHubDisplayName,
RootDirectories: []string{gitHubRoot},
PipelineDirectories: []string{filepath.Join(gitHubRoot, gitHubWorkflows)},
Files: generateFilePaths([]string{filepath.Join(gitHubRoot, gitHubWorkflows)}, pipelineFileNames),
DefaultFile: pipelineFileNames[0],
DisplayName: gitHubDisplayName,
},
ciProviderAzureDevOps: {
RootDirectory: azdoRoot,
PipelineDirectory: filepath.Join(azdoRoot, azdoPipelines),
Files: generateFilePaths(filepath.Join(azdoRoot, azdoPipelines), pipelineFileNames),
DefaultFile: pipelineFileNames[0],
DisplayName: azdoDisplayName,
RootDirectories: []string{azdoRoot, azdoRootAlt},
PipelineDirectories: []string{filepath.Join(azdoRoot, azdoPipelines), filepath.Join(azdoRootAlt, azdoPipelines)},
Files: generateFilePaths([]string{filepath.Join(azdoRoot, azdoPipelines),
filepath.Join(azdoRootAlt, azdoPipelines)}, pipelineFileNames),
DefaultFile: pipelineFileNames[0],
DisplayName: azdoDisplayName,
},
}
)

func generateFilePaths(directory string, fileNames []string) []string {
func generateFilePaths(directories []string, fileNames []string) []string {
var paths []string
for _, file := range fileNames {
paths = append(paths, filepath.Join(directory, file))
for _, dir := range directories {
for _, file := range fileNames {
paths = append(paths, filepath.Join(dir, file))
}
}
return paths
}
Expand Down
128 changes: 83 additions & 45 deletions cli/azd/pkg/pipeline/pipeline_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -823,8 +823,7 @@ func (pm *PipelineManager) savePipelineProviderToEnv(
}

// checkAndPromptForProviderFiles checks if the provider files are present and prompts the user to create them if not.
func (pm *PipelineManager) checkAndPromptForProviderFiles(
ctx context.Context, props projectProperties) error {
func (pm *PipelineManager) checkAndPromptForProviderFiles(ctx context.Context, props projectProperties) error {
log.Printf("Checking for provider files for: %s", props.CiProvider)

if !hasPipelineFile(props.CiProvider, props.RepoRoot) {
Expand All @@ -836,41 +835,65 @@ func (pm *PipelineManager) checkAndPromptForProviderFiles(
log.Println("Prompt for CI files completed successfully.")
}

dirPath := pipelineProviderFiles[props.CiProvider].PipelineDirectory
log.Printf("Checking if directory %s is empty", dirPath)
isEmpty, err := osutil.IsDirEmpty(filepath.Join(props.RepoRoot, dirPath), true)
if err != nil {
log.Println("Error checking if directory is empty:", err)
return fmt.Errorf("error checking if directory is empty: %w", err)
}

if isEmpty {
message := fmt.Sprintf(
"%s provider selected, but %s is empty. Please add pipeline files.",
pipelineProviderFiles[props.CiProvider].DisplayName, dirPath)
if props.CiProvider == ciProviderAzureDevOps {
message = fmt.Sprintf(
"%s provider selected, but %s is empty. Please add pipeline files and try again.",
pipelineProviderFiles[props.CiProvider].DisplayName, dirPath)
log.Println("Error:", message)
return fmt.Errorf(message)
var dirPaths []string
for _, dir := range pipelineProviderFiles[props.CiProvider].PipelineDirectories {
dirPaths = append(dirPaths, filepath.Join(props.RepoRoot, dir))
}

for _, dirPath := range dirPaths {
log.Printf("Checking if directory %s is empty", dirPath)
isEmpty, err := osutil.IsDirEmpty(dirPath, true)
if err != nil {
log.Println("Error checking if directory is empty:", err)
return fmt.Errorf("error checking if directory is empty: %w", err)
}
if !isEmpty {
log.Printf("Provider files are present in directory: %s", dirPath)
return nil
}
log.Println("Info:", message)
pm.console.Message(ctx, message)
pm.console.Message(ctx, "")
}

log.Printf("Provider files are present for: %s", props.CiProvider)
message := fmt.Sprintf(
"%s provider selected, but no pipeline files were found in any expected directories:\n%s\n"+
"Please add pipeline files.",
pipelineProviderFiles[props.CiProvider].DisplayName,
strings.Join(pipelineProviderFiles[props.CiProvider].PipelineDirectories, "\n"))

if props.CiProvider == ciProviderAzureDevOps {
message = fmt.Sprintf(
"%s provider selected, but no pipeline files were found in any expected directories:\n%s\n"+
"Please add pipeline files and try again.",
pipelineProviderFiles[props.CiProvider].DisplayName,
strings.Join(pipelineProviderFiles[props.CiProvider].PipelineDirectories, "\n"))
log.Println("Error:", message)
return fmt.Errorf(message)
}

log.Println("Info:", message)
pm.console.Message(ctx, message)
pm.console.Message(ctx, "")

log.Printf("Provider files are not present for: %s", props.CiProvider)
return nil
}

// promptForCiFiles creates CI/CD files for the specified provider, confirming with the user before creation.
func (pm *PipelineManager) promptForCiFiles(ctx context.Context, props projectProperties) error {
dirPath := filepath.Join(props.RepoRoot, pipelineProviderFiles[props.CiProvider].PipelineDirectory)
defaultFile := filepath.Join(dirPath, pipelineProviderFiles[props.CiProvider].DefaultFile)
var dirPaths []string
for _, dir := range pipelineProviderFiles[props.CiProvider].PipelineDirectories {
dirPaths = append(dirPaths, filepath.Join(props.RepoRoot, dir))
}

log.Printf("Directory path: %s", dirPath)
log.Printf("Default YAML path: %s", defaultFile)
var defaultFilePath string
for _, dirPath := range dirPaths {
defaultFilePath = filepath.Join(dirPath, pipelineProviderFiles[props.CiProvider].DefaultFile)
if osutil.DirExists(dirPath) || osutil.FileExists(defaultFilePath) {
break
}
}

log.Printf("Directory paths: %v", dirPaths)
log.Printf("Default YAML path: %s", defaultFilePath)

// Confirm with the user before adding the default file
pm.console.Message(ctx, "")
Expand All @@ -891,32 +914,47 @@ func (pm *PipelineManager) promptForCiFiles(ctx context.Context, props projectPr
pm.console.Message(ctx, "")

if confirm {
log.Printf("Confirmed creation of %s file at %s", filepath.Base(defaultFile), dirPath)
log.Printf("Confirmed creation of %s file at %s", filepath.Base(defaultFilePath), dirPaths)

created := false
for _, dirPath := range dirPaths {
if !osutil.DirExists(dirPath) {
log.Printf("Creating directory %s", dirPath)
if err := os.MkdirAll(dirPath, os.ModePerm); err != nil {
return fmt.Errorf("creating directory %s: %w", dirPath, err)
}
created = true
}

if !osutil.DirExists(dirPath) {
log.Printf("Creating directory %s", dirPath)
if err := os.MkdirAll(dirPath, os.ModePerm); err != nil {
return fmt.Errorf("creating directory %s: %w", dirPath, err)
if !osutil.FileExists(filepath.Join(dirPath, pipelineProviderFiles[props.CiProvider].DefaultFile)) {
if err := generatePipelineDefinition(filepath.Join(dirPath,
pipelineProviderFiles[props.CiProvider].DefaultFile), props); err != nil {
return err
}
pm.console.Message(ctx,
fmt.Sprintf(
"The %s file has been created at %s. You can use it as-is or modify it to suit your needs.",
output.WithHighLightFormat(filepath.Base(defaultFilePath)),
output.WithHighLightFormat(filepath.Join(dirPath,
pipelineProviderFiles[props.CiProvider].DefaultFile))),
)
pm.console.Message(ctx, "")
created = true
}
}

if !osutil.FileExists(defaultFile) {
if err := generatePipelineDefinition(defaultFile, props); err != nil {
return err
if created {
break
}
pm.console.Message(ctx,
fmt.Sprintf(
"The %s file has been created at %s. You can use it as-is or modify it to suit your needs.",
output.WithHighLightFormat(filepath.Base(defaultFile)),
output.WithHighLightFormat(defaultFile)),
)
pm.console.Message(ctx, "")
}

if !created {
log.Printf("User declined creation of %s file at %s", filepath.Base(defaultFilePath), dirPaths)
}

return nil
}

log.Printf("User declined creation of %s file at %s", filepath.Base(defaultFile), dirPath)
log.Printf("User declined creation of %s file at %s", filepath.Base(defaultFilePath), dirPaths)
return nil
}

Expand Down
Loading
Loading