Merge pull request #272 from ducflair/dev #174
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
| name: Release | |
| on: | |
| workflow_dispatch: | |
| push: | |
| branches: [main, next] | |
| paths: | |
| - 'packages/**' | |
| - 'package.json' | |
| - 'Cargo.toml' | |
| concurrency: | |
| group: release-${{ github.ref }} | |
| cancel-in-progress: false | |
| permissions: | |
| contents: write | |
| issues: write | |
| pull-requests: write | |
| id-token: write | |
| jobs: | |
| # ────────────────────────────────────────────────────────────────────── | |
| # Phase 1: Lightweight release detection (~30s) | |
| # Runs semantic-release --dry-run for each package to determine which | |
| # ones would get a new version. Skips expensive build/test/release | |
| # pipelines for packages with no pending release. | |
| # ────────────────────────────────────────────────────────────────────── | |
| release-gate: | |
| name: Detect Pending Releases | |
| runs-on: ubuntu-latest | |
| outputs: | |
| ducrs: ${{ steps.check.outputs.ducrs }} | |
| ducjs: ${{ steps.check.outputs.ducjs }} | |
| ducpy: ${{ steps.check.outputs.ducpy }} | |
| ducpdf: ${{ steps.check.outputs.ducpdf }} | |
| ducsvg: ${{ steps.check.outputs.ducsvg }} | |
| duc2pdf: ${{ steps.check.outputs.duc2pdf }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ github.ref_name }} | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: "latest" | |
| - name: Install deps | |
| run: bun install | |
| - name: Check all packages for pending releases | |
| id: check | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| check_release() { | |
| local name="$1" | |
| local dir="$2" | |
| pushd "$dir" > /dev/null | |
| local output | |
| output=$(npx semantic-release --dry-run 2>&1) || true | |
| popd > /dev/null | |
| if echo "$output" | grep -q "The next release version is"; then | |
| echo "${name}=true" >> "$GITHUB_OUTPUT" | |
| echo "📦 ${name}: release pending" | |
| else | |
| echo "${name}=false" >> "$GITHUB_OUTPUT" | |
| echo "⏭️ ${name}: no release" | |
| fi | |
| } | |
| check_release "ducrs" "./packages/ducrs" | |
| check_release "ducjs" "./packages/ducjs" | |
| check_release "ducpy" "./packages/ducpy" | |
| check_release "ducpdf" "./packages/ducpdf" | |
| check_release "ducsvg" "./packages/ducsvg" | |
| check_release "duc2pdf" "./packages/ducpdf/src/duc2pdf" | |
| # ────────────────────────────────────────────────────────────────────── | |
| # Phase 2: Release in dependency order (mirrors turbo.json build graph) | |
| # ────────────────────────────────────────────────────────────────────── | |
| release-ducrs: | |
| needs: release-gate | |
| if: needs.release-gate.outputs.ducrs == 'true' | |
| uses: ./.github/workflows/release-ducrs.yml | |
| secrets: inherit | |
| release-ducjs: | |
| needs: [release-gate, release-ducrs] | |
| if: always() && needs.release-gate.outputs.ducjs == 'true' && needs.release-gate.result == 'success' && (needs.release-ducrs.result == 'success' || needs.release-ducrs.result == 'skipped') | |
| uses: ./.github/workflows/release-ducjs.yml | |
| secrets: inherit | |
| release-ducpy: | |
| needs: [release-gate, release-ducrs] | |
| if: always() && needs.release-gate.outputs.ducpy == 'true' && needs.release-gate.result == 'success' && (needs.release-ducrs.result == 'success' || needs.release-ducrs.result == 'skipped') | |
| uses: ./.github/workflows/release-ducpy.yml | |
| secrets: inherit | |
| release-duc2pdf: | |
| needs: [release-gate, release-ducrs] | |
| if: always() && needs.release-gate.outputs.duc2pdf == 'true' && needs.release-gate.result == 'success' && (needs.release-ducrs.result == 'success' || needs.release-ducrs.result == 'skipped') | |
| uses: ./.github/workflows/release-duc2pdf.yml | |
| secrets: inherit | |
| release-ducpdf: | |
| needs: [release-gate, release-ducjs, release-duc2pdf] | |
| if: always() && needs.release-gate.outputs.ducpdf == 'true' && needs.release-gate.result == 'success' && (needs.release-ducjs.result == 'success' || needs.release-ducjs.result == 'skipped') && (needs.release-duc2pdf.result == 'success' || needs.release-duc2pdf.result == 'skipped') | |
| uses: ./.github/workflows/release-ducpdf.yml | |
| secrets: inherit | |
| release-ducsvg: | |
| needs: [release-gate, release-ducpdf] | |
| if: always() && needs.release-gate.outputs.ducsvg == 'true' && needs.release-gate.result == 'success' && (needs.release-ducpdf.result == 'success' || needs.release-ducpdf.result == 'skipped') | |
| uses: ./.github/workflows/release-ducsvg.yml | |
| secrets: inherit | |
| # ────────────────────────────────────────────────────────────────────── | |
| # Phase 3: Summary | |
| # Single view of all package release outcomes. | |
| # ────────────────────────────────────────────────────────────────────── | |
| summary: | |
| name: Release Summary | |
| needs: [release-gate, release-ducrs, release-ducjs, release-ducpy, release-duc2pdf, release-ducpdf, release-ducsvg] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Generate summary | |
| run: | | |
| status() { | |
| local pending="$1" | |
| local result="$2" | |
| if [ "$pending" != "true" ]; then | |
| echo "⏭️ Skipped" | |
| elif [ "$result" = "success" ]; then | |
| echo "✅ Released" | |
| elif [ "$result" = "failure" ]; then | |
| echo "❌ Failed" | |
| else | |
| echo "⏭️ Skipped" | |
| fi | |
| } | |
| { | |
| echo "## Release Summary" | |
| echo "" | |
| echo "| Package | Pending | Result |" | |
| echo "|---------|---------|--------|" | |
| echo "| ducrs | ${{ needs.release-gate.outputs.ducrs }} | $(status '${{ needs.release-gate.outputs.ducrs }}' '${{ needs.release-ducrs.result }}') |" | |
| echo "| ducjs | ${{ needs.release-gate.outputs.ducjs }} | $(status '${{ needs.release-gate.outputs.ducjs }}' '${{ needs.release-ducjs.result }}') |" | |
| echo "| ducpy | ${{ needs.release-gate.outputs.ducpy }} | $(status '${{ needs.release-gate.outputs.ducpy }}' '${{ needs.release-ducpy.result }}') |" | |
| echo "| duc2pdf | ${{ needs.release-gate.outputs.duc2pdf }} | $(status '${{ needs.release-gate.outputs.duc2pdf }}' '${{ needs.release-duc2pdf.result }}') |" | |
| echo "| ducpdf | ${{ needs.release-gate.outputs.ducpdf }} | $(status '${{ needs.release-gate.outputs.ducpdf }}' '${{ needs.release-ducpdf.result }}') |" | |
| echo "| ducsvg | ${{ needs.release-gate.outputs.ducsvg }} | $(status '${{ needs.release-gate.outputs.ducsvg }}' '${{ needs.release-ducsvg.result }}') |" | |
| echo "" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| # Fail if the gate itself failed | |
| if [ "${{ needs.release-gate.result }}" != "success" ]; then | |
| echo "### ❌ Release detection failed" >> "$GITHUB_STEP_SUMMARY" | |
| exit 1 | |
| fi | |
| # Fail if any triggered release job failed | |
| RESULTS="${{ needs.release-ducrs.result }} ${{ needs.release-ducjs.result }} ${{ needs.release-ducpy.result }} ${{ needs.release-duc2pdf.result }} ${{ needs.release-ducpdf.result }} ${{ needs.release-ducsvg.result }}" | |
| if echo "$RESULTS" | grep -q "failure"; then | |
| echo "### ❌ Some releases failed" >> "$GITHUB_STEP_SUMMARY" | |
| exit 1 | |
| fi | |
| echo "### ✅ All releases completed successfully" >> "$GITHUB_STEP_SUMMARY" |