Skip to content
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
6 changes: 6 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ inputs:
description: Setup tfenv
required: false
default: 'false'
post-plans-as-one-comment:
description: Post plans as one comment
required: false
default: 'false'
outputs:
output:
value: ${{ steps.digger.outputs.output }}
Expand Down Expand Up @@ -186,6 +190,7 @@ runs:
POLICY_CHECK_ENABLED: ${{ inputs.policy-check-enabled == 'true' }}
DIGGER_CLOUD_TOKEN: ${{ inputs.digger-cloud-token }}
DIGGER_CLOUD_HOSTNAME: ${{ inputs.digger-cloud-hostname }}
ACCUMULATE_PLANS: ${{ inputs.post-plans-as-one-comment == 'true' }}
run: |
cd ${{ github.action_path }}
go build -o digger ./cmd/digger
Expand All @@ -203,6 +208,7 @@ runs:
POLICY_CHECK_ENABLED: ${{ inputs.policy-check-enabled == 'true' }}
DIGGER_CLOUD_TOKEN: ${{ inputs.digger-cloud-token }}
DIGGER_CLOUD_HOSTNAME: ${{ inputs.digger-cloud-hostname }}
ACCUMULATE_PLANS: ${{ inputs.post-plans-as-one-comment == 'true' }}
id: digger
shell: bash
run: |
Expand Down
25 changes: 12 additions & 13 deletions pkg/core/execution/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,11 @@ func (d DiggerExecutor) storedPlanFilePath() string {
return path.Join(d.ProjectNamespace, d.planFileName())
}

func (d DiggerExecutor) Plan() (bool, error) {
func (d DiggerExecutor) Plan() (bool, string, error) {
plan := ""
locked, err := d.ProjectLock.Lock()
if err != nil {
return false, fmt.Errorf("error locking project: %v", err)
return false, "", fmt.Errorf("error locking project: %v", err)
}
log.Printf("Lock result: %t\n", locked)
if locked {
Expand All @@ -68,37 +69,35 @@ func (d DiggerExecutor) Plan() (bool, error) {
if step.Action == "init" {
_, _, err := d.TerraformExecutor.Init(step.ExtraArgs, d.StateEnvVars)
if err != nil {
return false, fmt.Errorf("error running init: %v", err)
return false, "", fmt.Errorf("error running init: %v", err)
}
}
if step.Action == "plan" {
planArgs := []string{"-out", d.planFileName()}
planArgs = append(planArgs, step.ExtraArgs...)
isNonEmptyPlan, stdout, stderr, err := d.TerraformExecutor.Plan(planArgs, d.CommandEnvVars)
if err != nil {
return false, fmt.Errorf("error executing plan: %v", err)
return false, "", fmt.Errorf("error executing plan: %v", err)
}
if d.PlanStorage != nil {
planExists, err := d.PlanStorage.PlanExists(d.storedPlanFilePath())
if err != nil {
return false, fmt.Errorf("error checking if plan exists: %v", err)
return false, "", fmt.Errorf("error checking if plan exists: %v", err)
}

if planExists {
err = d.PlanStorage.DeleteStoredPlan(d.storedPlanFilePath())
if err != nil {
return false, fmt.Errorf("error deleting plan: %v", err)
return false, "", fmt.Errorf("error deleting plan: %v", err)
}
}

err = d.PlanStorage.StorePlan(d.localPlanFilePath(), d.storedPlanFilePath())
if err != nil {
return false, fmt.Errorf("error storing plan: %v", err)
return false, "", fmt.Errorf("error storing plan: %v", err)
}
}
plan := cleanupTerraformPlan(isNonEmptyPlan, err, stdout, stderr)
comment := utils.GetTerraformOutputAsCollapsibleComment("Plan for **"+d.ProjectLock.LockId()+"**", plan)
err = d.Reporter.Report(comment)
plan = cleanupTerraformPlan(isNonEmptyPlan, err, stdout, stderr)
if err != nil {
fmt.Printf("error publishing comment: %v", err)
}
Expand All @@ -112,13 +111,13 @@ func (d DiggerExecutor) Plan() (bool, error) {
log.Printf("Running %v for **%v**\n", step.Value, d.ProjectLock.LockId())
_, _, err := d.CommandRunner.Run(d.ProjectPath, step.Shell, commands)
if err != nil {
return false, fmt.Errorf("error running command: %v", err)
return false, "", fmt.Errorf("error running command: %v", err)
}
}
}
return true, nil
return true, plan, nil
}
return false, nil
return false, plan, nil
}

func (d DiggerExecutor) Apply() (bool, error) {
Expand Down
36 changes: 34 additions & 2 deletions pkg/digger/digger.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ import (
"digger/pkg/core/runners"
"digger/pkg/core/storage"
"digger/pkg/core/terraform"
"digger/pkg/core/utils"
"digger/pkg/locking"
"digger/pkg/usage"
"errors"
"fmt"
"log"
"os"
"path"
"strings"
"time"
)

Expand Down Expand Up @@ -56,8 +58,22 @@ func DetectCI() CIName {

}

func RunCommandsPerProject(commandsPerProject []models.ProjectCommand, projectNamespace string, requestedBy string, eventName string, prNumber int, ciService ci.CIService, lock core_locking.Lock, reporter reporting.Reporter, planStorage storage.PlanStorage, policyChecker policy.Checker, workingDir string) (bool, bool, error) {
func RunCommandsPerProject(
commandsPerProject []models.ProjectCommand,
projectNamespace string,
requestedBy string,
eventName string,
prNumber int,
ciService ci.CIService,
lock core_locking.Lock,
reporter reporting.Reporter,
planStorage storage.PlanStorage,
policyChecker policy.Checker,
workingDir string,
) (bool, bool, error) {
accumulatePlans := os.Getenv("ACCUMULATE_PLANS") == "true"
appliesPerProject := make(map[string]bool)
plansToPublish := make([]string, 0)
for _, projectCommands := range commandsPerProject {
for _, command := range projectCommands.Commands {
policyInput := map[string]interface{}{"user": requestedBy, "action": command}
Expand Down Expand Up @@ -112,12 +128,21 @@ func RunCommandsPerProject(commandsPerProject []models.ProjectCommand, projectNa
case "digger plan":
usage.SendUsageRecord(requestedBy, eventName, "plan")
ciService.SetStatus(prNumber, "pending", projectCommands.ProjectName+"/plan")
planPerformed, err := diggerExecutor.Plan()
planPerformed, plan, err := diggerExecutor.Plan()
if err != nil {
log.Printf("Failed to run digger plan command. %v", err)
ciService.SetStatus(prNumber, "failure", projectCommands.ProjectName+"/plan")
return false, false, fmt.Errorf("failed to run digger plan command. %v", err)
} else if planPerformed {
comment := utils.GetTerraformOutputAsCollapsibleComment("Plan for **"+projectLock.LockId()+"**", plan)
if accumulatePlans {
plansToPublish = append(plansToPublish, comment)
} else {
err = reporter.Report(comment)
if err != nil {
log.Printf("Failed to report plan. %v", err)
}
}
ciService.SetStatus(prNumber, "success", projectCommands.ProjectName+"/plan")
}
case "digger apply":
Expand Down Expand Up @@ -164,6 +189,13 @@ func RunCommandsPerProject(commandsPerProject []models.ProjectCommand, projectNa
}
}

if len(plansToPublish) > 0 {
err := reporter.Report(strings.Join(plansToPublish, "\n"))
if err != nil {
log.Printf("Failed to report plans. %v", err)
}
}

allAppliesSuccess := true
for _, success := range appliesPerProject {
if !success {
Expand Down
2 changes: 1 addition & 1 deletion pkg/digger/digger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ func TestCorrectCommandExecutionWhenPlanning(t *testing.T) {

commandStrings := allCommandsInOrderWithParams(terraformExecutor, commandRunner, prManager, lock, planStorage)

assert.Equal(t, []string{"Lock ", "Init ", "Plan -out #.tfplan", "PlanExists #.tfplan", "StorePlan #.tfplan", "LockId ", "PublishComment 1 <details>\n <summary>Plan for ****</summary>\n\n ```terraform\n\n ```\n</details>", "LockId ", "Run echo"}, commandStrings)
assert.Equal(t, []string{"Lock ", "Init ", "Plan -out #.tfplan", "PlanExists #.tfplan", "StorePlan #.tfplan", "LockId ", "Run echo"}, commandStrings)
}

func allCommandsInOrderWithParams(terraformExecutor *MockTerraformExecutor, commandRunner *MockCommandRunner, prManager *MockPRManager, lock *MockProjectLock, planStorage *MockPlanStorage) []string {
Expand Down