ci: stop inheriting the runner's python and node
The runner executes jobs on the host rather than in a container, so a
workflow that says 'python3' or 'npm' is really saying 'whatever this
machine happens to have today'. Both of the test jobs added in c00a472
were red on the first run for exactly that reason.
uv venv with no --python takes the first interpreter it finds, which is
the host's 3.14 here. pyrogram's sync.py calls the bare
asyncio.get_event_loop() that 3.14 no longer auto-creates, so three
tests died at collection. Pinned to 3.13, which is both what uv will
fetch when the host has none and what python:3.13-slim actually builds.
npm was missing outright, and turned up an hour later as npm 12 on node
26 - the same push, minutes apart. Neither is the panel image's
node:22-alpine, so node is now installed from the official tarball the
way the other tools are, at the image's major. npm is checked by running
it rather than by looking it up, so a name that resolves to something
broken reads as not installed.
Renovate keeps NODE_VERSION in step with the Dockerfile's node: tag, and
the two are one grouped dependency: CI that tests on a different major
than it builds on is a gate that can pass over a real break.
This commit is contained in:
1 parent
a5d384a4d8
commit
4f74fe1778
5 files changed
+98
-10
No files matched your search
@@ -257,6 +257,12 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
# The pinned node, not whatever the runner has. Its system node is a
|
||||||
|
# rolling Arch package: during this very push its npm was missing
|
||||||
|
# entirely, and an hour later it was npm 12 on node 26. Both are the
|
||||||
|
# wrong major anyway — the panel image is node:22-alpine.
|
||||||
|
tools_dir="$(bash .gitea/workflows/install-ci-tools.sh node)"
|
||||||
|
export PATH="$tools_dir:$PATH"
|
||||||
cd userbot/panel/frontend
|
cd userbot/panel/frontend
|
||||||
npm ci
|
npm ci
|
||||||
npm audit --omit=dev --audit-level=high
|
npm audit --omit=dev --audit-level=high
|
||||||
@@ -286,8 +292,17 @@ jobs:
|
|||||||
# A venv in a temp dir rather than a checked-out one: the runner is
|
# A venv in a temp dir rather than a checked-out one: the runner is
|
||||||
# shared, and a leftover .venv would let a dependency the
|
# shared, and a leftover .venv would let a dependency the
|
||||||
# requirements no longer pin still satisfy an import.
|
# requirements no longer pin still satisfy an import.
|
||||||
|
#
|
||||||
|
# --python is not optional. uv otherwise takes whatever interpreter it
|
||||||
|
# finds first, and which one that is depends on the machine: this
|
||||||
|
# runner runs jobs on the host, where the only interpreter is 3.14,
|
||||||
|
# and pyrogram's sync.py calls the bare asyncio.get_event_loop() that
|
||||||
|
# 3.14 no longer auto-creates, so three tests fail at collection. The
|
||||||
|
# image is python:3.13-slim, so 3.13 is also the version worth
|
||||||
|
# testing: uv fetches a managed build of it when the host has none,
|
||||||
|
# which is what makes this job independent of the runner.
|
||||||
venv="$(mktemp -d)/venv"
|
venv="$(mktemp -d)/venv"
|
||||||
uv venv --quiet "$venv"
|
uv venv --python 3.13 --quiet "$venv"
|
||||||
uv pip install --quiet --python "$venv/bin/python" \
|
uv pip install --quiet --python "$venv/bin/python" \
|
||||||
-r userbot/panel/backend/requirements-dev.txt
|
-r userbot/panel/backend/requirements-dev.txt
|
||||||
|
|
||||||
@@ -314,6 +329,12 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
# The pinned node, not whatever the runner has. Its system node is a
|
||||||
|
# rolling Arch package: during this very push its npm was missing
|
||||||
|
# entirely, and an hour later it was npm 12 on node 26. Both are the
|
||||||
|
# wrong major anyway — the panel image is node:22-alpine.
|
||||||
|
tools_dir="$(bash .gitea/workflows/install-ci-tools.sh node)"
|
||||||
|
export PATH="$tools_dir:$PATH"
|
||||||
cd userbot/panel/frontend
|
cd userbot/panel/frontend
|
||||||
npm ci
|
npm ci
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Installs the pinned CI linters into "$TOOLS_DIR/bin" and echoes that directory
|
# Installs the pinned CI tools into "$TOOLS_DIR/bin" and echoes that directory
|
||||||
# on stdout, so callers can do:
|
# on stdout, so callers can do:
|
||||||
#
|
#
|
||||||
# export PATH="$(bash .gitea/workflows/install-ci-tools.sh kubeconform shellcheck):$PATH"
|
# export PATH="$(bash .gitea/workflows/install-ci-tools.sh kubeconform shellcheck):$PATH"
|
||||||
@@ -19,17 +19,20 @@ mkdir -p "$BIN_DIR"
|
|||||||
|
|
||||||
arch="$(uname -m)"
|
arch="$(uname -m)"
|
||||||
# Upstream projects disagree on arch spelling: kubeconform and actionlint use
|
# Upstream projects disagree on arch spelling: kubeconform and actionlint use
|
||||||
# Go names (amd64/arm64), shellcheck uses uname names (x86_64/aarch64), and
|
# Go names (amd64/arm64), shellcheck uses uname names (x86_64/aarch64), node
|
||||||
# hadolint mixes the two in a single release (x86_64 but arm64).
|
# uses neither (x64/arm64), and hadolint mixes the two in a single release
|
||||||
|
# (x86_64 but arm64).
|
||||||
case "$arch" in
|
case "$arch" in
|
||||||
x86_64 | amd64)
|
x86_64 | amd64)
|
||||||
goarch=amd64
|
goarch=amd64
|
||||||
sharch=x86_64
|
sharch=x86_64
|
||||||
|
nodearch=x64
|
||||||
hadolintarch=x86_64
|
hadolintarch=x86_64
|
||||||
;;
|
;;
|
||||||
aarch64 | arm64)
|
aarch64 | arm64)
|
||||||
goarch=arm64
|
goarch=arm64
|
||||||
sharch=aarch64
|
sharch=aarch64
|
||||||
|
nodearch=arm64
|
||||||
hadolintarch=arm64
|
hadolintarch=arm64
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
@@ -164,6 +167,32 @@ install_prettier() {
|
|||||||
ln -sfn "prettier-${PRETTIER_VERSION}/package/bin/prettier.cjs" "$BIN_DIR/prettier"
|
ln -sfn "prettier-${PRETTIER_VERSION}/package/bin/prettier.cjs" "$BIN_DIR/prettier"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
install_node() {
|
||||||
|
# npm gets checked by running it, not by looking it up: what matters is that
|
||||||
|
# it answers, so a stub, a half-removed Arch package or a name that resolves
|
||||||
|
# to something broken all have to read as "not installed". The runner's npm
|
||||||
|
# is a symlink into /usr/lib/node_modules/npm, which is exactly the kind of
|
||||||
|
# thing that disappears between runs.
|
||||||
|
if at_version node "v${NODE_VERSION}" && [ -n "$(installed_version npm)" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
# Same shape as prettier above: the tarball's bin/npm and bin/npx are links
|
||||||
|
# into lib/node_modules, so the whole tree has to survive next to them.
|
||||||
|
local dir="$BIN_DIR/node-${NODE_VERSION}"
|
||||||
|
if [ ! -x "$dir/bin/node" ]; then
|
||||||
|
rm -rf "$dir"
|
||||||
|
mkdir -p "$dir"
|
||||||
|
fetch "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-${nodearch}.tar.xz" \
|
||||||
|
"$dir/node.tar.xz"
|
||||||
|
tar -xJf "$dir/node.tar.xz" -C "$dir" --strip-components=1 "node-v${NODE_VERSION}-linux-${nodearch}"
|
||||||
|
rm -f "$dir/node.tar.xz"
|
||||||
|
fi
|
||||||
|
# Relative, so the whole tree stays valid if TOOLS_DIR is relocated.
|
||||||
|
for bin in node npm npx; do
|
||||||
|
ln -sfn "node-${NODE_VERSION}/bin/${bin}" "$BIN_DIR/${bin}"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
install_actionlint() {
|
install_actionlint() {
|
||||||
if at_version actionlint "${ACTIONLINT_VERSION}"; then
|
if at_version actionlint "${ACTIONLINT_VERSION}"; then
|
||||||
return 0
|
return 0
|
||||||
@@ -192,6 +221,7 @@ for tool in "${wanted[@]}"; do
|
|||||||
yamllint) install_yamllint ;;
|
yamllint) install_yamllint ;;
|
||||||
pip-audit) install_pip_audit ;;
|
pip-audit) install_pip_audit ;;
|
||||||
hadolint) install_hadolint ;;
|
hadolint) install_hadolint ;;
|
||||||
|
node) install_node ;;
|
||||||
uv) install_uv ;;
|
uv) install_uv ;;
|
||||||
*)
|
*)
|
||||||
echo "install-ci-tools: unknown tool: $tool" >&2
|
echo "install-ci-tools: unknown tool: $tool" >&2
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# Pinned versions of the CI linters installed by install-ci-tools.sh.
|
# Pinned versions of the CI tools installed by install-ci-tools.sh.
|
||||||
# Renovate keeps these up to date (see customManagers in renovate/renovate.json).
|
# Renovate keeps these up to date (see customManagers in renovate/renovate.json).
|
||||||
#
|
#
|
||||||
# Every version below matches what was already installed on the runner, so
|
# Every version here except NODE_VERSION matches what was already installed on
|
||||||
# pinning them changes what CI does not at all. It changes what CI does when
|
# the runner, so pinning them changes what CI does not at all. It changes what
|
||||||
# the runner is rebuilt with something else: today install-ci-tools.sh finds
|
# CI does when the runner is rebuilt with something else: today
|
||||||
# the pinned version already on PATH and installs nothing, and a runner that
|
# install-ci-tools.sh finds the pinned version already on PATH and installs
|
||||||
# drifts gets the pinned one installed over it.
|
# nothing, and a runner that drifts gets the pinned one installed over it.
|
||||||
#
|
#
|
||||||
# The renovate image version is NOT pinned here: renovate/k8s/cronjob.yaml is the
|
# The renovate image version is NOT pinned here: renovate/k8s/cronjob.yaml is the
|
||||||
# single source of truth and the workflows read the tag from it, so there is
|
# single source of truth and the workflows read the tag from it, so there is
|
||||||
@@ -24,3 +24,10 @@ PIP_AUDIT_VERSION="2.10.1"
|
|||||||
# uv builds the throwaway venv the pytest job runs in, and unpacks the PyPI
|
# uv builds the throwaway venv the pytest job runs in, and unpacks the PyPI
|
||||||
# wheels for ruff, yamllint and pip-audit.
|
# wheels for ruff, yamllint and pip-audit.
|
||||||
UV_VERSION="0.12.17"
|
UV_VERSION="0.12.17"
|
||||||
|
# node runs `npm ci` for the frontend tests and the npm audit, and it is the one
|
||||||
|
# pin here that does NOT come from the runner: the runner's system node is a
|
||||||
|
# rolling Arch package (it was node 26 with no npm at all when this was pinned),
|
||||||
|
# and the panel image is node:22-alpine. Pinned to the image's major on purpose,
|
||||||
|
# so the tree that gets tested is the tree that gets built. Renovate keeps this
|
||||||
|
# in step with the Dockerfile's node: tag via the "node runtime" group.
|
||||||
|
NODE_VERSION="22.23.3"
|
||||||
@@ -141,6 +141,14 @@ data:
|
|||||||
"datasourceTemplate": "github-tags",
|
"datasourceTemplate": "github-tags",
|
||||||
"depNameTemplate": "hadolint/hadolint"
|
"depNameTemplate": "hadolint/hadolint"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"customType": "regex",
|
||||||
|
"description": "node version the ci workflow runs npm with",
|
||||||
|
"managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"],
|
||||||
|
"matchStrings": ["(?:^|\\n)NODE_VERSION=\"(?<currentValue>[0-9.]+)\""],
|
||||||
|
"datasourceTemplate": "node",
|
||||||
|
"depNameTemplate": "node"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"customType": "regex",
|
"customType": "regex",
|
||||||
"description": "stakater/reloader chart version pinned in the deploy workflow",
|
"description": "stakater/reloader chart version pinned in the deploy workflow",
|
||||||
@@ -175,6 +183,13 @@ data:
|
|||||||
"groupName": "renovate self-update",
|
"groupName": "renovate self-update",
|
||||||
"automerge": false
|
"automerge": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "CI runs npm on the node the panel image is built from - the NODE_VERSION pin in tool-versions.env and node:22-alpine in the Dockerfile are the same dependency and move as one",
|
||||||
|
"matchPackageNames": ["node"],
|
||||||
|
"groupName": "node runtime",
|
||||||
|
"groupSlug": "node",
|
||||||
|
"automerge": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Helm chart bumps change PVC fields and admission behaviour, keep them reviewable",
|
"description": "Helm chart bumps change PVC fields and admission behaviour, keep them reviewable",
|
||||||
"matchDatasources": ["helm"],
|
"matchDatasources": ["helm"],
|
||||||
|
|||||||
@@ -130,6 +130,14 @@
|
|||||||
"datasourceTemplate": "github-tags",
|
"datasourceTemplate": "github-tags",
|
||||||
"depNameTemplate": "hadolint/hadolint"
|
"depNameTemplate": "hadolint/hadolint"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"customType": "regex",
|
||||||
|
"description": "node version the ci workflow runs npm with",
|
||||||
|
"managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"],
|
||||||
|
"matchStrings": ["(?:^|\\n)NODE_VERSION=\"(?<currentValue>[0-9.]+)\""],
|
||||||
|
"datasourceTemplate": "node",
|
||||||
|
"depNameTemplate": "node"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"customType": "regex",
|
"customType": "regex",
|
||||||
"description": "stakater/reloader chart version pinned in the deploy workflow",
|
"description": "stakater/reloader chart version pinned in the deploy workflow",
|
||||||
@@ -164,6 +172,13 @@
|
|||||||
"groupName": "renovate self-update",
|
"groupName": "renovate self-update",
|
||||||
"automerge": false
|
"automerge": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "CI runs npm on the node the panel image is built from - the NODE_VERSION pin in tool-versions.env and node:22-alpine in the Dockerfile are the same dependency and move as one",
|
||||||
|
"matchPackageNames": ["node"],
|
||||||
|
"groupName": "node runtime",
|
||||||
|
"groupSlug": "node",
|
||||||
|
"automerge": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Helm chart bumps change PVC fields and admission behaviour, keep them reviewable",
|
"description": "Helm chart bumps change PVC fields and admission behaviour, keep them reviewable",
|
||||||
"matchDatasources": ["helm"],
|
"matchDatasources": ["helm"],
|
||||||
|
|||||||
Reference in new issue
Block a user