Each service its own version: ipx, ipx-directory, the page (#207)
The workspace stops holding one version. ipx keeps 0.12.1-dev and its vX.Y.Z tags; the directory starts at 0.1.0-dev, tagged directory-vX.Y.Z, and the page at 0.1.0-dev in package.json, tagged web-vX.Y.Z, which web/build.mjs gives the script for the logo's tooltip beside ipx's. crates/feed is a library, not released. CLAUDE.md's release steps are for one service at a time. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
38
CLAUDE.md
38
CLAUDE.md
@@ -266,18 +266,32 @@ say, such as what was wrong before or what it cost to find out, it goes in the c
|
|||||||
body, where `git log` and `git blame` find it beside the change. (There was a long-form
|
body, where `git log` and `git blame` find it beside the change. (There was a long-form
|
||||||
`docs/history.md` until 0.7.0; it grew too large to be useful and was removed. It is in git.)
|
`docs/history.md` until 0.7.0; it grew too large to be useful and was removed. It is in git.)
|
||||||
|
|
||||||
Cutting a release, of the three together while they share one version: in each changelog, first read `[Unreleased]` against the last release, not line by line, and rewrite
|
Each service has its own version and is released on its own, when its changelog has something to
|
||||||
it as what changed between the two. Lines go in as the work happens, so they record the way there:
|
release:
|
||||||
a feature added and then reworked in the same release is one line saying what it is now, under
|
|
||||||
Added; something added and taken out again before the release, or a fix for a bug no release ever
|
| service | version in | changelog | tag |
|
||||||
had, is not there at all; and "instead of" and "no longer" compare with the last release, not with
|
|---|---|---|---|
|
||||||
last week. Issue numbers stay, all of them, on the line that survives. Then rename `[Unreleased]` to
|
| ipx | `crates/ipx/Cargo.toml` | `crates/ipx/CHANGELOG.md` | `vX.Y.Z`, as every release before the split |
|
||||||
`## [X.Y.Z] - YYYY-MM-DD` and open a new empty `[Unreleased]` above it, set `version` in the root `Cargo.toml` (`[workspace.package]`) to `X.Y.Z` (dropping `-dev`), tag the commit
|
| ipx-directory | `crates/directory/Cargo.toml` | `crates/directory/CHANGELOG.md` | `directory-vX.Y.Z` |
|
||||||
`vX.Y.Z`, and update the compare links at the bottom of each changelog. A service with nothing
|
| ipx-web, the page | `package.json` | `web/CHANGELOG.md` | `web-vX.Y.Z` |
|
||||||
under `[Unreleased]` gets no entry for that release. Then, in the next commit,
|
|
||||||
set `version` to the next patch with `-dev` (after 0.9.0, `0.9.1-dev`), so a build between releases says so
|
`crates/feed` is a library the two Rust services build in, not released (`0.0.0`); a change to it
|
||||||
in the logo's tooltip instead of claiming to be the last release. The release that follows can
|
is a line in the changelog of each service it changes. The page shows ipx's version and its own in
|
||||||
still be a minor or major one; `-dev` only says the work comes after `X.Y.Z`.
|
the logo's tooltip; the directory logs its own at start; each binary answers `--version`.
|
||||||
|
|
||||||
|
Cutting a release of one: first read its `[Unreleased]` against its last release, not line by
|
||||||
|
line, and rewrite it as what changed between the two. Lines go in as the work happens, so they
|
||||||
|
record the way there: a feature added and then reworked in the same release is one line saying
|
||||||
|
what it is now, under Added; something added and taken out again before the release, or a fix for
|
||||||
|
a bug no release ever had, is not there at all; and "instead of" and "no longer" compare with the
|
||||||
|
last release, not with last week. Issue numbers stay, all of them, on the line that survives. Then
|
||||||
|
rename `[Unreleased]` to `## [X.Y.Z] - YYYY-MM-DD` and open a new empty `[Unreleased]` above it,
|
||||||
|
set the service's version to `X.Y.Z` (dropping `-dev`), tag the commit with the service's tag, and
|
||||||
|
update the compare links at the bottom of its changelog. Then, in the next commit, set its version
|
||||||
|
to the next patch with `-dev` (after 0.9.0, `0.9.1-dev`), so a build between releases says so
|
||||||
|
instead of claiming to be the last release. The release that follows can still be a minor or major
|
||||||
|
one; `-dev` only says the work comes after `X.Y.Z`. A service never released is at `0.1.0-dev`,
|
||||||
|
working towards its first release, `0.1.0`.
|
||||||
|
|
||||||
**Traces first, logs second.** Anything that does work (a service, a handler, a job, a call to
|
**Traces first, logs second.** Anything that does work (a service, a handler, a job, a call to
|
||||||
another service) gets spans, and passes `traceparent` on to whatever it calls, so one trace in
|
another service) gets spans, and passes `traceparent` on to whatever it calls, so one trace in
|
||||||
|
|||||||
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -1856,7 +1856,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipx-directory"
|
name = "ipx-directory"
|
||||||
version = "0.12.1-dev"
|
version = "0.1.0-dev"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"axum",
|
"axum",
|
||||||
@@ -1881,7 +1881,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipx-feed"
|
name = "ipx-feed"
|
||||||
version = "0.12.1-dev"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"atom_syndication",
|
"atom_syndication",
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
members = ["crates/ipx", "crates/feed", "crates/directory"]
|
members = ["crates/ipx", "crates/feed", "crates/directory"]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
|
|
||||||
# One version for the whole workspace: what a release sets, and what the logo's tooltip shows.
|
# Each service has its own version, in its own Cargo.toml (ipx, ipx-directory) or package.json
|
||||||
|
# (the page), released and tagged on its own; see CLAUDE.md.
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.12.1-dev"
|
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
The directory of public podcasts that ipx servers share (docs/directory.md). ipx's changelog is
|
The directory of public podcasts that ipx servers share (docs/directory.md). ipx's changelog is
|
||||||
[crates/ipx/CHANGELOG.md](../ipx/CHANGELOG.md), the page's [web/CHANGELOG.md](../../web/CHANGELOG.md).
|
[crates/ipx/CHANGELOG.md](../ipx/CHANGELOG.md), the page's [web/CHANGELOG.md](../../web/CHANGELOG.md).
|
||||||
|
|
||||||
|
Its version is in `crates/directory/Cargo.toml`, and a release is tagged `directory-vX.Y.Z`.
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ipx-directory"
|
name = "ipx-directory"
|
||||||
version.workspace = true
|
version = "0.1.0-dev"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ async fn serve(path: &std::path::Path) -> Result<()> {
|
|||||||
let dropped = dir.set_tokens(tokens).await?;
|
let dropped = dir.set_tokens(tokens).await?;
|
||||||
|
|
||||||
let listener = tokio::net::TcpListener::bind(&cfg.bind).await.with_context(|| format!("listening on {}", cfg.bind))?;
|
let listener = tokio::net::TcpListener::bind(&cfg.bind).await.with_context(|| format!("listening on {}", cfg.bind))?;
|
||||||
tracing::info!(bind = %cfg.bind, tokens = cfg.tokens.len(), dropped, "directory started");
|
tracing::info!(version = env!("CARGO_PKG_VERSION"), bind = %cfg.bind, tokens = cfg.tokens.len(), dropped, "directory started");
|
||||||
tokio::spawn(ipx_directory::refresher(dir.clone()));
|
tokio::spawn(ipx_directory::refresher(dir.clone()));
|
||||||
tokio::spawn(ipx_directory::reloader(dir.clone(), path.to_owned()));
|
tokio::spawn(ipx_directory::reloader(dir.clone(), path.to_owned()));
|
||||||
axum::serve(listener, ipx_directory::router(dir))
|
axum::serve(listener, ipx_directory::router(dir))
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ipx-feed"
|
name = "ipx-feed"
|
||||||
version.workspace = true
|
version = "0.0.0"
|
||||||
|
publish = false
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ The daemon: the API, scans, downloads, DeadAir and the database. The page has it
|
|||||||
[crates/directory/CHANGELOG.md](../directory/CHANGELOG.md). Until 0.12.0 this was the changelog of
|
[crates/directory/CHANGELOG.md](../directory/CHANGELOG.md). Until 0.12.0 this was the changelog of
|
||||||
all of ipx, the page included; they were split with the services (#207).
|
all of ipx, the page included; they were split with the services (#207).
|
||||||
|
|
||||||
|
Its version is in `crates/ipx/Cargo.toml`, and a release is tagged `vX.Y.Z`.
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ipx"
|
name = "ipx"
|
||||||
version.workspace = true
|
version = "0.12.1-dev"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ipx-web",
|
"name": "ipx-web",
|
||||||
|
"version": "0.1.0-dev",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Builds the ipx web pages from web/src (web/build.mjs, run by build.rs) and tests them. The Rust tests cover the server.",
|
"description": "Builds the ipx web pages from web/src (web/build.mjs, run by build.rs) and tests them. The Rust tests cover the server.",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1444,6 +1444,8 @@ test('/api/status gives a dashboard the counts, with the shared token as a cooki
|
|||||||
const s = await r.json();
|
const s = await r.json();
|
||||||
for (const k of ['feeds', 'pending', 'downloaded']) expect(typeof s[k], k).toBe('number');
|
for (const k of ['feeds', 'pending', 'downloaded']) expect(typeof s[k], k).toBe('number');
|
||||||
expect(s.version).toMatch(/^\d+\.\d+\.\d+/);
|
expect(s.version).toMatch(/^\d+\.\d+\.\d+/);
|
||||||
|
// The logo names ipx's version and the page's, each its own (#207).
|
||||||
|
await expect(page.locator('#applogo')).toHaveAttribute('title', new RegExp(`^iPX ${s.version}, page \\d+\\.\\d+\\.\\d+`));
|
||||||
const none = await page.request.get('/api/status', { headers: { cookie: '' } });
|
const none = await page.request.get('/api/status', { headers: { cookie: '' } });
|
||||||
expect(none.status()).toBe(401);
|
expect(none.status()).toBe(401);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ The page, and the container that serves it (Caddy). ipx's changelog is
|
|||||||
[crates/ipx/CHANGELOG.md](../crates/ipx/CHANGELOG.md), which was the page's too until 0.12.0, and the
|
[crates/ipx/CHANGELOG.md](../crates/ipx/CHANGELOG.md), which was the page's too until 0.12.0, and the
|
||||||
directory's [crates/directory/CHANGELOG.md](../crates/directory/CHANGELOG.md).
|
directory's [crates/directory/CHANGELOG.md](../crates/directory/CHANGELOG.md).
|
||||||
|
|
||||||
|
Its version is in `package.json`, and a release is tagged `web-vX.Y.Z`.
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
@@ -11,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- The page has a version of its own, which the logo's tooltip shows beside ipx's (#207).
|
||||||
- The page is served by its own container, ipx-web (Caddy), which passes the API to ipx on the same address, so a change to the page deploys without rebuilding ipx (#203).
|
- The page is served by its own container, ipx-web (Caddy), which passes the API to ipx on the same address, so a change to the page deploys without rebuilding ipx (#203).
|
||||||
- Admin, Directory sets the directory's address and this server's token, and whether to share, and says how the last share went (#205).
|
- Admin, Directory sets the directory's address and this server's token, and whether to share, and says how the last share went (#205).
|
||||||
- A feed's settings have a Private switch, which keeps it out of the directory; on and locked for a feed whose address or login shows it is private (#205).
|
- A feed's settings have a Private switch, which keeps it out of the directory; on and locked for a feed whose address or login shows it is private (#205).
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import swc from '@swc/core';
|
|||||||
import html from '@swc/html';
|
import html from '@swc/html';
|
||||||
|
|
||||||
const here = path.dirname(fileURLToPath(import.meta.url));
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
// The page's own version (package.json), given to its script in place of __WEB_VERSION__.
|
||||||
|
const WEB_VERSION = JSON.parse(fs.readFileSync(path.join(here, '..', 'package.json'), 'utf8')).version;
|
||||||
|
|
||||||
// The files are one script, concatenated in this order, not modules: they share one top-level
|
// The files are one script, concatenated in this order, not modules: they share one top-level
|
||||||
// scope, as the single inline script did, and code that runs at load needs what came before it.
|
// scope, as the single inline script did, and code that runs at load needs what came before it.
|
||||||
@@ -57,7 +59,8 @@ export function buildStyle({ minify = true } = {}) {
|
|||||||
export function buildPage(name, { minify = true } = {}) {
|
export function buildPage(name, { minify = true } = {}) {
|
||||||
const { script, src: files } = PAGES[name];
|
const { script, src: files } = PAGES[name];
|
||||||
const src = files.map(f => fs.readFileSync(path.join(here, 'src', f + '.ts'), 'utf8')).join('\n')
|
const src = files.map(f => fs.readFileSync(path.join(here, 'src', f + '.ts'), 'utf8')).join('\n')
|
||||||
.replaceAll('__HLS_JS__', HLS_URL);
|
.replaceAll('__HLS_JS__', HLS_URL)
|
||||||
|
.replaceAll('__WEB_VERSION__', WEB_VERSION);
|
||||||
const js = swc.transformSync(src, {
|
const js = swc.transformSync(src, {
|
||||||
filename: name + '.ts',
|
filename: name + '.ts',
|
||||||
jsc: {
|
jsc: {
|
||||||
|
|||||||
@@ -719,7 +719,8 @@ $('#signout').onclick=async()=>{ await api('/api/logout',{method:'POST'}); locat
|
|||||||
api('/api/me').then(u=>{
|
api('/api/me').then(u=>{
|
||||||
S.me=u;
|
S.me=u;
|
||||||
$('#who').textContent=u.name+(u.admin?' · admin':'');
|
$('#who').textContent=u.name+(u.admin?' · admin':'');
|
||||||
$('#applogo').title=`iPX ${u.version}`;
|
// ipx's version and the page's, each released on its own.
|
||||||
|
$('#applogo').title=`iPX ${u.version}, page __WEB_VERSION__`;
|
||||||
// Whether the link to Admin shows, now and before the first frame of the next visit.
|
// Whether the link to Admin shows, now and before the first frame of the next visit.
|
||||||
document.documentElement.toggleAttribute('data-admin',!!u.admin);
|
document.documentElement.toggleAttribute('data-admin',!!u.admin);
|
||||||
try{ localStorage.setItem('ipx.admin',u.admin?'1':'0'); }catch{}
|
try{ localStorage.setItem('ipx.admin',u.admin?'1':'0'); }catch{}
|
||||||
|
|||||||
Reference in New Issue
Block a user