Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 90 additions & 23 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ on:
push:
branches:
- master
tags:
- latest-stable
workflow_dispatch:
inputs:
web:
Expand Down Expand Up @@ -59,8 +61,6 @@ jobs:
RUSTC_WRAPPER: /usr/bin/sccache
CARGO_INCREMENTAL: 0
SCCACHE_DIR: /var/lib/github-actions/.cache
INDEX_HTML_HEAD_REPLACEMENT: <script defer data-domain="dev.graphite.art" data-api="https://graphite.art/visit/event" src="https://graphite.art/visit/script.hash.js"></script>

steps:
- name: 📥 Clone repository
uses: actions/checkout@v6
Expand Down Expand Up @@ -90,9 +90,20 @@ jobs:
rustflags: ""
target: wasm32-unknown-unknown

- name: ✂ Replace template in <head> of index.html
- name: 🔀 Choose production deployment environment and insert template
id: production-env
if: github.event_name == 'push'
run: sed -i "s|<!-- INDEX_HTML_HEAD_REPLACEMENT -->|$INDEX_HTML_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="<script defer data-domain=\"$DOMAIN\" data-api=\"https://graphite.art/visit/event\" src=\"https://graphite.art/visit/script.hash.js\"></script>"
echo "template=$TEMPLATE" >> $GITHUB_OUTPUT
sed -i "s|<!-- INDEX_HTML_HEAD_REPLACEMENT -->|$TEMPLATE|" frontend/index.html

- name: 🌐 Build Graphite web code
env:
Expand All @@ -101,32 +112,75 @@ 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 }}
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="graphite-dev" --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 a GitHub environment deployment
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
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 \
--input - \
--jq '.id' <<EOF
{"ref":"$REF","environment":"$ENVIRONMENT","auto_merge":false,"required_contexts":[]}
EOF
)
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
if: github.event_name != 'pull_request' && github.event_name != 'merge_group'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CF_URL: ${{ steps.cloudflare.outputs.url }}
Expand All @@ -140,15 +194,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)
elif [ "${{ github.event_name }}" != "pull_request" ] && [ "${{ github.event_name }}" != "merge_group" ]; then
# Manual trigger (workflow_dispatch, !build): comment on the PR
PR_NUMBER="${{ inputs.pr_number }}"
if [ -z "$PR_NUMBER" ]; then
BRANCH=$(git rev-parse --abbrev-ref HEAD)
Expand All @@ -162,18 +219,28 @@ jobs:
fi
fi

- name: ✂ Strip analytics script from built output for clean artifact
- name: ✂ Strip template from completed build for a clean artifact
if: github.event_name == 'push'
run: sed -i "s|$INDEX_HTML_HEAD_REPLACEMENT||" 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'
uses: actions/upload-artifact@v6
with:
name: graphite-web-bundle
path: frontend/dist

- name: 👕 Lint Graphite web formatting
env:
NODE_ENV: production
run: |
cd frontend
npm run check

- 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: |
Expand All @@ -184,7 +251,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
Expand Down Expand Up @@ -214,8 +281,8 @@ jobs:
uses: actions/cache@v5
with:
path: |
${{ env.USERPROFILE }}\.cargo\registry
${{ env.USERPROFILE }}\.cargo\git
~/.cargo/registry
~/.cargo/git
target
key: cargo-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}

Expand Down Expand Up @@ -376,7 +443,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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why, I would expect it to also build on pushing the tag.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That build already occurs when we originally push the commit. We haven't yet set up GitHub Releases deployments, but this clause will be removed when we do that.

runs-on: macos-latest
permissions:
contents: read
Expand Down Expand Up @@ -554,7 +621,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
Expand Down
101 changes: 101 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
name: "Check"

on:
pull_request: {}
merge_group: {}

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@cf80db21fc70244e36487acc531b3f1118889b0a

# 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:
web: true

# 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
CARGO_INCREMENTAL: 0
SCCACHE_DIR: /var/lib/github-actions/.cache
steps:
- name: 📥 Clone repository
uses: actions/checkout@v6

- name: 🦀 Install Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
override: true
cache: false
rustflags: ""

- name: 🦀 Fetch Rust dependencies
run: cargo fetch --locked

- name: 🧪 Run Rust tests
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: 🦀 Install Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
override: true
cache: false
rustflags: ""
components: rustfmt

- name: 🔬 Check Rust formatting
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
uses: actions/checkout@v6

- name: 📜 Check crate license compatibility for root workspace
uses: EmbarkStudios/cargo-deny-action@v2
with:
command: check bans licenses sources

- name: 📜 Check crate license compatibility for /libraries/rawkit
uses: EmbarkStudios/cargo-deny-action@v2
with:
command: check bans licenses sources
manifest-path: libraries/rawkit/Cargo.toml
Loading
Loading