Skip to content

fix(plugin): repair three --from-config install bugs#457

Merged
intel352 merged 2 commits into
mainfrom
fix/plugin-install-from-config-bugs
Apr 22, 2026
Merged

fix(plugin): repair three --from-config install bugs#457
intel352 merged 2 commits into
mainfrom
fix/plugin-install-from-config-bugs

Conversation

@intel352
Copy link
Copy Markdown
Contributor

Summary

Fixes three bugs in `wfctl plugin install --from-config` that were exposed by BMW PR #147:

  1. Double-v URL bug: download URL became `releases/download/vv0.6.1/...` because the version (with a leading 'v' from app.yaml) was templated into a URL pattern that also prepends 'v'. Fixed.
  2. Name collision: `workflow-plugin-auth` was normalized to `auth` and matched the BUILTIN `auth` plugin (v0.3.51) instead of the EXTERNAL `workflow-plugin-auth` (v0.1.2). Fixed by preferring exact-match before normalized-name fallback.
  3. Static registry 404: github-fallback was always being hit because `gocodealone.github.io/workflow-registry/v1` returns 404 for plugin manifests. Fixed by ... [see PR diff for actual approach].

Why

BMW PR #147 replaced curl-based plugin installs in deploy.yml with `wfctl plugin install --from-config app.yaml`. The BMW Deploy run subsequently failed at the plugin install step because of the three bugs above. Without these fixes, the manifest-based install path is unusable.

Test plan

  • CI passes
  • After release: BMW deploy run succeeds at the "Install external plugins" step

🤖 Generated with Claude Code

…ame collision, stale static registry)

Bug 1 — double-v URL: pinManifestToVersion now normalises both versions by
stripping a leading 'v' before comparison, so manifest version '0.6.1' and
requested '@v0.6.1' are recognised as equal (no-op). When versions truly
differ the fallback URL replacement uses the bare numeric forms, preventing
'v0.5.0' from becoming 'vv0.6.1'.

Bug 2 — name collision: MultiRegistry.FetchManifest now tries the full
original name first across all sources before falling back to the
'workflow-plugin-' stripped short name. This stops 'workflow-plugin-auth'
resolving to the unrelated builtin 'auth' entry. installFromWorkflowConfig
also normalises req.Name when computing the installDir skip-check so the
already-installed detection matches the actual on-disk location.

Bug 3 — dead static registry: DefaultRegistryConfig promotes the GitHub
raw source to priority 0 (primary) and demotes the GitHub Pages mirror to
priority 100 (fallback). The Pages site was returning 404, forcing every
lookup through the fallback path that exacerbated Bug 2.

Tests: added TestPinManifestToVersion_VPrefixMismatchSameVersion,
TestPinManifestToVersion_CrossPrefixPin, TestMultiRegistryFetchOriginalNameFirst,
TestMultiRegistryFetchNormalizedFallback, TestInstallFromConfig_NormalizedSkipCheck;
updated registry config assertions and plugin_auth_test install-dir to match
new normalised behaviour.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 22, 2026 07:00
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes multiple issues in wfctl plugin install --from-config by improving registry defaults, version pin URL rewriting, and registry name resolution behavior, along with targeted tests to prevent regressions.

Changes:

  • Make the GitHub raw-content registry the default primary source, with GitHub Pages as a lower-priority mirror.
  • Make pinManifestToVersion v-prefix-tolerant to avoid corrupting GitHub release URLs (double-v).
  • Adjust multi-registry manifest lookup order (original name first, normalized fallback) and improve skip-check behavior for normalized install dirs.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
cmd/wfctl/registry_config.go Switch default registry ordering to GitHub primary + static mirror fallback.
cmd/wfctl/plugin_install.go Make version pinning tolerant of v prefix mismatches and avoid double-v URL rewrites.
cmd/wfctl/multi_registry.go Change manifest lookup order to prefer original name before normalized fallback.
cmd/wfctl/plugin_deps.go Normalize plugin name when checking the on-disk install directory (skip check).
cmd/wfctl/plugin_version_pin_test.go Add tests covering v-prefix mismatch and version pin URL rewrite behavior.
cmd/wfctl/plugin_from_config_test.go Add test ensuring from-config skip-check uses normalized install dir.
cmd/wfctl/plugin_auth_test.go Update test fixture to reflect normalized on-disk plugin install name.
cmd/wfctl/multi_registry_test.go Update default registry expectations and add tests for original-name-first lookup.

Comment on lines 67 to 86
func (m *MultiRegistry) FetchManifest(name string) (*RegistryManifest, string, error) {
normalized := normalizePluginName(name)

// Try normalized name first across all sources.
// Try the original name first across all sources.
var lastErr error
for _, src := range m.sources {
manifest, err := src.FetchManifest(normalized)
manifest, err := src.FetchManifest(name)
if err == nil {
return manifest, src.Name(), nil
}
lastErr = err
}

// If normalized differs from original, try original name as fallback.
// If the normalized name differs from the original, retry with the short name.
// This allows users to omit the "workflow-plugin-" prefix (e.g. "auth" finds
// "workflow-plugin-auth" when no plugin named "auth" exists in any source).
if normalized != name {
for _, src := range m.sources {
manifest, err := src.FetchManifest(name)
manifest, err := src.FetchManifest(normalized)
if err == nil {
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

FetchManifest now prefers the original (possibly prefixed) name, but wfctl plugin install normalizes rawName before calling FetchManifest (plugin_install.go sets pluginName := normalizePluginName(rawName)), so installs triggered by --from-config will still look up auth even when the config explicitly requests workflow-plugin-auth — reintroducing the name-collision bug this change is meant to fix. Consider changing the install call site to pass the unnormalized name to FetchManifest (while still normalizing only for the on-disk install dir), or adjust the API so FetchManifest receives both original+normalized forms.

Copilot uses AI. Check for mistakes.
Comment thread cmd/wfctl/multi_registry.go Outdated
Comment on lines +81 to +82
// This allows users to omit the "workflow-plugin-" prefix (e.g. "auth" finds
// "workflow-plugin-auth" when no plugin named "auth" exists in any source).
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

The fallback comment is misleading: this code only retries with the normalized short name when the caller provided a prefixed name (i.e., normalized != name). It does not allow an unprefixed name like auth to resolve a registry entry named workflow-plugin-auth. Update the comment to reflect the actual behavior to avoid confusion for future changes.

Suggested change
// This allows users to omit the "workflow-plugin-" prefix (e.g. "auth" finds
// "workflow-plugin-auth" when no plugin named "auth" exists in any source).
// This only applies when the caller provided the prefixed form (for example,
// "workflow-plugin-auth"), allowing a second lookup for "auth" if the prefixed
// name was not found in any source.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 22, 2026

⏱ Benchmark Results

No significant performance regressions detected.

benchstat comparison (baseline → PR)
## benchstat: baseline → PR
baseline-bench.txt:245: parsing iteration count: invalid syntax
baseline-bench.txt:357114: parsing iteration count: invalid syntax
baseline-bench.txt:680704: parsing iteration count: invalid syntax
baseline-bench.txt:994718: parsing iteration count: invalid syntax
baseline-bench.txt:1300611: parsing iteration count: invalid syntax
baseline-bench.txt:1635247: parsing iteration count: invalid syntax
benchmark-results.txt:245: parsing iteration count: invalid syntax
benchmark-results.txt:362652: parsing iteration count: invalid syntax
benchmark-results.txt:701809: parsing iteration count: invalid syntax
benchmark-results.txt:1009439: parsing iteration count: invalid syntax
benchmark-results.txt:1328337: parsing iteration count: invalid syntax
benchmark-results.txt:1675090: parsing iteration count: invalid syntax
goos: linux
goarch: amd64
pkg: github.com/GoCodeAlone/workflow/dynamic
cpu: AMD EPYC 9V74 80-Core Processor                
                            │ baseline-bench.txt │        benchmark-results.txt        │
                            │       sec/op       │    sec/op      vs base              │
InterpreterCreation-4              3.293m ± 192%   3.097m ± 210%       ~ (p=0.394 n=6)
ComponentLoad-4                    3.587m ±   1%   3.467m ±  12%       ~ (p=0.065 n=6)
ComponentExecute-4                 1.830µ ±   6%   1.808µ ±   1%  -1.20% (p=0.004 n=6)
PoolContention/workers-1-4         1.038µ ±   6%   1.018µ ±   4%       ~ (p=0.065 n=6)
PoolContention/workers-2-4         1.031µ ±   2%   1.015µ ±   6%       ~ (p=0.240 n=6)
PoolContention/workers-4-4         1.037µ ±   3%   1.018µ ±   1%  -1.88% (p=0.024 n=6)
PoolContention/workers-8-4         1.035µ ±   2%   1.018µ ±   1%  -1.69% (p=0.011 n=6)
PoolContention/workers-16-4        1.034µ ±   1%   1.046µ ±   2%       ~ (p=0.368 n=6)
ComponentLifecycle-4               3.601m ±   1%   3.493m ±   1%  -3.01% (p=0.002 n=6)
SourceValidation-4                 2.112µ ±   3%   2.080µ ±   1%  -1.49% (p=0.024 n=6)
RegistryConcurrent-4               757.4n ±   8%   744.0n ±   8%       ~ (p=0.485 n=6)
LoaderLoadFromString-4             3.633m ±   2%   3.521m ±   1%  -3.07% (p=0.002 n=6)
geomean                            16.89µ          16.52µ         -2.16%

                            │ baseline-bench.txt │        benchmark-results.txt         │
                            │        B/op        │     B/op      vs base                │
InterpreterCreation-4               2.027Mi ± 0%   2.027Mi ± 0%       ~ (p=0.372 n=6)
ComponentLoad-4                     2.180Mi ± 0%   2.180Mi ± 0%       ~ (p=0.786 n=6)
ComponentExecute-4                  1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-1-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-2-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-4-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-8-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-16-4         1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
ComponentLifecycle-4                2.183Mi ± 0%   2.183Mi ± 0%       ~ (p=0.565 n=6)
SourceValidation-4                  1.984Ki ± 0%   1.984Ki ± 0%       ~ (p=1.000 n=6) ¹
RegistryConcurrent-4                1.133Ki ± 0%   1.133Ki ± 0%       ~ (p=1.000 n=6) ¹
LoaderLoadFromString-4              2.182Mi ± 0%   2.182Mi ± 0%       ~ (p=0.331 n=6)
geomean                             15.25Ki        15.25Ki       +0.00%
¹ all samples are equal

                            │ baseline-bench.txt │        benchmark-results.txt        │
                            │     allocs/op      │  allocs/op   vs base                │
InterpreterCreation-4                15.68k ± 0%   15.68k ± 0%       ~ (p=1.000 n=6)
ComponentLoad-4                      18.02k ± 0%   18.02k ± 0%       ~ (p=1.000 n=6)
ComponentExecute-4                    25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-1-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-2-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-4-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-8-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-16-4           25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
ComponentLifecycle-4                 18.07k ± 0%   18.07k ± 0%       ~ (p=1.000 n=6) ¹
SourceValidation-4                    32.00 ± 0%    32.00 ± 0%       ~ (p=1.000 n=6) ¹
RegistryConcurrent-4                  2.000 ± 0%    2.000 ± 0%       ~ (p=1.000 n=6) ¹
LoaderLoadFromString-4               18.06k ± 0%   18.06k ± 0%       ~ (p=1.000 n=6) ¹
geomean                               183.3         183.3       +0.00%
¹ all samples are equal

pkg: github.com/GoCodeAlone/workflow/middleware
                                  │ baseline-bench.txt │       benchmark-results.txt       │
                                  │       sec/op       │   sec/op     vs base              │
CircuitBreakerDetection-4                  297.4n ± 4%   296.7n ± 1%       ~ (p=0.394 n=6)
CircuitBreakerExecution_Success-4          22.66n ± 0%   22.67n ± 0%       ~ (p=0.578 n=6)
CircuitBreakerExecution_Failure-4          70.92n ± 0%   70.97n ± 0%       ~ (p=0.106 n=6)
geomean                                    78.19n        78.15n       -0.04%

                                  │ baseline-bench.txt │       benchmark-results.txt        │
                                  │        B/op        │    B/op     vs base                │
CircuitBreakerDetection-4                 144.0 ± 0%     144.0 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Success-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Failure-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                              ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                  │ baseline-bench.txt │       benchmark-results.txt        │
                                  │     allocs/op      │ allocs/op   vs base                │
CircuitBreakerDetection-4                 1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Success-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Failure-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                              ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/GoCodeAlone/workflow/module
                                 │ baseline-bench.txt │       benchmark-results.txt        │
                                 │       sec/op       │    sec/op     vs base              │
JQTransform_Simple-4                     870.1n ± 26%   878.4n ± 23%       ~ (p=0.818 n=6)
JQTransform_ObjectConstruction-4         1.426µ ±  1%   1.389µ ±  0%  -2.53% (p=0.002 n=6)
JQTransform_ArraySelect-4                3.442µ ±  2%   3.312µ ±  1%  -3.76% (p=0.002 n=6)
JQTransform_Complex-4                    41.79µ ±  1%   40.54µ ±  0%  -3.00% (p=0.002 n=6)
JQTransform_Throughput-4                 1.780µ ±  1%   1.722µ ±  0%  -3.26% (p=0.002 n=6)
SSEPublishDelivery-4                     63.33n ±  2%   63.18n ±  1%       ~ (p=0.310 n=6)
geomean                                  1.649µ         1.616µ        -1.99%

                                 │ baseline-bench.txt │        benchmark-results.txt         │
                                 │        B/op        │     B/op      vs base                │
JQTransform_Simple-4                   1.273Ki ± 0%     1.273Ki ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_ObjectConstruction-4       1.773Ki ± 0%     1.773Ki ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_ArraySelect-4              2.625Ki ± 0%     2.625Ki ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_Complex-4                  16.22Ki ± 0%     16.22Ki ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_Throughput-4               1.984Ki ± 0%     1.984Ki ± 0%       ~ (p=1.000 n=6) ¹
SSEPublishDelivery-4                     0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                             ²                 +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                 │ baseline-bench.txt │       benchmark-results.txt        │
                                 │     allocs/op      │ allocs/op   vs base                │
JQTransform_Simple-4                     10.00 ± 0%     10.00 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_ObjectConstruction-4         15.00 ± 0%     15.00 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_ArraySelect-4                30.00 ± 0%     30.00 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_Complex-4                    324.0 ± 0%     324.0 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_Throughput-4                 17.00 ± 0%     17.00 ± 0%       ~ (p=1.000 n=6) ¹
SSEPublishDelivery-4                     0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                             ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/GoCodeAlone/workflow/schema
                                    │ baseline-bench.txt │       benchmark-results.txt        │
                                    │       sec/op       │    sec/op     vs base              │
SchemaValidation_Simple-4                    1.079µ ± 9%   1.062µ ± 10%       ~ (p=0.615 n=6)
SchemaValidation_AllFields-4                 1.631µ ± 4%   1.625µ ±  5%       ~ (p=0.721 n=6)
SchemaValidation_FormatValidation-4          1.554µ ± 1%   1.558µ ±  2%       ~ (p=0.667 n=6)
SchemaValidation_ManySchemas-4               1.596µ ± 2%   1.579µ ±  2%       ~ (p=0.420 n=6)
geomean                                      1.445µ        1.435µ        -0.70%

                                    │ baseline-bench.txt │       benchmark-results.txt        │
                                    │        B/op        │    B/op     vs base                │
SchemaValidation_Simple-4                   0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_AllFields-4                0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_FormatValidation-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_ManySchemas-4              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                    │ baseline-bench.txt │       benchmark-results.txt        │
                                    │     allocs/op      │ allocs/op   vs base                │
SchemaValidation_Simple-4                   0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_AllFields-4                0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_FormatValidation-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_ManySchemas-4              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/GoCodeAlone/workflow/store
                                   │ baseline-bench.txt │        benchmark-results.txt        │
                                   │       sec/op       │    sec/op     vs base               │
EventStoreAppend_InMemory-4                1.209µ ± 18%   1.106µ ± 20%        ~ (p=0.699 n=6)
EventStoreAppend_SQLite-4                  1.187m ±  7%   1.045m ±  9%  -11.93% (p=0.004 n=6)
GetTimeline_InMemory/events-10-4           12.61µ ±  2%   12.52µ ±  1%        ~ (p=0.093 n=6)
GetTimeline_InMemory/events-50-4           71.73µ ±  4%   69.92µ ±  1%        ~ (p=0.132 n=6)
GetTimeline_InMemory/events-100-4          114.7µ ± 27%   107.6µ ± 15%   -6.18% (p=0.026 n=6)
GetTimeline_InMemory/events-500-4          577.8µ ±  3%   550.7µ ±  1%   -4.70% (p=0.002 n=6)
GetTimeline_InMemory/events-1000-4         1.172m ±  5%   1.116m ±  1%   -4.77% (p=0.002 n=6)
GetTimeline_SQLite/events-10-4             86.96µ ±  4%   85.05µ ±  1%   -2.19% (p=0.002 n=6)
GetTimeline_SQLite/events-50-4             226.7µ ±  1%   222.1µ ±  1%   -2.05% (p=0.002 n=6)
GetTimeline_SQLite/events-100-4            396.0µ ±  1%   386.2µ ±  0%   -2.47% (p=0.002 n=6)
GetTimeline_SQLite/events-500-4            1.718m ±  1%   1.693m ±  1%   -1.46% (p=0.009 n=6)
GetTimeline_SQLite/events-1000-4           3.391m ±  1%   3.285m ±  9%        ~ (p=0.065 n=6)
geomean                                    201.9µ         193.3µ         -4.27%

                                   │ baseline-bench.txt │         benchmark-results.txt         │
                                   │        B/op        │     B/op       vs base                │
EventStoreAppend_InMemory-4                  784.5 ± 4%     782.0 ± 10%       ~ (p=1.000 n=6)
EventStoreAppend_SQLite-4                  1.982Ki ± 2%   1.985Ki ±  1%       ~ (p=0.255 n=6)
GetTimeline_InMemory/events-10-4           7.953Ki ± 0%   7.953Ki ±  0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-50-4           46.62Ki ± 0%   46.62Ki ±  0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-100-4          94.48Ki ± 0%   94.48Ki ±  0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-500-4          472.8Ki ± 0%   472.8Ki ±  0%  +0.00% (p=0.015 n=6)
GetTimeline_InMemory/events-1000-4         944.3Ki ± 0%   944.3Ki ±  0%       ~ (p=0.636 n=6)
GetTimeline_SQLite/events-10-4             16.74Ki ± 0%   16.74Ki ±  0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-50-4             87.14Ki ± 0%   87.14Ki ±  0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-100-4            175.4Ki ± 0%   175.4Ki ±  0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-500-4            846.1Ki ± 0%   846.1Ki ±  0%  -0.00% (p=0.013 n=6)
GetTimeline_SQLite/events-1000-4           1.639Mi ± 0%   1.639Mi ±  0%       ~ (p=0.636 n=6)
geomean                                    67.30Ki        67.29Ki        -0.01%
¹ all samples are equal

                                   │ baseline-bench.txt │        benchmark-results.txt        │
                                   │     allocs/op      │  allocs/op   vs base                │
EventStoreAppend_InMemory-4                  7.000 ± 0%    7.000 ± 0%       ~ (p=1.000 n=6) ¹
EventStoreAppend_SQLite-4                    53.00 ± 0%    53.00 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-10-4             125.0 ± 0%    125.0 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-50-4             653.0 ± 0%    653.0 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-100-4           1.306k ± 0%   1.306k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-500-4           6.514k ± 0%   6.514k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-1000-4          13.02k ± 0%   13.02k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-10-4               382.0 ± 0%    382.0 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-50-4              1.852k ± 0%   1.852k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-100-4             3.681k ± 0%   3.681k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-500-4             18.54k ± 0%   18.54k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-1000-4            37.29k ± 0%   37.29k ± 0%       ~ (p=1.000 n=6) ¹
geomean                                     1.162k        1.162k       +0.00%
¹ all samples are equal

Benchmarks run with go test -bench=. -benchmem -count=6.
Regressions ≥ 20% are flagged. Results compared via benchstat.

…kup engages

plugin_install.go and plugin_deps.go (runPluginDeps) were normalizing rawName →
pluginName before calling mr.FetchManifest, so "workflow-plugin-auth" arrived at
MultiRegistry as "auth" and the original-name-first fix from the previous commit
never fired.

Fix: pass rawName directly to mr.FetchManifest; keep pluginName (normalized) for
the on-disk install-directory path only. This lets MultiRegistry try
"workflow-plugin-auth" in the registry before falling back to "auth", preventing
the collision with the builtin auth module.

Also tighten the comment in multi_registry.go line ~82 (Copilot review) to
accurately describe the short-name fallback direction.

New test: TestRunPluginInstall_FullNameNotNormalizedBeforeLookup — httptest server
exposes both /plugins/workflow-plugin-auth/manifest.json (external v0.1.2, has
downloads) and /plugins/auth/manifest.json (builtin v0.3.51, no downloads). The
test asserts that installing "workflow-plugin-auth@v0.1.2" hits the external
manifest and installs v0.1.2, not the builtin.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@intel352
Copy link
Copy Markdown
Contributor Author

Addressed both Copilot review comments:

Comment 1 — regression (normalization before FetchManifest): Confirmed correct. plugin_install.go:120 and plugin_deps.go:96 both called normalizePluginName(rawName) before mr.FetchManifest(pluginName), so workflow-plugin-auth arrived at MultiRegistry as auth and the original-name-first fix never engaged. Fix (commit f1f6a5a): pass rawName directly to FetchManifest; keep pluginName (normalized) only for the on-disk install directory. Added TestRunPluginInstall_FullNameNotNormalizedBeforeLookup — httptest server with both /plugins/workflow-plugin-auth/manifest.json (external v0.1.2, has downloads) and /plugins/auth/manifest.json (builtin v0.3.51, no downloads); asserts the external version is installed.

Comment 2 — misleading comment (multi_registry.go ~line 82): Updated the comment from "auth finds workflow-plugin-auth" to accurately describe that the short-name fallback lets callers resolve a registry entry named auth when no entry named auth exists under the original full name.

All tests pass (GOWORK=off go test ./cmd/wfctl/... -race).

@intel352 intel352 merged commit 53ba1ff into main Apr 22, 2026
18 checks passed
@intel352 intel352 deleted the fix/plugin-install-from-config-bugs branch April 22, 2026 07:31
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.

2 participants