staging
與 production project
(或使用相同專案的不同 hosting site)FIREBASE_TOKEN
(由 firebase login:ci
取得)FIREBASE_PROJECT_ID_STAGING
(staging 專案 id)FIREBASE_PROJECT_ID_PROD
(production 專案 id)產生 FIREBASE_TOKEN:
firebase login:ci → 複製 token → GitHub repo > Settings > Secrets > New repository secret
package.json
{
"scripts": {
"lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'",
"test": "react-scripts test --ci --reporters=default",
"build": "react-scripts build",
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,md}\"",
"deploy:staging": "firebase deploy --only hosting --project $FIREBASE_PROJECT_ID_STAGING",
"deploy:prod": "firebase deploy --only hosting --project $FIREBASE_PROJECT_ID_PROD"
}
}
firebase.json
{
"hosting": {
"public": "build",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{ "source": "**", "destination": "/index.html" }
]
}
}
.github/workflows/pr-preview.yml
name: PR Preview Deploy
on:
pull_request:
types: [opened, synchronize, reopened, closed]
jobs:
preview:
runs-on: ubuntu-latest
if: github.event.pull_request != null
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 18
- name: Install deps
run: npm ci
- name: Lint
run: npm run lint
- name: Run tests
run: npm test
- name: Build
run: npm run build
- name: Deploy to preview channel
env:
FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
run: |
# preview channel name: pr-<number>
CHANNEL="pr-${{ github.event.number }}"
firebase hosting:channel:deploy $CHANNEL --project ${{ secrets.FIREBASE_PROJECT_ID_STAGING }} --expires 7d
- name: Post preview URL comment
if: ${{ github.event.action != 'closed' }}
uses: marocchino/sticky-pull-request-comment@v2
with:
header: preview-url
message: |
Preview deployed: https://$CHANNEL--${{ secrets.FIREBASE_PROJECT_ID_STAGING }}.web.app
.github/workflows/staging.yml
name: Deploy to Staging
on:
push:
branches:
- staging
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 18
- name: Cache node modules
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Run tests
run: npm test
- name: Build
run: npm run build
- name: Deploy to Staging (Firebase Hosting)
env:
FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
FIREBASE_PROJECT_ID_STAGING: ${{ secrets.FIREBASE_PROJECT_ID_STAGING }}
run: |
firebase deploy --only hosting --project $FIREBASE_PROJECT_ID_STAGING
.github/workflows/production.yml
name: Deploy to Production
on:
push:
branches:
- main
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 18
- name: Cache node modules
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Run tests
run: npm test
- name: Build
run: npm run build
- name: Deploy to Production
env:
FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
FIREBASE_PROJECT_ID_PROD: ${{ secrets.FIREBASE_PROJECT_ID_PROD }}
run: |
firebase deploy --only hosting --project $FIREBASE_PROJECT_ID_PROD