From a7a64aab6bc368e0a709bb9ab035d7394d1ad370 Mon Sep 17 00:00:00 2001 From: Max Ostapenko <1611259+max-ostapenko@users.noreply.github.com> Date: Wed, 11 Mar 2026 23:07:47 +0100 Subject: [PATCH 1/5] Add cloudbuild.yaml for Google Cloud Build configuration --- cloudbuild.yaml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 cloudbuild.yaml diff --git a/cloudbuild.yaml b/cloudbuild.yaml new file mode 100644 index 00000000000..6ccdf5edc7e --- /dev/null +++ b/cloudbuild.yaml @@ -0,0 +1,5 @@ +steps: +- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk' + entrypoint: 'bash' + args: ['-c', 'gcloud config set app/cloud_build_timeout 1600 && gcloud app deploy'] +timeout: '1600s' From bc289a9b3923c08b20727beaff853fefd3bcfa79 Mon Sep 17 00:00:00 2001 From: Max Ostapenko <1611259+max-ostapenko@users.noreply.github.com> Date: Thu, 12 Mar 2026 00:00:14 +0100 Subject: [PATCH 2/5] Enhance Cloud Build pipeline with detailed steps for installation, testing, and deployment --- .github/workflows/linter.yml | 3 - .github/workflows/predeploy.yml | 94 ------------------ .github/workflows/test_website.yml | 3 - cloudbuild.yaml | 151 ++++++++++++++++++++++++++++- 4 files changed, 147 insertions(+), 104 deletions(-) delete mode 100644 .github/workflows/predeploy.yml diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index bcaabecefdb..3b699424918 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -10,9 +10,6 @@ name: Lint Code Base on: workflow_dispatch: pull_request: - push: - branches: - - main jobs: lint: name: Lint Code Base diff --git a/.github/workflows/predeploy.yml b/.github/workflows/predeploy.yml deleted file mode 100644 index 8f630b792f0..00000000000 --- a/.github/workflows/predeploy.yml +++ /dev/null @@ -1,94 +0,0 @@ -###################################### -## Custom Web Almanac GitHub action ## -###################################### -# -# A pre-deploy script that must be run in GitHub Actions to: -# - Update timestamps and hashes -# - Generate the ebooks -# - Upload ebooks as artifacts -# - Submit a Pull Request with the changes -# -# This should be run before every release -# -name: Predeploy script - -env: - # Update periodically from https://www.princexml.com/latest/ - PRINCE_PACKAGE: 'prince_16.1-1_ubuntu24.04_amd64.deb' - -on: - workflow_dispatch: - inputs: - ebooks: - type: boolean - description: "Regenerate Ebooks?" - required: true - default: "true" - -jobs: - build: - name: Update Timestamps and Generate Ebooks - runs-on: ubuntu-latest - steps: - - name: Checkout branch - uses: actions/checkout@v6 - - name: Setup Node.js for use with actions - uses: actions/setup-node@v6 - with: - node-version: '20' - - name: Set up Python 3.12 - uses: actions/setup-python@v6 - with: - python-version: '3.12' - - name: Install Asian Fonts - if: ${{ github.event.inputs.ebooks == 'true' }} - run: | - # Install Japanese san-serif font - sudo apt-get install -y fonts-ipafont-gothic - # Install Chinese san-serif font - sudo apt-get install -y fonts-arphic-uming - # Install Korean san-serif font - sudo apt-get install -y fonts-unfonts-core - - name: Install PrinceXML - if: ${{ github.event.inputs.ebooks == 'true' }} - run: | - wget https://www.princexml.com/download/${{ env.PRINCE_PACKAGE }} --directory-prefix=/tmp - DEBIAN_FRONTEND=noninteractive sudo apt install -y /tmp/${{ env.PRINCE_PACKAGE }} - - name: Install pdftk - if: ${{ github.event.inputs.ebooks == 'true' }} - run: sudo apt install pdftk - - name: Run the website - run: ./src/tools/scripts/run_and_test_website.sh - - name: Generating Ebooks - if: ${{ github.event.inputs.ebooks == 'true' }} - run: | - cd src - mkdir static/pdfs/ - npm run ebooks - - name: Upload PDF artifact - if: ${{ github.event.inputs.ebooks == 'true' }} - uses: actions/upload-artifact@v7 - with: - name: pdfs - path: ./src/static/pdfs/*.pdf - if-no-files-found: warn # 'warn' or 'ignore' are also available, defaults to `warn` - retention-days: 5 # defaults to 90 - - name: Update timestamps - run: | - cd src - npm run timestamps - - name: Create Pull Request - id: cpr - uses: peter-evans/create-pull-request@v8 - with: - title: Pre-deploy Updates - branch-suffix: timestamp - commit-message: Update Timestamps - body: | - Updated Timestamps through GitHub action - - Auto-generated by [create-pull-request][1] GitHub Action - - [1]: https://github.com/peter-evans/create-pull-request - labels: generate chapters - - name: Check outputs - run: echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}" diff --git a/.github/workflows/test_website.yml b/.github/workflows/test_website.yml index db7efc0dca7..778cbb5c9c3 100644 --- a/.github/workflows/test_website.yml +++ b/.github/workflows/test_website.yml @@ -11,9 +11,6 @@ on: pull_request: paths-ignore: - 'sql/**' - push: - branches: - - main jobs: build: name: Build and Test Website diff --git a/cloudbuild.yaml b/cloudbuild.yaml index 6ccdf5edc7e..42ad397c914 100644 --- a/cloudbuild.yaml +++ b/cloudbuild.yaml @@ -1,5 +1,148 @@ +###################################### +## Cloud Build Deployment Pipeline ## +###################################### +# +# Triggered on every push to main (i.e. when a PR is merged). +# Update the trigger at: +# https://console.cloud.google.com/cloud-build/triggers;region=us-central1/edit/2bd8fcc6-6319-455d-88f2-38d564fe2db8?project=httparchive +# +# Steps: +# 1. build-and-test — install deps, generate chapters, run pytest + URL tests +# 2. generate-ebooks — update timestamps, install Prince, generate PDF ebooks +# 3. deploy — gcloud app deploy (App Engine version = $SHORT_SHA) +# 4. upload-ebooks — gsutil cp PDFs to GCS bucket +# +# $SHORT_SHA is set automatically by Cloud Build on a branch-push trigger. + +substitutions: + # Update when a new Prince version is released: https://www.princexml.com/latest/ + # python:3.12 is based on Debian Bookworm (12), so use the debian12 package. + _PRINCE_PACKAGE: 'prince_16.1-1_debian12_amd64.deb' + steps: -- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk' - entrypoint: 'bash' - args: ['-c', 'gcloud config set app/cloud_build_timeout 1600 && gcloud app deploy'] -timeout: '1600s' + # ───────────────────────────────────────────────────────────────────────── + # Step 1: Install dependencies, generate all chapters, run full test suite. + # Uses python:3.12 (Debian Bookworm) and installs Node 20 at runtime. + # node_modules persist in /workspace + # and are reused by later steps; Python packages are re-installed per step. + # ───────────────────────────────────────────────────────────────────────── + - name: 'python:3.12' + id: 'build-and-test' + entrypoint: 'bash' + dir: 'src' + args: + - '-c' + - | + set -e + # Install Node.js 20 (nodesource setup script also runs apt-get update) + curl -fsSL https://deb.nodesource.com/setup_20.x | bash - + apt-get install -y nodejs + + # Install Python dependencies + pip install --quiet -r requirements.txt + + # Install Node dependencies (uses package-lock.json for reproducibility) + npm ci + + # Start the web server in the background (required for URL tests) + python main.py background & + sleep 3 + + # Generate all chapters + npm run generate + + # Run Python unit tests + npm run pytest + + # Run full URL test suite against the running server + npm run test + + # ───────────────────────────────────────────────────────────────────────── + # Step 2: Update timestamps then generate PDF ebooks. + # Prince fetches pages from a running localhost server, so the Python server + # is re-started here. Chapters written to /workspace/src in step 1 are + # served directly; npm install is skipped (node_modules already present). + # ───────────────────────────────────────────────────────────────────────── + - name: 'python:3.12' + id: 'generate-ebooks' + entrypoint: 'bash' + dir: 'src' + args: + - '-c' + - | + set -e + apt-get update -qq + + # Install Node.js 20 + curl -fsSL https://deb.nodesource.com/setup_20.x | bash - + apt-get install -y nodejs + + # Update static-asset cache-busting hashes before deploying + npm run timestamps + + # Install CJK fonts for multilingual ebooks + apt-get install -y fonts-ipafont-gothic fonts-arphic-uming fonts-unfonts-core wget + + # Install pdftk (ebook post-processing) + apt-get install -y pdftk + + # Download and install Prince PDF generator + wget -q "https://www.princexml.com/download/${_PRINCE_PACKAGE}" -P /tmp + apt-get install -y "/tmp/${_PRINCE_PACKAGE}" + + # Install Python dependencies (needed to run the web server) + pip install --quiet -r requirements.txt + + # Re-start the web server so Prince can fetch pages from localhost + python main.py background & + sleep 3 + + # Generate PDF ebooks + mkdir -p static/pdfs + npm run ebooks + + # ───────────────────────────────────────────────────────────────────────── + # Step 3: Deploy to Google App Engine. + # app.yaml is read from /workspace/src (dir: src). + # App Engine version labels must start with a letter, so SHORT_SHA is + # prefixed: e.g. a1b2c3d → deploy-a1b2c3d. + # ───────────────────────────────────────────────────────────────────────── + - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk' + id: 'deploy' + entrypoint: 'bash' + dir: 'src' + args: + - '-c' + - | + set -e + VERSION="deploy-${SHORT_SHA}" + echo "Deploying version: ${VERSION}" + gcloud app deploy \ + --project webalmanac \ + --version="${VERSION}" \ + --stop-previous-version \ + --quiet + + # ───────────────────────────────────────────────────────────────────────── + # Step 4: Upload generated ebooks to GCS. + # Uses nullglob so missing PDFs produce a warning rather than an error. + # ───────────────────────────────────────────────────────────────────────── + - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk' + id: 'upload-ebooks' + entrypoint: 'bash' + args: + - '-c' + - | + set -e + shopt -s nullglob + pdfs=(/workspace/src/static/pdfs/web_almanac_*.pdf) + if [[ ${#pdfs[@]} -gt 0 ]]; then + gsutil -m cp "${pdfs[@]}" gs://httparchive/almanac/ebooks/ + echo "Uploaded ${#pdfs[@]} PDF(s) to GCS" + else + echo "Warning: no ebooks found to upload" + fi + +# Total timeout covers: ~10 min build/test + ~30 min ebook generation + +# ~10 min deploy + ~5 min GCS upload. +timeout: '3600s' From 96812e637ef78a8b0533e051b7977c99a2bcc878 Mon Sep 17 00:00:00 2001 From: Max Ostapenko <1611259+max-ostapenko@users.noreply.github.com> Date: Thu, 12 Mar 2026 00:08:07 +0100 Subject: [PATCH 3/5] clean --- cloudbuild.yaml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/cloudbuild.yaml b/cloudbuild.yaml index 42ad397c914..2f1ca8c147f 100644 --- a/cloudbuild.yaml +++ b/cloudbuild.yaml @@ -6,12 +6,6 @@ # Update the trigger at: # https://console.cloud.google.com/cloud-build/triggers;region=us-central1/edit/2bd8fcc6-6319-455d-88f2-38d564fe2db8?project=httparchive # -# Steps: -# 1. build-and-test — install deps, generate chapters, run pytest + URL tests -# 2. generate-ebooks — update timestamps, install Prince, generate PDF ebooks -# 3. deploy — gcloud app deploy (App Engine version = $SHORT_SHA) -# 4. upload-ebooks — gsutil cp PDFs to GCS bucket -# # $SHORT_SHA is set automatically by Cloud Build on a branch-push trigger. substitutions: @@ -23,8 +17,8 @@ steps: # ───────────────────────────────────────────────────────────────────────── # Step 1: Install dependencies, generate all chapters, run full test suite. # Uses python:3.12 (Debian Bookworm) and installs Node 20 at runtime. - # node_modules persist in /workspace - # and are reused by later steps; Python packages are re-installed per step. + # node_modules persist in /workspace and are reused by later steps; + # Python packages are re-installed per step. # ───────────────────────────────────────────────────────────────────────── - name: 'python:3.12' id: 'build-and-test' From 519cb01c54dc4fd35c1bf6d63d1cfbe2774eef73 Mon Sep 17 00:00:00 2001 From: Max Ostapenko <1611259+max-ostapenko@users.noreply.github.com> Date: Thu, 12 Mar 2026 13:03:43 +0100 Subject: [PATCH 4/5] Update cloudbuild.yaml Co-authored-by: Barry Pollard --- cloudbuild.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudbuild.yaml b/cloudbuild.yaml index 2f1ca8c147f..b102eb11aea 100644 --- a/cloudbuild.yaml +++ b/cloudbuild.yaml @@ -28,8 +28,8 @@ steps: - '-c' - | set -e - # Install Node.js 20 (nodesource setup script also runs apt-get update) - curl -fsSL https://deb.nodesource.com/setup_20.x | bash - + # Install Node.js 24 (nodesource setup script also runs apt-get update) + curl -fsSL https://deb.nodesource.com/setup_24.x | bash - apt-get install -y nodejs # Install Python dependencies From 8c7b6a4712e14e2592716c7dbfcf813d16883e2a Mon Sep 17 00:00:00 2001 From: Max Ostapenko <1611259+max-ostapenko@users.noreply.github.com> Date: Thu, 12 Mar 2026 13:03:49 +0100 Subject: [PATCH 5/5] Update cloudbuild.yaml Co-authored-by: Barry Pollard --- cloudbuild.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudbuild.yaml b/cloudbuild.yaml index b102eb11aea..353e21082e3 100644 --- a/cloudbuild.yaml +++ b/cloudbuild.yaml @@ -16,7 +16,7 @@ substitutions: steps: # ───────────────────────────────────────────────────────────────────────── # Step 1: Install dependencies, generate all chapters, run full test suite. - # Uses python:3.12 (Debian Bookworm) and installs Node 20 at runtime. + # Uses python:3.12 (Debian Bookworm) and installs Node 24 at runtime. # node_modules persist in /workspace and are reused by later steps; # Python packages are re-installed per step. # ─────────────────────────────────────────────────────────────────────────