From 8989bee80b17785c8b1ccbb93886715a5110508b Mon Sep 17 00:00:00 2001 From: manNomi Date: Mon, 16 Feb 2026 18:46:54 +0900 Subject: [PATCH 1/3] =?UTF-8?q?chore:=20main-stage/release-prod=20?= =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=EC=9B=8C=ED=81=AC=ED=94=8C=EB=A1=9C?= =?UTF-8?q?=EC=9A=B0=EB=A5=BC=20web/admin=20=EA=B3=B5=ED=86=B5=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-stage.yml | 4 +- .github/workflows/release.yml | 107 ++++++++++++++++++----------- 2 files changed, 70 insertions(+), 41 deletions(-) diff --git a/.github/workflows/deploy-stage.yml b/.github/workflows/deploy-stage.yml index 4ca30ba1..5e0c4021 100644 --- a/.github/workflows/deploy-stage.yml +++ b/.github/workflows/deploy-stage.yml @@ -42,7 +42,7 @@ jobs: if: needs.detect-changes.outputs.web == 'true' || needs.detect-changes.outputs.shared == 'true' || needs.detect-changes.outputs.root == 'true' env: VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} - VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID_WEB_STAGE }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID_WEB }} TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_TEAM: ${{ secrets.TURBO_TEAM }} steps: @@ -130,7 +130,7 @@ jobs: if: needs.detect-changes.outputs.admin == 'true' || needs.detect-changes.outputs.shared == 'true' || needs.detect-changes.outputs.root == 'true' env: VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} - VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID_ADMIN_STAGE }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID_ADMIN }} TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_TEAM: ${{ secrets.TURBO_TEAM }} steps: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 295a78b7..eeda3ad2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,10 +1,20 @@ -name: Promote Main to Release +name: Promote Main to Release Branches permissions: contents: write on: workflow_dispatch: + inputs: + target: + description: "Promote target" + required: true + default: "both" + type: choice + options: + - both + - web + - admin jobs: generate_tag: @@ -30,7 +40,7 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} promote_release_branch: - name: Promote main -> release + name: Promote main -> release branch(es) runs-on: ubuntu-latest needs: create_release steps: @@ -39,53 +49,72 @@ jobs: with: fetch-depth: 0 - - name: Promote main branch to release branch + - name: Promote main branch to selected release branch(es) run: | set -euo pipefail git fetch origin main - git fetch origin release || true - MAIN_SHA=$(git rev-parse origin/main) - if git show-ref --verify --quiet refs/remotes/origin/release; then - RELEASE_SHA=$(git rev-parse origin/release) - else - RELEASE_SHA="" - fi + TARGET="${{ github.event.inputs.target }}" - if [ -z "$RELEASE_SHA" ]; then - git push origin origin/main:refs/heads/release - { - echo "## Release Promotion" - echo "- Status: success (release branch created)" - echo "- Promoted main SHA: $MAIN_SHA" - echo "- Target branch: release" - echo "- Note: Vercel production deploy is triggered by release branch update" - } >> "$GITHUB_STEP_SUMMARY" - exit 0 - fi + case "$TARGET" in + web) + RELEASE_BRANCHES="release-web" + ;; + admin) + RELEASE_BRANCHES="release-admin" + ;; + both) + RELEASE_BRANCHES="release-web release-admin" + ;; + *) + echo "Unsupported target: $TARGET" >&2 + exit 1 + ;; + esac - if [ "$MAIN_SHA" = "$RELEASE_SHA" ]; then - { - echo "## Release Promotion" - echo "- Status: skipped (release is already up to date)" - echo "- main: $MAIN_SHA" - } >> "$GITHUB_STEP_SUMMARY" - exit 0 - fi + { + echo "## Release Promotion" + echo "- Selected target: $TARGET" + echo "- Promoted main SHA: $MAIN_SHA" + } >> "$GITHUB_STEP_SUMMARY" - if ! git merge-base --is-ancestor origin/release origin/main; then - echo "release branch is not an ancestor of main. Resolve release history before promotion." >&2 - exit 1 - fi + for BRANCH in $RELEASE_BRANCHES; do + git fetch origin "$BRANCH" || true - git push origin origin/main:refs/heads/release + if git show-ref --verify --quiet "refs/remotes/origin/$BRANCH"; then + RELEASE_SHA=$(git rev-parse "origin/$BRANCH") + else + RELEASE_SHA="" + fi + + if [ -z "$RELEASE_SHA" ]; then + git push origin origin/main:"refs/heads/$BRANCH" + { + echo "- $BRANCH: created from main" + } >> "$GITHUB_STEP_SUMMARY" + continue + fi + + if [ "$MAIN_SHA" = "$RELEASE_SHA" ]; then + { + echo "- $BRANCH: already up to date" + } >> "$GITHUB_STEP_SUMMARY" + continue + fi + + if ! git merge-base --is-ancestor "origin/$BRANCH" origin/main; then + echo "$BRANCH is not an ancestor of main. Resolve release history before promotion." >&2 + exit 1 + fi + + git push origin origin/main:"refs/heads/$BRANCH" + { + echo "- $BRANCH: updated" + } >> "$GITHUB_STEP_SUMMARY" + done { - echo "## Release Promotion" - echo "- Status: success" - echo "- Promoted main SHA: $MAIN_SHA" - echo "- Target branch: release" - echo "- Note: Vercel production deploy is triggered by release branch update" + echo "- Note: Vercel production deploy is triggered by corresponding release branch update" } >> "$GITHUB_STEP_SUMMARY" From 2e84e45e9cb3180bd5ee696828458e06135d1f24 Mon Sep 17 00:00:00 2001 From: manNomi Date: Mon, 16 Feb 2026 19:38:11 +0900 Subject: [PATCH 2/3] =?UTF-8?q?chore:=20=EB=A9=94=EC=9D=B8=20preview=20?= =?UTF-8?q?=EB=B0=B0=ED=8F=AC=EC=97=90=20=EA=B3=A0=EC=A0=95=20stage=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=20alias=20=EB=8B=A8=EA=B3=84=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-stage.yml | 44 ++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/.github/workflows/deploy-stage.yml b/.github/workflows/deploy-stage.yml index 5e0c4021..a325bfea 100644 --- a/.github/workflows/deploy-stage.yml +++ b/.github/workflows/deploy-stage.yml @@ -43,6 +43,7 @@ jobs: env: VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID_WEB }} + STAGE_ALIAS_DOMAIN: ${{ secrets.VERCEL_STAGE_WEB_ALIAS }} TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_TEAM: ${{ secrets.TURBO_TEAM }} steps: @@ -107,6 +108,27 @@ jobs: } >> "$GITHUB_STEP_SUMMARY" working-directory: apps/web + - name: Bind fixed stage alias (web) + if: steps.deploy_web.outputs.url != '' + run: | + set -euo pipefail + if [ -z "$STAGE_ALIAS_DOMAIN" ]; then + { + echo "## Web Stage Alias" + echo "- Status: skipped" + echo "- Reason: \\`VERCEL_STAGE_WEB_ALIAS\\` secret is not configured" + } >> "$GITHUB_STEP_SUMMARY" + exit 0 + fi + + vercel alias set "${{ steps.deploy_web.outputs.url }}" "$STAGE_ALIAS_DOMAIN" --token=${{ secrets.VERCEL_TOKEN }} 2>&1 | tee .vercel-ci/alias.log + { + echo "## Web Stage Alias" + echo "- Status: success" + echo "- Alias: $STAGE_ALIAS_DOMAIN" + } >> "$GITHUB_STEP_SUMMARY" + working-directory: apps/web + - name: Append failure summary (web stage) if: failure() run: | @@ -131,6 +153,7 @@ jobs: env: VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID_ADMIN }} + STAGE_ALIAS_DOMAIN: ${{ secrets.VERCEL_STAGE_ADMIN_ALIAS }} TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_TEAM: ${{ secrets.TURBO_TEAM }} steps: @@ -195,6 +218,27 @@ jobs: } >> "$GITHUB_STEP_SUMMARY" working-directory: apps/admin + - name: Bind fixed stage alias (admin) + if: steps.deploy_admin.outputs.url != '' + run: | + set -euo pipefail + if [ -z "$STAGE_ALIAS_DOMAIN" ]; then + { + echo "## Admin Stage Alias" + echo "- Status: skipped" + echo "- Reason: \\`VERCEL_STAGE_ADMIN_ALIAS\\` secret is not configured" + } >> "$GITHUB_STEP_SUMMARY" + exit 0 + fi + + vercel alias set "${{ steps.deploy_admin.outputs.url }}" "$STAGE_ALIAS_DOMAIN" --token=${{ secrets.VERCEL_TOKEN }} 2>&1 | tee .vercel-ci/alias.log + { + echo "## Admin Stage Alias" + echo "- Status: success" + echo "- Alias: $STAGE_ALIAS_DOMAIN" + } >> "$GITHUB_STEP_SUMMARY" + working-directory: apps/admin + - name: Append failure summary (admin stage) if: failure() run: | From 11e3ae6631deb2cb238639f0cbcc7f4081fd3bdb Mon Sep 17 00:00:00 2001 From: manNomi Date: Mon, 16 Feb 2026 19:43:44 +0900 Subject: [PATCH 3/3] =?UTF-8?q?chore:=20stage=20=EB=B0=B0=ED=8F=AC=20?= =?UTF-8?q?=EC=9B=8C=ED=81=AC=ED=94=8C=EB=A1=9C=EC=9A=B0=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20=EB=B0=8F=20turbo=20=EC=82=B0=EC=B6=9C=EB=AC=BC=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-stage.yml | 256 ----------------------------- turbo.json | 2 +- 2 files changed, 1 insertion(+), 257 deletions(-) delete mode 100644 .github/workflows/deploy-stage.yml diff --git a/.github/workflows/deploy-stage.yml b/.github/workflows/deploy-stage.yml deleted file mode 100644 index a325bfea..00000000 --- a/.github/workflows/deploy-stage.yml +++ /dev/null @@ -1,256 +0,0 @@ -name: Stage Deployment - -on: - push: - branches: [main] - -jobs: - detect-changes: - name: Detect Changes - runs-on: ubuntu-latest - outputs: - web: ${{ steps.filter.outputs.web }} - admin: ${{ steps.filter.outputs.admin }} - shared: ${{ steps.filter.outputs.shared }} - root: ${{ steps.filter.outputs.root }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Check changed files - uses: dorny/paths-filter@v3 - id: filter - with: - filters: | - web: - - 'apps/web/**' - admin: - - 'apps/admin/**' - shared: - - 'packages/**' - root: - - 'package.json' - - 'pnpm-lock.yaml' - - 'pnpm-workspace.yaml' - - 'turbo.json' - - '.github/workflows/**' - - deploy-web-stage: - name: Deploy Web (Stage) - runs-on: ubuntu-latest - needs: detect-changes - if: needs.detect-changes.outputs.web == 'true' || needs.detect-changes.outputs.shared == 'true' || needs.detect-changes.outputs.root == 'true' - env: - VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} - VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID_WEB }} - STAGE_ALIAS_DOMAIN: ${{ secrets.VERCEL_STAGE_WEB_ALIAS }} - TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} - TURBO_TEAM: ${{ secrets.TURBO_TEAM }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Install pnpm - uses: pnpm/action-setup@v3 - with: - version: 9 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: "22.x" - cache: "pnpm" - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Install Vercel CLI - run: pnpm add --global vercel@latest - - - name: Turbo cached build (web) - run: pnpm turbo run build --filter=@solid-connect/web - - - name: Prepare Vercel project metadata (web) - run: | - rm -rf .vercel - mkdir -p .vercel-ci - working-directory: apps/web - - - name: Pull Vercel environment (web stage) - run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }} 2>&1 | tee .vercel-ci/pull.log - working-directory: apps/web - - - name: Build prebuilt artifacts (web stage) - run: vercel build --yes --target=preview --token=${{ secrets.VERCEL_TOKEN }} 2>&1 | tee .vercel-ci/build.log - working-directory: apps/web - - - name: Verify prebuilt artifacts (web) - run: | - test -f .vercel/output/config.json - test -d .vercel/output/functions - working-directory: apps/web - - - name: Deploy prebuilt artifacts (web stage) - id: deploy_web - run: | - set -euo pipefail - vercel deploy --prebuilt --target=preview --token=${{ secrets.VERCEL_TOKEN }} 2>&1 | tee .vercel-ci/deploy.log - URL=$(grep -Eo 'https://[^ ]+\.vercel\.app' .vercel-ci/deploy.log | head -n 1) - if [ -z "$URL" ]; then - echo "Failed to parse deployment URL from Vercel output" >&2 - exit 1 - fi - echo "url=$URL" >> "$GITHUB_OUTPUT" - { - echo "## Web Stage Deployment" - echo "- Status: success" - echo "- URL: $URL" - } >> "$GITHUB_STEP_SUMMARY" - working-directory: apps/web - - - name: Bind fixed stage alias (web) - if: steps.deploy_web.outputs.url != '' - run: | - set -euo pipefail - if [ -z "$STAGE_ALIAS_DOMAIN" ]; then - { - echo "## Web Stage Alias" - echo "- Status: skipped" - echo "- Reason: \\`VERCEL_STAGE_WEB_ALIAS\\` secret is not configured" - } >> "$GITHUB_STEP_SUMMARY" - exit 0 - fi - - vercel alias set "${{ steps.deploy_web.outputs.url }}" "$STAGE_ALIAS_DOMAIN" --token=${{ secrets.VERCEL_TOKEN }} 2>&1 | tee .vercel-ci/alias.log - { - echo "## Web Stage Alias" - echo "- Status: success" - echo "- Alias: $STAGE_ALIAS_DOMAIN" - } >> "$GITHUB_STEP_SUMMARY" - working-directory: apps/web - - - name: Append failure summary (web stage) - if: failure() - run: | - { - echo "## Web Stage Deployment Failed" - echo "- Inspect artifact: web-stage-vercel-logs" - } >> "$GITHUB_STEP_SUMMARY" - - - name: Upload Vercel logs (web stage) - if: always() - uses: actions/upload-artifact@v4 - with: - name: web-stage-vercel-logs - path: apps/web/.vercel-ci/*.log - if-no-files-found: ignore - - deploy-admin-stage: - name: Deploy Admin (Stage) - runs-on: ubuntu-latest - needs: detect-changes - if: needs.detect-changes.outputs.admin == 'true' || needs.detect-changes.outputs.shared == 'true' || needs.detect-changes.outputs.root == 'true' - env: - VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} - VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID_ADMIN }} - STAGE_ALIAS_DOMAIN: ${{ secrets.VERCEL_STAGE_ADMIN_ALIAS }} - TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} - TURBO_TEAM: ${{ secrets.TURBO_TEAM }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Install pnpm - uses: pnpm/action-setup@v3 - with: - version: 9 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: "22.x" - cache: "pnpm" - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Install Vercel CLI - run: pnpm add --global vercel@latest - - - name: Turbo cached build (admin) - run: pnpm turbo run build --filter=@solid-connect/admin - - - name: Prepare Vercel project metadata (admin) - run: | - rm -rf .vercel - mkdir -p .vercel-ci - working-directory: apps/admin - - - name: Pull Vercel environment (admin stage) - run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }} 2>&1 | tee .vercel-ci/pull.log - working-directory: apps/admin - - - name: Build prebuilt artifacts (admin stage) - run: vercel build --yes --target=preview --token=${{ secrets.VERCEL_TOKEN }} 2>&1 | tee .vercel-ci/build.log - working-directory: apps/admin - - - name: Verify prebuilt artifacts (admin) - run: | - test -f .vercel/output/config.json - test -d .vercel/output/functions - working-directory: apps/admin - - - name: Deploy prebuilt artifacts (admin stage) - id: deploy_admin - run: | - set -euo pipefail - vercel deploy --prebuilt --target=preview --token=${{ secrets.VERCEL_TOKEN }} 2>&1 | tee .vercel-ci/deploy.log - URL=$(grep -Eo 'https://[^ ]+\.vercel\.app' .vercel-ci/deploy.log | head -n 1) - if [ -z "$URL" ]; then - echo "Failed to parse deployment URL from Vercel output" >&2 - exit 1 - fi - echo "url=$URL" >> "$GITHUB_OUTPUT" - { - echo "## Admin Stage Deployment" - echo "- Status: success" - echo "- URL: $URL" - } >> "$GITHUB_STEP_SUMMARY" - working-directory: apps/admin - - - name: Bind fixed stage alias (admin) - if: steps.deploy_admin.outputs.url != '' - run: | - set -euo pipefail - if [ -z "$STAGE_ALIAS_DOMAIN" ]; then - { - echo "## Admin Stage Alias" - echo "- Status: skipped" - echo "- Reason: \\`VERCEL_STAGE_ADMIN_ALIAS\\` secret is not configured" - } >> "$GITHUB_STEP_SUMMARY" - exit 0 - fi - - vercel alias set "${{ steps.deploy_admin.outputs.url }}" "$STAGE_ALIAS_DOMAIN" --token=${{ secrets.VERCEL_TOKEN }} 2>&1 | tee .vercel-ci/alias.log - { - echo "## Admin Stage Alias" - echo "- Status: success" - echo "- Alias: $STAGE_ALIAS_DOMAIN" - } >> "$GITHUB_STEP_SUMMARY" - working-directory: apps/admin - - - name: Append failure summary (admin stage) - if: failure() - run: | - { - echo "## Admin Stage Deployment Failed" - echo "- Inspect artifact: admin-stage-vercel-logs" - } >> "$GITHUB_STEP_SUMMARY" - - - name: Upload Vercel logs (admin stage) - if: always() - uses: actions/upload-artifact@v4 - with: - name: admin-stage-vercel-logs - path: apps/admin/.vercel-ci/*.log - if-no-files-found: ignore diff --git a/turbo.json b/turbo.json index 49171f7d..f2f03b7e 100644 --- a/turbo.json +++ b/turbo.json @@ -4,7 +4,7 @@ "tasks": { "build": { "dependsOn": ["^build"], - "outputs": ["dist/**", ".output/**", ".vercel/output/**"], + "outputs": ["dist/**", ".output/**"], "env": ["NODE_ENV", "NEXT_PUBLIC_*"] }, "@solid-connect/web#build": {