Skip to content

Commit 4eaa6d3

Browse files
committed
refactor(ci): move GitHub release creation after smoke tests, auto-cleanup on failure
Move GitHub release creation from semantic-release to the publish job, ensuring it only happens after smoke tests pass. If smoke tests fail, automatically clean up the release commit and tag from GitHub. Changes: - Remove @semantic-release/github plugin from .releaserc.yml - Extract version from pyproject.toml in release job and pass to downstream jobs - Add automatic cleanup in smoketest job if tests fail: - Verify HEAD commit is the release commit (sanity check) - Delete the release tag from remote - Revert the release commit with force push - Move GitHub release creation to publish job using --notes-from-tag - Use default GITHUB_TOKEN for creating releases (app token only needed for pushing to protected branch) Benefits: - GitHub releases only created after smoke tests pass - Automatic cleanup if tests fail (no manual intervention needed) - Simpler than preventing the tag/commit upfront - Handles the common case (tests pass) with no changes to workflow time
1 parent dcf1844 commit 4eaa6d3

2 files changed

Lines changed: 62 additions & 10 deletions

File tree

.github/workflows/release.yml

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ jobs:
9090
semantic-release
9191
fi
9292
93+
- name: Get version
94+
if: env.DRY_RUN != 'true'
95+
id: version
96+
run: |
97+
VERSION=$(grep '^version = ' pyproject.toml | cut -d'"' -f2)
98+
echo "version=$VERSION" >> $GITHUB_OUTPUT
99+
93100
- name: Upload wheel
94101
if: env.DRY_RUN != 'true'
95102
uses: actions/upload-artifact@v4
@@ -102,43 +109,90 @@ jobs:
102109
with:
103110
name: sdist
104111
path: dist/*.tar.gz
105-
outputs: # not reused in fact
106-
release_tag: ${{ steps.semantic-release.outputs.next_release_tag }}
112+
outputs:
113+
version: ${{ steps.version.outputs.version }}
107114

108115
smoketest:
109116
runs-on: ubuntu-latest
110117
needs: ['setup', 'release']
111-
if: github.event.inputs.dry_run!='true'
118+
if: github.event.inputs.dry_run!='true'
112119
continue-on-error: ${{ needs.setup.outputs.distribution_name!='appmap' }} # altered names won't work anyway
113120
steps:
121+
- name: Generate token
122+
uses: actions/create-github-app-token@v1
123+
id: app-token
124+
with:
125+
app-id: ${{ secrets.RELEASE_APP_ID }}
126+
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
114127
- uses: actions/checkout@v5
128+
with:
129+
token: ${{ steps.app-token.outputs.token }}
115130
- uses: ./.github/actions/refetch-artifacts
116131
- name: dockerhub login (for seamless docker pulling)
117132
uses: ./.github/actions/dockerhub-login
118133
env:
119134
DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }}
120135
DOCKERHUB_USERNAME: ${{ vars.DOCKERHUB_USERNAME }}
121136
continue-on-error: true
122-
- run: ci/scripts/run_tests.sh
137+
- name: Run smoke tests
138+
id: smoketest
139+
run: ci/scripts/run_tests.sh
140+
continue-on-error: true
123141
env:
124142
SMOKETEST_DOCKER_IMAGE: python:3.12-slim
125143
DISTRIBUTION_NAME: ${{ needs.setup.outputs.distribution_name }}
126144

145+
- name: Cleanup on failure
146+
if: steps.smoketest.outcome == 'failure'
147+
run: |
148+
echo "::error::Smoke tests failed, cleaning up release v${{ needs.release.outputs.version }}"
149+
150+
# Sanity check: verify HEAD commit is the release commit
151+
COMMIT_MSG=$(git log -1 --pretty=%s)
152+
if [[ "$COMMIT_MSG" != "chore(release): ${{ needs.release.outputs.version }}"* ]]; then
153+
echo "::error::HEAD commit message doesn't match expected release commit!"
154+
echo "::error::Expected: chore(release): ${{ needs.release.outputs.version }}"
155+
echo "::error::Got: $COMMIT_MSG"
156+
echo "::error::Aborting cleanup - manual intervention required"
157+
exit 1
158+
fi
159+
160+
# Delete the tag from remote
161+
git push --delete origin "v${{ needs.release.outputs.version }}" || true
162+
163+
# Reset to commit before the release commit and force push
164+
git reset --hard HEAD~1
165+
git push --force origin HEAD:${{ github.ref_name }}
166+
167+
exit 1
168+
127169
# as a workaround to ownership issues (lost access to project)
128170
publish:
129-
name: publish package on PyPI
130-
needs: ['setup', 'release','smoketest']
171+
name: publish package on PyPI and create GitHub release
172+
needs: ['setup', 'release', 'smoketest']
131173
if: (( github.event.inputs.dry_run != 'true' ) && ( (needs.setup.outputs.publish_env == 'pypi') || (needs.setup.outputs.publish_env == 'testpypi') ) )
132174
runs-on: ubuntu-latest
133175
environment:
134176
name: ${{ needs.setup.outputs.publish_env }}
135177
url: ${{ needs.setup.outputs.publish_to }}
136178
permissions:
137179
id-token: write
180+
contents: write # needed for creating GitHub releases
138181
steps:
139182
- uses: actions/checkout@v5
183+
with:
184+
ref: v${{ needs.release.outputs.version }} # checkout the tag
185+
140186
- uses: ./.github/actions/refetch-artifacts
141187

188+
- name: Create GitHub release
189+
run: |
190+
gh release create "v${{ needs.release.outputs.version }}" \
191+
dist/*.whl dist/*.tar.gz \
192+
--notes-from-tag
193+
env:
194+
GH_TOKEN: ${{ github.token }}
195+
142196
- name: Publish to PyPI
143197
if: needs.setup.outputs.publish_env=='pypi'
144198
uses: pypa/gh-action-pypi-publish@release/v1

.releaserc.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,5 @@ plugins:
4343
- - '@semantic-release/exec'
4444
- prepareCmd: |
4545
/bin/bash ./ci/scripts/build_with_poetry.sh
46-
- - '@semantic-release/github':
47-
- assets:
48-
- dist/*.whl
49-
- dist/*.tar.gz
46+
# NOTE: @semantic-release/github plugin removed - GitHub release creation
47+
# now happens in the publish job after smoke tests pass

0 commit comments

Comments
 (0)