Skip to content

Commit 8692acc

Browse files
authored
Add artifact bundle verifier (#184)
1 parent b23b686 commit 8692acc

6 files changed

Lines changed: 551 additions & 5 deletions

File tree

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"package:wordpress-plugin": "tsx scripts/build-wordpress-plugin-zip.ts",
3434
"package-distribution-smoke": "tsx scripts/package-distribution-smoke.ts",
3535
"release:package": "tsx scripts/package-release-artifact.ts",
36+
"artifact-bundle-verifier-smoke": "tsx scripts/artifact-bundle-verifier-smoke.ts",
3637
"policy-validation-smoke": "tsx scripts/policy-validation-smoke.ts",
3738
"workspace-policy-smoke": "tsx scripts/workspace-policy-smoke.ts",
3839
"artifact-contract-smoke": "tsx scripts/artifact-contract-smoke.ts",
@@ -58,7 +59,7 @@
5859
"recipe-staged-files-smoke": "tsx scripts/recipe-staged-files-smoke.ts",
5960
"wp-codebox": "node packages/cli/dist/index.js",
6061
"wordpress-plugin-smoke": "php tests/smoke-wordpress-plugin.php",
61-
"check": "npm run build && npm run discovery-command-smoke && npm run theme-check-normalization-smoke && npm run agent-sandbox-code-smoke && npm run policy-validation-smoke && npm run workspace-policy-smoke && npm run wordpress-plugin-smoke && npm run package-distribution-smoke && npm run artifact-contract-smoke && npm run external-adapter-contract-smoke && npm run runtime-episode-smoke && npm run core-phpunit-command-smoke && npm run plugin-check-normalization-smoke && npm run recipe-bench-smoke && npm run recipe-dry-run-smoke && npm run recipe-workflow-phases-smoke && npm run recipe-site-seed-smoke && npm run recipe-staged-files-smoke && npm run preview-port-smoke && npm run preview-public-url-canonical-smoke && npm run preview-response-body-smoke && npm run boot-preview-smoke && npm run blueprint-validation-smoke && npm run browser-probe-artifact-smoke && npm run wp-codebox -- run --mount ./examples/simple-plugin:/wordpress/wp-content/plugins/simple-plugin --command wordpress.run-php --arg code-file=./examples/simple-plugin/probe.php --artifacts ./artifacts --json"
62+
"check": "npm run build && npm run discovery-command-smoke && npm run theme-check-normalization-smoke && npm run agent-sandbox-code-smoke && npm run policy-validation-smoke && npm run workspace-policy-smoke && npm run wordpress-plugin-smoke && npm run package-distribution-smoke && npm run artifact-bundle-verifier-smoke && npm run artifact-contract-smoke && npm run external-adapter-contract-smoke && npm run runtime-episode-smoke && npm run core-phpunit-command-smoke && npm run plugin-check-normalization-smoke && npm run recipe-bench-smoke && npm run recipe-dry-run-smoke && npm run recipe-workflow-phases-smoke && npm run recipe-site-seed-smoke && npm run recipe-staged-files-smoke && npm run preview-port-smoke && npm run preview-public-url-canonical-smoke && npm run preview-response-body-smoke && npm run boot-preview-smoke && npm run blueprint-validation-smoke && npm run browser-probe-artifact-smoke && npm run wp-codebox -- run --mount ./examples/simple-plugin:/wordpress/wp-content/plugins/simple-plugin --command wordpress.run-php --arg code-file=./examples/simple-plugin/probe.php --artifacts ./artifacts --json"
6263
},
6364
"workspaces": [
6465
"packages/*"

packages/cli/src/index.ts

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import { basename, dirname, join, resolve } from "node:path"
88
import { Readable } from "node:stream"
99
import { pipeline } from "node:stream/promises"
1010
import { promisify } from "node:util"
11-
import { SANDBOX_DMC_PARENT_ONLY_ABILITIES, SANDBOX_DMC_SAFE_ABILITIES, SANDBOX_WORKSPACE_ROOT, checkWorkspacePolicy, createRuntime, validateRuntimePolicy, type ArtifactBundle, type ExecutionResult, type MountSpec, type Runtime, type RuntimeInfo, type RuntimePolicy, type SandboxWorkspaceContract, type SandboxWorkspaceMode, type WorkspacePolicyResult, type WorkspaceRecipe, type WorkspaceRecipeExtraPlugin, type WorkspaceRecipeSiteSeed, type WorkspaceRecipeStagedFile, type WorkspaceRecipeWorkspace } from "@chubes4/wp-codebox-core"
11+
import { SANDBOX_DMC_PARENT_ONLY_ABILITIES, SANDBOX_DMC_SAFE_ABILITIES, SANDBOX_WORKSPACE_ROOT, checkWorkspacePolicy, createRuntime, validateRuntimePolicy, verifyArtifactBundle, type ArtifactBundle, type ArtifactBundleVerificationResult, type ExecutionResult, type MountSpec, type Runtime, type RuntimeInfo, type RuntimePolicy, type SandboxWorkspaceContract, type SandboxWorkspaceMode, type WorkspacePolicyResult, type WorkspaceRecipe, type WorkspaceRecipeExtraPlugin, type WorkspaceRecipeSiteSeed, type WorkspaceRecipeStagedFile, type WorkspaceRecipeWorkspace } from "@chubes4/wp-codebox-core"
1212
import { createPlaygroundRuntimeBackend } from "@chubes4/wp-codebox-playground"
1313
import { agentRuntimeProbeCode, agentSandboxRunCode, resolveSandboxTaskCode } from "./agent-code.js"
14-
import { captureStdout, printBatchHumanOutput, printBlueprintValidateHumanOutput, printBootHumanOutput, printCommandCatalogHumanOutput, printHelp, printHumanOutput, printRecipeHumanOutput, printRecipeSchemaHumanOutput, printRecipeValidateHumanOutput, serializeError } from "./output.js"
14+
import { captureStdout, printArtifactVerifyHumanOutput, printBatchHumanOutput, printBlueprintValidateHumanOutput, printBootHumanOutput, printCommandCatalogHumanOutput, printHelp, printHumanOutput, printRecipeHumanOutput, printRecipeSchemaHumanOutput, printRecipeValidateHumanOutput, serializeError } from "./output.js"
1515

1616
interface CommandMetadata {
1717
id: string
@@ -138,6 +138,11 @@ interface WorkspacePolicyOptions {
138138
json: boolean
139139
}
140140

141+
interface ArtifactVerifyOptions {
142+
bundleDirectory: string
143+
json: boolean
144+
}
145+
141146
interface RecipeValidationIssue {
142147
code: string
143148
path: string
@@ -975,6 +980,25 @@ async function main(args: string[]): Promise<number> {
975980
return output.passed ? 0 : 1
976981
}
977982

983+
if (command === "artifacts") {
984+
const subcommand = args.shift()
985+
if (subcommand !== "verify") {
986+
console.error(`Unknown artifacts command: ${subcommand ?? ""}`)
987+
printHelp()
988+
return 1
989+
}
990+
991+
const options = parseArtifactVerifyOptions(args)
992+
const output = await verifyArtifacts(options)
993+
if (!options.json) {
994+
printArtifactVerifyHumanOutput(output)
995+
return output.valid ? 0 : 1
996+
}
997+
998+
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`)
999+
return output.valid ? 0 : 1
1000+
}
1001+
9781002
if (command === "commands") {
9791003
const json = parseDiscoveryJsonOption(args)
9801004
const output = commandCatalogOutput()
@@ -1529,6 +1553,10 @@ async function validateRecipe(options: RecipeValidateOptions): Promise<RecipeVal
15291553
}
15301554
}
15311555

1556+
async function verifyArtifacts(options: ArtifactVerifyOptions): Promise<ArtifactBundleVerificationResult> {
1557+
return verifyArtifactBundle(resolve(options.bundleDirectory))
1558+
}
1559+
15321560
async function mapWithConcurrency<T, R>(items: T[], concurrency: number, callback: (item: T, index: number) => Promise<R>): Promise<R[]> {
15331561
const results: R[] = new Array(items.length)
15341562
let nextIndex = 0
@@ -2506,6 +2534,40 @@ function parseRecipeValidateOptions(args: string[]): RecipeValidateOptions {
25062534
return options as RecipeValidateOptions
25072535
}
25082536

2537+
function parseArtifactVerifyOptions(args: string[]): ArtifactVerifyOptions {
2538+
const options: Partial<ArtifactVerifyOptions> = { json: false }
2539+
2540+
for (let index = 0; index < args.length; index++) {
2541+
const arg = args[index]
2542+
2543+
if (arg === "--json") {
2544+
options.json = true
2545+
continue
2546+
}
2547+
2548+
const [name, inlineValue] = arg.split("=", 2)
2549+
const value = inlineValue ?? args[++index]
2550+
2551+
if (!name.startsWith("--") || value === undefined) {
2552+
throw new Error(`Invalid argument: ${arg}`)
2553+
}
2554+
2555+
switch (name) {
2556+
case "--bundle":
2557+
options.bundleDirectory = value
2558+
break
2559+
default:
2560+
throw new Error(`Unknown option: ${name}`)
2561+
}
2562+
}
2563+
2564+
if (!options.bundleDirectory) {
2565+
throw new Error("Missing required option: --bundle")
2566+
}
2567+
2568+
return options as ArtifactVerifyOptions
2569+
}
2570+
25092571
function parseWorkspaceRecipe(raw: string, recipePath: string): WorkspaceRecipe {
25102572
const recipe = JSON.parse(raw) as WorkspaceRecipe
25112573

packages/cli/src/output.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { ArtifactBundle, ExecutionResult, RuntimeInfo } from "@chubes4/wp-codebox-core"
1+
import type { ArtifactBundle, ArtifactBundleVerificationResult, ExecutionResult, RuntimeInfo } from "@chubes4/wp-codebox-core"
22

33
interface CliError {
44
name: string
@@ -195,6 +195,15 @@ export function printRecipeValidateHumanOutput(output: RecipeValidateOutputLike)
195195
}
196196
}
197197

198+
export function printArtifactVerifyHumanOutput(output: ArtifactBundleVerificationResult): void {
199+
console.log("WP Codebox artifact bundle verification")
200+
console.log(`Bundle: ${output.bundleDirectory}`)
201+
console.log(`Valid: ${output.valid ? "yes" : "no"}`)
202+
for (const violation of output.violations) {
203+
console.log(`- ${violation.code} ${violation.path}: ${violation.message}`)
204+
}
205+
}
206+
198207
export function printBatchHumanOutput(output: BatchOutputLike): void {
199208
console.log("WP Codebox batch")
200209
console.log(`Runs: ${output.completed}/${output.total} completed`)
@@ -233,13 +242,15 @@ export function printHelp(): void {
233242
wp-codebox schema recipe [--json]
234243
wp-codebox workspace-policy check --workspace-root <path> --writable-root <path> [options]
235244
wp-codebox recipe validate --recipe <path> [--json]
245+
wp-codebox artifacts verify --bundle <dir> [--json]
236246
wp-codebox validate-blueprint --blueprint <json|file> [options]
237247
wp-codebox recipe-run --recipe <path> [options]
238248
wp-codebox boot [--mount <host>:<vfs>] [options]
239249
wp-codebox run --mount <host>:<vfs> --command <id> [options]
240250
241251
Options:
242252
--recipe <path> Workspace recipe JSON file for recipe-run or recipe validate.
253+
--bundle <dir> Artifact bundle directory for artifacts verify.
243254
--mount <host:vfs> Mount a host path into the runtime. Repeatable.
244255
--command <id> Command/action id to execute.
245256
--arg <key=value> Command argument. Repeatable. Recipe commands include wordpress.run-php, wordpress.phpunit, wordpress.core-phpunit, wordpress.plugin-check, wordpress.wp-cli, wordpress.ability, wordpress.bench, and wordpress.browser-probe.

0 commit comments

Comments
 (0)