check / check (push) Waiting to run
A plain `docker build .` now stamps the version from git rather than `dev`/`0.0.0`. After `tsc`, `script/build` writes into `dist/package.json` the version `script/version` decides: `VERSION` when given, otherwise `git describe --tags --always` (the tag; or tag, commits since and short commit; or the short commit), otherwise `package.json`'s. A checkout with `.git` that yields an empty, `dev` or `unknown` version fails the build. `.dockerignore` sends `.git` but not `.git/config`, so no remote URL or credential reaches the image. `ARG VERSION` has no default, and the host scripts' version still wins. Not changed: `REPO_POLICIES.md` still says `ARG VERSION=dev` until the shared policy changes. Model: opus-5-5 Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
83 lines
2.7 KiB
Python
Executable File
83 lines
2.7 KiB
Python
Executable File
#!/bin/sh
|
|
# script/build: compile the TypeScript sources into dist/, stamp the version
|
|
# script/version prints into it, then verify that the artifacts package.json
|
|
# advertises are among the files the compiler actually wrote. tsc reports success by exit status alone and knows nothing
|
|
# about the manifest, so without this step a green build can still ship a
|
|
# package whose main, types or bin resolve to nothing. Our own extension to
|
|
# scripts-to-rule-them-all.
|
|
set -eu
|
|
|
|
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
|
|
|
# Reads package.json, requires every declared entrypoint to exist, requires
|
|
# each bin entry to have kept its shebang, and makes the bin entries
|
|
# executable: tsc copies the shebang through but not the mode bits, and an
|
|
# installed CLI has to be runnable.
|
|
verify_entrypoints() {
|
|
node -e '
|
|
const { readFileSync, statSync, chmodSync } = require("node:fs");
|
|
|
|
const pkg = JSON.parse(readFileSync("package.json", "utf-8"));
|
|
const bins = Object.values(pkg.bin ?? {});
|
|
const fail = (message) => {
|
|
console.error("build: " + message);
|
|
process.exit(1);
|
|
};
|
|
|
|
for (const declared of [pkg.main, pkg.types, ...bins]) {
|
|
if (!declared) continue;
|
|
try {
|
|
statSync(declared);
|
|
} catch {
|
|
fail("package.json declares " + declared + ", which the build did not produce");
|
|
}
|
|
console.log("build: verified " + declared);
|
|
}
|
|
|
|
for (const bin of bins) {
|
|
const firstLine = readFileSync(bin, "utf-8").split("\n")[0];
|
|
if (!firstLine.startsWith("#!")) {
|
|
fail(bin + " lost its shebang, so it cannot be executed directly");
|
|
}
|
|
chmodSync(bin, 0o755);
|
|
console.log("build: " + bin + " is executable (" + firstLine + ")");
|
|
}
|
|
'
|
|
}
|
|
|
|
# src/index.ts imports ../package.json for the version, which tsc copies to
|
|
# dist/package.json. The version script/version prints is written into that
|
|
# copy only; the repo's own package.json is left as it is.
|
|
stamp_version() {
|
|
node -e '
|
|
const { readFileSync, writeFileSync } = require("node:fs");
|
|
|
|
const pkg = JSON.parse(readFileSync("dist/package.json", "utf-8"));
|
|
pkg.version = process.argv[1];
|
|
writeFileSync("dist/package.json", JSON.stringify(pkg, null, 4) + "\n");
|
|
' "$1"
|
|
}
|
|
|
|
# Running the built CLI proves the import resolves from dist/ and reports
|
|
# the stamped version.
|
|
verify_version() {
|
|
built="$(node dist/bin/quak.js --version)"
|
|
if [ "$built" != "$1" ]; then
|
|
echo "build: dist/bin/quak.js reports $built, the build stamped $1" >&2
|
|
exit 1
|
|
fi
|
|
echo "build: dist/bin/quak.js reports version $built"
|
|
}
|
|
|
|
main() {
|
|
cd "$ROOT"
|
|
# Own line, so that a failing script/version stops the build.
|
|
version="$("$ROOT/script/version")"
|
|
yarn run tsc
|
|
stamp_version "$version"
|
|
verify_entrypoints
|
|
verify_version "$version"
|
|
}
|
|
|
|
main "$@"
|