Skip to content

Commit a706dd2

Browse files
authored
ci: auto GitHub Releases + parallel test scenarios (#11)
## Summary Two CI improvements: ### Auto-create GitHub Release after GHCR publish - New `create-release` job runs after `post-publish` verification - Uses `gh release create --generate-notes` for PR-based changelog - Scopes notes to changes since the previous tag - Uses `env:` block for `github.ref_name` (security best practice) ### Split test-scenarios into 3 parallel groups (~60% faster) The 17 scenarios are split by estimated build cost: - **fast** (6 scenarios): default_options, completions_pipeline, mcp_enabled, negative_validation, security_permissions, completions_disabled - **medium** (6 scenarios): custom_version, custom_install_path, custom_node_version, install_path_with_completions, mount_host_config, node_preinstalled - **slow** (5 scenarios): idempotency, upgrade_version, multi_feature_combo, alpine_specific, fedora_default Each group dynamically filters `scenarios.json` via `jq` at runtime — no file duplication. Expected wall-clock: ~3.5 min (down from ~8-10 min serial). ## Test plan - [x] All 3 scenario groups pass (fast, medium, slow) - [x] All 8 image matrix jobs pass - [x] arm64 tests pass - [x] No scenarios silently dropped (all 17 present across groups)
1 parent 14c56f0 commit a706dd2

2 files changed

Lines changed: 74 additions & 5 deletions

File tree

.github/workflows/release.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,29 @@ jobs:
9999
exit 1
100100
}
101101
echo "Published feature verified successfully."
102+
103+
create-release:
104+
needs: post-publish
105+
runs-on: ubuntu-latest
106+
timeout-minutes: 5
107+
permissions:
108+
contents: write
109+
steps:
110+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
111+
with:
112+
fetch-depth: 0 # needed for tag history
113+
114+
- name: Create GitHub Release
115+
env:
116+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
117+
TAG_NAME: ${{ github.ref_name }}
118+
run: |
119+
PREV_TAG="$(git tag --sort=-v:refname | sed -n '2p')"
120+
GENERATE_ARGS=(--generate-notes)
121+
if [[ -n "${PREV_TAG}" ]]; then
122+
GENERATE_ARGS+=(--notes-start-tag "${PREV_TAG}")
123+
fi
124+
gh release create "${TAG_NAME}" \
125+
--title "${TAG_NAME}" \
126+
"${GENERATE_ARGS[@]}" \
127+
--verify-tag

.github/workflows/test.yml

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,43 @@ jobs:
7272
done < <(find . -name '*.sh' -not -path './.git/*' -print0)
7373
exit "$FAIL"
7474
75-
# Run all per-scenario tests (options-specific assertions)
75+
# Run per-scenario tests in parallel groups for faster CI.
76+
# The 17 scenarios are split into 3 groups by estimated build cost.
77+
# Each group dynamically filters scenarios.json to run only its subset.
7678
test-scenarios:
79+
name: test-scenarios (${{ matrix.group.name }})
7780
needs: lint
7881
runs-on: ubuntu-latest
79-
timeout-minutes: 90 # 17 scenarios building containers takes time
82+
timeout-minutes: 30
8083
permissions:
8184
contents: read
85+
strategy:
86+
fail-fast: false
87+
matrix:
88+
group:
89+
- name: fast
90+
scenarios: >-
91+
default_options
92+
completions_pipeline
93+
mcp_enabled
94+
negative_validation
95+
security_permissions
96+
completions_disabled
97+
- name: medium
98+
scenarios: >-
99+
custom_version
100+
custom_install_path
101+
custom_node_version
102+
install_path_with_completions
103+
mount_host_config
104+
node_preinstalled
105+
- name: slow
106+
scenarios: >-
107+
idempotency
108+
upgrade_version
109+
multi_feature_combo
110+
alpine_specific
111+
fedora_default
82112
steps:
83113
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
84114

@@ -88,11 +118,24 @@ jobs:
88118
- name: Install devcontainer CLI
89119
run: npm install -g @devcontainers/[email protected]
90120

91-
- name: Run all scenarios
121+
- name: Generate group scenarios.json
122+
env:
123+
SCENARIOS: ${{ matrix.group.scenarios }}
124+
GROUP_NAME: ${{ matrix.group.name }}
125+
run: |
126+
FULL="test/claude-code/scenarios.json"
127+
# Build a jq filter that selects only the named scenarios
128+
JQ_FILTER=$(echo "${SCENARIOS}" | tr -s ' ' '\n' | sed 's/.*/"&"/' | paste -sd, | sed 's/^/[/;s/$/]/')
129+
jq --argjson keys "${JQ_FILTER}" 'with_entries(select(.key as $k | $keys | index($k)))' "${FULL}" > /tmp/group-scenarios.json
130+
echo "::group::scenarios.json for group '${GROUP_NAME}'"
131+
cat /tmp/group-scenarios.json
132+
echo "::endgroup::"
133+
cp /tmp/group-scenarios.json "${FULL}"
134+
135+
- name: Run scenarios (${{ matrix.group.name }})
92136
run: |
93137
devcontainer features test --project-folder . 2>&1 | tee /tmp/scenario-test-output.log
94138
# Workaround: devcontainers/[email protected] exits 0 even when feature install fails.
95-
# Grep for known failure strings and fail explicitly. Revisit on CLI upgrade.
96139
if grep -qE "Exit code [1-9][0-9]*|failed to install|Failed to launch|Failed:| FAIL:" /tmp/scenario-test-output.log; then
97140
echo "ERROR: Test output contains failures."
98141
exit 1
@@ -119,7 +162,7 @@ jobs:
119162
if: failure()
120163
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
121164
with:
122-
name: logs-scenarios
165+
name: logs-scenarios-${{ matrix.group.name }}
123166
path: /tmp/scenario-test-output.log
124167
retention-days: 7
125168

0 commit comments

Comments
 (0)