Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Merge GitHub pull request #8 (claude/cool-newton-7nm8r9): Docker deployments on g1t.page behind a switch that stays off until Cloudflare's container APIs are checked live, workflows a workspace manages in its repositories, tarball and zipball downloads, refs/pull/* kept to g1t alone, framework detection for g1t.page builds, and a workflow's event payload matching GitHub's. | 1 | import assert from "node:assert/strict"; |
| 2 | import { execFileSync } from "node:child_process"; | |
| 3 | import { lstatSync, mkdtempSync, readFileSync, readlinkSync, rmSync, writeFileSync } from "node:fs"; | |
| 4 | import { tmpdir } from "node:os"; | |
| 5 | import { join } from "node:path"; | |
| 6 | import { test } from "node:test"; | |
| 7 | import { gunzipSync } from "node:zlib"; | |
| 8 | ||
| 9 | import { paxRecord, splitPath, tar, tarGz } from "./tar.ts"; | |
| 10 | ||
| 11 | const text = (value: string) => new TextEncoder().encode(value); | |
| 12 | const SHA = "4807077b296e6edbf410d55e72749d3e1170c291"; | |
| 13 | const ROOT = "acme-rocket-4807077"; | |
| 14 | const LONG = `${ROOT}/${"deep/".repeat(40)}${"n".repeat(120)}.txt`; | |
| 15 | ||
| 16 | const entries = [ | |
| 17 | { path: `${ROOT}/README.md`, data: text("hello\n") }, | |
| 18 | { path: `${ROOT}/bin/run`, data: text("#!/bin/sh\necho hi\n"), kind: "exec" as const }, | |
| 19 | { path: `${ROOT}/latest`, data: text("README.md"), kind: "symlink" as const }, | |
| 20 | { path: `${ROOT}/empty`, data: new Uint8Array() }, | |
| 21 | { path: LONG, data: text("far down\n") }, | |
| 22 | { path: `${ROOT}/${"p".repeat(90)}/${"q".repeat(90)}.md`, data: text("split\n") }, | |
| 23 | ]; | |
| 24 | ||
| 25 | /** Whether `tar` can be run here. */ | |
| A Windows runner finds the folders a build's own Wrangler config names. Resolving the config's path on Windows gave a verbatim path (\?\C:\…), in which / and .. are not read, so "directory": "../client" joined to it named nothing; drive paths now lose the prefix. The tar read-back test is skipped on Windows, whose tar takes C: for a remote host and whose files keep no Unix modes or symlinks; CI reads the archive back on Linux. | 26 | /** |
| 27 | * Why the archive is not read back here, if it is not: no tar, or Windows, | |
| 28 | * whose tar takes `C:` for a remote host and whose files keep no Unix | |
| 29 | * modes or symlinks to check. CI reads it back on Linux. | |
| 30 | */ | |
| 31 | function readBackSkip(): string | false { | |
| 32 | if (process.platform === "win32") return "Windows keeps no Unix modes or symlinks to check"; | |
| 33 | return !hasTar() && "no tar here"; | |
| 34 | } | |
| 35 | ||
| Merge GitHub pull request #8 (claude/cool-newton-7nm8r9): Docker deployments on g1t.page behind a switch that stays off until Cloudflare's container APIs are checked live, workflows a workspace manages in its repositories, tarball and zipball downloads, refs/pull/* kept to g1t alone, framework detection for g1t.page builds, and a workflow's event payload matching GitHub's. | 36 | function hasTar(): boolean { |
| 37 | try { | |
| 38 | execFileSync("tar", ["--version"], { stdio: "ignore" }); | |
| 39 | return true; | |
| 40 | } catch { | |
| 41 | return false; | |
| 42 | } | |
| 43 | } | |
| 44 | ||
| 45 | test("a pax record's length counts itself", () => { | |
| 46 | const record = new TextDecoder().decode(paxRecord("comment", text(SHA))); | |
| 47 | assert.equal(record, `52 comment=${SHA}\n`); | |
| 48 | assert.equal(record.length, 52); | |
| 49 | // Where adding the length's digits adds a digit. | |
| 50 | const value = "x".repeat(91); | |
| 51 | const near = new TextDecoder().decode(paxRecord("path", text(value))); | |
| 52 | assert.equal(Number(near.split(" ")[0]), near.length); | |
| 53 | }); | |
| 54 | ||
| 55 | test("a long path splits into ustar's prefix and name when it can", () => { | |
| 56 | assert.deepEqual(splitPath(text("a/b")), { name: text("a/b"), prefix: new Uint8Array() }); | |
| 57 | const split = splitPath(text(`${"p".repeat(90)}/${"q".repeat(90)}`))!; | |
| 58 | assert.equal(new TextDecoder().decode(split.prefix), "p".repeat(90)); | |
| 59 | assert.equal(new TextDecoder().decode(split.name), "q".repeat(90)); | |
| 60 | assert.equal(splitPath(text(`a/${"n".repeat(120)}`)), null); | |
| 61 | assert.equal(splitPath(text(`${"p".repeat(200)}/b`)), null); | |
| 62 | }); | |
| 63 | ||
| 64 | test("headers are ustar, with the commit in a pax global header", () => { | |
| 65 | const out = tar(entries, { mtime: 1_700_000_000, comment: SHA }); | |
| 66 | assert.equal(out.length % 512, 0); | |
| 67 | const decoder = new TextDecoder(); | |
| 68 | // The global header first, as git archive writes it. | |
| 69 | assert.equal(decoder.decode(out.subarray(0, 17)), "pax_global_header"); | |
| 70 | assert.equal(String.fromCharCode(out[156]!), "g"); | |
| 71 | assert.equal(decoder.decode(out.subarray(257, 263)), "ustar\0"); | |
| 72 | assert.equal(decoder.decode(out.subarray(512, 564)), `52 comment=${SHA}\n`); | |
| 73 | // Every header's checksum holds. | |
| 74 | const second = out.subarray(1024, 1536); | |
| 75 | const stored = parseInt(decoder.decode(second.subarray(148, 154)), 8); | |
| 76 | let sum = 0; | |
| 77 | for (let i = 0; i < 512; i++) sum += i >= 148 && i < 156 ? 32 : second[i]!; | |
| 78 | assert.equal(stored, sum); | |
| 79 | // Ends in two zero blocks. | |
| 80 | assert.ok(out.subarray(out.length - 1024).every((byte) => byte === 0)); | |
| 81 | }); | |
| 82 | ||
| A Windows runner finds the folders a build's own Wrangler config names. Resolving the config's path on Windows gave a verbatim path (\?\C:\…), in which / and .. are not read, so "directory": "../client" joined to it named nothing; drive paths now lose the prefix. The tar read-back test is skipped on Windows, whose tar takes C: for a remote host and whose files keep no Unix modes or symlinks; CI reads the archive back on Linux. | 83 | test("tar reads the archive back: files, folders, modes, a symlink and long paths", { skip: readBackSkip() }, async () => { |
| Merge GitHub pull request #8 (claude/cool-newton-7nm8r9): Docker deployments on g1t.page behind a switch that stays off until Cloudflare's container APIs are checked live, workflows a workspace manages in its repositories, tarball and zipball downloads, refs/pull/* kept to g1t alone, framework detection for g1t.page builds, and a workflow's event payload matching GitHub's. | 84 | const dir = mkdtempSync(join(tmpdir(), "g1t-tar-")); |
| 85 | try { | |
| 86 | const file = join(dir, "archive.tar.gz"); | |
| 87 | const archive = await tarGz(entries, { mtime: 1_700_000_000, comment: SHA }); | |
| 88 | // A gzip stream around the tar. | |
| 89 | assert.equal(gunzipSync(archive).length % 512, 0); | |
| 90 | writeFileSync(file, archive); | |
| 91 | const listed = execFileSync("tar", ["-tzf", file], { encoding: "utf8" }).trim().split("\n"); | |
| 92 | assert.equal(listed[0], `${ROOT}/`); | |
| 93 | for (const entry of entries) assert.ok(listed.includes(entry.path), entry.path); | |
| 94 | assert.ok(listed.includes(`${ROOT}/bin/`)); | |
| 95 | // One top-level folder, as actions/checkout expects. | |
| 96 | assert.deepEqual([...new Set(listed.map((path) => path.split("/")[0]))], [ROOT]); | |
| 97 | const out = join(dir, "out"); | |
| 98 | execFileSync("mkdir", [out]); | |
| 99 | execFileSync("tar", ["-xzf", file, "-C", out]); | |
| 100 | assert.equal(readFileSync(join(out, ROOT, "README.md"), "utf8"), "hello\n"); | |
| 101 | assert.equal(readFileSync(join(out, LONG), "utf8"), "far down\n"); | |
| 102 | assert.equal(readFileSync(join(out, ROOT, "empty"), "utf8"), ""); | |
| 103 | assert.equal(lstatSync(join(out, ROOT, "bin/run")).mode & 0o111, 0o111); | |
| 104 | assert.equal(lstatSync(join(out, ROOT, "README.md")).mode & 0o111, 0); | |
| 105 | assert.ok(lstatSync(join(out, ROOT, "latest")).isSymbolicLink()); | |
| 106 | assert.equal(readlinkSync(join(out, ROOT, "latest")), "README.md"); | |
| 107 | // Dated by the commit. | |
| 108 | assert.equal(Math.round(lstatSync(join(out, ROOT, "README.md")).mtimeMs / 1000), 1_700_000_000); | |
| 109 | // git's own reader finds the commit. | |
| 110 | try { | |
| 111 | const id = execFileSync("sh", ["-c", `gzip -dc "${file}" | git get-tar-commit-id`], { encoding: "utf8" }).trim(); | |
| 112 | assert.equal(id, SHA); | |
| 113 | } catch (error) { | |
| 114 | if ((error as { status?: number }).status === 127) return; | |
| 115 | throw error; | |
| 116 | } | |
| 117 | } finally { | |
| 118 | rmSync(dir, { recursive: true, force: true }); | |
| 119 | } | |
| 120 | }); |
This file's history is long; its oldest lines are credited to the oldest commit read.