From ef57cb00425c60009eac3714fbc1acafe884948a Mon Sep 17 00:00:00 2001
From: Yevhen <38507392+JenyaL@users.noreply.github.com>
Date: Sat, 27 Sep 2025 19:46:58 -0600
Subject: [PATCH 01/20] docs(fetch-vercel-logs.yml)
---
.github/workflows/fetch-vercel-logs.yml | 321 ++++++++++++++++--------
1 file changed, 220 insertions(+), 101 deletions(-)
diff --git a/.github/workflows/fetch-vercel-logs.yml b/.github/workflows/fetch-vercel-logs.yml
index aee8977..4cb7d31 100644
--- a/.github/workflows/fetch-vercel-logs.yml
+++ b/.github/workflows/fetch-vercel-logs.yml
@@ -1,144 +1,263 @@
-name: Fetch Vercel Logs
+name: Fetch Vercel Logs (PR + auto on Vercel checks)
on:
- # This enables the "Run workflow" button in GitHub Actions UI
+ pull_request:
+ types: [opened, synchronize, reopened]
+ check_run:
+ types: [completed]
workflow_dispatch:
inputs:
pr:
- description: 'Pull Request number (optional)'
- required: false
- sha:
- description: 'Commit SHA (optional, overrides PR if set)'
- required: false
+ description: "PR number (e.g. 9)"
+ required: true
permissions:
contents: read
- pull-requests: write # required to add a comment in PRs
+ pull-requests: write
+ checks: write
jobs:
fetch:
+ if: |
+ github.event_name == 'pull_request' ||
+ github.event_name == 'workflow_dispatch' ||
+ (github.event_name == 'check_run' &&
+ (contains(github.event.check_run.name, 'Vercel') ||
+ contains(github.event.check_run.app.slug, 'vercel')))
runs-on: ubuntu-latest
env:
- # Secrets stored in GitHub → Settings → Secrets and variables → Actions
- VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
- VERCEL_PROJECT: ${{ secrets.VERCEL_PROJECT_ID }}
- VERCEL_ORG: ${{ secrets.VERCEL_ORG_ID }}
+ VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} # personal token
+ VERCEL_PROJECT: ${{ secrets.VERCEL_PROJECT_ID }} # prj_...
+ VERCEL_ORG: ${{ secrets.VERCEL_ORG_ID }} # team_...
+ # optional: Settings → Secrets → Actions
+ VERCEL_DEPLOY_HOOK_URL: ${{ secrets.VERCEL_DEPLOY_HOOK_URL }}
steps:
- uses: actions/checkout@v4
- # Step 1: Resolve PR number and Commit SHA
- - name: Resolve PR number and SHA
- id: ctx
- shell: bash
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ - name: Ensure jq
run: |
- PR_INPUT="${{ github.event.inputs.pr }}"
- SHA_INPUT="${{ github.event.inputs.sha }}"
-
- # If SHA provided manually, use it
- if [ -n "$SHA_INPUT" ]; then
- echo "sha=$SHA_INPUT" >> $GITHUB_OUTPUT
- else
- # If PR number provided, fetch its HEAD SHA
- if [ -n "$PR_INPUT" ]; then
- SHA=$(gh pr view "$PR_INPUT" --json headRefOid --jq .headRefOid)
- echo "sha=$SHA" >> $GITHUB_OUTPUT
- echo "pr=$PR_INPUT" >> $GITHUB_OUTPUT
- else
- # Try to find PR automatically by current commit
- PR=$(gh pr list --search "${{ github.sha }}" --json number --jq '.[0].number' || true)
- if [ -n "$PR" ] && [ "$PR" != "null" ]; then
- SHA=$(gh pr view "$PR" --json headRefOid --jq .headRefOid)
- echo "pr=$PR" >> $GITHUB_OUTPUT
- echo "sha=$SHA" >> $GITHUB_OUTPUT
- else
- # Fallback: just use current commit SHA
- echo "sha=${{ github.sha }}" >> $GITHUB_OUTPUT
- fi
- fi
+ if ! command -v jq >/dev/null 2>&1; then
+ sudo apt-get update && sudo apt-get install -y jq
fi
- # Step 2: Find latest Vercel deployment for this PR/commit
- - name: Find Vercel deployment
+ # (optional) redeploy from GitHub before fetching logs
+ - name: Optional redeploy via Deploy Hook
+ if: ${{ github.event_name == 'workflow_dispatch' && inputs.redeploy == 'yes' && env.VERCEL_DEPLOY_HOOK_URL != '' }}
+ run: |
+ echo "Triggering Vercel Deploy Hook…"
+ curl -sS -X POST "$VERCEL_DEPLOY_HOOK_URL" -o /dev/null
+ # give Vercel time to kick the new build & post a check
+ sleep 25
+
+ # resolve PR/SHA: inputs → event → latest open PR
+ - name: Resolve PR & SHA
+ id: ctx
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const {owner, repo} = context.repo;
+ const inputPr = core.getInput('pr');
+ let pr = inputPr ? Number(inputPr) : null;
+ let sha = null;
+
+ if (context.eventName === 'pull_request') {
+ pr = pr ?? context.payload.pull_request.number;
+ sha = context.payload.pull_request.head.sha;
+ } else if (context.eventName === 'check_run') {
+ const cr = context.payload.check_run;
+ sha = cr.head_sha;
+ if (!pr && sha) {
+ const prs = await github.rest.repos.listPullRequestsAssociatedWithCommit({ owner, repo, commit_sha: sha });
+ pr = prs.data[0]?.number || null;
+ }
+ }
+
+ if (!pr) {
+ const prs = await github.rest.pulls.list({ owner, repo, state: 'open', per_page: 1, sort: 'updated', direction: 'desc' });
+ pr = prs.data[0]?.number || null;
+ }
+ if (!sha && pr) {
+ const prInfo = await github.rest.pulls.get({ owner, repo, pull_number: pr });
+ sha = prInfo.data.head.sha;
+ }
+
+ core.setOutput('pr', pr ? String(pr) : '');
+ core.setOutput('sha', sha ? String(sha) : '');
+
+ - name: Seed deployment from check_run
+ id: seed
+ if: ${{ github.event_name == 'check_run' }}
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const url = context.payload.check_run?.details_url || '';
+ let dep = '';
+ let host = '';
+ if (url) {
+ const m1 = url.match(/dpl_[A-Za-z0-9]+/);
+ if (m1) dep = m1[0];
+ const m2 = url.match(/https?:\/\/([^\/\s]+\.vercel\.app)/i);
+ if (m2) host = m2[1];
+ }
+ core.setOutput('id', dep);
+ core.setOutput('host', host);
+
+ # find deployment by PR (meta.githubPrId), or by preview host, or latest preview
+ - name: Find deployment
id: dep
- shell: bash
run: |
- set -e
- test -n "$VERCEL_TOKEN" || { echo "VERCEL_TOKEN is empty"; exit 1; }
- test -n "$VERCEL_PROJECT" || { echo "VERCEL_PROJECT_ID is empty"; exit 1; }
- test -n "$VERCEL_ORG" || { echo "VERCEL_ORG_ID is empty"; exit 1; }
+ if [ -n "${{ steps.seed.outputs.id }}" ]; then
+ echo "id=${{ steps.seed.outputs.id }}" >> $GITHUB_OUTPUT
+ exit 0
+ fi
- # Get list of latest preview deployments for the project
curl -sS -H "Authorization: Bearer $VERCEL_TOKEN" \
- "https://api.vercel.com/v6/deployments?projectId=$VERCEL_PROJECT&target=preview&limit=50" \
- > deployments.json
+ "https://api.vercel.com/v6/deployments?projectId=$VERCEL_PROJECT&target=preview&limit=50" > deployments.json
- # Prefer to match by PR number
- DEP_ID=$(jq -r --arg PR "${{ steps.ctx.outputs.pr }}" '.deployments[]
- | select(.meta.githubPrId == $PR) | .uid' deployments.json | head -n1)
+ PR="${{ steps.ctx.outputs.pr }}"
+ DEP_ID=""
+ if [ -n "$PR" ]; then
+ DEP_ID=$(jq -r --arg PR "$PR" '.deployments[] | select(.meta.githubPrId == $PR) | .uid' deployments.json | head -n1)
+ fi
- # If not found, try to match by commit SHA
if [ -z "$DEP_ID" ] || [ "$DEP_ID" = "null" ]; then
- DEP_ID=$(jq -r --arg SHA "${{ steps.ctx.outputs.sha }}" '.deployments[]
- | select(.meta.githubCommitSha == $SHA) | .uid' deployments.json | head -n1)
+ HOST="${{ steps.seed.outputs.host }}"
+ if [ -n "$HOST" ]; then
+ DEP_ID=$(jq -r --arg HN "$HOST" '.deployments[] | select(.url == $HN) | .uid' deployments.json | head -n1)
+ fi
fi
- # If still not found, fail
if [ -z "$DEP_ID" ] || [ "$DEP_ID" = "null" ]; then
- echo "No deployment found for this PR/SHA"; exit 1;
+ DEP_ID=$(jq -r '.deployments[0].uid' deployments.json)
fi
+ test -n "$DEP_ID" && [ "$DEP_ID" != "null" ] || { echo "No deployment id found"; exit 1; }
echo "id=$DEP_ID" >> $GITHUB_OUTPUT
- # Step 3: Fetch deployment logs (events) and convert to plain text
- - name: Fetch logs (events → text)
- shell: bash
+ - name: Fetch events JSON
+ id: ev
run: |
- curl -sS -H "Authorization: Bearer $VERCEL_TOKEN" \
- "https://api.vercel.com/v2/deployments/${{ steps.dep.outputs.id }}/events?orgId=$VERCEL_ORG" \
- > vercel-events.json
+ DEP="${{ steps.dep.outputs.id || steps.seed.outputs.id }}"
+ URL="https://api.vercel.com/v2/deployments/${DEP}/events?teamId=${{ env.VERCEL_ORG }}&limit=1000"
+ curl -sS -H "Authorization: Bearer $VERCEL_TOKEN" "$URL" > vercel-events.json
- # Extract only text log lines from events
- jq -r '.events[] | .payload.text // empty' vercel-events.json > vercel-build.log || true
+ - name: Parse to plaintext
+ id: logs
+ run: |
+ jq -r '
+ def evs:
+ if type=="array" then .
+ elif has("events") then .events
+ else [] end;
+ evs
+ | .[]
+ | ( .payload? // .entry? // . )
+ | ( .text?, .message?, .error?, .error?.message?, (.entries? // [])[]?, (.logs? // [])[]? )
+ | strings
+ ' vercel-events.json > vercel-build.log || true
- # Prepare a short tail for PR comment (last 120 lines)
- tail -n 120 vercel-build.log > vercel-build-tail.txt || true
+ if [ -s vercel-build.log ]; then
+ tail -n 200 vercel-build.log > tail.txt
+ head -c 60000 vercel-build.log > full.txt
+ else
+ echo "[no events text returned by Vercel API]" > tail.txt
+ cp tail.txt full.txt
+ fi
- # Step 4: Upload full log file as artifact
- - name: Upload full log
- uses: actions/upload-artifact@v4
- with:
- name: vercel-build.log
- path: vercel-build.log
- if-no-files-found: ignore
+ - name: Deployment info
+ id: info
+ run: |
+ DEP="${{ steps.dep.outputs.id || steps.seed.outputs.id }}"
+ curl -sS -H "Authorization: Bearer $VERCEL_TOKEN" \
+ "https://api.vercel.com/v13/deployments/${DEP}?teamId=${{ env.VERCEL_ORG }}" > vercel-deployment.json
+ echo "url=$(jq -r '.url // empty' vercel-deployment.json)" >> $GITHUB_OUTPUT
+ echo "state=$(jq -r '.readyState // empty' vercel-deployment.json)" >> $GITHUB_OUTPUT
- # Step 5: Post log tail as a PR comment (only if PR number is known)
- - name: Comment tail to PR
- if: ${{ steps.ctx.outputs.pr != '' }}
+ # Job summary — полезно в Checks → job
+ - name: Job summary (tail 200)
+ run: |
+ ICON="✅"; [ "${{ steps.info.outputs.state }}" = "ERROR" ] && ICON="❌"
+ echo "### ${ICON} Vercel Logs (tail 200)" >> "$GITHUB_STEP_SUMMARY"
+ if [ -n "${{ steps.info.outputs.url }}" ]; then
+ echo "Preview: https://${{ steps.info.outputs.url }}" >> "$GITHUB_STEP_SUMMARY"
+ echo "" >> "$GITHUB_STEP_SUMMARY"
+ fi
+ echo '```' >> "$GITHUB_STEP_SUMMARY"
+ cat tail.txt >> "$GITHUB_STEP_SUMMARY"
+ echo '```' >> "$GITHUB_STEP_SUMMARY"
+
+ - name: Publish check run (Vercel Logs)
+ if: ${{ steps.ctx.outputs.sha != '' }}
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
- const pr = Number('${{ steps.ctx.outputs.pr }}');
- const dep = '${{ steps.dep.outputs.id }}';
- let tail = '(no log lines)';
- try { tail = fs.readFileSync('vercel-build-tail.txt','utf8'); } catch {}
-
- const body = [
- `❌ Vercel deployment **${dep}** failed (PR #${pr})`,
- '',
- 'Last 120 lines:',
- '```',
- tail || '(empty)',
- '```',
- '📎 Full log attached as artifact (vercel-build.log).'
- ].join('\n');
-
- await github.rest.issues.createComment({
- owner: context.repo.owner,
- repo: context.repo.repo,
- issue_number: pr,
- body
- })
+ const {owner, repo} = context.repo;
+ const sha = '${{ steps.ctx.outputs.sha }}';
+ const state= '${{ steps.info.outputs.state }}';
+ const url = '${{ steps.info.outputs.url }}';
+ const tail = fs.readFileSync('tail.txt','utf8');
+ const isFail = state === 'ERROR';
+ await github.rest.checks.create({
+ owner, repo,
+ name: 'Vercel Logs',
+ head_sha: sha,
+ status: 'completed',
+ conclusion: isFail ? 'failure' : 'success',
+ output: {
+ title: `Vercel Logs (${state || 'unknown'})`,
+ summary: `${url ? `Preview: https://${url}\n\n` : ''}Last 200 lines:\n\n\`\`\`\n${tail}\n\`\`\``
+ }
+ });
+
+ - name: Build PR comment markdown (failure only)
+ if: ${{ steps.ctx.outputs.pr != '' && steps.info.outputs.state == 'ERROR' }}
+ run: |
+ ICON="❌"
+ PREVIEW=""
+ if [ -n "${{ steps.info.outputs.url }}" ]; then
+ PREVIEW="https://${{ steps.info.outputs.url }}"
+ fi
+ {
+ echo "${ICON} Deployment: \`${{ steps.dep.outputs.id || steps.seed.outputs.id }}\` — state: **${{ steps.info.outputs.state }}**"
+ if [ -n "$PREVIEW" ]; then
+ echo
+ echo "Preview: $PREVIEW"
+ fi
+ echo
+ echo "Show full log (truncated)
"
+ echo
+ echo '```'
+ cat full.txt
+ echo '```'
+ echo
+ echo " "
+ } > comment.md
+
+ - name: Comment to PR on failure
+ if: ${{ steps.ctx.outputs.pr != '' && steps.info.outputs.state == 'ERROR' }}
+ uses: peter-evans/create-or-update-comment@v4
+ with:
+ issue-number: ${{ steps.ctx.outputs.pr }}
+ body-file: comment.md
+ edit-mode: replace
+ search: 'Deployment:'
+
+ - name: Fail job if Vercel ERROR
+ if: ${{ steps.info.outputs.state == 'ERROR' }}
+ run: |
+ echo "Vercel readyState = ERROR → failing job."
+ exit 1
+
+ - name: Upload raw (debug)
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: vercel-raw
+ path: |
+ vercel-events.json
+ vercel-deployment.json
+ vercel-build.log
+ tail.txt
+ full.txt
\ No newline at end of file
From 90bdee7e7daa0860ed2068982099908bb2db4efd Mon Sep 17 00:00:00 2001
From: Yevhen <38507392+JenyaL@users.noreply.github.com>
Date: Sat, 27 Sep 2025 19:50:30 -0600
Subject: [PATCH 02/20] docs(fetch-vercel-logs.yml) test 1.0
---
.github/workflows/fetch-vercel-logs.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/fetch-vercel-logs.yml b/.github/workflows/fetch-vercel-logs.yml
index 4cb7d31..89fcdd6 100644
--- a/.github/workflows/fetch-vercel-logs.yml
+++ b/.github/workflows/fetch-vercel-logs.yml
@@ -174,7 +174,7 @@ jobs:
echo "url=$(jq -r '.url // empty' vercel-deployment.json)" >> $GITHUB_OUTPUT
echo "state=$(jq -r '.readyState // empty' vercel-deployment.json)" >> $GITHUB_OUTPUT
- # Job summary — полезно в Checks → job
+ # Job summary — полезно в Checks → job.
- name: Job summary (tail 200)
run: |
ICON="✅"; [ "${{ steps.info.outputs.state }}" = "ERROR" ] && ICON="❌"
From e00aba5762b06cd16234ef494b5c1e67724124a3 Mon Sep 17 00:00:00 2001
From: Yevhen <38507392+JenyaL@users.noreply.github.com>
Date: Sat, 27 Sep 2025 19:55:13 -0600
Subject: [PATCH 03/20] docs(index.js) test 1.0
---
index.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/index.js b/index.js
index 12104bd..db7d83c 100644
--- a/index.js
+++ b/index.js
@@ -1,6 +1,6 @@
require("dotenv").config();
- if (process.env.BUILD_RESULT === "success") {
+ if (process.env.BUILD_RESULT !== "success") {
console.log("Build success");
process.exit(0);
}
From 0d3785e624feb50464a1c43dd37f9b90600a6a39 Mon Sep 17 00:00:00 2001
From: Yevhen <38507392+JenyaL@users.noreply.github.com>
Date: Sat, 27 Sep 2025 19:56:16 -0600
Subject: [PATCH 04/20] docs(index.js) test 1.1
---
index.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/index.js b/index.js
index db7d83c..12104bd 100644
--- a/index.js
+++ b/index.js
@@ -1,6 +1,6 @@
require("dotenv").config();
- if (process.env.BUILD_RESULT !== "success") {
+ if (process.env.BUILD_RESULT === "success") {
console.log("Build success");
process.exit(0);
}
From 4cb5f882daed84b7681d39117afc03e1b7f0be93 Mon Sep 17 00:00:00 2001
From: Yevhen <38507392+JenyaL@users.noreply.github.com>
Date: Wed, 8 Oct 2025 20:00:47 -0600
Subject: [PATCH 05/20] chore(ci): add GitHub Actions workflow for build and
Vercel deploy
---
.github/workflows/ci-deploy.yml | 116 ++++++++++++++++++++++++++++++++
1 file changed, 116 insertions(+)
create mode 100644 .github/workflows/ci-deploy.yml
diff --git a/.github/workflows/ci-deploy.yml b/.github/workflows/ci-deploy.yml
new file mode 100644
index 0000000..8e36992
--- /dev/null
+++ b/.github/workflows/ci-deploy.yml
@@ -0,0 +1,116 @@
+name: CI + Deploy (POC)
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main ]
+
+permissions:
+ contents: read
+
+env:
+ VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
+ VERCEL_PROJECT: ${{ secrets.VERCEL_PROJECT_ID }}
+ VERCEL_ORG: ${{ secrets.VERCEL_ORG_ID }}
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 18
+ cache: npm
+
+ - name: Install
+ run: |
+ if [ -f package-lock.json ]; then npm ci; else npm install; fi
+
+ - name: Build
+ run: npm run build
+
+ - name: Upload build summary
+ run: |
+ echo "Build ✅" >> "$GITHUB_STEP_SUMMARY"
+
+ deploy:
+ needs: build
+ if: ${{ success() }}
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 18
+
+ - name: Install Vercel CLI
+ run: npm i -g vercel@latest
+
+ - name: Decide target
+ id: tgt
+ run: |
+ if [ "${{ github.event_name }}" = "pull_request" ]; then
+ echo "target=preview" >> $GITHUB_OUTPUT
+ else
+ echo "target=production" >> $GITHUB_OUTPUT
+ fi
+
+ - name: Deploy to Vercel (from GitHub)
+ id: deploy
+ env:
+ VC_TOKEN: ${{ env.VERCEL_TOKEN }}
+ VC_PROJ: ${{ env.VERCEL_PROJECT }}
+ VC_TEAM: ${{ env.VERCEL_ORG }}
+ TARGET: ${{ steps.tgt.outputs.target }}
+ run: |
+ set -e
+
+
+ DEPLOY_OUTPUT=$(vercel --yes \
+ --token="$VC_TOKEN" \
+ --scope="$VC_TEAM" \
+ --project="$VC_PROJ" \
+ --target="$TARGET" \
+ --confirm 2>&1)
+
+ echo "$DEPLOY_OUTPUT"
+
+ URL=$(printf "%s\n" "$DEPLOY_OUTPUT" | grep -Eo 'https?://[a-zA-Z0-9.-]+\.vercel\.app' | tail -n1 || true)
+ echo "url=$URL" >> $GITHUB_OUTPUT
+
+ HOST=$(echo "$URL" | sed -E 's#https?://##')
+ echo "host=$HOST" >> $GITHUB_OUTPUT
+
+ - name: Resolve Deployment ID
+ id: dep
+ if: ${{ steps.deploy.outputs.host != '' }}
+ env:
+ VC_TOKEN: ${{ env.VERCEL_TOKEN }}
+ VC_TEAM: ${{ env.VERCEL_ORG }}
+ run: |
+ # /v13/deployments?teamId=...&url=
+ HOST="${{ steps.deploy.outputs.host }}"
+ UID=$(curl -sS -H "Authorization: Bearer $VC_TOKEN" \
+ "https://api.vercel.com/v13/deployments?teamId=$VC_TEAM&url=$HOST" \
+ | jq -r '.deployments[0].uid // empty')
+ echo "id=$UID" >> $GITHUB_OUTPUT
+
+ - name: Summary (URL + ID)
+ run: |
+ echo "### Vercel Deployment" >> "$GITHUB_STEP_SUMMARY"
+ echo "" >> "$GITHUB_STEP_SUMMARY"
+ echo "- Target: **${{ steps.tgt.outputs.target }}**" >> "$GITHUB_STEP_SUMMARY"
+ if [ -n "${{ steps.deploy.outputs.url }}" ]; then
+ echo "- URL: ${{ steps.deploy.outputs.url }}" >> "$GITHUB_STEP_SUMMARY"
+ else
+ echo "- URL: (not detected)" >> "$GITHUB_STEP_SUMMARY"
+ fi
+ if [ -n "${{ steps.dep.outputs.id }}" ]; then
+ echo "- Deployment ID: \`${{ steps.dep.outputs.id }}\`" >> "$GITHUB_STEP_SUMMARY"
+ else
+ echo "- Deployment ID: (not detected)" >> "$GITHUB_STEP_SUMMARY"
+ fi
\ No newline at end of file
From 9581a3254c65f83c47cd85c3c40ba6bbc054553c Mon Sep 17 00:00:00 2001
From: Yevhen <38507392+JenyaL@users.noreply.github.com>
Date: Wed, 8 Oct 2025 20:08:42 -0600
Subject: [PATCH 06/20] ci: add initial build + deploy workflow (POC)
---
index.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/index.js b/index.js
index 12104bd..4d4aaf1 100644
--- a/index.js
+++ b/index.js
@@ -5,4 +5,4 @@ require("dotenv").config();
process.exit(0);
}
-throw new Error("Build failed");
+// throw new Error("Build failed");
From 856d629233a67ee3a3bbedb2c1710f03acf00ddb Mon Sep 17 00:00:00 2001
From: Yevhen <38507392+JenyaL@users.noreply.github.com>
Date: Wed, 8 Oct 2025 20:13:49 -0600
Subject: [PATCH 07/20] ci: add build and deploy workflow to Vercel (POC)
---
.github/workflows/ci-deploy.yml | 56 +++++++++++++++------------------
1 file changed, 26 insertions(+), 30 deletions(-)
diff --git a/.github/workflows/ci-deploy.yml b/.github/workflows/ci-deploy.yml
index 8e36992..9eccf15 100644
--- a/.github/workflows/ci-deploy.yml
+++ b/.github/workflows/ci-deploy.yml
@@ -33,8 +33,7 @@ jobs:
run: npm run build
- name: Upload build summary
- run: |
- echo "Build ✅" >> "$GITHUB_STEP_SUMMARY"
+ run: echo "Build ✅" >> "$GITHUB_STEP_SUMMARY"
deploy:
needs: build
@@ -47,6 +46,12 @@ jobs:
with:
node-version: 18
+ - name: Ensure jq
+ run: |
+ if ! command -v jq >/dev/null 2>&1; then
+ sudo apt-get update && sudo apt-get install -y jq
+ fi
+
- name: Install Vercel CLI
run: npm i -g vercel@latest
@@ -67,37 +72,28 @@ jobs:
VC_TEAM: ${{ env.VERCEL_ORG }}
TARGET: ${{ steps.tgt.outputs.target }}
run: |
- set -e
+ set -euo pipefail
+ # Сформируем базовую команду деплоя
+ CMD=(vercel deploy --yes --token="$VC_TOKEN" --scope="$VC_TEAM" --project="$VC_PROJ" --confirm --output=json)
+ # Прод деплой – только для push в main
+ if [ "$TARGET" = "production" ]; then
+ CMD+=(--prod)
+ fi
- DEPLOY_OUTPUT=$(vercel --yes \
- --token="$VC_TOKEN" \
- --scope="$VC_TEAM" \
- --project="$VC_PROJ" \
- --target="$TARGET" \
- --confirm 2>&1)
+ # Запускаем и получаем JSON
+ DEPLOY_JSON="$("${CMD[@]}")"
+ echo "$DEPLOY_JSON" > deploy.json
- echo "$DEPLOY_OUTPUT"
+ # Вытаскиваем основные поля (CLI обычно отдаёт url и/или aliases[])
+ URL=$(jq -r '.url // empty' deploy.json)
+ if [ -z "$URL" ] || [ "$URL" = "null" ]; then
+ URL=$(jq -r '.aliases[0] // empty' deploy.json)
+ fi
+ ID=$(jq -r '.id // .uid // empty' deploy.json)
- URL=$(printf "%s\n" "$DEPLOY_OUTPUT" | grep -Eo 'https?://[a-zA-Z0-9.-]+\.vercel\.app' | tail -n1 || true)
echo "url=$URL" >> $GITHUB_OUTPUT
-
- HOST=$(echo "$URL" | sed -E 's#https?://##')
- echo "host=$HOST" >> $GITHUB_OUTPUT
-
- - name: Resolve Deployment ID
- id: dep
- if: ${{ steps.deploy.outputs.host != '' }}
- env:
- VC_TOKEN: ${{ env.VERCEL_TOKEN }}
- VC_TEAM: ${{ env.VERCEL_ORG }}
- run: |
- # /v13/deployments?teamId=...&url=
- HOST="${{ steps.deploy.outputs.host }}"
- UID=$(curl -sS -H "Authorization: Bearer $VC_TOKEN" \
- "https://api.vercel.com/v13/deployments?teamId=$VC_TEAM&url=$HOST" \
- | jq -r '.deployments[0].uid // empty')
- echo "id=$UID" >> $GITHUB_OUTPUT
+ echo "id=$ID" >> $GITHUB_OUTPUT
- name: Summary (URL + ID)
run: |
@@ -109,8 +105,8 @@ jobs:
else
echo "- URL: (not detected)" >> "$GITHUB_STEP_SUMMARY"
fi
- if [ -n "${{ steps.dep.outputs.id }}" ]; then
- echo "- Deployment ID: \`${{ steps.dep.outputs.id }}\`" >> "$GITHUB_STEP_SUMMARY"
+ if [ -n "${{ steps.deploy.outputs.id }}" ]; then
+ echo "- Deployment ID: \`${{ steps.deploy.outputs.id }}\`" >> "$GITHUB_STEP_SUMMARY"
else
echo "- Deployment ID: (not detected)" >> "$GITHUB_STEP_SUMMARY"
fi
\ No newline at end of file
From 2d965ff684168c23ec6d751ce49b012db8307904 Mon Sep 17 00:00:00 2001
From: Yevhen <38507392+JenyaL@users.noreply.github.com>
Date: Wed, 8 Oct 2025 20:19:38 -0600
Subject: [PATCH 08/20] fix: update Vercel deploy step and remove deprecated
flags
---
.github/workflows/ci-deploy.yml | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/.github/workflows/ci-deploy.yml b/.github/workflows/ci-deploy.yml
index 9eccf15..b64e81c 100644
--- a/.github/workflows/ci-deploy.yml
+++ b/.github/workflows/ci-deploy.yml
@@ -68,24 +68,19 @@ jobs:
id: deploy
env:
VC_TOKEN: ${{ env.VERCEL_TOKEN }}
- VC_PROJ: ${{ env.VERCEL_PROJECT }}
VC_TEAM: ${{ env.VERCEL_ORG }}
TARGET: ${{ steps.tgt.outputs.target }}
run: |
set -euo pipefail
- # Сформируем базовую команду деплоя
- CMD=(vercel deploy --yes --token="$VC_TOKEN" --scope="$VC_TEAM" --project="$VC_PROJ" --confirm --output=json)
- # Прод деплой – только для push в main
+ CMD=(vercel deploy --yes --token="$VC_TOKEN" --scope="$VC_TEAM" --prebuilt --confirm --output=json)
if [ "$TARGET" = "production" ]; then
CMD+=(--prod)
fi
- # Запускаем и получаем JSON
DEPLOY_JSON="$("${CMD[@]}")"
echo "$DEPLOY_JSON" > deploy.json
- # Вытаскиваем основные поля (CLI обычно отдаёт url и/или aliases[])
URL=$(jq -r '.url // empty' deploy.json)
if [ -z "$URL" ] || [ "$URL" = "null" ]; then
URL=$(jq -r '.aliases[0] // empty' deploy.json)
From dc55384ae684c3ae8a23333e7dd5ff01af66ff87 Mon Sep 17 00:00:00 2001
From: Yevhen <38507392+JenyaL@users.noreply.github.com>
Date: Wed, 8 Oct 2025 20:23:40 -0600
Subject: [PATCH 09/20] chore(ci): fix Vercel deploy step for latest CLI and
remove deprecated options
---
.github/workflows/ci-deploy.yml | 25 ++++++++++---------------
1 file changed, 10 insertions(+), 15 deletions(-)
diff --git a/.github/workflows/ci-deploy.yml b/.github/workflows/ci-deploy.yml
index b64e81c..90d6221 100644
--- a/.github/workflows/ci-deploy.yml
+++ b/.github/workflows/ci-deploy.yml
@@ -10,9 +10,8 @@ permissions:
contents: read
env:
- VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
- VERCEL_PROJECT: ${{ secrets.VERCEL_PROJECT_ID }}
- VERCEL_ORG: ${{ secrets.VERCEL_ORG_ID }}
+ VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
+ VERCEL_ORG: ${{ secrets.VERCEL_ORG_ID }}
jobs:
build:
@@ -73,22 +72,18 @@ jobs:
run: |
set -euo pipefail
- CMD=(vercel deploy --yes --token="$VC_TOKEN" --scope="$VC_TEAM" --prebuilt --confirm --output=json)
+ ARGS=(deploy --yes --token "$VC_TOKEN" --scope "$VC_TEAM" --prebuilt)
if [ "$TARGET" = "production" ]; then
- CMD+=(--prod)
+ ARGS+=(--prod)
fi
- DEPLOY_JSON="$("${CMD[@]}")"
- echo "$DEPLOY_JSON" > deploy.json
+ URL="$(vercel "${ARGS[@]}")"
+ echo "url=$URL" >> "$GITHUB_OUTPUT"
- URL=$(jq -r '.url // empty' deploy.json)
- if [ -z "$URL" ] || [ "$URL" = "null" ]; then
- URL=$(jq -r '.aliases[0] // empty' deploy.json)
- fi
- ID=$(jq -r '.id // .uid // empty' deploy.json)
-
- echo "url=$URL" >> $GITHUB_OUTPUT
- echo "id=$ID" >> $GITHUB_OUTPUT
+ HOST="${URL#https://}"
+ ID="$(curl -fsSL -H "Authorization: Bearer $VC_TOKEN" \
+ "https://api.vercel.com/v13/deployments/$HOST" | jq -r '.id // empty')"
+ echo "id=$ID" >> "$GITHUB_OUTPUT"
- name: Summary (URL + ID)
run: |
From e6c2979be4eb91a1a7449dd2bd9d64243fde94a4 Mon Sep 17 00:00:00 2001
From: Yevhen <38507392+JenyaL@users.noreply.github.com>
Date: Wed, 8 Oct 2025 20:28:00 -0600
Subject: [PATCH 10/20] ci: add vercel prebuild step for true prebuilt
deployment flow
---
.github/workflows/ci-deploy.yml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/.github/workflows/ci-deploy.yml b/.github/workflows/ci-deploy.yml
index 90d6221..cc1983b 100644
--- a/.github/workflows/ci-deploy.yml
+++ b/.github/workflows/ci-deploy.yml
@@ -63,6 +63,10 @@ jobs:
echo "target=production" >> $GITHUB_OUTPUT
fi
+ - name: Prebuild for Vercel
+ run: |
+ vercel build --token "${{ env.VERCEL_TOKEN }}" --scope "${{ env.VERCEL_ORG }}"
+
- name: Deploy to Vercel (from GitHub)
id: deploy
env:
From b3a7478a61bab224974d76bfed5ad5bdd49cbdec Mon Sep 17 00:00:00 2001
From: Yevhen <38507392+JenyaL@users.noreply.github.com>
Date: Wed, 8 Oct 2025 20:31:35 -0600
Subject: [PATCH 11/20] ci: add vercel pull step before build to fetch project
settings
---
.github/workflows/ci-deploy.yml | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/ci-deploy.yml b/.github/workflows/ci-deploy.yml
index cc1983b..f808a0d 100644
--- a/.github/workflows/ci-deploy.yml
+++ b/.github/workflows/ci-deploy.yml
@@ -63,9 +63,18 @@ jobs:
echo "target=production" >> $GITHUB_OUTPUT
fi
+ - name: Pull Vercel Project Settings
+ run: |
+ vercel pull --yes \
+ --environment "${{ steps.tgt.outputs.target }}" \
+ --token "${{ env.VERCEL_TOKEN }}" \
+ --scope "${{ env.VERCEL_ORG }}"
+
- name: Prebuild for Vercel
run: |
- vercel build --token "${{ env.VERCEL_TOKEN }}" --scope "${{ env.VERCEL_ORG }}"
+ vercel build \
+ --token "${{ env.VERCEL_TOKEN }}" \
+ --scope "${{ env.VERCEL_ORG }}"
- name: Deploy to Vercel (from GitHub)
id: deploy
From d19a146b2f9b89b3dc69c21c18c0089d5c563c84 Mon Sep 17 00:00:00 2001
From: Yevhen <38507392+JenyaL@users.noreply.github.com>
Date: Wed, 8 Oct 2025 20:42:54 -0600
Subject: [PATCH 12/20] ci: split build and deploy stages with tests before
prebuilt Vercel deploy
---
.github/workflows/ci-deploy.yml | 77 +++++++++++++++++----------------
1 file changed, 39 insertions(+), 38 deletions(-)
diff --git a/.github/workflows/ci-deploy.yml b/.github/workflows/ci-deploy.yml
index f808a0d..4037cda 100644
--- a/.github/workflows/ci-deploy.yml
+++ b/.github/workflows/ci-deploy.yml
@@ -1,4 +1,4 @@
-name: CI + Deploy (POC)
+name: CI + Deploy (prebuilt)
on:
push:
@@ -14,28 +14,46 @@ env:
VERCEL_ORG: ${{ secrets.VERCEL_ORG_ID }}
jobs:
- build:
+ build_and_test:
runs-on: ubuntu-latest
+ outputs:
+ is_pr: ${{ github.event_name == 'pull_request' }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- cache: npm
+ cache: 'npm'
- name: Install
run: |
- if [ -f package-lock.json ]; then npm ci; else npm install; fi
+ npm ci || npm install
- - name: Build
+ # Линтер (если есть "lint" в package.json — запустится, иначе шаг пропустится)
+ - name: Lint
+ run: |
+ if npm run | grep -q " lint"; then npm run lint; else echo "No lint script"; fi
+
+ # Юнит-тесты (если есть "test" — запустится, иначе пропуск)
+ - name: Unit tests
+ run: |
+ if npm run | grep -q " test"; then npm test --ci --passWithNoTests=false; else echo "No test script"; fi
+
+ # E2E (опционально; с флагом пропуска, если нет сценария)
+ - name: E2E tests (optional)
+ run: |
+ if npm run | grep -q " e2e"; then npm run e2e; else echo "No e2e script"; fi
+
+ # Локальный билд проекта (ваш обычный билдер: Next/.vite/webpack и т.д.)
+ - name: App build
run: npm run build
- - name: Upload build summary
- run: echo "Build ✅" >> "$GITHUB_STEP_SUMMARY"
+ - name: Build summary
+ run: echo "Build & tests passed ✅" >> "$GITHUB_STEP_SUMMARY"
deploy:
- needs: build
+ needs: build_and_test
if: ${{ success() }}
runs-on: ubuntu-latest
steps:
@@ -44,16 +62,12 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 18
-
- - name: Ensure jq
- run: |
- if ! command -v jq >/dev/null 2>&1; then
- sudo apt-get update && sudo apt-get install -y jq
- fi
+ cache: 'npm'
- name: Install Vercel CLI
run: npm i -g vercel@latest
+ # Определяем окружение деплоя: preview для PR, production для пушей в main
- name: Decide target
id: tgt
run: |
@@ -63,53 +77,40 @@ jobs:
echo "target=production" >> $GITHUB_OUTPUT
fi
- - name: Pull Vercel Project Settings
+ # Тянем настройки проекта (создаёт .vercel/project.json)
+ - name: Pull Vercel project settings
run: |
vercel pull --yes \
--environment "${{ steps.tgt.outputs.target }}" \
--token "${{ env.VERCEL_TOKEN }}" \
--scope "${{ env.VERCEL_ORG }}"
- - name: Prebuild for Vercel
+ # Собираем prebuilt артефакт в .vercel/output
+ - name: Vercel prebuild
run: |
vercel build \
--token "${{ env.VERCEL_TOKEN }}" \
--scope "${{ env.VERCEL_ORG }}"
- - name: Deploy to Vercel (from GitHub)
+ # Деплой строго из prebuilt (Vercel не билдит)
+ - name: Deploy (prebuilt)
id: deploy
env:
VC_TOKEN: ${{ env.VERCEL_TOKEN }}
VC_TEAM: ${{ env.VERCEL_ORG }}
TARGET: ${{ steps.tgt.outputs.target }}
run: |
- set -euo pipefail
-
+ set -e
ARGS=(deploy --yes --token "$VC_TOKEN" --scope "$VC_TEAM" --prebuilt)
if [ "$TARGET" = "production" ]; then
ARGS+=(--prod)
fi
-
URL="$(vercel "${ARGS[@]}")"
echo "url=$URL" >> "$GITHUB_OUTPUT"
+ echo "Deployed: $URL"
- HOST="${URL#https://}"
- ID="$(curl -fsSL -H "Authorization: Bearer $VC_TOKEN" \
- "https://api.vercel.com/v13/deployments/$HOST" | jq -r '.id // empty')"
- echo "id=$ID" >> "$GITHUB_OUTPUT"
-
- - name: Summary (URL + ID)
+ - name: Summary
run: |
- echo "### Vercel Deployment" >> "$GITHUB_STEP_SUMMARY"
- echo "" >> "$GITHUB_STEP_SUMMARY"
+ echo "### Deployment" >> "$GITHUB_STEP_SUMMARY"
echo "- Target: **${{ steps.tgt.outputs.target }}**" >> "$GITHUB_STEP_SUMMARY"
- if [ -n "${{ steps.deploy.outputs.url }}" ]; then
- echo "- URL: ${{ steps.deploy.outputs.url }}" >> "$GITHUB_STEP_SUMMARY"
- else
- echo "- URL: (not detected)" >> "$GITHUB_STEP_SUMMARY"
- fi
- if [ -n "${{ steps.deploy.outputs.id }}" ]; then
- echo "- Deployment ID: \`${{ steps.deploy.outputs.id }}\`" >> "$GITHUB_STEP_SUMMARY"
- else
- echo "- Deployment ID: (not detected)" >> "$GITHUB_STEP_SUMMARY"
- fi
\ No newline at end of file
+ echo "- URL: ${{ steps.deploy.outputs.url }}" >> "$GITHUB_STEP_SUMMARY"
\ No newline at end of file
From 9003c1ef4f70e4a74b5909842ef52131a30585bf Mon Sep 17 00:00:00 2001
From: Yevhen <38507392+JenyaL@users.noreply.github.com>
Date: Thu, 9 Oct 2025 18:06:18 -0600
Subject: [PATCH 13/20] ci: deleted comments
---
.github/workflows/ci-deploy.yml | 8 --------
1 file changed, 8 deletions(-)
diff --git a/.github/workflows/ci-deploy.yml b/.github/workflows/ci-deploy.yml
index 4037cda..ccd034a 100644
--- a/.github/workflows/ci-deploy.yml
+++ b/.github/workflows/ci-deploy.yml
@@ -30,22 +30,18 @@ jobs:
run: |
npm ci || npm install
- # Линтер (если есть "lint" в package.json — запустится, иначе шаг пропустится)
- name: Lint
run: |
if npm run | grep -q " lint"; then npm run lint; else echo "No lint script"; fi
- # Юнит-тесты (если есть "test" — запустится, иначе пропуск)
- name: Unit tests
run: |
if npm run | grep -q " test"; then npm test --ci --passWithNoTests=false; else echo "No test script"; fi
- # E2E (опционально; с флагом пропуска, если нет сценария)
- name: E2E tests (optional)
run: |
if npm run | grep -q " e2e"; then npm run e2e; else echo "No e2e script"; fi
- # Локальный билд проекта (ваш обычный билдер: Next/.vite/webpack и т.д.)
- name: App build
run: npm run build
@@ -67,7 +63,6 @@ jobs:
- name: Install Vercel CLI
run: npm i -g vercel@latest
- # Определяем окружение деплоя: preview для PR, production для пушей в main
- name: Decide target
id: tgt
run: |
@@ -77,7 +72,6 @@ jobs:
echo "target=production" >> $GITHUB_OUTPUT
fi
- # Тянем настройки проекта (создаёт .vercel/project.json)
- name: Pull Vercel project settings
run: |
vercel pull --yes \
@@ -85,14 +79,12 @@ jobs:
--token "${{ env.VERCEL_TOKEN }}" \
--scope "${{ env.VERCEL_ORG }}"
- # Собираем prebuilt артефакт в .vercel/output
- name: Vercel prebuild
run: |
vercel build \
--token "${{ env.VERCEL_TOKEN }}" \
--scope "${{ env.VERCEL_ORG }}"
- # Деплой строго из prebuilt (Vercel не билдит)
- name: Deploy (prebuilt)
id: deploy
env:
From bcd14faca29501b43fc99b52c8cac3e8122d3d34 Mon Sep 17 00:00:00 2001
From: Yevhen <38507392+JenyaL@users.noreply.github.com>
Date: Thu, 9 Oct 2025 18:09:28 -0600
Subject: [PATCH 14/20] ci: Added version number into index.html
---
public/index.html | 1 +
1 file changed, 1 insertion(+)
diff --git a/public/index.html b/public/index.html
index 9c9eb81..92d7b49 100644
--- a/public/index.html
+++ b/public/index.html
@@ -6,5 +6,6 @@
Hello from build-test
+1.0 v