Conversation
img2 add npm:<name>[@<version>] fetches a plugin from npm instead of git: npm view resolves the version and dist.integrity, npm pack fetches the tarball, and the harness extracts it itself (stripping the package/ prefix) into the same staging -> validate -> link flow git installs already go through. Default trust is the @img2threejs scope, mirroring the img2threejs/* git org. img2 update [<id>] [--check] re-checks every registered plugin's source (newest npm version, or newest reachable git tag) and re-fetches it in place if newer, backing up the previous clone; a link: row is left alone. Also fixes two git assumptions doctor/list/add made about a plugin directory: the .gitignore check now only runs against an actual git checkout, and the short-sha display shows a useful slice of an npm dist.integrity string instead of the literal sha512- prefix.
…arball npm pack does not consult .gitignore for a directory explicitly listed in package.json's files field (verified against npm 10.9.8) -- a local __pycache__/ or leftover .omc/ state dir under docs/ or img2_core/ would ship in the published tarball otherwise. Negate them directly in files, which npm does apply.
ci.yml runs the full test suite on every PR and push to main. publish.yml publishes img2 to npm via trusted publishing (OIDC, no token secret) on a vX.Y.Z tag push, after re-running the suite and checking the tag matches package.json's version. CONTRIBUTING.md documents the release-and-publish steps, including that the first-ever publish needs a manual, logged-in npm publish before trusted publishing can take over.
README's quickstart and layout section, and PLUGIN_CONTRACT.md's registry-row and trust-boundary sections (6, 7), now cover npm:<name> as a repo form alongside org/repo, a git URL, and link:<path>, plus the img2 update command. CHANGELOG records the release.
… npm view npm() and tar() now share one runTool wrapper instead of repeating the ENOENT/failure message shape. The staging-directory rm+mkdir pattern (repeated for git clone staging, npm staging, and npm pack staging) is now one freshStagingDir helper. resolveNpmAndFetch is split into npmViewInfo (view + parse + validate) and fetchNpmTarball (pack + extract) so img2 update can view a package once and reuse that result to fetch it, instead of viewing it a second time right after.
…inline logic ci.yml and publish.yml now uses: img2threejs/ci-workflows's python-ci.yml and npm-publish.yml, pinned by full commit SHA per org policy, rather than carrying their own test/publish steps. Publish authenticates via the NPM_TOKEN secret (a granular npm automation token), not OIDC trusted publishing; the reusable workflow validates the tag against package.json, refuses install-time lifecycle scripts, and no-ops on an already-published version. Both workflow refs point at a feature branch of ci-workflows pending img2threejs/ci-workflows#2; re-pin to its merged main SHA once that lands.
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.
Summary
npm:plugin source alongside the existing git source:img2 add npm:<name>[@<version>]resolves the version anddist.integrityvianpm view, fetches the tarball vianpm pack, and extracts it itself (stripping thepackage/prefix) into the same staging → validate → link flow a git install already goes through. Default trust is the@img2threejsscope, mirroring theimg2threejs/*git org — anything else needs--allow-any-source, same confirmation prompt the git path already uses.img2 update [<id>] [--check](there was no update command before this PR). It re-resolves each registered plugin's source — newest npm version, or newest reachable git tag — and re-fetches it in place if it differs from the recordedref, backing up the previous clone; alink:row is a local dev checkout and is left alone.--checkreports what's pending without fetching, exiting non-zero if anything is.{id, repo, ref, resolvedSha, addedAt}. For an npm row,repobecomesnpm:<name>,refis the resolved version, andresolvedShais npm's owndist.integrity(asha512-…string) rather than a git SHA — this harness's only other notion of "pinned content hash" for a package it fetched instead of cloning.resolvedSha,git(,cloneDir, and doctor/verify logic): found two real ones and fixed both — (1) doctor's.gitignorecovers_img2_local.pycheck unconditionally rangit check-ignoreagainst every plugin dir and would always FAIL an npm-fetched or bare--link'd directory (no.gitto have a.gitignorein); it's now guarded byisGitRepo(dir)and skipped where there's no git checkout to have the hazard it guards against. (2) the short-SHA display inimg2 add/img2 listsliced the first 7 characters ofresolvedSha, which for an npm integrity string (sha512-…) just printed the literalsha512-prefix; it now shows a useful slice of whichever part actually varies.npm pack --dry-runshowed this session's own leftoverdocs/.omc/state/**andimg2_core/__pycache__/**in the tarball. Verified against real npm 10.9.8 in an isolated repro: npm does not consult.gitignorefor a directory explicitly listed inpackage.json'sfilesfield when there's no.npmignore— it packs the directory's full contents regardless. Fixed by negating those patterns directly infiles(the one mechanism that does work here), and cleaned up the stray untracked cruft from the working tree..github/workflows/ci.ymlandpublish.ymlare thin callers into the org's sharedimg2threejs/ci-workflowsreusable workflows, pinned by full commit SHA per org policy —ci.ymlusespython-ci.ymlto run the full test suite on every PR and push tomain;publish.ymlusesnpm-publish.ymlto publishimg2to npm on avX.Y.Ztag push. The reusable workflow validates the tag againstpackage.json, refuses install-time lifecycle scripts, runs the suite in a job with no access to the npm token, and publishes withnpm publish --provenance --access publicauthenticated by anNPM_TOKENsecret (a granular npm automation token — not OIDC trusted publishing). Both refs currently point at a feature branch ofci-workflowspending img2threejs/ci-workflows#2 — this PR has that cross-repo dependency; re-pin bothuses:lines toci-workflows's mergedmainSHA once it lands.CONTRIBUTING.mddocuments all of this, including thatNPM_TOKENmust exist as an org/repo secret before the first tag push (no separate manual first-publish step is needed once it does).npm:source,img2 update) anddocs/PLUGIN_CONTRACT.md§6/§7 (registry row shape, trust boundary) now describe the npm source and update command.CHANGELOG.mdhas a new[Unreleased]entry.Test evidence
Full suite, run locally after every change (final run on the committed tree):
New coverage: unit tests for
parseNpmSpec/resolveNpmSource(scope parsing, default-org trust); e2e tests forimg2 add npm:...(default scope, explicit@version, non-default scope needing--allow-any-source+ confirmation, npm missing from PATH, tolerating non-empty stderr on success, doctor staying clean on a non-git plugin dir) against a PATH-shimmed fakenpm(a realnpm view/npm packregistry doesn't exist in CI, and the harness's own extraction code — the part actually worth testing — still runs against a tarball built with the realtar); and e2e tests forimg2 update(npm row fetches a newer version and updates the row,--checkreports pending without touching anything, alink:row is left alone, a git row updates to its newest tag).Decisions made that weren't fully specified
img2 updatedidn't exist before this PR — grepped the whole repo and history for it; there was no command to extend, so this PR designs and implements it from scratch (task 5 assumed there was an existing one to extend). Scope: for a git row, "newer" means a newer reachable semver tag — a plugin pinned to a moving branch (no tags on the remote) is reported as "no tag; nothing to compare against" rather than guessed at, since the registry doesn't record whetherrefwas an explicit branch pin.update, the same asadd --forcealready does on every re-add, rather than trusting it forever once accepted once./simplifypass (reuse/simplification/efficiency/altitude) on the diff caught three real issues, now fixed:npm()/tar()shared one subprocess wrapper instead of two near-identical copies; the staging-directory rm+mkdir pattern (repeated 3×) is now onefreshStagingDirhelper; andimg2 update's npm path no longer callsnpm viewtwice (once to check, once inside the fetch) for a package it's about to update — it views once and reuses the result. The efficiency pass's other finding (resolveRefAndClonecallsgit ls-remote --tagsbefore agit clonethat would also fetch the tags) is pre-existing behavior I only extracted into a named helper, not something this PR introduced — left alone as out of scope.Anomalies
--allow-any-source) — there's no unscoped equivalent ofimg2threejs/*, so this seemed like the only sound default.