Skip to content

Commit

Permalink
feat(cli): performs dry-runs on deployments (#120)
Browse files Browse the repository at this point in the history
  • Loading branch information
jmgilman authored Jan 17, 2025
1 parent 924e6bb commit 710ecda
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ jobs:
earthly_token: ${{ secrets.earthly_token }}

final:
needs: [check, build, package, test, release]
needs: [check, build, package, test, release, deploy]
if: ${{ always() && (contains(needs.*.result, 'failure') || !failure() && !cancelled()) }}
runs-on: ubuntu-latest
steps:
Expand Down
7 changes: 4 additions & 3 deletions cli/cmd/cmds/deploy/deploy.go → cli/cmd/cmds/deploy/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ func (c *PushCmd) Run(ctx run.RunContext) error {
return fmt.Errorf("could not load project: %w", err)
}

var dryrun bool
eh := events.NewDefaultEventHandler(ctx.Logger)
if !eh.Firing(&project, project.GetDeploymentEvents()) && !c.Force {
ctx.Logger.Info("No deployment event is firing, skipping deployment")
return nil
ctx.Logger.Info("No deployment event is firing, performing dry-run")
dryrun = true
}

deployer := deployment.NewGitopsDeployer(&project, &ctx.SecretStore, ctx.Logger)
deployer := deployment.NewGitopsDeployer(&project, &ctx.SecretStore, ctx.Logger, dryrun)
if err := deployer.Load(); err != nil {
return fmt.Errorf("could not load deployer: %w", err)
}
Expand Down
37 changes: 24 additions & 13 deletions cli/pkg/deployment/gitops.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func (g gitRemote) Push(repo *git.Repository, o *git.PushOptions) error {

// GitopsDeployer is a deployer that deploys projects to a GitOps repository.
type GitopsDeployer struct {
dryrun bool
fs billy.Filesystem
repo *git.Repository
kcl KCLRunner
Expand Down Expand Up @@ -108,21 +109,29 @@ func (g *GitopsDeployer) Deploy() error {
}
}

changes, err := g.hasChanges()
if err != nil {
return fmt.Errorf("could not check if worktree has changes: %w", err)
} else if !changes {
return ErrNoChanges
}
if !g.dryrun {
changes, err := g.hasChanges()
if err != nil {
return fmt.Errorf("could not check if worktree has changes: %w", err)
} else if !changes {
return ErrNoChanges
}

g.logger.Info("Committing changes", "path", bundlePath)
if err := g.commit(); err != nil {
return fmt.Errorf("could not commit changes: %w", err)
}
g.logger.Info("Committing changes", "path", bundlePath)
if err := g.commit(); err != nil {
return fmt.Errorf("could not commit changes: %w", err)
}

g.logger.Info("Pushing changes")
if err := g.push(); err != nil {
return fmt.Errorf("could not push changes: %w", err)
g.logger.Info("Pushing changes")
if err := g.push(); err != nil {
return fmt.Errorf("could not push changes: %w", err)
}
} else {
g.logger.Info("Dry-run: not committing or pushing changes")
g.logger.Info("Dumping manifests")
for _, r := range result {
fmt.Print(r.Manifests)
}
}

return nil
Expand Down Expand Up @@ -245,12 +254,14 @@ func NewGitopsDeployer(
project *project.Project,
store *secrets.SecretStore,
logger *slog.Logger,
dryrun bool,
) GitopsDeployer {
if logger == nil {
logger = slog.New(slog.NewTextHandler(io.Discard, nil))
}

return GitopsDeployer{
dryrun: dryrun,
fs: memfs.New(),
kcl: NewKCLRunner(logger),
logger: logger,
Expand Down
26 changes: 25 additions & 1 deletion cli/pkg/deployment/gitops_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ func TestDeploy(t *testing.T) {
project projectParams
yaml string
execFail bool
dryrun bool
setup func(*testing.T, *GitopsDeployer, *testutils.InMemRepo)
validate func(*testing.T, *GitopsDeployer, mockGitRemote, *testutils.InMemRepo)
expectErr bool
Expand All @@ -87,6 +88,7 @@ func TestDeploy(t *testing.T) {
project: defaultParams,
yaml: "yaml",
execFail: false,
dryrun: false,
setup: func(t *testing.T, deployer *GitopsDeployer, repo *testutils.InMemRepo) {
deployer.token = "test"
repo.MkdirAll(t, "deploy/dev/apps")
Expand All @@ -106,6 +108,27 @@ func TestDeploy(t *testing.T) {
expectErr: false,
expectedErr: "",
},
{
name: "dry-run",
mock: mockGitRemote{},
project: defaultParams,
yaml: "yaml",
execFail: false,
dryrun: true,
setup: func(t *testing.T, deployer *GitopsDeployer, repo *testutils.InMemRepo) {
deployer.token = "test"
repo.MkdirAll(t, "deploy/dev/apps")
},
validate: func(t *testing.T, deployer *GitopsDeployer, mock mockGitRemote, repo *testutils.InMemRepo) {
assert.True(t, repo.Exists(t, "deploy/dev/apps/test/main.yaml"), "main.yaml does not exist")
assert.Equal(t, repo.ReadFile(t, "deploy/dev/apps/test/main.yaml"), []byte("yaml"), "main.yaml content is incorrect")

_, err := repo.Repo.Head()
require.Error(t, err) // No commit should be made
},
expectErr: false,
expectedErr: "",
},
{
name: "no changes",
mock: mockGitRemote{},
Expand Down Expand Up @@ -138,7 +161,8 @@ func TestDeploy(t *testing.T) {
repo := testutils.NewInMemRepo(t)
var calls []string
deployer := GitopsDeployer{
fs: repo.Fs,
dryrun: tt.dryrun,
fs: repo.Fs,
kcl: KCLRunner{
logger: testutils.NewNoopLogger(),
kcl: newWrappedExecuterMock(tt.yaml, &calls, tt.execFail),
Expand Down
2 changes: 1 addition & 1 deletion cli/pkg/deployment/kcl.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func encodeValues(ctx *cue.Context, module schema.Module) ([]byte, error) {

// run runs a KCL module with the given module container and arguments.
func (k *KCLRunner) run(container string, moduleArgs KCLModuleArgs) ([]byte, error) {
args := []string{"run", "-q"}
args := []string{"run", "-q", "--no_style"}
args = append(args, moduleArgs.Serialize()...)
args = append(args, fmt.Sprintf("oci://%s", container))

Expand Down
4 changes: 2 additions & 2 deletions cli/pkg/deployment/kcl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ func TestKCLRunnerRunDeployment(t *testing.T) {
assert.Equal(t, "key: value\n", r.result["main"].Values)
assert.Equal(t, "output", r.result["support"].Manifests)
assert.Equal(t, "key1: value1\n", r.result["support"].Values)
assert.Contains(t, r.calls, "run -q -D name= -D namespace=default -D values={\"key\":\"value\"} -D 1.0.0 oci://test.com/module")
assert.Contains(t, r.calls, "run -q -D name= -D namespace=default -D values={\"key1\":\"value1\"} -D 1.0.0 oci://test.com/module1")
assert.Contains(t, r.calls, "run -q --no_style -D name= -D namespace=default -D values={\"key\":\"value\"} -D 1.0.0 oci://test.com/module")
assert.Contains(t, r.calls, "run -q --no_style -D name= -D namespace=default -D values={\"key1\":\"value1\"} -D 1.0.0 oci://test.com/module1")
},
},
{
Expand Down
10 changes: 4 additions & 6 deletions foundry/api/blueprint.cue
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ project: {
}
deployment: {
on: {
//merge: {}
//tag: {}
always: {}
merge: {}
tag: {}
}
environment: "dev"
modules: {
Expand Down Expand Up @@ -47,9 +46,8 @@ project: {
release: {
docker: {
on: {
//merge: {}
//tag: {}
always: {}
merge: {}
tag: {}
}
config: {
tag: _ @forge(name="GIT_HASH_OR_TAG")
Expand Down

0 comments on commit 710ecda

Please sign in to comment.