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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ target/
.release-version
binaries/
release-assets/
# written from README.md at release (scripts/release.mjs stamp)
npm/skillpouch/README.md
10 changes: 0 additions & 10 deletions npm/skillpouch/README.md

This file was deleted.

27 changes: 26 additions & 1 deletion scripts/release.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
// build jobs:
//
// node scripts/release.mjs verify <version> check the version, note it in .release-version
// node scripts/release.mjs stamp <version> write it into Cargo.toml, Cargo.lock and npm/skillpouch
// node scripts/release.mjs stamp <version> write it into Cargo.toml, Cargo.lock and npm/skillpouch,
// with README.md as the npm package's README
// node scripts/release.mjs publish <version> publish the platform packages built from $BINARIES
//
// The version is planned once (a dry run), the binaries are built with it, and
Expand Down Expand Up @@ -67,6 +68,26 @@ export function stampPackageJson(text, version) {
return `${JSON.stringify(stamped, null, '\t')}\n`;
}

const REPOSITORY = 'skillpouch/skillpouch-cli';
const IMAGE = /\.(png|jpe?g|gif|webp|svg)$/i;

/**
* The repository README for the npm page: npm doesn't know where relative
* links point, so they go to this release's tag on GitHub.
*/
export function npmReadme(text, version) {
const absolute = (link) => {
if (/^([a-z][a-z0-9+.-]*:|#|\/\/)/i.test(link)) return link;
const path = link.replace(/^\.?\//, '');
return IMAGE.test(path.split(/[?#]/)[0])
? `https://raw.githubusercontent.com/${REPOSITORY}/v${version}/${path}`
: `https://github.com/${REPOSITORY}/blob/v${version}/${path}`;
};
return text
.replace(/\b(src|href)="([^"]+)"/g, (_, attribute, link) => `${attribute}="${absolute(link)}"`)
.replace(/(\]\()([^)\s]+)/g, (_, open, link) => `${open}${absolute(link)}`);
}

function rewrite(path, change) {
const file = join(root, path);
writeFileSync(file, change(readFileSync(file, 'utf8')));
Expand All @@ -76,6 +97,10 @@ function stamp(version) {
rewrite('Cargo.toml', (text) => stampCargoToml(text, version));
rewrite('Cargo.lock', (text) => stampCargoLock(text, version));
rewrite('npm/skillpouch/package.json', (text) => stampPackageJson(text, version));
writeFileSync(
join(root, 'npm/skillpouch/README.md'),
npmReadme(readFileSync(join(root, 'README.md'), 'utf8'), version),
);
}

function published(name, version) {
Expand Down
40 changes: 38 additions & 2 deletions scripts/release.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@
import assert from 'node:assert/strict';
import { mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { test } from 'node:test';
import { PLATFORMS, build } from './npm-packages.mjs';
import { checkVersion, stampCargoLock, stampCargoToml, stampPackageJson } from './release.mjs';
import {
checkVersion,
npmReadme,
stampCargoLock,
stampCargoToml,
stampPackageJson,
} from './release.mjs';

test('only the workspace version in Cargo.toml changes', () => {
const text = [
Expand Down Expand Up @@ -117,3 +124,32 @@ test('Windows line endings are stamped too', () => {
assert.match(stamped, /name = "serde"\r\nversion = "1.0.0"\r\n/);
assert.match(stamped, /name = "skillpouch"\r\nversion = "2.0.0"\r\n/);
});

test('the npm README links to the release on GitHub', () => {
const text = [
'<img src=".github/assets/banner.png" alt="" />',
'<a href="https://skillpouch.net">Website</a> · <a href="#install">Install</a>',
'![Dashboard](.github/assets/tui-overview.webp)',
'See [`sp-harness`](crates/sp-harness) and [the licence](./LICENSE).',
].join('\n');
const readme = npmReadme(text, '1.2.3');
const raw = 'https://raw.githubusercontent.com/skillpouch/skillpouch-cli/v1.2.3';
const blob = 'https://github.com/skillpouch/skillpouch-cli/blob/v1.2.3';
assert.match(readme, new RegExp(`src="${raw}/.github/assets/banner.png"`));
assert.match(readme, new RegExp(`\\(${raw}/.github/assets/tui-overview.webp\\)`));
assert.match(readme, new RegExp(`\\(${blob}/crates/sp-harness\\)`));
assert.match(readme, new RegExp(`\\(${blob}/LICENSE\\)`));
assert.match(readme, /href="https:\/\/skillpouch.net"/);
assert.match(readme, /href="#install"/);
});

test('every link in the real README works from npm', () => {
const root = join(dirname(fileURLToPath(import.meta.url)), '..');
const readme = npmReadme(readFileSync(join(root, 'README.md'), 'utf8'), '1.2.3');
const links = [
...readme.matchAll(/\b(?:src|href)="([^"]+)"/g),
...readme.matchAll(/\]\(([^)\s]+)/g),
].map(([, link]) => link);
assert.ok(links.length > 10);
for (const link of links) assert.match(link, /^(https:\/\/|#)/, link);
});
Loading