From e5a4d8f556d7e9bade48d4e96d2e19c4c8ef185e Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Wed, 18 Mar 2026 23:08:30 -0700 Subject: [PATCH 1/6] Restore the CI "View deployment" button in PRs after building --- .github/workflows/build.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 58467c78ec..8f1863bb9d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -126,6 +126,33 @@ jobs: echo "All $MAX_ATTEMPTS Cloudflare Pages publish attempts failed." exit 1 + - name: ๐Ÿš€ Create GitHub Deployment for "View deployment" button + if: inputs.checkout_repo == '' || inputs.checkout_repo == github.repository + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CF_URL: ${{ steps.cloudflare.outputs.url }} + run: | + if [ -z "$CF_URL" ]; then + echo "No Cloudflare URL available, skipping deployment." + exit 0 + fi + DEPLOY_ID=$(gh api \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + repos/${{ github.repository }}/deployments \ + -f ref="$(git rev-parse HEAD)" \ + -f environment="graphite-dev (Preview)" \ + -F auto_merge=false \ + -f required_contexts="[]" \ + --jq '.id') + gh api \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + repos/${{ github.repository }}/deployments/$DEPLOY_ID/statuses \ + -f state=success \ + -f environment_url="$CF_URL" \ + -f log_url="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + - name: ๐Ÿ’ฌ Comment with the build link env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From e3d25b6aefde826e2e9e371bf9e922035edc8cd3 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Thu, 19 Mar 2026 00:33:04 -0700 Subject: [PATCH 2/6] Consolidate release.yml functionality into build.yml --- .github/workflows/build.yml | 41 +++++++++++++------- .github/workflows/release.yml | 72 ----------------------------------- 2 files changed, 28 insertions(+), 85 deletions(-) delete mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8f1863bb9d..1e9cee5744 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,6 +4,8 @@ on: push: branches: - master + tags: + - latest-stable workflow_dispatch: inputs: web: @@ -59,9 +61,19 @@ jobs: RUSTC_WRAPPER: /usr/bin/sccache CARGO_INCREMENTAL: 0 SCCACHE_DIR: /var/lib/github-actions/.cache - INDEX_HTML_HEAD_REPLACEMENT: - steps: + - name: ๐Ÿ”€ Set deploy target + id: deploy-target + run: | + if [[ "${{ github.ref }}" == "refs/tags/latest-stable" ]]; then + echo "cf_project=graphite-editor" >> $GITHUB_OUTPUT + DOMAIN="editor.graphite.art" + else + echo "cf_project=graphite-dev" >> $GITHUB_OUTPUT + DOMAIN="dev.graphite.art" + fi + echo "head_replacement=" >> $GITHUB_OUTPUT + - name: ๐Ÿ“ฅ Clone repository uses: actions/checkout@v6 with: @@ -92,7 +104,7 @@ jobs: - name: โœ‚ Replace template in of index.html if: github.event_name == 'push' - run: sed -i "s||$INDEX_HTML_HEAD_REPLACEMENT|" frontend/index.html + run: sed -i "s||${{ steps.deploy-target.outputs.head_replacement }}|" frontend/index.html - name: ๐ŸŒ Build Graphite web code env: @@ -109,7 +121,7 @@ jobs: DELAY=30 for ATTEMPT in $(seq 1 $MAX_ATTEMPTS); do echo "Attempt $ATTEMPT of $MAX_ATTEMPTS..." - if OUTPUT=$(npx wrangler@3 pages deploy "frontend/dist" --project-name="graphite-dev" --commit-dirty=true 2>&1); then + if OUTPUT=$(npx wrangler@3 pages deploy "frontend/dist" --project-name="${{ steps.deploy-target.outputs.cf_project }}" --commit-dirty=true 2>&1); then URL=$(echo "$OUTPUT" | grep -oP 'https://[^\s]+\.pages\.dev' | head -1) echo "url=$URL" >> "$GITHUB_OUTPUT" echo "Published successfully: $URL" @@ -167,15 +179,18 @@ jobs: |-| | $CF_URL |" - if [ "${{ github.event_name }}" = "push" ]; then - # Comment on the commit hash page + if [ "${{ github.ref }}" = "refs/tags/latest-stable" ]; then + # Push tag: skip commenting (commit was already commented on master merge) + echo "Tag push, skipping comment." + elif [ "${{ github.event_name }}" = "push" ]; then + # Push master: comment on the commit hash page gh api \ -X POST \ -H "Accept: application/vnd.github+json" \ repos/${{ github.repository }}/commits/$(git rev-parse HEAD)/comments \ -f body="$COMMENT_BODY" else - # Comment on the PR (use provided PR number from !build, or look it up by branch name) + # Non-push (workflow_dispatch, !build): comment on the PR PR_NUMBER="${{ inputs.pr_number }}" if [ -z "$PR_NUMBER" ]; then BRANCH=$(git rev-parse --abbrev-ref HEAD) @@ -189,9 +204,9 @@ jobs: fi fi - - name: โœ‚ Strip analytics script from built output for clean artifact + - name: โœ‚ Strip injected script from built output for clean artifact if: github.event_name == 'push' - run: sed -i "s|$INDEX_HTML_HEAD_REPLACEMENT||" frontend/dist/index.html + run: sed -i "s|${{ steps.deploy-target.outputs.head_replacement }}||" frontend/dist/index.html - name: ๐Ÿ“ฆ Upload web bundle artifact uses: actions/upload-artifact@v6 @@ -200,7 +215,7 @@ jobs: path: frontend/dist - name: ๐Ÿ“ƒ Trigger website rebuild if auto-generated code docs are stale - if: github.event_name == 'push' + if: github.event_name == 'push' && github.ref != 'refs/tags/latest-stable' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | @@ -211,7 +226,7 @@ jobs: || gh workflow run website.yml --ref master windows: - if: github.event_name == 'push' || inputs.windows + if: (github.event_name == 'push' && github.ref != 'refs/tags/latest-stable') || inputs.windows runs-on: windows-latest permissions: contents: read @@ -403,7 +418,7 @@ jobs: path: target/artifacts mac: - if: github.event_name == 'push' || inputs.mac + if: (github.event_name == 'push' && github.ref != 'refs/tags/latest-stable') || inputs.mac runs-on: macos-latest permissions: contents: read @@ -581,7 +596,7 @@ jobs: path: target/artifacts linux: - if: github.event_name == 'push' || inputs.linux + if: (github.event_name == 'push' && github.ref != 'refs/tags/latest-stable') || inputs.linux runs-on: ubuntu-latest permissions: contents: read diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index b04ad5f156..0000000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: "Release" - -on: - push: - tags: - - "latest-stable" - -env: - CARGO_TERM_COLOR: always - -jobs: - build: - runs-on: self-hosted - permissions: - contents: write - deployments: write - env: - RUSTC_WRAPPER: /usr/bin/sccache - CARGO_INCREMENTAL: 0 - SCCACHE_DIR: /var/lib/github-actions/.cache - INDEX_HTML_HEAD_REPLACEMENT: - - steps: - - name: ๐Ÿ“ฅ Clone repository - uses: actions/checkout@v6 - - - name: ๐Ÿ—‘ Clear wasm-bindgen cache - run: rm -r ~/.cache/.wasm-pack - - - name: ๐ŸŸข Install Node.js - uses: actions/setup-node@v6 - with: - node-version-file: .nvmrc - - - name: ๐Ÿšง Install build dependencies - run: | - cd frontend - npm run setup - - - name: ๐Ÿฆ€ Install Rust - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - toolchain: stable - override: true - cache: false - rustflags: "" - target: wasm32-unknown-unknown - - - name: โœ‚ Replace template in of index.html - run: sed -i "s||$INDEX_HTML_HEAD_REPLACEMENT|" frontend/index.html - - - name: ๐ŸŒ Build Graphite web code - env: - NODE_ENV: production - run: mold -run cargo run build web - - - name: ๐Ÿ“ค Publish to Cloudflare Pages - env: - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - run: npx wrangler@3 pages deploy "frontend/dist" --project-name="graphite-editor" --branch="master" --commit-dirty=true - - - name: ๐Ÿ“ฆ Upload assets to GitHub release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - DATE=$(git log -1 --format=%cd --date=format:%Y-%m-%d) - cd frontend - sed -i "s|$INDEX_HTML_HEAD_REPLACEMENT||" dist/index.html - mv dist "graphite-$DATE" - zip -r "graphite-self-hosted-build.zip" "graphite-$DATE" - gh release upload latest-stable "graphite-self-hosted-build.zip" --clobber From 8a69a8de04e07b5a2737de0be9a7c3cef611d24b Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Thu, 19 Mar 2026 01:31:44 -0700 Subject: [PATCH 3/6] Move build from ci.yml to a delegated run in build.yml --- .github/workflows/build.yml | 88 +++++++++++++++++++++++-------------- .github/workflows/ci.yml | 77 +++----------------------------- 2 files changed, 60 insertions(+), 105 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1e9cee5744..0d101b23b2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,18 +62,6 @@ jobs: CARGO_INCREMENTAL: 0 SCCACHE_DIR: /var/lib/github-actions/.cache steps: - - name: ๐Ÿ”€ Set deploy target - id: deploy-target - run: | - if [[ "${{ github.ref }}" == "refs/tags/latest-stable" ]]; then - echo "cf_project=graphite-editor" >> $GITHUB_OUTPUT - DOMAIN="editor.graphite.art" - else - echo "cf_project=graphite-dev" >> $GITHUB_OUTPUT - DOMAIN="dev.graphite.art" - fi - echo "head_replacement=" >> $GITHUB_OUTPUT - - name: ๐Ÿ“ฅ Clone repository uses: actions/checkout@v6 with: @@ -102,9 +90,20 @@ jobs: rustflags: "" target: wasm32-unknown-unknown - - name: โœ‚ Replace template in of index.html + - name: ๐Ÿ”€ Choose production deployment environment and insert template + id: production-env if: github.event_name == 'push' - run: sed -i "s||${{ steps.deploy-target.outputs.head_replacement }}|" frontend/index.html + run: | + if [[ "${{ github.ref }}" == "refs/tags/latest-stable" ]]; then + echo "cf_project=graphite-editor" >> $GITHUB_OUTPUT + DOMAIN="editor.graphite.art" + else + echo "cf_project=graphite-dev" >> $GITHUB_OUTPUT + DOMAIN="dev.graphite.art" + fi + TEMPLATE="" + echo "template=$TEMPLATE" >> $GITHUB_OUTPUT + sed -i "s||$TEMPLATE|" frontend/index.html - name: ๐ŸŒ Build Graphite web code env: @@ -117,28 +116,32 @@ jobs: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} run: | - MAX_ATTEMPTS=5 - DELAY=30 + if [ -z "$CLOUDFLARE_API_TOKEN" ]; then + echo "No Cloudflare API token available (fork PR), skipping deploy." + exit 0 + fi + MAX_ATTEMPTS=8 + DELAY=15 for ATTEMPT in $(seq 1 $MAX_ATTEMPTS); do echo "Attempt $ATTEMPT of $MAX_ATTEMPTS..." - if OUTPUT=$(npx wrangler@3 pages deploy "frontend/dist" --project-name="${{ steps.deploy-target.outputs.cf_project }}" --commit-dirty=true 2>&1); then - URL=$(echo "$OUTPUT" | grep -oP 'https://[^\s]+\.pages\.dev' | head -1) + npx wrangler@3 pages deploy "frontend/dist" --project-name="${{ steps.production-env.outputs.cf_project || 'graphite-dev' }}" --commit-dirty=true 2>&1 | tee /tmp/wrangler_output + if [ ${PIPESTATUS[0]} -eq 0 ]; then + URL=$(grep -oP 'https://[^\s]+\.pages\.dev' /tmp/wrangler_output | head -1) echo "url=$URL" >> "$GITHUB_OUTPUT" echo "Published successfully: $URL" exit 0 fi - echo "Attempt $ATTEMPT failed:" - echo "$OUTPUT" + echo "Attempt $ATTEMPT failed." if [ "$ATTEMPT" -lt "$MAX_ATTEMPTS" ]; then echo "Retrying in ${DELAY}s..." sleep $DELAY - DELAY=$((DELAY * 3)) + DELAY=$((DELAY * 2)) fi done echo "All $MAX_ATTEMPTS Cloudflare Pages publish attempts failed." exit 1 - - name: ๐Ÿš€ Create GitHub Deployment for "View deployment" button + - name: ๐Ÿš€ Create a GitHub environment deployment if: inputs.checkout_repo == '' || inputs.checkout_repo == github.repository env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -148,15 +151,25 @@ jobs: echo "No Cloudflare URL available, skipping deployment." exit 0 fi + if [ "${{ github.ref }}" = "refs/tags/latest-stable" ]; then + REF="latest-stable" + ENVIRONMENT="graphite-editor (Production)" + elif [ "${{ github.event_name }}" = "push" ]; then + REF="master" + ENVIRONMENT="graphite-dev (Production)" + else + REF="${{ inputs.checkout_ref || github.head_ref || github.ref_name }}" + ENVIRONMENT="graphite-dev (Preview)" + fi DEPLOY_ID=$(gh api \ -X POST \ -H "Accept: application/vnd.github+json" \ repos/${{ github.repository }}/deployments \ - -f ref="$(git rev-parse HEAD)" \ - -f environment="graphite-dev (Preview)" \ - -F auto_merge=false \ - -f required_contexts="[]" \ - --jq '.id') + --input - \ + --jq '.id' < Date: Thu, 19 Mar 2026 03:59:26 -0700 Subject: [PATCH 4/6] Rename CI to Check --- .github/workflows/{ci.yml => check.yml} | 2 +- .github/workflows/comment-clippy-warnings.yaml | 4 ++-- .github/workflows/comment-profiling-changes.yaml | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) rename .github/workflows/{ci.yml => check.yml} (99%) diff --git a/.github/workflows/ci.yml b/.github/workflows/check.yml similarity index 99% rename from .github/workflows/ci.yml rename to .github/workflows/check.yml index ad78dc81b0..94f3180ba9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/check.yml @@ -1,4 +1,4 @@ -name: "CI" +name: "Check" on: pull_request: {} diff --git a/.github/workflows/comment-clippy-warnings.yaml b/.github/workflows/comment-clippy-warnings.yaml index 9e352bf6e6..b2f516b136 100644 --- a/.github/workflows/comment-clippy-warnings.yaml +++ b/.github/workflows/comment-clippy-warnings.yaml @@ -52,7 +52,7 @@ jobs: fi - name: Delete previous comments - uses: actions/github-script@v6 + uses: actions/github-script@v8 with: github-token: ${{secrets.GITHUB_TOKEN}} script: | @@ -76,7 +76,7 @@ jobs: - name: Comment PR if: steps.clippy.outputs.CLIPPY_ISSUES_FOUND == 'true' - uses: actions/github-script@v6 + uses: actions/github-script@v8 with: github-token: ${{secrets.GITHUB_TOKEN}} script: | diff --git a/.github/workflows/comment-profiling-changes.yaml b/.github/workflows/comment-profiling-changes.yaml index e336139aa0..e07fb9d2f8 100644 --- a/.github/workflows/comment-profiling-changes.yaml +++ b/.github/workflows/comment-profiling-changes.yaml @@ -98,7 +98,7 @@ jobs: - name: Make old comments collapsed by default # Only run if we have write permissions (not a fork) if: github.event.pull_request.head.repo.full_name == github.repository - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: github-token: ${{secrets.GITHUB_TOKEN}} script: | @@ -124,7 +124,7 @@ jobs: - name: Analyze profiling changes id: analyze - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: | const fs = require('fs'); @@ -183,7 +183,7 @@ jobs: - name: Comment PR if: github.event.pull_request.head.repo.full_name == github.repository - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: github-token: ${{secrets.GITHUB_TOKEN}} script: | @@ -333,7 +333,7 @@ jobs: - name: Fail on significant regressions if: steps.analyze.outputs.has-significant-changes == 'true' - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: | const regressionDetails = JSON.parse('${{ steps.analyze.outputs.regression-details }}'); From 5dec45f61d3a433ba7031306577d5a2f7432dc07 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Thu, 19 Mar 2026 04:19:59 -0700 Subject: [PATCH 5/6] Code review fixes --- .github/workflows/build.yml | 5 ++++- .github/workflows/check.yml | 34 +++++++++++++++++++++------------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0d101b23b2..a954284367 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -112,6 +112,7 @@ jobs: - name: ๐Ÿ“ค Publish to Cloudflare Pages id: cloudflare + continue-on-error: ${{ github.event_name != 'push' }} env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} @@ -220,7 +221,9 @@ jobs: - name: โœ‚ Strip template from completed build for a clean artifact if: github.event_name == 'push' - run: sed -i "s|${{ steps.production-env.outputs.template }}||" frontend/dist/index.html + env: + TEMPLATE: ${{ steps.production-env.outputs.template }} + run: sed -i "s|$TEMPLATE||" frontend/dist/index.html - name: ๐Ÿ“ฆ Upload web bundle artifact if: github.event_name != 'pull_request' diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 94f3180ba9..ccd308a5ae 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -8,8 +8,23 @@ env: CARGO_TERM_COLOR: always jobs: + # Check if CI can be skipped (for merge queue deduplication) + skip-check: + runs-on: ubuntu-latest + outputs: + skip: ${{ steps.check.outputs.skip-check }} + steps: + - name: ๐Ÿ“ฅ Clone repository + uses: actions/checkout@v6 + + - name: ๐Ÿšฆ Check if CI can be skipped + id: check + uses: cariad-tech/merge-queue-ci-skipper@main + # Build the web app via the shared build workflow build: + needs: skip-check + if: needs.skip-check.outputs.skip != 'true' uses: ./.github/workflows/build.yml secrets: inherit with: @@ -17,6 +32,8 @@ jobs: # Run the Rust tests on the self-hosted native runner test: + needs: skip-check + if: needs.skip-check.outputs.skip != 'true' runs-on: [self-hosted, target/native] env: RUSTC_WRAPPER: /usr/bin/sccache @@ -26,12 +43,7 @@ jobs: - name: ๐Ÿ“ฅ Clone repository uses: actions/checkout@v6 - - name: ๐Ÿšฆ Check if CI can be skipped - id: skip-check - uses: cariad-tech/merge-queue-ci-skipper@main - - name: ๐Ÿฆ€ Install Rust - if: steps.skip-check.outputs.skip-check != 'true' uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: stable @@ -40,28 +52,23 @@ jobs: rustflags: "" - name: ๐Ÿฆ€ Fetch Rust dependencies - if: steps.skip-check.outputs.skip-check != 'true' run: cargo fetch --locked - name: ๐Ÿงช Run Rust tests - if: steps.skip-check.outputs.skip-check != 'true' env: RUSTFLAGS: -Dwarnings run: mold -run cargo test --all-features # Rust format check on GitHub runner rust-fmt: + needs: skip-check + if: needs.skip-check.outputs.skip != 'true' runs-on: ubuntu-latest steps: - name: ๐Ÿ“ฅ Clone repository uses: actions/checkout@v6 - - name: ๐Ÿšฆ Check if CI can be skipped - id: skip-check - uses: cariad-tech/merge-queue-ci-skipper@main - - name: ๐Ÿฆ€ Install Rust - if: steps.skip-check.outputs.skip-check != 'true' uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: stable @@ -71,11 +78,12 @@ jobs: components: rustfmt - name: ๐Ÿ”ฌ Check Rust formatting - if: steps.skip-check.outputs.skip-check != 'true' run: cargo fmt --all -- --check # License compatibility check on GitHub runner check-licenses: + needs: skip-check + if: needs.skip-check.outputs.skip != 'true' runs-on: ubuntu-latest steps: - name: ๐Ÿ“ฅ Clone repository From 2bd4c44690a3e39be42287e063b26981ff65cb2b Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Thu, 19 Mar 2026 04:27:30 -0700 Subject: [PATCH 6/6] Review 2 --- .github/workflows/check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index ccd308a5ae..e82247bc48 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -19,7 +19,7 @@ jobs: - name: ๐Ÿšฆ Check if CI can be skipped id: check - uses: cariad-tech/merge-queue-ci-skipper@main + uses: cariad-tech/merge-queue-ci-skipper@cf80db21fc70244e36487acc531b3f1118889b0a # Build the web app via the shared build workflow build: