Conversation
…in.json Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
capabilities in plugin.json
There was a problem hiding this comment.
Pull request overview
This PR updates plugin manifest parsing to be backward-compatible with external plugins that encode capabilities as a JSON object (with nested moduleTypes/stepTypes/triggerTypes), preventing LoadManifest failures that caused tooling like wfctl docs generate to skip those plugins.
Changes:
- Added
PluginManifest.UnmarshalJSONto support both canonical[]CapabilityDeclcapabilities and a legacy object format, promoting nested type lists to top-level fields. - Introduced
legacyCapabilitiesObjectand anappendUniquehelper to merge/deduplicate promoted type lists. - Added tests covering legacy object-style capabilities via both direct
json.UnmarshalandLoadManifest.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| plugin/manifest.go | Adds custom JSON unmarshalling to gracefully handle legacy object-style capabilities and promote nested type lists. |
| plugin/manifest_test.go | Adds coverage for parsing/loading manifests that use legacy object-style capabilities. |
| func TestManifestLegacyCapabilitiesObject(t *testing.T) { | ||
| const legacyJSON = `{ | ||
| "name": "workflow-plugin-authz", |
* docs: add wfctl audit and plugin ecosystem design Addresses PRs #321, #322, and issue #316. Covers 13 CLI fixes, 5 registry data fixes, and holistic plugin ecosystem plan including goreleaser standardization, GitHub URL install, lockfile support, minEngineVersion checks, and auto-sync CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add wave 2 integration plugins design + release/validation plan Wave 2 design: Okta (~130 steps), Datadog (~120), LaunchDarkly (~100), Permit.io (~80), Salesforce (~75), OpenLMS (~120) — all MIT, community tier. Release plan: tag untagged plugins, create validation scenarios 51-59 with mock HTTP backends in workflow-scenarios. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: add wfctl audit implementation plan (20 tasks) Phase A: 13 CLI fixes (help exit codes, plugin-dir rename, flag ordering, etc.) Phase B: 2 registry data fix tasks Phase C: 5 plugin ecosystem tasks (GitHub URL install, lockfile, goreleaser) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: add B5 schema validation gap to Task 14 Alignment check identified missing coverage for design requirement B5. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: move Permit.io into workflow-plugin-authz as provider Permit.io fits alongside Casbin in the existing authz plugin rather than a standalone repo. Same multi-provider pattern as payments (Stripe + PayPal). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: rename -data-dir to -plugin-dir in plugin subcommands Rename the flag from -data-dir to -plugin-dir across runPluginInstall, runPluginList, runPluginUpdate, runPluginRemove, and runPluginInfo for consistency with other wfctl commands. Keep -data-dir as a working deprecated alias. Update pluginUsage() to document -plugin-dir. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(wfctl): add trailing flag detection helper Add checkTrailingFlags() to detect when flags are passed after positional arguments, and wire it into runPluginInit, runRegistryAdd, and runRegistryRemove. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: --help exits 0 and suppresses engine error leakage Add isHelpRequested() helper that detects flag.ErrHelp propagated through the pipeline engine. In main(): - No-args case now exits 0 (showing help is not an error) - Help requests in dispatch exit 0 without printing engine error Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(wfctl): add plugin name normalization to multi-registry Add normalizePluginName() stripping "workflow-plugin-" prefix so users can reference plugins by short name (e.g. "authz") or full name (e.g. "workflow-plugin-authz") interchangeably. Wire into FetchManifest and SearchPlugins. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: validate --dir skips non-workflow YAML files Add isWorkflowYAML() that checks the first 100 lines of a file for top-level modules:, workflows:, or pipelines: keys. Files found by --dir that don't match are skipped with a stderr message, preventing false validation failures on GitHub Actions CI files and other YAML. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add version check to plugin update command Before downloading, compare installed plugin.json version against the registry manifest version. If equal, print "already at latest version" and skip. If different, print "Updating from X to Y..." and proceed. Also adds -config flag to plugin update for registry config override. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: infra commands show actionable error when no config found Improve the error message from the generic "no config file found" to a helpful message that explains what the config needs and how to generate one. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(wfctl): add TestPluginInstallRespectsPluginDir Verify that install uses -plugin-dir (custom dir) instead of the default data/plugins path. This guards the fix from Task 2 that updated runPluginUpdate to pass --plugin-dir, not --data-dir, when calling runPluginInstall. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: handle missing go.sum in init Dockerfile templates Replace `COPY go.mod go.sum ./` with `COPY go.mod ./` + `COPY go.sum* ./` so the generated Dockerfile works whether or not go.sum exists yet. The glob pattern in the second COPY is a no-op when go.sum is absent. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: log resolved imports during validate Read raw YAML to extract the imports: list before calling config.LoadFromFile. After loading, print "Resolved N import(s): ..." to stderr so users can see which files were included. This makes the validate command transparent about include/import resolution. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(wfctl): accept positional config arg in deploy subcommands Allow users to pass the config file as a positional argument instead of always requiring -config, e.g. `wfctl deploy cloud myapp.yaml`. Applied to: runDeployDocker, runK8sGenerate, runK8sApply, runDeployCloud. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: PluginManifest UnmarshalJSON handles legacy capabilities object format Add custom UnmarshalJSON on PluginManifest that detects whether the capabilities field is an array (new CapabilityDecl format) or an object (legacy registry format with moduleTypes/stepTypes/triggerTypes). When the legacy object format is detected, its type lists are merged into the top-level ModuleTypes, StepTypes, and TriggerTypes fields so callers always find types in a consistent location regardless of plugin.json format. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: plugin info shows absolute binary path Resolve pluginDir to absolute path before displaying the binary path, so users see /absolute/path/to/plugin instead of relative data/plugins/foo. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: engine warns when plugin minEngineVersion exceeds current version Add MinEngineVersion field to PluginManifest and checkEngineCompatibility() to plugin/loader.go. The loader calls this on every plugin load — if the running engine version is older than the plugin's declared minimum, a WARNING is printed to stderr. No hard fail to allow testing newer plugins against older engines. SetEngineVersion() on PluginLoader threads the engine version through. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add GitHub URL install support to plugin install Add parseGitHubRef() to detect owner/repo[@Version] references. Add installFromGitHub() to query GitHub Releases API and download assets matching {repo}_{os}_{arch}.tar.gz. In runPluginInstall, when the registry lookup fails and the input looks like a GitHub ref, fall back to direct GitHub installation. This allows: wfctl plugin install GoCodeAlone/workflow-plugin-authz@v0.3.1 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: plugin lockfile support via .wfctl.yaml plugins section Add PluginLockfile/PluginLockEntry types with load/save that preserve all other .wfctl.yaml fields (project, git, deploy) on round-trip. Wire into plugin install: - `wfctl plugin install` (no args): reads .wfctl.yaml plugins section and installs all pinned entries - `wfctl plugin install <name>@<version>`: after successful install, updates/creates the plugins entry in .wfctl.yaml Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: add plugin goreleaser reference config Reference .goreleaser.yml for plugin repos documenting required conventions: - binary using {{ .ProjectName }} template - archives include plugin.json - before/after hooks to template plugin.json version from release tag Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: correct FetchManifest arg and destDir for GitHub installs Two bugs in Task 16 GitHub install fallback: 1. Pass pluginName (version-stripped) to FetchManifest, not nameArg which may carry @Version suffix and cause lookup failures. 2. Compute destDir after normalizing the repo short name so owner/repo installs go to data/plugins/<shortname> not the raw input. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: lockfile install doesn't re-pin; engine compat uses slog Task 17: installFromLockfile now passes just the plugin name (no @Version) to runPluginInstall so updateLockfile doesn't fire and overwrite the pinned entry in .wfctl.yaml during a lockfile-driven restore. Task 18: checkEngineCompatibility uses slog.Warn instead of fmt.Fprintf to stderr, consistent with the rest of the codebase. Remove unused "os" import from loader.go. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: use published @gocodealone/workflow-editor from GitHub Packages Replace local file path reference with ^0.2.0 from GitHub Packages registry. Fixes CI failures where the local tarball doesn't exist. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove trailing punctuation from infra error string (ST1005) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
|
@copilot apply changes based on the comments in this thread Also fix the merge conflict |
Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Done in 5932d16. |
|
@copilot Fix the merge conflict |
…awCaps pattern, move Dependency Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Fixed in 62dfb7f. Incorporated all structural changes from |
⏱ Benchmark Results✅ No significant performance regressions detected. benchstat comparison (baseline → PR)
|
External plugins (e.g.
workflow-plugin-authz) declarecapabilitiesas a flat JSON object withmoduleTypes/stepTypes/triggerTypeskeys, whilePluginManifest.Capabilitiesexpects[]CapabilityDecl. This causedLoadManifestto fail, andwfctl docs generateto skip those plugins with a warning.Changes
plugin/manifest.go— AddedUnmarshalJSONonPluginManifestthat dispatches on the first non-whitespace byte of the rawcapabilitiesJSON:[→ decoded as canonical[]CapabilityDeclarray{→ decoded as legacy object format;moduleTypes/stepTypes/triggerTypes/workflowTypesare promoted into the top-level manifest fields viaappendUnique(deduplicating against any already-present values)null/absent → treated as omittedLoadManifestsurfaces it instead of silently accepting a malformed manifestUses
type rawManifest PluginManifest+type withRawCaps structto break theUnmarshalJSONrecursion (cleaner than the embedded-alias approach).Added
firstNonSpacehelper for whitespace-safe peeking andappendUniquehelper for deduplicating merged type lists.Added
MinEngineVersionfield toPluginManifestand movedDependencystruct to immediately followCapabilityDecl, aligning with upstreammain.plugin/manifest_test.go— Added:TestManifestLegacyCapabilitiesObject— verifies the legacy object format is parsed correctly and type lists are promoted to top-level fieldsTestManifestLegacyCapabilitiesObjectFile— verifiesLoadManifestsucceeds end-to-end for a plugin.json using the legacy formatTestManifestCapabilitiesInvalidFormat— negative cases (string, number, bool) confirming that unsupportedcapabilitiestypes are rejected with an error💬 Send tasks to Copilot coding agent from Slack and Teams to turn conversations into code. Copilot posts an update in your thread when it's finished.