diff --git a/.gitignore b/.gitignore index 0f8a8f1..c9e96e8 100644 --- a/.gitignore +++ b/.gitignore @@ -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/ diff --git a/deno.json b/deno.json index e73060a..4ce6a3b 100644 --- a/deno.json +++ b/deno.json @@ -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", diff --git a/docs/decisions/0004-vendor-the-version-generator-for-now.md b/docs/decisions/0004-vendor-the-version-generator-for-now.md new file mode 100644 index 0000000..e2ed425 --- /dev/null +++ b/docs/decisions/0004-vendor-the-version-generator-for-now.md @@ -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. diff --git a/src/version.js b/src/version.js deleted file mode 100644 index 1e39bb8..0000000 --- a/src/version.js +++ /dev/null @@ -1,21 +0,0 @@ -// CL-web-components version and license information. - -export const version = '0.0.12', -releaseDate = '2025-07-17', -releaseHash = '9729d57', -licenseText = ` - -Copyright (c) 2025, Caltech -All rights not granted herein are expressly reserved by Caltech. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -`; \ No newline at end of file diff --git a/tools/generate-version.js b/tools/generate-version.js new file mode 100755 index 0000000..78da849 --- /dev/null +++ b/tools/generate-version.js @@ -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}`);