Skip to content

wfctl plugin conformance: artifact mode binary discovery#650

Merged
intel352 merged 4 commits into
mainfrom
copilot/fix-artifact-mode-binary-discovery
May 14, 2026
Merged

wfctl plugin conformance: artifact mode binary discovery#650
intel352 merged 4 commits into
mainfrom
copilot/fix-artifact-mode-binary-discovery

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 13, 2026

Artifact mode only looked for a binary named normalizePluginName(manifest.Name) (e.g. digitalocean). GoReleaser archives ship binaries named after the full project (e.g. workflow-plugin-digitalocean), causing artifact conformance to fall through to go build, fail with exit status 1, and write no evidence.

Changes

plugin_conformance.go

  • discoverArtifactBinaryCandidates(sourceDir, manifestName, installName) — new function returning candidates in priority order:

    1. installName (normalized, e.g. digitalocean)
    2. manifestName when it differs (full name, e.g. workflow-plugin-digitalocean)
    3. Windows .exe variants of the above
    4. Any other executable in the archive root whose name starts with installName or manifestName (case-insensitive), e.g. digitalocean_linux_amd64. This covers platform-suffixed GoReleaser binaries while preventing execution of arbitrary unrelated executables bundled in the archive.
  • runPluginConformanceCheck — artifact path now:

    • Calls discoverArtifactBinaryCandidates, then runs the go-plugin handshake on each candidate in order
    • Selects the first that passes; prepends candidate diagnostics to stderrTail in evidence
    • If every candidate fails the handshake and go.mod is absent (binary-only artifact) → returns status: fail evidence with per-candidate failure reasons instead of a bare exit status 1
    • If every candidate fails the handshake but go.mod exists (source-in-archive tarball) → falls back to go build, preserving existing behaviour for source tarballs that happen to include a pre-built or unrelated executable
    • If no executables matching the naming patterns are found → falls through to go build directly
artifact binary discovery: install="digitalocean" manifest="workflow-plugin-digitalocean" candidates=[digitalocean, workflow-plugin-digitalocean]
  [skip] "digitalocean": stat …: no such file or directory
  [pass] "workflow-plugin-digitalocean" selected

plugin_conformance_test.go

  • writeTarGzFiles helper: creates binary-only archives from map[name→srcPath]
  • buildFixtureBinary helper: compiles a fixture to a named binary for use in archive tests
  • TestDiscoverArtifactBinaryCandidates — verifies priority ordering; platform-suffixed names (e.g. digitalocean_linux_amd64) are included; unrelated executables (e.g. some-helper) are excluded
  • TestDiscoverArtifactBinaryCandidatesSameNames — deduplication when installName and manifestName are identical
  • TestPluginConformanceArtifactDiscoversByManifestName — archive containing only workflow-plugin-iac-pass (no iac-pass) → pass
  • TestPluginConformanceArtifactWrongBinaryEmitsEvidence — non-plugin executable → status: fail evidence with diagnostic lines naming the candidate
  • TestPluginConformanceArtifactGoModFallback — source archive with a non-plugin executable matching the name prefix → handshake fails → go.mod present → go build fallback → pass
  • TestPluginConformanceArtifactNoGoModFailsCleanly — binary-only archive (no go.mod) with a non-plugin executable → status: fail evidence with diagnostics

docs/WFCTL.md

Documents the multi-candidate discovery, handshake-verification, and go.mod-based fallback behaviour under plugin conformance.

Copilot AI requested review from Copilot and removed request for Copilot May 13, 2026 05:58
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 13, 2026

⏱ Benchmark Results

No significant performance regressions detected.

benchstat comparison (baseline → PR)
## benchstat: baseline → PR
baseline-bench.txt:264: parsing iteration count: invalid syntax
baseline-bench.txt:281038: parsing iteration count: invalid syntax
baseline-bench.txt:532724: parsing iteration count: invalid syntax
baseline-bench.txt:811272: parsing iteration count: invalid syntax
baseline-bench.txt:1081828: parsing iteration count: invalid syntax
baseline-bench.txt:1579256: parsing iteration count: invalid syntax
benchmark-results.txt:264: parsing iteration count: invalid syntax
benchmark-results.txt:330994: parsing iteration count: invalid syntax
benchmark-results.txt:633536: parsing iteration count: invalid syntax
benchmark-results.txt:992613: parsing iteration count: invalid syntax
benchmark-results.txt:1312273: parsing iteration count: invalid syntax
benchmark-results.txt:1575749: parsing iteration count: invalid syntax
goos: linux
goarch: amd64
pkg: github.com/GoCodeAlone/workflow/dynamic
cpu: AMD EPYC 7763 64-Core Processor                
                            │ baseline-bench.txt │       benchmark-results.txt        │
                            │       sec/op       │    sec/op     vs base              │
InterpreterCreation-4               9.690m ± 64%   7.406m ± 58%       ~ (p=0.485 n=6)
ComponentLoad-4                     3.978m ± 14%   3.587m ±  9%  -9.84% (p=0.002 n=6)
ComponentExecute-4                  2.141µ ±  1%   1.949µ ±  0%  -8.97% (p=0.002 n=6)
PoolContention/workers-1-4          1.188µ ±  2%   1.083µ ±  1%  -8.88% (p=0.002 n=6)
PoolContention/workers-2-4          1.194µ ±  3%   1.088µ ±  2%  -8.88% (p=0.002 n=6)
PoolContention/workers-4-4          1.180µ ±  1%   1.089µ ±  1%  -7.71% (p=0.002 n=6)
PoolContention/workers-8-4          1.168µ ±  1%   1.095µ ±  0%  -6.25% (p=0.002 n=6)
PoolContention/workers-16-4         1.175µ ±  2%   1.105µ ±  4%  -6.04% (p=0.002 n=6)
ComponentLifecycle-4                3.962m ±  0%   3.700m ±  1%  -6.62% (p=0.002 n=6)
SourceValidation-4                  2.558µ ±  2%   2.418µ ±  1%  -5.45% (p=0.002 n=6)
RegistryConcurrent-4                861.2n ±  2%   867.6n ±  5%       ~ (p=0.556 n=6)
LoaderLoadFromString-4              4.023m ±  1%   3.708m ±  1%  -7.84% (p=0.002 n=6)
geomean                             20.83µ         19.07µ        -8.44%

                            │ baseline-bench.txt │        benchmark-results.txt         │
                            │        B/op        │     B/op      vs base                │
InterpreterCreation-4               2.027Mi ± 0%   2.027Mi ± 0%       ~ (p=0.784 n=6)
ComponentLoad-4                     2.180Mi ± 0%   2.180Mi ± 0%       ~ (p=0.937 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.848 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.699 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                  321.1n ± 1%   286.0n ± 5%  -10.92% (p=0.002 n=6)
CircuitBreakerExecution_Success-4          23.62n ± 2%   21.54n ± 0%   -8.83% (p=0.002 n=6)
CircuitBreakerExecution_Failure-4          69.65n ± 1%   66.39n ± 0%   -4.68% (p=0.002 n=6)
geomean                                    80.84n        74.23n        -8.18%

                                  │ 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                    1036.5n ± 24%   858.1n ± 29%        ~ (p=0.121 n=6)
JQTransform_ObjectConstruction-4         1.591µ ±  2%   1.420µ ±  0%  -10.72% (p=0.002 n=6)
JQTransform_ArraySelect-4                3.728µ ± 10%   3.309µ ±  2%  -11.24% (p=0.002 n=6)
JQTransform_Complex-4                    42.75µ ±  2%   38.12µ ±  2%  -10.82% (p=0.002 n=6)
JQTransform_Throughput-4                 1.961µ ±  2%   1.764µ ±  4%  -10.05% (p=0.002 n=6)
SSEPublishDelivery-4                     70.85n ±  6%   63.36n ± 10%  -10.57% (p=0.002 n=6)
geomean                                  1.821µ         1.606µ        -11.80%

                                 │ 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.228µ ±  5%   1.110µ ±  2%   -9.65% (p=0.002 n=6)
SchemaValidation_AllFields-4                1.888µ ± 18%   1.653µ ± 28%  -12.45% (p=0.041 n=6)
SchemaValidation_FormatValidation-4         1.766µ ±  5%   1.585µ ±  1%  -10.25% (p=0.002 n=6)
SchemaValidation_ManySchemas-4              2.042µ ±  4%   1.830µ ±  2%  -10.36% (p=0.002 n=6)
geomean                                     1.700µ         1.519µ        -10.68%

                                    │ 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.600µ ± 20%   1.248µ ± 11%  -21.97% (p=0.026 n=6)
EventStoreAppend_SQLite-4                  2.229m ± 17%   1.341m ±  2%  -39.84% (p=0.002 n=6)
GetTimeline_InMemory/events-10-4           15.06µ ±  5%   13.31µ ±  6%  -11.58% (p=0.002 n=6)
GetTimeline_InMemory/events-50-4           84.72µ ±  3%   75.91µ ±  3%  -10.41% (p=0.002 n=6)
GetTimeline_InMemory/events-100-4          167.7µ ± 19%   121.6µ ±  0%  -27.47% (p=0.002 n=6)
GetTimeline_InMemory/events-500-4          706.9µ ±  1%   623.7µ ±  0%  -11.76% (p=0.002 n=6)
GetTimeline_InMemory/events-1000-4         1.451m ±  1%   1.272m ±  1%  -12.33% (p=0.002 n=6)
GetTimeline_SQLite/events-10-4             117.2µ ±  1%   105.0µ ±  2%  -10.46% (p=0.002 n=6)
GetTimeline_SQLite/events-50-4             277.6µ ±  1%   245.3µ ±  1%  -11.62% (p=0.002 n=6)
GetTimeline_SQLite/events-100-4            480.6µ ±  2%   415.1µ ±  1%  -13.63% (p=0.002 n=6)
GetTimeline_SQLite/events-500-4            2.029m ±  1%   1.775m ±  1%  -12.52% (p=0.002 n=6)
GetTimeline_SQLite/events-1000-4           3.961m ±  1%   3.453m ±  1%  -12.85% (p=0.002 n=6)
geomean                                    260.6µ         216.6µ        -16.88%

                                   │ baseline-bench.txt │         benchmark-results.txt         │
                                   │        B/op        │     B/op       vs base                │
EventStoreAppend_InMemory-4                 782.0 ± 11%     770.5 ± 12%       ~ (p=0.699 n=6)
EventStoreAppend_SQLite-4                 1.986Ki ±  3%   1.984Ki ±  1%       ~ (p=0.937 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%       ~ (p=1.000 n=6)
GetTimeline_InMemory/events-1000-4        944.3Ki ±  0%   944.3Ki ±  0%       ~ (p=0.987 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%       ~ (p=0.273 n=6)
GetTimeline_SQLite/events-1000-4          1.639Mi ±  0%   1.639Mi ±  0%       ~ (p=0.165 n=6)
geomean                                   67.29Ki         67.20Ki        -0.13%
¹ 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.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 13, 2026

Codecov Report

❌ Patch coverage is 80.73394% with 21 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
cmd/wfctl/plugin_conformance.go 80.73% 13 Missing and 8 partials ⚠️

📢 Thoughts on this report? Let us know!

Copilot AI requested review from Copilot and removed request for Copilot May 13, 2026 06:37
Copilot AI requested review from Copilot and removed request for Copilot May 13, 2026 06:38
Copilot AI changed the title [WIP] Fix wfctl plugin conformance artifact mode to discover release binaries wfctl plugin conformance: artifact mode binary discovery May 13, 2026
Copilot AI requested a review from intel352 May 13, 2026 06:40
@intel352 intel352 marked this pull request as ready for review May 13, 2026 22:15
Copilot AI review requested due to automatic review settings May 13, 2026 22:15
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

Improves wfctl plugin conformance artifact-mode usability by discovering and validating plugin binaries under multiple candidate names (install name vs manifest/project name), then emitting clearer evidence/diagnostics when selection or handshake fails—aimed at supporting common GoReleaser archive naming.

Changes:

  • Add multi-candidate artifact binary discovery + per-candidate handshake attempts, with diagnostics included in evidence output.
  • Expand conformance tests with helpers for building fixture binaries and constructing binary-only tar.gz archives; add coverage for discovery ordering and failure-evidence behavior.
  • Document the new artifact binary discovery/handshake behavior in docs/WFCTL.md.

Reviewed changes

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

Show a summary per file
File Description
cmd/wfctl/plugin_conformance.go Implements multi-candidate artifact binary discovery and enhanced evidence diagnostics for artifact conformance.
cmd/wfctl/plugin_conformance_test.go Adds helpers and new unit/integration tests validating candidate ordering, selection by manifest name, and failure evidence.
docs/WFCTL.md Documents artifact-mode multi-candidate discovery and handshake verification behavior.
plugin/external/sdk/iacserver_test.go Minor formatting change for an embedded stub type in tests.
cmd/wfctl/deploy_providers.go Minor struct field alignment/formatting change.

Comment thread cmd/wfctl/plugin_conformance.go Outdated
Comment on lines +407 to +435
// Scan the archive root for any additional executables not already considered.
entries, err := os.ReadDir(sourceDir)
if err != nil {
return out
}
for _, entry := range entries {
if entry.IsDir() {
continue
}
name := entry.Name()
if seen[name] {
continue
}
seen[name] = true
info, statErr := entry.Info()
if statErr != nil {
continue
}
if info.Mode()&0o111 == 0 {
continue
}
// Skip files with well-known non-binary extensions.
switch strings.ToLower(filepath.Ext(name)) {
case ".json", ".yaml", ".yml", ".md", ".txt", ".sh", ".bat", ".ps1",
".go", ".sum", ".mod", ".toml", ".lock", ".html", ".xml":
continue
}
out = append(out, name)
}
Comment on lines +232 to +307
if opts.ArtifactPath != "" {
candidates := discoverArtifactBinaryCandidates(sourceDir, manifest.Name, installName)
if len(candidates) > 0 {
var diagLines []string
diagLines = append(diagLines, fmt.Sprintf("artifact binary discovery: install=%q manifest=%q candidates=[%s]",
installName, manifest.Name, strings.Join(candidates, ", ")))

var lastCheckErr error
for _, cand := range candidates {
srcPath := filepath.Join(sourceDir, cand)
srcInfo, statErr := os.Stat(srcPath)
if statErr != nil {
diagLines = append(diagLines, fmt.Sprintf(" [skip] %q: %v", cand, statErr))
continue
}
if copyErr := copyFile(srcPath, binaryPath, srcInfo.Mode()); copyErr != nil {
diagLines = append(diagLines, fmt.Sprintf(" [fail] %q: copy error: %v", cand, copyErr))
lastCheckErr = copyErr
continue
}
cstdout, cstderr, checkErr := checkTypedIaCPlugin(opts.Timeout, filepath.Join(tmp, "plugins"), installName)
conformanceStdout = cstdout
conformanceStderr = cstderr
if checkErr == nil {
diagLines = append(diagLines, fmt.Sprintf(" [pass] %q selected", cand))
conformanceChecked = true
lastCheckErr = nil
break
}
lastCheckErr = checkErr
diagLines = append(diagLines, fmt.Sprintf(" [fail] %q: %v", cand, checkErr))
}

diagMsg := strings.Join(diagLines, "\n")
if conformanceStderr != "" {
conformanceStderr = diagMsg + "\n" + conformanceStderr
} else {
conformanceStderr = diagMsg
}

if !conformanceChecked {
// All candidates failed; build and return fail evidence with diagnostics.
if lastCheckErr == nil {
lastCheckErr = fmt.Errorf("no executable artifact candidate could be staged from archive (candidates: %s)", strings.Join(candidates, ", "))
}
manifestSHA, _ := hashFileSHA256(filepath.Join(installDir, "plugin.json"))
binarySHA := ""
if _, statErr := os.Stat(binaryPath); statErr == nil {
binarySHA, _ = hashFileSHA256(binaryPath)
}
ev := PluginCompatibilityEvidence{
Plugin: manifest.Name,
Version: manifest.Version,
EngineVersion: opts.EngineVersion,
WfctlVersion: buildVersion(),
Mode: opts.Mode,
Status: PluginCompatibilityStatusFail,
OS: runtime.GOOS,
Arch: runtime.GOARCH,
ArchiveSHA256: archiveSHA,
BinarySHA256: binarySHA,
PluginManifestSHA256: manifestSHA,
GeneratedBy: "wfctl plugin conformance",
StdoutTail: conformanceStdout,
StderrTail: conformanceStderr,
}
if normalized, normErr := ValidateCompatibilityEvidence(ev); normErr == nil {
ev = normalized
}
return ev, lastCheckErr
}
// conformanceChecked=true: a candidate passed; binary is at binaryPath.
}
} else {
// len(candidates)==0: no executables found in archive root; fall through to go build
// below (supports source-in-archive tarballs that include Go source).
}
@intel352
Copy link
Copy Markdown
Contributor

@copilot apply changes based on the comments in this thread

and fix coverage: #650 (comment)

…for source tarballs

Agent-Logs-Url: https://github.com/GoCodeAlone/workflow/sessions/bb2b2d56-8dd7-4665-b9bf-35b09f4ddb47

Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 14, 2026

@copilot apply changes based on the comments in this thread

and fix coverage: https://github.com/G...

Applied in commit 432fec7:

  1. Restricted fallback scandiscoverArtifactBinaryCandidates now only includes executables whose names start with installName or manifestName (case-insensitive). Arbitrary binaries in the archive (e.g. some-helper) are never executed; only plausible plugin names like digitalocean_linux_amd64 qualify.

  2. go.mod fallback — when all named candidates fail the handshake, the code checks for go.mod. If present (source-in-archive tarball), it falls through to go build instead of emitting fail evidence immediately. If absent (binary-only artifact), it returns fail evidence with per-candidate diagnostics as before. binaryPath is removed before the go build to avoid the "not an object file" error when a failed candidate was previously staged there.

  3. Coverage — added TestPluginConformanceArtifactGoModFallback (covers the new fallback path) and TestPluginConformanceArtifactNoGoModFailsCleanly (covers binary-only artifact fail path); updated TestDiscoverArtifactBinaryCandidates to verify the restricted scan.

@intel352 intel352 merged commit 0339c2d into main May 14, 2026
25 checks passed
@intel352 intel352 deleted the copilot/fix-artifact-mode-binary-discovery branch May 14, 2026 00:16
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.

wfctl plugin conformance artifact mode should discover release binary names

3 participants