From 209c7ad07c7280a39431f48151021167910367c1 Mon Sep 17 00:00:00 2001 From: Ricard Bejarano Date: Tue, 27 Sep 2022 13:20:44 +0200 Subject: [PATCH 1/7] added custom templates support --- server/events/markdown_renderer.go | 204 ++++-------------- .../templates/apply_unwrapped_success.tmpl | 5 + .../templates/apply_wrapped_success.tmpl | 8 + .../templates/approve_all_projects.tmpl | 13 ++ server/events/templates/failure.tmpl | 3 + server/events/templates/failure_with_log.tmpl | 9 + .../events/templates/multi_project_apply.tmpl | 17 ++ .../events/templates/multi_project_plan.tmpl | 20 ++ .../templates/multi_project_version.tmpl | 17 ++ .../templates/plan_success_unwrapped.tmpl | 13 ++ .../templates/plan_success_wrapped.tmpl | 17 ++ .../policy_check_success_unwrapped.tmpl | 13 ++ .../policy_check_success_wrapped.tmpl | 16 ++ .../templates/single_project_apply.tmpl | 12 ++ .../single_project_plan_success.tmpl | 17 ++ .../single_project_plan_unsuccessful.tmpl | 12 ++ .../single_project_version_success.tmpl | 12 ++ .../single_project_version_unsuccessful.tmpl | 12 ++ server/events/templates/unwrapped_err.tmpl | 10 + .../templates/unwrapped_err_with_log.tmpl | 16 ++ .../templates/version_unwrapped_success.tmpl | 4 + .../templates/version_wrapped_success.tmpl | 8 + server/events/templates/wrapped_err.tmpl | 9 + 23 files changed, 300 insertions(+), 167 deletions(-) create mode 100644 server/events/templates/apply_unwrapped_success.tmpl create mode 100644 server/events/templates/apply_wrapped_success.tmpl create mode 100644 server/events/templates/approve_all_projects.tmpl create mode 100644 server/events/templates/failure.tmpl create mode 100644 server/events/templates/failure_with_log.tmpl create mode 100644 server/events/templates/multi_project_apply.tmpl create mode 100644 server/events/templates/multi_project_plan.tmpl create mode 100644 server/events/templates/multi_project_version.tmpl create mode 100644 server/events/templates/plan_success_unwrapped.tmpl create mode 100644 server/events/templates/plan_success_wrapped.tmpl create mode 100644 server/events/templates/policy_check_success_unwrapped.tmpl create mode 100644 server/events/templates/policy_check_success_wrapped.tmpl create mode 100644 server/events/templates/single_project_apply.tmpl create mode 100644 server/events/templates/single_project_plan_success.tmpl create mode 100644 server/events/templates/single_project_plan_unsuccessful.tmpl create mode 100644 server/events/templates/single_project_version_success.tmpl create mode 100644 server/events/templates/single_project_version_unsuccessful.tmpl create mode 100644 server/events/templates/unwrapped_err.tmpl create mode 100644 server/events/templates/unwrapped_err_with_log.tmpl create mode 100644 server/events/templates/version_unwrapped_success.tmpl create mode 100644 server/events/templates/version_wrapped_success.tmpl create mode 100644 server/events/templates/wrapped_err.tmpl diff --git a/server/events/markdown_renderer.go b/server/events/markdown_renderer.go index c9605004cf..362da96f55 100644 --- a/server/events/markdown_renderer.go +++ b/server/events/markdown_renderer.go @@ -15,6 +15,7 @@ package events import ( "bytes" + "embed" "fmt" "strings" "text/template" @@ -33,8 +34,20 @@ var ( // maxUnwrappedLines is the maximum number of lines the Terraform output // can be before we wrap it in an expandable template. maxUnwrappedLines = 12 + + //go:embed templates/* + templatesFS embed.FS + templates *template.Template ) +func init() { + templates, _ = template.New("").Funcs(sprig.TxtFuncMap()).ParseFS(templatesFS, "templates/*.tmpl") + if overrides, err := templates.ParseFiles("templates/*"); err == nil { + // doesn't override if templates directory doesn't exist + templates = overrides + } +} + // MarkdownRenderer renders responses as markdown. type MarkdownRenderer struct { // GitlabSupportsCommonMark is true if the version of GitLab we're @@ -113,10 +126,10 @@ func (m *MarkdownRenderer) Render(res command.Result, cmdName command.Name, log EnableDiffMarkdownFormat: m.EnableDiffMarkdownFormat, } if res.Error != nil { - return m.renderTemplate(unwrappedErrWithLogTmpl, errData{res.Error.Error(), common}) + return m.renderTemplate(templates.Lookup("unwrappedErrWithLog"), errData{res.Error.Error(), common}) } if res.Failure != "" { - return m.renderTemplate(failureWithLogTmpl, failureData{res.Failure, common}) + return m.renderTemplate(templates.Lookup("failureWithLog"), failureData{res.Failure, common}) } return m.renderProjectResults(res.ProjectResults, common, vcsHost) } @@ -134,9 +147,9 @@ func (m *MarkdownRenderer) renderProjectResults(results []command.ProjectResult, ProjectName: result.ProjectName, } if result.Error != nil { - tmpl := unwrappedErrTmpl + tmpl := templates.Lookup("unwrappedErr") if m.shouldUseWrappedTmpl(vcsHost, result.Error.Error()) { - tmpl = wrappedErrTmpl + tmpl = templates.Lookup("wrappedErr") } resultData.Rendered = m.renderTemplate(tmpl, struct { Command string @@ -146,7 +159,7 @@ func (m *MarkdownRenderer) renderProjectResults(results []command.ProjectResult, Error: result.Error.Error(), }) } else if result.Failure != "" { - resultData.Rendered = m.renderTemplate(failureTmpl, struct { + resultData.Rendered = m.renderTemplate(templates.Lookup("failure"), struct { Command string Failure string }{ @@ -155,29 +168,29 @@ func (m *MarkdownRenderer) renderProjectResults(results []command.ProjectResult, }) } else if result.PlanSuccess != nil { if m.shouldUseWrappedTmpl(vcsHost, result.PlanSuccess.TerraformOutput) { - resultData.Rendered = m.renderTemplate(planSuccessWrappedTmpl, planSuccessData{PlanSuccess: *result.PlanSuccess, PlanSummary: result.PlanSuccess.Summary(), PlanWasDeleted: common.PlansDeleted, DisableApply: common.DisableApply, DisableRepoLocking: common.DisableRepoLocking, EnableDiffMarkdownFormat: common.EnableDiffMarkdownFormat}) + resultData.Rendered = m.renderTemplate(templates.Lookup("planSuccessWrapped"), planSuccessData{PlanSuccess: *result.PlanSuccess, PlanSummary: result.PlanSuccess.Summary(), PlanWasDeleted: common.PlansDeleted, DisableApply: common.DisableApply, DisableRepoLocking: common.DisableRepoLocking, EnableDiffMarkdownFormat: common.EnableDiffMarkdownFormat}) } else { - resultData.Rendered = m.renderTemplate(planSuccessUnwrappedTmpl, planSuccessData{PlanSuccess: *result.PlanSuccess, PlanWasDeleted: common.PlansDeleted, DisableApply: common.DisableApply, DisableRepoLocking: common.DisableRepoLocking, EnableDiffMarkdownFormat: common.EnableDiffMarkdownFormat}) + resultData.Rendered = m.renderTemplate(templates.Lookup("planSuccessUnwrapped"), planSuccessData{PlanSuccess: *result.PlanSuccess, PlanWasDeleted: common.PlansDeleted, DisableApply: common.DisableApply, DisableRepoLocking: common.DisableRepoLocking, EnableDiffMarkdownFormat: common.EnableDiffMarkdownFormat}) } numPlanSuccesses++ } else if result.PolicyCheckSuccess != nil { if m.shouldUseWrappedTmpl(vcsHost, result.PolicyCheckSuccess.PolicyCheckOutput) { - resultData.Rendered = m.renderTemplate(policyCheckSuccessWrappedTmpl, policyCheckSuccessData{PolicyCheckSuccess: *result.PolicyCheckSuccess}) + resultData.Rendered = m.renderTemplate(templates.Lookup("policyCheckSuccessWrapped"), policyCheckSuccessData{PolicyCheckSuccess: *result.PolicyCheckSuccess}) } else { - resultData.Rendered = m.renderTemplate(policyCheckSuccessUnwrappedTmpl, policyCheckSuccessData{PolicyCheckSuccess: *result.PolicyCheckSuccess}) + resultData.Rendered = m.renderTemplate(templates.Lookup("policyCheckSuccessUnwrapped"), policyCheckSuccessData{PolicyCheckSuccess: *result.PolicyCheckSuccess}) } numPolicyCheckSuccesses++ } else if result.ApplySuccess != "" { if m.shouldUseWrappedTmpl(vcsHost, result.ApplySuccess) { - resultData.Rendered = m.renderTemplate(applyWrappedSuccessTmpl, struct{ Output string }{result.ApplySuccess}) + resultData.Rendered = m.renderTemplate(templates.Lookup("applyWrappedSuccess"), struct{ Output string }{result.ApplySuccess}) } else { - resultData.Rendered = m.renderTemplate(applyUnwrappedSuccessTmpl, struct{ Output string }{result.ApplySuccess}) + resultData.Rendered = m.renderTemplate(templates.Lookup("applyUnwrappedSuccess"), struct{ Output string }{result.ApplySuccess}) } } else if result.VersionSuccess != "" { if m.shouldUseWrappedTmpl(vcsHost, result.VersionSuccess) { - resultData.Rendered = m.renderTemplate(versionWrappedSuccessTmpl, struct{ Output string }{result.VersionSuccess}) + resultData.Rendered = m.renderTemplate(templates.Lookup("versionWrappedSuccess"), struct{ Output string }{result.VersionSuccess}) } else { - resultData.Rendered = m.renderTemplate(versionUnwrappedSuccessTmpl, struct{ Output string }{result.VersionSuccess}) + resultData.Rendered = m.renderTemplate(templates.Lookup("versionUnwrappedSuccess"), struct{ Output string }{result.VersionSuccess}) } numVersionSuccesses++ } else { @@ -189,28 +202,28 @@ func (m *MarkdownRenderer) renderProjectResults(results []command.ProjectResult, var tmpl *template.Template switch { case len(resultsTmplData) == 1 && common.Command == planCommandTitle && numPlanSuccesses > 0: - tmpl = singleProjectPlanSuccessTmpl + tmpl = templates.Lookup("singleProjectPlanSuccess") case len(resultsTmplData) == 1 && common.Command == planCommandTitle && numPlanSuccesses == 0: - tmpl = singleProjectPlanUnsuccessfulTmpl + tmpl = templates.Lookup("singleProjectPlanUnsuccessful") case len(resultsTmplData) == 1 && common.Command == policyCheckCommandTitle && numPolicyCheckSuccesses > 0: - tmpl = singleProjectPlanSuccessTmpl + tmpl = templates.Lookup("singleProjectPlanSuccess") case len(resultsTmplData) == 1 && common.Command == policyCheckCommandTitle && numPolicyCheckSuccesses == 0: - tmpl = singleProjectPlanUnsuccessfulTmpl + tmpl = templates.Lookup("singleProjectPlanUnsuccessful") case len(resultsTmplData) == 1 && common.Command == versionCommandTitle && numVersionSuccesses > 0: - tmpl = singleProjectVersionSuccessTmpl + tmpl = templates.Lookup("singleProjectVersionSuccess") case len(resultsTmplData) == 1 && common.Command == versionCommandTitle && numVersionSuccesses == 0: - tmpl = singleProjectVersionUnsuccessfulTmpl + tmpl = templates.Lookup("singleProjectVersionUnsuccessful") case len(resultsTmplData) == 1 && common.Command == applyCommandTitle: - tmpl = singleProjectApplyTmpl + tmpl = templates.Lookup("singleProjectApply") case common.Command == planCommandTitle, common.Command == policyCheckCommandTitle: - tmpl = multiProjectPlanTmpl + tmpl = templates.Lookup("multiProjectPlan") case common.Command == approvePoliciesCommandTitle: - tmpl = approveAllProjectsTmpl + tmpl = templates.Lookup("approveAllProjects") case common.Command == applyCommandTitle: - tmpl = multiProjectApplyTmpl + tmpl = templates.Lookup("multiProjectApply") case common.Command == versionCommandTitle: - tmpl = multiProjectVersionTmpl + tmpl = templates.Lookup("multiProjectVersion") default: return "no template matched–this is a bug" } @@ -245,146 +258,3 @@ func (m *MarkdownRenderer) renderTemplate(tmpl *template.Template, data interfac } return buf.String() } - -// todo: refactor to remove duplication #refactor -var singleProjectApplyTmpl = template.Must(template.New("").Parse( - "{{$result := index .Results 0}}Ran {{.Command}} for {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}`\n\n{{$result.Rendered}}\n" + logTmpl)) -var singleProjectPlanSuccessTmpl = template.Must(template.New("").Parse( - "{{$result := index .Results 0}}Ran {{.Command}} for {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}`\n\n{{$result.Rendered}}\n" + - "\n" + - "{{ if ne .DisableApplyAll true }}---\n" + - "* :fast_forward: To **apply** all unapplied plans from this pull request, comment:\n" + - " * `atlantis apply`\n" + - "* :put_litter_in_its_place: To delete all plans and locks for the PR, comment:\n" + - " * `atlantis unlock`{{ end }}" + logTmpl)) -var singleProjectPlanUnsuccessfulTmpl = template.Must(template.New("").Parse( - "{{$result := index .Results 0}}Ran {{.Command}} for dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}`\n\n" + - "{{$result.Rendered}}\n" + logTmpl)) -var singleProjectVersionSuccessTmpl = template.Must(template.New("").Parse( - "{{$result := index .Results 0}}Ran {{.Command}} for {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}`\n\n{{$result.Rendered}}\n" + logTmpl)) -var singleProjectVersionUnsuccessfulTmpl = template.Must(template.New("").Parse( - "{{$result := index .Results 0}}Ran {{.Command}} for dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}`\n\n{{$result.Rendered}}\n" + logTmpl)) -var approveAllProjectsTmpl = template.Must(template.New("").Funcs(sprig.TxtFuncMap()).Parse( - "Approved Policies for {{ len .Results }} projects:\n\n" + - "{{ range $result := .Results }}" + - "1. {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}`\n" + - "{{end}}\n" + logTmpl)) -var multiProjectPlanTmpl = template.Must(template.New("").Funcs(sprig.TxtFuncMap()).Parse( - "Ran {{.Command}} for {{ len .Results }} projects:\n\n" + - "{{ range $result := .Results }}" + - "1. {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}`\n" + - "{{end}}\n" + - "{{ $disableApplyAll := .DisableApplyAll }}{{ range $i, $result := .Results }}" + - "### {{add $i 1}}. {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}`\n" + - "{{$result.Rendered}}\n\n" + - "{{ if ne $disableApplyAll true }}---\n{{end}}{{end}}{{ if ne .DisableApplyAll true }}{{ if and (gt (len .Results) 0) (not .PlansDeleted) }}* :fast_forward: To **apply** all unapplied plans from this pull request, comment:\n" + - " * `atlantis apply`\n" + - "* :put_litter_in_its_place: To delete all plans and locks for the PR, comment:\n" + - " * `atlantis unlock`" + - "{{end}}{{end}}" + - logTmpl)) -var multiProjectApplyTmpl = template.Must(template.New("").Funcs(sprig.TxtFuncMap()).Parse( - "Ran {{.Command}} for {{ len .Results }} projects:\n\n" + - "{{ range $result := .Results }}" + - "1. {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}`\n" + - "{{end}}\n" + - "{{ range $i, $result := .Results }}" + - "### {{add $i 1}}. {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}`\n" + - "{{$result.Rendered}}\n\n" + - "---\n{{end}}" + - logTmpl)) -var multiProjectVersionTmpl = template.Must(template.New("").Funcs(sprig.TxtFuncMap()).Parse( - "Ran {{.Command}} for {{ len .Results }} projects:\n\n" + - "{{ range $result := .Results }}" + - "1. {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}`\n" + - "{{end}}\n" + - "{{ range $i, $result := .Results }}" + - "### {{add $i 1}}. {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}`\n" + - "{{$result.Rendered}}\n\n" + - "---\n{{end}}" + - logTmpl)) -var planSuccessUnwrappedTmpl = template.Must(template.New("").Parse( - "```diff\n" + - "{{ if .EnableDiffMarkdownFormat }}{{.DiffMarkdownFormattedTerraformOutput}}{{else}}{{.TerraformOutput}}{{end}}\n" + - "```\n\n" + planNextSteps + - "{{ if .HasDiverged }}\n\n:warning: The branch we're merging into is ahead, it is recommended to pull new commits first.{{end}}")) - -var planSuccessWrappedTmpl = template.Must(template.New("").Parse( - "
Show Output\n\n" + - "```diff\n" + - "{{ if .EnableDiffMarkdownFormat }}{{.DiffMarkdownFormattedTerraformOutput}}{{else}}{{.TerraformOutput}}{{end}}\n" + - "```\n\n" + - planNextSteps + "\n" + - "
" + "\n" + - "{{.PlanSummary}}" + - "{{ if .HasDiverged }}\n\n:warning: The branch we're merging into is ahead, it is recommended to pull new commits first.{{end}}")) - -var policyCheckSuccessUnwrappedTmpl = template.Must(template.New("").Parse( - "```diff\n" + - "{{.PolicyCheckOutput}}\n" + - "```\n\n" + policyCheckNextSteps + - "{{ if .HasDiverged }}\n\n:warning: The branch we're merging into is ahead, it is recommended to pull new commits first.{{end}}")) - -var policyCheckSuccessWrappedTmpl = template.Must(template.New("").Parse( - "
Show Output\n\n" + - "```diff\n" + - "{{.PolicyCheckOutput}}\n" + - "```\n\n" + - policyCheckNextSteps + "\n" + - "
" + - "{{ if .HasDiverged }}\n\n:warning: The branch we're merging into is ahead, it is recommended to pull new commits first.{{end}}")) - -// policyCheckNextSteps are instructions appended after successful plans as to what -// to do next. -var policyCheckNextSteps = "* :arrow_forward: To **apply** this plan, comment:\n" + - " * `{{.ApplyCmd}}`\n" + - "* :put_litter_in_its_place: To **delete** this plan click [here]({{.LockURL}})\n" + - "* :repeat: To re-run policies **plan** this project again by commenting:\n" + - " * `{{.RePlanCmd}}`" - -// planNextSteps are instructions appended after successful plans as to what -// to do next. -var planNextSteps = "{{ if .PlanWasDeleted }}This plan was not saved because one or more projects failed and automerge requires all plans pass.{{ else }}" + - "{{ if not .DisableApply }}* :arrow_forward: To **apply** this plan, comment:\n" + - " * `{{.ApplyCmd}}`\n{{end}}" + - "{{ if not .DisableRepoLocking }}* :put_litter_in_its_place: To **delete** this plan click [here]({{.LockURL}})\n{{end}}" + - "* :repeat: To **plan** this project again, comment:\n" + - " * `{{.RePlanCmd}}`{{end}}" -var applyUnwrappedSuccessTmpl = template.Must(template.New("").Parse( - "```diff\n" + - "{{.Output}}\n" + - "```")) -var applyWrappedSuccessTmpl = template.Must(template.New("").Parse( - "
Show Output\n\n" + - "```diff\n" + - "{{.Output}}\n" + - "```\n" + - "
")) -var versionUnwrappedSuccessTmpl = template.Must(template.New("").Parse("```\n{{.Output}}```")) -var versionWrappedSuccessTmpl = template.Must(template.New("").Parse( - "
Show Output\n\n" + - "```\n" + - "{{.Output}}" + - "```\n" + - "
")) -var unwrappedErrTmplText = "**{{.Command}} Error**\n" + - "```\n" + - "{{.Error}}\n" + - "```" + - "{{ if eq .Command \"Policy Check\" }}" + - "\n* :heavy_check_mark: To **approve** failing policies an authorized approver can comment:\n" + - " * `atlantis approve_policies`\n" + - "* :repeat: Or, address the policy failure by modifying the codebase and re-planning.\n" + - "{{ end }}" -var wrappedErrTmplText = "**{{.Command}} Error**\n" + - "
Show Output\n\n" + - "```\n" + - "{{.Error}}\n" + - "```\n
" -var unwrappedErrTmpl = template.Must(template.New("").Parse(unwrappedErrTmplText)) -var unwrappedErrWithLogTmpl = template.Must(template.New("").Parse(unwrappedErrTmplText + logTmpl)) -var wrappedErrTmpl = template.Must(template.New("").Parse(wrappedErrTmplText)) -var failureTmplText = "**{{.Command}} Failed**: {{.Failure}}" -var failureTmpl = template.Must(template.New("").Parse(failureTmplText)) -var failureWithLogTmpl = template.Must(template.New("").Parse(failureTmplText + logTmpl)) -var logTmpl = "{{if .Verbose}}\n
Log\n

\n\n```\n{{.Log}}```\n

{{end}}\n" diff --git a/server/events/templates/apply_unwrapped_success.tmpl b/server/events/templates/apply_unwrapped_success.tmpl new file mode 100644 index 0000000000..30a894b231 --- /dev/null +++ b/server/events/templates/apply_unwrapped_success.tmpl @@ -0,0 +1,5 @@ +{{ define "applyUnwrappedSuccess" -}} +```diff +{{.Output}} +``` +{{- end }} diff --git a/server/events/templates/apply_wrapped_success.tmpl b/server/events/templates/apply_wrapped_success.tmpl new file mode 100644 index 0000000000..a3ddd9240a --- /dev/null +++ b/server/events/templates/apply_wrapped_success.tmpl @@ -0,0 +1,8 @@ +{{ define "applyWrappedSuccess" -}} +
Show Output + +```diff +{{.Output}} +``` +
+{{- end }} diff --git a/server/events/templates/approve_all_projects.tmpl b/server/events/templates/approve_all_projects.tmpl new file mode 100644 index 0000000000..05c60320da --- /dev/null +++ b/server/events/templates/approve_all_projects.tmpl @@ -0,0 +1,13 @@ +{{ define "approveAllProjects" -}} +Approved Policies for {{ len .Results }} projects: + +{{ range $result := .Results }}1. {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` +{{end}} +{{if .Verbose}} +
Log +

+ +``` +{{.Log}}``` +

{{end}} +{{ end }} diff --git a/server/events/templates/failure.tmpl b/server/events/templates/failure.tmpl new file mode 100644 index 0000000000..ea7078066f --- /dev/null +++ b/server/events/templates/failure.tmpl @@ -0,0 +1,3 @@ +{{ define "failure" -}} +**{{.Command}} Failed**: {{.Failure}} +{{- end }} diff --git a/server/events/templates/failure_with_log.tmpl b/server/events/templates/failure_with_log.tmpl new file mode 100644 index 0000000000..8de8cc517b --- /dev/null +++ b/server/events/templates/failure_with_log.tmpl @@ -0,0 +1,9 @@ +{{ define "failureWithLog" -}} +**{{.Command}} Failed**: {{.Failure}}{{if .Verbose}} +
Log +

+ +``` +{{.Log}}``` +

{{end}} +{{ end }} diff --git a/server/events/templates/multi_project_apply.tmpl b/server/events/templates/multi_project_apply.tmpl new file mode 100644 index 0000000000..6585fb0f48 --- /dev/null +++ b/server/events/templates/multi_project_apply.tmpl @@ -0,0 +1,17 @@ +{{ define "multiProjectApply" -}} +Ran {{.Command}} for {{ len .Results }} projects: + +{{ range $result := .Results }}1. {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` +{{end}} +{{ range $i, $result := .Results }}### {{add $i 1}}. {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` +{{$result.Rendered}} + +--- +{{end}}{{if .Verbose}} +
Log +

+ +``` +{{.Log}}``` +

{{end}} +{{ end }} diff --git a/server/events/templates/multi_project_plan.tmpl b/server/events/templates/multi_project_plan.tmpl new file mode 100644 index 0000000000..9cf94bf933 --- /dev/null +++ b/server/events/templates/multi_project_plan.tmpl @@ -0,0 +1,20 @@ +{{ define "multiProjectPlan" -}} +Ran {{.Command}} for {{ len .Results }} projects: + +{{ range $result := .Results }}1. {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` +{{end}} +{{ $disableApplyAll := .DisableApplyAll }}{{ range $i, $result := .Results }}### {{add $i 1}}. {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` +{{$result.Rendered}} + +{{ if ne $disableApplyAll true }}--- +{{end}}{{end}}{{ if ne .DisableApplyAll true }}{{ if and (gt (len .Results) 0) (not .PlansDeleted) }}* :fast_forward: To **apply** all unapplied plans from this pull request, comment: + * `atlantis apply` +* :put_litter_in_its_place: To delete all plans and locks for the PR, comment: + * `atlantis unlock`{{end}}{{end}}{{if .Verbose}} +
Log +

+ +``` +{{.Log}}``` +

{{end}} +{{ end }} diff --git a/server/events/templates/multi_project_version.tmpl b/server/events/templates/multi_project_version.tmpl new file mode 100644 index 0000000000..5af28aa79e --- /dev/null +++ b/server/events/templates/multi_project_version.tmpl @@ -0,0 +1,17 @@ +{{ define "multiProjectVersion" -}} +Ran {{.Command}} for {{ len .Results }} projects: + +{{ range $result := .Results }}1. {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` +{{end}} +{{ range $i, $result := .Results }}### {{add $i 1}}. {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` +{{$result.Rendered}} + +--- +{{end}}{{if .Verbose}} +
Log +

+ +``` +{{.Log}}``` +

{{end}} +{{ end }} diff --git a/server/events/templates/plan_success_unwrapped.tmpl b/server/events/templates/plan_success_unwrapped.tmpl new file mode 100644 index 0000000000..adb0258768 --- /dev/null +++ b/server/events/templates/plan_success_unwrapped.tmpl @@ -0,0 +1,13 @@ +{{ define "planSuccessUnwrapped" -}} +```diff +{{ if .EnableDiffMarkdownFormat }}{{.DiffMarkdownFormattedTerraformOutput}}{{else}}{{.TerraformOutput}}{{end}} +``` + +{{ if .PlanWasDeleted }}This plan was not saved because one or more projects failed and automerge requires all plans pass.{{ else }}{{ if not .DisableApply }}* :arrow_forward: To **apply** this plan, comment: + * `{{.ApplyCmd}}` +{{end}}{{ if not .DisableRepoLocking }}* :put_litter_in_its_place: To **delete** this plan click [here]({{.LockURL}}) +{{end}}* :repeat: To **plan** this project again, comment: + * `{{.RePlanCmd}}`{{end}}{{ if .HasDiverged }} + +:warning: The branch we're merging into is ahead, it is recommended to pull new commits first.{{end}} +{{- end }} diff --git a/server/events/templates/plan_success_wrapped.tmpl b/server/events/templates/plan_success_wrapped.tmpl new file mode 100644 index 0000000000..ffbe7c4c46 --- /dev/null +++ b/server/events/templates/plan_success_wrapped.tmpl @@ -0,0 +1,17 @@ +{{ define "planSuccessWrapped" -}} +
Show Output + +```diff +{{ if .EnableDiffMarkdownFormat }}{{.DiffMarkdownFormattedTerraformOutput}}{{else}}{{.TerraformOutput}}{{end}} +``` + +{{ if .PlanWasDeleted }}This plan was not saved because one or more projects failed and automerge requires all plans pass.{{ else }}{{ if not .DisableApply }}* :arrow_forward: To **apply** this plan, comment: + * `{{.ApplyCmd}}` +{{end}}{{ if not .DisableRepoLocking }}* :put_litter_in_its_place: To **delete** this plan click [here]({{.LockURL}}) +{{end}}* :repeat: To **plan** this project again, comment: + * `{{.RePlanCmd}}`{{end}} +
+{{.PlanSummary}}{{ if .HasDiverged }} + +:warning: The branch we're merging into is ahead, it is recommended to pull new commits first.{{end}} +{{- end }} diff --git a/server/events/templates/policy_check_success_unwrapped.tmpl b/server/events/templates/policy_check_success_unwrapped.tmpl new file mode 100644 index 0000000000..d594ec030a --- /dev/null +++ b/server/events/templates/policy_check_success_unwrapped.tmpl @@ -0,0 +1,13 @@ +{{ define "policyCheckSuccessUnwrapped" -}} +```diff +{{.PolicyCheckOutput}} +``` + +* :arrow_forward: To **apply** this plan, comment: + * `{{.ApplyCmd}}` +* :put_litter_in_its_place: To **delete** this plan click [here]({{.LockURL}}) +* :repeat: To re-run policies **plan** this project again by commenting: + * `{{.RePlanCmd}}`{{ if .HasDiverged }} + +:warning: The branch we're merging into is ahead, it is recommended to pull new commits first.{{end}} +{{- end }} diff --git a/server/events/templates/policy_check_success_wrapped.tmpl b/server/events/templates/policy_check_success_wrapped.tmpl new file mode 100644 index 0000000000..89c34d2152 --- /dev/null +++ b/server/events/templates/policy_check_success_wrapped.tmpl @@ -0,0 +1,16 @@ +{{ define "policyCheckSuccessWrapped" -}} +
Show Output + +```diff +{{.PolicyCheckOutput}}\n" + +``` + +* :arrow_forward: To **apply** this plan, comment: + * `{{.ApplyCmd}}` +* :put_litter_in_its_place: To **delete** this plan click [here]({{.LockURL}}) +* :repeat: To re-run policies **plan** this project again by commenting: + * `{{.RePlanCmd}}` +
{{ if .HasDiverged }} + +:warning: The branch we're merging into is ahead, it is recommended to pull new commits first.{{end}} +{{- end }} diff --git a/server/events/templates/single_project_apply.tmpl b/server/events/templates/single_project_apply.tmpl new file mode 100644 index 0000000000..c3d0e65725 --- /dev/null +++ b/server/events/templates/single_project_apply.tmpl @@ -0,0 +1,12 @@ +{{ define "singleProjectApply" -}} +{{$result := index .Results 0}}Ran {{.Command}} for {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` + +{{$result.Rendered}} +{{if .Verbose}} +
Log +

+ +``` +{{.Log}}``` +

{{end}} +{{ end }} diff --git a/server/events/templates/single_project_plan_success.tmpl b/server/events/templates/single_project_plan_success.tmpl new file mode 100644 index 0000000000..44923cc837 --- /dev/null +++ b/server/events/templates/single_project_plan_success.tmpl @@ -0,0 +1,17 @@ +{{ define "singleProjectPlanSuccess" -}} +{{$result := index .Results 0}}Ran {{.Command}} for {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` + +{{$result.Rendered}} + +{{ if ne .DisableApplyAll true }}--- +* :fast_forward: To **apply** all unapplied plans from this pull request, comment: + * `atlantis apply` +* :put_litter_in_its_place: To delete all plans and locks for the PR, comment: + * `atlantis unlock`{{ end }}{{if .Verbose}} +
Log +

+ +``` +{{.Log}}``` +

{{end}} +{{ end }} diff --git a/server/events/templates/single_project_plan_unsuccessful.tmpl b/server/events/templates/single_project_plan_unsuccessful.tmpl new file mode 100644 index 0000000000..54f7625f94 --- /dev/null +++ b/server/events/templates/single_project_plan_unsuccessful.tmpl @@ -0,0 +1,12 @@ +{{ define "singleProjectPlanUnsuccessful" -}} +{{$result := index .Results 0}}Ran {{.Command}} for dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` + +{{$result.Rendered}} +{{if .Verbose}} +
Log +

+ +``` +{{.Log}}``` +

{{end}} +{{ end }} diff --git a/server/events/templates/single_project_version_success.tmpl b/server/events/templates/single_project_version_success.tmpl new file mode 100644 index 0000000000..4293446bf1 --- /dev/null +++ b/server/events/templates/single_project_version_success.tmpl @@ -0,0 +1,12 @@ +{{ define "singleProjectVersionSuccess" -}} +{{$result := index .Results 0}}Ran {{.Command}} for {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` + +{{$result.Rendered}} +{{if .Verbose}} +
Log +

+ +``` +{{.Log}}``` +

{{end}} +{{ end }} diff --git a/server/events/templates/single_project_version_unsuccessful.tmpl b/server/events/templates/single_project_version_unsuccessful.tmpl new file mode 100644 index 0000000000..b6e2c88b90 --- /dev/null +++ b/server/events/templates/single_project_version_unsuccessful.tmpl @@ -0,0 +1,12 @@ +{{ define "singleProjectVersionUnsuccessful" -}} +{{$result := index .Results 0}}Ran {{.Command}} for dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` + +{{$result.Rendered}} +{{if .Verbose}} +
Log +

+ +``` +{{.Log}}``` +

{{end}} +{{ end }} diff --git a/server/events/templates/unwrapped_err.tmpl b/server/events/templates/unwrapped_err.tmpl new file mode 100644 index 0000000000..3d732e4ae6 --- /dev/null +++ b/server/events/templates/unwrapped_err.tmpl @@ -0,0 +1,10 @@ +{{ define "unwrappedErr" -}} +**{{.Command}} Error** +``` +{{.Error}} +```{{ if eq .Command "Policy Check" }} +* :heavy_check_mark: To **approve** failing policies an authorized approver can comment: + * `atlantis approve_policies` +* :repeat: Or, address the policy failure by modifying the codebase and re-planning. +{{ end }} +{{- end }} diff --git a/server/events/templates/unwrapped_err_with_log.tmpl b/server/events/templates/unwrapped_err_with_log.tmpl new file mode 100644 index 0000000000..0e30ed5d78 --- /dev/null +++ b/server/events/templates/unwrapped_err_with_log.tmpl @@ -0,0 +1,16 @@ +{{ define "unwrappedErrWithLog" -}} +**{{.Command}} Error** +``` +{{.Error}} +```{{ if eq .Command "Policy Check" }} +* :heavy_check_mark: To **approve** failing policies an authorized approver can comment: + * `atlantis approve_policies` +* :repeat: Or, address the policy failure by modifying the codebase and re-planning. +{{ end }}{{if .Verbose}} +
Log +

+ +``` +{{.Log}}``` +

{{end}} +{{ end }} diff --git a/server/events/templates/version_unwrapped_success.tmpl b/server/events/templates/version_unwrapped_success.tmpl new file mode 100644 index 0000000000..cf513e633d --- /dev/null +++ b/server/events/templates/version_unwrapped_success.tmpl @@ -0,0 +1,4 @@ +{{ define "versionUnwrappedSuccess" -}} +``` +{{.Output}}``` +{{ end }} diff --git a/server/events/templates/version_wrapped_success.tmpl b/server/events/templates/version_wrapped_success.tmpl new file mode 100644 index 0000000000..f5c3c116f3 --- /dev/null +++ b/server/events/templates/version_wrapped_success.tmpl @@ -0,0 +1,8 @@ +{{ define "versionWrappedSuccess" -}} +
Show Output + +``` +{{.Output}} +``` +
+{{- end }} diff --git a/server/events/templates/wrapped_err.tmpl b/server/events/templates/wrapped_err.tmpl new file mode 100644 index 0000000000..39050b92dd --- /dev/null +++ b/server/events/templates/wrapped_err.tmpl @@ -0,0 +1,9 @@ +{{ define "wrappedErr" -}} +**{{.Command}} Error** +
Show Output + +``` +{{.Error}} +``` +
+{{- end }} From 7a2fb61b195b08f4840126f19d167a4eb9558172 Mon Sep 17 00:00:00 2001 From: Ricard Bejarano Date: Tue, 27 Sep 2022 17:20:12 +0200 Subject: [PATCH 2/7] made default templates DRY --- .../templates/apply_wrapped_success.tmpl | 4 +--- .../events/templates/approve_all_projects.tmpl | 8 +------- server/events/templates/diverged.tmpl | 5 +++++ server/events/templates/failure_with_log.tmpl | 8 +------- server/events/templates/log.tmpl | 9 +++++++++ .../events/templates/multi_project_apply.tmpl | 13 ++----------- .../events/templates/multi_project_header.tmpl | 6 ++++++ .../events/templates/multi_project_plan.tmpl | 13 ++----------- .../templates/multi_project_version.tmpl | 18 ++---------------- .../templates/plan_success_unwrapped.tmpl | 4 +--- .../events/templates/plan_success_wrapped.tmpl | 4 +--- .../policy_check_success_unwrapped.tmpl | 4 +--- .../policy_check_success_wrapped.tmpl | 6 ++---- .../events/templates/single_project_apply.tmpl | 8 +------- .../templates/single_project_plan_success.tmpl | 8 +------- .../single_project_plan_unsuccessful.tmpl | 8 +------- .../single_project_version_success.tmpl | 8 +------- .../single_project_version_unsuccessful.tmpl | 13 ++----------- .../templates/unwrapped_err_with_log.tmpl | 15 +-------------- .../templates/version_unwrapped_success.tmpl | 3 ++- .../templates/version_wrapped_success.tmpl | 4 +--- 21 files changed, 44 insertions(+), 125 deletions(-) create mode 100644 server/events/templates/diverged.tmpl create mode 100644 server/events/templates/log.tmpl create mode 100644 server/events/templates/multi_project_header.tmpl diff --git a/server/events/templates/apply_wrapped_success.tmpl b/server/events/templates/apply_wrapped_success.tmpl index a3ddd9240a..c5d16a3cd5 100644 --- a/server/events/templates/apply_wrapped_success.tmpl +++ b/server/events/templates/apply_wrapped_success.tmpl @@ -1,8 +1,6 @@ {{ define "applyWrappedSuccess" -}}
Show Output -```diff -{{.Output}} -``` +{{ template "applyUnwrappedSuccess" . }}
{{- end }} diff --git a/server/events/templates/approve_all_projects.tmpl b/server/events/templates/approve_all_projects.tmpl index 05c60320da..8ce1d2d953 100644 --- a/server/events/templates/approve_all_projects.tmpl +++ b/server/events/templates/approve_all_projects.tmpl @@ -3,11 +3,5 @@ Approved Policies for {{ len .Results }} projects: {{ range $result := .Results }}1. {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` {{end}} -{{if .Verbose}} -
Log -

- -``` -{{.Log}}``` -

{{end}} +{{ template "log" . }} {{ end }} diff --git a/server/events/templates/diverged.tmpl b/server/events/templates/diverged.tmpl new file mode 100644 index 0000000000..092f5d8a60 --- /dev/null +++ b/server/events/templates/diverged.tmpl @@ -0,0 +1,5 @@ +{{ define "diverged" -}} +{{ if .HasDiverged }} + +:warning: The branch we're merging into is ahead, it is recommended to pull new commits first.{{end}} +{{- end }} diff --git a/server/events/templates/failure_with_log.tmpl b/server/events/templates/failure_with_log.tmpl index 8de8cc517b..371be01a12 100644 --- a/server/events/templates/failure_with_log.tmpl +++ b/server/events/templates/failure_with_log.tmpl @@ -1,9 +1,3 @@ {{ define "failureWithLog" -}} -**{{.Command}} Failed**: {{.Failure}}{{if .Verbose}} -
Log -

- -``` -{{.Log}}``` -

{{end}} +{{ template "failure" . }}{{ template "log" . }} {{ end }} diff --git a/server/events/templates/log.tmpl b/server/events/templates/log.tmpl new file mode 100644 index 0000000000..4dbb97c7a8 --- /dev/null +++ b/server/events/templates/log.tmpl @@ -0,0 +1,9 @@ +{{ define "log" -}} +{{if .Verbose}} +
Log +

+ +``` +{{.Log}}``` +

{{end}} +{{- end }} diff --git a/server/events/templates/multi_project_apply.tmpl b/server/events/templates/multi_project_apply.tmpl index 6585fb0f48..ff17c53df8 100644 --- a/server/events/templates/multi_project_apply.tmpl +++ b/server/events/templates/multi_project_apply.tmpl @@ -1,17 +1,8 @@ {{ define "multiProjectApply" -}} -Ran {{.Command}} for {{ len .Results }} projects: - -{{ range $result := .Results }}1. {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` -{{end}} +{{ template "multiProjectHeader" . }} {{ range $i, $result := .Results }}### {{add $i 1}}. {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` {{$result.Rendered}} --- -{{end}}{{if .Verbose}} -
Log -

- -``` -{{.Log}}``` -

{{end}} +{{end}}{{ template "log" . }} {{ end }} diff --git a/server/events/templates/multi_project_header.tmpl b/server/events/templates/multi_project_header.tmpl new file mode 100644 index 0000000000..66d0f39745 --- /dev/null +++ b/server/events/templates/multi_project_header.tmpl @@ -0,0 +1,6 @@ +{{ define "multiProjectHeader" -}} +Ran {{.Command}} for {{ len .Results }} projects: + +{{ range $result := .Results }}1. {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` +{{end}} +{{- end }} diff --git a/server/events/templates/multi_project_plan.tmpl b/server/events/templates/multi_project_plan.tmpl index 9cf94bf933..9ee33c590f 100644 --- a/server/events/templates/multi_project_plan.tmpl +++ b/server/events/templates/multi_project_plan.tmpl @@ -1,8 +1,5 @@ {{ define "multiProjectPlan" -}} -Ran {{.Command}} for {{ len .Results }} projects: - -{{ range $result := .Results }}1. {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` -{{end}} +{{ template "multiProjectHeader" . }} {{ $disableApplyAll := .DisableApplyAll }}{{ range $i, $result := .Results }}### {{add $i 1}}. {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` {{$result.Rendered}} @@ -10,11 +7,5 @@ Ran {{.Command}} for {{ len .Results }} projects: {{end}}{{end}}{{ if ne .DisableApplyAll true }}{{ if and (gt (len .Results) 0) (not .PlansDeleted) }}* :fast_forward: To **apply** all unapplied plans from this pull request, comment: * `atlantis apply` * :put_litter_in_its_place: To delete all plans and locks for the PR, comment: - * `atlantis unlock`{{end}}{{end}}{{if .Verbose}} -
Log -

- -``` -{{.Log}}``` -

{{end}} + * `atlantis unlock`{{end}}{{end}}{{ template "log" . }} {{ end }} diff --git a/server/events/templates/multi_project_version.tmpl b/server/events/templates/multi_project_version.tmpl index 5af28aa79e..f8557b5244 100644 --- a/server/events/templates/multi_project_version.tmpl +++ b/server/events/templates/multi_project_version.tmpl @@ -1,17 +1,3 @@ {{ define "multiProjectVersion" -}} -Ran {{.Command}} for {{ len .Results }} projects: - -{{ range $result := .Results }}1. {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` -{{end}} -{{ range $i, $result := .Results }}### {{add $i 1}}. {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` -{{$result.Rendered}} - ---- -{{end}}{{if .Verbose}} -
Log -

- -``` -{{.Log}}``` -

{{end}} -{{ end }} +{{ template "multiProjectApply" . }} +{{- end }} diff --git a/server/events/templates/plan_success_unwrapped.tmpl b/server/events/templates/plan_success_unwrapped.tmpl index adb0258768..2514334053 100644 --- a/server/events/templates/plan_success_unwrapped.tmpl +++ b/server/events/templates/plan_success_unwrapped.tmpl @@ -7,7 +7,5 @@ * `{{.ApplyCmd}}` {{end}}{{ if not .DisableRepoLocking }}* :put_litter_in_its_place: To **delete** this plan click [here]({{.LockURL}}) {{end}}* :repeat: To **plan** this project again, comment: - * `{{.RePlanCmd}}`{{end}}{{ if .HasDiverged }} - -:warning: The branch we're merging into is ahead, it is recommended to pull new commits first.{{end}} + * `{{.RePlanCmd}}`{{end}}{{ template "diverged" . }} {{- end }} diff --git a/server/events/templates/plan_success_wrapped.tmpl b/server/events/templates/plan_success_wrapped.tmpl index ffbe7c4c46..23df190f5e 100644 --- a/server/events/templates/plan_success_wrapped.tmpl +++ b/server/events/templates/plan_success_wrapped.tmpl @@ -11,7 +11,5 @@ {{end}}* :repeat: To **plan** this project again, comment: * `{{.RePlanCmd}}`{{end}} -{{.PlanSummary}}{{ if .HasDiverged }} - -:warning: The branch we're merging into is ahead, it is recommended to pull new commits first.{{end}} +{{.PlanSummary}}{{ template "diverged" . }} {{- end }} diff --git a/server/events/templates/policy_check_success_unwrapped.tmpl b/server/events/templates/policy_check_success_unwrapped.tmpl index d594ec030a..34fa9757ac 100644 --- a/server/events/templates/policy_check_success_unwrapped.tmpl +++ b/server/events/templates/policy_check_success_unwrapped.tmpl @@ -7,7 +7,5 @@ * `{{.ApplyCmd}}` * :put_litter_in_its_place: To **delete** this plan click [here]({{.LockURL}}) * :repeat: To re-run policies **plan** this project again by commenting: - * `{{.RePlanCmd}}`{{ if .HasDiverged }} - -:warning: The branch we're merging into is ahead, it is recommended to pull new commits first.{{end}} + * `{{.RePlanCmd}}`{{ template "diverged" . }} {{- end }} diff --git a/server/events/templates/policy_check_success_wrapped.tmpl b/server/events/templates/policy_check_success_wrapped.tmpl index 89c34d2152..0066a385c0 100644 --- a/server/events/templates/policy_check_success_wrapped.tmpl +++ b/server/events/templates/policy_check_success_wrapped.tmpl @@ -2,7 +2,7 @@
Show Output ```diff -{{.PolicyCheckOutput}}\n" + +{{.PolicyCheckOutput}} ``` * :arrow_forward: To **apply** this plan, comment: @@ -10,7 +10,5 @@ * :put_litter_in_its_place: To **delete** this plan click [here]({{.LockURL}}) * :repeat: To re-run policies **plan** this project again by commenting: * `{{.RePlanCmd}}` -
{{ if .HasDiverged }} - -:warning: The branch we're merging into is ahead, it is recommended to pull new commits first.{{end}} +{{ template "diverged" . }} {{- end }} diff --git a/server/events/templates/single_project_apply.tmpl b/server/events/templates/single_project_apply.tmpl index c3d0e65725..da5174a0b5 100644 --- a/server/events/templates/single_project_apply.tmpl +++ b/server/events/templates/single_project_apply.tmpl @@ -2,11 +2,5 @@ {{$result := index .Results 0}}Ran {{.Command}} for {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` {{$result.Rendered}} -{{if .Verbose}} -
Log -

- -``` -{{.Log}}``` -

{{end}} +{{ template "log" . }} {{ end }} diff --git a/server/events/templates/single_project_plan_success.tmpl b/server/events/templates/single_project_plan_success.tmpl index 44923cc837..08c1624037 100644 --- a/server/events/templates/single_project_plan_success.tmpl +++ b/server/events/templates/single_project_plan_success.tmpl @@ -7,11 +7,5 @@ * :fast_forward: To **apply** all unapplied plans from this pull request, comment: * `atlantis apply` * :put_litter_in_its_place: To delete all plans and locks for the PR, comment: - * `atlantis unlock`{{ end }}{{if .Verbose}} -
Log -

- -``` -{{.Log}}``` -

{{end}} + * `atlantis unlock`{{ end }}{{ template "log" . }} {{ end }} diff --git a/server/events/templates/single_project_plan_unsuccessful.tmpl b/server/events/templates/single_project_plan_unsuccessful.tmpl index 54f7625f94..96157d9507 100644 --- a/server/events/templates/single_project_plan_unsuccessful.tmpl +++ b/server/events/templates/single_project_plan_unsuccessful.tmpl @@ -2,11 +2,5 @@ {{$result := index .Results 0}}Ran {{.Command}} for dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` {{$result.Rendered}} -{{if .Verbose}} -
Log -

- -``` -{{.Log}}``` -

{{end}} +{{ template "log" . }} {{ end }} diff --git a/server/events/templates/single_project_version_success.tmpl b/server/events/templates/single_project_version_success.tmpl index 4293446bf1..201cde63b5 100644 --- a/server/events/templates/single_project_version_success.tmpl +++ b/server/events/templates/single_project_version_success.tmpl @@ -2,11 +2,5 @@ {{$result := index .Results 0}}Ran {{.Command}} for {{ if $result.ProjectName }}project: `{{$result.ProjectName}}` {{ end }}dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` {{$result.Rendered}} -{{if .Verbose}} -
Log -

- -``` -{{.Log}}``` -

{{end}} +{{ template "log" . }} {{ end }} diff --git a/server/events/templates/single_project_version_unsuccessful.tmpl b/server/events/templates/single_project_version_unsuccessful.tmpl index b6e2c88b90..69985b3316 100644 --- a/server/events/templates/single_project_version_unsuccessful.tmpl +++ b/server/events/templates/single_project_version_unsuccessful.tmpl @@ -1,12 +1,3 @@ {{ define "singleProjectVersionUnsuccessful" -}} -{{$result := index .Results 0}}Ran {{.Command}} for dir: `{{$result.RepoRelDir}}` workspace: `{{$result.Workspace}}` - -{{$result.Rendered}} -{{if .Verbose}} -
Log -

- -``` -{{.Log}}``` -

{{end}} -{{ end }} +{{ template "singleProjectPlanUnsuccessful" . }} +{{- end }} diff --git a/server/events/templates/unwrapped_err_with_log.tmpl b/server/events/templates/unwrapped_err_with_log.tmpl index 0e30ed5d78..b27533a540 100644 --- a/server/events/templates/unwrapped_err_with_log.tmpl +++ b/server/events/templates/unwrapped_err_with_log.tmpl @@ -1,16 +1,3 @@ {{ define "unwrappedErrWithLog" -}} -**{{.Command}} Error** -``` -{{.Error}} -```{{ if eq .Command "Policy Check" }} -* :heavy_check_mark: To **approve** failing policies an authorized approver can comment: - * `atlantis approve_policies` -* :repeat: Or, address the policy failure by modifying the codebase and re-planning. -{{ end }}{{if .Verbose}} -
Log -

- -``` -{{.Log}}``` -

{{end}} +{{ template "unwrappedErr" . }}{{ template "log" . }} {{ end }} diff --git a/server/events/templates/version_unwrapped_success.tmpl b/server/events/templates/version_unwrapped_success.tmpl index cf513e633d..efcb6e5009 100644 --- a/server/events/templates/version_unwrapped_success.tmpl +++ b/server/events/templates/version_unwrapped_success.tmpl @@ -1,4 +1,5 @@ {{ define "versionUnwrappedSuccess" -}} ``` -{{.Output}}``` +{{.Output}} +``` {{ end }} diff --git a/server/events/templates/version_wrapped_success.tmpl b/server/events/templates/version_wrapped_success.tmpl index f5c3c116f3..84371ce1c2 100644 --- a/server/events/templates/version_wrapped_success.tmpl +++ b/server/events/templates/version_wrapped_success.tmpl @@ -1,8 +1,6 @@ {{ define "versionWrappedSuccess" -}}
Show Output -``` -{{.Output}} -``` +{{ template "versionUnwrappedSuccess" . }}
{{- end }} From 65bdf3b5c27b6511f8f9e06bf18d1337b7d66b2a Mon Sep 17 00:00:00 2001 From: Ross Strickland Date: Mon, 7 Nov 2022 09:20:21 -0600 Subject: [PATCH 3/7] Change init to anonymous function. --- server/events/markdown_renderer.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/server/events/markdown_renderer.go b/server/events/markdown_renderer.go index 362da96f55..754d5e462d 100644 --- a/server/events/markdown_renderer.go +++ b/server/events/markdown_renderer.go @@ -40,12 +40,14 @@ var ( templates *template.Template ) -func init() { - templates, _ = template.New("").Funcs(sprig.TxtFuncMap()).ParseFS(templatesFS, "templates/*.tmpl") - if overrides, err := templates.ParseFiles("templates/*"); err == nil { +func markdown_renderer() { + func (){ + templates, _ = template.New("").Funcs(sprig.TxtFuncMap()).ParseFS(templatesFS, "templates/*.tmpl") + if overrides, err := templates.ParseFiles("templates/*"); err == nil { // doesn't override if templates directory doesn't exist templates = overrides - } + } + }() } // MarkdownRenderer renders responses as markdown. From 8675e2ae5057161624fa5f4c36ce7c47aeac3eca Mon Sep 17 00:00:00 2001 From: Ross Strickland Date: Mon, 7 Nov 2022 09:57:28 -0600 Subject: [PATCH 4/7] Initialize anonymous function inside var --- server/events/markdown_renderer.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/server/events/markdown_renderer.go b/server/events/markdown_renderer.go index 754d5e462d..f6530df83b 100644 --- a/server/events/markdown_renderer.go +++ b/server/events/markdown_renderer.go @@ -38,17 +38,16 @@ var ( //go:embed templates/* templatesFS embed.FS templates *template.Template -) - -func markdown_renderer() { - func (){ + // Initialize templates using a var instead of init() + _ = func() struct{} { templates, _ = template.New("").Funcs(sprig.TxtFuncMap()).ParseFS(templatesFS, "templates/*.tmpl") if overrides, err := templates.ParseFiles("templates/*"); err == nil { - // doesn't override if templates directory doesn't exist - templates = overrides + // doesn't override if templates directory doesn't exist + templates = overrides } + return struct{}{} }() -} +) // MarkdownRenderer renders responses as markdown. type MarkdownRenderer struct { From cc806630911ffbaa2d4ace4f69280e1fb8024bb3 Mon Sep 17 00:00:00 2001 From: Ross Strickland Date: Tue, 8 Nov 2022 21:58:32 +0000 Subject: [PATCH 5/7] - Add flag/cfg param for custom template dir - Add test for custom template --- cmd/server.go | 198 +++++++++++------- cmd/server_test.go | 138 ++++++------ .../events/events_controller_e2e_test.go | 2 +- server/events/command_runner_test.go | 2 +- server/events/markdown_renderer.go | 43 +++- server/events/markdown_renderer_test.go | 164 ++++++++++++--- server/server.go | 17 +- server/user_config.go | 1 + 8 files changed, 368 insertions(+), 197 deletions(-) diff --git a/cmd/server.go b/cmd/server.go index 877aa6c25c..4190b66d52 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -37,63 +37,64 @@ import ( // 3. Add your flag's description etc. to the stringFlags, intFlags, or boolFlags slices. const ( // Flag names. - ADWebhookPasswordFlag = "azuredevops-webhook-password" // nolint: gosec - ADWebhookUserFlag = "azuredevops-webhook-user" - ADTokenFlag = "azuredevops-token" // nolint: gosec - ADUserFlag = "azuredevops-user" - ADHostnameFlag = "azuredevops-hostname" - AllowForkPRsFlag = "allow-fork-prs" - AllowRepoConfigFlag = "allow-repo-config" - AtlantisURLFlag = "atlantis-url" - AutomergeFlag = "automerge" - AutoplanFileListFlag = "autoplan-file-list" - BitbucketBaseURLFlag = "bitbucket-base-url" - BitbucketTokenFlag = "bitbucket-token" - BitbucketUserFlag = "bitbucket-user" - BitbucketWebhookSecretFlag = "bitbucket-webhook-secret" - ConfigFlag = "config" - CheckoutStrategyFlag = "checkout-strategy" - DataDirFlag = "data-dir" - DefaultTFVersionFlag = "default-tf-version" - DisableApplyAllFlag = "disable-apply-all" - DisableApplyFlag = "disable-apply" - DisableAutoplanFlag = "disable-autoplan" - DisableMarkdownFoldingFlag = "disable-markdown-folding" - DisableRepoLockingFlag = "disable-repo-locking" - EnablePolicyChecksFlag = "enable-policy-checks" - EnableRegExpCmdFlag = "enable-regexp-cmd" - EnableDiffMarkdownFormat = "enable-diff-markdown-format" - GHHostnameFlag = "gh-hostname" - GHTeamAllowlistFlag = "gh-team-allowlist" - GHTokenFlag = "gh-token" - GHUserFlag = "gh-user" - GHAppIDFlag = "gh-app-id" - GHAppKeyFlag = "gh-app-key" - GHAppKeyFileFlag = "gh-app-key-file" - GHAppSlugFlag = "gh-app-slug" - GHOrganizationFlag = "gh-org" - GHWebhookSecretFlag = "gh-webhook-secret" // nolint: gosec - GHAllowMergeableBypassApply = "gh-allow-mergeable-bypass-apply" // nolint: gosec - GitlabHostnameFlag = "gitlab-hostname" - GitlabTokenFlag = "gitlab-token" - GitlabUserFlag = "gitlab-user" - GitlabWebhookSecretFlag = "gitlab-webhook-secret" // nolint: gosec - APISecretFlag = "api-secret" - HidePrevPlanComments = "hide-prev-plan-comments" - LockingDBType = "locking-db-type" - LogLevelFlag = "log-level" - ParallelPoolSize = "parallel-pool-size" - StatsNamespace = "stats-namespace" - AllowDraftPRs = "allow-draft-prs" - PortFlag = "port" - RedisDB = "redis-db" - RedisHost = "redis-host" - RedisPassword = "redis-password" - RedisPort = "redis-port" - RedisTLSEnabled = "redis-tls-enabled" - RedisInsecureSkipVerify = "redis-insecure-skip-verify" - RepoConfigFlag = "repo-config" - RepoConfigJSONFlag = "repo-config-json" + ADWebhookPasswordFlag = "azuredevops-webhook-password" // nolint: gosec + ADWebhookUserFlag = "azuredevops-webhook-user" + ADTokenFlag = "azuredevops-token" // nolint: gosec + ADUserFlag = "azuredevops-user" + ADHostnameFlag = "azuredevops-hostname" + AllowForkPRsFlag = "allow-fork-prs" + AllowRepoConfigFlag = "allow-repo-config" + AtlantisURLFlag = "atlantis-url" + AutomergeFlag = "automerge" + AutoplanFileListFlag = "autoplan-file-list" + BitbucketBaseURLFlag = "bitbucket-base-url" + BitbucketTokenFlag = "bitbucket-token" + BitbucketUserFlag = "bitbucket-user" + BitbucketWebhookSecretFlag = "bitbucket-webhook-secret" + ConfigFlag = "config" + CheckoutStrategyFlag = "checkout-strategy" + DataDirFlag = "data-dir" + DefaultTFVersionFlag = "default-tf-version" + DisableApplyAllFlag = "disable-apply-all" + DisableApplyFlag = "disable-apply" + DisableAutoplanFlag = "disable-autoplan" + DisableMarkdownFoldingFlag = "disable-markdown-folding" + DisableRepoLockingFlag = "disable-repo-locking" + EnablePolicyChecksFlag = "enable-policy-checks" + EnableRegExpCmdFlag = "enable-regexp-cmd" + EnableDiffMarkdownFormat = "enable-diff-markdown-format" + GHHostnameFlag = "gh-hostname" + GHTeamAllowlistFlag = "gh-team-allowlist" + GHTokenFlag = "gh-token" + GHUserFlag = "gh-user" + GHAppIDFlag = "gh-app-id" + GHAppKeyFlag = "gh-app-key" + GHAppKeyFileFlag = "gh-app-key-file" + GHAppSlugFlag = "gh-app-slug" + GHOrganizationFlag = "gh-org" + GHWebhookSecretFlag = "gh-webhook-secret" // nolint: gosec + GHAllowMergeableBypassApply = "gh-allow-mergeable-bypass-apply" // nolint: gosec + GitlabHostnameFlag = "gitlab-hostname" + GitlabTokenFlag = "gitlab-token" + GitlabUserFlag = "gitlab-user" + GitlabWebhookSecretFlag = "gitlab-webhook-secret" // nolint: gosec + APISecretFlag = "api-secret" + HidePrevPlanComments = "hide-prev-plan-comments" + LockingDBType = "locking-db-type" + LogLevelFlag = "log-level" + MarkdownTemplateOverridesDirFlag = "markdown-template-overrides-dir" + ParallelPoolSize = "parallel-pool-size" + StatsNamespace = "stats-namespace" + AllowDraftPRs = "allow-draft-prs" + PortFlag = "port" + RedisDB = "redis-db" + RedisHost = "redis-host" + RedisPassword = "redis-password" + RedisPort = "redis-port" + RedisTLSEnabled = "redis-tls-enabled" + RedisInsecureSkipVerify = "redis-insecure-skip-verify" + RepoConfigFlag = "repo-config" + RepoConfigJSONFlag = "repo-config-json" // RepoWhitelistFlag is deprecated for RepoAllowlistFlag. RepoWhitelistFlag = "repo-whitelist" RepoAllowlistFlag = "repo-allowlist" @@ -121,30 +122,31 @@ const ( WebPasswordFlag = "web-password" // NOTE: Must manually set these as defaults in the setDefaults function. - DefaultADBasicUser = "" - DefaultADBasicPassword = "" - DefaultADHostname = "dev.azure.com" - DefaultAutoplanFileList = "**/*.tf,**/*.tfvars,**/*.tfvars.json,**/terragrunt.hcl,**/.terraform.lock.hcl" - DefaultCheckoutStrategy = "branch" - DefaultBitbucketBaseURL = bitbucketcloud.BaseURL - DefaultDataDir = "~/.atlantis" - DefaultGHHostname = "github.com" - DefaultGitlabHostname = "gitlab.com" - DefaultLockingDBType = "boltdb" - DefaultLogLevel = "info" - DefaultParallelPoolSize = 15 - DefaultStatsNamespace = "atlantis" - DefaultPort = 4141 - DefaultRedisDB = 0 - DefaultRedisPort = 6379 - DefaultRedisTLSEnabled = false - DefaultRedisInsecureSkipVerify = false - DefaultTFDownloadURL = "https://releases.hashicorp.com" - DefaultTFEHostname = "app.terraform.io" - DefaultVCSStatusName = "atlantis" - DefaultWebBasicAuth = false - DefaultWebUsername = "atlantis" - DefaultWebPassword = "atlantis" + DefaultADBasicUser = "" + DefaultADBasicPassword = "" + DefaultADHostname = "dev.azure.com" + DefaultAutoplanFileList = "**/*.tf,**/*.tfvars,**/*.tfvars.json,**/terragrunt.hcl,**/.terraform.lock.hcl" + DefaultCheckoutStrategy = "branch" + DefaultBitbucketBaseURL = bitbucketcloud.BaseURL + DefaultDataDir = "~/.atlantis" + DefaultMarkdownTemplateOverridesDir = "~/.markdown_templates" + DefaultGHHostname = "github.com" + DefaultGitlabHostname = "gitlab.com" + DefaultLockingDBType = "boltdb" + DefaultLogLevel = "info" + DefaultParallelPoolSize = 15 + DefaultStatsNamespace = "atlantis" + DefaultPort = 4141 + DefaultRedisDB = 0 + DefaultRedisPort = 6379 + DefaultRedisTLSEnabled = false + DefaultRedisInsecureSkipVerify = false + DefaultTFDownloadURL = "https://releases.hashicorp.com" + DefaultTFEHostname = "app.terraform.io" + DefaultVCSStatusName = "atlantis" + DefaultWebBasicAuth = false + DefaultWebUsername = "atlantis" + DefaultWebPassword = "atlantis" ) var stringFlags = map[string]stringFlag{ @@ -284,6 +286,10 @@ var stringFlags = map[string]stringFlag{ description: "Log level. Either debug, info, warn, or error.", defaultValue: DefaultLogLevel, }, + MarkdownTemplateOverridesDirFlag: { + description: "Directory for custom overrides to the markdown templates used for comments.", + defaultValue: DefaultMarkdownTemplateOverridesDir, + }, StatsNamespace: { description: "Namespace for aggregating stats.", defaultValue: DefaultStatsNamespace, @@ -666,6 +672,9 @@ func (s *ServerCmd) run() error { if err := s.setDataDir(&userConfig); err != nil { return err } + if err := s.setMarkdownTemplateOverridesDir(&userConfig); err != nil { + return err + } s.setVarFileAllowlist(&userConfig) if err := s.deprecationWarnings(&userConfig); err != nil { return err @@ -716,6 +725,9 @@ func (s *ServerCmd) setDefaults(c *server.UserConfig) { if c.LogLevel == "" { c.LogLevel = DefaultLogLevel } + if c.MarkdownTemplateOverridesDir == "" { + c.MarkdownTemplateOverridesDir = DefaultMarkdownTemplateOverridesDir + } if c.ParallelPoolSize == 0 { c.ParallelPoolSize = DefaultParallelPoolSize } @@ -881,6 +893,30 @@ func (s *ServerCmd) setDataDir(userConfig *server.UserConfig) error { return nil } +// setMarkdownTemplateOverridesDir checks if ~ was used in markdown-template-overrides-dir and converts it to the actual +// home directory. If we don't do this, we'll create a directory called "~" +// instead of actually using home. It also converts relative paths to absolute. +func (s *ServerCmd) setMarkdownTemplateOverridesDir(userConfig *server.UserConfig) error { + finalPath := userConfig.MarkdownTemplateOverridesDir + + // Convert ~ to the actual home dir. + if strings.HasPrefix(finalPath, "~/") { + var err error + finalPath, err = homedir.Expand(finalPath) + if err != nil { + return errors.Wrap(err, "determining home directory") + } + } + + // Convert relative paths to absolute. + finalPath, err := filepath.Abs(finalPath) + if err != nil { + return errors.Wrap(err, "making markdown-template-overrides-dir absolute") + } + userConfig.MarkdownTemplateOverridesDir = finalPath + return nil +} + // setVarFileAllowlist checks if var-file-allowlist is unassigned and makes it default to data-dir for better backward // compatibility. func (s *ServerCmd) setVarFileAllowlist(userConfig *server.UserConfig) { diff --git a/cmd/server_test.go b/cmd/server_test.go index c8110dfa48..09941e4aba 100644 --- a/cmd/server_test.go +++ b/cmd/server_test.go @@ -51,66 +51,67 @@ func (s *ServerStarterMock) Start() error { // Adding a new flag? Add it to this slice for testing in alphabetical // order. var testFlags = map[string]interface{}{ - ADTokenFlag: "ad-token", - ADUserFlag: "ad-user", - ADWebhookPasswordFlag: "ad-wh-pass", - ADWebhookUserFlag: "ad-wh-user", - AtlantisURLFlag: "url", - AllowForkPRsFlag: true, - AllowRepoConfigFlag: true, - AutomergeFlag: true, - AutoplanFileListFlag: "**/*.tf,**/*.yml", - BitbucketBaseURLFlag: "https://bitbucket-base-url.com", - BitbucketTokenFlag: "bitbucket-token", - BitbucketUserFlag: "bitbucket-user", - BitbucketWebhookSecretFlag: "bitbucket-secret", - CheckoutStrategyFlag: "merge", - DataDirFlag: "/path", - DefaultTFVersionFlag: "v0.11.0", - DisableApplyAllFlag: true, - DisableApplyFlag: true, - DisableMarkdownFoldingFlag: true, - DisableRepoLockingFlag: true, - GHHostnameFlag: "ghhostname", - GHTokenFlag: "token", - GHUserFlag: "user", - GHAppIDFlag: int64(0), - GHAppKeyFlag: "", - GHAppKeyFileFlag: "", - GHAppSlugFlag: "atlantis", - GHOrganizationFlag: "", - GHWebhookSecretFlag: "secret", - GitlabHostnameFlag: "gitlab-hostname", - GitlabTokenFlag: "gitlab-token", - GitlabUserFlag: "gitlab-user", - GitlabWebhookSecretFlag: "gitlab-secret", - LockingDBType: "boltdb", - LogLevelFlag: "debug", - StatsNamespace: "atlantis", - AllowDraftPRs: true, - PortFlag: 8181, - ParallelPoolSize: 100, - RepoAllowlistFlag: "github.com/runatlantis/atlantis", - RequireApprovalFlag: true, - RequireMergeableFlag: true, - SilenceNoProjectsFlag: false, - SilenceForkPRErrorsFlag: true, - SilenceAllowlistErrorsFlag: true, - SilenceVCSStatusNoPlans: true, - SkipCloneNoChanges: true, - SlackTokenFlag: "slack-token", - SSLCertFileFlag: "cert-file", - SSLKeyFileFlag: "key-file", - TFDownloadURLFlag: "https://my-hostname.com", - TFEHostnameFlag: "my-hostname", - TFELocalExecutionModeFlag: true, - TFETokenFlag: "my-token", - VCSStatusName: "my-status", - WriteGitCredsFlag: true, - DisableAutoplanFlag: true, - EnablePolicyChecksFlag: false, - EnableRegExpCmdFlag: false, - EnableDiffMarkdownFormat: false, + ADTokenFlag: "ad-token", + ADUserFlag: "ad-user", + ADWebhookPasswordFlag: "ad-wh-pass", + ADWebhookUserFlag: "ad-wh-user", + AtlantisURLFlag: "url", + AllowForkPRsFlag: true, + AllowRepoConfigFlag: true, + AutomergeFlag: true, + AutoplanFileListFlag: "**/*.tf,**/*.yml", + BitbucketBaseURLFlag: "https://bitbucket-base-url.com", + BitbucketTokenFlag: "bitbucket-token", + BitbucketUserFlag: "bitbucket-user", + BitbucketWebhookSecretFlag: "bitbucket-secret", + CheckoutStrategyFlag: "merge", + DataDirFlag: "/path", + DefaultTFVersionFlag: "v0.11.0", + DisableApplyAllFlag: true, + DisableApplyFlag: true, + DisableMarkdownFoldingFlag: true, + DisableRepoLockingFlag: true, + GHHostnameFlag: "ghhostname", + GHTokenFlag: "token", + GHUserFlag: "user", + GHAppIDFlag: int64(0), + GHAppKeyFlag: "", + GHAppKeyFileFlag: "", + GHAppSlugFlag: "atlantis", + GHOrganizationFlag: "", + GHWebhookSecretFlag: "secret", + GitlabHostnameFlag: "gitlab-hostname", + GitlabTokenFlag: "gitlab-token", + GitlabUserFlag: "gitlab-user", + GitlabWebhookSecretFlag: "gitlab-secret", + LockingDBType: "boltdb", + LogLevelFlag: "debug", + MarkdownTemplateOverridesDirFlag: "/path2", + StatsNamespace: "atlantis", + AllowDraftPRs: true, + PortFlag: 8181, + ParallelPoolSize: 100, + RepoAllowlistFlag: "github.com/runatlantis/atlantis", + RequireApprovalFlag: true, + RequireMergeableFlag: true, + SilenceNoProjectsFlag: false, + SilenceForkPRErrorsFlag: true, + SilenceAllowlistErrorsFlag: true, + SilenceVCSStatusNoPlans: true, + SkipCloneNoChanges: true, + SlackTokenFlag: "slack-token", + SSLCertFileFlag: "cert-file", + SSLKeyFileFlag: "key-file", + TFDownloadURLFlag: "https://my-hostname.com", + TFEHostnameFlag: "my-hostname", + TFELocalExecutionModeFlag: true, + TFETokenFlag: "my-token", + VCSStatusName: "my-status", + WriteGitCredsFlag: true, + DisableAutoplanFlag: true, + EnablePolicyChecksFlag: false, + EnableRegExpCmdFlag: false, + EnableDiffMarkdownFormat: false, } func TestExecute_Defaults(t *testing.T) { @@ -128,17 +129,20 @@ func TestExecute_Defaults(t *testing.T) { hostname, err := os.Hostname() Ok(t, err) - // Get our home dir since that's what data-dir defaulted to. + // Get our home dir since that's what data-dir and markdown-template-overrides-dir defaulted to. dataDir, err := homedir.Expand("~/.atlantis") Ok(t, err) + markdownTemplateOverridesDir, err := homedir.Expand("~/.markdown_templates") + Ok(t, err) strExceptions := map[string]string{ - GHUserFlag: "user", - GHTokenFlag: "token", - DataDirFlag: dataDir, - AtlantisURLFlag: "http://" + hostname + ":4141", - RepoAllowlistFlag: "*", - VarFileAllowlistFlag: dataDir, + GHUserFlag: "user", + GHTokenFlag: "token", + DataDirFlag: dataDir, + MarkdownTemplateOverridesDirFlag: markdownTemplateOverridesDir, + AtlantisURLFlag: "http://" + hostname + ":4141", + RepoAllowlistFlag: "*", + VarFileAllowlistFlag: dataDir, } strIgnore := map[string]bool{ "config": true, diff --git a/server/controllers/events/events_controller_e2e_test.go b/server/controllers/events/events_controller_e2e_test.go index 400174bd8e..6ec7f164de 100644 --- a/server/controllers/events/events_controller_e2e_test.go +++ b/server/controllers/events/events_controller_e2e_test.go @@ -1036,7 +1036,7 @@ func setupE2E(t *testing.T, repoDir string) (events_controllers.VCSEventsControl pullUpdater := &events.PullUpdater{ HidePrevPlanComments: false, VCSClient: e2eVCSClient, - MarkdownRenderer: &events.MarkdownRenderer{}, + MarkdownRenderer: events.GetMarkdownRenderer(false, false, false, false, false, false, ""), } autoMerger := &events.AutoMerger{ diff --git a/server/events/command_runner_test.go b/server/events/command_runner_test.go index 3ae94b6669..fd2cf76df9 100644 --- a/server/events/command_runner_test.go +++ b/server/events/command_runner_test.go @@ -99,7 +99,7 @@ func setup(t *testing.T) *vcsmocks.MockClient { pullUpdater = &events.PullUpdater{ HidePrevPlanComments: false, VCSClient: vcsClient, - MarkdownRenderer: &events.MarkdownRenderer{}, + MarkdownRenderer: events.GetMarkdownRenderer(false, false, false, false, false, false, ""), } autoMerger = &events.AutoMerger{ diff --git a/server/events/markdown_renderer.go b/server/events/markdown_renderer.go index f6530df83b..74960dec49 100644 --- a/server/events/markdown_renderer.go +++ b/server/events/markdown_renderer.go @@ -37,16 +37,6 @@ var ( //go:embed templates/* templatesFS embed.FS - templates *template.Template - // Initialize templates using a var instead of init() - _ = func() struct{} { - templates, _ = template.New("").Funcs(sprig.TxtFuncMap()).ParseFS(templatesFS, "templates/*.tmpl") - if overrides, err := templates.ParseFiles("templates/*"); err == nil { - // doesn't override if templates directory doesn't exist - templates = overrides - } - return struct{}{} - }() ) // MarkdownRenderer renders responses as markdown. @@ -60,6 +50,7 @@ type MarkdownRenderer struct { DisableMarkdownFolding bool DisableRepoLocking bool EnableDiffMarkdownFormat bool + MarkdownTemplates *template.Template } // commonData is data that all responses have. @@ -112,6 +103,33 @@ type projectResultTmplData struct { Rendered string } +// Initialize templates +func GetMarkdownRenderer( + GitlabSupportsCommonMark bool, + DisableApplyAll bool, + DisableApply bool, + DisableMarkdownFolding bool, + DisableRepoLocking bool, + EnableDiffMarkdownFormat bool, + MarkdownTemplateOverridesDir string, +) *MarkdownRenderer { + var templates *template.Template + templates, _ = template.New("").Funcs(sprig.TxtFuncMap()).ParseFS(templatesFS, "templates/*.tmpl") + if overrides, err := templates.ParseGlob(fmt.Sprintf("%s/*.tmpl", MarkdownTemplateOverridesDir)); err == nil { + // doesn't override if templates directory doesn't exist + templates = overrides + } + return &MarkdownRenderer{ + GitlabSupportsCommonMark: GitlabSupportsCommonMark, + DisableApplyAll: DisableApplyAll, + DisableMarkdownFolding: DisableMarkdownFolding, + DisableApply: DisableApply, + DisableRepoLocking: DisableRepoLocking, + EnableDiffMarkdownFormat: EnableDiffMarkdownFormat, + MarkdownTemplates: templates, + } +} + // Render formats the data into a markdown string. // nolint: interfacer func (m *MarkdownRenderer) Render(res command.Result, cmdName command.Name, log string, verbose bool, vcsHost models.VCSHostType) string { @@ -126,6 +144,9 @@ func (m *MarkdownRenderer) Render(res command.Result, cmdName command.Name, log DisableRepoLocking: m.DisableRepoLocking, EnableDiffMarkdownFormat: m.EnableDiffMarkdownFormat, } + + templates := m.MarkdownTemplates + if res.Error != nil { return m.renderTemplate(templates.Lookup("unwrappedErrWithLog"), errData{res.Error.Error(), common}) } @@ -141,6 +162,8 @@ func (m *MarkdownRenderer) renderProjectResults(results []command.ProjectResult, numPolicyCheckSuccesses := 0 numVersionSuccesses := 0 + templates := m.MarkdownTemplates + for _, result := range results { resultData := projectResultTmplData{ Workspace: result.Workspace, diff --git a/server/events/markdown_renderer_test.go b/server/events/markdown_renderer_test.go index 779bc546d4..f7b4a41b8b 100644 --- a/server/events/markdown_renderer_test.go +++ b/server/events/markdown_renderer_test.go @@ -16,6 +16,7 @@ package events_test import ( "errors" "fmt" + "os" "strings" "testing" @@ -56,7 +57,7 @@ func TestRenderErr(t *testing.T) { }, } - r := events.MarkdownRenderer{} + r := events.GetMarkdownRenderer(false, false, false, false, false, false, "") for _, c := range cases { res := command.Result{ Error: c.Error, @@ -101,7 +102,7 @@ func TestRenderFailure(t *testing.T) { }, } - r := events.MarkdownRenderer{} + r := events.GetMarkdownRenderer(false, false, false, false, false, false, "") for _, c := range cases { res := command.Result{ Failure: c.Failure, @@ -120,7 +121,7 @@ func TestRenderFailure(t *testing.T) { } func TestRenderErrAndFailure(t *testing.T) { - r := events.MarkdownRenderer{} + r := events.GetMarkdownRenderer(false, false, false, false, false, false, "") res := command.Result{ Error: errors.New("error"), Failure: "failure", @@ -750,7 +751,7 @@ $$$ }, } - r := events.MarkdownRenderer{} + r := events.GetMarkdownRenderer(false, false, false, false, false, false, "") for _, c := range cases { t.Run(c.Description, func(t *testing.T) { res := command.Result{ @@ -901,9 +902,15 @@ $$$ `, }, } - r := events.MarkdownRenderer{ - DisableApplyAll: true, - } + r := events.GetMarkdownRenderer( + false, // GitlabSupportsCommonMark + true, // DisableApplyAll + false, // DisableApply + false, // DisableMarkdownFolding + false, // DisableRepoLocking + false, // EnableDiffMarkdownFormat + "", // MarkdownTemplateOverridesDir + ) for _, c := range cases { t.Run(c.Description, func(t *testing.T) { res := command.Result{ @@ -1046,10 +1053,16 @@ $$$ `, }, } - r := events.MarkdownRenderer{ - DisableApplyAll: true, - DisableApply: true, - } + + r := events.GetMarkdownRenderer( + false, // GitlabSupportsCommonMark + true, // DisableApplyAll + true, // DisableApply + false, // DisableMarkdownFolding + false, // DisableRepoLocking + false, // EnableDiffMarkdownFormat + "", // MarkdownTemplateOverridesDir + ) for _, c := range cases { t.Run(c.Description, func(t *testing.T) { res := command.Result{ @@ -1070,11 +1083,55 @@ $$$ } } +// Run policy check with a custom template to validate custom template rendering. +func TestRenderCustomPolicyCheckTemplate_DisableApplyAll(t *testing.T) { + tmpDir, cleanup := TempDir(t) + filePath := fmt.Sprintf("%s/templates.tmpl", tmpDir) + _, err := os.Create(filePath) + Ok(t, err) + err = os.WriteFile(filePath, []byte("{{ define \"policyCheckSuccessUnwrapped\" -}}somecustometext{{- end}}\n"), 0600) + Ok(t, err) + defer cleanup() + r := events.GetMarkdownRenderer( + false, // GitlabSupportsCommonMark + true, // DisableApplyAll + true, // DisableApply + false, // DisableMarkdownFolding + false, // DisableRepoLocking + false, // EnableDiffMarkdownFormat + tmpDir, // MarkdownTemplateOverridesDir + ) + + rendered := r.Render(command.Result{ + ProjectResults: []command.ProjectResult{ + { + Workspace: "workspace", + RepoRelDir: "path", + PolicyCheckSuccess: &models.PolicyCheckSuccess{ + PolicyCheckOutput: "4 tests, 4 passed, 0 warnings, 0 failures, 0 exceptions", + LockURL: "lock-url", + ApplyCmd: "atlantis apply -d path -w workspace", + RePlanCmd: "atlantis plan -d path -w workspace", + }, + }, + }, + }, command.PolicyCheck, "log", false, models.Github) + fmt.Println(rendered) + Equals(t, rendered, "Ran Policy Check for dir: `path` workspace: `workspace`\n\nsomecustometext\n\n\n") + +} + // Test that if folding is disabled that it's not used. func TestRenderProjectResults_DisableFolding(t *testing.T) { - mr := events.MarkdownRenderer{ - DisableMarkdownFolding: true, - } + mr := events.GetMarkdownRenderer( + false, // GitlabSupportsCommonMark + false, // DisableApplyAll + false, // DisableApply + true, // DisableMarkdownFolding + false, // DisableRepoLocking + false, // EnableDiffMarkdownFormat + "", // MarkdownTemplateOverridesDir + ) rendered := mr.Render(command.Result{ ProjectResults: []command.ProjectResult{ @@ -1156,9 +1213,15 @@ func TestRenderProjectResults_WrappedErr(t *testing.T) { for _, c := range cases { t.Run(fmt.Sprintf("%s_%v", c.VCSHost.String(), c.ShouldWrap), func(t *testing.T) { - mr := events.MarkdownRenderer{ - GitlabSupportsCommonMark: c.GitlabCommonMarkSupport, - } + mr := events.GetMarkdownRenderer( + c.GitlabCommonMarkSupport, // GitlabSupportsCommonMark + false, // DisableApplyAll + false, // DisableApply + false, // DisableMarkdownFolding + false, // DisableRepoLocking + false, // EnableDiffMarkdownFormat + "", // MarkdownTemplateOverridesDir + ) rendered := mr.Render(command.Result{ ProjectResults: []command.ProjectResult{ @@ -1268,9 +1331,15 @@ func TestRenderProjectResults_WrapSingleProject(t *testing.T) { for _, cmd := range []command.Name{command.Plan, command.Apply} { t.Run(fmt.Sprintf("%s_%s_%v", c.VCSHost.String(), cmd.String(), c.ShouldWrap), func(t *testing.T) { - mr := events.MarkdownRenderer{ - GitlabSupportsCommonMark: c.GitlabCommonMarkSupport, - } + mr := events.GetMarkdownRenderer( + c.GitlabCommonMarkSupport, // GitlabSupportsCommonMark + false, // DisableApplyAll + false, // DisableApply + false, // DisableMarkdownFolding + false, // DisableRepoLocking + false, // EnableDiffMarkdownFormat + "", // MarkdownTemplateOverridesDir + ) var pr command.ProjectResult switch cmd { case command.Plan: @@ -1373,7 +1442,15 @@ $$$ } func TestRenderProjectResults_MultiProjectApplyWrapped(t *testing.T) { - mr := events.MarkdownRenderer{} + mr := events.GetMarkdownRenderer( + false, // GitlabSupportsCommonMark + false, // DisableApplyAll + false, // DisableApply + false, // DisableMarkdownFolding + false, // DisableRepoLocking + false, // EnableDiffMarkdownFormat + "", // MarkdownTemplateOverridesDir + ) tfOut := strings.Repeat("line\n", 13) rendered := mr.Render(command.Result{ ProjectResults: []command.ProjectResult{ @@ -1419,7 +1496,15 @@ $$$ } func TestRenderProjectResults_MultiProjectPlanWrapped(t *testing.T) { - mr := events.MarkdownRenderer{} + mr := events.GetMarkdownRenderer( + false, // GitlabSupportsCommonMark + false, // DisableApplyAll + false, // DisableApply + false, // DisableMarkdownFolding + false, // DisableRepoLocking + false, // EnableDiffMarkdownFormat + "", // MarkdownTemplateOverridesDir + ) tfOut := strings.Repeat("line\n", 13) + "Plan: 1 to add, 0 to change, 0 to destroy." rendered := mr.Render(command.Result{ ProjectResults: []command.ProjectResult{ @@ -1592,7 +1677,15 @@ This plan was not saved because one or more projects failed and automerge requir for name, c := range cases { t.Run(name, func(t *testing.T) { - mr := events.MarkdownRenderer{} + mr := events.GetMarkdownRenderer( + false, // GitlabSupportsCommonMark + false, // DisableApplyAll + false, // DisableApply + false, // DisableMarkdownFolding + false, // DisableRepoLocking + false, // EnableDiffMarkdownFormat + "", // MarkdownTemplateOverridesDir + ) rendered := mr.Render(c.cr, command.Plan, "log", false, models.Github) expWithBackticks := strings.Replace(c.exp, "$", "`", -1) Equals(t, expWithBackticks, rendered) @@ -2052,7 +2145,15 @@ $$$ }, } - r := events.MarkdownRenderer{} + r := events.GetMarkdownRenderer( + false, // GitlabSupportsCommonMark + false, // DisableApplyAll + false, // DisableApply + false, // DisableMarkdownFolding + false, // DisableRepoLocking + false, // EnableDiffMarkdownFormat + "", // MarkdownTemplateOverridesDir + ) r.DisableRepoLocking = true for _, c := range cases { t.Run(c.Description, func(t *testing.T) { @@ -2481,11 +2582,16 @@ Plan: 1 to add, 2 to change, 1 to destroy. `, }, } - r := events.MarkdownRenderer{ - DisableApplyAll: true, - DisableApply: true, - EnableDiffMarkdownFormat: true, - } + r := events.GetMarkdownRenderer( + false, // GitlabSupportsCommonMark + true, // DisableApplyAll + true, // DisableApply + false, // DisableMarkdownFolding + false, // DisableRepoLocking + true, // EnableDiffMarkdownFormat + "", // MarkdownTemplateOverridesDir + ) + for _, c := range cases { t.Run(c.Description, func(t *testing.T) { res := command.Result{ diff --git a/server/server.go b/server/server.go index 34d99befc0..bedbb45cb1 100644 --- a/server/server.go +++ b/server/server.go @@ -389,14 +389,15 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { if err != nil && flag.Lookup("test.v") == nil { return nil, errors.Wrap(err, "initializing terraform") } - markdownRenderer := &events.MarkdownRenderer{ - GitlabSupportsCommonMark: gitlabClient.SupportsCommonMark(), - DisableApplyAll: userConfig.DisableApplyAll, - DisableMarkdownFolding: userConfig.DisableMarkdownFolding, - DisableApply: userConfig.DisableApply, - DisableRepoLocking: userConfig.DisableRepoLocking, - EnableDiffMarkdownFormat: userConfig.EnableDiffMarkdownFormat, - } + markdownRenderer := events.GetMarkdownRenderer( + gitlabClient.SupportsCommonMark(), + userConfig.DisableApplyAll, + userConfig.DisableMarkdownFolding, + userConfig.DisableApply, + userConfig.DisableRepoLocking, + userConfig.EnableDiffMarkdownFormat, + userConfig.MarkdownTemplateOverridesDir, + ) var lockingClient locking.Locker var applyLockingClient locking.ApplyLocker diff --git a/server/user_config.go b/server/user_config.go index c8f5435669..00756b7d97 100644 --- a/server/user_config.go +++ b/server/user_config.go @@ -51,6 +51,7 @@ type UserConfig struct { HidePrevPlanComments bool `mapstructure:"hide-prev-plan-comments"` LockingDBType string `mapstructure:"locking-db-type"` LogLevel string `mapstructure:"log-level"` + MarkdownTemplateOverridesDir string `mapstructure:"markdown-template-overrides-dir"` ParallelPoolSize int `mapstructure:"parallel-pool-size"` StatsNamespace string `mapstructure:"stats-namespace"` PlanDrafts bool `mapstructure:"allow-draft-prs"` From acdb4eb864e4c04e1db7e20c25e857802b178dba Mon Sep 17 00:00:00 2001 From: Ross Strickland Date: Wed, 9 Nov 2022 16:20:23 +0000 Subject: [PATCH 6/7] Add documentation. --- runatlantis.io/docs/server-configuration.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/runatlantis.io/docs/server-configuration.md b/runatlantis.io/docs/server-configuration.md index 6cbf5617e8..4410f0470e 100644 --- a/runatlantis.io/docs/server-configuration.md +++ b/runatlantis.io/docs/server-configuration.md @@ -456,6 +456,21 @@ Values are chosen in this order: ``` Log level. Defaults to `info`. +### `--markdown-template-overrides-dir` + ```bash + atlantis server --markdown-template-overrides-dir="path/to/templates/" + ``` + Directory where Atlantis will read in overrides for markdown templates used to render comments on pull requests. + Markdown template overrides may be specified either in individual files, or all together in a single file. All template + override files _must_ have the `.tmpl` extension, otherwise they will not be parsed. + + Markdown templates which may have overrides can be found [here](../../server/events/templates) + + Please be mindful that settings like `--enable-diff-markdown-format` depend on logic defined in the templates. It is + possible to diverge from expected behavior, if care is not taken when overriding default templates. + + Defaults to `~/.markdown_templates/` + ### `--parallel-pool-size` ```bash atlantis server --parallel-pool-size=100 From 497dc047970ef0ba0093473ae11129b1e0c36d8c Mon Sep 17 00:00:00 2001 From: Ross Strickland Date: Thu, 10 Nov 2022 10:09:24 -0600 Subject: [PATCH 7/7] Address PR comments. --- runatlantis.io/docs/server-configuration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runatlantis.io/docs/server-configuration.md b/runatlantis.io/docs/server-configuration.md index 4410f0470e..6b9a95fd85 100644 --- a/runatlantis.io/docs/server-configuration.md +++ b/runatlantis.io/docs/server-configuration.md @@ -464,12 +464,12 @@ Values are chosen in this order: Markdown template overrides may be specified either in individual files, or all together in a single file. All template override files _must_ have the `.tmpl` extension, otherwise they will not be parsed. - Markdown templates which may have overrides can be found [here](../../server/events/templates) + Markdown templates which may have overrides can be found [here](https://github.com/runatlantis/atlantis/tree/master/server/events/templates) Please be mindful that settings like `--enable-diff-markdown-format` depend on logic defined in the templates. It is possible to diverge from expected behavior, if care is not taken when overriding default templates. - Defaults to `~/.markdown_templates/` + Defaults to the atlantis home directory `/home/atlantis/.markdown_templates/` in `/$HOME/.markdown_templates`. ### `--parallel-pool-size` ```bash