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
5 changes: 5 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
*.py text eol=lf
*.sh text eol=lf
*.md text eol=lf
# The GPUI fork patches are a build input now, not just an audit artifact:
# `.shots/gpui_patches.py --materialize` applies them before every cargo
# command on all three CI hosts. `* text=auto` above would hand the Windows
# runner CRLF copies, so pin them the way every other source file is pinned.
*.patch text eol=lf
# Oxfmt checks LF endings, including on Windows checkouts.
web/**/*.ts text eol=lf
web/**/*.tsx text eol=lf
Expand Down
27 changes: 27 additions & 0 deletions .github/actions/rust-env/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,33 @@ inputs:
runs:
using: composite
steps:
# The patched GPUI sources are not in the repository; five
# `[patch.crates-io]` entries point into the gitignored `.vendor/` tree that
# `.shots/gpui_patches.py --materialize` builds from the pinned published
# packages plus `docs/upstream/patches/*.patch`. Cargo fails at manifest
# load when a patch path is missing ("failed to load source for
# dependency"), so this has to happen before the first cargo command in the
# job -- which is why it lives in the shared action rather than in each
# job, where one omission would break a whole leg.
#
# It also has to happen before `Swatinem/rust-cache` below: that action runs
# `cargo metadata` of its own to key the cache, and that call is a cargo
# command like any other. The cost is that a cold job downloads the five
# `.crate` files before the registry cache is restored; they are small next
# to the dependency graph the cache is actually for, and a warm `.vendor/`
# check is a hash comparison that finishes in under a tenth of a second.
#
# `setup-python` rather than the runner's default interpreter: `python3` is
# not on PATH on the Windows runners, and pinning the version keeps the
# three OS legs on one interpreter.
- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Materialize the patched GPUI sources
shell: bash
run: python .shots/gpui_patches.py --materialize

- name: Disable Windows Defender and configure exclusions
# Windows-only. GitHub Actions Windows runners run Defender by default.
# Scanning every .rlib, .rmeta, .exe, and cache tar extraction causes
Expand Down
55 changes: 46 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ jobs:
working-directory: web
run: pnpm install --frozen-lockfile

# `cargo fmt --all` is a cargo command, so it needs the patched GPUI
# sources materialized like every other one -- and this job needs it for a
# second reason besides `[patch.crates-io]`:
# `crates/herogpui-components/tests/rounded_clip_shaders.rs` includes the
# renderer's own `shaders.rs` by `#[path]`, so rustfmt fails on a missing
# module before cargo ever resolves a dependency.
- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Materialize the patched GPUI sources
run: python .shots/gpui_patches.py --materialize

- name: Format check
run: cargo fmt --all -- --check

Expand Down Expand Up @@ -146,15 +159,23 @@ jobs:
with:
tool: cargo-machete

# The patches are the only record of the fork, so this is the gate that
# keeps them honest: each one has to apply to the pristine published
# package with no fuzz and no offset, and reproduce the `.vendor/` tree
# the rest of this job just built against, byte for byte. A hand-edit
# under `.vendor/` that never made it back into a patch fails here.
- name: Verify GPUI renderer patches
run: python .shots/gpui_patches.py --check
run: |
python .shots/gpui_patches.py --self-test
python .shots/gpui_patches.py --check

# Unused-dependency gate. cargo-machete reads every manifest in the
# tree and greps the crate sources for each dependency's name -- no
# build, not even a toolchain -- so it answers in seconds and sits
# before the clippy step below, which pays for a full compile. The
# vendored `crates/gpui_web` fork is clean under it too (verified by
# running machete on that directory directly).
# before the clippy step below, which pays for a full compile. It walks
# the working directory rather than the dependency graph, and the
# materialized GPUI sources under `.vendor/` are hidden from it by the
# leading dot, so it sees only HeroGPUI's own manifests.
#
# There is no ignore list today, deliberately: the tree is clean with
# none (verified against cargo-machete 0.9.2, what install-action
Expand All @@ -164,7 +185,7 @@ jobs:
# that crate's manifest, or the same table under
# `[workspace.metadata.cargo-machete]` in the root. Every entry must
# carry its reason in a comment beside it, the way
# `[package.metadata.cargo-shear]` does in crates/gpui_web; an entry
# `[package.metadata.cargo-shear]` does upstream; an entry
# without a reason is a hole in the gate.
- name: Unused dependencies (cargo-machete)
run: cargo machete
Expand Down Expand Up @@ -338,10 +359,12 @@ jobs:
# channel
# --> .../wasm_thread-0.3.3/src/lib.rs:1:1
#
# `multithreaded` is now off in the vendored fork's own manifest
# (`crates/gpui_web/Cargo.toml`, which `[patch.crates-io]` substitutes --
# the only place it can be switched, because `gpui_platform`'s wasm32
# edge enables default features and cargo unions feature sets). With it
# `multithreaded` is now off in the forked manifest itself -- a
# `default = []` hunk in `docs/upstream/patches/gpui-pre-web-0.3.3.patch`,
# materialized into `.vendor/gpui-pre-web-0.3.3/Cargo.toml`, which
# `[patch.crates-io]` substitutes. That is the only place it can be
# switched, because `gpui_platform`'s wasm32 edge enables default features
# and cargo unions feature sets. With it
# off, `wasm_thread` is not in the graph and stable builds this target,
# profile included. Nothing was using it: `crates/herogpui-web` starts the
# app with `single_threaded_web()`, and the multi-threaded platform needs
Expand All @@ -350,6 +373,20 @@ jobs:
- name: Add the wasm32 target to the pinned toolchain
run: rustup target add wasm32-unknown-unknown

# This job builds its own environment instead of using
# `./.github/actions/rust-env`, so it repeats that action's
# materialization step. `gpui-pre-web` is the package that matters most
# here: its `default = []` hunk is what keeps `wasm_thread` -- and with it
# a nightly toolchain -- out of the wasm32 graph, and it now lives only in
# `docs/upstream/patches/gpui-pre-web-0.3.3.patch`. Before
# `Swatinem/rust-cache`, which runs a `cargo metadata` of its own.
- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Materialize the patched GPUI sources
run: python .shots/gpui_patches.py --materialize

- uses: Swatinem/rust-cache@v2
with:
key: wasm32
Expand Down
22 changes: 22 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,17 @@ jobs:
libxcb-xfixes0-dev \
libfontconfig-dev \
libfreetype-dev
# The patched GPUI sources are not in the repository -- the five
# `[patch.crates-io]` paths under `.vendor/` are built from
# `docs/upstream/patches/*.patch` by this command, and cargo aborts at
# manifest load without them. Before `Swatinem/rust-cache`, which runs a
# `cargo metadata` of its own.
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Materialize the patched GPUI sources
shell: bash
run: python .shots/gpui_patches.py --materialize
- uses: Swatinem/rust-cache@v2
- run: cargo test --workspace --locked

Expand Down Expand Up @@ -93,6 +104,17 @@ jobs:
libxcb-xfixes0-dev \
libfontconfig-dev \
libfreetype-dev
# The patched GPUI sources are not in the repository -- the five
# `[patch.crates-io]` paths under `.vendor/` are built from
# `docs/upstream/patches/*.patch` by this command, and cargo aborts at
# manifest load without them. Before `Swatinem/rust-cache`, which runs a
# `cargo metadata` of its own.
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Materialize the patched GPUI sources
shell: bash
run: python .shots/gpui_patches.py --materialize
- uses: Swatinem/rust-cache@v2
- run: cargo build --locked --release -p herogpui-gallery
- name: Stage release binary
Expand Down
9 changes: 6 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ web/.verify/
.vercel
.env*

# Vendored gpui-pre forks are standalone workspaces and may be built in
# place; their target output is not source.
crates/gpui_pre*/target/
# The patched GPUI sources. The repository records the fork as patches under
# docs/upstream/patches/; `.shots/gpui_patches.py --materialize` rebuilds this
# tree from the pinned published packages plus those patches, and every cargo
# command (and rust-analyzer) needs it to exist first. ~153,000 lines of
# dependency source that is not ours and is never committed.
/.vendor/
22 changes: 5 additions & 17 deletions .shots/fence_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,6 @@

SCAN_ROOT = 'crates'

# These are exact-version gpui-pre source forks selected through the workspace
# `[patch.crates-io]` block. Their rustdoc belongs to the external dependency;
# the application fence contract is audited in HeroGPUI-owned crates below.
EXTERNAL_GPUI_DIRS = {
'gpui_pre',
'gpui_pre_apple',
'gpui_pre_wgpu',
'gpui_pre_windows',
'gpui_web',
}

# (path relative to the repository root, why running it is not an option).
# Every entry is asserted to still match a `no_run` fence in that file, so an
# example that stops needing the exemption fails this audit until its entry
Expand Down Expand Up @@ -128,12 +117,11 @@ def rust_files(root):
"""Every `.rs` file under `root`, sorted, skipping build output."""
found = []
for base, dirs, names in os.walk(root):
dirs[:] = [
d for d in dirs
if d != 'target'
and not d.startswith('.')
and d not in EXTERNAL_GPUI_DIRS
]
# The patched gpui-pre forks used to sit under `crates/` and had to be
# named here to keep an external dependency's rustdoc out of this
# contract. They are materialized into `.vendor/` now, which the
# dot-prefix rule already excludes -- and `SCAN_ROOT` does not reach it.
dirs[:] = [d for d in dirs if d != 'target' and not d.startswith('.')]
for name in names:
if name.endswith('.rs'):
found.append(os.path.join(base, name).replace(os.sep, '/'))
Expand Down
Loading
Loading