Build #551
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
| name: Build | |
| on: | |
| push: | |
| branches: | |
| - master | |
| pull_request: | |
| schedule: | |
| - cron: "17 5 * * *" # Runs at 05:17 UTC | |
| - cron: "17 17 * * *" # Runs at 17:17 UTC | |
| workflow_dispatch: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| build: | |
| name: Build | |
| runs-on: ubuntu-24.04-arm | |
| steps: | |
| - uses: smorimoto/tune-github-hosted-runner-network@v1 | |
| # https://github.com/actions/runner-images/issues/1187 | |
| - uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| - name: Grab Building Folder | |
| id: ramdisk | |
| run: | | |
| echo "build_dir=previous-build-${{ github.run_id }}-${{ github.run_number }}" >> $GITHUB_OUTPUT | |
| - name: Get current date | |
| id: date | |
| run: | | |
| echo "date=$(date +'%Y-%m-%d %H:%M:%S')" >> $GITHUB_OUTPUT | |
| echo "year=$(date +'%Y')" >> $GITHUB_OUTPUT | |
| echo "month=$(date +'%m')" >> $GITHUB_OUTPUT | |
| echo "day=$(date +'%d')" >> $GITHUB_OUTPUT | |
| echo "hour=$(date +'%H')" >> $GITHUB_OUTPUT | |
| echo "minute=$(date +'%M')" >> $GITHUB_OUTPUT | |
| echo "second=$(date +'%S')" >> $GITHUB_OUTPUT | |
| # Download previous build and restore cache.db in the background, | |
| # while the Node.js toolchain is set up in the foreground | |
| - name: Download Previous Build | |
| id: download_previous_build | |
| uses: actions/checkout@v6 | |
| background: true | |
| with: | |
| repository: SukkaLab/ruleset.skk.moe | |
| # during a race condition the dist repo may be private, use token to clone | |
| token: ${{ secrets.GIT_TOKEN }} | |
| persist-credentials: false | |
| filter: "tree:0" # we don't care about git history here | |
| fetch-tags: false | |
| path: ${{ steps.ramdisk.outputs.build_dir }} | |
| - name: Restore cache.db | |
| uses: actions/cache/restore@v6 | |
| id: cache-db-restore | |
| background: true | |
| with: | |
| path: | | |
| .cache | |
| key: ${{ runner.os }}-v3-${{ steps.date.outputs.year }}-${{ steps.date.outputs.month }}-${{ steps.date.outputs.day }} ${{ steps.date.outputs.hour }}:${{ steps.date.outputs.minute }}:${{ steps.date.outputs.second }} | |
| # If source files changed but packages didn't, rebuild from a prior cache. | |
| restore-keys: | | |
| ${{ runner.os }}-v3-${{ steps.date.outputs.year }}-${{ steps.date.outputs.month }}-${{ steps.date.outputs.day }} ${{ steps.date.outputs.hour }}:${{ steps.date.outputs.minute }}: | |
| ${{ runner.os }}-v3-${{ steps.date.outputs.year }}-${{ steps.date.outputs.month }}-${{ steps.date.outputs.day }} ${{ steps.date.outputs.hour }}: | |
| ${{ runner.os }}-v3-${{ steps.date.outputs.year }}-${{ steps.date.outputs.month }}-${{ steps.date.outputs.day }} | |
| ${{ runner.os }}-v3-${{ steps.date.outputs.year }}-${{ steps.date.outputs.month }}- | |
| ${{ runner.os }}-v3-${{ steps.date.outputs.year }}- | |
| ${{ runner.os }}-v3- | |
| # Set up pnpm and Node.js in parallel; both must finish before pnpm runs. | |
| - name: Set up pnpm | |
| id: setup_pnpm | |
| uses: pnpm/action-setup@v6 | |
| background: true | |
| with: | |
| run_install: false | |
| cache: true | |
| - name: Set up Node.js | |
| id: setup_node | |
| uses: actions/setup-node@v6 | |
| background: true | |
| with: | |
| node-version-file: ".node-version" | |
| package-manager-cache: false | |
| - wait: [setup_pnpm, setup_node] | |
| - run: pnpm config set --location=global minimumReleaseAge 0 | |
| - run: pnpm install | |
| # Foreground pnpm + Node.js setup complete, ensure previous build and cache.db are ready | |
| - wait: [download_previous_build, cache-db-restore] | |
| # Double check build folder before starting build | |
| - name: Setup build folder | |
| run: | | |
| if [ ! -d ${{ steps.ramdisk.outputs.build_dir }}/.git ]; then | |
| echo ".git not found" | |
| exit 1 | |
| fi | |
| rm -rf "${{ steps.ramdisk.outputs.build_dir }}/.git" | |
| if [ ! -d ${{ steps.ramdisk.outputs.build_dir }}/List ]; then | |
| echo "List not found" | |
| exit 1 | |
| fi | |
| echo "public directory is ready: ${{ steps.ramdisk.outputs.build_dir }}" | |
| - run: pnpm run build | |
| env: | |
| PUBLIC_DIR: ${{ steps.ramdisk.outputs.build_dir }} | |
| - name: Pre-deploy check | |
| # If the public directory doesn't exist, the build should fail. | |
| # If the public directory is empty, the build should fail. | |
| run: | | |
| if [ ! -d ${{ steps.ramdisk.outputs.build_dir }} ]; then | |
| echo "public directory not found" | |
| exit 1 | |
| fi | |
| if [ ! "$(ls -A ${{ steps.ramdisk.outputs.build_dir }})" ]; then | |
| echo "public directory is empty" | |
| exit 1 | |
| fi | |
| if [ ! -f .BUILD_FINISHED ]; then | |
| echo ".BUILD_FINISHED not found" | |
| exit 1 | |
| fi | |
| echo "public directory is ready: ${{ steps.ramdisk.outputs.build_dir }}" | |
| # Upload artifact and save cache.db in parallel | |
| - uses: actions/upload-artifact@v7 | |
| id: upload_build_artifact | |
| background: true | |
| with: | |
| name: build-artifact-${{ github.sha }}-${{ github.run_number }} | |
| path: ${{ steps.ramdisk.outputs.build_dir }} | |
| if-no-files-found: error | |
| retention-days: 1 | |
| compression-level: 4 | |
| include-hidden-files: false | |
| - name: Cache cache.db | |
| if: always() | |
| uses: actions/cache/save@v6 | |
| with: | |
| path: | | |
| .cache | |
| key: ${{ runner.os }}-v3-${{ steps.date.outputs.year }}-${{ steps.date.outputs.month }}-${{ steps.date.outputs.day }} ${{ steps.date.outputs.hour }}:${{ steps.date.outputs.minute }}:${{ steps.date.outputs.second }} | |
| - wait: upload_build_artifact | |
| diff_deployment_on_pr: | |
| if: github.ref != 'refs/heads/master' | |
| needs: | |
| - build | |
| name: Diff output | |
| runs-on: ubuntu-slim | |
| steps: | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| name: build-artifact-${{ github.sha }}-${{ github.run_number }} | |
| path: public | |
| - name: Diff | |
| run: | | |
| git clone --filter=tree:0 --no-tags https://${GH_USER}:${GH_TOKEN}@github.com/SukkaLab/ruleset.skk.moe.git ./deploy-git >/dev/null 2>&1 | |
| cd ./deploy-git | |
| git fetch origin master >/dev/null 2>&1 | |
| rm -rf ./* | |
| cp -rf ../public/* ./ | |
| git --no-pager diff --minimal | |
| env: | |
| GH_USER: ${{ secrets.GIT_USER }} | |
| GH_TOKEN: ${{ secrets.GIT_TOKEN }} | |
| deploy: | |
| needs: | |
| - build | |
| name: Deploy | |
| if: github.ref == 'refs/heads/master' | |
| runs-on: ubuntu-slim | |
| env: | |
| WRANGLER_VERSION: "3.114.17" | |
| steps: | |
| - name: Get NPM cache directory path | |
| id: npm_cache_path | |
| shell: sh | |
| run: echo dir=$(npm config get cache) >> $GITHUB_OUTPUT | |
| - uses: actions/cache@v6 | |
| with: | |
| path: | | |
| ${{ steps.npm_cache_path.outputs.dir }} | |
| node_modules | |
| key: deploy-to-cloudflare-npm-${{ runner.os }}-${{ runner.arch }}-wrangler-${{ env.WRANGLER_VERSION }} | |
| restore-keys: | | |
| deploy-to-cloudflare-npm-${{ runner.os }}-${{ runner.arch }}-wrangler- | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| name: build-artifact-${{ github.sha }}-${{ github.run_number }} | |
| path: public | |
| - name: Prepare GitHub Pages deployment marker | |
| id: deploy_marker | |
| run: | | |
| echo "marker_content=${GITHUB_SHA}-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" >> $GITHUB_OUTPUT | |
| - name: Remove build artifact | |
| uses: geekyeggo/delete-artifact@v6 | |
| background: true | |
| with: | |
| name: build-artifact-${{ github.sha }}-${{ github.run_number }} | |
| # Deploy to Cloudflare, GitLab, and GitHub in parallel | |
| - name: Deploy to Cloudflare Pages | |
| id: deploy_cloudflare | |
| uses: cloudflare/wrangler-action@v3 | |
| background: true | |
| with: | |
| apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| command: pages deploy public --project-name=sukkaw-ruleset --commit-dirty=true --branch=main | |
| wranglerVersion: ${{ env.WRANGLER_VERSION }} | |
| - name: Upload Dist to GitLab | |
| id: deploy_gitlab | |
| background: true | |
| run: | | |
| git clone --filter=tree:0 --no-tags https://${GITLAB_TOKEN_NAME}:${GITLAB_TOKEN}@gitlab.com/SukkaW/ruleset.skk.moe.git ./deploy-git-gitlab | |
| cd ./deploy-git-gitlab | |
| git config push.default matching | |
| git config user.email "${GITLAB_EMAIL}" | |
| git config user.name "${GITLAB_USER}" | |
| rm -rf ./* | |
| cp -rf ../public/* ./ | |
| git add --all . | |
| git commit -m "deploy: https://github.com/SukkaW/Surge/commit/${GITHUB_SHA}" | |
| git push --quiet --force origin HEAD:master | |
| cd .. | |
| rm -rf ./deploy-git-gitlab | |
| env: | |
| GITLAB_EMAIL: ${{ secrets.GITLAB_EMAIL }} | |
| GITLAB_USER: ${{ secrets.GITLAB_USER }} | |
| GITLAB_TOKEN_NAME: ${{ secrets.GITLAB_TOKEN_NAME }} | |
| GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }} | |
| - name: Upload Dist to GitHub | |
| id: upload_dist_to_github | |
| background: true | |
| run: | | |
| gh repo unarchive SukkaLab/ruleset.skk.moe --yes | |
| git clone --filter=tree:0 --no-tags https://${GH_USER}:${GH_TOKEN}@github.com/SukkaLab/ruleset.skk.moe.git ./deploy-git-github | |
| cd ./deploy-git-github | |
| git config push.default matching | |
| git config user.email "${GH_EMAIL}" | |
| git config user.name "${GH_USER}" | |
| rm -rf ./* | |
| cp -rf ../public/* ./ | |
| echo "ruleset-mirror.skk.moe" > CNAME | |
| echo "${{ steps.deploy_marker.outputs.marker_content }}" > deploy-check.txt | |
| git add --all . | |
| git commit -m "deploy: https://github.com/SukkaW/Surge/commit/${GITHUB_SHA}" | |
| git push --quiet --force origin HEAD:master | |
| cd .. | |
| rm -rf ./deploy-git-github | |
| env: | |
| GH_EMAIL: ${{ secrets.GIT_EMAIL }} | |
| GH_USER: ${{ secrets.GIT_USER }} | |
| GH_TOKEN: ${{ secrets.GIT_TOKEN }} | |
| - wait: upload_dist_to_github | |
| - name: Wait for GitHub Pages to serve deployed marker | |
| if: steps.upload_dist_to_github.outcome == 'success' | |
| id: wait_for_github_pages | |
| run: | | |
| expected="${{ steps.deploy_marker.outputs.marker_content }}" | |
| marker_url="https://ruleset-mirror.skk.moe/deploy-check.txt" | |
| for _ in $(seq 1 30); do | |
| content=$(curl --fail --silent --show-error --location "${marker_url}" || true) | |
| if [ "${content}" = "${expected}" ]; then | |
| echo "GitHub Pages is serving the correct deploy marker" | |
| exit 0 | |
| fi | |
| sleep 10 | |
| done | |
| echo "Timed out waiting for GitHub Pages deployment" | |
| exit 1 | |
| - name: Archive dist repo | |
| if: steps.wait_for_github_pages.outcome == 'success' | |
| run: | | |
| gh repo archive SukkaLab/ruleset.skk.moe --yes | |
| env: | |
| GH_TOKEN: ${{ secrets.GIT_TOKEN }} | |
| - wait-all: |