Skip to content
Open
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
11 changes: 6 additions & 5 deletions src/cli.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// repo-releases CLI: `list <owner/repo> [--fixture path] [--limit N]`.
// repo-releases CLI: `list <owner/repo> [--fixture path] [--limit N] [--json]`.
// Uses a local cache over the releases seam; with --fixture it reads a canned
// upstream response instead of the network (the fast/offline path).
import { readFileSync } from "node:fs";
import { rawReleases, parseReleases } from "./github.js";
import { ReleaseCache } from "./cache.js";
import { formatTags } from "./format.js";
import { formatTags, formatJson } from "./format.js";

const cache = new ReleaseCache({ ttlMs: 60_000 });

async function list(slug, { fixture, limit }) {
async function list(slug, { fixture, limit, json }) {
const [owner, repo] = String(slug).split("/");
if (!owner || !repo) throw new Error(`expected <owner/repo>, got "${slug}"`);
const key = `${owner}/${repo}`;
Expand All @@ -20,7 +20,7 @@ async function list(slug, { fixture, limit }) {
parsed = parseReleases(raw);
cache.put(key, parsed);
}
return formatTags(parsed, limit);
return json ? formatJson(parsed, limit) : formatTags(parsed, limit);
}

function parseArgs(argv) {
Expand All @@ -29,13 +29,14 @@ function parseArgs(argv) {
for (let i = 0; i < rest.length; i++) {
if (rest[i] === "--fixture") opts.fixture = rest[++i];
else if (rest[i] === "--limit") opts.limit = Number(rest[++i]);
else if (rest[i] === "--json") opts.json = true;
}
return { cmd, slug, opts };
}

const { cmd, slug, opts } = parseArgs(process.argv.slice(2));
if (cmd !== "list" || !slug) {
process.stderr.write("usage: repo-releases list <owner/repo> [--fixture path] [--limit N]\n");
process.stderr.write("usage: repo-releases list <owner/repo> [--fixture path] [--limit N] [--json]\n");
process.exit(2);
}
list(slug, opts)
Expand Down
5 changes: 5 additions & 0 deletions src/format.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
// Render domain releases as a JSON array, newest first, top N (machine-readable).
export function formatJson(releases, limit = 10) {
return JSON.stringify(releases.slice(0, limit), null, 2);
}

// Render domain releases as aligned "tag name date" lines, newest first, top N.
export function formatTags(releases, limit = 10) {
const rows = releases.slice(0, limit);
Expand Down
10 changes: 10 additions & 0 deletions tests/e2e/json.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { test } from "node:test";
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";

test("e2e: `list --json` emits valid JSON with the newest tag", () => {
const r = spawnSync("node", ["src/cli.js", "list", "cli/cli", "--json", "--fixture", "fixtures/releases.cli-cli.json"], { encoding: "utf8" });
assert.equal(r.status, 0, "cli exits 0");
const arr = JSON.parse(r.stdout);
assert.equal(arr[0].tag, "v2.62.0", "newest tag first");
});
7 changes: 6 additions & 1 deletion tests/format.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { test } from "node:test";
import assert from "node:assert/strict";
import { parseReleases } from "../src/github.js";
import { formatTags } from "../src/format.js";
import { formatTags, formatJson } from "../src/format.js";

test("parse maps real API fields and drops drafts", () => {
const parsed = parseReleases([
Expand All @@ -15,3 +15,8 @@ test("format lists tag and name", () => {
const out = formatTags([{ tag: "v2", name: "two", publishedAt: "2024-02-01T00:00:00Z" }], 5);
assert.match(out, /v2\s+two/);
});

test("formatJson emits a JSON array of {tag,name,publishedAt}", () => {
const out = formatJson([{ tag: "v2", name: "two", publishedAt: "2024-02-01T00:00:00Z" }], 5);
assert.deepEqual(JSON.parse(out), [{ tag: "v2", name: "two", publishedAt: "2024-02-01T00:00:00Z" }]);
});
Loading