Merge pull request #10 from Timwood0x10/dev #63
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Push to main: lint → CI (build + test + package) → auto release draft | |
| name: Build & Test | |
| on: | |
| push: | |
| branches: [main] | |
| permissions: | |
| contents: read | |
| actions: write | |
| concurrency: | |
| group: build-${{ github.sha }} | |
| cancel-in-progress: true | |
| jobs: | |
| lint: | |
| uses: ./.github/workflows/_lint.yml | |
| ci: | |
| needs: [lint] | |
| if: ${{ !cancelled() && needs.lint.result == 'success' }} | |
| uses: ./.github/workflows/_ci.yml | |
| with: | |
| skip_perf: true | |
| skip_package: false | |
| # ── Auto create draft release ── | |
| release-draft: | |
| needs: [ci] | |
| if: ${{ !cancelled() && needs.ci.result == 'success' }} | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 15 | |
| permissions: | |
| contents: write | |
| actions: read | |
| steps: | |
| - uses: actions/checkout@v7 # v4.2.2 | |
| with: | |
| fetch-depth: 0 | |
| - uses: actions/download-artifact@v8 # v4.2.1 | |
| with: | |
| merge-multiple: true | |
| path: artifacts | |
| - name: Read version from RELEASE.md | |
| id: version | |
| run: | | |
| VERSION=$(head -1 RELEASE.md | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' || echo "unknown") | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "Detected version: $VERSION" | |
| - name: Validate version | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| # Reject "unknown" or invalid semver | |
| if [ "$VERSION" = "unknown" ] || ! echo "$VERSION" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then | |
| echo "ERROR: Invalid version '$VERSION'. RELEASE.md line 1 must be '# CodeScope vX.Y.Z'" | |
| exit 1 | |
| fi | |
| # Check monotonicity: version must be > latest tag | |
| LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0") | |
| echo "Latest tag: $LATEST_TAG, new version: $VERSION" | |
| # Compare semver (strip leading 'v', compare major.minor.patch) | |
| NEW_VER="${VERSION#v}" | |
| OLD_VER="${LATEST_TAG#v}" | |
| NEW_MAJOR=$(echo "$NEW_VER" | cut -d. -f1) | |
| NEW_MINOR=$(echo "$NEW_VER" | cut -d. -f2) | |
| NEW_PATCH=$(echo "$NEW_VER" | cut -d. -f3) | |
| OLD_MAJOR=$(echo "$OLD_VER" | cut -d. -f1) | |
| OLD_MINOR=$(echo "$OLD_VER" | cut -d. -f2) | |
| OLD_PATCH=$(echo "$OLD_VER" | cut -d. -f3) | |
| IS_NEWER=false | |
| if [ "$NEW_MAJOR" -gt "$OLD_MAJOR" ]; then | |
| IS_NEWER=true | |
| elif [ "$NEW_MAJOR" -eq "$OLD_MAJOR" ] && [ "$NEW_MINOR" -gt "$OLD_MINOR" ]; then | |
| IS_NEWER=true | |
| elif [ "$NEW_MAJOR" -eq "$OLD_MAJOR" ] && [ "$NEW_MINOR" -eq "$OLD_MINOR" ] && [ "$NEW_PATCH" -gt "$OLD_PATCH" ]; then | |
| IS_NEWER=true | |
| fi | |
| if [ "$IS_NEWER" = "false" ]; then | |
| echo "ERROR: Version $VERSION is not greater than latest tag $LATEST_TAG" | |
| echo "Versions must be monotonically increasing." | |
| exit 1 | |
| fi | |
| echo "✓ Version $VERSION is valid and greater than $LATEST_TAG" | |
| - name: Check if release already exists | |
| id: check_release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| if gh release view "$VERSION" >/dev/null 2>&1; then | |
| echo "exists=true" >> "$GITHUB_OUTPUT" | |
| echo "Release $VERSION already exists, skipping" | |
| else | |
| echo "exists=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Write release notes to file | |
| if: steps.check_release.outputs.exists != 'true' | |
| run: | | |
| tail -n +2 RELEASE.md > /tmp/release_body.md | |
| echo "Release body written ($(wc -c < /tmp/release_body.md) bytes)" | |
| - name: Generate combined checksums | |
| if: steps.check_release.outputs.exists != 'true' | |
| run: | | |
| cd artifacts | |
| sha256sum *.tar.gz > ../checksums.txt | |
| - name: Create tag | |
| if: steps.check_release.outputs.exists != 'true' | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| git config user.name "github-actions" | |
| git config user.email "github-actions@github.com" | |
| # Non-destructive: only create the tag if it does not already exist | |
| # (e.g. a previous run created the tag but failed before the release). | |
| if git rev-parse "$VERSION" >/dev/null 2>&1; then | |
| echo "Tag $VERSION already exists locally, skipping creation" | |
| else | |
| git tag "$VERSION" -m "Release $VERSION" | |
| fi | |
| # No --force: a divergent remote tag must surface as a loud failure | |
| # rather than silently overwriting published history. | |
| git push origin "$VERSION" | |
| - name: Create Draft Release | |
| if: steps.check_release.outputs.exists != 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| gh release create "$VERSION" \ | |
| --draft \ | |
| --title "$VERSION" \ | |
| --notes-file /tmp/release_body.md \ | |
| artifacts/*.tar.gz \ | |
| checksums.txt | |
| echo "✅ Release $VERSION created with binaries" |