Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ jobs:
llvm_artifacts_linux_x64:
name: test (Linux x64 LLVM artifacts, no clang)
runs-on: ubuntu-24.04
timeout-minutes: 25
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
Expand All @@ -135,10 +135,10 @@ jobs:
echo "$RUNNER_TEMP/llvm-22.1.8/bin" >> "$GITHUB_PATH"
- run: pnpm install --frozen-lockfile
- run: pnpm --filter @scriptc/llvm-linux-x64-gnu build:native
- run: CC=clang AR=llvm-ar pnpm --filter @scriptc/runtime-linux-x64-gnu build:native
- uses: vercel-labs/setup-zig@v1
with:
version: 0.16.0
- run: CC=zig AR=zig pnpm --filter @scriptc/runtime-linux-x64-gnu build:native

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- run: CC=zig AR=zig pnpm --filter @scriptc/runtime-linux-x64-gnu build:native
- run: CC=zig AR=zig pnpm --filter @scriptc/runtime-linux-x64-gnu build:native

- name: Build and verify Linux x64 musl release packages
run: |
pnpm --filter @scriptc/llvm-linux-x64-musl build:native
Expand Down Expand Up @@ -173,6 +173,14 @@ jobs:
run: |
node packages/cli/dist/main.js build tests/corpus/001-hello.ts -o "$RUNNER_TEMP/hello"
test "$("$RUNNER_TEMP/hello")" = 'hello world'
- name: Debian 12 glibc 2.36 runtime-pack smoke

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- name: Debian 12 glibc 2.36 runtime-pack smoke
- name: Debian 12 glibc 2.36 runtime-pack smoke

run: |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
run: |
run: |
- ```

docker run --rm --volume "$GITHUB_WORKSPACE:/work:ro" --workdir /work node:24-bookworm-slim sh -ec '
apt-get update
apt-get install --yes --no-install-recommends clang
SCRIPTC_NO_CACHE=1 SCRIPTC_LEGACY_C_PIPELINE=0 node packages/cli/dist/main.js build tests/corpus/001-hello.ts -o /tmp/hello
test "$(/tmp/hello)" = "hello world"
'
- name: WASI helper object/runtime-pack smoke
run: pnpm test packages/cli/test/wasi-runtime-pack.test.ts

Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,13 @@ jobs:
helper: llvm-linux-x64-gnu
runtime: runtime-linux-x64-gnu
llvm_asset: LLVM-22.1.8-Linux-X64
use_zig: true
- platform: linux-arm64
runner: ubuntu-24.04-arm
helper: llvm-linux-arm64-gnu
runtime: runtime-linux-arm64-gnu
llvm_asset: LLVM-22.1.8-Linux-ARM64
use_zig: true
- platform: windows-x64
runner: windows-2022
helper: llvm-win32-x64-msvc
Expand Down
7 changes: 1 addition & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,7 @@ pnpm install && pnpm -r build # build the workspace
pnpm test:sandbox # full gate: ~4m custom image, ~9m cold managed fallback
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
```


The ordinary workspace build does not rebuild packaged native artifacts.
When changing native assembly/object emission or runtime-pack selection,
install CMake, Ninja, and the pinned LLVM 22 development package, then run the
matching `@scriptc/llvm-<platform>` and `@scriptc/runtime-<platform>`
`build:native` scripts explicitly. The macOS full test suite also needs its
generated artifacts.
The ordinary workspace build does not rebuild packaged native artifacts. When changing native assembly/object emission or runtime-pack selection, install CMake, Ninja, the pinned LLVM 22 development package, and Zig 0.16.0, then run the matching `@scriptc/llvm-<platform>` and `@scriptc/runtime-<platform>` `build:native` scripts explicitly. The macOS full test suite also needs its generated artifacts.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The ordinary workspace build does not rebuild packaged native artifacts. When changing native assembly/object emission or runtime-pack selection, install CMake, Ninja, the pinned LLVM 22 development package, and Zig 0.16.0, then run the matching `@scriptc/llvm-<platform>` and `@scriptc/runtime-<platform>` `build:native` scripts explicitly. The macOS full test suite also needs its generated artifacts.
The ordinary workspace build does not rebuild packaged native artifacts. When changing native assembly/object emission or runtime-pack selection, install CMake, Ninja, the pinned LLVM 22 development package, and Zig 0.16.0, then run the matching `@scriptc/llvm-<platform>` and `@scriptc/runtime-<platform>` `build:native` scripts explicitly. The macOS full test suite also needs its generated artifacts.


Use focused local tests while iterating, then use `pnpm test:sandbox` whenever a
full validation gate is required. It loads Sandbox configuration from the
Expand Down
5 changes: 5 additions & 0 deletions Dockerfile.sandbox
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ FROM ubuntu:24.04

ARG NODE_VERSION
ARG PNPM_VERSION=11.1.3
ARG ZIG_VERSION=0.16.0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ARG ZIG_VERSION=0.16.0
ARG ZIG_VERSION=0.16.0


ENV DEBIAN_FRONTEND=noninteractive

Expand Down Expand Up @@ -45,6 +46,10 @@ RUN curl --fail --silent --show-error --location \
&& tar -xJf /tmp/node.tar.xz --directory /usr/local --strip-components=1 \
&& rm /tmp/node.tar.xz /tmp/SHASUMS256.txt

COPY scripts/install-zig.sh /tmp/install-zig.sh
RUN sh /tmp/install-zig.sh "$ZIG_VERSION" \
&& rm /tmp/install-zig.sh

RUN npm install --global "pnpm@${PNPM_VERSION}"

WORKDIR /workspace
Expand Down
13 changes: 10 additions & 3 deletions packages/runtime-linux-arm64-gnu/scripts/build.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { dirname } from "node:path"; import { fileURLToPath } from "node:url";
if (process.platform !== "linux" || process.arch !== "arm64") { process.stdout.write("@scriptc/runtime-linux-arm64-gnu: skipped on this host\n"); process.exit(0); }
import { dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { glibcRuntimeToolchain } from "../../runtime-pack-common/scripts/glibc-toolchain.mjs";

if (process.platform !== "linux" || process.arch !== "arm64") {
process.stdout.write("@scriptc/runtime-linux-arm64-gnu: skipped on this host\n");
process.exit(0);
}
process.env.SCRIPTC_RUNTIME_PACK_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
process.env.SCRIPTC_RUNTIME_PACK_CONFIG = JSON.stringify({ platform: "linux", runtimeDefines: ["_GNU_SOURCE"], threadArgs: ["-pthread"], target: { name: "linux-arm64-gnu", llvm_triple: "aarch64-unknown-linux-gnu", architecture: "arm64", object_format: "elf", minimum_os: "glibc 2.36" }, targetArgs: ["-target", "aarch64-unknown-linux-gnu"], compileFlags: ["-ffunction-sections", "-fdata-sections"], systemLibraries: [{ name: "m", predicate: true }] });
const { minimumOs, ...toolchain } = glibcRuntimeToolchain("arm64");
process.env.SCRIPTC_RUNTIME_PACK_CONFIG = JSON.stringify({ platform: "linux", runtimeDefines: ["_GNU_SOURCE"], threadArgs: ["-pthread"], target: { name: "linux-arm64-gnu", llvm_triple: "aarch64-unknown-linux-gnu", architecture: "arm64", object_format: "elf", minimum_os: minimumOs }, ...toolchain, compileFlags: ["-ffunction-sections", "-fdata-sections"], systemLibraries: [{ name: "m", predicate: true }] });
await import("../../runtime-pack-common/scripts/build.mjs");
13 changes: 10 additions & 3 deletions packages/runtime-linux-x64-gnu/scripts/build.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { dirname } from "node:path"; import { fileURLToPath } from "node:url";
if (process.platform !== "linux" || process.arch !== "x64") { process.stdout.write("@scriptc/runtime-linux-x64-gnu: skipped on this host\n"); process.exit(0); }
import { dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { glibcRuntimeToolchain } from "../../runtime-pack-common/scripts/glibc-toolchain.mjs";

if (process.platform !== "linux" || process.arch !== "x64") {
process.stdout.write("@scriptc/runtime-linux-x64-gnu: skipped on this host\n");
process.exit(0);
}
process.env.SCRIPTC_RUNTIME_PACK_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
process.env.SCRIPTC_RUNTIME_PACK_CONFIG = JSON.stringify({ platform: "linux", runtimeDefines: ["_GNU_SOURCE"], threadArgs: ["-pthread"], target: { name: "linux-x64-gnu", llvm_triple: "x86_64-unknown-linux-gnu", architecture: "x64", object_format: "elf", minimum_os: "glibc 2.36" }, targetArgs: ["-target", "x86_64-unknown-linux-gnu"], compileFlags: ["-ffunction-sections", "-fdata-sections"], systemLibraries: [{ name: "m", predicate: true }] });
const { minimumOs, ...toolchain } = glibcRuntimeToolchain("x64");
process.env.SCRIPTC_RUNTIME_PACK_CONFIG = JSON.stringify({ platform: "linux", runtimeDefines: ["_GNU_SOURCE"], threadArgs: ["-pthread"], target: { name: "linux-x64-gnu", llvm_triple: "x86_64-unknown-linux-gnu", architecture: "x64", object_format: "elf", minimum_os: minimumOs }, ...toolchain, compileFlags: ["-ffunction-sections", "-fdata-sections"], systemLibraries: [{ name: "m", predicate: true }] });
await import("../../runtime-pack-common/scripts/build.mjs");
32 changes: 32 additions & 0 deletions packages/runtime-pack-common/scripts/artifact-policy.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { readFile, readdir } from "node:fs/promises";
import { join, relative, sep } from "node:path";

export async function assertArtifactsExcludeStrings(root, forbiddenStrings) {
if (!Array.isArray(forbiddenStrings)) {
throw new Error("runtime-pack forbidden artifact strings must be an array");
}
if (forbiddenStrings.length === 0) return;
if (forbiddenStrings.some((value) => typeof value !== "string" || value.length === 0)) {
throw new Error("runtime-pack forbidden artifact strings must be non-empty strings");
}
const forbidden = forbiddenStrings.map((value) => ({ value, bytes: Buffer.from(value) }));
const visit = async (directory) => {
for (const entry of await readdir(directory, { withFileTypes: true })) {
const path = join(directory, entry.name);
if (entry.isDirectory()) {
await visit(path);
continue;
}
if (!entry.isFile()) continue;
const bytes = await readFile(path);
const match = forbidden.find((candidate) => bytes.includes(candidate.bytes));
if (match !== undefined) {
const artifact = relative(root, path).split(sep).join("/");
throw new Error(
`runtime-pack artifact ${artifact} contains forbidden symbol family ${match.value}`,
);
}
}
};
await visit(root);
}
9 changes: 7 additions & 2 deletions packages/runtime-pack-common/scripts/build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import { execFile } from "node:child_process";
import { createHash } from "node:crypto";
import { availableParallelism, tmpdir } from "node:os";
import { copyFile, mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
import { basename, dirname, join, relative, sep } from "node:path";
import { dirname, join, relative, sep } from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
import { createRuntimePackMatrix } from "../runtime-pack-matrix.mjs";
import { assertArtifactsExcludeStrings } from "./artifact-policy.mjs";
import { createDeterministicArchive } from "./archive.mjs";
import { installRuntimePack, withBuildLock } from "./build-state.mjs";

Expand Down Expand Up @@ -35,7 +36,7 @@ async function build() {
const archiver = process.env.AR ?? config.archiver ?? "ar";
const archiverArgs = config.archiverArgs ?? [];
const commonFlags = [
...config.targetArgs, "-std=c11", ...(config.threadArgs ?? []), "-fno-math-errno", "-fno-strict-aliasing",
...config.targetArgs, ...(config.compilerFlags ?? []), "-std=c11", ...(config.threadArgs ?? []), "-fno-math-errno", "-fno-strict-aliasing",
...matrix.executable_section_elimination.compile_flags, "-Wno-deprecated-declarations", "-I", runtimeSrc,
...(config.runtimeDefines ?? []).map((define) => `-D${define}`),
];
Expand All @@ -55,6 +56,7 @@ async function build() {
for (let i = 0; i < items.length; i += width) await Promise.all(items.slice(i, i + width).map(task));
};
const archive = async (id, sources, sourceRoot, flags) => {
process.stdout.write(`building ${packageManifest.name} ${id} archive\n`);
const root = join(stagedOutputRoot, "vendor", id);
const objectRoot = join(root, "objects");
await parallel(sources, async (source) => compile(join(sourceRoot, source), join(objectRoot, source.replace(/\.c$/, ".o")), flags));
Expand All @@ -80,6 +82,7 @@ async function build() {
}
const flavors = {};
for (const [flavor, flavorSpec] of Object.entries(matrix.flavors)) {
process.stdout.write(`building ${packageManifest.name} ${flavor} runtime\n`);
const units = [];
for (const unit of matrix.runtime_units) {
const variants = [];
Expand All @@ -103,6 +106,7 @@ async function build() {
// selected, so vendor sources inherit the target's runtime defines too.
const vendorTarget = [
...config.targetArgs,
...(config.compilerFlags ?? []),
...(config.runtimeDefines ?? []).map((define) => `-D${define}`),
];
const requestedArchives = new Set(matrix.archives.map((entry) => entry.id));
Expand All @@ -112,6 +116,7 @@ async function build() {
...(requestedArchives.has("zlib") ? [await archive("zlib", zlibSources, zlib, [...vendorTarget, "-std=c11", "-Os", "-I", zlib])] : []),
...(requestedArchives.has("mbedtls") ? [await archive("mbedtls", mbedtlsSources, join(mbedtls, "library"), [...vendorTarget, "-std=c11", "-Os", "-I", join(mbedtls, "include"), "-I", join(mbedtls, "library")])] : []),
];
await assertArtifactsExcludeStrings(stagedOutputRoot, config.forbiddenArtifactStrings ?? []);
const archiveSpecs = new Map(matrix.archives.map((entry) => [entry.id, entry]));
const licensed = [[join(runtimeRoot, "LICENSE"), "artifacts/licenses/scriptc-runtime.txt", "Apache-2.0"], [join(quickjs, "LICENSE"), "artifacts/licenses/quickjs-ng.txt", "MIT"], [join(vendorRoot, "ryu", "LICENSE-Boost"), "artifacts/licenses/ryu.txt", "BSL-1.0"], [join(zlib, "LICENSE"), "artifacts/licenses/zlib.txt", "Zlib"], [join(mbedtls, "LICENSE"), "artifacts/licenses/mbedtls.txt", "Apache-2.0"]];
await Promise.all(licensed.map(async ([source, destination]) => { const output = join(buildRoot, destination); await mkdir(dirname(output), { recursive: true }); await copyFile(source, output); }));
Expand Down
23 changes: 23 additions & 0 deletions packages/runtime-pack-common/scripts/glibc-toolchain.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const GLIBC_RUNTIME_FLOOR = "2.36";

const GNU_ARCHITECTURES = {
x64: "x86_64",
arm64: "aarch64",
};

export function glibcRuntimeToolchain(architecture) {
const targetArchitecture = GNU_ARCHITECTURES[architecture];
if (targetArchitecture === undefined) {
throw new Error(`unsupported GNU runtime architecture: ${architecture}`);
}
return {
minimumOs: `glibc ${GLIBC_RUNTIME_FLOOR}`,
compiler: "zig",
compilerArgs: ["cc"],
archiver: "zig",
archiverArgs: ["ar"],
targetArgs: ["-target", `${targetArchitecture}-linux-gnu.${GLIBC_RUNTIME_FLOOR}`],
compilerFlags: ["-fno-sanitize=undefined"],
forbiddenArtifactStrings: ["__isoc23_", "__ubsan_"],
};
}
31 changes: 31 additions & 0 deletions packages/runtime-pack-common/test/artifact-policy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { expect, test } from "vitest";
import { assertArtifactsExcludeStrings } from "../scripts/artifact-policy.mjs";

test("accepts artifacts whose imports stay within the declared libc floor", async () => {
const root = await mkdtemp(join(tmpdir(), "scriptc-artifact-policy-"));
try {
await writeFile(join(root, "runtime.o"), Buffer.from("strtol\0__isoc99_sscanf\0"));
await expect(assertArtifactsExcludeStrings(root, ["__isoc23_", "__ubsan_"])).resolves.toBeUndefined();
} finally {
await rm(root, { recursive: true, force: true });
}
});

test.each([
["__isoc23_sscanf", "__isoc23_"],
["__ubsan_handle_add_overflow", "__ubsan_"],
])("rejects the %s import in nested runtime artifacts", async (symbol, family) => {
const root = await mkdtemp(join(tmpdir(), "scriptc-artifact-policy-"));
try {
await mkdir(join(root, "release", "runtime"), { recursive: true });
await writeFile(join(root, "release", "runtime", "scr_lib.o"), Buffer.from(`${symbol}\0`));
await expect(assertArtifactsExcludeStrings(root, ["__isoc23_", "__ubsan_"])).rejects.toThrow(
`runtime-pack artifact release/runtime/scr_lib.o contains forbidden symbol family ${family}`,
);
} finally {
await rm(root, { recursive: true, force: true });
}
});
24 changes: 24 additions & 0 deletions packages/runtime-pack-common/test/glibc-toolchain.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { expect, test } from "vitest";
import { glibcRuntimeToolchain } from "../scripts/glibc-toolchain.mjs";

test.each([
["x64", "x86_64-linux-gnu.2.36"],
["arm64", "aarch64-linux-gnu.2.36"],
])("pins the %s GNU runtime to its glibc floor", (architecture, target) => {
expect(glibcRuntimeToolchain(architecture)).toEqual({
minimumOs: "glibc 2.36",
compiler: "zig",
compilerArgs: ["cc"],
archiver: "zig",
archiverArgs: ["ar"],
targetArgs: ["-target", target],
compilerFlags: ["-fno-sanitize=undefined"],
forbiddenArtifactStrings: ["__isoc23_", "__ubsan_"],
});
});

test("rejects unknown GNU runtime architectures", () => {
expect(() => glibcRuntimeToolchain("riscv64")).toThrow(
"unsupported GNU runtime architecture: riscv64",
);
});
72 changes: 72 additions & 0 deletions scripts/install-zig.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/bin/sh
set -eu

version=${1:?usage: install-zig.sh <version>}
case "$version" in
*[!0-9A-Za-z.+-]*) echo "invalid Zig version: $version" >&2; exit 1 ;;
esac

case "$(uname -s)-$(uname -m)" in
Linux-x86_64) target=x86_64-linux ;;
Linux-aarch64 | Linux-arm64) target=aarch64-linux ;;
*) echo "unsupported Zig host: $(uname -s)/$(uname -m)" >&2; exit 1 ;;
esac

if command -v zig >/dev/null 2>&1 && [ "$(zig version)" = "$version" ]; then
exit 0
fi

temporary=$(mktemp -d)
trap 'rm -rf "$temporary"' EXIT HUP INT TERM

curl --fail --silent --show-error --location \
https://ziglang.org/download/index.json \
--output "$temporary/index.json"
index_size=$(wc -c <"$temporary/index.json" | tr -d ' ')
if [ "$index_size" -gt 2097152 ]; then
echo "Zig download index exceeds 2 MiB" >&2
exit 1
fi

node --input-type=module - "$version" "$target" "$temporary/index.json" >"$temporary/metadata" <<'NODE'
import { readFile } from "node:fs/promises";

const [version, target, indexPath] = process.argv.slice(2);
const release = JSON.parse(await readFile(indexPath, "utf8"))[version];
const artifact = release?.[target];
if (!artifact?.tarball || !artifact?.shasum || !artifact?.size) {
throw new Error(`Zig ${version} does not publish ${target}`);
}
const url = new URL(artifact.tarball);
if (url.protocol !== "https:" || !/^[0-9a-f]{64}$/.test(artifact.shasum)) {
throw new Error(`Zig ${version} returned invalid ${target} metadata`);
}
process.stdout.write(`${url.href}\n${artifact.shasum}\n${artifact.size}\n`);
NODE

url=$(sed -n '1p' "$temporary/metadata")
sha256=$(sed -n '2p' "$temporary/metadata")
expected_size=$(sed -n '3p' "$temporary/metadata")
archive="$temporary/zig.tar.xz"
curl --fail --silent --show-error --location "$url" --output "$archive"
printf '%s %s\n' "$sha256" "$archive" | sha256sum --check --strict -
actual_size=$(wc -c <"$archive" | tr -d ' ')
if [ "$actual_size" != "$expected_size" ]; then
echo "Zig archive size mismatch: expected $expected_size, got $actual_size" >&2
exit 1
fi

as_root() {
if [ "$(id -u)" -eq 0 ]; then
"$@"
else
sudo "$@"
fi
}

destination="/opt/zig-$version"
as_root rm -rf "$destination"
as_root mkdir -p "$destination"
as_root tar -xJf "$archive" --directory "$destination" --strip-components=1
as_root ln -sf "$destination/zig" /usr/local/bin/zig
test "$(zig version)" = "$version"
7 changes: 5 additions & 2 deletions scripts/sandbox-bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ set -eu

node_version=$(tr -d '\r\n' < .node-version)
pnpm_version=$(sed -n 's/^ARG PNPM_VERSION=//p' Dockerfile.sandbox)
if [ -z "$node_version" ] || [ -z "$pnpm_version" ]; then
echo "could not read the pinned Node or pnpm version" >&2
zig_version=$(sed -n 's/^ARG ZIG_VERSION=//p' Dockerfile.sandbox)
if [ -z "$node_version" ] || [ -z "$pnpm_version" ] || [ -z "$zig_version" ]; then
echo "could not read the pinned Node, pnpm, or Zig version" >&2
exit 1
fi

Expand Down Expand Up @@ -50,11 +51,13 @@ grep " ${node_archive}$" /tmp/node-SHASUMS256.txt \
| sed "s# ${node_archive}# /tmp/${node_archive}#" \
| sha256sum --check --strict -
sudo tar -xJf "/tmp/${node_archive}" --directory /usr/local --strip-components=1
sh scripts/install-zig.sh "$zig_version"
sudo npm install --global "pnpm@${pnpm_version}"

rm -f "/tmp/${node_archive}" /tmp/node-SHASUMS256.txt /tmp/llvm-snapshot.gpg.key /tmp/llvm-snapshot.gpg
sudo rm -rf /var/lib/apt/lists/*

node --version
pnpm --version
zig version
clang --version | sed -n '1p'
Loading
Loading