Skip to content

Commit 4076535

Browse files
amondnetCopilot
andauthored
feat: add vercel-build action input for local Vercel build step (#361)
* docs(track): add auto-vercel-build-20260430 Add track for opt-in vercel-build action input that runs vercel pull and vercel build locally before deployment, then routes through the existing prebuilt deploy path. Refs #360 * chore(track): start auto-vercel-build-20260430 implementation Refs #360 * feat(config): add vercel-build input with mutual-exclusivity check Add a new boolean action input `vercel-build` that opts into running `vercel pull` + `vercel build` locally before deployment. When combined with `prebuilt: true`, the action fails fast at config parse time since the two flags are conceptually mutually exclusive. T001+T002+T003 of auto-vercel-build-20260430. Refs #360 * feat(vercel-build): add BuildFailedError, pull/build runners, orchestrator Add new module src/vercel-build.ts: - BuildFailedError with stderr-tail capture (last 20 lines, fallback to stdout when stderr is empty) - runVercelPull(config): `vercel pull --yes --environment=<target>` - runVercelBuild(config): `vercel build [--prod]` with buildEnv merged into the exec environment - runBuildStep(config): pull → build sequencing, returns { prebuilt: true, vercelOutputDir } so callers can route through the existing prebuilt deploy path T004–T009 of auto-vercel-build-20260430. Refs #360 * feat(index): wire vercel-build runner into run() with failure comments Wire runBuildStep into run() before deployment when vercel-build: true. On success, mutate the config to set prebuilt=true and vercelOutputDir so the existing API client uploads the produced .vercel/output. On BuildFailedError, post a build-failure comment (PR or commit) with the captured stderr tail (last 20 lines), then rethrow so the action exits non-zero. Also export run() and guard the top-level invocation behind `process.env.VITEST` so tests can drive run() without triggering the module-level fire-and-forget call. T010-T013 of auto-vercel-build-20260430. Refs #360 * feat: add vercel-build integration test, README, dist rebuild - Integration test (src/__integration__/vercel-build.test.ts): 3 tests covering runBuildStep output, end-to-end deploy via emulator with the prebuilt path, and fail-fast on pull error. - README: `vercel-build` input row in the API inputs table, plus a new "Method 4 - Build inside the action" section with a workflow example and notes on mutual exclusivity, build-env, target=production, and failure-comment behavior. - dist/: regenerated via @vercel/ncc. Coverage: vercel-build.ts 94.39% (NFR-4 target >80%), suite 272 tests passing. T014-T016 of auto-vercel-build-20260430. Refs #360 * fix(vercel-build): pass token via VERCEL_TOKEN env, escape fenced blocks Address code-review findings on PR #361: - Critical (security): Stop passing the Vercel token via `-t <token>` argv, which @actions/exec echoes verbatim to stdout via toolrunner's [command]... line before the runner's secret mask can claim it. Pass the token through the VERCEL_TOKEN environment variable instead — the documented non-interactive auth path per the Vercel CLI source (packages/cli/src/commands/build/index.ts:309). Also set silent: true on the ExecOptions to suppress the [command] echo entirely. - Important (security): `stderrTail` is interpolated into a Markdown fenced code block in PR/commit comments. Untrusted build output containing triple-backticks could close the fence and inject Markdown. Escape any run of 3+ backticks before interpolation. - Important (tests): Add three orchestration tests in run-build.test.ts: 1. Asserts createBuildFailureCommentOnPullRequest is called with the BuildFailedError's exitCode and stderrTail (AC-4 wiring) 2. Asserts createBuildFailureCommentOnCommit is called for push events instead of the PR helper 3. Asserts no failure comment is posted when github-comment is false - Add 4 vercel-build.test.ts tests asserting the token is NOT in argv and IS in env, plus that exec runs with silent: true. dist/ rebuilt. Suite: 280 tests passing (was 272). Refs #360 * fix(vercel-build): honor vercel-output-dir via --output flag Previously, when a user combined `vercel-build: true` with a custom `vercel-output-dir`, the build wrote to the default `.vercel/output` while the prebuilt deploy looked at `config.vercelOutputDir` — silently mismatched. The user's vercel-output-dir was effectively ignored during the build. Fix: - runVercelBuild now passes `--output <dir>` to `vercel build` whenever `config.vercelOutputDir` is set (relative paths resolved against working-directory; absolute paths used as-is). - runBuildStep now returns the user's `vercelOutputDir` (resolved) instead of always defaulting to `<workingDir>/.vercel/output`, so the deploy reads from the same place the build wrote to. - Spec FR-2 + new AC-7 + plan architecture diagram updated to reflect `[--output <dir>]` and the VERCEL_TOKEN env transport. - README documents both the env-var auth path and the --output passthrough. - 5 new unit tests covering --output omission, absolute path, and relative-path resolution. Addresses gemini-code-assist[bot] review threads on PR #361. Refs #360 * chore(track): auto-vercel-build-20260430 PR 제출 완료 - Move track active/ → completed/ - Update metadata: status=review, updated_at, pr=#361 - Update tracks.jsonl: section=completed, phase=finalize - Sync to product-specs: SPEC-001 deployment/vercel-build.md (created) - Append retrospective + tech-debt items (TD-001 vercel-cli token transport, TD-002 e2e gap) Refs #360 * chore: apply AI code review suggestions (round 2) Apply 5 review suggestions from copilot + cubic-dev-ai on PR #361: - src/vercel-build.ts: bound captured stdout/stderr to ~64 KB per stream (appendBounded helper). Long vercel build runs no longer risk runner OOM; the stderrTail tail-line behavior is preserved. - src/index.ts: replace negative `!process.env.VITEST` auto-invoke guard with positive `process.env.GITHUB_ACTIONS === 'true'` check. The previous guard would silently disable the action in any workflow that happens to set VITEST. GITHUB_ACTIONS is the canonical runner-set sentinel. - src/config.ts: add fail-fast mutex between `vercel-build: true` and non-empty `vercel-args`. Without it, the action would build locally via the prebuilt path then route the deploy through the CLI path, silently ignoring the local build output. - src/github-comments.ts: change build-failure comment wording from 'this pull request' to 'this workflow run' so the same body works for both PR comments and commit comments (push events). - README.md: fix broken `vercel-build` anchor link to use the actual GitHub-rendered slug (`#method-4---build-inside-the-action-vercel-build`). 3 new tests: vercel-args mutex (positive + whitespace edge case), huge-stderr bounded buffer. Suite: 305 tests passing (was 302). Lint clean, tsc clean. dist/ rebuilt. Refs #360 * fix(tests): clear GITHUB_ACTIONS env in vitest unit project to prevent auto-invocation on import Agent-Logs-Url: https://github.com/amondnet/vercel-action/sessions/e840b930-a078-4381-91dc-06dc24402e36 Co-authored-by: amondnet <1964421+amondnet@users.noreply.github.com> * chore(dist): rebuild bundle to match src/index.ts comment update The check-dist CI step regenerates dist/ and fails if it differs from the committed bundle. Commit 45de99c updated the auto-invoke guard comment in src/index.ts and added a vitest env override but did not rebuild dist/. Regenerate via @vercel/ncc so the committed bundle matches source. Refs #360 --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: amondnet <1964421+amondnet@users.noreply.github.com>
1 parent 31671aa commit 4076535

26 files changed

Lines changed: 2133 additions & 29 deletions
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
---
2+
id: SPEC-001
3+
level: V_M
4+
domain: deployment
5+
feature: vercel-build
6+
depends: []
7+
conflicts: []
8+
traces: []
9+
created_at: 2026-04-30T00:49:41Z
10+
updated_at: 2026-04-30T00:49:41Z
11+
source_tracks: ["auto-vercel-build-20260430"]
12+
---
13+
14+
# Local Vercel Build Step (`vercel-build` input) Specification
15+
16+
## Purpose
17+
18+
Specifies the opt-in `vercel-build` action input that runs the official Vercel CLI workflow (`vercel pull``vercel build`) inside the GitHub Actions runner before deployment, then uploads `.vercel/output` via the existing prebuilt deploy path. Mirrors the official Vercel KB recommended GitHub Actions workflow and lets users build inside CI with their own secrets and runtime instead of relying on Vercel's remote build.
19+
20+
## Requirements
21+
22+
### Requirement: Add `vercel-build` boolean input
23+
24+
The system MUST expose a new boolean action input `vercel-build` in `action.yml` with default `false`.
25+
26+
#### Scenario: Add `vercel-build` boolean input
27+
28+
- GIVEN the action is consumed in a workflow
29+
- WHEN the user inspects the inputs
30+
- THEN `vercel-build` is available as a boolean input defaulting to `false`
31+
32+
### Requirement: Run `vercel pull` and `vercel build` in working directory when enabled
33+
34+
The system MUST execute, in order, in the configured `working-directory` when `vercel-build: true` and `prebuilt: false`: (1) `vercel pull --yes --environment=<target> [--scope <scope>]`, (2) `vercel build [--prod] [--scope <scope>] [--output <dir>]`, (3) treat the resulting build output directory as the deploy artifact via the existing prebuilt code path. Authentication MUST be supplied via the `VERCEL_TOKEN` environment variable, never via a `--token`/`-t` CLI argument.
35+
36+
#### Scenario: Run `vercel pull` and `vercel build` in working directory when enabled
37+
38+
- GIVEN `vercel-build: true` and `prebuilt: false`
39+
- WHEN the action runs
40+
- THEN `vercel pull` runs first, `vercel build` runs second, and `.vercel/output` (or the configured `vercel-output-dir`) is uploaded as a prebuilt deployment
41+
42+
### Requirement: Reject mutually exclusive `vercel-build` + `prebuilt`
43+
44+
The system MUST fail fast at config-parse time with a clear error when both `vercel-build: true` and `prebuilt: true` are set.
45+
46+
#### Scenario: Reject mutually exclusive `vercel-build` + `prebuilt`
47+
48+
- GIVEN `vercel-build: true` AND `prebuilt: true`
49+
- WHEN config parsing runs
50+
- THEN the action exits non-zero before any I/O with a message identifying the conflict
51+
52+
### Requirement: Preserve current behavior when disabled
53+
54+
The system MUST preserve the current source-upload behavior unchanged when `vercel-build: false` (default).
55+
56+
#### Scenario: Preserve current behavior when disabled
57+
58+
- GIVEN `vercel-build` is unset or `false`
59+
- WHEN the action runs
60+
- THEN deployment payloads, calls, and outputs are byte-identical to the implementation prior to introducing `vercel-build`
61+
62+
### Requirement: Invoke pull/build via `@actions/exec` against the bundled `vercel` package
63+
64+
The system MUST invoke `vercel pull` and `vercel build` via `@actions/exec` against the bundled `vercel` CLI when `@vercel/client` does not expose a programmatic build/pull API.
65+
66+
#### Scenario: Invoke pull/build via `@actions/exec` against the bundled `vercel` package
67+
68+
- GIVEN the project depends on the `vercel` package and `@vercel/client` exposes no build/pull primitive
69+
- WHEN the build step runs
70+
- THEN both commands are spawned through `@actions/exec` rather than fetching the CLI on the fly
71+
72+
### Requirement: Propagate team scope to pull and build
73+
74+
The system MUST forward `vercel-org-id` / `--scope` to both `vercel pull` and `vercel build` invocations whenever a scope is configured.
75+
76+
#### Scenario: Propagate team scope to pull and build
77+
78+
- GIVEN `vercel-org-id` (or scope) is configured
79+
- WHEN the build step runs
80+
- THEN both `vercel pull` and `vercel build` receive the scope via `--scope`
81+
82+
### Requirement: Make `build-env` available to local build
83+
84+
The system MUST make `build-env` KEY=VALUE pairs available to the local `vercel build` execution as environment variables.
85+
86+
#### Scenario: Make `build-env` available to local build
87+
88+
- GIVEN `build-env` contains one or more KEY=VALUE pairs
89+
- WHEN `vercel build` runs
90+
- THEN the child process environment contains those variables in addition to `VERCEL_TOKEN`
91+
92+
### Requirement: Surface build failures with exit, log, and PR/commit comment
93+
94+
The system MUST, on `vercel build` failure, exit non-zero, stream stdout and stderr into the GitHub Actions log, and (when `github-comment` is enabled) post a comment on the PR or commit summarizing the failure with a truncated tail of build output.
95+
96+
#### Scenario: Surface build failures with exit, log, and PR/commit comment
97+
98+
- GIVEN `vercel build` exits non-zero and `github-comment` is not `false`
99+
- WHEN the failure is observed
100+
- THEN the action exits non-zero, the failure is visible in the log, and a comment is posted with the captured stderr tail (escaped to prevent fenced-block breakout)
101+
102+
### Requirement: Honor `vercel-output-dir` end-to-end
103+
104+
The system MUST pass `--output <dir>` to `vercel build` whenever `vercel-output-dir` is set, and the prebuilt deploy step MUST upload from the same directory. Relative paths are resolved against `working-directory`.
105+
106+
#### Scenario: Honor `vercel-output-dir` end-to-end
107+
108+
- GIVEN `vercel-build: true` AND `vercel-output-dir` is set to a custom path
109+
- WHEN the build step runs
110+
- THEN `vercel build --output <resolved-dir>` is invoked and the prebuilt deploy reads from the same `<resolved-dir>`
111+
112+
## Non-functional Requirements
113+
114+
### Requirement: No breaking change to existing inputs
115+
116+
The system SHOULD NOT introduce any breaking change to existing inputs or default behavior — existing workflows continue to work unchanged.
117+
118+
### Requirement: Stream build output without buffering
119+
120+
The system SHOULD stream build stdout and stderr without buffering the entire log in memory; only a bounded tail is captured for failure reporting.
121+
122+
### Requirement: Never expose Vercel token in logs or comments
123+
124+
The system SHOULD ensure the Vercel token never appears in any log line, PR/commit comment, or error message. Token transport uses the `VERCEL_TOKEN` environment variable; `@actions/exec` is configured with `silent: true` to suppress the `[command]…` echo line.
125+
126+
### Requirement: Maintain >80% test coverage on new code
127+
128+
The system SHOULD maintain test coverage above 80% for the new `vercel-build` module and orchestration code.
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Product Specs Index
22

3-
> Auto-maintained by /please:spec --product.
3+
> Auto-maintained by /please:sync-specs.
44
5-
| Spec | Feature | Created | Related Tracks |
6-
|------|---------|---------|----------------|
5+
| Spec | Domain | Feature | Created | Related Tracks |
6+
|------|--------|---------|---------|----------------|
7+
| SPEC-001 | deployment | vercel-build | 2026-04-30 | ["auto-vercel-build-20260430"] |

.please/docs/tracks.jsonl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
{"id":"relative-working-dir-20260423","type":"bugfix","status":"review","phase":"finalize","issue":"#341","pr":"#349","created":"2026-04-23","section":"completed"}
33
{"id":"build-exit-255-20260423","type":"bugfix","status":"review","phase":"finalize","issue":"#336","pr":"#350","created":"2026-04-23","section":"completed"}
44
{"id":"fix-vercel-validation-20260430","type":"bugfix","status":"review","phase":"finalize","issue":"#359","pr":"#364","created":"2026-04-30","section":"completed"}
5+
{"id":"auto-vercel-build-20260430","type":"feature","status":"review","phase":"finalize","issue":"#360","pr":"#361","created":"2026-04-30","section":"completed"}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"track_id": "auto-vercel-build-20260430",
3+
"type": "feature",
4+
"status": "review",
5+
"created_at": "2026-04-29T18:55:48Z",
6+
"updated_at": "2026-04-30T00:50:48Z",
7+
"issue": "#360",
8+
"pr": "#361",
9+
"project": "",
10+
"project_item_id": ""
11+
}

0 commit comments

Comments
 (0)