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
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ bin/
dist/
scratch/

# Generated from codemeta.json by `deno task generate-version`, which
# `deno task build` runs first. It was tracked previously, which is why the
# CDN bundle reported 0.0.12 while codemeta.json said 0.0.16 -- nothing
# regenerated it.
/src/version.js

# The site is assembled by CI and uploaded as an artifact, never committed.
/_site/

Expand Down
5 changes: 3 additions & 2 deletions deno.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"tasks": {
"build": "deno bundle --platform browser --outdir=./dist src/*.js ; deno bundle --platform browser --output=dist/cl-web-components.js mod.js",
"release": "deno bundle --platform=browser --outdir=./dist src/*.js ; deno bundle --platform=browser --output=dist/cl-web-components.js mod.js"
"generate-version": "deno run --allow-read --allow-write --allow-run tools/generate-version.js",
"build": "deno task generate-version && deno bundle --platform browser --outdir=./dist src/*.js ; deno bundle --platform browser --output=dist/cl-web-components.js mod.js",
"release": "deno task build"
},
"imports": {
"@std/assert": "jsr:@std/assert@^1.0.11",
Expand Down
110 changes: 110 additions & 0 deletions docs/decisions/0004-vendor-the-version-generator-for-now.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# 4. Vendor the version generator until it can be shared

- Status: accepted
- Date: 2026-09-02

## Context and Problem Statement

[ADR-0003](0003-separate-sources-from-generated-files.md) established that build
output is not committed. `src/version.js` is build output that was committed
anyway, and it went stale: it reported `0.0.12` while `codemeta.json` said
`0.0.16`, so that was the version served from the CDN for over a year.

It went stale because nothing wrote it. `cmt` is the generator, and the
Makefile invoked it:

```make
version.js: .FORCE
cmt codemeta.json version.js
```

That writes the repository root. The bundler reads `src/version.js`. The two
have been different files since the `src/` reorganization in July 2025, and
the target cannot be corrected in place, because `cmt` treats the output
filename as the format identifier (`const format = outputName`) — so
`cmt codemeta.json src/version.js` exits with `unsupported format`.

So the file must be generated by something, and the question is only where
that something lives. It is not specific to this project: any Deno project
with a `codemeta.json` and a license file needs the same thing.

## Decision

Vendor it. `tools/generate-version.js` is a 57-line script in this repository,
run by `deno task generate-version`, which `deno task build` depends on.

**This is a placeholder for a shared package**, not a preferred design. The
script is deliberately project-agnostic so that moving it costs one line.

## Considered Options

1. Keep using `cmt` via the Makefile
2. Fix `cmt` to write subdirectories, and publish it to JSR
3. Add the function to `@caltechlibrary/metadatatools`
4. Reference a script by raw GitHub URL
5. Publish a new package to JSR from `caltechlibrary/workflows`
6. Vendor a script in this repository

## Decision Outcome

**Chosen: option 6, with option 5 the most likely successor.**

### Option 1: keep using `cmt` — rejected

It cannot write to `src/`. This is the defect being fixed, not an alternative
to it. It also puts a compiled binary, installed via a script, on the critical
path of every contributor's first build.

### Option 2: fix and publish `cmt` — rejected for now

The best long-term answer, and the least duplicative: `cmt` stays canonical
and every Deno repository benefits. But it is two changes to a tool this
repository does not own, and the JSR half has unexplained history — see
option 3.

### Option 3: add it to `metadatatools` — rejected

Wrong domain and unavailable. The package is scholarly identifier validation
(`doi.ts`, `arxiv.ts`, `isbn.ts`, `orcid.ts`, `ror.ts`), not build tooling.
It has one published version, `0.0.6`, and it is yanked; `latest` is `null`.
CMTools imports that yanked version.

### Option 4: raw GitHub URL — rejected

Does not work. `raw.githubusercontent.com` serves `text/plain; charset=utf-8`,
which Deno refuses to load as a module.

### Option 5: a new JSR package — deferred

Viable. A `deno/` directory in `caltechlibrary/workflows`, published to the
existing `@caltechlibrary` scope via OIDC, consumed as
`deno run -A jsr:@caltechlibrary/...`. A contributor would never clone the
workflows repository — the module arrives the same way `@std/csv` already
does. Deferred because standing up JSR publishing is out of scope right now.

### Option 6: vendor it — chosen

Fixes the defect today with no new infrastructure and no dependency on
decisions owned by others. Twelve repositories in the organization have a
`deno.json`, but only this one has the stale-version defect, so the cost of
duplication is currently theoretical.

## Consequences

Good:

- A fresh clone builds a correct version with only `deno` installed. No `cmt`,
no `make`.
- Nothing about the offline story changes. `src/textarea-csv.js` already
imports `jsr:@std/csv`, so a cold cache has always needed the network.

Bad, and accepted:

- It is a copy. If another Deno repository adopts it, there are two, and they
will drift.
- Replacing it means remembering this decision, which is why it is written
here rather than in the pull request.

## More Information

- The delta to option 5 is one line in `deno.json` and deleting one file.
21 changes: 0 additions & 21 deletions src/version.js

This file was deleted.

60 changes: 60 additions & 0 deletions tools/generate-version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Generates src/version.js from codemeta.json. This script is a build tool;
// src/version.js is its output and is not committed.
//
// This is the same output CMTools' version.js generator produces, without
// requiring cmt to be installed. Nothing here is specific to this project:
// any Deno project with a codemeta.json and a license file can use it
// unchanged.
//
// version codemeta.json "version"
// releaseDate codemeta.json "datePublished"
// releaseHash git rev-parse --short HEAD
// licenseText LICENSE.txt, or LICENSE
//
// Run via `deno task generate-version`; `deno task build` depends on it.
//
// This script is vendored, not shared. See
// docs/decisions/0004-vendor-the-version-generator-for-now.md

const OUT = "src/version.js";

const meta = JSON.parse(await Deno.readTextFile("codemeta.json"));

let licenseText = "";
for (const name of ["LICENSE.txt", "LICENSE"]) {
try {
licenseText = await Deno.readTextFile(name);
break;
} catch (err) {
if (!(err instanceof Deno.errors.NotFound)) throw err;
}
}
if (licenseText === "") {
console.error("no LICENSE.txt or LICENSE found");
Deno.exit(1);
}

// The text is interpolated into a template literal.
const escaped = licenseText.replace(/\\/g, "\\\\").replace(/`/g, "\\`")
.replace(/\$\{/g, "\\${");

const git = new Deno.Command("git", {
args: ["rev-parse", "--short", "HEAD"],
}).outputSync();
if (!git.success) {
console.error("git rev-parse failed");
Deno.exit(1);
}
const releaseHash = new TextDecoder().decode(git.stdout).trim();

const src = `// ${meta.name} version and license information.

export const version = '${meta.version}',
releaseDate = '${meta.datePublished}',
releaseHash = '${releaseHash}',
licenseText = \`
${escaped}
\`;`;

await Deno.writeTextFile(OUT, src);
console.log(`${OUT}: ${meta.version} ${meta.datePublished} ${releaseHash}`);