diff --git a/.github/workflows/build-memory.yml b/.github/workflows/build-memory.yml new file mode 100644 index 0000000000..23a8ade02c --- /dev/null +++ b/.github/workflows/build-memory.yml @@ -0,0 +1,129 @@ +# Measures how much memory a full production build needs and compares it +# against the capacity of the Vercel build machine the docs are running on. +# +# Run this before a release. Adding a documentation version is what moves the +# number, so this answers "do we need a bigger build machine before we ship?" +# while there is still time to change it. +# +# Preview deployments only build English, so they never exercise the Japanese +# locale and cannot catch this. This workflow builds both. + +name: 'Docs Build Memory Check' + +on: + workflow_dispatch: + inputs: + machine: + description: 'Which Vercel build machine type is ionic-docs on? (Project Settings > Build and Deployment > Build Machine)' + required: true + type: choice + default: Standard + options: + - Standard + - Enhanced + - Turbo + - Elastic + +permissions: + contents: read + +jobs: + measure: + name: πŸ“ Measure Production Build Memory + # Must be Linux. `/usr/bin/time -v` is GNU specific, and Vercel builds on + # Linux, so the numbers are comparable. + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: βš™οΈ Use Node.js 20 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 20 + + - name: πŸ•ΈοΈ Install Dependencies + run: npm ci --legacy-peer-deps + + - name: πŸ—οΈ Build All Locales + env: + # `npm run build` resolves to build:${VERCEL_ENV:-preview}, and + # build:preview is English only. Without this the job would measure + # the half that already fits and would never catch the problem. + VERCEL_ENV: production + # scripts/release-notes.mjs exits non-zero in CI without a token. + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # prebuild runs `crowdin upload` when this is non-empty, which pushes + # source strings to Crowdin. This job only measures memory and must + # never publish, so it is pinned empty rather than relying on the + # secret not being exposed to this workflow. + CROWDIN_PERSONAL_TOKEN: '' + run: /usr/bin/time -v -o build-memory.log npm run build + shell: bash + + - name: πŸ“Š Report Against Machine Capacity + env: + MACHINE: ${{ inputs.machine }} + run: | + set -euo pipefail + + PEAK_KB=$(awk '/Maximum resident set size/ { print $NF }' build-memory.log) + if [ -z "$PEAK_KB" ]; then + echo "::error::Could not read peak memory from build-memory.log" + exit 1 + fi + PEAK_GB=$(awk -v kb="$PEAK_KB" 'BEGIN { printf "%.2f", kb / 1048576 }') + + # A build must stay well below a machine's advertised size, for two + # reasons. Vercel's own processes need part of that memory, so the + # nominal figure is not all available: the build that failed during + # the v9 release measured about 8.2 GB and was killed on a nominally + # 8 GB machine. And identical builds vary between runs, measured at + # 7.86 GB and 8.42 GB on the same input. + # + # This margin is a judgment call, not a number published by Vercel. + # It only becomes load bearing when the build comes within a couple + # of GB of a tier boundary. + THRESHOLD_PCT=75 + + # Smallest fixed tier this build fits on under the threshold. Falls + # back to Turbo, the largest, when nothing fits. + RECOMMENDED=$(awk -v peak="$PEAK_GB" -v t="$THRESHOLD_PCT" 'BEGIN { + split("Standard:8 Enhanced:16 Turbo:60", tiers, " ") + for (i = 1; i <= 3; i++) { + split(tiers[i], tier, ":") + if (peak / tier[2] * 100 <= t) { print tier[1]; exit } + } + print "Turbo" + }') + + # Elastic has no fixed capacity to check against. It sizes each build + # from recent successful builds, so when earlier builds were killed + # before recording what they needed it can assign a machine smaller + # than the build requires. Report the size, but make no pass or fail + # claim. + if [ "$MACHINE" = "Elastic" ]; then + echo "::warning title=Check the machine Elastic will assign::Elastic sizes each build from recent successful builds, so there is no fixed capacity to check against. Confirm what it will assign under Project Settings > Build and Deployment > Build Machine, where it names the machine your next deployment will use. Otherwise switch to ${RECOMMENDED}, which this ${PEAK_GB} GB build fits on." + exit 0 + fi + + case "$MACHINE" in + Standard) CAPACITY=8 ;; + Enhanced) CAPACITY=16 ;; + Turbo) CAPACITY=60 ;; + *) + echo "::error::Unknown machine type: $MACHINE" + exit 1 + ;; + esac + + if awk -v cap="$CAPACITY" -v peak="$PEAK_GB" -v t="$THRESHOLD_PCT" 'BEGIN { + exit (peak / cap * 100 > t) ? 1 : 0 + }'; then + echo "::notice title=Success! No action needed::Build peaks at ${PEAK_GB} GB, so it should have no issues on the ${MACHINE} machine." + exit 0 + fi + + echo "::error title=Move to ${RECOMMENDED} before releasing::Build peaks at ${PEAK_GB} GB, too close to the ${CAPACITY} GB ceiling of the ${MACHINE} machine. Vercel needs part of that memory for its own processes, so the build cannot use all of it." + exit 1 + shell: bash