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
51 changes: 28 additions & 23 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
#!/bin/sh
# Blocks a commit when any staged file is unformatted or fails lint.
# Blocks a commit unless the whole codebase, exactly as it will be committed, is formatted and
# passes lint.
#
# oxfmt is the formatter (.oxfmtrc.json); oxlint is the linter (.oxlintrc.json). Wired up by
# the "prepare" script in package.json (git config core.hooksPath .githooks) on npm install.
# Emergency bypass: git commit --no-verify
#
# This file is shared verbatim with jetstreamapp/soql-parser-js - keep the two copies in
# sync when changing it.
# This file is shared verbatim by jetstreamapp/sf-formula-parser, jetstreamapp/soql-parser-js,
# jetstreamapp/simple-excel and jetstreamapp/simple-xml - keep every copy in sync when changing it.

OXFMT="./node_modules/.bin/oxfmt"
OXLINT="./node_modules/.bin/oxlint"
root=$(git rev-parse --show-toplevel) || exit 1
OXFMT="$root/node_modules/.bin/oxfmt"
OXLINT="$root/node_modules/.bin/oxlint"

for bin in "$OXFMT" "$OXLINT"; do
if [ ! -x "$bin" ]; then
Expand All @@ -18,43 +20,46 @@ for bin in "$OXFMT" "$OXLINT"; do
fi
done

# Added, copied, modified and renamed paths only - deleted paths are not there to check.
staged=$(git diff --cached --name-only --diff-filter=ACMR)
# The checks run against a snapshot of the index rather than the working tree. The index is
# exactly what the commit will contain, so a file formatted on disk but never re-staged cannot
# slip through, and unstaged edits cannot block a clean commit. git points GIT_INDEX_FILE at the
# right index for `commit -a` and `commit <path>` too, and checkout-index honours it. Every
# tracked file is exported, so the whole codebase is checked, not only the files being changed;
# --ignore-skip-worktree-bits includes paths a sparse checkout or `update-index --skip-worktree`
# leaves out of the working tree.
Comment thread
paustint marked this conversation as resolved.
snapshot=$(mktemp -d "${TMPDIR:-/tmp}/pre-commit.XXXXXX") || exit 1
trap 'rm -rf "$snapshot"' EXIT
trap 'exit 1' HUP INT TERM

if [ -z "$staged" ]; then
exit 0
fi
git -C "$root" checkout-index --all --ignore-skip-worktree-bits --prefix="$snapshot/" || exit 1

# Some lint rules resolve imports, so the snapshot shares the installed dependencies.
ln -s "$root/node_modules" "$snapshot/node_modules" || exit 1

# Both tools skip what they do not handle, so the whole staged list goes to each rather than
# being filtered by extension here, which would drift as the tools gain file types.
# --no-error-on-unmatched-pattern: a staged list with nothing for that tool is not an error.
# The list is newline delimited, so convert to NUL to keep paths containing spaces intact.
check() {
printf '%s\n' "$staged" | tr '\n' '\0' | xargs -0 "$@" --no-error-on-unmatched-pattern
}
cd "$snapshot" || exit 1

# Both run even when the first fails, so one commit attempt reports everything to fix.
status=0

if ! check "$OXFMT" --check; then
if ! "$OXFMT" --check; then
cat >&2 <<'MSG'

Commit blocked: the staged files listed above are not formatted.
Commit blocked: the files listed above are not formatted.

npm run format # format the repo
git add -u # re-stage the result
git add -u # stage the result
MSG
status=1
fi

if ! check "$OXLINT"; then
if ! "$OXLINT"; then
cat >&2 <<'MSG'

Commit blocked: the staged files listed above have lint errors.
Commit blocked: the files listed above have lint errors.

npm run lint # review the full report
npm run lint:fix # apply the fixes oxlint can make automatically
git add -u # re-stage the result
git add -u # stage the result
MSG
status=1
fi
Expand Down
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ under `/Applications`, Microsoft Excel with Automation permission (`oracle/excel

**Always run `npm run format` and `npm run lint` after any change** and fix what lint reports. oxfmt settings are in
`.oxfmtrc.json`, oxlint settings in `.oxlintrc.json` (scoped overrides for `scripts/`, `oracle/`, `bench/`,
`fixtures/` and tests; prefer fixing code over adding overrides). The `.githooks/pre-commit` hook blocks commits
that fail either check. Use conventional commit messages.
`fixtures/` and tests; prefer fixing code over adding overrides). The `.githooks/pre-commit` hook runs both checks
against a snapshot of the whole index, not the working tree, and blocks the commit if either fails. Use conventional
commit messages.

## Types and build

Expand Down
Loading