feat: secrets input accepts a map; log per-call API response time#16
Conversation
Three changes from the latest deploy run feedback. 1. Map-shaped `secrets` input Mirror the `tags` refactor: accept a YAML block mapping (the canonical workflow form) or a JSON object string, instead of multi-line `KEY=VALUE` lines. Same key validation as before — must start with a letter or underscore, alphanumeric + underscores only — and secret values can still contain any characters. Each value is added to the runner's secret-mask list before any API call. 2. Per-call API response time Every Spice Cloud Management API call now logs `<METHOD> <path> → <status> <statusText> (<durationMs>ms)` so the user can see each request's latency inline. Network failures log `<METHOD> <path> → network error in <durationMs>ms: <message>`. Removed the redundant pre-call path logs in deploy.ts that the timing line now covers. 3. Branch missing from step summary The list-deployments endpoint sometimes returns Deployment objects without `branch` even when the create request set it. The summary now falls back to the `branch` and `commit-sha` inputs we sent so the right values surface even when the API echoes them inconsistently. Tests: 107 passing (up from 101). New cases cover the YAML and JSON secret forms (incl. quote-stripping, special characters in values, and JSON validation) and the post-rebuild `dist/` round-trip.
- package.json: 1.0.0 → 1.1.0.
- CHANGELOG.md: collapse the [Unreleased] block into a [1.1.0] section
dated 2026-05-03, with explicit Breaking changes / Added / Changed /
Fixed buckets. Added an empty [Unreleased] header for future entries.
The release rolls up everything since v1.0.0:
- Breaking: secrets input switched from KEY=VALUE to YAML/JSON map
(matches `tags`); tag-value validation tightened to alphanumeric +
`_@-` only.
- Added: `org`, `flight-url`, `dataset-ready-timeout-seconds` inputs;
`datasets` output; auto-captured `repository` tag; per-call
`<METHOD> <path> → <status> (<ms>ms)` API timing in logs.
- Changed: action runtime bumped from Node 20 to Node 24.
- Fixed: app-url URL pattern, SQL probe gRPC connection, api-key field
names, step-summary branch fallback, punycode deprecation warning,
action.yml `${{ … }}` example evaluation bug.
|
Bumped git tag v1.1.0
git push origin v1.1.0The release workflow will move the Why minor (not patch, not major)?
|
There was a problem hiding this comment.
Pull request overview
This PR updates the deploy action’s ergonomics and observability by moving secrets to the same map-style input model as tags, adding per-request timing logs for management API calls, and making the step summary more resilient when deployment metadata is missing from list responses.
Changes:
- Reworked
secretsinput handling from multilineKEY=VALUEentries to YAML/JSON map parsing, with matching docs, examples, and tests. - Added inline latency logging for Spice Cloud Management API calls and removed now-redundant pre-call path logs from deploy flow.
- Passed action inputs into summary generation so branch/commit fields can fall back to the submitted inputs when the API omits them.
Reviewed changes
Copilot reviewed 11 out of 13 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
src/secrets.ts |
Replaces legacy secrets parsing with map-style parsing and centralizes name validation/masking. |
src/main.ts |
Threads inputs into summary generation and adds branch/commit fallback logic. |
src/deploy.ts |
Simplifies deploy logging now that API client emits per-call timing lines. |
src/api.ts |
Logs status and duration for each management API request, including network failures. |
examples/full.yml |
Updates the example workflow to the new secrets map syntax. |
action.yml |
Documents the new secrets input format and examples. |
__tests__/secrets.test.ts |
Rewrites parser coverage around YAML/JSON secret maps. |
__tests__/deploy.test.ts |
Updates deploy test fixtures to use the new secrets syntax. |
README.md |
Refreshes input docs and usage examples for map-style secrets. |
CHANGELOG.md |
Records the breaking secrets change, API timing logs, and summary fallback fix. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const colon = line.indexOf(":"); | ||
| if (colon === -1) { | ||
| throw new InputValidationError( | ||
| `secrets line ${i + 1}: name "${name}" must start with a letter or underscore and contain only letters, numbers, and underscores.`, | ||
| `secrets line ${i + 1}: expected "KEY: VALUE" (got "${line.trim()}").`, | ||
| ); | ||
| } | ||
|
|
||
| const name = line.slice(0, colon).trim(); | ||
| const value = stripQuotes(line.slice(colon + 1).trim()); |
| const name = line.slice(0, colon).trim(); | ||
| const value = stripQuotes(line.slice(colon + 1).trim()); |
| const branch = deployment.branch || inputs.branch || ""; | ||
| const commitSha = deployment.commit_sha || inputs.commitSha || ""; |
| @@ -117,6 +120,9 @@ export class SpiceApiClient { | |||
| ); | |||
| } | |||
|
|
|||
| const durationMs = Date.now() - startMs; | |||
| core.info(`${method} ${path} → ${res.status} ${res.statusText} (${durationMs}ms)`); | |||
| if (colon === -1) { | ||
| throw new InputValidationError( | ||
| `secrets line ${i + 1}: name "${name}" must start with a letter or underscore and contain only letters, numbers, and underscores.`, | ||
| `secrets line ${i + 1}: expected "KEY: VALUE" (got "${line.trim()}").`, |
| const colon = line.indexOf(":"); | ||
| if (colon === -1) { | ||
| throw new InputValidationError( | ||
| `secrets line ${i + 1}: name "${name}" must start with a letter or underscore and contain only letters, numbers, and underscores.`, | ||
| `secrets line ${i + 1}: expected "KEY: VALUE" (got "${line.trim()}").`, | ||
| ); | ||
| } | ||
|
|
||
| const name = line.slice(0, colon).trim(); | ||
| const value = stripQuotes(line.slice(colon + 1).trim()); | ||
|
|
||
| validateName(name, `secrets line ${i + 1}`); |
This reverts commit 57ecfd2.
- src/main.ts: Use a length-aware fallback (deployment.branch?.length ? deployment.branch : inputs.branch ?? "") so the step summary still falls back when the API returns an empty string, while documenting why we treat empty-string as equivalent to missing here. - src/secrets.ts: Stop calling `.trim()` on secret values. Strip only the leading whitespace separator after `:` and a matching pair of outer quotes; trailing whitespace and inner whitespace are preserved verbatim. Also pass the un-trimmed raw input to parseBlockMap so a trailing space on the last line isn't lost. Whitespace-significant values still need quoting to make the boundaries explicit. - src/api.ts: Move the timing log to AFTER the response body is read so the duration reflects true end-to-end request latency, not just time-to-first-byte. Body-read errors retry on the next attempt and fail with a clear `Failed to read response body for …` message on exhaustion. New tests cover trailing whitespace preserved in unquoted secret values, leading whitespace preserved inside quoted secret values, and multiple-spaces-after-colon handling.
Summary
Three changes from the latest deploy-run feedback.
1. `secrets` input is now a map (matches `tags`)
Same migration as #12 did for `tags`. Accept a YAML block mapping (canonical workflow form) or a JSON object string, instead of multi-line `KEY=VALUE`:
```yaml
with:
secrets: |
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
PG_PASSWORD: ${{ secrets.PG_PASSWORD }}
```
Secret-name validation is unchanged (starts with a letter or underscore, alphanumeric + underscores only). Secret values can contain any characters — only the name is constrained — and each value is added to the runner's secret-mask list before any API call.
2. Per-call response time in API logs
Every Spice Cloud Management API call now emits one line:
```
PUT /v1/apps/4573 → 200 OK (312ms)
POST /v1/apps/4573/secrets → 201 Created (148ms)
POST /v1/apps/4573/deployments → 202 Accepted (203ms)
GET /v1/apps/4573/deployments?limit=20 → 200 OK (87ms)
```
Network failures log ` → network error in ms: `. Removed the redundant pre-call path logs in `deploy.ts` (`PUT /v1/apps/X tags=...`, `POST /v1/apps/X/secrets — KEY`) — the timing line covers them and the `Tags: ...` / `Secret: ...` info lines stay for context.
To answer the question on which other inputs should be maps: I checked everything in `action.yml` — `tags` and `secrets` are the only flat key/value inputs. The rest is either scalar (URLs, region, timeouts), a file path (`spicepod`), or a structured API body (`test-search`, `test-mcp-arguments`) that needs to match the underlying API's JSON shape rather than being a flat map. No other refactors needed.
3. Empty Branch in step summary
The list-deployments endpoint occasionally returns Deployment objects without a `branch` field even when the create request explicitly set it (the user just hit this — saw `Commit: 0ceabc...` populated but `Branch:` empty in the step summary). Fix: fall back to the `branch` and `commit-sha` inputs we sent so the summary stays accurate even when the API echoes them inconsistently.
Test plan