diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index f8c0ae7..6b591be 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -7,7 +7,7 @@ { "name": "lvbt-contributions", "description": "Create readable LVBT GitHub issues and pull requests.", - "version": "0.5.2", + "version": "0.5.3", "source": "./packages/cli/plugins/lvbt-contributions" } ] diff --git a/docs/reference/release-0-5-3.md b/docs/reference/release-0-5-3.md new file mode 100644 index 0000000..39db547 --- /dev/null +++ b/docs/reference/release-0-5-3.md @@ -0,0 +1,9 @@ +# Repository tooling 0.5.3 + +A repository that stores files with Git LFS no longer needs its own step in `.githooks/pre-push`. +The shared pre-push hook now uploads LFS objects with the push, as the hook `git lfs install` writes +would, once `pnpm check` passes. It does this only when `git-lfs` is installed and `.gitattributes` +has a `filter=lfs` rule, so nothing changes for a repository that doesn't use LFS. + +If you added `git lfs pre-push` to your repository's `.githooks/pre-push`, remove it after updating; +the file then matches the standard's copy again and `pnpm standards:check` stops warning about it. diff --git a/examples/basic/.claude/settings.json b/examples/basic/.claude/settings.json index 39b3306..ef6bb4e 100644 --- a/examples/basic/.claude/settings.json +++ b/examples/basic/.claude/settings.json @@ -4,7 +4,7 @@ "source": { "source": "github", "repo": "LasVegasForTransit/repository-tooling", - "ref": "v0.5.2" + "ref": "v0.5.3" } } }, diff --git a/examples/basic/package.json b/examples/basic/package.json index 31805b1..d9925f4 100644 --- a/examples/basic/package.json +++ b/examples/basic/package.json @@ -21,8 +21,8 @@ "*": "prettier --write --ignore-unknown" }, "devDependencies": { - "@lasvegasfortransit/cli": "0.5.2", - "@lasvegasfortransit/prettier-config": "0.5.2", + "@lasvegasfortransit/cli": "0.5.3", + "@lasvegasfortransit/prettier-config": "0.5.3", "lint-staged": "catalog:", "markdownlint-cli2": "catalog:", "markdownlint-rule-relative-links": "catalog:", diff --git a/examples/basic/packages/example/package.json b/examples/basic/packages/example/package.json index 1932630..0d4bf04 100644 --- a/examples/basic/packages/example/package.json +++ b/examples/basic/packages/example/package.json @@ -16,9 +16,9 @@ "test": "vitest run" }, "devDependencies": { - "@lasvegasfortransit/eslint-config": "0.5.2", - "@lasvegasfortransit/typescript-config": "0.5.2", - "@lasvegasfortransit/vitest-config": "0.5.2", + "@lasvegasfortransit/eslint-config": "0.5.3", + "@lasvegasfortransit/typescript-config": "0.5.3", + "@lasvegasfortransit/vitest-config": "0.5.3", "@types/node": "catalog:", "eslint": "catalog:", "typescript": "catalog:", diff --git a/examples/with-astro/.claude/settings.json b/examples/with-astro/.claude/settings.json index 39b3306..ef6bb4e 100644 --- a/examples/with-astro/.claude/settings.json +++ b/examples/with-astro/.claude/settings.json @@ -4,7 +4,7 @@ "source": { "source": "github", "repo": "LasVegasForTransit/repository-tooling", - "ref": "v0.5.2" + "ref": "v0.5.3" } } }, diff --git a/examples/with-astro/apps/site/package.json b/examples/with-astro/apps/site/package.json index 8f18b97..1738170 100644 --- a/examples/with-astro/apps/site/package.json +++ b/examples/with-astro/apps/site/package.json @@ -21,10 +21,10 @@ }, "devDependencies": { "@astrojs/check": "catalog:", - "@lasvegasfortransit/eslint-config": "0.5.2", - "@lasvegasfortransit/playwright-config": "0.5.2", - "@lasvegasfortransit/typescript-config": "0.5.2", - "@lasvegasfortransit/vitest-config": "0.5.2", + "@lasvegasfortransit/eslint-config": "0.5.3", + "@lasvegasfortransit/playwright-config": "0.5.3", + "@lasvegasfortransit/typescript-config": "0.5.3", + "@lasvegasfortransit/vitest-config": "0.5.3", "@playwright/test": "catalog:", "@types/node": "catalog:", "eslint": "catalog:", diff --git a/examples/with-astro/package.json b/examples/with-astro/package.json index 1c75559..f49a276 100644 --- a/examples/with-astro/package.json +++ b/examples/with-astro/package.json @@ -23,8 +23,8 @@ "*": "prettier --write --ignore-unknown" }, "devDependencies": { - "@lasvegasfortransit/cli": "0.5.2", - "@lasvegasfortransit/prettier-config": "0.5.2", + "@lasvegasfortransit/cli": "0.5.3", + "@lasvegasfortransit/prettier-config": "0.5.3", "lint-staged": "catalog:", "markdownlint-cli2": "catalog:", "markdownlint-rule-relative-links": "catalog:", diff --git a/examples/with-vite-react/.claude/settings.json b/examples/with-vite-react/.claude/settings.json index 39b3306..ef6bb4e 100644 --- a/examples/with-vite-react/.claude/settings.json +++ b/examples/with-vite-react/.claude/settings.json @@ -4,7 +4,7 @@ "source": { "source": "github", "repo": "LasVegasForTransit/repository-tooling", - "ref": "v0.5.2" + "ref": "v0.5.3" } } }, diff --git a/examples/with-vite-react/apps/app/package.json b/examples/with-vite-react/apps/app/package.json index 9e209af..680e16e 100644 --- a/examples/with-vite-react/apps/app/package.json +++ b/examples/with-vite-react/apps/app/package.json @@ -17,10 +17,10 @@ "react-dom": "catalog:" }, "devDependencies": { - "@lasvegasfortransit/eslint-config": "0.5.2", - "@lasvegasfortransit/playwright-config": "0.5.2", - "@lasvegasfortransit/typescript-config": "0.5.2", - "@lasvegasfortransit/vitest-config": "0.5.2", + "@lasvegasfortransit/eslint-config": "0.5.3", + "@lasvegasfortransit/playwright-config": "0.5.3", + "@lasvegasfortransit/typescript-config": "0.5.3", + "@lasvegasfortransit/vitest-config": "0.5.3", "@playwright/test": "catalog:", "@tailwindcss/vite": "catalog:", "@types/node": "catalog:", diff --git a/examples/with-vite-react/package.json b/examples/with-vite-react/package.json index dc1bbf2..efece57 100644 --- a/examples/with-vite-react/package.json +++ b/examples/with-vite-react/package.json @@ -23,8 +23,8 @@ "*": "prettier --write --ignore-unknown" }, "devDependencies": { - "@lasvegasfortransit/cli": "0.5.2", - "@lasvegasfortransit/prettier-config": "0.5.2", + "@lasvegasfortransit/cli": "0.5.3", + "@lasvegasfortransit/prettier-config": "0.5.3", "lint-staged": "catalog:", "markdownlint-cli2": "catalog:", "markdownlint-rule-relative-links": "catalog:", diff --git a/package.json b/package.json index 20900d9..42d9499 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "lvbt-repository-tooling", "private": true, - "version": "0.5.2", + "version": "0.5.3", "type": "module", "description": "Source of truth for the LVBT repository standard: the shared @lasvegasfortransit packages and the example repositories create-turbo copies.", "packageManager": "pnpm@11.25.0", diff --git a/packages/cli/hooks/pre-push.sh b/packages/cli/hooks/pre-push.sh index bb3a6e3..f8b7fe0 100755 --- a/packages/cli/hooks/pre-push.sh +++ b/packages/cli/hooks/pre-push.sh @@ -9,5 +9,12 @@ while IFS= read -r variable; do done </dev/null 2>&1 && grep -qs 'filter=lfs' "$ROOT/.gitattributes"; then + printf '%s\n' "$REFS" | git lfs pre-push "$@" +fi diff --git a/packages/cli/package.json b/packages/cli/package.json index 54bc966..37e3ad8 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@lasvegasfortransit/cli", - "version": "0.5.2", + "version": "0.5.3", "description": "The lvbt command every LVBT repository runs for bootstrap, preflight, and deploy, plus the production platform setup, the shared git hooks, and the lvbt-contributions agent plugin.", "license": "MIT", "type": "module", diff --git a/packages/cli/plugins/lvbt-contributions/.claude-plugin/plugin.json b/packages/cli/plugins/lvbt-contributions/.claude-plugin/plugin.json index ce879c6..bede3d2 100644 --- a/packages/cli/plugins/lvbt-contributions/.claude-plugin/plugin.json +++ b/packages/cli/plugins/lvbt-contributions/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "lvbt-contributions", - "version": "0.5.2", + "version": "0.5.3", "description": "Create readable LVBT GitHub issues and pull requests through the organization workflow.", "author": { "name": "Las Vegans for Better Transit", diff --git a/packages/cli/plugins/lvbt-contributions/.codex-plugin/plugin.json b/packages/cli/plugins/lvbt-contributions/.codex-plugin/plugin.json index f18002b..e837208 100644 --- a/packages/cli/plugins/lvbt-contributions/.codex-plugin/plugin.json +++ b/packages/cli/plugins/lvbt-contributions/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "lvbt-contributions", - "version": "0.5.2", + "version": "0.5.3", "description": "Create readable LVBT GitHub issues and pull requests through the organization workflow.", "author": { "name": "Las Vegans for Better Transit", diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index d1d9c1b..b6b4edf 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -1,6 +1,6 @@ { "name": "@lasvegasfortransit/eslint-config", - "version": "0.5.2", + "version": "0.5.3", "description": "The ESLint configurations every LVBT repository uses.", "license": "MIT", "type": "module", diff --git a/packages/playwright-config/package.json b/packages/playwright-config/package.json index 2d45257..151991d 100644 --- a/packages/playwright-config/package.json +++ b/packages/playwright-config/package.json @@ -1,6 +1,6 @@ { "name": "@lasvegasfortransit/playwright-config", - "version": "0.5.2", + "version": "0.5.3", "description": "The Playwright configuration every LVBT repository spreads into its own: end-to-end tests under tests/e2e, desktop and mobile projects, traces on failure.", "license": "MIT", "type": "module", diff --git a/packages/prettier-config/package.json b/packages/prettier-config/package.json index 8502cee..d9c38a3 100644 --- a/packages/prettier-config/package.json +++ b/packages/prettier-config/package.json @@ -1,6 +1,6 @@ { "name": "@lasvegasfortransit/prettier-config", - "version": "0.5.2", + "version": "0.5.3", "description": "The Prettier configuration every LVBT repository uses.", "license": "MIT", "type": "module", diff --git a/packages/typescript-config/package.json b/packages/typescript-config/package.json index a39afe9..4e431c0 100644 --- a/packages/typescript-config/package.json +++ b/packages/typescript-config/package.json @@ -1,6 +1,6 @@ { "name": "@lasvegasfortransit/typescript-config", - "version": "0.5.2", + "version": "0.5.3", "description": "TypeScript configurations every LVBT repository extends.", "license": "MIT", "repository": { diff --git a/packages/vitest-config/package.json b/packages/vitest-config/package.json index f3edfbe..3aeb4d0 100644 --- a/packages/vitest-config/package.json +++ b/packages/vitest-config/package.json @@ -1,6 +1,6 @@ { "name": "@lasvegasfortransit/vitest-config", - "version": "0.5.2", + "version": "0.5.3", "description": "The Vitest configuration every LVBT repository spreads into its own.", "license": "MIT", "type": "module", diff --git a/packages/web-platform/package.json b/packages/web-platform/package.json index e7ed44a..de966a8 100644 --- a/packages/web-platform/package.json +++ b/packages/web-platform/package.json @@ -1,6 +1,6 @@ { "name": "@lasvegasfortransit/web-platform", - "version": "0.5.2", + "version": "0.5.3", "description": "Provider-neutral deployment, provisioning, and validation primitives for LVBT web repositories.", "license": "MIT", "type": "module", diff --git a/tests/hooks.test.mjs b/tests/hooks.test.mjs index 1647289..b8229b2 100644 --- a/tests/hooks.test.mjs +++ b/tests/hooks.test.mjs @@ -1,5 +1,5 @@ import assert from 'node:assert/strict'; -import { chmod, mkdir, mkdtemp, readFile, writeFile } from 'node:fs/promises'; +import { chmod, mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import path from 'node:path'; import { spawnSync } from 'node:child_process'; @@ -193,6 +193,44 @@ test('the pre-push hook clears repository-local Git variables before checks', as assert.equal(await readFile(resultFile, 'utf8'), 'unset|unset'); }); +test('the pre-push hook uploads Git LFS objects after the check, only where LFS is used', async (t) => { + const directory = await mkdtemp(path.join(tmpdir(), 'lvbt-pre-push-lfs-')); + t.after(() => rm(directory, { recursive: true, force: true })); + const repository = path.join(directory, 'repository'); + const log = path.join(directory, 'log'); + spawnSync('git', ['init', '--quiet', repository]); + await writeFile(path.join(directory, 'pnpm'), `#!/bin/sh\necho check >> "${log}"\n`); + await writeFile(path.join(directory, 'git-lfs'), `#!/bin/sh\necho "lfs $* $(cat)" >> "${log}"\n`); + await chmod(path.join(directory, 'pnpm'), 0o755); + await chmod(path.join(directory, 'git-lfs'), 0o755); + const push = () => + spawnSync( + path.join(repositoryRoot, 'packages/cli/hooks/pre-push.sh'), + ['origin', 'git@example.org:repo.git'], + { + cwd: repository, + encoding: 'utf8', + input: 'refs/heads/main abc refs/heads/main def\n', + env: { ...process.env, PATH: `${directory}:${process.env.PATH}` }, + }, + ); + + assert.equal(push().status, 0); + assert.equal(await readFile(log, 'utf8'), 'check\n', 'no LFS upload without an LFS rule'); + + await writeFile( + path.join(repository, '.gitattributes'), + '*.png filter=lfs diff=lfs merge=lfs -text\n', + ); + await rm(log); + const result = push(); + assert.equal(result.status, 0, result.stderr); + assert.equal( + await readFile(log, 'utf8'), + 'check\nlfs pre-push origin git@example.org:repo.git refs/heads/main abc refs/heads/main def\n', + ); +}); + test('the shared and source repository hooks pass ShellCheck', (context) => { const available = spawnSync('shellcheck', ['--version'], { encoding: 'utf8' }); if (available.error?.code === 'ENOENT') {