this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(controller): push to OCI after updating app version

Also split Git activities for more reliable flow.

Khue Doan 8226a41b 4399d4ed

+356 -64
+1 -1
README.md
··· 150 150 ## TODOs 151 151 152 152 - Fix OCI plain HTTP for local development 153 - - Remove hardcoded git username and email 153 + - Config git username and email 154 154 - Credentials for the worker (SSH priv + pub + knowhosts?) 155 155 156 156 ## Acknowledgments and References
+33 -15
controller/activities/git.go
··· 87 87 return modules, nil 88 88 } 89 89 90 - func GitSync(ctx context.Context, path string) error { 90 + func GitAdd(ctx context.Context, path string) error { 91 91 logger := activity.GetLogger(ctx) 92 92 93 93 dir := filepath.Dir(path) 94 94 relPath := filepath.Base(path) 95 95 96 - cmds := [][]string{ 97 - {"git", "-C", dir, "config", "user.name", "Bot"}, 98 - {"git", "-C", dir, "config", "user.email", "bot@khuedoan.com"}, 99 - {"git", "-C", dir, "add", relPath}, 100 - {"git", "-C", dir, "commit", "-m", "Update app version"}, 101 - {"git", "-C", dir, "push"}, 96 + cmd := exec.Command("git", "-C", dir, "add", relPath) 97 + cmd.Stdout = os.Stdout 98 + cmd.Stderr = os.Stderr 99 + if err := cmd.Run(); err != nil { 100 + logger.Error("git add failed", "error", err) 101 + return fmt.Errorf("git add failed: %w", err) 102 102 } 103 103 104 - for _, args := range cmds { 105 - cmd := exec.Command(args[0], args[1:]...) 106 - cmd.Stdout = os.Stdout 107 - cmd.Stderr = os.Stderr 108 - if err := cmd.Run(); err != nil { 109 - logger.Error("command %v failed: %w", args, err) 110 - return err 111 - } 104 + return nil 105 + } 106 + 107 + func GitCommit(ctx context.Context, dir string, message string) error { 108 + logger := activity.GetLogger(ctx) 109 + 110 + cmd := exec.Command("git", "-C", dir, "commit", "-m", message) 111 + cmd.Stdout = os.Stdout 112 + cmd.Stderr = os.Stderr 113 + if err := cmd.Run(); err != nil { 114 + logger.Error("git commit failed", "error", err) 115 + return fmt.Errorf("git commit failed: %w", err) 116 + } 117 + 118 + return nil 119 + } 120 + 121 + func GitPush(ctx context.Context, dir string) error { 122 + logger := activity.GetLogger(ctx) 123 + 124 + cmd := exec.Command("git", "-C", dir, "push") 125 + cmd.Stdout = os.Stdout 126 + cmd.Stderr = os.Stderr 127 + if err := cmd.Run(); err != nil { 128 + logger.Error("git push failed", "error", err) 129 + return fmt.Errorf("git push failed: %w", err) 112 130 } 113 131 114 132 return nil
+114 -27
controller/activities/git_test.go
··· 181 181 return modules 182 182 } 183 183 184 - func TestGitSync_PathParsing(t *testing.T) { 185 - // Test the path parsing logic in GitSync without requiring actual git commands 184 + func TestGitAdd_PathParsing(t *testing.T) { 185 + // Test the path parsing logic in GitAdd without requiring actual git commands 186 186 tests := []struct { 187 187 name string 188 188 inputPath string ··· 211 211 212 212 for _, tt := range tests { 213 213 t.Run(tt.name, func(t *testing.T) { 214 - // Test the path manipulation logic that GitSync uses 214 + // Test the path manipulation logic that GitAdd uses 215 215 actualDir := filepath.Dir(tt.inputPath) 216 216 actualFile := filepath.Base(tt.inputPath) 217 217 ··· 226 226 } 227 227 } 228 228 229 - func TestGitSync_CommandStructure(t *testing.T) { 230 - // Test that GitSync constructs the expected git commands 231 - // This test validates the command structure without executing them 229 + func TestGitCommit_PathParsing(t *testing.T) { 230 + // Test the path parsing logic in GitCommit 231 + tests := []struct { 232 + name string 233 + inputPath string 234 + expectedDir string 235 + message string 236 + }{ 237 + { 238 + name: "simple file with default message", 239 + inputPath: "/tmp/test.yaml", 240 + expectedDir: "/tmp", 241 + message: "chore(test/app): update local version", 242 + }, 243 + { 244 + name: "nested file with custom message", 245 + inputPath: "/apps/namespace/app/cluster.yaml", 246 + expectedDir: "/apps/namespace/app", 247 + message: "feat: update application configuration", 248 + }, 249 + } 250 + 251 + for _, tt := range tests { 252 + t.Run(tt.name, func(t *testing.T) { 253 + // Test the path manipulation logic that GitCommit uses 254 + actualDir := filepath.Dir(tt.inputPath) 255 + 256 + if actualDir != tt.expectedDir { 257 + t.Errorf("Expected directory '%s', got '%s'", tt.expectedDir, actualDir) 258 + } 259 + 260 + // Verify message is not empty 261 + if tt.message == "" { 262 + t.Error("Commit message should not be empty") 263 + } 264 + }) 265 + } 266 + } 267 + 268 + func TestGitPush_PathParsing(t *testing.T) { 269 + // Test the path parsing logic in GitPush 270 + tests := []struct { 271 + name string 272 + inputPath string 273 + expectedDir string 274 + }{ 275 + { 276 + name: "simple file", 277 + inputPath: "/tmp/test.yaml", 278 + expectedDir: "/tmp", 279 + }, 280 + { 281 + name: "nested file", 282 + inputPath: "/apps/namespace/app/cluster.yaml", 283 + expectedDir: "/apps/namespace/app", 284 + }, 285 + } 286 + 287 + for _, tt := range tests { 288 + t.Run(tt.name, func(t *testing.T) { 289 + // Test the path manipulation logic that GitPush uses 290 + actualDir := filepath.Dir(tt.inputPath) 291 + 292 + if actualDir != tt.expectedDir { 293 + t.Errorf("Expected directory '%s', got '%s'", tt.expectedDir, actualDir) 294 + } 295 + }) 296 + } 297 + } 232 298 299 + func TestGitActivities_CommandStructure(t *testing.T) { 300 + // Test that the separate git activities construct the expected commands 233 301 testPath := "/tmp/test/app/cluster.yaml" 234 302 expectedDir := "/tmp/test/app" 235 303 expectedFile := "cluster.yaml" 304 + commitMessage := "chore(khuedoan/blog): update production version" 236 305 237 306 // Verify the path parsing logic 238 307 actualDir := filepath.Dir(testPath) ··· 246 315 t.Errorf("Expected filename '%s', got '%s'", expectedFile, actualFile) 247 316 } 248 317 249 - // The GitSync function should construct these commands: 250 - expectedCommands := [][]string{ 251 - {"git", "-C", expectedDir, "add", expectedFile}, 252 - {"git", "-C", expectedDir, "commit", "-m", "Update app version"}, 253 - {"git", "-C", expectedDir, "push"}, 318 + // Verify the expected command structures for each activity 319 + tests := []struct { 320 + name string 321 + expectedCommand []string 322 + description string 323 + }{ 324 + { 325 + name: "GitAdd command", 326 + expectedCommand: []string{"git", "-C", expectedDir, "add", expectedFile}, 327 + description: "GitAdd should construct git add command", 328 + }, 329 + { 330 + name: "GitCommit command", 331 + expectedCommand: []string{"git", "-C", expectedDir, "commit", "-m", commitMessage}, 332 + description: "GitCommit should construct git commit command with message", 333 + }, 334 + { 335 + name: "GitPush command", 336 + expectedCommand: []string{"git", "-C", expectedDir, "push"}, 337 + description: "GitPush should construct git push command", 338 + }, 254 339 } 255 340 256 - // Verify the command structure is as expected 257 - if len(expectedCommands) != 3 { 258 - t.Errorf("Expected 3 git commands, got %d", len(expectedCommands)) 259 - } 341 + for _, tt := range tests { 342 + t.Run(tt.name, func(t *testing.T) { 343 + cmd := tt.expectedCommand 260 344 261 - // Check each command structure 262 - for i, cmd := range expectedCommands { 263 - if len(cmd) < 2 { 264 - t.Errorf("Command %d should have at least 2 parts, got %d", i, len(cmd)) 265 - continue 266 - } 345 + if len(cmd) < 3 { 346 + t.Errorf("%s should have at least 3 parts, got %d", tt.description, len(cmd)) 347 + return 348 + } 349 + 350 + if cmd[0] != "git" { 351 + t.Errorf("%s should start with 'git', got '%s'", tt.description, cmd[0]) 352 + } 267 353 268 - if cmd[0] != "git" { 269 - t.Errorf("Command %d should start with 'git', got '%s'", i, cmd[0]) 270 - } 354 + if cmd[1] != "-C" { 355 + t.Errorf("%s should have '-C' as second argument, got '%s'", tt.description, cmd[1]) 356 + } 271 357 272 - if cmd[1] != "-C" { 273 - t.Errorf("Command %d should have '-C' as second argument, got '%s'", i, cmd[1]) 274 - } 358 + if cmd[2] != expectedDir { 359 + t.Errorf("%s should use directory '%s', got '%s'", tt.description, expectedDir, cmd[2]) 360 + } 361 + }) 275 362 } 276 363 } 277 364
+3 -1
controller/worker/main.go
··· 32 32 w.RegisterActivity(activities.PushRenderedApp) 33 33 w.RegisterActivity(activities.DiscoverApps) 34 34 w.RegisterActivity(activities.UpdateAppVersion) 35 - w.RegisterActivity(activities.GitSync) 35 + w.RegisterActivity(activities.GitAdd) 36 + w.RegisterActivity(activities.GitCommit) 37 + w.RegisterActivity(activities.GitPush) 36 38 37 39 w.RegisterWorkflow(workflows.Infra) 38 40 w.RegisterWorkflow(workflows.Platform)
+51 -6
controller/workflows/app_update.go
··· 17 17 Namespace string 18 18 App string 19 19 Cluster string 20 + Registry string 20 21 NewImages []activities.Image 21 22 } 22 23 ··· 67 68 "app", input.App, 68 69 "cluster", input.Cluster) 69 70 70 - // Step 3: Sync changes back to git 71 + // Step 3: Git add changes 71 72 appFilePath := filepath.Join(appsDir, input.Namespace, input.App, fmt.Sprintf("%s.yaml", input.Cluster)) 73 + if err := workflow.ExecuteActivity( 74 + workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ 75 + StartToCloseTimeout: 30 * time.Second, 76 + }), 77 + activities.GitAdd, 78 + appFilePath, 79 + ).Get(ctx, nil); err != nil { 80 + logger.Error("Failed to add changes to git", "error", err) 81 + return fmt.Errorf("failed to add changes to git: %w", err) 82 + } 83 + 84 + // Step 4: Git commit changes 85 + commitMessage := fmt.Sprintf("chore(%s/%s): update %s version", input.Namespace, input.App, input.Cluster) 86 + if err := workflow.ExecuteActivity( 87 + workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ 88 + StartToCloseTimeout: 30 * time.Second, 89 + }), 90 + activities.GitCommit, 91 + workspace, 92 + commitMessage, 93 + ).Get(ctx, nil); err != nil { 94 + logger.Error("Failed to commit changes to git", "error", err) 95 + return fmt.Errorf("failed to commit changes to git: %w", err) 96 + } 97 + 98 + // Step 5: Git push changes 72 99 if err := workflow.ExecuteActivity( 73 100 workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ 74 101 StartToCloseTimeout: 1 * time.Minute, 75 102 }), 76 - activities.GitSync, 77 - appFilePath, 103 + activities.GitPush, 104 + workspace, 78 105 ).Get(ctx, nil); err != nil { 79 - logger.Error("Failed to sync changes to git", "error", err) 80 - return fmt.Errorf("failed to sync changes to git: %w", err) 106 + logger.Error("Failed to push changes to git", "error", err) 107 + return fmt.Errorf("failed to push changes to git: %w", err) 108 + } 109 + 110 + // Step 6: Push rendered app to registry 111 + var pushResult *activities.PushResult 112 + if err := workflow.ExecuteActivity( 113 + workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ 114 + StartToCloseTimeout: 2 * time.Minute, 115 + }), 116 + activities.PushRenderedApp, 117 + appsDir, 118 + input.Namespace, 119 + input.App, 120 + input.Cluster, 121 + input.Registry, 122 + ).Get(ctx, &pushResult); err != nil { 123 + logger.Error("Failed to push rendered app to registry", "error", err) 124 + return fmt.Errorf("failed to push rendered app to registry: %w", err) 81 125 } 82 126 83 127 logger.Info("AppUpdate workflow completed successfully", 84 128 "namespace", input.Namespace, 85 129 "app", input.App, 86 130 "cluster", input.Cluster, 87 - "updated_images", len(input.NewImages)) 131 + "updated_images", len(input.NewImages), 132 + "rendered_app_digest", pushResult.Digest) 88 133 89 134 return nil 90 135 }
+154 -14
controller/workflows/app_update_test.go
··· 35 35 Namespace: "khuedoan", 36 36 App: "blog", 37 37 Cluster: "production", 38 + Registry: "registry.example.com", 38 39 NewImages: []activities.Image{ 39 40 {Repository: "docker.io/khuedoan/blog", Tag: "abc123def456789"}, 40 41 }, 41 42 } 42 43 workspace := "/tmp/cloudlab-repos/abc123" 44 + appFilePath := workspace + "/apps/khuedoan/blog/production.yaml" 45 + mockPushResult := &activities.PushResult{ 46 + Reference: "registry.example.com/khuedoan/blog:production", 47 + Digest: "sha256:abc123def456", 48 + } 43 49 44 50 s.env.OnActivity(activities.Clone, mock.Anything, input.Url, input.Revision).Return(workspace, nil) 45 51 s.env.OnActivity(activities.UpdateAppVersion, mock.Anything, 46 52 workspace+"/apps", input.Namespace, input.App, input.Cluster, input.NewImages).Return(nil) 47 - s.env.OnActivity(activities.GitSync, mock.Anything, 48 - workspace+"/apps/khuedoan/blog/production.yaml").Return(nil) 53 + s.env.OnActivity(activities.GitAdd, mock.Anything, appFilePath).Return(nil) 54 + s.env.OnActivity(activities.GitCommit, mock.Anything, workspace, "chore(khuedoan/blog): update production version").Return(nil) 55 + s.env.OnActivity(activities.GitPush, mock.Anything, workspace).Return(nil) 56 + s.env.OnActivity(activities.PushRenderedApp, mock.Anything, 57 + workspace+"/apps", input.Namespace, input.App, input.Cluster, input.Registry).Return(mockPushResult, nil) 49 58 50 59 s.env.ExecuteWorkflow(AppUpdate, input) 51 60 ··· 60 69 Namespace: "test", 61 70 App: "app", 62 71 Cluster: "local", 72 + Registry: "registry.127.0.0.1.sslip.io", 63 73 NewImages: []activities.Image{ 64 74 {Repository: "test/app", Tag: "v1.0.0"}, 65 75 }, ··· 81 91 Namespace: "finance", 82 92 App: "actualbudget", 83 93 Cluster: "local", 94 + Registry: "registry.127.0.0.1.sslip.io", 84 95 NewImages: []activities.Image{ 85 96 {Repository: "docker.io/actualbudget/actual-server", Tag: "25.7.0-alpine"}, 86 97 }, ··· 99 110 s.Contains(s.env.GetWorkflowError().Error(), "failed to update app version") 100 111 } 101 112 102 - func (s *AppUpdateWorkflowTestSuite) TestAppUpdate_GitSyncFailure() { 113 + func (s *AppUpdateWorkflowTestSuite) TestAppUpdate_GitAddFailure() { 103 114 input := AppUpdateInput{ 104 115 Url: "https://github.com/example/cloudlab.git", 105 116 Revision: "main", 106 117 Namespace: "khuedoan", 107 118 App: "notes", 108 119 Cluster: "production", 120 + Registry: "registry.example.com", 109 121 NewImages: []activities.Image{ 110 122 {Repository: "ghcr.io/silverbulletmd/silverbullet", Tag: "v3"}, 111 123 }, 112 124 } 113 125 workspace := "/tmp/cloudlab-repos/ghi789" 126 + appFilePath := workspace + "/apps/khuedoan/notes/production.yaml" 114 127 115 128 s.env.OnActivity(activities.Clone, mock.Anything, input.Url, input.Revision).Return(workspace, nil) 116 129 s.env.OnActivity(activities.UpdateAppVersion, mock.Anything, 117 130 workspace+"/apps", input.Namespace, input.App, input.Cluster, input.NewImages).Return(nil) 118 - s.env.OnActivity(activities.GitSync, mock.Anything, 119 - workspace+"/apps/khuedoan/notes/production.yaml").Return( 131 + s.env.OnActivity(activities.GitAdd, mock.Anything, appFilePath).Return( 132 + errors.New("git add failed: file not found")) 133 + 134 + s.env.ExecuteWorkflow(AppUpdate, input) 135 + 136 + s.True(s.env.IsWorkflowCompleted()) 137 + s.Error(s.env.GetWorkflowError()) 138 + s.Contains(s.env.GetWorkflowError().Error(), "failed to add changes to git") 139 + } 140 + 141 + func (s *AppUpdateWorkflowTestSuite) TestAppUpdate_GitCommitFailure() { 142 + input := AppUpdateInput{ 143 + Url: "https://github.com/example/cloudlab.git", 144 + Revision: "main", 145 + Namespace: "khuedoan", 146 + App: "notes", 147 + Cluster: "production", 148 + Registry: "registry.example.com", 149 + NewImages: []activities.Image{ 150 + {Repository: "ghcr.io/silverbulletmd/silverbullet", Tag: "v3"}, 151 + }, 152 + } 153 + workspace := "/tmp/cloudlab-repos/ghi789" 154 + appFilePath := workspace + "/apps/khuedoan/notes/production.yaml" 155 + 156 + s.env.OnActivity(activities.Clone, mock.Anything, input.Url, input.Revision).Return(workspace, nil) 157 + s.env.OnActivity(activities.UpdateAppVersion, mock.Anything, 158 + workspace+"/apps", input.Namespace, input.App, input.Cluster, input.NewImages).Return(nil) 159 + s.env.OnActivity(activities.GitAdd, mock.Anything, appFilePath).Return(nil) 160 + s.env.OnActivity(activities.GitCommit, mock.Anything, workspace, "chore(khuedoan/notes): update production version").Return( 161 + errors.New("git commit failed: nothing to commit")) 162 + 163 + s.env.ExecuteWorkflow(AppUpdate, input) 164 + 165 + s.True(s.env.IsWorkflowCompleted()) 166 + s.Error(s.env.GetWorkflowError()) 167 + s.Contains(s.env.GetWorkflowError().Error(), "failed to commit changes to git") 168 + } 169 + 170 + func (s *AppUpdateWorkflowTestSuite) TestAppUpdate_GitPushFailure() { 171 + input := AppUpdateInput{ 172 + Url: "https://github.com/example/cloudlab.git", 173 + Revision: "main", 174 + Namespace: "khuedoan", 175 + App: "notes", 176 + Cluster: "production", 177 + Registry: "registry.example.com", 178 + NewImages: []activities.Image{ 179 + {Repository: "ghcr.io/silverbulletmd/silverbullet", Tag: "v3"}, 180 + }, 181 + } 182 + workspace := "/tmp/cloudlab-repos/ghi789" 183 + appFilePath := workspace + "/apps/khuedoan/notes/production.yaml" 184 + 185 + s.env.OnActivity(activities.Clone, mock.Anything, input.Url, input.Revision).Return(workspace, nil) 186 + s.env.OnActivity(activities.UpdateAppVersion, mock.Anything, 187 + workspace+"/apps", input.Namespace, input.App, input.Cluster, input.NewImages).Return(nil) 188 + s.env.OnActivity(activities.GitAdd, mock.Anything, appFilePath).Return(nil) 189 + s.env.OnActivity(activities.GitCommit, mock.Anything, workspace, "chore(khuedoan/notes): update production version").Return(nil) 190 + s.env.OnActivity(activities.GitPush, mock.Anything, workspace).Return( 120 191 errors.New("git push failed: authentication required")) 121 192 122 193 s.env.ExecuteWorkflow(AppUpdate, input) 123 194 124 195 s.True(s.env.IsWorkflowCompleted()) 125 196 s.Error(s.env.GetWorkflowError()) 126 - s.Contains(s.env.GetWorkflowError().Error(), "failed to sync changes to git") 197 + s.Contains(s.env.GetWorkflowError().Error(), "failed to push changes to git") 198 + } 199 + 200 + func (s *AppUpdateWorkflowTestSuite) TestAppUpdate_PushRenderedAppFailure() { 201 + input := AppUpdateInput{ 202 + Url: "https://github.com/example/cloudlab.git", 203 + Revision: "main", 204 + Namespace: "khuedoan", 205 + App: "notes", 206 + Cluster: "production", 207 + Registry: "registry.example.com", 208 + NewImages: []activities.Image{ 209 + {Repository: "ghcr.io/silverbulletmd/silverbullet", Tag: "v3"}, 210 + }, 211 + } 212 + workspace := "/tmp/cloudlab-repos/ghi789" 213 + appFilePath := workspace + "/apps/khuedoan/notes/production.yaml" 214 + 215 + s.env.OnActivity(activities.Clone, mock.Anything, input.Url, input.Revision).Return(workspace, nil) 216 + s.env.OnActivity(activities.UpdateAppVersion, mock.Anything, 217 + workspace+"/apps", input.Namespace, input.App, input.Cluster, input.NewImages).Return(nil) 218 + s.env.OnActivity(activities.GitAdd, mock.Anything, appFilePath).Return(nil) 219 + s.env.OnActivity(activities.GitCommit, mock.Anything, workspace, "chore(khuedoan/notes): update production version").Return(nil) 220 + s.env.OnActivity(activities.GitPush, mock.Anything, workspace).Return(nil) 221 + s.env.OnActivity(activities.PushRenderedApp, mock.Anything, 222 + workspace+"/apps", input.Namespace, input.App, input.Cluster, input.Registry).Return( 223 + nil, errors.New("helm template failed: chart not found")) 224 + 225 + s.env.ExecuteWorkflow(AppUpdate, input) 226 + 227 + s.True(s.env.IsWorkflowCompleted()) 228 + s.Error(s.env.GetWorkflowError()) 229 + s.Contains(s.env.GetWorkflowError().Error(), "failed to push rendered app to registry") 127 230 } 128 231 129 232 func (s *AppUpdateWorkflowTestSuite) TestAppUpdate_MultipleImages() { ··· 133 236 Namespace: "test", 134 237 App: "example", 135 238 Cluster: "local", 239 + Registry: "zot.zot.svc.cluster.local", 136 240 NewImages: []activities.Image{ 137 241 {Repository: "zot.zot.svc.cluster.local/example-service", Tag: "newcommithash123"}, 138 242 {Repository: "docker.io/redis", Tag: "7.0-alpine"}, 139 243 }, 140 244 } 141 245 workspace := "/tmp/cloudlab-repos/jkl012" 246 + appFilePath := workspace + "/apps/test/example/local.yaml" 247 + mockPushResult := &activities.PushResult{ 248 + Reference: "zot.zot.svc.cluster.local/test/example:local", 249 + Digest: "sha256:def789abc123", 250 + } 142 251 143 252 s.env.OnActivity(activities.Clone, mock.Anything, input.Url, input.Revision).Return(workspace, nil) 144 253 s.env.OnActivity(activities.UpdateAppVersion, mock.Anything, 145 254 workspace+"/apps", input.Namespace, input.App, input.Cluster, input.NewImages).Return(nil) 146 - s.env.OnActivity(activities.GitSync, mock.Anything, 147 - workspace+"/apps/test/example/local.yaml").Return(nil) 255 + s.env.OnActivity(activities.GitAdd, mock.Anything, appFilePath).Return(nil) 256 + s.env.OnActivity(activities.GitCommit, mock.Anything, workspace, "chore(test/example): update local version").Return(nil) 257 + s.env.OnActivity(activities.GitPush, mock.Anything, workspace).Return(nil) 258 + s.env.OnActivity(activities.PushRenderedApp, mock.Anything, 259 + workspace+"/apps", input.Namespace, input.App, input.Cluster, input.Registry).Return(mockPushResult, nil) 148 260 149 261 s.env.ExecuteWorkflow(AppUpdate, input) 150 262 ··· 160 272 Namespace: "khuedoan", 161 273 App: "blog", 162 274 Cluster: "production", 275 + Registry: "registry.cloudlab.khuedoan.com", 163 276 NewImages: []activities.Image{ 164 277 {Repository: "docker.io/khuedoan/blog", Tag: "1234567890abcdef1234567890abcdef12345678"}, 165 278 }, 166 279 } 167 280 workspace := "/tmp/cloudlab-repos/realworld123" 281 + appFilePath := workspace + "/apps/khuedoan/blog/production.yaml" 282 + mockPushResult := &activities.PushResult{ 283 + Reference: "registry.cloudlab.khuedoan.com/khuedoan/blog:production", 284 + Digest: "sha256:realworld789", 285 + } 168 286 169 287 s.env.OnActivity(activities.Clone, mock.Anything, input.Url, input.Revision).Return(workspace, nil) 170 288 s.env.OnActivity(activities.UpdateAppVersion, mock.Anything, 171 289 workspace+"/apps", input.Namespace, input.App, input.Cluster, input.NewImages).Return(nil) 172 - s.env.OnActivity(activities.GitSync, mock.Anything, 173 - workspace+"/apps/khuedoan/blog/production.yaml").Return(nil) 290 + s.env.OnActivity(activities.GitAdd, mock.Anything, appFilePath).Return(nil) 291 + s.env.OnActivity(activities.GitCommit, mock.Anything, workspace, "chore(khuedoan/blog): update production version").Return(nil) 292 + s.env.OnActivity(activities.GitPush, mock.Anything, workspace).Return(nil) 293 + s.env.OnActivity(activities.PushRenderedApp, mock.Anything, 294 + workspace+"/apps", input.Namespace, input.App, input.Cluster, input.Registry).Return(mockPushResult, nil) 174 295 175 296 s.env.ExecuteWorkflow(AppUpdate, input) 176 297 ··· 185 306 Namespace: "test", 186 307 App: "slow-app", 187 308 Cluster: "production", 309 + Registry: "registry.example.com", 188 310 NewImages: []activities.Image{ 189 311 {Repository: "test/slow-app", Tag: "v1.0.0"}, 190 312 }, ··· 206 328 Namespace: "test", 207 329 App: "app", 208 330 Cluster: "local", 331 + Registry: "registry.127.0.0.1.sslip.io", 209 332 NewImages: []activities.Image{}, // Empty images array 210 333 } 211 334 workspace := "/tmp/cloudlab-repos/empty123" 335 + appFilePath := workspace + "/apps/test/app/local.yaml" 336 + mockPushResult := &activities.PushResult{ 337 + Reference: "registry.127.0.0.1.sslip.io/test/app:local", 338 + Digest: "sha256:empty456", 339 + } 212 340 213 341 s.env.OnActivity(activities.Clone, mock.Anything, input.Url, input.Revision).Return(workspace, nil) 214 342 s.env.OnActivity(activities.UpdateAppVersion, mock.Anything, 215 343 workspace+"/apps", input.Namespace, input.App, input.Cluster, input.NewImages).Return(nil) 216 - s.env.OnActivity(activities.GitSync, mock.Anything, 217 - workspace+"/apps/test/app/local.yaml").Return(nil) 344 + s.env.OnActivity(activities.GitAdd, mock.Anything, appFilePath).Return(nil) 345 + s.env.OnActivity(activities.GitCommit, mock.Anything, workspace, "chore(test/app): update local version").Return(nil) 346 + s.env.OnActivity(activities.GitPush, mock.Anything, workspace).Return(nil) 347 + s.env.OnActivity(activities.PushRenderedApp, mock.Anything, 348 + workspace+"/apps", input.Namespace, input.App, input.Cluster, input.Registry).Return(mockPushResult, nil) 218 349 219 350 s.env.ExecuteWorkflow(AppUpdate, input) 220 351 ··· 229 360 Namespace: "test-namespace", 230 361 App: "app-with-dashes", 231 362 Cluster: "staging-env", 363 + Registry: "registry.example.com", 232 364 NewImages: []activities.Image{ 233 365 {Repository: "registry.example.com/test/app-with-dashes", Tag: "v1.2.3-rc1"}, 234 366 }, 235 367 } 236 368 workspace := "/tmp/cloudlab-repos/special456" 369 + appFilePath := workspace + "/apps/test-namespace/app-with-dashes/staging-env.yaml" 370 + mockPushResult := &activities.PushResult{ 371 + Reference: "registry.example.com/test-namespace/app-with-dashes:staging-env", 372 + Digest: "sha256:special123", 373 + } 237 374 238 375 s.env.OnActivity(activities.Clone, mock.Anything, input.Url, input.Revision).Return(workspace, nil) 239 376 s.env.OnActivity(activities.UpdateAppVersion, mock.Anything, 240 377 workspace+"/apps", input.Namespace, input.App, input.Cluster, input.NewImages).Return(nil) 241 - s.env.OnActivity(activities.GitSync, mock.Anything, 242 - workspace+"/apps/test-namespace/app-with-dashes/staging-env.yaml").Return(nil) 378 + s.env.OnActivity(activities.GitAdd, mock.Anything, appFilePath).Return(nil) 379 + s.env.OnActivity(activities.GitCommit, mock.Anything, workspace, "chore(test-namespace/app-with-dashes): update staging-env version").Return(nil) 380 + s.env.OnActivity(activities.GitPush, mock.Anything, workspace).Return(nil) 381 + s.env.OnActivity(activities.PushRenderedApp, mock.Anything, 382 + workspace+"/apps", input.Namespace, input.App, input.Cluster, input.Registry).Return(mockPushResult, nil) 243 383 244 384 s.env.ExecuteWorkflow(AppUpdate, input) 245 385