Skip to content
Open
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
56 changes: 56 additions & 0 deletions .bazelignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Bazel must not treat pnpm's node_modules trees as packages. rules_js
# verifies every workspace importer's node_modules is listed here
# (see verify_node_modules_ignored in MODULE.bazel). Keep this list in
# sync with the package globs in pnpm-workspace.yaml.
node_modules
packages/artifacts/node_modules
packages/build/node_modules
packages/build-cli/node_modules
packages/canonical/node_modules
packages/capability/node_modules
packages/chain/node_modules
packages/cli/node_modules
packages/control/node_modules
packages/core/node_modules
packages/crypto/node_modules
packages/database/node_modules
packages/engine/node_modules
packages/engine-harness/node_modules
packages/engine-store/node_modules
packages/evals/node_modules
packages/flow/node_modules
packages/flows/node_modules
packages/fs/node_modules
packages/gateway/node_modules
packages/harness/node_modules
packages/jj/node_modules
packages/journal/node_modules
packages/kernel/node_modules
packages/keys/node_modules
packages/memory/node_modules
packages/model/node_modules
packages/notifications/node_modules
packages/observability/node_modules
packages/patterns/node_modules
packages/plan/node_modules
packages/platform-browser/node_modules
packages/platform-bun/node_modules
packages/platform-node/node_modules
packages/plugin/node_modules
packages/registry/node_modules
packages/run-store/node_modules
packages/sandbox/node_modules
packages/scorers/node_modules
packages/std/node_modules
packages/step-cache/node_modules
packages/sync/node_modules
packages/targets/node_modules
packages/testing/node_modules
packages/time-travel/node_modules
packages/triggers/node_modules
packages/build/infra/node_modules
examples/node_modules
apps/server/node_modules
apps/shared/node_modules
apps/tui/node_modules
apps/ui/node_modules
110 changes: 110 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Bazel configuration for the flows workspace.
#
# Bazelisk reads .bazelversion and runs Bazel 8.7.0. 9.2.0 is the current
# release line; 8.7.0 is the newest 8.x LTS, which is the line the Aspect
# rules (rules_js, rules_ts, rules_lint) and rules_rust test against. Upgrade
# deliberately, not by accident.

# This repository is bzlmod-only. There is no WORKSPACE file; disabling the
# workspace mechanism makes a stray WORKSPACE file an error instead of a
# silent second dependency graph.
common --enable_bzlmod
common --enable_workspace=false

# Print which rc files and options took effect. Cheap insurance when a flag
# "isn't applying".
common --announce_rc

# ---------------------------------------------------------------------------
# Caching
# ---------------------------------------------------------------------------

# On-disk action cache, shared across `bazel clean` and output-base resets.
# --disk_cache takes a path; a valueless spelling does NOT mean "default
# location". rc files are one token stream, so a bare `--disk_cache`
# consumes the next flag as its path (here it created a cache directory
# literally named `--spawn_strategy=sandboxed` in the workspace and silently
# dropped the spawn-strategy flag). `%workspace%` is not substituted in flag
# values either (only in import paths); a relative path resolves against the
# client's working directory, so run Bazel from the workspace root. The cache
# directory is gitignored at any depth.
build --disk_cache=.bazel-cache

# Bazel's default repository cache (under the output user root) already
# persists external fetches (npm tarballs, crate archives, toolchains)
# across cleans and invocations; nothing to configure.

# ---------------------------------------------------------------------------
# Hermeticity and sandboxing
# ---------------------------------------------------------------------------

# Actions run in a sandbox with only their declared inputs. These are the
# defaults on Bazel 8 for Linux and macOS; they are stated explicitly because
# this repository's reproducibility claims rest on them.
build --spawn_strategy=sandboxed
build --incompatible_strict_action_env

# Sandbox failures should be loud, not silently retried without a sandbox.
build --sandbox_debug

# The cargo bridge actions (tools/cargo) invoke the host rustup shims, which
# live under $HOME/.cargo/bin. The toolchain itself is pinned by
# rust-toolchain.toml; HOME passthrough only locates the shims.
build --action_env=HOME
test --action_env=HOME

# ---------------------------------------------------------------------------
# Remote cache
# ---------------------------------------------------------------------------
#
# `--config=remote` layers the remote cache on top of the local disk cache.
# Bazel does not expand environment variables in rc files, so the endpoint and
# credentials are passed by CI (see .github/workflows/bazel.yml) or written to
# a gitignored .bazelrc.remote:
#
# build:remote --remote_cache=https://build.example.com
# build:remote --remote_header=Authorization=Bearer <token>
#
# try-import does not fail when the file is absent.
try-import %workspace%/.bazelrc.remote

# Build without the Bytes: with a remote cache, download only the outputs the
# final targets actually need, not every intermediate. Correct for CI, where
# most actions are cache hits. toplevel and minimal are two spellings of the
# same knob (--remote_download_outputs); naming both sets it twice, and the
# last one silently wins.
build:remote --remote_download_toplevel

# ---------------------------------------------------------------------------
# CI
# ---------------------------------------------------------------------------
#
# `bazel test --config=ci //...` is the CI entry point.
build:ci --config=remote
build:ci --color=yes
build:ci --show_timestamps
# Never flake a gate on a hung download.
build:ci --remote_timeout=60
# Test output: print failures in full, keep passes quiet.
test:ci --test_output=errors
test:ci --test_summary=detailed

# ---------------------------------------------------------------------------
# Lint
# ---------------------------------------------------------------------------
#
# `bazel build --config=lint //...` runs every registered linter as an aspect
# over the normal build: lint actions are cached and remotely executed like
# any other action, and a lint failure fails the build.
build:lint --aspects=//tools/lint:linters.bzl%eslint
build:lint --output_groups=+rules_lint_human

# ---------------------------------------------------------------------------
# Gazelle
# ---------------------------------------------------------------------------
#
# `bazel run //:gazelle` regenerates BUILD.bazel files from imports.
# `bazel run //:gazelle.check` fails when the committed files are stale.

# The JS gazelle plugin is prebuilt (aspect_gazelle_prebuilt); no Go, Rust,
# or LLVM toolchain is needed to run it.
1 change: 1 addition & 0 deletions .bazelversion
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
8.7.0
97 changes: 97 additions & 0 deletions .github/workflows/bazel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
name: Bazel

on:
push:
branches: [main]
pull_request:

jobs:
test:
name: bazel build + test
runs-on: ubuntu-latest
steps:
# submodules: vendor/jj holds the pinned jj fork that
# //crates/flows-jj builds against.
- uses: actions/checkout@v4
with:
submodules: true

# Bazelisk reads .bazelversion (Bazel 8.7.0) and downloads that exact
# Bazel. No Node or pnpm setup follows: rules_js fetches the pinned
# Node toolchain and every npm package itself.
- name: Install bazelisk
run: |
curl -fsSL -o /usr/local/bin/bazel https://github.com/bazelbuild/bazelisk/releases/download/v1.26.0/bazelisk-linux-amd64
chmod +x /usr/local/bin/bazel

# rust-toolchain.toml pins the toolchain for the cargo actions under
# //crates/flows-jj; a bare `rustup toolchain install` reads it.
- name: Install pinned Rust toolchain
run: rustup toolchain install

# Warm the two caches Bazel populates: the repository cache (external
# fetches: npm tarballs, crate archives, toolchains) and the disk cache
# (action outputs). The action graph itself is recomputed each run.
- name: Cache Bazel repositories and actions
uses: actions/cache@v4
with:
path: |
~/.cache/bazel/repository_cache
~/.cache/bazel-disk-cache
key: bazel-${{ runner.os }}-${{ hashFiles('MODULE.bazel.lock', 'pnpm-lock.yaml', 'Cargo.lock') }}
restore-keys: |
bazel-${{ runner.os }}-

# Optional remote cache. When the SMITHERS_BAZEL_CACHE_URL variable is
# set, layer a Bazel Remote Cache protocol endpoint (gRPC or HTTP) on
# top of the local caches. The smithers cache worker in
# packages/build/infra does not speak that protocol today; see
# docs/build-systems/bazel.md.
- name: Configure remote cache
if: vars.SMITHERS_BAZEL_CACHE_URL != ''
run: |
{
echo "build:remote --remote_cache=${SMITHERS_BAZEL_CACHE_URL}"
echo "build:remote --remote_header=Authorization=Bearer ${SMITHERS_BAZEL_CACHE_TOKEN}"
} > .bazelrc.remote
env:
SMITHERS_BAZEL_CACHE_URL: ${{ vars.SMITHERS_BAZEL_CACHE_URL }}
SMITHERS_BAZEL_CACHE_TOKEN: ${{ secrets.SMITHERS_BAZEL_CACHE_TOKEN }}

# The target set is explicit, not //...: the kernel <-> platform-browser
# packages declare a runtime dependency cycle, which pnpm tolerates and
# Bazel cannot analyze, and the generated npm store farm that mirrors it
# lives in the root package, so no wildcard that includes the root
# package analyzes. Per-package link targets are tagged manual by
# rules_js, so the per-directory wildcards below are clean. Details and
# the fix are in docs/build-systems/bazel.md.
# --disk_cache is a command option, not a startup option: it must follow
# the subcommand (`bazel --disk_cache=... build` is a fatal "unknown
# startup option"). $HOME, not ~: bash does not expand a tilde after `=`.
- name: Build
run: bazel build --disk_cache=$HOME/.cache/bazel-disk-cache --config=ci //packages/... //apps/... //examples/... //crates/... //tools/...

- name: Test
run: bazel test --disk_cache=$HOME/.cache/bazel-disk-cache --config=ci //packages/... //apps/... //examples/... //crates/... //tools/...

# Lint aspects over the same graph; lint actions are cached like any
# other.
- name: Lint
run: bazel build --disk_cache=$HOME/.cache/bazel-disk-cache --config=ci --config=lint //packages/... --output_groups=+rules_lint_human

# Formatting gate (dprint check). Runs in the workspace, not a sandbox:
# dprint downloads its wasm plugins on first use.
- name: Format check
run: bazel run //tools/format:format.check

# Generated-file drift gate: fails when BUILD.bazel files are stale
# relative to imports and package.json files.
- name: Gazelle drift check
run: bazel run //:gazelle.check

# The wasm reproducibility gate is meaningful only on the canonical
# artifact host (x86_64-unknown-linux-gnu), which this job is. It
# compares the sandboxed Bazel build against the committed
# packages/jj/wasm/flows_jj.wasm byte for byte.
- name: Wasm reproducibility gate
run: bazel test --disk_cache=$HOME/.cache/bazel-disk-cache --config=ci //crates/flows-jj:wasm_repro_test
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,9 @@ packages/*/.smithers/

# Wrangler local dev-server state (miniflare sqlite, DO storage, traces)
.wrangler/

# Bazel
bazel-*

# Local Bazel disk cache (configured in .bazelrc)
.bazel-cache/
92 changes: 92 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Root Bazel package for the flows workspace.
#
# NOTE: the `BUILD.ts` files in this repository belong to the in-repo smithers
# build system, not to Bazel. Bazel loads only files named `BUILD` or
# `BUILD.bazel`; this repository standardizes on `BUILD.bazel` so the two
# systems can never be confused, and no `BUILD.ts` file is read, modified, or
# deleted by anything in the Bazel setup.
load("@aspect_gazelle_prebuilt//:def.bzl", "aspect_gazelle")
load("@aspect_rules_js//js:defs.bzl", "js_library")
load("@aspect_rules_ts//ts:defs.bzl", "ts_config")
load("@npm//:defs.bzl", "npm_link_all_packages")

# ---------------------------------------------------------------------------
# Gazelle
#
# `bazel run //:gazelle` walks the workspace and generates BUILD.bazel files
# from package.json files and import statements. `bazel run //:gazelle.check`
# (wired with_check below) fails in CI when the committed files are stale.
#
# Directives for the JS plugin. Most packages keep their tsconfig in
# tsconfig.json and the test tsconfig in tsconfig.test.json; the second
# js_tsconfig_file line applies the test tsconfig to the generated *_tests
# target group.
# ---------------------------------------------------------------------------
# gazelle:js_pnpm_lockfile pnpm-lock.yaml
# gazelle:js_files .bazel-no-root-sources
# gazelle:js_test_files .bazel-no-root-test-sources
# gazelle:exclude lint
# gazelle:exclude apps/ui/.smithers
# gazelle:js_tsconfig_file tsconfig.json
# gazelle:js_tsconfig_file {dirname}_tests tsconfig.test.json
# gazelle:js_validate_import_statements warn
# gazelle:js_tsconfig_package_deps enabled
# gazelle:js_npm_package_target_name pkg
# gazelle:js_visibility //:__subpackages__
# gazelle:exclude vendor
# gazelle:exclude docs
# gazelle:exclude evals
aspect_gazelle(
name = "gazelle",
languages = ["js"],
with_check = True,
)

exports_files(
[
".npmrc",
"Cargo.lock",
"Cargo.toml",
"eslint.jsdoc.js",
"rust-toolchain.toml",
"pnpm-lock.yaml",
"pnpm-workspace.yaml",
"tsconfig.base.json",
],
visibility = ["//visibility:public"],
)

# The shared jsdoc convention, imported by every package's eslint.config.js as
# ../../eslint.jsdoc.js. Wrapped in a js_library so package-level configs can
# declare it as a dependency.
js_library(
name = "eslint_jsdoc",
srcs = ["eslint.jsdoc.js"],
visibility = ["//visibility:public"],
deps = [":node_modules/eslint-plugin-jsdoc"],
)

# The aggregator flat config the lint aspect discovers from the bin root.
# Deps carry each package's config and plugin closure into the action.
js_library(
name = "eslint_config",
srcs = ["eslint.config.js"],
visibility = ["//visibility:public"],
deps = [
"//packages/canonical:eslint_config",
"//packages/crypto:eslint_config",
"//packages/keys:eslint_config",
],
)

ts_config(
name = "tsconfig",
src = "tsconfig.json",
visibility = [":__subpackages__"],
deps = [
":package.json",
":tsconfig.base.json",
],
)

npm_link_all_packages(name = "node_modules")
Loading
Loading