name: CD on: workflow_dispatch: push: branches: [develop, master] concurrency: group: cd-${{ github.ref }} cancel-in-progress: ${{ github.ref != 'refs/heads/master' }} permissions: contents: write pull-requests: write jobs: # ═══════════════════════════════════════════════ # DEVELOP PATH: Pre-release # ═══════════════════════════════════════════════ pre-release: if: >- github.ref == 'refs/heads/develop' || (github.event_name == 'workflow_dispatch' && github.ref != 'refs/heads/master') runs-on: ubuntu-latest outputs: tag: ${{ steps.tag.outputs.tag }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 fetch-tags: true - name: Compute version from commits like release please id: tag run: | LATEST_TAG=$(git tag -l 'v[0-9]*.[0-9]*.[0-9]*' --sort=-version:refname | grep -v '-' | head -1) if [ -z "$LATEST_TAG" ]; then echo "::error::No stable release tag found" exit 1 fi LATEST_VERSION="${LATEST_TAG#v}" echo "Latest release: $LATEST_TAG" # ── Analyse conventional commits since that tag ── COMMITS=$(git log "${LATEST_TAG}..HEAD" --format="%s") HAS_BREAKING=$(echo "$COMMITS" | grep -cE '^[a-z]+(\(.+\))?!:' || true) HAS_FEAT=$(echo "$COMMITS" | grep -cE '^feat(\(.+\))?:' || true) HAS_FIX=$(echo "$COMMITS" | grep -cE '^fix(\(.+\))?:' || true) echo "Commits since ${LATEST_TAG} — breaking=$HAS_BREAKING feat=$HAS_FEAT fix=$HAS_FIX" # ── Compute next version (matches release-please observed behaviour) ── # Pre-1.0 with bump-minor-pre-major: breaking → minor, feat → minor, fix → patch IFS='.' read -r MAJOR MINOR PATCH <<< "$LATEST_VERSION" if [ "$MAJOR" -eq 0 ]; then if [ "$HAS_BREAKING" -gt 0 ] || [ "$HAS_FEAT" -gt 0 ]; then MINOR=$((MINOR + 1)); PATCH=0 # breaking or feat → minor else PATCH=$((PATCH + 1)) # fix only → patch fi else if [ "$HAS_BREAKING" -gt 0 ]; then MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 # breaking → major elif [ "$HAS_FEAT" -gt 0 ]; then MINOR=$((MINOR + 1)); PATCH=0 # feat → minor else PATCH=$((PATCH + 1)) # fix → patch fi fi VERSION="${MAJOR}.${MINOR}.${PATCH}" TAG="dev-${VERSION}-rc.${{ github.run_number }}" echo "Next version: $VERSION (from $LATEST_VERSION)" echo "Pre-release tag: $TAG" # Safety: fail if this exact tag already exists if git ls-remote --tags origin "refs/tags/${TAG}" | grep -q .; then echo "::error::Tag ${TAG} already exists" exit 1 fi echo "tag=$TAG" >> $GITHUB_OUTPUT build-prerelease: name: Build pre-release needs: pre-release if: needs.pre-release.outputs.tag != '' uses: ./.github/workflows/release.yml with: tag: ${{ needs.pre-release.outputs.tag }} prerelease: true permissions: contents: write secrets: inherit # ═══════════════════════════════════════════════ # MASTER PATH: Full release # ═══════════════════════════════════════════════ release-please: if: github.ref == 'refs/heads/master' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') runs-on: ubuntu-latest outputs: release_created: ${{ steps.release.outputs.release_created }} tag_name: ${{ steps.release.outputs.tag_name }} steps: - uses: actions/create-github-app-token@v3 id: app-token with: client-id: ${{ secrets.APP_CLIENT_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} permission-contents: write permission-pull-requests: write - uses: googleapis/release-please-action@v4 id: release with: release-type: rust package-name: rtk token: ${{ steps.app-token.outputs.token }} target-branch: ${{ github.ref_name }} build-release: name: Build and upload release assets needs: release-please if: ${{ needs.release-please.outputs.release_created == 'true' }} uses: ./.github/workflows/release.yml with: tag: ${{ needs.release-please.outputs.tag_name }} permissions: contents: write secrets: inherit update-latest-tag: name: Update 'latest' tag needs: [release-please, build-release] if: ${{ needs.release-please.outputs.release_created == 'true' }} runs-on: ubuntu-latest steps: - uses: actions/create-github-app-token@v3 id: app-token with: client-id: ${{ secrets.APP_CLIENT_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} permission-contents: write - uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ steps.app-token.outputs.token }} - name: Update latest tag run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git tag -fa latest -m "Latest stable release (${{ needs.release-please.outputs.tag_name }})" git push origin latest --force