Skip to content

feat/alias for project names #2070

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

Merged
merged 5 commits into from
Aug 6, 2025
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
1 change: 1 addition & 0 deletions backend/models/scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ func (j *DiggerJob) MapToJsonStruct() (orchestrator_scheduler.SerializedJob, err
JobString: j.SerializedJobSpec,
PlanFootprint: j.PlanFootprint,
ProjectName: job.ProjectName,
ProjectAlias: job.ProjectAlias,
WorkflowRunUrl: j.WorkflowRunUrl,
PRCommentUrl: j.PRCommentUrl,
ResourcesCreated: j.DiggerJobSummary.ResourcesCreated,
Expand Down
4 changes: 2 additions & 2 deletions backend/utils/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,13 @@ func SetPRStatusForJobs(prService ci.PullRequestService, prNumber int, jobs []sc
"prNumber", prNumber,
"project", job.ProjectName,
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Consider enriching debug logs with the alias information to improve traceability when troubleshooting status updates

Context Used: Context - Enrich debug logs with relevant context. Always include identifiers like 'prNumber' in logs to facilitate easier tracing and troubleshooting. (link)

)
err = prService.SetStatus(prNumber, "pending", job.ProjectName+"/plan")
err = prService.SetStatus(prNumber, "pending", job.GetProjectAlias()+"/plan")
case "digger apply":
slog.Debug("Setting PR status for apply",
"prNumber", prNumber,
"project", job.ProjectName,
)
err = prService.SetStatus(prNumber, "pending", job.ProjectName+"/apply")
err = prService.SetStatus(prNumber, "pending", job.GetProjectAlias()+"/apply")
}
if err != nil {
slog.Error("Failed to set PR status",
Expand Down
4 changes: 2 additions & 2 deletions backend/utils/pr_comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ func ReportInitialJobsStatus(cr *CommentReporter, jobs []scheduler.Job) error {
message = message + fmt.Sprintf("|---------|--------|\n")
for _, job := range jobs {
message = message + fmt.Sprintf(""+
"|:clock11: **%v**|pending...|\n", job.ProjectName)
"|:clock11: **%v**|pending...|\n", job.GetProjectAlias())
}
}

Expand Down Expand Up @@ -223,7 +223,7 @@ func ReportLayersTableForJobs(cr *CommentReporter, jobs []scheduler.Job) error {
message = message + fmt.Sprintf("|---------|--------|\n")
for _, job := range jobs {
message = message + fmt.Sprintf(""+
"|:clock11: **%v**|%v|\n", job.ProjectName, job.Layer)
"|:clock11: **%v**|%v|\n", job.GetProjectAlias(), job.Layer)
}
}

Expand Down
14 changes: 7 additions & 7 deletions cli/pkg/digger/digger.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ func run(command string, job orchestrator.Job, policyChecker policy.Checker, org
if err != nil {
slog.Error("failed to send usage report", "error", err)
}
err = prService.SetStatus(*job.PullRequestNumber, "pending", job.ProjectName+"/plan")
err = prService.SetStatus(*job.PullRequestNumber, "pending", job.GetProjectAlias()+"/plan")
if err != nil {
msg := fmt.Sprintf("Failed to set PR status. %v", err)
return nil, msg, fmt.Errorf("%s", msg)
Expand All @@ -286,7 +286,7 @@ func run(command string, job orchestrator.Job, policyChecker policy.Checker, org
if err != nil {
msg := fmt.Sprintf("Failed to Run digger plan command. %v", err)
slog.Error("Failed to Run digger plan command", "error", err)
err := prService.SetStatus(*job.PullRequestNumber, "failure", job.ProjectName+"/plan")
err := prService.SetStatus(*job.PullRequestNumber, "failure", job.GetProjectAlias()+"/plan")
if err != nil {
msg := fmt.Sprintf("Failed to set PR status. %v", err)
return nil, msg, fmt.Errorf("%s", msg)
Expand Down Expand Up @@ -340,7 +340,7 @@ func run(command string, job orchestrator.Job, policyChecker policy.Checker, org
} else {
reportEmptyPlanOutput(reporter, projectLock.LockId())
}
err := prService.SetStatus(*job.PullRequestNumber, "success", job.ProjectName+"/plan")
err := prService.SetStatus(*job.PullRequestNumber, "success", job.GetProjectAlias()+"/plan")
if err != nil {
msg := fmt.Sprintf("Failed to set PR status. %v", err)
return nil, msg, fmt.Errorf("%s", msg)
Expand All @@ -361,7 +361,7 @@ func run(command string, job orchestrator.Job, policyChecker policy.Checker, org
if err != nil {
slog.Error("failed to send usage report.", "error", err)
}
err = prService.SetStatus(*job.PullRequestNumber, "pending", job.ProjectName+"/apply")
err = prService.SetStatus(*job.PullRequestNumber, "pending", job.GetProjectAlias()+"/apply")
if err != nil {
msg := fmt.Sprintf("Failed to set PR status. %v", err)
return nil, msg, fmt.Errorf("%s", msg)
Expand All @@ -382,7 +382,7 @@ func run(command string, job orchestrator.Job, policyChecker policy.Checker, org
slog.Info("PR status Information", "mergeable", isMergeable, "merged", isMerged, "skipMergeCheck", job.SkipMergeCheck)
if !isMergeable && !isMerged && !job.SkipMergeCheck {
comment := reportApplyMergeabilityError(reporter)
prService.SetStatus(*job.PullRequestNumber, "failure", job.ProjectName+"/apply")
prService.SetStatus(*job.PullRequestNumber, "failure", job.GetProjectAlias()+"/apply")

return nil, comment, fmt.Errorf("%s", comment)
} else {
Expand Down Expand Up @@ -428,15 +428,15 @@ func run(command string, job orchestrator.Job, policyChecker policy.Checker, org
if err != nil {
//TODO reuse executor error handling
slog.Error("Failed to Run digger apply command.", "error", err)
err := prService.SetStatus(*job.PullRequestNumber, "failure", job.ProjectName+"/apply")
err := prService.SetStatus(*job.PullRequestNumber, "failure", job.GetProjectAlias()+"/apply")
if err != nil {
msg := fmt.Sprintf("Failed to set PR status. %v", err)
return nil, msg, fmt.Errorf("%s", msg)
}
msg := fmt.Sprintf("Failed to run digger apply command. %v", err)
return nil, msg, fmt.Errorf("%s", msg)
} else if applyPerformed {
err := prService.SetStatus(*job.PullRequestNumber, "success", job.ProjectName+"/apply")
err := prService.SetStatus(*job.PullRequestNumber, "success", job.GetProjectAlias()+"/apply")
if err != nil {
msg := fmt.Sprintf("Failed to set PR status. %v", err)
return nil, msg, fmt.Errorf("%s", msg)
Expand Down
1 change: 1 addition & 0 deletions libs/ci/generic/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ func CreateJobsForProjects(projects []digger_config.Project, command string, eve
workspace := project.Workspace
jobs = append(jobs, scheduler.Job{
ProjectName: project.Name,
ProjectAlias: project.Alias,
ProjectDir: project.Dir,
ProjectWorkspace: workspace,
ProjectWorkflow: project.Workflow,
Expand Down
4 changes: 4 additions & 0 deletions libs/ci/github/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@ func ConvertGithubPullRequestEventToJobs(payload *github.PullRequestEvent, impac

jobs = append(jobs, scheduler.Job{
ProjectName: project.Name,
ProjectAlias: project.Alias,
ProjectDir: project.Dir,
ProjectWorkspace: project.Workspace,
ProjectWorkflow: project.Workflow,
Expand Down Expand Up @@ -524,6 +525,7 @@ func ConvertGithubPullRequestEventToJobs(payload *github.PullRequestEvent, impac

jobs = append(jobs, scheduler.Job{
ProjectName: project.Name,
ProjectAlias: project.Alias,
ProjectDir: project.Dir,
ProjectWorkspace: project.Workspace,
ProjectWorkflow: project.Workflow,
Expand Down Expand Up @@ -555,6 +557,7 @@ func ConvertGithubPullRequestEventToJobs(payload *github.PullRequestEvent, impac

jobs = append(jobs, scheduler.Job{
ProjectName: project.Name,
ProjectAlias: project.Alias,
ProjectDir: project.Dir,
ProjectWorkspace: project.Workspace,
ProjectWorkflow: project.Workflow,
Expand Down Expand Up @@ -594,6 +597,7 @@ func ConvertGithubPullRequestEventToJobs(payload *github.PullRequestEvent, impac

jobs = append(jobs, scheduler.Job{
ProjectName: project.Name,
ProjectAlias: project.Alias,
ProjectDir: project.Dir,
ProjectWorkspace: project.Workspace,
ProjectWorkflow: project.Workflow,
Expand Down
9 changes: 4 additions & 5 deletions libs/comment_utils/summary/updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,18 @@ func (b BasicCommentUpdater) UpdateComment(jobs []scheduler.SerializedJob, prNum
message = message + fmt.Sprintf("| Project | Status | %v | + | ~ | - |\n", jobTypeTitle)
message = message + fmt.Sprintf("|---------|--------|------|---|---|---|\n")

for i, job := range jobs {
jobSpec := jobSpecs[i]
for _, job := range jobs {
prCommentUrl := job.PRCommentUrl

// Safe handling of WorkflowRunUrl pointer
workflowUrl := "#"
if job.WorkflowRunUrl != nil {
workflowUrl = *job.WorkflowRunUrl
}

message = message + fmt.Sprintf("|%v **%v** |<a href='%v'>%v</a> | <a href='%v'>%v</a> | %v | %v | %v|\n",
job.Status.ToEmoji(),
jobSpec.ProjectName,
scheduler.GetProjectAlias(job),
workflowUrl,
job.Status.ToString(),
prCommentUrl,
Expand Down
1 change: 1 addition & 0 deletions libs/digger_config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type AssumeRoleForProject struct {
type Project struct {
BlockName string // the block name if this is a generated project
Name string
Alias string
Dir string
Workspace string
Terragrunt bool
Expand Down
3 changes: 2 additions & 1 deletion libs/digger_config/converters.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ func copyProjects(projects []*ProjectYaml) []Project {
result := make([]Project, len(projects))
for i, p := range projects {
driftDetection := true
layer := uint(0)
if p.DriftDetection != nil {
driftDetection = *p.DriftDetection
}

layer := uint(0)
if p.Layer != nil {
layer = *p.Layer
}
Expand Down Expand Up @@ -76,6 +76,7 @@ func copyProjects(projects []*ProjectYaml) []Project {
item := Project{
p.BlockName,
p.Name,
p.Alias,
p.Dir,
workspace,
p.Terragrunt,
Expand Down
9 changes: 8 additions & 1 deletion libs/digger_config/digger_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -733,8 +733,13 @@ func hydrateDiggerConfigYamlWithTerragrunt(configYaml *DiggerConfigYaml, parsing
root = path.Join(workingDir, *parsingConfig.GitRoot)
slog.Debug("using custom git root", "root", root)
}
projectExternalChilds := true

projectAliasDelimiter := "_"
if parsingConfig.ProjectAliasDelimiter != "" {
projectAliasDelimiter = parsingConfig.ProjectAliasDelimiter
}

projectExternalChilds := true
if parsingConfig.CreateHclProjectExternalChilds != nil {
projectExternalChilds = *parsingConfig.CreateHclProjectExternalChilds
}
Expand Down Expand Up @@ -788,6 +793,7 @@ func hydrateDiggerConfigYamlWithTerragrunt(configYaml *DiggerConfigYaml, parsing
parsingConfig.DefaultApplyRequirements,
parsingConfig.AutoPlan,
parsingConfig.DefaultTerraformVersion,
projectAliasDelimiter,
parsingConfig.CreateProjectName,
parsingConfig.CreateWorkspace,
parsingConfig.PreserveProjects,
Expand Down Expand Up @@ -851,6 +857,7 @@ func hydrateDiggerConfigYamlWithTerragrunt(configYaml *DiggerConfigYaml, parsing
diggerProject := &ProjectYaml{
BlockName: blockName,
Name: atlantisProject.Name,
Alias: atlantisProject.Alias,
Dir: projectDir,
Layer: &executionOrderGroup,
Workspace: atlantisProject.Workspace,
Expand Down
2 changes: 2 additions & 0 deletions libs/digger_config/terragrunt/tac/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ type AtlantisProject struct {
// Define project name
Name string `yaml:"name,omitempty"`

Alias string `yaml:"alias,omitempty"`

// Autoplan settings for which plans affect other plans
Autoplan AutoplanConfig `yaml:"autoplan"`

Expand Down
49 changes: 17 additions & 32 deletions libs/digger_config/terragrunt/tac/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ func getDependencies(ignoreParentTerragrunt bool, ignoreDependencyBlocks bool, g
}
}

func createBaseProject(dir string, workflow string, terraformVersion string, applyRequirements *[]string, autoPlan bool, dependencies []string, createProjectName bool, createWorkspace bool) *AtlantisProject {
func createBaseProject(dir string, workflow string, terraformVersion string, applyRequirements *[]string, autoPlan bool, dependencies []string, createProjectName bool, createWorkspace bool, aliasDelimiter string) *AtlantisProject {
project := &AtlantisProject{
Dir: filepath.ToSlash(dir),
Workflow: workflow,
Expand All @@ -329,9 +329,10 @@ func createBaseProject(dir string, workflow string, terraformVersion string, app
}

if createProjectName || createWorkspace {
projectName := projectNameFromDir(project.Dir)
projectName := SanitizeDirName(project.Dir, "_")
if createProjectName {
project.Name = projectName
project.Alias = SanitizeDirName(projectName, aliasDelimiter)
}
if createWorkspace {
project.Workspace = projectName
Expand All @@ -342,7 +343,7 @@ func createBaseProject(dir string, workflow string, terraformVersion string, app
}

// Creates an AtlantisProject for a directory
func createProject(ignoreParentTerragrunt bool, ignoreDependencyBlocks bool, gitRoot string, cascadeDependencies bool, defaultWorkflow string, defaultApplyRequirements []string, autoPlan bool, defaultTerraformVersion string, createProjectName bool, createWorkspace bool, sourcePath string, triggerProjectsFromDirOnly bool) (*AtlantisProject, []string, error) {
func createProject(ignoreParentTerragrunt bool, ignoreDependencyBlocks bool, gitRoot string, cascadeDependencies bool, defaultWorkflow string, defaultApplyRequirements []string, autoPlan bool, defaultTerraformVersion string, createProjectName bool, createWorkspace bool, sourcePath string, triggerProjectsFromDirOnly bool, aliasDelimiter string) (*AtlantisProject, []string, error) {
options, err := options.NewTerragruntOptionsWithConfigPath(sourcePath)

var potentialProjectDependencies []string
Expand Down Expand Up @@ -373,16 +374,7 @@ func createProject(ignoreParentTerragrunt bool, ignoreDependencyBlocks bool, git
return nil, potentialProjectDependencies, nil
}

project := createBaseProject(
relativeSourceDir,
defaultWorkflow,
defaultTerraformVersion,
&defaultApplyRequirements,
autoPlan,
relativeDependencies,
createProjectName,
createWorkspace,
)
project := createBaseProject(relativeSourceDir, defaultWorkflow, defaultTerraformVersion, &defaultApplyRequirements, autoPlan, relativeDependencies, createProjectName, createWorkspace, aliasDelimiter)
return project, potentialProjectDependencies, nil
}

Expand Down Expand Up @@ -413,7 +405,7 @@ func createProject(ignoreParentTerragrunt bool, ignoreDependencyBlocks bool, git
if !filepath.IsAbs(absolutePath) {
absolutePath = makePathAbsolute(gitRoot, dependencyPath, sourcePath)
}
potentialProjectDependencies = append(potentialProjectDependencies, projectNameFromDir(filepath.Dir(strings.TrimPrefix(absolutePath, gitRoot))))
potentialProjectDependencies = append(potentialProjectDependencies, SanitizeDirName(filepath.Dir(strings.TrimPrefix(absolutePath, gitRoot)), "_"))

relativePath, err := filepath.Rel(absoluteSourceDir, absolutePath)
if err != nil {
Expand Down Expand Up @@ -446,21 +438,13 @@ func createProject(ignoreParentTerragrunt bool, ignoreDependencyBlocks bool, git
terraformVersion = locals.TerraformVersion
}

project := createBaseProject(
relativeSourceDir,
workflow,
terraformVersion,
applyRequirements,
resolvedAutoPlan,
relativeDependencies,
createProjectName,
createWorkspace,
)

projectName := projectNameFromDir(project.Dir)
project := createBaseProject(relativeSourceDir, workflow, terraformVersion, applyRequirements, resolvedAutoPlan, relativeDependencies, createProjectName, createWorkspace, aliasDelimiter)

projectName := SanitizeDirName(project.Dir, "_")
projectAlias := SanitizeDirName(project.Dir, aliasDelimiter)
if createProjectName {
project.Name = projectName
project.Alias = projectAlias
}

if createWorkspace {
Expand All @@ -470,19 +454,19 @@ func createProject(ignoreParentTerragrunt bool, ignoreDependencyBlocks bool, git
return project, potentialProjectDependencies, nil
}

func projectNameFromDir(projectDir string) string {
func SanitizeDirName(projectDir string, delimiter string) string {
// Terraform Cloud limits the workspace names to be less than 90 characters
// with letters, numbers, -, and _
// https://www.terraform.io/docs/cloud/workspaces/naming.html
// It is not clear from documentation whether the normal workspaces have those limitations
// However a workspace 97 chars long has been working perfectly.
// We are going to use the same name for both workspace & project name as it is unique.
regex := regexp.MustCompile(`[^a-zA-Z0-9_-]+`)
projectName := regex.ReplaceAllString(projectDir, "_")
projectName := regex.ReplaceAllString(projectDir, delimiter)
return projectName
}

func createHclProject(defaultWorkflow string, defaultApplyRequirements []string, autoplan bool, useProjectMarkers bool, defaultTerraformVersion string, ignoreParentTerragrunt bool, ignoreDependencyBlocks bool, gitRoot string, cascadeDependencies bool, createProjectName bool, createWorkspace bool, sourcePaths []string, workingDir string, projectHcl string) (*AtlantisProject, error) {
func createHclProject(defaultWorkflow string, defaultApplyRequirements []string, autoplan bool, useProjectMarkers bool, defaultTerraformVersion string, ignoreParentTerragrunt bool, ignoreDependencyBlocks bool, gitRoot string, cascadeDependencies bool, createProjectName bool, createWorkspace bool, sourcePaths []string, workingDir string, projectHcl string, aliasDelimiter string) (*AtlantisProject, error) {
var projectHclDependencies []string
var childDependencies []string
workflow := defaultWorkflow
Expand Down Expand Up @@ -618,6 +602,7 @@ func createHclProject(defaultWorkflow string, defaultApplyRequirements []string,

if createProjectName {
project.Name = projectName
project.Alias = SanitizeDirName(projectName, aliasDelimiter)
}

if createWorkspace {
Expand Down Expand Up @@ -715,7 +700,7 @@ func getAllTerragruntProjectHclFiles(projectHclFiles []string, gitRoot string) m
return uniqueHclFileAbsPaths
}

func Parse(gitRoot string, projectHclFiles []string, createHclProjectExternalChilds bool, autoMerge bool, parallel bool, filterPaths []string, createHclProjectChilds bool, ignoreParentTerragrunt bool, ignoreDependencyBlocks bool, cascadeDependencies bool, defaultWorkflow string, defaultApplyRequirements []string, autoPlan bool, defaultTerraformVersion string, createProjectName bool, createWorkspace bool, preserveProjects bool, useProjectMarkers bool, executionOrderGroups bool, triggerProjectsFromDirOnly bool, oldConfig *AtlantisConfig) (*AtlantisConfig, map[string][]string, error) {
func Parse(gitRoot string, projectHclFiles []string, createHclProjectExternalChilds bool, autoMerge bool, parallel bool, filterPaths []string, createHclProjectChilds bool, ignoreParentTerragrunt bool, ignoreDependencyBlocks bool, cascadeDependencies bool, defaultWorkflow string, defaultApplyRequirements []string, autoPlan bool, defaultTerraformVersion string, projectAliasDelimiter string, createProjectName bool, createWorkspace bool, preserveProjects bool, useProjectMarkers bool, executionOrderGroups bool, triggerProjectsFromDirOnly bool, oldConfig *AtlantisConfig) (*AtlantisConfig, map[string][]string, error) {
// Ensure the gitRoot has a trailing slash and is an absolute path
absoluteGitRoot, err := filepath.Abs(gitRoot)
if err != nil {
Expand Down Expand Up @@ -785,7 +770,7 @@ func Parse(gitRoot string, projectHclFiles []string, createHclProjectExternalChi

errGroup.Go(func() error {
defer sem.Release(1)
project, projDeps, err := createProject(ignoreParentTerragrunt, ignoreDependencyBlocks, gitRoot, cascadeDependencies, defaultWorkflow, defaultApplyRequirements, autoPlan, defaultTerraformVersion, createProjectName, createWorkspace, terragruntPath, triggerProjectsFromDirOnly)
project, projDeps, err := createProject(ignoreParentTerragrunt, ignoreDependencyBlocks, gitRoot, cascadeDependencies, defaultWorkflow, defaultApplyRequirements, autoPlan, defaultTerraformVersion, createProjectName, createWorkspace, terragruntPath, triggerProjectsFromDirOnly, projectAliasDelimiter)
if err != nil {
return err
}
Expand Down Expand Up @@ -844,7 +829,7 @@ func Parse(gitRoot string, projectHclFiles []string, createHclProjectExternalChi

errGroup.Go(func() error {
defer sem.Release(1)
project, err := createHclProject(defaultWorkflow, defaultApplyRequirements, autoPlan, useProjectMarkers, defaultTerraformVersion, ignoreParentTerragrunt, ignoreDependencyBlocks, gitRoot, cascadeDependencies, createProjectName, createWorkspace, terragruntFiles, workingDir, projectHcl)
project, err := createHclProject(defaultWorkflow, defaultApplyRequirements, autoPlan, useProjectMarkers, defaultTerraformVersion, ignoreParentTerragrunt, ignoreDependencyBlocks, gitRoot, cascadeDependencies, createProjectName, createWorkspace, terragruntFiles, workingDir, projectHcl, projectAliasDelimiter)
if err != nil {
return err
}
Expand Down
Loading
Loading