1 Commits
Author SHA1 Message Date
sneak 3dc8b419b8 Install node, yarn and python from the distribution (closes #146)
check / check (push) Canceled after 0s
Follows sneak's ruling at
#146 (comment). Runtimes
and the installer tools bundled with python come from the distribution,
with no version and no hash. Third-party packages stay pinned by hash.

script/bootstrap drops nvm and corepack. It installs node, yarn and the
node packages only when yarn.lock is in the tree, and stops there when
yarn's major version is not 1. This repository's Dockerfile starts from
Alpine 3.24. The Python example starts from debian:trixie-slim, keeps
pipenv at $HOME/.pipenv on a host and in an image, and sets
PIP_ONLY_BINARY=:all:, so pip downloads no build tools without a hash
check.

Model: opus-5-5
2026-11-04 07:26:19 +00:00
4 changed files with 32 additions and 23 deletions

No files matched your search

+2 -1
View File
@@ -8,7 +8,8 @@ last_modified: 2026-11-04
1. Use yarn for package management, avoid using npm. 1. Use yarn for package management, avoid using npm.
1. Install node from the distribution, in whatever version it packages. 1. Install node from the distribution. If its version is too old, use a newer
distribution.
1. Use `prettier` for code formatting, with four spaces for indentation. 1. Use `prettier` for code formatting, with four spaces for indentation.
+7 -7
View File
@@ -88,12 +88,12 @@ with your task.
`pip install --require-hashes`. It installs the packages from `Pipfile.lock` `pip install --require-hashes`. It installs the packages from `Pipfile.lock`
with `pipenv install --deploy`, plus `--dev` when the tests need the with `pipenv install --deploy`, plus `--dev` when the tests need the
development packages. It sets `PIP_ONLY_BINARY=:all:` for both installs, so development packages. It sets `PIP_ONLY_BINARY=:all:` for both installs, so
a package with no wheel for the image stops the install. It installs no a package with no wheel for the image stops the install. In that step it
Python there, because the tests use the distribution's, which a step above installs no Python, because a step above it already did. `Pipfile` names no
it installs. `Pipfile` names no `python_version`, because `--deploy` stops `python_version`, because `--deploy` stops when the distribution's Python
when the distribution's Python differs from it. The tests run after differs from it. The tests run after `ARG CACHEBUST`, and every other stage
`ARG CACHEBUST`, and every other stage installs its Python packages after installs its Python packages after it, as in the Python `test` phase example
it, as in the Python `test` phase example in `REPO_POLICIES.md` in `REPO_POLICIES.md`
- [ ] The gate phases invoke their tools directly, never through `make lint` or - [ ] The gate phases invoke their tools directly, never through `make lint` or
`script/test` — those are themselves a `docker build` and would recurse `script/test` — those are themselves a `docker build` and would recurse
@@ -208,7 +208,7 @@ with your task.
with a one-line message when yarn's major version is not 1. with a one-line message when yarn's major version is not 1.
- [ ] A `Dockerfile` stage copies `yarn.lock` before it runs `script/bootstrap` - [ ] 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 only if that stage needs yarn. A stage that needs yarn starts from an
image whose distribution packages yarn 1, which today means Alpine. image whose distribution packages yarn 1, such as Alpine.
- [ ] `script/bootstrap`, `script/fmt` and `script/fmt-check` run yarn by - [ ] `script/bootstrap`, `script/fmt` and `script/fmt-check` run yarn by
whichever name is present: `yarn`, or `yarnpkg` on Debian and Ubuntu. With whichever name is present: `yarn`, or `yarnpkg` on Debian and Ubuntu. With
neither name on `PATH`, they print one line saying to run neither name on `PATH`, they print one line saying to run
+7 -7
View File
@@ -156,12 +156,12 @@ Template files can be fetched from:
the packages from `Pipfile.lock` with `pipenv install --deploy`, plus the packages from `Pipfile.lock` with `pipenv install --deploy`, plus
`--dev` when the tests need the development packages. It sets `--dev` when the tests need the development packages. It sets
`PIP_ONLY_BINARY=:all:` for both installs, so a package with no wheel `PIP_ONLY_BINARY=:all:` for both installs, so a package with no wheel
for the image stops the install. It installs no Python there, because for the image stops the install. In that step it installs no Python,
the tests use the distribution's, which a step above it installs. because a step above it already did. `Pipfile` names no
`Pipfile` names no `python_version`, because `--deploy` stops when the `python_version`, because `--deploy` stops when the distribution's
distribution's Python differs from it. The tests run after Python differs from it. The tests run after `ARG CACHEBUST`, and every
`ARG CACHEBUST`, and every other stage installs its Python packages other stage installs its Python packages after it, as in the Python
after it, as in the Python `test` phase example in `REPO_POLICIES.md` `test` phase example in `REPO_POLICIES.md`
- Server: the final stage builds and runs the application - Server: the final stage builds and runs the application
- Non-server: the final stage brings up the dev environment - Non-server: the final stage brings up the dev environment
@@ -204,7 +204,7 @@ are thin shims calling them. Model scripts:
inline or by running it. inline or by running it.
- [ ] A `Dockerfile` stage copies `yarn.lock` before it runs `script/bootstrap` - [ ] 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 only if that stage needs yarn. A stage that needs yarn starts from an
image whose distribution packages yarn 1, which today means Alpine. image whose distribution packages yarn 1, such as Alpine.
- [ ] `script/setup` / `make setup` — readies a fresh clone: runs `bootstrap`, - [ ] `script/setup` / `make setup` — readies a fresh clone: runs `bootstrap`,
then `install-precommit`, plus repo-specific init then `install-precommit`, plus repo-specific init
- [ ] `script/test` / `make test` — - [ ] `script/test` / `make test` —
+16 -8
View File
@@ -135,13 +135,14 @@ style conventions are in separate documents:
packages. Debian 13 and Ubuntu 26.04 package yarn 4. With packages. Debian 13 and Ubuntu 26.04 package yarn 4. With
`--frozen-lockfile`, yarn 4 refuses a yarn 1 `yarn.lock`. Without that flag, `--frozen-lockfile`, yarn 4 refuses a yarn 1 `yarn.lock`. Without that flag,
it rewrites the file in its own format, with its own hashes in place of it rewrites the file in its own format, with its own hashes in place of
these. Alpine and Ubuntu 24.04 package yarn 1, so a host uses one of those. these. A host whose distribution packages yarn 4 moves to one that packages
yarn 1, such as Alpine or Ubuntu 24.04.
A `Dockerfile` stage copies `yarn.lock` before it runs `script/bootstrap` 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 only if that stage needs yarn. A stage that needs yarn starts from an image
whose distribution packages yarn 1, which today means Alpine. The Python and whose distribution packages yarn 1, such as Alpine. The Python and Go stages
Go stages below need no yarn, so they copy no `yarn.lock` before that step below need no yarn, so they copy no `yarn.lock` before that step and never
and never reach the stop. reach the stop.
`script/cibuild` runs the CI build, and the Gitea workflow calls it. The `script/cibuild` runs the CI build, and the Gitea workflow calls it. The
script changes to the repo root, runs `script/bootstrap`, runs script changes to the repo root, runs `script/bootstrap`, runs
@@ -552,9 +553,9 @@ style conventions are in separate documents:
A cache mount is shared by every build on the host, and nothing checks its A cache mount is shared by every build on the host, and nothing checks its
files again. files again.
A Python repository's `script/bootstrap` adds these lines. The `ensurepip` A Python repository's `script/bootstrap` adds these lines in `main`, above
test fails when python is missing, and also when Debian's venv package is the last `echo`. The `ensurepip` test fails when python is missing, and also
missing: when Debian's venv package is missing:
```sh ```sh
if [ ! -x "$HOME/.pipenv/bin/pip" ]; then if [ ! -x "$HOME/.pipenv/bin/pip" ]; then
@@ -568,6 +569,13 @@ style conventions are in separate documents:
"$HOME/.pipenv/bin/pipenv" install --dev --deploy "$HOME/.pipenv/bin/pipenv" install --dev --deploy
``` ```
Make `script/requirements.txt` once with
`echo pipenv | pip-compile --generate-hashes --allow-unsafe -o script/requirements.txt -`,
from Alpine's `py3-pip-tools` package, because Debian and Ubuntu do not
package it. Make the first `Pipfile.lock` with
`$HOME/.pipenv/bin/pipenv lock`, after the `pip install` line has installed
pipenv.
In the example below, `script/bootstrap` finds `make`, `git`, python and In the example below, `script/bootstrap` finds `make`, `git`, python and
venv already installed. The example copies no `yarn.lock`, so venv already installed. The example copies no `yarn.lock`, so
`script/bootstrap` installs no node and no yarn. `script/bootstrap` installs no node and no yarn.
@@ -583,7 +591,7 @@ style conventions are in separate documents:
# stay in their own layer when script/ changes. # stay in their own layer when script/ changes.
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates make git \ && apt-get install -y --no-install-recommends ca-certificates make git \
python3 python3-pip python3-venv \ python3 python3-venv \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# script/bootstrap installs pipenv in a virtual environment at # script/bootstrap installs pipenv in a virtual environment at
# $HOME/.pipenv, and the tests below run that pipenv. # $HOME/.pipenv, and the tests below run that pipenv.