-
-
Notifications
You must be signed in to change notification settings - Fork 116
feat: add vercel-build action input for local Vercel build step #361
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
6c25365
docs(track): add auto-vercel-build-20260430
amondnet 603cb29
chore(track): start auto-vercel-build-20260430 implementation
amondnet ff19a9f
feat(config): add vercel-build input with mutual-exclusivity check
amondnet 7da6a43
feat(vercel-build): add BuildFailedError, pull/build runners, orchest…
amondnet 501bb6e
feat(index): wire vercel-build runner into run() with failure comments
amondnet 761e123
feat: add vercel-build integration test, README, dist rebuild
amondnet 6cf21ad
fix(vercel-build): pass token via VERCEL_TOKEN env, escape fenced blocks
amondnet f7a132f
fix(vercel-build): honor vercel-output-dir via --output flag
amondnet fb288ab
chore(track): auto-vercel-build-20260430 PR 제출 완료
amondnet 64420fe
Merge remote-tracking branch 'origin/master' into amondnet/vercel-bui…
amondnet b57e354
chore: apply AI code review suggestions (round 2)
amondnet 45de99c
fix(tests): clear GITHUB_ACTIONS env in vitest unit project to preven…
Copilot faeeff0
chore(dist): rebuild bundle to match src/index.ts comment update
amondnet File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| {"id":"prebuilt-project-id-bug-20260408","type":"bugfix","status":"review","phase":"finalize","issue":"#330","created":"2026-04-08","section":"completed"} | ||
| {"id":"relative-working-dir-20260423","type":"bugfix","status":"review","phase":"finalize","issue":"#341","pr":"#349","created":"2026-04-23","section":"completed"} | ||
| {"id":"build-exit-255-20260423","type":"bugfix","status":"review","phase":"finalize","issue":"#336","pr":"#350","created":"2026-04-23","section":"completed"} | ||
| {"id":"auto-vercel-build-20260430","type":"feature","status":"in_progress","phase":"implement","issue":"#360","created":"2026-04-30","section":"active"} |
11 changes: 11 additions & 0 deletions
11
.please/docs/tracks/active/auto-vercel-build-20260430/metadata.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "track_id": "auto-vercel-build-20260430", | ||
| "type": "feature", | ||
| "status": "in_progress", | ||
| "created_at": "2026-04-29T18:55:48Z", | ||
| "updated_at": "2026-04-29T19:06:03Z", | ||
| "issue": "#360", | ||
| "pr": "", | ||
| "project": "", | ||
| "project_item_id": "" | ||
| } |
142 changes: 142 additions & 0 deletions
142
.please/docs/tracks/active/auto-vercel-build-20260430/plan.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| # Plan: Local Vercel Build Step (`vercel-build` input) | ||
|
|
||
| > Track: auto-vercel-build-20260430 | ||
| > Spec: [spec.md](./spec.md) | ||
|
|
||
| ## Overview | ||
|
|
||
| - **Source**: /please:plan | ||
| - **Track**: auto-vercel-build-20260430 | ||
| - **Issue**: TBD (created by /please:new-track) | ||
| - **Created**: 2026-04-30 | ||
| - **Approach**: New `VercelBuildRunner` module + pre-deploy step in `run()` | ||
|
|
||
| ## Purpose | ||
|
|
||
| Add an opt-in `vercel-build` action input that executes the official Vercel CLI workflow (`vercel pull` → `vercel build`) inside the GitHub Actions runner before deployment, then reuses the existing prebuilt deploy path to upload `.vercel/output`. Lets users build inside CI with their own secrets/runtime instead of relying on Vercel's remote build. | ||
|
|
||
| ## Context | ||
|
|
||
| The action currently has two deploy paths: API-based (default, uploads source via `@vercel/client`) and CLI-based (when `vercel-args` is provided, runs `npx vercel ...`). The `prebuilt` input already exists and routes to the API path with prebuilt upload of `.vercel/output`. | ||
|
|
||
| This track adds a third orchestration: when `vercel-build: true`, run `vercel pull` + `vercel build` locally first, then funnel into the existing prebuilt API path. The change is additive and opt-in — existing pipelines see no behavior change. | ||
|
|
||
| ## Architecture Decision | ||
|
|
||
| **Chosen: New `src/vercel-build.ts` module + pre-deploy orchestration in `src/index.ts`** (Approach A from architecture review). | ||
|
|
||
| Rejected: embedding build logic inside `VercelApiClient.deploy()` (Approach B). Reasons for rejection: couples subprocess execution to the API client, violates AGENTS.md "isolate side effects at boundary," and harder to unit-test in isolation. | ||
|
|
||
| Key design points: | ||
| - `@vercel/client` v17 does **not** expose `build`/`pull` primitives; the SDK fallback in spec FR-5 is therefore void. We invoke the bundled `vercel` package via `@actions/exec` (same pattern as `vercel-cli.ts`). | ||
| - Mutual exclusivity (`vercel-build: true` + `prebuilt: true`) is enforced in `getActionConfig()` so the action fails before any network/IO. | ||
| - After a successful local build, the runner mutates a copy of `ActionConfig` to set `prebuilt = true`, then the existing `VercelApiClient` deploys `.vercel/output` unchanged. No changes to `vercel-api.ts` are required. | ||
| - Build failure surfaces as a custom `BuildFailedError` carrying a truncated stderr tail; the `run()` catch block recognizes this error type and posts a build-failure comment (PR or commit) before rethrowing. | ||
|
|
||
| ## Architecture Diagram | ||
|
|
||
| ``` | ||
| run() in src/index.ts | ||
| │ | ||
| ├─► getActionConfig() [config.ts] | ||
| │ └─ validates vercelBuild XOR prebuilt ← FR-3 | ||
| │ | ||
| ├─► [if config.vercelBuild] | ||
| │ └─► runVercelBuild(config) [NEW: vercel-build.ts] | ||
| │ ├─ exec: npx vercel pull --yes --environment=<target> | ||
| │ └─ exec: npx vercel build [--prod] | ||
| │ ├─ throws BuildFailedError on failure (with stderr tail) | ||
| │ └─ on success: returns { prebuilt: true, vercelOutputDir } | ||
| │ └─► config = { ...config, prebuilt: true, vercelOutputDir } | ||
| │ | ||
| ├─► createVercelClient(config) [vercel.ts] ← unchanged | ||
| │ └─ chooses API or CLI client | ||
| │ | ||
| └─► client.deploy(config, ctx) [vercel-api.ts] ← unchanged | ||
| └─ uses existing prebuilt upload path | ||
|
|
||
| [catch BuildFailedError] | ||
| └─► createBuildFailureComment(...) [github-comments.ts] | ||
| ``` | ||
|
|
||
| ## Tasks | ||
|
|
||
| - [ ] T001 Add `vercel-build` input to action.yml and `vercelBuild` field to `ActionConfig` type (file: action.yml, src/types.ts) | ||
| - [ ] T002 [P] Add unit tests for `vercel-build` input parsing in `getActionConfig()` (file: src/__tests__/config.test.ts) (depends on T001) | ||
| - [ ] T003 Parse `vercel-build` input in `getActionConfig()` and enforce mutual exclusivity with `prebuilt` (file: src/config.ts) (depends on T002) | ||
| - [ ] T004 [P] Add unit tests for `BuildFailedError` class (file: src/__tests__/vercel-build.test.ts) | ||
| - [ ] T005 Implement `BuildFailedError` class with stderr tail capture (file: src/vercel-build.ts) (depends on T004) | ||
| - [ ] T006 [P] Add unit tests for `runVercelPull()` and `runVercelBuild()` exec wrappers — covers happy path, scope propagation, build-env, target=production, exit-code failure (file: src/__tests__/vercel-build.test.ts) (depends on T005) | ||
| - [ ] T007 Implement `runVercelPull()` and `runVercelBuild()` using `@actions/exec` with streamed stdout/stderr listeners (file: src/vercel-build.ts) (depends on T006) | ||
| - [ ] T008 [P] Add unit tests for `runBuildStep()` orchestrator — verifies pull → build sequencing, returns `{ prebuilt, vercelOutputDir }`, propagates `BuildFailedError` (file: src/__tests__/vercel-build.test.ts) (depends on T007) | ||
| - [ ] T009 Implement `runBuildStep(config)` orchestrator that calls pull + build and returns updated config fragment (file: src/vercel-build.ts) (depends on T008) | ||
| - [ ] T010 [P] Add tests for build-failure comment helpers — verifies truncated tail formatting and PR/commit dispatch (file: src/__tests__/github-comments.test.ts) (depends on T009) | ||
| - [ ] T011 Add `createBuildFailureCommentOnPullRequest()` and `createBuildFailureCommentOnCommit()` helpers (file: src/github-comments.ts) (depends on T010) | ||
| - [ ] T012 [P] Add tests for `run()` integration — `vercel-build: true` invokes build runner before deploy and routes through prebuilt path; build failure posts comment and exits non-zero (file: src/__tests__/index.test.ts or new src/__tests__/run-build.test.ts) (depends on T011) | ||
| - [ ] T013 Wire `runBuildStep()` into `run()` in src/index.ts: call before `createVercelClient`, mutate config to `prebuilt = true`, catch `BuildFailedError` to post comment then rethrow (file: src/index.ts) (depends on T012) | ||
| - [ ] T014 [P] Add integration test using emulate.dev that verifies `vercel-build: true` flow end-to-end against a fixture project with `.vercel/output` (file: src/__integration__/vercel-build.test.ts) (depends on T013) | ||
| - [ ] T015 Update README.md with `vercel-build` input documentation, usage example, and the official Vercel KB workflow reference (file: README.md) (depends on T013) | ||
| - [ ] T016 Rebuild dist/ via `pnpm build` and commit the bundled output (file: dist/index.js) (depends on T015) | ||
|
|
||
| ## Dependencies | ||
|
|
||
| ``` | ||
| T001 ─► T002 ─► T003 | ||
| │ | ||
| T004 ─► T005 ─────┤ | ||
| ▼ | ||
| T006 ─► T007 ─► T008 ─► T009 ─► T010 ─► T011 ─► T012 ─► T013 ─┬─► T014 | ||
| │ | ||
| ├─► T015 ─► T016 | ||
| ``` | ||
|
|
||
| `[P]` markers indicate test-first parallel-capable tasks within their dependency cluster (RED phase can be authored while previous GREEN is being verified, but the implementation task immediately following each `[P]` test must wait for the test to be in place). | ||
|
|
||
| ## Key Files | ||
|
|
||
| | Path | Role | | ||
| |---|---| | ||
| | `action.yml` | Add `vercel-build` input definition | | ||
| | `src/types.ts` | Add `vercelBuild: boolean` to `ActionConfig` | | ||
| | `src/config.ts` | Parse input + enforce mutual exclusivity in `getActionConfig()` | | ||
| | `src/vercel-build.ts` | **NEW**: `BuildFailedError`, `runVercelPull()`, `runVercelBuild()`, `runBuildStep()` | | ||
| | `src/index.ts` | Wire `runBuildStep()` into `run()` orchestrator and handle `BuildFailedError` | | ||
| | `src/github-comments.ts` | Add build-failure comment helpers | | ||
| | `src/__tests__/config.test.ts` | Cover input parsing + mutual exclusivity | | ||
| | `src/__tests__/vercel-build.test.ts` | **NEW**: Unit tests for build runner | | ||
| | `src/__tests__/github-comments.test.ts` | Cover new comment helpers | | ||
| | `src/__tests__/index.test.ts` (or new `run-build.test.ts`) | Cover `run()` integration | | ||
| | `src/__integration__/vercel-build.test.ts` | **NEW**: Emulate-based end-to-end test | | ||
| | `README.md` | Document new input + example workflow | | ||
| | `dist/index.js` | Rebuilt bundle (committed) | | ||
|
|
||
| ## Verification | ||
|
|
||
| Manual verification (run after T013): | ||
|
|
||
| 1. **Happy path (preview)**: In `example/nextjs`, set `vercel-build: 'true'`, `prebuilt: 'false'`. Run the action against a real Vercel project (preview token). Expect: log shows `vercel pull` then `vercel build` invocations, `.vercel/output` is uploaded, deployment URL returned. | ||
| 2. **Happy path (production)**: Set `target: production` + `vercel-build: 'true'`. Expect: `vercel pull --environment=production` and `vercel build --prod` invoked. | ||
| 3. **Mutual exclusivity**: Set `vercel-build: 'true'` AND `prebuilt: 'true'`. Expect: action fails immediately with a clear conflict message; no API calls made. | ||
| 4. **Build failure**: In a fixture project, introduce a syntax error so `vercel build` fails. Expect: action exits non-zero, GitHub Actions log shows the build error, PR comment is posted with truncated tail (when `github-comment: true`). | ||
| 5. **Backward compatibility**: With `vercel-build` unset (or `false`), run an existing-style deployment. Expect: identical behavior to current main branch — no `vercel pull`/`vercel build` invocations. | ||
| 6. **Token redaction**: Inspect logs from steps 1–4. The Vercel token MUST NOT appear in any log line, comment, or error message. | ||
|
|
||
| Automated: | ||
| - `pnpm test` — all unit tests green | ||
| - `pnpm test --coverage` — coverage for new code > 80% | ||
| - `pnpm run lint` — no errors | ||
| - `pnpm run build` — clean build, no warnings | ||
|
|
||
| ## Progress | ||
|
|
||
| (populated by /please:implement during execution) | ||
|
|
||
| ## Decision Log | ||
|
|
||
| - **2026-04-30 — Approach A (new module) chosen over embedding in VercelApiClient.** Reason: separation of concerns, AGENTS.md compliance (boundary isolation), reuse of existing prebuilt deploy path with zero modification. | ||
| - **2026-04-30 — `@actions/exec` chosen over SDK.** Reason: `@vercel/client` v17 does not expose `build`/`pull` primitives; the spec's "SDK if available, else exec" condition resolves to "exec" deterministically. | ||
| - **2026-04-30 — Mutual exclusivity validated in `getActionConfig()`.** Reason: fail-fast at the input boundary before any network or filesystem I/O. | ||
|
|
||
| ## Surprises & Discoveries | ||
|
|
||
| (populated by /please:implement) | ||
65 changes: 65 additions & 0 deletions
65
.please/docs/tracks/active/auto-vercel-build-20260430/spec.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| --- | ||
| product_spec_domain: deployment/vercel-build | ||
| --- | ||
|
|
||
| # Local Vercel Build Step (`vercel-build` input) | ||
|
|
||
| > Track: auto-vercel-build-20260430 | ||
|
|
||
| ## Overview | ||
|
|
||
| Add a new `vercel-build` action input that, when set to `true`, instructs the action to execute the official Vercel CLI workflow locally before deployment: `vercel pull --environment=<target>` → `vercel build` → upload `.vercel/output` via the existing prebuilt deploy path. This mirrors the recommended workflow in the [Vercel KB GitHub Actions guide](https://vercel.com/kb/guide/how-can-i-use-github-actions-with-vercel#configuring-github-actions-for-vercel) and lets users build inside CI (with their own secrets and runtime) instead of relying on Vercel's remote build. | ||
|
|
||
| The current default behavior (uploading source via `@vercel/client` for remote build) is preserved when `vercel-build` is `false` (the default), maintaining backward compatibility with existing pipelines. | ||
|
|
||
| ## Requirements | ||
|
|
||
| ### Functional Requirements | ||
|
|
||
| - [ ] FR-1: Add a new boolean action input `vercel-build` to `action.yml` (default: `false`). | ||
| - [ ] FR-2: When `vercel-build: true` and `prebuilt: false`, the action MUST execute, in order, in the configured `working-directory`: | ||
| 1. `vercel pull --yes --environment=<target> --token=<vercel-token>` (where `<target>` is `production` or `preview` based on the existing `production`/`target` inputs) | ||
| 2. `vercel build [--prod] --token=<vercel-token>` (the `--prod` flag is included only when targeting production) | ||
|
amondnet marked this conversation as resolved.
Outdated
|
||
| 3. Treat the resulting `.vercel/output` directory as the deploy artifact, using the existing prebuilt deploy code path (`config.prebuilt = true`, `vercelOutputDir` defaulted to `<working-directory>/.vercel/output`). | ||
|
amondnet marked this conversation as resolved.
Outdated
|
||
| - [ ] FR-3: When `vercel-build: true` AND `prebuilt: true`, the action MUST fail fast with a clear error: the two flags are mutually exclusive (prebuilt means the user already built; `vercel-build: true` asks the action to build). | ||
| - [ ] FR-4: When `vercel-build: false` (default), preserve the current source-upload behavior — no behavior change for existing users. | ||
| - [ ] FR-5: The `vercel pull` and `vercel build` commands MUST be invoked programmatically through the `@vercel/client` SDK if it exposes a build/pull API; otherwise fall back to executing the bundled `vercel` package via `@actions/exec`. (Investigation of `@vercel/client` capability happens in the plan phase.) | ||
| - [ ] FR-6: If the team scope is configured (`vercel-org-id` / `--scope`), it MUST be propagated to both `vercel pull` and `vercel build` invocations. | ||
| - [ ] FR-7: Build environment variables (`build-env`) MUST be available to the local `vercel build` execution. | ||
| - [ ] FR-8: On `vercel build` failure, the action MUST: | ||
| - Fail the action with a non-zero exit code, | ||
| - Stream/capture build stdout+stderr into the GitHub Actions log, | ||
| - Post a comment on the PR/commit (when comments are enabled) summarizing the failure with a truncated tail of build output. | ||
|
|
||
| ### Non-functional Requirements | ||
|
|
||
| - [ ] NFR-1: No breaking change to existing inputs or default behavior — existing workflows continue to work unchanged. | ||
| - [ ] NFR-2: Build output streaming MUST not buffer the entire log in memory (use streamed exec). | ||
| - [ ] NFR-3: Vercel token MUST never appear in logs, comments, or error messages (existing secret-masking guarantees apply). | ||
| - [ ] NFR-4: Test coverage for new code MUST exceed 80% (per `workflow.md`). | ||
|
|
||
| ## Acceptance Criteria | ||
|
|
||
| - [ ] AC-1: Setting `vercel-build: true` with `prebuilt: false` runs `vercel pull` then `vercel build` in `working-directory`, then deploys `.vercel/output` as a prebuilt deployment, and the resulting deployment URL is returned via the `preview-url` output. | ||
| - [ ] AC-2: Setting `vercel-build: true` with `prebuilt: true` fails the action immediately with a message identifying the conflict, and does not call any Vercel API. | ||
| - [ ] AC-3: With `vercel-build` unset or `false`, deployments produce identical behavior, payloads, and outputs to the current implementation (verified via existing integration tests). | ||
| - [ ] AC-4: When `vercel build` fails, the action exits non-zero, the failure is visible in the GitHub Actions log, and a PR/commit comment containing the truncated tail of build output is posted (when comments are enabled). | ||
| - [ ] AC-5: Targeting production (`production: true`) passes `--prod` to `vercel build` and `--environment=production` to `vercel pull`; otherwise uses `preview`. | ||
| - [ ] AC-6: Build secrets (`build-env`) and team scope (`vercel-org-id`) are honored by the local `vercel build` execution. | ||
|
|
||
| ## Out of Scope | ||
|
|
||
| - Auto-detection of when a build is needed (no inference; behavior is purely flag-driven). | ||
| - Caching `.vercel/output` between runs (tracked separately as a future optimization). | ||
| - Custom build commands beyond `vercel build` (users should configure `buildCommand` in `vercel.json`). | ||
| - Deprecating the current source-upload path (a future major version may revisit defaults; not part of this track). | ||
| - Running `vercel build` independently of deployment (no "build only" mode in this track). | ||
| - Changes to the legacy `vercel-args` CLI fallback path. | ||
|
|
||
| ## Assumptions | ||
|
|
||
| - The `@vercel/client` SDK (v17.2.65, currently a dependency) is the preferred entry point. If it does not expose a build/pull primitive, the bundled `vercel` package (v50.0.0, already a dependency) will be invoked via `@actions/exec`. This decision is deferred to the plan phase. | ||
| - The `working-directory` input, after normalization in `parseWorkingDirectory()` (`src/config.ts`), is an absolute path — confirmed by existing gotcha #341. The new build step relies on this same guarantee. | ||
| - Existing prebuilt deploy code path (`vercel-api.ts:41-45`, `buildClientOptions`) correctly handles `.vercel/output` upload and only needs to be invoked after a successful local build. | ||
| - `production: true` is the existing convention for production deployments; the new build step infers `--prod` from this flag rather than introducing a new target input. | ||
| - Out-of-scope decisions in Section "Out of Scope" reflect the user's intent to keep this track tightly focused on the build-step feature; revisit in follow-up tracks if needed. | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.