Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion .github/workflows/create-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ jobs:
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.next_version.outputs.tag }}
release_tag: ${{ steps.next_version.outputs.tag }}

steps:
- name: Check out code
Expand Down Expand Up @@ -81,12 +82,24 @@ jobs:
git tag "${{ steps.next_version.outputs.tag }}"
git push origin "${{ steps.next_version.outputs.tag }}"

- name: Write release metadata
run: |
mkdir -p release-metadata
printf '%s\n' "${{ steps.next_version.outputs.tag }}" > release-metadata/release-tag.txt

- name: Upload release metadata
uses: actions/upload-artifact@v4
with:
name: release-metadata
path: release-metadata/release-tag.txt
if-no-files-found: error

release:
name: Build and publish release
needs: tag
uses: ./.github/workflows/release.yml
with:
tag_name: ${{ needs.tag.outputs.tag }}
tag_name: ${{ needs.tag.outputs.release_tag }}
secrets:
repo_dispatch_token: ${{ secrets.repo_dispatch_token }}
permissions:
Expand Down
5 changes: 3 additions & 2 deletions cmd/wfctl/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,11 +284,12 @@ func renderPluginAuditReport(out io.Writer, report pluginAuditReport) {
}

func pluginContractCoverageSummary(coverage pluginContractCoverage) string {
return fmt.Sprintf("module %d/%d strict, step %d/%d strict, trigger %d/%d strict, service method %d/%d strict",
return fmt.Sprintf("module %d/%d strict, step %d/%d strict, trigger %d/%d strict, service method %d/%d strict, message %d/%d strict",
coverage.Modules.Strict, coverage.Modules.Total,
coverage.Steps.Strict, coverage.Steps.Total,
coverage.Triggers.Strict, coverage.Triggers.Total,
coverage.ServiceMethods.Strict, coverage.ServiceMethods.Total)
coverage.ServiceMethods.Strict, coverage.ServiceMethods.Total,
coverage.Messages.Strict, coverage.Messages.Total)
}

func pluginFindingCodes(findings []planFinding) []string {
Expand Down
12 changes: 12 additions & 0 deletions cmd/wfctl/editor_bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,10 @@ func editorBundleContractIDFromPluginDescriptor(descriptor *pluginContractDescri
return id, nil
}
return "", fmt.Errorf("malformed service_method contract descriptor: serviceName and method are required when moduleType or serviceName is set")
case "message":
if typ := descriptor.contractType(kind); typ != "" {
return "message:" + typ, nil
}
}
return "", nil
}
Expand Down Expand Up @@ -349,6 +353,14 @@ func contractDescriptorFromPluginDescriptor(descriptor *pluginContractDescriptor
if _, ok := editorBundleServiceContractID(contract.ModuleType, contract.ServiceName, contract.Method); !ok {
return nil, fmt.Errorf("malformed service_method contract descriptor: serviceName and method are required when moduleType or serviceName is set")
}
case "message":
contract.Kind = pb.ContractKind_CONTRACT_KIND_MESSAGE
contract.ContractType = descriptor.ContractType
contract.ProtoPackage = descriptor.ProtoPackage
contract.MessageNames = append([]string(nil), descriptor.MessageNames...)
contract.GoImportPath = descriptor.GoImportPath
contract.SchemaDigest = descriptor.SchemaDigest
contract.ProtocolVersion = descriptor.ProtocolVersion
default:
return nil, nil
}
Expand Down
70 changes: 70 additions & 0 deletions cmd/wfctl/editor_bundle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,44 @@ func TestRunEditorBundleLoadsPluginContractDescriptorSetReference(t *testing.T)
}
}

func TestRunEditorBundleLoadsMessageContractDescriptor(t *testing.T) {
dir := t.TempDir()
outPath := filepath.Join(dir, "editor-bundle.json")

if err := runEditorBundle([]string{"--registry=false", "--plugin-dir", "testdata/plugins/message-contract", "--output", outPath}); err != nil {
t.Fatalf("editor-bundle failed: %v", err)
}

data, err := os.ReadFile(outPath)
if err != nil {
t.Fatalf("read output: %v", err)
}
var bundle struct {
Contracts map[string]struct {
DescriptorSetRef string `json:"descriptorSetRef"`
ProtoPackage string `json:"protoPackage"`
MessageNames []string `json:"messageNames"`
ProtocolVersion string `json:"protocolVersion"`
} `json:"contracts"`
}
if err := json.Unmarshal(data, &bundle); err != nil {
t.Fatalf("bundle is not valid JSON: %v", err)
}
contract := bundle.Contracts["message:compute.network_audit_evidence.v1"]
if contract.DescriptorSetRef != "descriptors/message.pb" {
t.Fatalf("descriptorSetRef = %q", contract.DescriptorSetRef)
}
if contract.ProtoPackage != "workflow_plugin_compute_core.protocol.v1" {
t.Fatalf("protoPackage = %q", contract.ProtoPackage)
}
if len(contract.MessageNames) != 2 || contract.MessageNames[0] != "NetworkAuditRecord" {
t.Fatalf("messageNames = %v", contract.MessageNames)
}
if contract.ProtocolVersion != "compute.v1alpha1" {
t.Fatalf("protocolVersion = %q", contract.ProtocolVersion)
}
}

func TestRunEditorBundleRejectsMalformedPluginContractDescriptors(t *testing.T) {
dir := t.TempDir()
pluginDir := filepath.Join(dir, "workflow-plugin-bad-contracts")
Expand Down Expand Up @@ -298,6 +336,26 @@ func TestRunEditorBundlePreservesPerContractDescriptorSetReferences(t *testing.T
"input": "workflow.two.Input",
"output": "workflow.two.Output",
"descriptorSetRef": "proto/two.pb"
},
{
"kind": "message",
"contractType": "message.one",
"mode": "strict",
"protoPackage": "workflow.one",
"messageNames": ["Event"],
"schemaDigest": "sha256:one",
"protocolVersion": "v1",
"descriptorSetRef": "proto/message-one.pb"
},
{
"kind": "message",
"contractType": "message.two",
"mode": "strict",
"protoPackage": "workflow.two",
"messageNames": ["Event"],
"schemaDigest": "sha256:two",
"protocolVersion": "v1",
"descriptorSetRef": "proto/message-two.pb"
}
]
}`), 0644); err != nil {
Expand Down Expand Up @@ -339,6 +397,18 @@ func TestRunEditorBundlePreservesPerContractDescriptorSetReferences(t *testing.T
if got := bundle.Messages["workflow.two.Input"].DescriptorSetRef; got != "proto/two.pb" {
t.Fatalf("workflow.two.Input descriptorSetRef = %q", got)
}
if got := bundle.Contracts["message:message.one"].DescriptorSetRef; got != "proto/message-one.pb" {
t.Fatalf("message.one descriptorSetRef = %q", got)
}
if got := bundle.Contracts["message:message.two"].DescriptorSetRef; got != "proto/message-two.pb" {
t.Fatalf("message.two descriptorSetRef = %q", got)
}
if got := bundle.Messages["workflow.one.Event"].DescriptorSetRef; got != "proto/message-one.pb" {
t.Fatalf("workflow.one.Event descriptorSetRef = %q", got)
}
if got := bundle.Messages["workflow.two.Event"].DescriptorSetRef; got != "proto/message-two.pb" {
t.Fatalf("workflow.two.Event descriptorSetRef = %q", got)
}
if bundle.DescriptorSets["proto/one.pb"].ExternalRef != "proto/one.pb" {
t.Fatalf("descriptor set one reference missing: %+v", bundle.DescriptorSets)
}
Expand Down
3 changes: 3 additions & 0 deletions cmd/wfctl/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ func runPlugin(args []string) error {
return runPluginRemove(args[1:])
case "validate":
return runPluginValidate(args[1:])
case "audit":
return runPluginAudit(args[1:])
case "validate-contract":
return runPluginValidateContract(args[1:])
case "verify-capabilities":
Expand Down Expand Up @@ -69,6 +71,7 @@ Subcommands:
update Update an installed plugin to its latest version
remove Uninstall a plugin (also removes from manifest + lockfile)
validate Validate a plugin manifest from the registry or a local file
audit Audit a single plugin source directory
validate-contract Validate a plugin source directory against the release contract (workflow#758)
verify-capabilities Spawn plugin binary, verify runtime GetManifest matches plugin.json
registry-sync Sync registry manifest versions/capabilities from upstream release tags; subcommands: core, readme (workflow#762)
Expand Down
Loading
Loading