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
8 changes: 6 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,15 @@ jobs:
fi
cargo xtask install --root "$RUNNER_TEMP/install"
python3 scripts/release.py pack --version "$version" --target "$TARGET" --install "$RUNNER_TEMP/install" --output dist
- name: Verify extracted archive without Bun or checkout assets
- name: Verify extracted archives without Bun or checkout assets
env:
TARGET: ${{ matrix.target }}
run: |
version=$(sed -n 's/^version = "\([^"]*\)"/\1/p' Cargo.toml | head -1)
mkdir -p "$RUNNER_TEMP/extracted/bin"
tar -xzf dist/*.tar.gz -C "$RUNNER_TEMP/extracted/bin"
tar -xzf "dist/diffr-$version-$TARGET.tar.gz" -C "$RUNNER_TEMP/extracted/bin"
python3 xtask/tests/smoke_install.py "$RUNNER_TEMP/extracted"
python3 scripts/smoke_cli_archive.py "dist/diffr-cli-$version-$TARGET.tar.gz" "$version"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ matrix.target }}
Expand Down
3 changes: 2 additions & 1 deletion diffr-ts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ Keep these files in sync:
## Release

After tagging the matching Rust release and building with `cargo build --locked`,
run from `diffr-ts`:
run from `diffr-ts`. New pins select the CLI-only release archives; older pins
continue to use the full archives:

```sh
bun install --frozen-lockfile
Expand Down
4 changes: 3 additions & 1 deletion diffr-ts/bin/fetch.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ async function main() {
}
if (values.check) throw new Error(`${binary} is missing or not diffr ${version} (${target})`);

const asset = `diffr-${version}-${target}.tar.gz`;
const artifact = pins.artifact ?? "diffr";
if (!["diffr", "diffr-cli"].includes(artifact)) throw new Error(`unknown pinned artifact: ${artifact}`);
const asset = `${artifact}-${version}-${target}.tar.gz`;
const url = `https://github.com/devdotfast/diffr/releases/download/${version}/${asset}`;
let bytes;
try {
Expand Down
5 changes: 3 additions & 2 deletions diffr-ts/scripts/pin.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ const here = dirname(fileURLToPath(import.meta.url));
const root = join(here, "..");
const version = JSON.parse(readFileSync(join(root, "package.json"), "utf8")).version;
const targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"];
const artifact = "diffr-cli";
const sha256 = {};
for (const target of targets) {
const url = `https://github.com/devdotfast/diffr/releases/download/${version}/diffr-${version}-${target}.tar.gz`;
const url = `https://github.com/devdotfast/diffr/releases/download/${version}/${artifact}-${version}-${target}.tar.gz`;
const response = await fetch(url, { redirect: "follow", signal: AbortSignal.timeout(120_000) });
if (!response.ok) {
console.error(`pin: GET ${url} failed with ${response.status} ${response.statusText}`);
Expand All @@ -19,5 +20,5 @@ for (const target of targets) {
sha256[target] = createHash("sha256").update(Buffer.from(await response.arrayBuffer())).digest("hex");
console.log(`${target} ${sha256[target]}`);
}
writeFileSync(join(root, "pins.json"), `${JSON.stringify({ version, sha256 }, null, 2)}\n`);
writeFileSync(join(root, "pins.json"), `${JSON.stringify({ version, artifact, sha256 }, null, 2)}\n`);
console.log(`wrote pins.json for ${version}`);
20 changes: 18 additions & 2 deletions diffr-ts/src/fetch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ afterEach(() => {
for (const dir of dirs.splice(0)) rmSync(dir, { recursive: true, force: true });
});

function fixture(entry = "diffr") {
function fixture(entry = "diffr", artifact?: string) {
if (!target) throw new Error("Fetch tests require a supported release platform");
const dir = mkdtempSync(join(tmpdir(), "diffr-fetch-test-"));
dirs.push(dir);
Expand All @@ -23,7 +23,7 @@ function fixture(entry = "diffr") {
expect(Bun.spawnSync(["tar", "-czf", archive, "-C", dir, entry]).exitCode).toBe(0);
const hash = createHash("sha256").update(readFileSync(archive)).digest("hex");
const pins = join(dir, "pins.json");
writeFileSync(pins, JSON.stringify({ version, sha256: { [target]: hash } }));
writeFileSync(pins, JSON.stringify({ version, artifact, sha256: { [target]: hash } }));
const mock = join(dir, "mock.mjs");
// Intercept only the network boundary, leaving the actual CLI and tar intact.
writeFileSync(mock, `
Expand Down Expand Up @@ -119,3 +119,19 @@ test("missing option values fail clearly", () => {
expect(f.run(["--pins"]).code).toBe(1);
expect(f.run(["--into"]).code).toBe(1);
});

test("CLI-only pins download the CLI archive", () => {
const f = fixture("diffr", "diffr-cli");
const result = f.run(["--required"]);
expect(result.code, result.err).toBe(0);
expect(readFileSync(join(f.dir, "requested"), "utf8")).toBe(
`https://github.com/devdotfast/diffr/releases/download/${version}/diffr-cli-${version}-${target}.tar.gz`,
);
expect(readFileSync(join(f.into, "diffr"), "utf8")).toBe(f.content);
});

test("unknown pinned artifacts fail before downloading", () => {
const f = fixture("diffr", "other");
expect(f.run().err).toContain("unknown pinned artifact");
expect(existsSync(join(f.dir, "requested"))).toBe(false);
});
29 changes: 16 additions & 13 deletions scripts/release.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,31 @@
ROOT = Path(__file__).resolve().parent.parent


def archive_name(version, target):
return f"diffr-{version}-{target}.tar.gz"
def archive_name(version, target, artifact="diffr"):
return f"{artifact}-{version}-{target}.tar.gz"


def pack(version, target, install, output):
actual = subprocess.check_output([install / "bin/diffr", "--version"], text=True).strip()
if actual != f"diffr {version}":
raise ValueError(f"Release version mismatch: {actual}")
with tarfile.open(output / archive_name(version, target), "w:gz") as archive:
for name in ("diffr", "diffr-tui"):
archive.add(install / "bin" / name, arcname=name)
for name in ("LICENSE", "NOTICE", "tui/LICENSE", "tui/themes/LICENSE"):
archive.add(ROOT / name, arcname=name)
for artifact in ("diffr", "diffr-cli"):
with tarfile.open(output / archive_name(version, target, artifact), "w:gz") as archive:
binaries = ("diffr", "diffr-tui") if artifact == "diffr" else ("diffr",)
for name in binaries:
archive.add(install / "bin" / name, arcname=name)
for name in ("LICENSE", "NOTICE", "tui/LICENSE", "tui/themes/LICENSE"):
archive.add(ROOT / name, arcname=name)


def formula(version, output):
checksums = {}
for target in TARGETS:
name = archive_name(version, target)
checksums[target] = hashlib.sha256((output / name).read_bytes()).hexdigest()
for artifact in ("diffr", "diffr-cli"):
name = archive_name(version, target, artifact)
checksums[name] = hashlib.sha256((output / name).read_bytes()).hexdigest()
(output / "SHA256SUMS").write_text("".join(
f"{checksums[target]} {archive_name(version, target)}\n" for target in TARGETS
f"{checksum} {name}\n" for name, checksum in checksums.items()
))
url = f"https://github.com/devdotfast/diffr/releases/download/{version}/diffr-{version}"
(output / "diffr.rb").write_text(f'''class Diffr < Formula
Expand All @@ -43,18 +46,18 @@ def formula(version, output):
on_macos do
on_arm do
url "{url}-aarch64-apple-darwin.tar.gz"
sha256 "{checksums['aarch64-apple-darwin']}"
sha256 "{checksums[archive_name(version, 'aarch64-apple-darwin')]}"
end
on_intel do
url "{url}-x86_64-apple-darwin.tar.gz"
sha256 "{checksums['x86_64-apple-darwin']}"
sha256 "{checksums[archive_name(version, 'x86_64-apple-darwin')]}"
end
end

on_linux do
depends_on arch: :x86_64
url "{url}-x86_64-unknown-linux-gnu.tar.gz"
sha256 "{checksums['x86_64-unknown-linux-gnu']}"
sha256 "{checksums[archive_name(version, 'x86_64-unknown-linux-gnu')]}"
end

def install
Expand Down
38 changes: 38 additions & 0 deletions scripts/smoke_cli_archive.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""Verify a CLI-only release archive without Bun, Git, or user configuration."""
import json
from pathlib import Path
import subprocess
import sys
import tarfile
import tempfile


def check(archive_path, version):
with tempfile.TemporaryDirectory(prefix="diffr-cli-smoke-") as directory:
root = Path(directory)
with tarfile.open(archive_path) as archive:
assert set(archive.getnames()) == {
"diffr", "LICENSE", "NOTICE", "tui/LICENSE", "tui/themes/LICENSE",
}
archive.extractall(root, filter="data")
env = {"PATH": "", "HOME": str(root), "XDG_CONFIG_HOME": str(root / "config")}

def run(*args):
return subprocess.check_output(
[root / "diffr", *args], cwd=root, env=env, text=True, timeout=30,
)

assert run("--version").strip() == f"diffr {version}"
(root / "before.rs").write_text("fn main() { let x = 1; }\n")
(root / "after.rs").write_text("fn main() { let x = 2; }\n")
records = [json.loads(line) for line in run(
"--no-index", "--format", "ndjson", "--syntax", "--", "before.rs", "after.rs",
).splitlines()]
assert records[-1]["type"] == "complete", records
assert records[-1]["succeeded"] == 1 and records[-1]["failed"] == 0, records
assert any(record["type"] == "file" and "diff" in record for record in records), records
print(f"PASS CLI-only archive: {archive_path}")


if __name__ == "__main__":
check(Path(sys.argv[1]).resolve(), sys.argv[2])
Loading