Repository navigation
fix(ci): check web projects and Rust inputs #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,16 +4,18 @@ set -euo pipefail | |
| ROOT="$(git rev-parse --show-toplevel)" | ||
| cd "$ROOT" | ||
|
|
||
| STAGED="$(git diff --cached --name-only --diff-filter=ACMR)" | ||
| PATHS="$(mktemp)" | ||
| trap 'rm -f "$PATHS"' EXIT | ||
| git diff --cached --name-only --no-renames -z > "$PATHS" | ||
|
|
||
| if [[ -z "$STAGED" ]]; then | ||
| exit 0 | ||
| fi | ||
|
|
||
| if ! printf '%s\n' "$STAGED" | rg -q '^(Cargo\.toml|Cargo\.lock|src/core/|src/infra/homie-protocol/|src/infra/roci/)'; then | ||
| exit 0 | ||
| fi | ||
|
|
||
| echo "pre-commit: running homie-core clippy (staged Rust/core changes detected)" | ||
| cargo clippy -p homie-core --all-targets --all-features -- -D warnings | ||
| while IFS= read -r -d '' path; do | ||
| case "$path" in | ||
| Cargo.toml|Cargo.lock|.gitmodules|.cargo/*|rust-toolchain*|rustfmt.toml|.rustfmt.toml|*.rs|*/Cargo.toml|src/core/*|src/infra/homie-protocol/*|src/infra/roci|src/infra/roci/*|.githooks/*) | ||
| echo "pre-commit: checking Rust workspace formatting and homie-core clippy" | ||
| cargo fmt --all -- --check | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check staged Rust contents, not the working tree. If a developer stages a formatted Rust file and then makes an unstaged formatting edit,
|
||
| cargo clippy --locked -p homie-core --all-targets --all-features -- -D warnings | ||
| exit 0 | ||
| ;; | ||
| esac | ||
| done < "$PATHS" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,26 +9,43 @@ on: | |
| - Cargo.toml | ||
| - Cargo.lock | ||
| - src/core/** | ||
| - src/gateway/** | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| - src/infra/homie-protocol/** | ||
| - src/infra/roci | ||
| - src/infra/roci/** | ||
| - .gitmodules | ||
| - .cargo/** | ||
| - rust-toolchain* | ||
| - rustfmt.toml | ||
| - .rustfmt.toml | ||
| - .github/workflows/homie-core-lint.yml | ||
| pull_request: | ||
| paths: | ||
| - Cargo.toml | ||
| - Cargo.lock | ||
| - src/core/** | ||
| - src/gateway/** | ||
| - src/infra/homie-protocol/** | ||
| - src/infra/roci | ||
| - src/infra/roci/** | ||
| - .gitmodules | ||
| - .cargo/** | ||
| - rust-toolchain* | ||
| - rustfmt.toml | ||
| - .rustfmt.toml | ||
| - .github/workflows/homie-core-lint.yml | ||
| workflow_dispatch: | ||
|
|
||
| jobs: | ||
| homie-core-lint: | ||
| runs-on: ubuntu-latest | ||
| runs-on: boat | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| timeout-minutes: 20 | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| submodules: recursive | ||
| persist-credentials: false | ||
|
|
||
| - name: Install Rust toolchain | ||
| uses: dtolnay/rust-toolchain@stable | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| # Local CI checks | ||
|
|
||
| Install existing pnpm workspace dependencies with `pnpm install --frozen-lockfile`. | ||
| Build shared declarations with `pnpm shared:build` before checking consumers. | ||
| `pnpm web:typecheck` now explicitly checks both the application and Vite/Node | ||
| projects. The root solution config has no files; plain `tsc --noEmit` against it | ||
| was not a web source check. Explicit project checks retain `noEmit` and do not | ||
| write build-info files. | ||
|
|
||
| The existing Rust lint workflow checks out the recorded roci submodule recursively, | ||
| without persisting checkout credentials. It retains the pinned gitlink, rather | ||
| than fetching a moving upstream branch. Gateway, submodule identity and shared | ||
| Rust configuration now trigger the existing workflow. Its Linux job uses Boat; | ||
| owner-only public execution is configured separately, and no workflow was dispatched. | ||
|
|
||
| `python3 scripts/test-git-checks.py` runs offline disposable Git fixtures against | ||
| the executable pre-commit hook and existing CI path filters. The hook handles | ||
| staged deletions and renames, spaces and gitlinks, runs nonmutating workspace | ||
| format verification and preserves homie-core Clippy failure codes. No hook was | ||
| installed as part of this change. | ||
|
|
||
| Qualification limits from secret-free Linux checks follow. | ||
|
|
||
| - Web app and Node/Vite typechecks pass. Deliberate errors in each fail. | ||
| - Shared typecheck passes. Mobile typecheck still fails on missing Vitest/Node | ||
| test-tool declarations in its existing package contract. | ||
| - Pinned roci retrieval succeeds and resolves the missing-path dependency failure. | ||
| - Workspace formatting passes. The sandbox Clippy baseline reports existing | ||
| `collapsible_match` and `question_mark` diagnostics in homie-core. | ||
|
|
||
| A new mandatory full-graph pre-push gate remains deferred until those existing | ||
| baseline failures are resolved. No new schedule, required-check policy, native | ||
| acceptance or credentialed integration tests were enabled. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| #!/usr/bin/env python3 | ||
| """Offline fixtures for the executable pre-commit and existing CI path scopes.""" | ||
| import fnmatch | ||
| import os | ||
| from pathlib import Path | ||
| import subprocess | ||
| import tempfile | ||
| import unittest | ||
|
|
||
| ROOT = Path(__file__).resolve().parents[1] | ||
|
|
||
|
|
||
| class HookTests(unittest.TestCase): | ||
| def setUp(self): | ||
| self.temp = tempfile.TemporaryDirectory(prefix="homie hook ") | ||
| self.addCleanup(self.temp.cleanup) | ||
| self.repo = Path(self.temp.name) | ||
| self.git("init", "-q") | ||
| self.git("config", "user.email", "fixture@example.invalid") | ||
| self.git("config", "user.name", "Fixture") | ||
| (self.repo / "README.md").write_text("fixture\n") | ||
| self.git("add", "README.md") | ||
| self.git("commit", "-qm", "fixture") | ||
| self.bin = self.repo / "bin" | ||
| self.bin.mkdir() | ||
| cargo = self.bin / "cargo" | ||
| cargo.write_text('#!/bin/sh\nprintf "%s\\n" "$*" >> "$CALLS"\n' | ||
| 'if [ "$1" = "$FAIL_COMMAND" ]; then exit 23; fi\n') | ||
| cargo.chmod(0o755) | ||
| self.calls = self.repo / "calls" | ||
|
|
||
| def git(self, *args): | ||
| return subprocess.check_output(["git", *args], cwd=self.repo, text=True).strip() | ||
|
|
||
| def stage(self, path): | ||
| file = self.repo / path | ||
| file.parent.mkdir(parents=True, exist_ok=True) | ||
| file.write_text("fixture\n") | ||
| self.git("add", "--", path) | ||
|
|
||
| def hook(self, fail=""): | ||
| self.calls.write_text("") | ||
| result = subprocess.run([str(ROOT / ".githooks/pre-commit")], cwd=self.repo, | ||
| env={**os.environ, "PATH": f"{self.bin}:{os.environ['PATH']}", | ||
| "CALLS": str(self.calls), "FAIL_COMMAND": fail}, | ||
| capture_output=True, text=True) | ||
| return result, self.calls.read_text().splitlines() | ||
|
|
||
| def test_workspace_shared_and_gitlink_inputs(self): | ||
| for path in ["src/gateway/src/path with spaces.rs", "Cargo.toml", "Cargo.lock", | ||
| ".gitmodules", ".cargo/config.toml", "rust-toolchain.toml"]: | ||
| with self.subTest(path=path): | ||
| self.git("reset", "-q") | ||
| self.stage(path) | ||
| result, calls = self.hook() | ||
| self.assertEqual(result.returncode, 0, result.stderr) | ||
| self.assertEqual(calls, ["fmt --all -- --check", "clippy --locked -p homie-core --all-targets --all-features -- -D warnings"]) | ||
| self.git("reset", "-q") | ||
| self.git("update-index", "--add", "--cacheinfo", f"160000,{self.git('rev-parse', 'HEAD')},src/infra/roci") | ||
| self.assertEqual(len(self.hook()[1]), 2) | ||
|
|
||
| def test_deleted_and_renamed_rust_inputs(self): | ||
| self.stage("src/gateway/deleted.rs") | ||
| self.git("commit", "-qm", "source") | ||
| self.git("rm", "src/gateway/deleted.rs") | ||
| self.assertEqual(len(self.hook()[1]), 2) | ||
| self.git("reset", "--hard", "-q") | ||
| self.git("mv", "src/gateway/deleted.rs", "renamed.md") | ||
| self.assertEqual(len(self.hook()[1]), 2) | ||
|
|
||
| def test_embedded_markdown_modification_and_deletion_check_core(self): | ||
| self.stage('src/core/system_prompt.md') | ||
| self.git('commit', '-qm', 'embedded input') | ||
| self.stage('src/core/system_prompt.md') | ||
| (self.repo / 'src/core/system_prompt.md').write_text('changed input\n') | ||
| self.git('add', 'src/core/system_prompt.md') | ||
| self.assertEqual(len(self.hook()[1]), 2) | ||
| self.git('reset', '--hard', '-q') | ||
| self.git('rm', 'src/core/system_prompt.md') | ||
| self.assertEqual(len(self.hook()[1]), 2) | ||
|
|
||
| def test_nonzero_commands_stop_gate(self): | ||
| self.stage("Cargo.lock") | ||
| for command in ["fmt", "clippy"]: | ||
| with self.subTest(command=command): | ||
| self.assertEqual(self.hook(command)[0].returncode, 23) | ||
|
|
||
| def test_empty_and_docs_skip(self): | ||
| self.assertEqual(self.hook()[1], []) | ||
| self.stage("README.md") | ||
| self.assertEqual(self.hook()[1], []) | ||
|
|
||
| def test_existing_ci_triggers_cover_full_workspace(self): | ||
| text = (ROOT / ".github/workflows/homie-core-lint.yml").read_text() | ||
| for event in ["push:", "pull_request:"]: | ||
| block = text.split(" " + event, 1)[1].split(" workflow_dispatch:", 1)[0] | ||
| if event == "push:": | ||
| block = block.split(" pull_request:", 1)[0] | ||
| patterns = [line.strip()[2:] for line in block.splitlines() if line.strip().startswith("- ")] | ||
| for path in ["src/gateway/src/lib.rs", "src/core/src/lib.rs", "Cargo.toml", "Cargo.lock", | ||
| ".gitmodules", "src/infra/roci", "src/infra/homie-protocol/Cargo.toml", | ||
| ".cargo/config.toml", "rust-toolchain.toml"]: | ||
| self.assertTrue(any(fnmatch.fnmatchcase(path, pattern) for pattern in patterns), path) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resolve baseline Clippy failures before expanding the hook gate.
When a developer stages only a gateway change, this new pattern runs
homie-coreClippy. The documented Linux baseline already producescollapsible_matchandquestion_markdiagnostics, so-D warningsrejects that commit without a core change. Fix the baseline diagnostics before expanding the gate, or avoid making unrelated gateway commits depend on the failing check. The command-only fixtures cannot detect this failure. (github.com)