Skip to content

hy-2xeb: Install pipeline for GitHub-URL plugins (hy-gh-1)#9

Merged
philcunliffe merged 1 commit into
integration/github-installfrom
polecat/hy-2xeb
May 21, 2026
Merged

hy-2xeb: Install pipeline for GitHub-URL plugins (hy-gh-1)#9
philcunliffe merged 1 commit into
integration/github-installfrom
polecat/hy-2xeb

Conversation

@philcunliffe
Copy link
Copy Markdown
Contributor

Summary

Implements the install pipeline for GitHub-URL plugins (hy-gh-1 / bead hy-2xeb).

New modules src/core/plugin_install/git_source.js and git_fetch.js handle git URL parsing and the clone / checkout / resolve / validate / copy pipeline, with per-stage spans (plugin.git.clone, plugin.git.checkout, plugin.git.resolve_ref, plugin.artifact.validate, plugin.artifact.copy). Lock entries now carry resolved_ref. CLI accepts --ref / --path with source_ambiguous and git_subdir_unsupported guards.

Test plan

  • npm test — 48/48 green (includes 20 new unit tests for git source resolver, entrypoint validation, symlink rejection, content hash stability)
  • Hermetic smoke plugin_install_git_url (created in this PR) — green
  • Existing smoke plugin_install_local_dir — green
  • Typecheck + lint — green
  • Telemetry produces plugin.git.* and plugin.artifact.* spans for the smoke fixture
  • Lock file shows the new git source shape; no credentials in any logged output

Parent bead: hy-2xeb → integration/github-install (epic hy-dnk9)

Extend src/core/plugin_install to support git sources. Parses GitHub URL
forms (https with optional .git/#ref, github: shorthand, git@github.com
SSH) plus generic git+/git:/ssh:/gitlab:/bitbucket:/file:// URLs through
a new pure git_source parser. Adds --ref and --path CLI flags to the
plugin install command; --ref conflicting with a URL #ref is rejected
as source_ambiguous, and --path is reserved as git_subdir_unsupported.

The fetch path clones with --filter=blob:none --no-checkout, checks out
the requested ref (or the remote's HEAD symref by default), resolves
HEAD to a commit, validates the manifest and entrypoint, refuses any
symlinks in the artifact tree, then copies into an atomic install
directory swap. Lock entries gain resolved_ref; the update probe runs
git ls-remote and flips update.available when the upstream commit moved.

Telemetry: the existing plugin.install span gains hyp_source_kind=git,
git_url_host/owner/repo/ref/resolved_ref, content_hash, and
manifest_hash. Child spans plugin.git.clone, plugin.git.checkout,
plugin.git.resolve_ref, plugin.artifact.validate, and
plugin.artifact.copy each carry status and error_kind. Git stderr is
redacted for credentials before being surfaced.

Tests cover URL parsing variants, the --ref/URL-fragment conflict, the
subdir reservation, entrypoint traversal rejection, symlink detection,
and content-hash stability. New hermetic smoke plugin_install_git_url
builds a local bare repo and exercises the install end-to-end through
the dispatcher.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@philcunliffe philcunliffe merged commit a1a74bb into integration/github-install May 21, 2026
6 checks passed
@philcunliffe philcunliffe deleted the polecat/hy-2xeb branch May 21, 2026 20:10
philcunliffe added a commit that referenced this pull request May 22, 2026
* chore: seed integration/github-install for feature flow

* feat: install pipeline for GitHub-URL plugins (hy-2xeb) (#9)

Extend src/core/plugin_install to support git sources. Parses GitHub URL
forms (https with optional .git/#ref, github: shorthand, git@github.com
SSH) plus generic git+/git:/ssh:/gitlab:/bitbucket:/file:// URLs through
a new pure git_source parser. Adds --ref and --path CLI flags to the
plugin install command; --ref conflicting with a URL #ref is rejected
as source_ambiguous, and --path is reserved as git_subdir_unsupported.

The fetch path clones with --filter=blob:none --no-checkout, checks out
the requested ref (or the remote's HEAD symref by default), resolves
HEAD to a commit, validates the manifest and entrypoint, refuses any
symlinks in the artifact tree, then copies into an atomic install
directory swap. Lock entries gain resolved_ref; the update probe runs
git ls-remote and flips update.available when the upstream commit moved.

Telemetry: the existing plugin.install span gains hyp_source_kind=git,
git_url_host/owner/repo/ref/resolved_ref, content_hash, and
manifest_hash. Child spans plugin.git.clone, plugin.git.checkout,
plugin.git.resolve_ref, plugin.artifact.validate, and
plugin.artifact.copy each carry status and error_kind. Git stderr is
redacted for credentials before being surfaced.

Tests cover URL parsing variants, the --ref/URL-fragment conflict, the
subdir reservation, entrypoint traversal rejection, symlink detection,
and content-hash stability. New hermetic smoke plugin_install_git_url
builds a local bare repo and exercises the install end-to-end through
the dispatcher.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat: boot integration for installed plugins (hy-jky3) (#11)

bootKernel now reads plugin-lock.json, merges bundled and installed
manifest pools, resolves dependencies across the merged set, and
activates installed third-party plugins when the config names them.
Rejects boot with `installed_shadows_bundled` when an installed
plugin tries to shadow a bundled first-party name. Adds telemetry
attributes (installed_available, installed_failed,
installed_shadow_collisions, installed_selected) and a
plugin.installed_active log per installed-and-selected plugin.

Threads installed manifest metadata into the cross-plugin config
validator via a new mergeInstalledManifestsIntoKnown helper, so
third-party plugin sections are no longer flagged plugin_unknown
and their provides/requires participate in sink-pair and
capability-ambiguity checks. Updates hyp config validate, hyp init
--from-file, and daemon status to feed the merged map in.

Adds boot-installed unit tests covering merged pool, dep resolution
over bundled+installed, shadow rejection, and config-validator
behavior, plus a hermetic plugin_boot_installed smoke that installs
a git-fixture plugin, activates it via config, runs its contributed
command, and asserts the boot telemetry surface.

* feat: update + trust UX for installed plugins (hy-qvmo) (#13)

Add a confirmation gate to `hyp plugin install` and a real update flow
to `hyp plugin update <plugin>` so remote installs no longer commit
without the user (or `--yes`) seeing what is about to land.

Install (git sources)
- New `--yes`/`-y` flag.
- Non-TTY without `--yes` rejects with `remote_install_confirmation_required`
  (exit code 2).
- TTY without `--yes` prints a summary (source URL, resolved commit,
  plugin name + version, permissions, entrypoint, content_hash) and
  prompts `Proceed? [y/N]`. Both summary and prompt land on stderr so
  stdout stays parseable.
- Local-dir installs keep their current non-interactive behavior — the
  confirmation gate only fires inside `fetchGitSource`.

Update
- `hyp plugin update <plugin>` now re-clones with the same source, runs
  manifest validate, and shows old vs. new resolved_ref/version/content_hash
  before the artifact swap. `--yes` skips the prompt; non-TTY without
  `--yes` rejects as above. On rejection the prior install is left
  intact because the rename swap only fires once `beforeCommit` returns
  `proceed=true`.
- The bare `hyp plugin update` (no plugin name) keeps the legacy
  "refresh update_check state for every plugin" behavior so `outdated`
  can still be recomputed without committing to a re-install.

Soft warnings (stderr only)
- Manifest permissions containing `network` warn about broad scope.
- Branch-shaped refs (and missing refs) warn that pinning a tag or
  commit SHA gives reproducible installs.

Telemetry
- `plugin.install` and a new `plugin.update` span both carry a
  `confirmation` attribute drawn from `confirmed`, `auto_yes`,
  `rejected`, `non_tty_no_yes`. The `plugin.installed` log row mirrors
  the attribute when it was set.
- `fetch.FetchErrorKind` / `git_fetch.GitFetchErrorKind` gain
  `remote_install_confirmation_required` and `remote_install_rejected`.

Tests + smoke
- New `test/core/plugin-install-confirm.test.js` covers the pure
  helpers (`decideConfirmation`, `buildWarnings`, `sourceIsUnpinnedBranch`,
  `renderConfirmationSummary`) and exercises the `installPlugin` +
  `updatePlugin` integration against a hermetic file:// bare repo.
- New env-gated acceptance smoke
  `hypaware-core/smoke/flows/plugin_install_github_url.js` installs a
  real GitHub plugin fixture at a pinned tag (opt-in via
  `HYP_SMOKE_REAL_GITHUB=1`, with overridable URL/ref/name env vars).
  Skips with a clear marker when the env var is unset so CI runs of
  `npm run smoke` do not hit the network.
- The existing hermetic git-url and boot-installed smokes now pass
  `--yes` (they run with non-TTY buffer streams) and assert
  `confirmation=auto_yes` on the install span.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: review findings on integration/github-install plugin install (hy-lpo3) (#18)

Address all blocker and major findings from the dual-review on PR #6:

confirm.js — switch to node:readline/promises so the awaited
rl.question() actually returns a Promise and the interactive TTY
prompt path can return a decision instead of throwing.

update_check.js — pickLsRemoteSha now prefers the peeled `^{}` SHA
when present so annotated tags compare against entry.resolved_ref
correctly instead of producing spurious "update available" signals.
Also wraps the ls-remote spawn with a hard timeout (default 5s,
HYP_GIT_PROBE_TIMEOUT_MS-overridable, clamped) that emits a
git_probe_timeout error_kind, and the post-install path now passes
freshlyResolved=true so the probe is skipped entirely when we just
resolved the upstream ref — the span still emits with
probe_skipped=freshly_resolved for observability.

git_source.js / resolver.js / install.js — strip user:pass@ userinfo
from source.raw and source.gitUrl before they reach the lock entry,
the confirmation prompt, or the install span. Persistence and display
are always credential-free; clones rely on the user's git credential
helper. Adds redactGitUrl / redactRawSource helpers.

git_fetch.js / update_check.js — insert `--` end-of-options separator
before user-controlled positionals in `git clone`, `git checkout`,
and `git ls-remote` so a hostile URL or ref like `--upload-pack=<cmd>`
cannot be parsed as a git option (CVE-2018-17456 family). Also reject
leading-dash inputs in parseGitSource, applyGitSourceFlags, and the
CLI parser as belt-and-braces defense before the spawn boundary.

git_source.js — applyGitSourceFlags JSDoc no longer claims the subdir
value is "still recorded on the returned spec" since the function
throws unconditionally; the unreachable subdir spread is removed.

Tests cover redactGitUrl/redactRawSource, leading-dash rejection
across parser layers, peeled-tag SHA preference in pickLsRemoteSha,
and the real readline/promises behavior of buildTtyPrompt via
PassThrough streams.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: feature-launch <feature-launch@gas.city>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant