Skip to content

fix: materialize linked resources before npm publishing - #579

Open
neubig wants to merge 3 commits into
mainfrom
fix/npm-linked-resources
Open

fix: materialize linked resources before npm publishing#579
neubig wants to merge 3 commits into
mainfrom
fix/npm-linked-resources

Conversation

@neubig

@neubig neubig commented Sep 13, 2026

Copy link
Copy Markdown
Member
  • A human has tested these changes.

Why

npm omits source symlinks, so the published package loses linked skill resources and vendor manifests. Git-based skill installation correctly resolves them, but npm consumers receive an incomplete directory.

Summary

Stage a dereferenced copy with cp -RL, then run the existing package verification and publication from that directory. Canonical source links and runtime/installer behavior stay unchanged.

Issue Number

Closes #578

How to Test

Packed actual npm archives from clean main and composed factory checkouts before and after staging, then extracted them. Main's linked skills/github/.claude-plugin/plugin.json and all four factory scripts/github_client.py resources are absent before, regular files with identical canonical bytes after. All four standalone workers import successfully from the extracted package. Archives are about 1.1–1.2 MB. Workflow syntax and whitespace checks pass.

Video/Screenshots

This changes release packaging, not Canvas UI. The actual packed-package checks exercise the distribution artifact used by Canvas.

Notes

Review before #570#573 are released. A raw npm pack from the source checkout still omits symlinks; the release workflow consistently packs and publishes its materialized copy. No npm release was published during validation. Use cp -RL, not archive mode: cp -aL retains hard-link identity, which produced malformed archives or npm failures during verification.

Recorded evidence

Actual npm tarballs omit linked resources before staging and contain regular files with canonical bytes afterward. All four standalone factory workers import from the extracted package. No npm release was published. Report and artifacts. Links are pinned to the artifact commit so review cleanup cannot invalidate them.

@github-actions github-actions Bot added the type: fix A bug fix label Sep 13, 2026
@github-actions

Copy link
Copy Markdown

📁 PR Artifacts Notice

This PR contains a .pr/ directory with temporary PR-specific documents. The directory will be automatically removed when the PR is approved.

@all-hands-bot

Copy link
Copy Markdown
Contributor

🤖 OpenHands is reviewing this PR.

Head commit: a249bd4883bf62bcad198bd1896418d6b094ab57
View the conversation: https://oss-agent-canvas.ngrok.dev/conversations/c25dc127-5cb0-4063-b15d-a0bbafb3c8ae

This comment was posted by an AI agent (OpenHands).

@all-hands-bot all-hands-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This review was created by an AI agent (OpenHands) on behalf of the repository maintainers.

Review: fix: materialize linked resources before npm publishing

Taste Rating: 🟢 Good taste

The change is minimal and targeted. cp -RL is the correct tool - it follows symlinks and creates independent regular files, which is exactly what npm needs since it omits symlinks. The PR description's reasoning for rejecting cp -aL (hard-link identity causing malformed archives) is sound and grounded in actual testing.

The approach of staging a release-only copy while retaining the canonical linked source layout in the checkout is clean. No runtime, loader, or installer behavior is changed.

One minor inconsistency (non-blocking)

The Validate package version matches release tag step (line 70) does not set working-directory, so it reads package.json from the original checkout ($GITHUB_WORKSPACE) rather than from the staged copy. This is functionally correct today because cp -RL copies package.json unchanged. However, if a future change to the staging step ever modifies package.json (e.g., stripping dev dependencies, rewriting paths), the validation would check the wrong copy. Consider adding working-directory: ${{ runner.temp }}/extensions-package to that step for consistency.

Evidence

The PR description includes concrete, pinned evidence: actual npm tarballs were packed before and after staging, linked resources were confirmed absent before and present as regular files with matching canonical bytes after, and all four standalone factory workers imported successfully from the extracted package. This satisfies the evidence requirement.

Repository fit

This change belongs in this repo - it modifies the release workflow for @openhands/extensions, which is published from this repository.

[RISK ASSESSMENT]

  • [Overall PR] Risk Assessment: 🟢 LOW
    The change is confined to release packaging. No runtime, loader, or import code is modified. The canonical source layout is preserved. The staging step uses a safe, standard cp command with no injection surface. The only operational risk is the validate-step working-directory inconsistency noted above, which is non-blocking.

VERDICT:Worth merging - Core logic is sound, solves a real packaging problem with a minimal, well-tested change.

KEY INSIGHT: The fix correctly identifies that npm's symlink omission is a packaging-time concern, solved at packaging time without touching the canonical source layout.


Improve this review? If any feedback above seems incorrect or irrelevant to this repository, you can teach the reviewer to do better:

  1. Add a .agents/skills/custom-codereview-guide.md file to your branch (or edit it if one already exists) with the /codereview trigger and the context the reviewer is missing (e.g., "Security concerns about X do not apply here because Y"). See the customization docs for the required frontmatter format.
  2. Re-request a review - the reviewer reads guidelines from the PR branch, so your changes take effect immediately.
  3. When your PR is merged, the guideline file goes through normal code review by repository maintainers.

Resolve with AI? Install the iterate skill in your agent and run /iterate to automatically drive this PR through CI, review, and QA until it's merge-ready.

Was this review helpful? React with 👍 or 👎 to give feedback.

Comment thread .github/workflows/npm-publish.yml
@all-hands-bot

Copy link
Copy Markdown
Contributor

👋 This PR needs a couple of things fixed before OpenHands can review it:

  • the PR description's HUMAN: section needs at least 20 characters describing what you tested, not just the template placeholder

Push an update once this is addressed and this check re-runs automatically.

This is an automated check - no AI was used to generate this comment.

@neubig

neubig commented Sep 13, 2026

Copy link
Copy Markdown
Member Author

Addressed the version-check consistency suggestion: it now reads package.json from the same staged directory as npm verification/publication. Workflow YAML parses and all three step directories agree. Existing actual npm archive evidence remains applicable.

Requesting another all-hands-bot review after this audit.

This comment was posted by an AI agent.

@all-hands-bot

Copy link
Copy Markdown
Contributor

🤖 OpenHands is reviewing this PR.

Head commit: e119ed94e57a71c4507b4b9a545b209af4567e8b
View the conversation: https://oss-agent-canvas.ngrok.dev/conversations/2de952b7-6054-4f16-b873-166a93961c27

This comment was posted by an AI agent (OpenHands).

@all-hands-bot all-hands-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This review was created by an AI agent (OpenHands) on behalf of the repository maintainers.

Review: fix: materialize linked resources before npm publishing

Taste Rating: Good taste

This is a clean, minimal fix for a real problem. npm omits symlinks from published tarballs, so consumers installing @openhands/extensions via npm were missing linked resources like skills/github/.claude-plugin/plugin.json and the shared scripts/github_client.py files across factory skills. The solution - staging a dereferenced copy with cp -RL and running the existing pack/verify/publish steps from that directory - is the simplest correct approach.

Analysis

Problem is real and verified. Confirmed in the workspace: 155 symlinks exist in the repo (e.g., skills/github/.claude-plugin -> .plugin, plugins/pr-review/skills/code-review -> ../../../skills/code-review). npm pack --dry-run from the source checkout omits these entirely. After cp -RL, they become regular files and are included.

cp -RL vs cp -aL choice is well-reasoned. The PR description documents that cp -aL preserves hard-link identity, which caused malformed archives and npm packaging failures. cp -RL creates independent regular files. This is a pragmatic, tested decision.

All three downstream steps correctly redirect. npm pack --dry-run, version validation, and npm publish all use working-directory: ${{ runner.temp }}/extensions-package. No step is left operating on the original symlinked checkout.

No security risk. All symlinks in the repo point to relative paths within the repository itself. cp -RL will not dereference anything external.

No breaking change. The canonical source layout in the checkout is unchanged. Git-based skill installation is unaffected. Only the release workflow's staging directory is materialized.

Evidence is thorough. The PR includes actual tarball extraction, byte-level comparison against canonical source, and import testing of all four factory workers from the extracted package. This is end-to-end proof, not just unit test output.

.pr/ artifacts are appropriate. The evidence files in .pr/ are PR-specific artifacts covered by the PR Artifacts workflow, which cleans them up on approval and post-merge. They will not be present on main when release tags are cut.

No material findings

No bugs, security issues, or design flaws found. The change is minimal, well-tested, and solves the stated problem correctly.

[RISK ASSESSMENT]

  • Overall PR: LOW
    • Change is confined to release packaging workflow; no runtime, loader, or API changes.
    • All symlinks are internal to the repo, so cp -RL cannot pull in external content.
    • Version validation still reads package.json from the staged copy, preserving the existing safety check.
    • The only new code is two lines (mkdir -p + cp -RL) plus three working-directory additions.

VERDICT: Worth merging

KEY INSIGHT: Staging a dereferenced copy is the right boundary - it fixes npm's symlink omission without touching the canonical source layout that git-based consumers depend on.


Improve this review? If any feedback above seems incorrect or irrelevant to this repository, you can teach the reviewer to do better:

  1. Add a .agents/skills/custom-codereview-guide.md file to your branch (or edit it if one already exists) with the /codereview trigger and the context the reviewer is missing (e.g., "Security concerns about X do not apply here because Y"). See the customization docs for the required frontmatter format.
  2. Re-request a review - the reviewer reads guidelines from the PR branch, so your changes take effect immediately.
  3. When your PR is merged, the guideline file goes through normal code review by repository maintainers.

Resolve with AI? Install the iterate skill in your agent and run /iterate to automatically drive this PR through CI, review, and QA until it's merge-ready.

Was this review helpful? React with thumbs up or thumbs down to give feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: fix A bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Preserve linked skill resources in published npm packages

3 participants