feat(npm): mkS390xNpmPublishMulti — multi-version npm publish per tag (#192)

Mirrors mkPyPiWheelPublishMulti for npm: publishes a fixed {version,file,
distTag?} list, each staged into its own dir and npm-published with its
dist-tag (idempotent). file may be a .node or a plain binary; packageJson
declares main-vs-bin. Unblocks nextjs-swc (next15 dist-tag) + sentry-cli.
Shared parity_npm_publish_dir helper added.
This commit is contained in:
Oleks
2026-06-02 08:56:38 +03:00
parent cda7a190c0
commit af64a8ea4c
4 changed files with 150 additions and 0 deletions
+116
View File
@@ -355,6 +355,121 @@ let
};
};
# =========================================================================
# s390x npm native addon / binary, MULTI-version (cluster #192). Publishes a
# fixed list of { version; file; distTag? } per tag — the behaviour repos like
# nextjs-swc (16.1.6 @latest + 15.2.0 @next15) and sentry-cli (3.2.2 + 2.38.2)
# rely on. `file` may be a .node addon OR a plain binary; `fileName` is its name
# inside the package and `packageJson` (with a $VERSION the stage heredoc
# expands) declares the shape (a `main` for an addon, a `bin` for an exe). Each
# version is staged into its own dir and published with its dist-tag; idempotent
# (npm "already exists" == success). No dev-tag guard: explicit fixed version
# list, gated by the dry-run default.
# Args: { pname, versions = [ { version; file; distTag ? "latest"; } ],
# fileName, packageJson, arch ? "s390x", registry* }
# =========================================================================
mkS390xNpmPublishMulti =
{
pname,
versions,
fileName,
packageJson,
arch ? "s390x",
registryHost ? "git.oleks.space",
registryOwner ? "oleks",
passEntry ? "infra/gitea/personal_access_token_packages_rw",
}:
let
head = preamble { inherit registryHost registryOwner passEntry; };
nVersions = builtins.length versions;
# One stage block per version: its own dir, the file, a version-stamped
# package.json (the <<EOF heredoc expands the per-version $VERSION), and a
# .disttag marker the publish loop reads.
stageEntries = lib.concatMapStringsSep "\n" (e: ''
vd="$d/${e.version}"
mkdir -p "$vd"
install -m 0755 ${lib.escapeShellArg "${e.file}"} "$vd/${fileName}"
printf '%s' ${lib.escapeShellArg (e.distTag or "latest")} >"$vd/.disttag"
VERSION=${lib.escapeShellArg e.version}
cat >"$vd/package.json" <<PARITY_PKGJSON
${packageJson}
PARITY_PKGJSON
'') versions;
stageText = ''
${head}
echo " staging ${pname} (${toString nVersions} versions, ${arch}, no registry contact)"
d="$(parity_stage_reset ${lib.escapeShellArg arch})"
${stageEntries}
printf 'npm-multi %s %s\n' ${lib.escapeShellArg pname} "${toString nVersions}" >"$d/.parity-meta"
echo " staged ${toString nVersions} version dir(s) under $d"
'';
stage = pkgs.writeShellApplication {
name = "stage-${arch}";
runtimeInputs = baseInputs ++ [ pkgs.nodejs ];
text = stageText;
};
dryList = ''
echo "[dry-run] would 'npm publish' these ${pname} versions -> https://$PARITY_REGISTRY_HOST/api/packages/$PARITY_REGISTRY_OWNER/npm/:"
for vd in "$d"/*/; do
[ -f "$vd/package.json" ] || continue
echo " - $(basename "$vd") (dist-tag $(cat "$vd/.disttag"))"
done
echo "[dry-run] re-run with --publish / PUBLISH=1 to push."
'';
uploadLoop = ''
tok="$(parity_resolve_token)"
parity_registry_preflight
rc=0
for vd in "$d"/*/; do
[ -f "$vd/package.json" ] || continue
parity_npm_publish_dir "$vd" "$tok" "$(cat "$vd/.disttag")" || rc=1
done
exit "$rc"
'';
publish = pkgs.writeShellApplication {
name = "publish-${arch}";
runtimeInputs = baseInputs ++ [ pkgs.nodejs ];
text = ''
${head}
parity_parse_args "Build + publish all ${toString nVersions} ${pname} ${arch} npm versions to the Gitea npm registry" "$@"
${lib.getExe stage}
d="$(parity_stage_path ${lib.escapeShellArg arch})"
if [ "$PARITY_PUBLISH" != "1" ]; then
${dryList}
exit 0
fi
${uploadLoop}
'';
};
pushStaged = pkgs.writeShellApplication {
name = "push-staged";
runtimeInputs = baseInputs ++ [ pkgs.nodejs ];
text = ''
${head}
parity_parse_args "Replay the staged ${pname} ${arch} npm versions from .parity-stage" "$@"
d="$(parity_stage_path ${lib.escapeShellArg arch})"
if ! find "$d" -mindepth 2 -name package.json -print -quit | grep -q .; then
echo "BLOCKER(no-stage): no staged versions under $d run 'nix run .#stage-${arch}' first." >&2
exit 1
fi
if [ "$PARITY_PUBLISH" != "1" ]; then
${dryList}
exit 0
fi
${uploadLoop}
'';
};
in
{
"stage-${arch}" =
mkApp stage "Stage all ${nVersions} ${pname} ${arch} npm versions into .parity-stage (no registry contact).";
"publish-${arch}" =
mkApp publish "Stage + publish all ${pname} ${arch} npm versions (dry-run by default; --publish to push).";
"publish" = mkApp publish "Publish all ${pname} ${arch} npm versions (${arch} only for this repo).";
"push-staged" =
mkApp pushStaged "Replay the staged ${pname} ${arch} npm versions from .parity-stage (dry-run by default; --publish to push).";
};
# =========================================================================
# Generic binary (single-arch). Stage a built binary, PUT it to the Gitea
# generic registry (idempotent: 201 created / 409 exists).
@@ -857,6 +972,7 @@ in
mkPyPiWheelPublish
mkPyPiWheelPublishMulti
mkS390xNpmPublish
mkS390xNpmPublishMulti
mkGenericBinaryPublish
mkNix2ContainerPublish
mkGoBinaryPublish