feat(doenetml-versions): pin tracked versions to concrete releases#3011
Open
dqnykamp wants to merge 2 commits into
Open
feat(doenetml-versions): pin tracked versions to concrete releases#3011dqnykamp wants to merge 2 commits into
dqnykamp wants to merge 2 commits into
Conversation
Store a concrete @doenet/standalone version (e.g. 0.7.21, 0.7.21-dev.343) in
doenetmlVersions.fullVersion instead of a moving npm tag ("latest"/"dev"), so
the jsDelivr bundle URL is immutable and browsers pick up new DoenetML releases
without waiting out a browser cache.
- Add nullable, unique `trackingNpmTag` mapping a row to an npm dist-tag
(migration backfills 0.7 -> latest, 0.7dev -> dev; 0.6 stays unpinned).
- Add a secret-guarded, idempotent POST /api/info/updateTrackedDoenetmlVersion
endpoint that pins the tracked row to a published version. Auth is a
timing-safe bearer secret (DOENETML_VERSION_UPDATE_SECRET), modeled on the
Discourse SSO shared-secret pattern; the version is validated against the
SemVer grammar so it is safe to interpolate into the CDN URL.
- Omit trackingNpmTag from the public version-list responses.
- Wire the secret into .env.example and the prod/dev3 ECS task definitions.
Called by DoenetML's publish workflow (separate PR) after each npm publish.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014i6e7VFUmE7svTjjSyida7
The npm dist-tag fallback path wrote the registry-supplied version into fullVersion (which builds the immutable jsDelivr bundle URL) with only a non-empty-string check, bypassing the SemVer validation applied to an explicit request-body version. Validate the resolved version with the same schema at this choke point so a malformed npm response fails loudly instead of silently corrupting the pinned version. Adds a covering test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
doenetmlVersions.fullVersioncurrently holds a moving npm dist-tag —0.7→latest,0.7dev→dev. That value is passed straight to@doenet/doenetml-iframe, which builds the bundle URLhttps://cdn.jsdelivr.net/npm/@doenet/standalone@${fullVersion}/doenet-standalone.js. Because the URL contains the tag, its content changes on a new publish but the URL does not — so browsers keep serving the cached old bundle until users manually clear their cache. jsDelivr purges fix the CDN edge, not end-user browsers.This PR lets us store a concrete version (
0.7.21,0.7.21-dev.343) so the URL is immutable and version-specific: browsers cache it forever and pick up a new bundle the instant the row changes (content references the row by FK, so one update covers all content on that channel).Changes
trackingNpmTagtodoenetmlVersions. Migration backfills0.7→latest,0.7dev→dev;0.6stays unpinned (never auto-updated).POST /api/info/updateTrackedDoenetmlVersion— machine-to-machine (no session), timing-safe bearer-secret auth (DOENETML_VERSION_UPDATE_SECRET, modeled on the Discourse SSO shared-secret pattern), zod-validated, idempotent update: it pins the existing row that tracks the given tag and returns 404 if no row tracks it (it does not create rows). Both an explicit request-body version and a version resolved from npm (when the body omits it) are validated against the SemVer grammar so the value is safe to interpolate into the CDN URL.trackingNpmTagfrom the public version-list responses (API surface unchanged)..env.exampleand the prod + dev3 ECS task definitions (SSM parameter/{env}/doenet/DoenetmlVersionUpdateSecret).The endpoint is called by DoenetML's publish workflow (separate PR in
Doenet/DoenetML) with the exact version just published.Rollout / operational prerequisites
Ship this first (expand-migrate-contract; the migration is additive and safe alone). Before/after deploy:
/{env}/doenet/DoenetmlVersionUpdateSecretfor prod and dev3, and set the matchingDOENET_VERSION_UPDATE_SECRETrepo secret inDoenet/DoenetML.versionto have it read npm), then merge the DoenetML PR.Verification
changed:true→changed:falseidempotency, public read route reflects the pin and does not leaktrackingNpmTag.🤖 Generated with Claude Code