Skip to content

Commit fb58ddc

Browse files
committed
feat(workflow): add declarative workflow execution system
Implement comprehensive workflow engine with step outputs, variable interpolation, and resumable execution. Similar to ASC-CLI PR #966. Features: - JSON workflow definitions with multi-step pipelines - Step output capture from JSON stdout - Variable interpolation: ${steps.name.field} and ${env.VAR} - Persistent run state with --resume flag - Dry-run mode for validation - Progress indicators [1/5] for step execution - Step retry logic with linear/exponential backoff - Parallel step execution (maxParallel: 4) - Watch mode with text/json/tui output formats - workflow init command with templates (simple, release, cicd, custom) - workflow logs command to view step outputs - workflow validate command with comprehensive checks CLI Commands: - gpd workflow run --file workflow.json [--resume <id>] - gpd workflow list [--all] - gpd workflow show <workflow> - gpd workflow status <run-id> - gpd workflow validate <file> - gpd workflow init <name> [--template] - gpd workflow logs <run-id> [step] New Files: - internal/workflow/*.go: Core engine (workflow, state, interpolate, parser, runner, watcher) - internal/cli/kong_workflow.go: CLI commands - docs/WORKFLOWS.md: Complete documentation (500+ lines) - docs/examples/workflows/: 13 example workflows including: * cicd-pipeline, production-release, staged-rollout-monitoring * multi-track-release, rollback-workflow, release-with-testing Tests: - 29 integration tests covering end-to-end execution, resume, variable interpolation, state persistence, error scenarios - 76% code coverage Closes: declarative workflow feature request
1 parent 9bddde9 commit fb58ddc

25 files changed

Lines changed: 8712 additions & 0 deletions

docs/WORKFLOWS.md

Lines changed: 910 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"name": "cicd-pipeline",
3+
"description": "Complete CI/CD pipeline from build to production",
4+
"env": {
5+
"PACKAGE": "com.example.app"
6+
},
7+
"steps": [
8+
{
9+
"name": "validate_config",
10+
"command": "gpd automation validate --package ${env.PACKAGE} --checks all --strict"
11+
},
12+
{
13+
"name": "build",
14+
"type": "shell",
15+
"command": "./gradlew bundleRelease",
16+
"dependsOn": ["validate_config"]
17+
},
18+
{
19+
"name": "upload",
20+
"command": "gpd publish upload app/build/outputs/bundle/release/app-release.aab --package ${env.PACKAGE} --output json",
21+
"dependsOn": ["build"],
22+
"captureOutputs": ["versionCode"]
23+
},
24+
{
25+
"name": "release_internal",
26+
"command": "gpd publish release --package ${env.PACKAGE} --track internal --version-code ${steps.upload.versionCode} --status completed",
27+
"dependsOn": ["upload"]
28+
},
29+
{
30+
"name": "promote_beta",
31+
"command": "gpd automation promote --package ${env.PACKAGE} --from-track internal --to-track beta --verify --verify-timeout 15m",
32+
"dependsOn": ["release_internal"]
33+
},
34+
{
35+
"name": "staged_rollout",
36+
"command": "gpd automation rollout --package ${env.PACKAGE} --track production --start-percentage 5 --target-percentage 100 --step-size 25 --step-interval 4h --health-threshold 0.01 --auto-rollback",
37+
"dependsOn": ["promote_beta"],
38+
"timeout": "48h"
39+
},
40+
{
41+
"name": "monitor",
42+
"command": "gpd automation monitor --package ${env.PACKAGE} --track production --duration 30m --health-threshold 0.01",
43+
"dependsOn": ["staged_rollout"]
44+
}
45+
]
46+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "conditional-beta-release",
3+
"description": "Release to beta only if IS_BETA environment variable is set",
4+
"env": {
5+
"PACKAGE": "com.example.app",
6+
"IS_BETA": "false"
7+
},
8+
"steps": [
9+
{
10+
"name": "upload",
11+
"command": "gpd publish upload app.aab --package ${env.PACKAGE} --output json",
12+
"captureOutputs": ["versionCode"]
13+
},
14+
{
15+
"name": "release_internal",
16+
"command": "gpd publish release --package ${env.PACKAGE} --track internal --version-code ${steps.upload.versionCode}",
17+
"dependsOn": ["upload"]
18+
},
19+
{
20+
"name": "release_beta",
21+
"command": "gpd publish release --package ${env.PACKAGE} --track beta --version-code ${steps.upload.versionCode}",
22+
"dependsOn": ["release_internal"],
23+
"condition": "${env.IS_BETA}"
24+
},
25+
{
26+
"name": "release_production",
27+
"command": "gpd publish release --package ${env.PACKAGE} --track production --version-code ${steps.upload.versionCode}",
28+
"dependsOn": ["release_internal"],
29+
"condition": "${env.IS_BETA} == false"
30+
}
31+
]
32+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"name": "multi-app-release",
3+
"description": "Release multiple apps in parallel",
4+
"env": {
5+
"APP1_PACKAGE": "com.example.app1",
6+
"APP2_PACKAGE": "com.example.app2"
7+
},
8+
"steps": [
9+
{
10+
"name": "upload_app1",
11+
"command": "gpd publish upload app1.aab --package ${env.APP1_PACKAGE} --output json",
12+
"captureOutputs": ["versionCode"]
13+
},
14+
{
15+
"name": "upload_app2",
16+
"command": "gpd publish upload app2.aab --package ${env.APP2_PACKAGE} --output json",
17+
"captureOutputs": ["versionCode"]
18+
},
19+
{
20+
"name": "release_app1",
21+
"command": "gpd publish release --package ${env.APP1_PACKAGE} --track internal --version-code ${steps.upload_app1.versionCode}",
22+
"dependsOn": ["upload_app1"]
23+
},
24+
{
25+
"name": "release_app2",
26+
"command": "gpd publish release --package ${env.APP2_PACKAGE} --track internal --version-code ${steps.upload_app2.versionCode}",
27+
"dependsOn": ["upload_app2"]
28+
},
29+
{
30+
"name": "validate_app1",
31+
"command": "gpd testing validate --package ${env.APP1_PACKAGE} --comprehensive",
32+
"dependsOn": ["release_app1"]
33+
},
34+
{
35+
"name": "validate_app2",
36+
"command": "gpd testing validate --package ${env.APP2_PACKAGE} --comprehensive",
37+
"dependsOn": ["release_app2"]
38+
},
39+
{
40+
"name": "promote_app1",
41+
"command": "gpd publish promote --package ${env.APP1_PACKAGE} --from-track internal --to-track production",
42+
"dependsOn": ["validate_app1", "validate_app2"]
43+
},
44+
{
45+
"name": "promote_app2",
46+
"command": "gpd publish promote --package ${env.APP2_PACKAGE} --from-track internal --to-track production",
47+
"dependsOn": ["validate_app1", "validate_app2"]
48+
}
49+
]
50+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
{
2+
"name": "multi-track-release",
3+
"description": "Release to multiple tracks simultaneously (internal, alpha, beta) with different release notes per track. Uses upload once, then parallel releases.",
4+
"env": {
5+
"PACKAGE": "${PACKAGE:-com.example.app}",
6+
"AAB_PATH": "${AAB_PATH:-app/build/outputs/bundle/release/app-release.aab}",
7+
"RELEASE_NAME": "${RELEASE_NAME:-Release}",
8+
"INTERNAL_NOTES": "${INTERNAL_NOTES:-Internal testing build}",
9+
"ALPHA_NOTES": "${ALPHA_NOTES:-Alpha release for early adopters}",
10+
"BETA_NOTES": "${BETA_NOTES:-Beta release for broader testing}",
11+
"BUILD_VERSION": "${BUILD_VERSION:-1.0.0}",
12+
"AUTO_PROMOTE_INTERNAL": "${AUTO_PROMOTE_INTERNAL:-true}",
13+
"AUTO_PROMOTE_ALPHA": "${AUTO_PROMOTE_ALPHA:-true}"
14+
},
15+
"steps": [
16+
{
17+
"name": "validate_app",
18+
"description": "Validate app configuration before upload",
19+
"command": "gpd automation validate --package ${env.PACKAGE} --checks aab,signing,permissions --strict",
20+
"timeout": "10m"
21+
},
22+
{
23+
"name": "upload_aab",
24+
"description": "Upload AAB once to be used across all tracks",
25+
"command": "gpd publish upload ${env.AAB_PATH} --package ${env.PACKAGE} --output json",
26+
"dependsOn": ["validate_app"],
27+
"captureOutputs": ["versionCode", "editId", "sha256", "type"]
28+
},
29+
{
30+
"name": "prepare_release_notes_internal",
31+
"description": "Prepare release notes for internal track",
32+
"type": "shell",
33+
"command": "echo '${env.INTERNAL_NOTES}' > internal-release-notes.txt",
34+
"dependsOn": ["upload_aab"]
35+
},
36+
{
37+
"name": "prepare_release_notes_alpha",
38+
"description": "Prepare release notes for alpha track",
39+
"type": "shell",
40+
"command": "echo '${env.ALPHA_NOTES}' > alpha-release-notes.txt",
41+
"dependsOn": ["upload_aab"]
42+
},
43+
{
44+
"name": "prepare_release_notes_beta",
45+
"description": "Prepare release notes for beta track",
46+
"type": "shell",
47+
"command": "echo '${env.BETA_NOTES}' > beta-release-notes.txt",
48+
"dependsOn": ["upload_aab"]
49+
},
50+
{
51+
"name": "release_internal",
52+
"description": "Release to internal track with internal notes (COMPLETED status)",
53+
"command": "gpd publish release --package ${env.PACKAGE} --track internal --version-code ${steps.upload_aab.versionCode} --status completed --release-name '${env.RELEASE_NAME} Internal (${env.BUILD_VERSION})' --release-notes internal-release-notes.txt",
54+
"dependsOn": ["prepare_release_notes_internal"],
55+
"captureOutputs": ["status", "track"]
56+
},
57+
{
58+
"name": "release_alpha",
59+
"description": "Release to alpha track with alpha notes (COMPLETED status)",
60+
"command": "gpd publish release --package ${env.PACKAGE} --track alpha --version-code ${steps.upload_aab.versionCode} --status completed --release-name '${env.RELEASE_NAME} Alpha (${env.BUILD_VERSION})' --release-notes alpha-release-notes.txt",
61+
"dependsOn": ["prepare_release_notes_alpha"],
62+
"captureOutputs": ["status", "track"]
63+
},
64+
{
65+
"name": "release_beta",
66+
"description": "Release to beta track with beta notes (IN_PROGRESS status for staged rollout)",
67+
"command": "gpd publish release --package ${env.PACKAGE} --track beta --version-code ${steps.upload_aab.versionCode} --status inProgress --user-fraction 0.50 --release-name '${env.RELEASE_NAME} Beta (${env.BUILD_VERSION})' --release-notes beta-release-notes.txt",
68+
"dependsOn": ["prepare_release_notes_beta"],
69+
"captureOutputs": ["status", "track", "userFraction"]
70+
},
71+
{
72+
"name": "wait_for_tracks",
73+
"description": "Wait for all track releases to stabilize",
74+
"type": "shell",
75+
"command": "echo 'All tracks released. Internal: completed, Alpha: completed, Beta: 50% rollout' && sleep 30",
76+
"dependsOn": ["release_internal", "release_alpha", "release_beta"]
77+
},
78+
{
79+
"name": "verify_internal_track",
80+
"description": "Verify internal track release",
81+
"command": "gpd publish status --package ${env.PACKAGE} --track internal",
82+
"dependsOn": ["wait_for_tracks"],
83+
"captureOutputs": ["releases", "track"]
84+
},
85+
{
86+
"name": "verify_alpha_track",
87+
"description": "Verify alpha track release",
88+
"command": "gpd publish status --package ${env.PACKAGE} --track alpha",
89+
"dependsOn": ["wait_for_tracks"],
90+
"captureOutputs": ["releases", "track"]
91+
},
92+
{
93+
"name": "verify_beta_track",
94+
"description": "Verify beta track release",
95+
"command": "gpd publish status --package ${env.PACKAGE} --track beta",
96+
"dependsOn": ["wait_for_tracks"],
97+
"captureOutputs": ["releases", "track"]
98+
},
99+
{
100+
"name": "check_track_health",
101+
"description": "Quick health check across all tracks",
102+
"type": "shell",
103+
"command": "echo 'Checking health across all tracks...' && echo 'Internal Track: Released to all internal testers' && echo 'Alpha Track: Released to all alpha testers' && echo 'Beta Track: Rolling out to 50% of beta testers'",
104+
"dependsOn": ["verify_internal_track", "verify_alpha_track", "verify_beta_track"]
105+
},
106+
{
107+
"name": "notify_slack_internal",
108+
"description": "Notify about internal track release",
109+
"type": "shell",
110+
"command": "echo 'Internal track released: Version ${steps.upload_aab.versionCode}'",
111+
"dependsOn": ["release_internal"]
112+
},
113+
{
114+
"name": "notify_slack_alpha",
115+
"description": "Notify about alpha track release",
116+
"type": "shell",
117+
"command": "echo 'Alpha track released: Version ${steps.upload_aab.versionCode}'",
118+
"dependsOn": ["release_alpha"]
119+
},
120+
{
121+
"name": "notify_slack_beta",
122+
"description": "Notify about beta track release",
123+
"type": "shell",
124+
"command": "echo 'Beta track released (50% rollout): Version ${steps.upload_aab.versionCode}'",
125+
"dependsOn": ["release_beta"]
126+
},
127+
{
128+
"name": "final_summary",
129+
"description": "Generate final release summary",
130+
"type": "shell",
131+
"command": "echo '========================================' && echo 'Multi-Track Release Complete' && echo '========================================' && echo 'Package: ${env.PACKAGE}' && echo 'Version Code: ${steps.upload_aab.versionCode}' && echo 'SHA256: ${steps.upload_aab.sha256}' && echo '' && echo 'Track Status:' && echo ' - Internal: Released (all testers)' && echo ' - Alpha: Released (all testers)' && echo ' - Beta: Rolling out (50% of testers)' && echo '========================================'",
132+
"dependsOn": ["check_track_health", "notify_slack_internal", "notify_slack_alpha", "notify_slack_beta"]
133+
}
134+
]
135+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
{
2+
"name": "parallel-release-pipeline",
3+
"description": "Example workflow demonstrating parallel step execution",
4+
"maxParallel": 4,
5+
"env": {
6+
"PACKAGE": "com.example.app"
7+
},
8+
"steps": [
9+
{
10+
"name": "lint",
11+
"type": "shell",
12+
"command": "./gradlew lint",
13+
"parallel": true,
14+
"continueOnError": false
15+
},
16+
{
17+
"name": "unit_tests",
18+
"type": "shell",
19+
"command": "./gradlew test",
20+
"parallel": true,
21+
"continueOnError": false
22+
},
23+
{
24+
"name": "integration_tests",
25+
"type": "shell",
26+
"command": "./gradlew connectedAndroidTest",
27+
"parallel": true,
28+
"timeout": "10m"
29+
},
30+
{
31+
"name": "build",
32+
"type": "shell",
33+
"command": "./gradlew bundleRelease",
34+
"dependsOn": ["lint", "unit_tests", "integration_tests"],
35+
"captureOutputs": ["versionCode", "versionName"]
36+
},
37+
{
38+
"name": "upload_internal",
39+
"command": "gpd publish upload app/build/outputs/bundle/release/app-release.aab --package ${env.PACKAGE} --track internal --output json",
40+
"dependsOn": ["build"],
41+
"parallel": true,
42+
"captureOutputs": ["versionCode", "editId"]
43+
},
44+
{
45+
"name": "upload_alpha",
46+
"command": "gpd publish upload app/build/outputs/bundle/release/app-release.aab --package ${env.PACKAGE} --track alpha",
47+
"dependsOn": ["build"],
48+
"parallel": true
49+
},
50+
{
51+
"name": "upload_beta",
52+
"command": "gpd publish upload app/build/outputs/bundle/release/app-release.aab --package ${env.PACKAGE} --track beta",
53+
"dependsOn": ["build"],
54+
"parallel": true,
55+
"condition": "${env.UPLOAD_TO_BETA}"
56+
},
57+
{
58+
"name": "promote_to_production",
59+
"command": "gpd automation promote --package ${env.PACKAGE} --from-track internal --to-track production --verify",
60+
"dependsOn": ["upload_internal", "upload_alpha"]
61+
}
62+
]
63+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"name": "production-release",
3+
"description": "Complete production release with validation and staged rollout",
4+
"env": {
5+
"PACKAGE": "com.example.app"
6+
},
7+
"steps": [
8+
{
9+
"name": "validate",
10+
"command": "gpd automation validate --package ${env.PACKAGE} --checks all --strict",
11+
"retryCount": 2,
12+
"retryDelay": "5s",
13+
"retryBackoff": "linear"
14+
},
15+
{
16+
"name": "upload",
17+
"command": "gpd publish upload app.aab --package ${env.PACKAGE} --output json",
18+
"dependsOn": ["validate"],
19+
"captureOutputs": ["versionCode"],
20+
"retryCount": 3,
21+
"retryDelay": "10s",
22+
"retryBackoff": "exponential"
23+
},
24+
{
25+
"name": "internal",
26+
"command": "gpd publish release --package ${env.PACKAGE} --track internal --version-code ${steps.upload.versionCode} --status completed",
27+
"dependsOn": ["upload"],
28+
"retryCount": 2,
29+
"retryDelay": "5s"
30+
},
31+
{
32+
"name": "promote_beta",
33+
"command": "gpd automation promote --package ${env.PACKAGE} --from-track internal --to-track beta --verify",
34+
"dependsOn": ["internal"]
35+
},
36+
{
37+
"name": "rollout",
38+
"command": "gpd automation rollout --package ${env.PACKAGE} --track production --start-percentage 1 --target-percentage 100 --step-size 10 --step-interval 2h --auto-rollback",
39+
"dependsOn": ["promote_beta"],
40+
"timeout": "24h"
41+
}
42+
]
43+
}

0 commit comments

Comments
 (0)