diff --git a/README.md b/README.md index 10f36e2..1109b3e 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,7 @@ make ## TODOs - Fix OCI plain HTTP for local development -- Remove hardcoded git username and email +- Config git username and email - Credentials for the worker (SSH priv + pub + knowhosts?) ## Acknowledgments and References diff --git a/controller/activities/git.go b/controller/activities/git.go index 1e1ec68..e95057b 100644 --- a/controller/activities/git.go +++ b/controller/activities/git.go @@ -87,28 +87,46 @@ func ChangedModules(ctx context.Context, repoPath string, oldRevision string) ([ return modules, nil } -func GitSync(ctx context.Context, path string) error { +func GitAdd(ctx context.Context, path string) error { logger := activity.GetLogger(ctx) dir := filepath.Dir(path) relPath := filepath.Base(path) - cmds := [][]string{ - {"git", "-C", dir, "config", "user.name", "Bot"}, - {"git", "-C", dir, "config", "user.email", "bot@khuedoan.com"}, - {"git", "-C", dir, "add", relPath}, - {"git", "-C", dir, "commit", "-m", "Update app version"}, - {"git", "-C", dir, "push"}, + cmd := exec.Command("git", "-C", dir, "add", relPath) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + logger.Error("git add failed", "error", err) + return fmt.Errorf("git add failed: %w", err) } - for _, args := range cmds { - cmd := exec.Command(args[0], args[1:]...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - logger.Error("command %v failed: %w", args, err) - return err - } + return nil +} + +func GitCommit(ctx context.Context, dir string, message string) error { + logger := activity.GetLogger(ctx) + + cmd := exec.Command("git", "-C", dir, "commit", "-m", message) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + logger.Error("git commit failed", "error", err) + return fmt.Errorf("git commit failed: %w", err) + } + + return nil +} + +func GitPush(ctx context.Context, dir string) error { + logger := activity.GetLogger(ctx) + + cmd := exec.Command("git", "-C", dir, "push") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + logger.Error("git push failed", "error", err) + return fmt.Errorf("git push failed: %w", err) } return nil diff --git a/controller/activities/git_test.go b/controller/activities/git_test.go index 71f1eb0..f7f708d 100644 --- a/controller/activities/git_test.go +++ b/controller/activities/git_test.go @@ -181,8 +181,8 @@ func getChangedModulesFromFiles(repoPath string, changedFiles []string) []string return modules } -func TestGitSync_PathParsing(t *testing.T) { - // Test the path parsing logic in GitSync without requiring actual git commands +func TestGitAdd_PathParsing(t *testing.T) { + // Test the path parsing logic in GitAdd without requiring actual git commands tests := []struct { name string inputPath string @@ -211,7 +211,7 @@ func TestGitSync_PathParsing(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Test the path manipulation logic that GitSync uses + // Test the path manipulation logic that GitAdd uses actualDir := filepath.Dir(tt.inputPath) actualFile := filepath.Base(tt.inputPath) @@ -226,13 +226,82 @@ func TestGitSync_PathParsing(t *testing.T) { } } -func TestGitSync_CommandStructure(t *testing.T) { - // Test that GitSync constructs the expected git commands - // This test validates the command structure without executing them +func TestGitCommit_PathParsing(t *testing.T) { + // Test the path parsing logic in GitCommit + tests := []struct { + name string + inputPath string + expectedDir string + message string + }{ + { + name: "simple file with default message", + inputPath: "/tmp/test.yaml", + expectedDir: "/tmp", + message: "chore(test/app): update local version", + }, + { + name: "nested file with custom message", + inputPath: "/apps/namespace/app/cluster.yaml", + expectedDir: "/apps/namespace/app", + message: "feat: update application configuration", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Test the path manipulation logic that GitCommit uses + actualDir := filepath.Dir(tt.inputPath) + + if actualDir != tt.expectedDir { + t.Errorf("Expected directory '%s', got '%s'", tt.expectedDir, actualDir) + } + // Verify message is not empty + if tt.message == "" { + t.Error("Commit message should not be empty") + } + }) + } +} + +func TestGitPush_PathParsing(t *testing.T) { + // Test the path parsing logic in GitPush + tests := []struct { + name string + inputPath string + expectedDir string + }{ + { + name: "simple file", + inputPath: "/tmp/test.yaml", + expectedDir: "/tmp", + }, + { + name: "nested file", + inputPath: "/apps/namespace/app/cluster.yaml", + expectedDir: "/apps/namespace/app", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Test the path manipulation logic that GitPush uses + actualDir := filepath.Dir(tt.inputPath) + + if actualDir != tt.expectedDir { + t.Errorf("Expected directory '%s', got '%s'", tt.expectedDir, actualDir) + } + }) + } +} + +func TestGitActivities_CommandStructure(t *testing.T) { + // Test that the separate git activities construct the expected commands testPath := "/tmp/test/app/cluster.yaml" expectedDir := "/tmp/test/app" expectedFile := "cluster.yaml" + commitMessage := "chore(khuedoan/blog): update production version" // Verify the path parsing logic actualDir := filepath.Dir(testPath) @@ -246,32 +315,50 @@ func TestGitSync_CommandStructure(t *testing.T) { t.Errorf("Expected filename '%s', got '%s'", expectedFile, actualFile) } - // The GitSync function should construct these commands: - expectedCommands := [][]string{ - {"git", "-C", expectedDir, "add", expectedFile}, - {"git", "-C", expectedDir, "commit", "-m", "Update app version"}, - {"git", "-C", expectedDir, "push"}, + // Verify the expected command structures for each activity + tests := []struct { + name string + expectedCommand []string + description string + }{ + { + name: "GitAdd command", + expectedCommand: []string{"git", "-C", expectedDir, "add", expectedFile}, + description: "GitAdd should construct git add command", + }, + { + name: "GitCommit command", + expectedCommand: []string{"git", "-C", expectedDir, "commit", "-m", commitMessage}, + description: "GitCommit should construct git commit command with message", + }, + { + name: "GitPush command", + expectedCommand: []string{"git", "-C", expectedDir, "push"}, + description: "GitPush should construct git push command", + }, } - // Verify the command structure is as expected - if len(expectedCommands) != 3 { - t.Errorf("Expected 3 git commands, got %d", len(expectedCommands)) - } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cmd := tt.expectedCommand - // Check each command structure - for i, cmd := range expectedCommands { - if len(cmd) < 2 { - t.Errorf("Command %d should have at least 2 parts, got %d", i, len(cmd)) - continue - } + if len(cmd) < 3 { + t.Errorf("%s should have at least 3 parts, got %d", tt.description, len(cmd)) + return + } - if cmd[0] != "git" { - t.Errorf("Command %d should start with 'git', got '%s'", i, cmd[0]) - } + if cmd[0] != "git" { + t.Errorf("%s should start with 'git', got '%s'", tt.description, cmd[0]) + } - if cmd[1] != "-C" { - t.Errorf("Command %d should have '-C' as second argument, got '%s'", i, cmd[1]) - } + if cmd[1] != "-C" { + t.Errorf("%s should have '-C' as second argument, got '%s'", tt.description, cmd[1]) + } + + if cmd[2] != expectedDir { + t.Errorf("%s should use directory '%s', got '%s'", tt.description, expectedDir, cmd[2]) + } + }) } } diff --git a/controller/worker/main.go b/controller/worker/main.go index de50315..cec6354 100644 --- a/controller/worker/main.go +++ b/controller/worker/main.go @@ -32,7 +32,9 @@ func main() { w.RegisterActivity(activities.PushRenderedApp) w.RegisterActivity(activities.DiscoverApps) w.RegisterActivity(activities.UpdateAppVersion) - w.RegisterActivity(activities.GitSync) + w.RegisterActivity(activities.GitAdd) + w.RegisterActivity(activities.GitCommit) + w.RegisterActivity(activities.GitPush) w.RegisterWorkflow(workflows.Infra) w.RegisterWorkflow(workflows.Platform) diff --git a/controller/workflows/app_update.go b/controller/workflows/app_update.go index c48ca06..d168e87 100644 --- a/controller/workflows/app_update.go +++ b/controller/workflows/app_update.go @@ -17,6 +17,7 @@ type AppUpdateInput struct { Namespace string App string Cluster string + Registry string NewImages []activities.Image } @@ -67,24 +68,68 @@ func AppUpdate(ctx workflow.Context, input AppUpdateInput) error { "app", input.App, "cluster", input.Cluster) - // Step 3: Sync changes back to git + // Step 3: Git add changes appFilePath := filepath.Join(appsDir, input.Namespace, input.App, fmt.Sprintf("%s.yaml", input.Cluster)) if err := workflow.ExecuteActivity( workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ - StartToCloseTimeout: 1 * time.Minute, + StartToCloseTimeout: 30 * time.Second, }), - activities.GitSync, + activities.GitAdd, appFilePath, ).Get(ctx, nil); err != nil { - logger.Error("Failed to sync changes to git", "error", err) - return fmt.Errorf("failed to sync changes to git: %w", err) + logger.Error("Failed to add changes to git", "error", err) + return fmt.Errorf("failed to add changes to git: %w", err) + } + + // Step 4: Git commit changes + commitMessage := fmt.Sprintf("chore(%s/%s): update %s version", input.Namespace, input.App, input.Cluster) + if err := workflow.ExecuteActivity( + workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ + StartToCloseTimeout: 30 * time.Second, + }), + activities.GitCommit, + workspace, + commitMessage, + ).Get(ctx, nil); err != nil { + logger.Error("Failed to commit changes to git", "error", err) + return fmt.Errorf("failed to commit changes to git: %w", err) + } + + // Step 5: Git push changes + if err := workflow.ExecuteActivity( + workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ + StartToCloseTimeout: 1 * time.Minute, + }), + activities.GitPush, + workspace, + ).Get(ctx, nil); err != nil { + logger.Error("Failed to push changes to git", "error", err) + return fmt.Errorf("failed to push changes to git: %w", err) + } + + // Step 6: Push rendered app to registry + var pushResult *activities.PushResult + if err := workflow.ExecuteActivity( + workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ + StartToCloseTimeout: 2 * time.Minute, + }), + activities.PushRenderedApp, + appsDir, + input.Namespace, + input.App, + input.Cluster, + input.Registry, + ).Get(ctx, &pushResult); err != nil { + logger.Error("Failed to push rendered app to registry", "error", err) + return fmt.Errorf("failed to push rendered app to registry: %w", err) } logger.Info("AppUpdate workflow completed successfully", "namespace", input.Namespace, "app", input.App, "cluster", input.Cluster, - "updated_images", len(input.NewImages)) + "updated_images", len(input.NewImages), + "rendered_app_digest", pushResult.Digest) return nil } diff --git a/controller/workflows/app_update_test.go b/controller/workflows/app_update_test.go index c021efc..7080b8a 100644 --- a/controller/workflows/app_update_test.go +++ b/controller/workflows/app_update_test.go @@ -35,17 +35,26 @@ func (s *AppUpdateWorkflowTestSuite) TestAppUpdate_Success() { Namespace: "khuedoan", App: "blog", Cluster: "production", + Registry: "registry.example.com", NewImages: []activities.Image{ {Repository: "docker.io/khuedoan/blog", Tag: "abc123def456789"}, }, } workspace := "/tmp/cloudlab-repos/abc123" + appFilePath := workspace + "/apps/khuedoan/blog/production.yaml" + mockPushResult := &activities.PushResult{ + Reference: "registry.example.com/khuedoan/blog:production", + Digest: "sha256:abc123def456", + } s.env.OnActivity(activities.Clone, mock.Anything, input.Url, input.Revision).Return(workspace, nil) s.env.OnActivity(activities.UpdateAppVersion, mock.Anything, workspace+"/apps", input.Namespace, input.App, input.Cluster, input.NewImages).Return(nil) - s.env.OnActivity(activities.GitSync, mock.Anything, - workspace+"/apps/khuedoan/blog/production.yaml").Return(nil) + s.env.OnActivity(activities.GitAdd, mock.Anything, appFilePath).Return(nil) + s.env.OnActivity(activities.GitCommit, mock.Anything, workspace, "chore(khuedoan/blog): update production version").Return(nil) + s.env.OnActivity(activities.GitPush, mock.Anything, workspace).Return(nil) + s.env.OnActivity(activities.PushRenderedApp, mock.Anything, + workspace+"/apps", input.Namespace, input.App, input.Cluster, input.Registry).Return(mockPushResult, nil) s.env.ExecuteWorkflow(AppUpdate, input) @@ -60,6 +69,7 @@ func (s *AppUpdateWorkflowTestSuite) TestAppUpdate_CloneFailure() { Namespace: "test", App: "app", Cluster: "local", + Registry: "registry.127.0.0.1.sslip.io", NewImages: []activities.Image{ {Repository: "test/app", Tag: "v1.0.0"}, }, @@ -81,6 +91,7 @@ func (s *AppUpdateWorkflowTestSuite) TestAppUpdate_UpdateAppVersionFailure() { Namespace: "finance", App: "actualbudget", Cluster: "local", + Registry: "registry.127.0.0.1.sslip.io", NewImages: []activities.Image{ {Repository: "docker.io/actualbudget/actual-server", Tag: "25.7.0-alpine"}, }, @@ -99,31 +110,123 @@ func (s *AppUpdateWorkflowTestSuite) TestAppUpdate_UpdateAppVersionFailure() { s.Contains(s.env.GetWorkflowError().Error(), "failed to update app version") } -func (s *AppUpdateWorkflowTestSuite) TestAppUpdate_GitSyncFailure() { +func (s *AppUpdateWorkflowTestSuite) TestAppUpdate_GitAddFailure() { + input := AppUpdateInput{ + Url: "https://github.com/example/cloudlab.git", + Revision: "main", + Namespace: "khuedoan", + App: "notes", + Cluster: "production", + Registry: "registry.example.com", + NewImages: []activities.Image{ + {Repository: "ghcr.io/silverbulletmd/silverbullet", Tag: "v3"}, + }, + } + workspace := "/tmp/cloudlab-repos/ghi789" + appFilePath := workspace + "/apps/khuedoan/notes/production.yaml" + + s.env.OnActivity(activities.Clone, mock.Anything, input.Url, input.Revision).Return(workspace, nil) + s.env.OnActivity(activities.UpdateAppVersion, mock.Anything, + workspace+"/apps", input.Namespace, input.App, input.Cluster, input.NewImages).Return(nil) + s.env.OnActivity(activities.GitAdd, mock.Anything, appFilePath).Return( + errors.New("git add failed: file not found")) + + s.env.ExecuteWorkflow(AppUpdate, input) + + s.True(s.env.IsWorkflowCompleted()) + s.Error(s.env.GetWorkflowError()) + s.Contains(s.env.GetWorkflowError().Error(), "failed to add changes to git") +} + +func (s *AppUpdateWorkflowTestSuite) TestAppUpdate_GitCommitFailure() { input := AppUpdateInput{ Url: "https://github.com/example/cloudlab.git", Revision: "main", Namespace: "khuedoan", App: "notes", Cluster: "production", + Registry: "registry.example.com", NewImages: []activities.Image{ {Repository: "ghcr.io/silverbulletmd/silverbullet", Tag: "v3"}, }, } workspace := "/tmp/cloudlab-repos/ghi789" + appFilePath := workspace + "/apps/khuedoan/notes/production.yaml" s.env.OnActivity(activities.Clone, mock.Anything, input.Url, input.Revision).Return(workspace, nil) s.env.OnActivity(activities.UpdateAppVersion, mock.Anything, workspace+"/apps", input.Namespace, input.App, input.Cluster, input.NewImages).Return(nil) - s.env.OnActivity(activities.GitSync, mock.Anything, - workspace+"/apps/khuedoan/notes/production.yaml").Return( + s.env.OnActivity(activities.GitAdd, mock.Anything, appFilePath).Return(nil) + s.env.OnActivity(activities.GitCommit, mock.Anything, workspace, "chore(khuedoan/notes): update production version").Return( + errors.New("git commit failed: nothing to commit")) + + s.env.ExecuteWorkflow(AppUpdate, input) + + s.True(s.env.IsWorkflowCompleted()) + s.Error(s.env.GetWorkflowError()) + s.Contains(s.env.GetWorkflowError().Error(), "failed to commit changes to git") +} + +func (s *AppUpdateWorkflowTestSuite) TestAppUpdate_GitPushFailure() { + input := AppUpdateInput{ + Url: "https://github.com/example/cloudlab.git", + Revision: "main", + Namespace: "khuedoan", + App: "notes", + Cluster: "production", + Registry: "registry.example.com", + NewImages: []activities.Image{ + {Repository: "ghcr.io/silverbulletmd/silverbullet", Tag: "v3"}, + }, + } + workspace := "/tmp/cloudlab-repos/ghi789" + appFilePath := workspace + "/apps/khuedoan/notes/production.yaml" + + s.env.OnActivity(activities.Clone, mock.Anything, input.Url, input.Revision).Return(workspace, nil) + s.env.OnActivity(activities.UpdateAppVersion, mock.Anything, + workspace+"/apps", input.Namespace, input.App, input.Cluster, input.NewImages).Return(nil) + s.env.OnActivity(activities.GitAdd, mock.Anything, appFilePath).Return(nil) + s.env.OnActivity(activities.GitCommit, mock.Anything, workspace, "chore(khuedoan/notes): update production version").Return(nil) + s.env.OnActivity(activities.GitPush, mock.Anything, workspace).Return( errors.New("git push failed: authentication required")) s.env.ExecuteWorkflow(AppUpdate, input) s.True(s.env.IsWorkflowCompleted()) s.Error(s.env.GetWorkflowError()) - s.Contains(s.env.GetWorkflowError().Error(), "failed to sync changes to git") + s.Contains(s.env.GetWorkflowError().Error(), "failed to push changes to git") +} + +func (s *AppUpdateWorkflowTestSuite) TestAppUpdate_PushRenderedAppFailure() { + input := AppUpdateInput{ + Url: "https://github.com/example/cloudlab.git", + Revision: "main", + Namespace: "khuedoan", + App: "notes", + Cluster: "production", + Registry: "registry.example.com", + NewImages: []activities.Image{ + {Repository: "ghcr.io/silverbulletmd/silverbullet", Tag: "v3"}, + }, + } + workspace := "/tmp/cloudlab-repos/ghi789" + appFilePath := workspace + "/apps/khuedoan/notes/production.yaml" + + s.env.OnActivity(activities.Clone, mock.Anything, input.Url, input.Revision).Return(workspace, nil) + s.env.OnActivity(activities.UpdateAppVersion, mock.Anything, + workspace+"/apps", input.Namespace, input.App, input.Cluster, input.NewImages).Return(nil) + s.env.OnActivity(activities.GitAdd, mock.Anything, appFilePath).Return(nil) + s.env.OnActivity(activities.GitCommit, mock.Anything, workspace, "chore(khuedoan/notes): update production version").Return(nil) + s.env.OnActivity(activities.GitPush, mock.Anything, workspace).Return(nil) + s.env.OnActivity(activities.PushRenderedApp, mock.Anything, + workspace+"/apps", input.Namespace, input.App, input.Cluster, input.Registry).Return( + nil, errors.New("helm template failed: chart not found")) + + s.env.ExecuteWorkflow(AppUpdate, input) + + s.True(s.env.IsWorkflowCompleted()) + s.Error(s.env.GetWorkflowError()) + s.Contains(s.env.GetWorkflowError().Error(), "failed to push rendered app to registry") } func (s *AppUpdateWorkflowTestSuite) TestAppUpdate_MultipleImages() { @@ -133,18 +236,27 @@ func (s *AppUpdateWorkflowTestSuite) TestAppUpdate_MultipleImages() { Namespace: "test", App: "example", Cluster: "local", + Registry: "zot.zot.svc.cluster.local", NewImages: []activities.Image{ {Repository: "zot.zot.svc.cluster.local/example-service", Tag: "newcommithash123"}, {Repository: "docker.io/redis", Tag: "7.0-alpine"}, }, } workspace := "/tmp/cloudlab-repos/jkl012" + appFilePath := workspace + "/apps/test/example/local.yaml" + mockPushResult := &activities.PushResult{ + Reference: "zot.zot.svc.cluster.local/test/example:local", + Digest: "sha256:def789abc123", + } s.env.OnActivity(activities.Clone, mock.Anything, input.Url, input.Revision).Return(workspace, nil) s.env.OnActivity(activities.UpdateAppVersion, mock.Anything, workspace+"/apps", input.Namespace, input.App, input.Cluster, input.NewImages).Return(nil) - s.env.OnActivity(activities.GitSync, mock.Anything, - workspace+"/apps/test/example/local.yaml").Return(nil) + s.env.OnActivity(activities.GitAdd, mock.Anything, appFilePath).Return(nil) + s.env.OnActivity(activities.GitCommit, mock.Anything, workspace, "chore(test/example): update local version").Return(nil) + s.env.OnActivity(activities.GitPush, mock.Anything, workspace).Return(nil) + s.env.OnActivity(activities.PushRenderedApp, mock.Anything, + workspace+"/apps", input.Namespace, input.App, input.Cluster, input.Registry).Return(mockPushResult, nil) s.env.ExecuteWorkflow(AppUpdate, input) @@ -160,17 +272,26 @@ func (s *AppUpdateWorkflowTestSuite) TestAppUpdate_RealWorldExample() { Namespace: "khuedoan", App: "blog", Cluster: "production", + Registry: "registry.cloudlab.khuedoan.com", NewImages: []activities.Image{ {Repository: "docker.io/khuedoan/blog", Tag: "1234567890abcdef1234567890abcdef12345678"}, }, } workspace := "/tmp/cloudlab-repos/realworld123" + appFilePath := workspace + "/apps/khuedoan/blog/production.yaml" + mockPushResult := &activities.PushResult{ + Reference: "registry.cloudlab.khuedoan.com/khuedoan/blog:production", + Digest: "sha256:realworld789", + } s.env.OnActivity(activities.Clone, mock.Anything, input.Url, input.Revision).Return(workspace, nil) s.env.OnActivity(activities.UpdateAppVersion, mock.Anything, workspace+"/apps", input.Namespace, input.App, input.Cluster, input.NewImages).Return(nil) - s.env.OnActivity(activities.GitSync, mock.Anything, - workspace+"/apps/khuedoan/blog/production.yaml").Return(nil) + s.env.OnActivity(activities.GitAdd, mock.Anything, appFilePath).Return(nil) + s.env.OnActivity(activities.GitCommit, mock.Anything, workspace, "chore(khuedoan/blog): update production version").Return(nil) + s.env.OnActivity(activities.GitPush, mock.Anything, workspace).Return(nil) + s.env.OnActivity(activities.PushRenderedApp, mock.Anything, + workspace+"/apps", input.Namespace, input.App, input.Cluster, input.Registry).Return(mockPushResult, nil) s.env.ExecuteWorkflow(AppUpdate, input) @@ -185,6 +306,7 @@ func (s *AppUpdateWorkflowTestSuite) TestAppUpdate_ActivityTimeout() { Namespace: "test", App: "slow-app", Cluster: "production", + Registry: "registry.example.com", NewImages: []activities.Image{ {Repository: "test/slow-app", Tag: "v1.0.0"}, }, @@ -206,15 +328,24 @@ func (s *AppUpdateWorkflowTestSuite) TestAppUpdate_EmptyImages() { Namespace: "test", App: "app", Cluster: "local", + Registry: "registry.127.0.0.1.sslip.io", NewImages: []activities.Image{}, // Empty images array } workspace := "/tmp/cloudlab-repos/empty123" + appFilePath := workspace + "/apps/test/app/local.yaml" + mockPushResult := &activities.PushResult{ + Reference: "registry.127.0.0.1.sslip.io/test/app:local", + Digest: "sha256:empty456", + } s.env.OnActivity(activities.Clone, mock.Anything, input.Url, input.Revision).Return(workspace, nil) s.env.OnActivity(activities.UpdateAppVersion, mock.Anything, workspace+"/apps", input.Namespace, input.App, input.Cluster, input.NewImages).Return(nil) - s.env.OnActivity(activities.GitSync, mock.Anything, - workspace+"/apps/test/app/local.yaml").Return(nil) + s.env.OnActivity(activities.GitAdd, mock.Anything, appFilePath).Return(nil) + s.env.OnActivity(activities.GitCommit, mock.Anything, workspace, "chore(test/app): update local version").Return(nil) + s.env.OnActivity(activities.GitPush, mock.Anything, workspace).Return(nil) + s.env.OnActivity(activities.PushRenderedApp, mock.Anything, + workspace+"/apps", input.Namespace, input.App, input.Cluster, input.Registry).Return(mockPushResult, nil) s.env.ExecuteWorkflow(AppUpdate, input) @@ -229,17 +360,26 @@ func (s *AppUpdateWorkflowTestSuite) TestAppUpdate_SpecialCharactersInPath() { Namespace: "test-namespace", App: "app-with-dashes", Cluster: "staging-env", + Registry: "registry.example.com", NewImages: []activities.Image{ {Repository: "registry.example.com/test/app-with-dashes", Tag: "v1.2.3-rc1"}, }, } workspace := "/tmp/cloudlab-repos/special456" + appFilePath := workspace + "/apps/test-namespace/app-with-dashes/staging-env.yaml" + mockPushResult := &activities.PushResult{ + Reference: "registry.example.com/test-namespace/app-with-dashes:staging-env", + Digest: "sha256:special123", + } s.env.OnActivity(activities.Clone, mock.Anything, input.Url, input.Revision).Return(workspace, nil) s.env.OnActivity(activities.UpdateAppVersion, mock.Anything, workspace+"/apps", input.Namespace, input.App, input.Cluster, input.NewImages).Return(nil) - s.env.OnActivity(activities.GitSync, mock.Anything, - workspace+"/apps/test-namespace/app-with-dashes/staging-env.yaml").Return(nil) + s.env.OnActivity(activities.GitAdd, mock.Anything, appFilePath).Return(nil) + s.env.OnActivity(activities.GitCommit, mock.Anything, workspace, "chore(test-namespace/app-with-dashes): update staging-env version").Return(nil) + s.env.OnActivity(activities.GitPush, mock.Anything, workspace).Return(nil) + s.env.OnActivity(activities.PushRenderedApp, mock.Anything, + workspace+"/apps", input.Namespace, input.App, input.Cluster, input.Registry).Return(mockPushResult, nil) s.env.ExecuteWorkflow(AppUpdate, input)