Commit yarn 4 in the tree and install with --immutable (closes #149)
check / check (push) Successful in 16s
check / check (push) Successful in 16s
Follows sneak's ruling at #149 (comment). This repository now carries yarn 4.18.1 under .yarn/releases. The distribution's yarn only starts that file, and that file does the work. yarn.lock is converted once, and script/bootstrap installs with --immutable instead of stopping on yarn's major version. Node is the distribution's newest Node package: nodejs-current on Alpine and nodejs_latest on nix. Every Dockerfile stage that needs yarn also copies .yarnrc.yml and .yarn/releases/. The policies and both checklists say so, with the steps for converting and for a new repository. Model: opus-5-5
This commit was merged in pull request #151.
This commit is contained in:
1 parent
3066aae835
commit
9389bf0172
15 files changed
+1209
-80
No files matched your search
@@ -164,12 +164,39 @@ with your task.
|
||||
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.golangci.yml` and,
|
||||
in the same commit, set the lint phase digest to the one named in the
|
||||
`.golangci.yml` paragraph of `REPO_POLICIES.md`)
|
||||
- [ ] JS: `package.json`, `yarn.lock`, `.prettierrc`, `.prettierignore`
|
||||
(fetch from
|
||||
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierrc` and
|
||||
- [ ] JS: `package.json`, `yarn.lock`, `.yarnrc.yml`, `.yarn/releases/`,
|
||||
`.gitattributes`, `.prettierrc`, `.prettierignore` (fetch the last two
|
||||
from `https://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierrc`
|
||||
and
|
||||
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierignore`).
|
||||
Make the first `yarn.lock` with yarn 1, from a distribution that
|
||||
packages it, such as Alpine (`apk add nodejs yarn`).
|
||||
- [ ] Any repository with `yarn.lock`: that file is in yarn 4's format. If
|
||||
its second line says `yarn lockfile v1`, convert it with the steps
|
||||
below. Do them in one commit with the re-vendored scripts, because
|
||||
yarn 1 ignores `--immutable` and exits 0 on a tree with no
|
||||
`.yarnrc.yml`. Run them with any distribution's node and yarn, and use
|
||||
`yarnpkg` on Debian and Ubuntu.
|
||||
1. Read the version from `yarnPath` in
|
||||
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.yarnrc.yml`.
|
||||
2. Run `yarn set version <version>`. It writes
|
||||
`.yarn/releases/yarn-<version>.cjs`, `yarnPath` in `.yarnrc.yml`, and
|
||||
`packageManager` in `package.json`.
|
||||
3. Add `nodeLinker: node-modules` and `enableTelemetry: false` to
|
||||
`.yarnrc.yml`.
|
||||
4. Run `yarn install` once. It converts `yarn.lock`, and each package
|
||||
keeps its version and gains a `checksum`.
|
||||
5. Delete `approvedGitRepositories`, `enableScripts` and
|
||||
`npmMinimalAgeGate` from `.yarnrc.yml`. The conversion wrote them.
|
||||
They allow install scripts, any git repository and packages of any
|
||||
age, which yarn otherwise refuses.
|
||||
6. In `package.json`, name any package that must run its install script,
|
||||
as `"dependenciesMeta": { "<name>": { "built": true } }`, and run
|
||||
`yarn install` again.
|
||||
7. Add `.yarn/*` and `!.yarn/releases` above the secret patterns in
|
||||
`.gitignore` and `.dockerignore`. Docker obeys the last matching
|
||||
line, and the secret patterns must win. Add `.yarn/` to
|
||||
`.prettierignore`, and `.yarn/releases/** binary` to
|
||||
`.gitattributes`.
|
||||
8. Run `yarn install --immutable`. It must pass and change nothing.
|
||||
- [ ] Python: `pyproject.toml`
|
||||
- [ ] Docs/writing: `.prettierrc`, `.prettierignore` (same URLs as above)
|
||||
|
||||
@@ -206,11 +233,14 @@ with your task.
|
||||
installed.
|
||||
- [ ] `script/bootstrap` installs node, yarn and the node packages only when
|
||||
`yarn.lock` is in the tree. node and yarn come from the distribution when
|
||||
they are missing. With `yarn.lock` in the tree, `script/bootstrap` stops
|
||||
with a one-line message when yarn's major version is not 1.
|
||||
- [ ] A `Dockerfile` stage copies `yarn.lock` before it runs `script/bootstrap`
|
||||
only if that stage needs yarn. A stage that needs yarn starts from an
|
||||
image whose distribution packages yarn 1, such as Alpine.
|
||||
they are missing. It installs the node packages with
|
||||
`yarn install --immutable`. The distribution's yarn only starts the
|
||||
release file that `yarnPath` in `.yarnrc.yml` names, and that file does
|
||||
the work.
|
||||
- [ ] A `Dockerfile` stage that needs yarn may start from any distribution that
|
||||
packages node and yarn. It copies `package.json`, `yarn.lock`,
|
||||
`.yarnrc.yml` and `.yarn/releases/` before it runs `script/bootstrap`. A
|
||||
stage that needs no yarn copies none of them.
|
||||
- [ ] `script/bootstrap`, `script/fmt` and `script/fmt-check` run yarn by
|
||||
whichever name is present: `yarn`, or `yarnpkg` on Debian and Ubuntu. With
|
||||
neither name on `PATH`, `script/fmt` and `script/fmt-check` print one line
|
||||
@@ -247,7 +277,11 @@ with your task.
|
||||
this exception
|
||||
- [ ] node, yarn, npm, python, pip and venv come from the distribution wherever
|
||||
possible, never from nvm, pyenv, corepack or `npm install -g yarn`. If the
|
||||
distribution's version is too old, use a newer distribution
|
||||
distribution's version is too old, use a newer distribution. node means
|
||||
the distribution's newest Node package, such as `nodejs-current` on Alpine
|
||||
3.24
|
||||
- [ ] yarn's release file is in git under `.yarn/releases/`, written once by
|
||||
`yarn set version` with an exact version (repositories with `yarn.lock`)
|
||||
- [ ] Using `yarn`, not `npm` (JS projects)
|
||||
|
||||
# Directory Structure
|
||||
|
||||
@@ -179,9 +179,22 @@ Template files can be fetched from:
|
||||
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.golangci.yml` and,
|
||||
in the same commit, set the lint phase digest to the one named in the
|
||||
`.golangci.yml` paragraph of `REPO_POLICIES.md`)
|
||||
- [ ] JS: `yarn init`, `yarn add --dev prettier`. `script/bootstrap`
|
||||
installs no yarn until `yarn.lock` exists, so use yarn 1 from a
|
||||
distribution that packages it, such as Alpine (`apk add nodejs yarn`).
|
||||
- [ ] JS, or any repository that runs prettier through yarn: make the first
|
||||
`yarn.lock` with the steps below. `script/bootstrap` installs no yarn
|
||||
until `yarn.lock` exists, so run them with any distribution's node and
|
||||
yarn, such as Alpine's (`apk add nodejs-current yarn`). Use `yarnpkg`
|
||||
on Debian and Ubuntu.
|
||||
1. Read the version from `yarnPath` in
|
||||
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.yarnrc.yml`.
|
||||
2. Run `yarn set version <version>`. It writes
|
||||
`.yarn/releases/yarn-<version>.cjs`, `.yarnrc.yml` and
|
||||
`package.json`.
|
||||
3. Add `nodeLinker: node-modules` and `enableTelemetry: false` to
|
||||
`.yarnrc.yml`.
|
||||
4. Add `"private": true` to `package.json`.
|
||||
5. Run `yarn add --dev prettier`. It writes `yarn.lock`.
|
||||
6. Add `.yarn/releases/** binary` to `.gitattributes`.
|
||||
7. Run `yarn install --immutable`. It must pass and change nothing.
|
||||
- [ ] Python: `pyproject.toml`
|
||||
|
||||
## Configure script/ Entrypoints and Makefile
|
||||
@@ -198,15 +211,17 @@ are thin shims calling them. Model scripts:
|
||||
- It installs node, yarn and the node packages only when `yarn.lock` is in
|
||||
the tree. node and yarn come from that package manager when they are
|
||||
missing.
|
||||
- With `yarn.lock` in the tree, it stops with a one-line message when yarn's
|
||||
major version is not 1.
|
||||
- It installs the node packages with `yarn install --immutable`. The
|
||||
distribution's yarn only starts the release file that `yarnPath` in
|
||||
`.yarnrc.yml` names, and that file does the work.
|
||||
- A non-server repo's development environment stage runs it, with only
|
||||
system packages installed inline above it.
|
||||
- A gate phase or the build stage installs what its base image lacks, either
|
||||
inline or by running it.
|
||||
- [ ] A `Dockerfile` stage copies `yarn.lock` before it runs `script/bootstrap`
|
||||
only if that stage needs yarn. A stage that needs yarn starts from an
|
||||
image whose distribution packages yarn 1, such as Alpine.
|
||||
- [ ] A `Dockerfile` stage that needs yarn may start from any distribution that
|
||||
packages node and yarn. It copies `package.json`, `yarn.lock`,
|
||||
`.yarnrc.yml` and `.yarn/releases/` before it runs `script/bootstrap`. A
|
||||
stage that needs no yarn copies none of them.
|
||||
- [ ] `script/setup` / `make setup` — readies a fresh clone: runs `bootstrap`,
|
||||
then `install-precommit`, plus repo-specific init
|
||||
- [ ] `script/test` / `make test` —
|
||||
@@ -278,7 +293,11 @@ are thin shims calling them. Model scripts:
|
||||
that a `Dockerfile` adds itself does not get this exception
|
||||
- [ ] node, yarn, npm, python, pip and venv come from the distribution wherever
|
||||
possible, never from nvm, pyenv, corepack or `npm install -g yarn`. If the
|
||||
distribution's version is too old, use a newer distribution
|
||||
distribution's version is too old, use a newer distribution. node means
|
||||
the distribution's newest Node package, such as `nodejs-current` on Alpine
|
||||
3.24
|
||||
- [ ] yarn's release file is in git under `.yarn/releases/`, written once by
|
||||
`yarn set version` with an exact version (repositories with `yarn.lock`)
|
||||
- [ ] No unnecessary files in repo root
|
||||
- [ ] All dates written as YYYY-MM-DD
|
||||
|
||||
|
||||
+59
-29
@@ -35,7 +35,7 @@ style conventions are in separate documents:
|
||||
- Docker: `@sha256:...`
|
||||
- Go: the module hash in `go.sum`
|
||||
- Python: the package hash in `Pipfile.lock`
|
||||
- npm: the integrity hash in the lockfile
|
||||
- npm: the `checksum` in `yarn.lock`
|
||||
- GitHub Actions: `@<commit-sha>`
|
||||
|
||||
This also means never `curl | bash` to install a tool such as rustup.
|
||||
@@ -56,7 +56,8 @@ style conventions are in separate documents:
|
||||
- `apk add`
|
||||
|
||||
These come from the distribution wherever possible:
|
||||
- node
|
||||
- node, meaning the distribution's newest Node package, such as
|
||||
`nodejs-current` on Alpine 3.24
|
||||
- yarn
|
||||
- npm
|
||||
- python
|
||||
@@ -69,6 +70,9 @@ style conventions are in separate documents:
|
||||
- corepack
|
||||
- `npm install -g yarn`
|
||||
|
||||
yarn's release file is third-party code, and git pins it. `yarn set version`
|
||||
writes it to `.yarn/releases/` once, given an exact version.
|
||||
|
||||
These are not trusted, so everything from them stays pinned by hash:
|
||||
- Docker Hub
|
||||
- PyPI
|
||||
@@ -123,9 +127,32 @@ style conventions are in separate documents:
|
||||
|
||||
`script/bootstrap` installs node, yarn and the node packages only when
|
||||
`yarn.lock` is in the tree. With no `yarn.lock`, it installs no node and no
|
||||
yarn, and it does not check yarn's version. Make a repository's first
|
||||
`yarn.lock` with yarn 1, from a distribution that packages it, such as
|
||||
Alpine (`apk add nodejs yarn`).
|
||||
yarn. It installs the node packages with `yarn install --immutable`. That
|
||||
install fails when a package's `checksum` is wrong, or when `package.json`
|
||||
is out of step with `yarn.lock`.
|
||||
|
||||
Each repository commits the yarn that runs. `yarnPath` in `.yarnrc.yml`
|
||||
points at the release file in `.yarn/releases/`. The distribution's yarn
|
||||
only starts that file, and that file does the work. Alpine packages yarn 1,
|
||||
and Debian 13 packages yarn 4.1. The same committed yarn does the work on
|
||||
both. Corepack never runs, and no yarn is downloaded.
|
||||
|
||||
`.yarnrc.yml` holds these settings:
|
||||
- `yarnPath`
|
||||
- `nodeLinker: node-modules`
|
||||
- `enableTelemetry: false`, because yarn otherwise sends usage reports from
|
||||
any host that is not CI
|
||||
|
||||
By default, yarn runs no dependency's install script. A dependency that must
|
||||
run one is named in `package.json`, as
|
||||
`"dependenciesMeta": { "<name>": { "built": true } }`.
|
||||
|
||||
`.gitattributes` carries `.yarn/releases/** binary`, so diffs and merges
|
||||
treat the release file as one opaque file.
|
||||
|
||||
yarn 1 ignores `--immutable` and exits 0, so on a tree with no `.yarnrc.yml`
|
||||
the install is not frozen. A repository therefore re-vendors
|
||||
`script/bootstrap` and converts its `yarn.lock` in one commit.
|
||||
|
||||
Debian and Ubuntu name the yarn command `yarnpkg`. `script/bootstrap`,
|
||||
`script/fmt` and `script/fmt-check` each carry the same small function that
|
||||
@@ -133,18 +160,18 @@ style conventions are in separate documents:
|
||||
and `script/fmt-check` print one line saying to run `script/bootstrap`, and
|
||||
exit 1.
|
||||
|
||||
With `yarn.lock` in the tree, `script/bootstrap` stops with a one-line
|
||||
message when yarn's major version is not 1, before it installs any node
|
||||
packages. Debian 13 and Ubuntu 26.04 package yarn 4. With
|
||||
`--frozen-lockfile`, yarn 4 refuses a yarn 1 `yarn.lock`. Without that flag,
|
||||
it rewrites the file in its own format, with yarn 4 hashes in place of the
|
||||
yarn 1 ones. A host whose distribution packages yarn 4 moves to one that
|
||||
packages yarn 1, such as Alpine or Ubuntu 24.04.
|
||||
A `Dockerfile` stage that needs yarn may start from any distribution that
|
||||
packages node and yarn. It copies all of these before it runs
|
||||
`script/bootstrap`:
|
||||
- `package.json`
|
||||
- `yarn.lock`
|
||||
- `.yarnrc.yml`
|
||||
- `.yarn/releases/`
|
||||
|
||||
A `Dockerfile` stage copies `yarn.lock` before it runs `script/bootstrap`
|
||||
only if that stage needs yarn. A stage that needs yarn starts from an image
|
||||
whose distribution packages yarn 1, such as Alpine. The Python and Go stages
|
||||
below need no yarn and copy no `yarn.lock`, so they never reach the stop.
|
||||
With only `package.json` and `yarn.lock`, the install fails. Alpine's yarn 1
|
||||
then says to enable corepack; never do that, and copy the missing files
|
||||
instead. The Python and Go stages below need no yarn and copy no
|
||||
`yarn.lock`, so `script/bootstrap` installs no node and no yarn there.
|
||||
|
||||
`script/cibuild` runs the CI build, and the Gitea workflow calls it. The
|
||||
script changes to the repo root, runs `script/bootstrap`, runs
|
||||
@@ -210,8 +237,9 @@ style conventions are in separate documents:
|
||||
running `script/bootstrap`. Only system packages may be installed inline
|
||||
above it, so that check builds keep them; the `prompts` repo's own
|
||||
`Dockerfile` does this. A stage that runs `script/bootstrap` COPYs `script/`
|
||||
and the dependency manifests it needs (`package.json` + `yarn.lock`,
|
||||
`go.mod` + `go.sum`, etc.) before running it.
|
||||
and the dependency manifests it needs before running it. These are examples:
|
||||
- `package.json`, `yarn.lock`, `.yarnrc.yml` and `.yarn/releases/`
|
||||
- `go.mod` and `go.sum`
|
||||
|
||||
- **The lint and test gates run in Docker, as phases of the `Dockerfile`.**
|
||||
There is no separate lint file. `script/lint` and `script/test` each build one
|
||||
@@ -771,6 +799,7 @@ style conventions are in separate documents:
|
||||
- editor files (`.swp`, `*~`)
|
||||
- in-repo agent scratch directories (`.claude/`)
|
||||
- `node_modules/`
|
||||
- `.yarn/*`, with `!.yarn/releases` so that git keeps yarn's release file
|
||||
- the repo's own build outputs
|
||||
|
||||
Fetch the standard `.gitignore` from
|
||||
@@ -792,16 +821,16 @@ style conventions are in separate documents:
|
||||
Such a `.dockerignore` is more dangerous than a short file with no secret
|
||||
patterns at all, because it reads as solved and stops anyone looking. Give
|
||||
every depth-independent pattern the `**/` prefix, and leave only genuinely
|
||||
root-anchored entries unprefixed. Those are `.claude`, and the repo's own
|
||||
host-built binary. The binary is written `/myapp` and never `**/myapp`, which
|
||||
would also match `cmd/myapp/` and delete the package directory from the
|
||||
context. Matching is case-sensitive, and an all-caps copy of each pattern
|
||||
still misses `Server.Key`. So secret names use character ranges:
|
||||
`**/*.[kK][eE][yY]`, `**/*.[pP][eE][mM]`, and likewise for `.envrc` and the
|
||||
extensionless SSH keys. Where such a pattern also catches something the build
|
||||
needs, re-include it with a negation (`!docs/example.env`). Deleting the
|
||||
pattern reopens the exposure for every other file it covers. Fetch the
|
||||
standard `.dockerignore` from
|
||||
root-anchored entries unprefixed. Those are `.claude`, `.yarn/*`,
|
||||
`!.yarn/releases`, and the repo's own host-built binary. The binary is written
|
||||
`/myapp` and never `**/myapp`, which would also match `cmd/myapp/` and delete
|
||||
the package directory from the context. Matching is case-sensitive, and an
|
||||
all-caps copy of each pattern still misses `Server.Key`. So secret names use
|
||||
character ranges: `**/*.[kK][eE][yY]`, `**/*.[pP][eE][mM]`, and likewise for
|
||||
`.envrc` and the extensionless SSH keys. Where such a pattern also catches
|
||||
something the build needs, re-include it with a negation
|
||||
(`!docs/example.env`). Deleting the pattern reopens the exposure for every
|
||||
other file it covers. Fetch the standard `.dockerignore` from
|
||||
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.dockerignore` and extend
|
||||
it with the repo's own artifacts.
|
||||
|
||||
@@ -1105,7 +1134,8 @@ style conventions are in separate documents:
|
||||
- `Dockerfile`, `.dockerignore`
|
||||
- `.gitea/workflows/check.yml`
|
||||
- Go: `go.mod`, `go.sum`, `.golangci.yml`
|
||||
- JS: `package.json`, `yarn.lock`, `.prettierrc`, `.prettierignore`
|
||||
- JS: `package.json`, `yarn.lock`, `.yarnrc.yml`, `.yarn/releases/`,
|
||||
`.gitattributes`, `.prettierrc`, `.prettierignore`
|
||||
- Python: `pyproject.toml`
|
||||
|
||||
- Guidance for coding agents lives in one `AGENTS.md` at the repository root. It
|
||||
|
||||
Reference in new issue
Block a user