From 299281433bd4dea571ba6b4df835d0b0c036be56 Mon Sep 17 00:00:00 2001 From: Rishi Tank Date: Thu, 8 Oct 2026 14:52:11 +0100 Subject: [PATCH 1/9] ci: add runtime gate and Jest CI in the Node 14 toolchain, security scan, workflow lint and house Renovate preset Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012rYJxu978iUSGFZ1UuqTsi --- .github/runtime-gate.md | 13 ++++++++ .github/workflows/ci.yml | 43 ++++++++++++++++++++++++ .github/workflows/runtime.yml | 49 ++++++++++++++++++++++++++++ .github/workflows/security.yml | 34 +++++++++++++++++++ .github/workflows/workflows-lint.yml | 18 ++++++++++ renovate.json | 2 +- 6 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 .github/runtime-gate.md create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/runtime.yml create mode 100644 .github/workflows/security.yml create mode 100644 .github/workflows/workflows-lint.yml diff --git a/.github/runtime-gate.md b/.github/runtime-gate.md new file mode 100644 index 0000000..b7787ee --- /dev/null +++ b/.github/runtime-gate.md @@ -0,0 +1,13 @@ +# test: notes for the runtime explorer + +**What it is.** An ejected Create React App 2 starter kept for reproducing +bugs. It renders a single page: a header with the spinning React logo and +"Welcome to React", and a line of intro text. Bootstrap 3 (Sass) and jQuery +are bundled, and a service worker is registered in production builds. + +**Flows worth trying.** +1. Load `/` and check that the header, logo and intro text render. +2. Resize to phone width and check nothing overflows. +3. Reload, so the service worker serves the page, and check it still renders. + +**No logins, forms or data.** There is nothing else to explore. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1a7edfa --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,43 @@ +# The repo's own Jest suite, run in the toolchain the app was written for. +# Ejected Create React App 2 (Jest 23, Babel 6/7, node-sass 4) does not +# install or run on current Node, so install and test run inside node:14. +# There is no lockfile: `npm install` resolves today's newest in-range +# versions, so expect drift. The production build and the browser checks are +# in runtime.yml. +name: ci + +on: + pull_request: + push: + branches: [master] + workflow_dispatch: + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + test: + name: test (node:14) + runs-on: ubuntu-latest + timeout-minutes: 20 + env: + IMAGE: node:14-bullseye + steps: + - uses: actions/checkout@v7.0.1 + with: + persist-credentials: false + + - name: Install + run: | + docker run --rm -v "${GITHUB_WORKSPACE}:/w" -w /w -u "$(id -u):$(id -g)" \ + -e HOME=/tmp -e CI=true "${IMAGE}" npm install + + # scripts/test.js only leaves watch mode when CI is set. + - name: Test + run: | + docker run --rm -v "${GITHUB_WORKSPACE}:/w" -w /w -u "$(id -u):$(id -g)" \ + -e HOME=/tmp -e CI=true "${IMAGE}" npm test diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml new file mode 100644 index 0000000..fe73409 --- /dev/null +++ b/.github/workflows/runtime.yml @@ -0,0 +1,49 @@ +# Runtime gate: boots the app and uses it in a real browser before a change +# (a Renovate update included) can merge. Fails on runtime errors: uncaught +# exceptions, console errors, React hydration errors, failed same-origin +# requests, 4xx/5xx, blank pages, crash screens. Advisory on top: screenshot +# diff against the last master run, and an AI explorer (Gemini free +# tier, GEMINI_API_KEY secret) that uses the app like a person for major or +# framework updates, the `ai-explore` label, the weekly schedule and manual +# runs. See docs/runtime-gate.md in rishitank/.github. +name: runtime + +on: + pull_request: + push: + branches: [master] + schedule: + - cron: "43 5 * * 4" + workflow_dispatch: + +concurrency: + group: runtime-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + runtime: + name: runtime + uses: rishitank/.github/.github/workflows/runtime-gate.yml@7793cd7224a45518e7f4aabe58c6dcb52640bdb9 + permissions: + contents: read + actions: read + pull-requests: write + with: + # Legacy app (ejected Create React App 2: webpack 4, Babel 6/7, Jest 23, + # node-sass 4), revived in its own toolchain. There is no lockfile, so + # `npm install` resolves today's newest in-range versions (expect + # drift); node-sass then resolves to 4.14.x, which supports Node <= 14. + mode: static + build-image: node:14-bullseye + # The gate exports CI=true, which this CRA build script turns into + # "treat warnings as errors". That is a lint policy, not a runtime + # check, so it is switched off for the build only. + build: CI=false npm run build + static-dir: build + spa: true + port: 3000 + secrets: + gemini-api-key: ${{ secrets.GEMINI_API_KEY }} diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..2109792 --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,34 @@ +# Vulnerable dependencies (osv-scanner), leaked secrets (gitleaks, full +# history) and the ecosystem's own audit, via the shared workflow. +name: security + +on: + push: + branches: [master] + pull_request: + schedule: + - cron: "40 4 * * 1" + workflow_dispatch: + +concurrency: + group: security-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + security: + uses: rishitank/.github/.github/workflows/security.yml@v1 + with: + ecosystem: node + # No .nvmrc in this repo: the audit's setup-node reads package.json + # instead (no engines field there, so it uses the runner's Node). + node-version-file: package.json + # The repo has no lockfile, so osv-scanner finds no package sources and + # would exit 128. Allow that rather than fail on a missing input; + # gitleaks still scans the full history. + osv-scan-args: | + --allow-no-lockfiles + --recursive + ./ diff --git a/.github/workflows/workflows-lint.yml b/.github/workflows/workflows-lint.yml new file mode 100644 index 0000000..f927d2f --- /dev/null +++ b/.github/workflows/workflows-lint.yml @@ -0,0 +1,18 @@ +# actionlint over this repo's workflows. Catches bad `uses:` refs, undefined +# contexts and shell bugs in `run:` blocks before they cost a red build. +name: workflows-lint + +on: + pull_request: + paths: [".github/workflows/**"] + push: + branches: [master] + paths: [".github/workflows/**"] + workflow_dispatch: + +permissions: + contents: read + +jobs: + actionlint: + uses: rishitank/.github/.github/workflows/workflows-lint.yml@v1 diff --git a/renovate.json b/renovate.json index 5db72dd..464a147 100644 --- a/renovate.json +++ b/renovate.json @@ -1,6 +1,6 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ - "config:recommended" + "github>rishitank/.github:node" ] } From 8bad6605c3f07db1ecab9f3a4039a788a7d6c821 Mon Sep 17 00:00:00 2001 From: Rishi Tank Date: Thu, 8 Oct 2026 15:18:19 +0100 Subject: [PATCH 2/9] ci: pin runtime-gate to 8040381 (operates every safe control) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012rYJxu978iUSGFZ1UuqTsi --- .github/workflows/runtime.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index fe73409..f3c162c 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -26,7 +26,7 @@ permissions: jobs: runtime: name: runtime - uses: rishitank/.github/.github/workflows/runtime-gate.yml@7793cd7224a45518e7f4aabe58c6dcb52640bdb9 + uses: rishitank/.github/.github/workflows/runtime-gate.yml@80403813373bbcadc5e3d90158fbf81f80613cd6 permissions: contents: read actions: read From c883d475cefdbf3cb578d5e53c11462b563c629a Mon Sep 17 00:00:00 2001 From: Rishi Tank Date: Thu, 8 Oct 2026 16:08:27 +0100 Subject: [PATCH 3/9] ci: pin runtime-gate to 8e8a197 (fills and submits forms) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012rYJxu978iUSGFZ1UuqTsi --- .github/workflows/runtime.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index f3c162c..75ebd4a 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -26,7 +26,7 @@ permissions: jobs: runtime: name: runtime - uses: rishitank/.github/.github/workflows/runtime-gate.yml@80403813373bbcadc5e3d90158fbf81f80613cd6 + uses: rishitank/.github/.github/workflows/runtime-gate.yml@8e8a1977309ac958bafd85825c7cd800be8c8ac8 permissions: contents: read actions: read From 1cf525828d2abd16e16a11b84fe2f50d3697ec6d Mon Sep 17 00:00:00 2001 From: Rishi Tank Date: Thu, 8 Oct 2026 16:37:33 +0100 Subject: [PATCH 4/9] fix(build): move CSS extraction to mini-css-extract-plugin and bridge Babel 7 for Jest Three pre-existing problems that CI surfaced, fixed minimally: - Build: extract-text-webpack-plugin@3 predates webpack 4 and crashes in seal ("Chunk.entrypoints: Use Chunks.groupsIterable ..."). The prod config now uses mini-css-extract-plugin@^0.12 (the webpack-4 replacement): its loader replaces ExtractTextPlugin.extract(), same filename and relative publicPath handling. css-loader 2 also rejects the removed `minimize` option ("CSS Loader Invalid Options"), so it is dropped; CSS is no longer minified, everything else is unchanged. - Build: with no lockfile, bootstrap-sass ^3.4.0 now resolves to 3.4.3, which uses `@use "sass:math"` / math.div. node-sass 4 (libsass) cannot parse that, so bootstrap-sass is pinned to 3.4.1, the last release that compiles with node-sass. - Jest: babel-jest 23 loads `babel-core`, which was Babel 6 and cannot run babel-preset-react-app 7 ("Unknown option ... overrides"). Swapped babel-core for 7.0.0-bridge.0 and added @babel/core ^7.2.2, the standard Jest 23 + Babel 7 setup. Verified locally on a reconstruction of the branch: npm test passes (1/1) and npm run build compiles (with dart-sass standing in for node-sass, which cannot build on the sandbox's Node 22). No lockfile exists, so none changes. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012rYJxu978iUSGFZ1UuqTsi --- config/webpack.config.prod.js | 114 ++++++++++++++++------------------ package.json | 7 ++- 2 files changed, 56 insertions(+), 65 deletions(-) diff --git a/config/webpack.config.prod.js b/config/webpack.config.prod.js index 88c4fef..14b823b 100644 --- a/config/webpack.config.prod.js +++ b/config/webpack.config.prod.js @@ -11,7 +11,7 @@ const eslintFormatter = require('react-dev-utils/eslintFormatter'); const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); const paths = require('./paths'); const getClientEnvironment = require('./env'); -const ExtractTextPlugin = require('extract-text-webpack-plugin'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); // Webpack uses `publicPath` to determine where the app is being served from. // It requires a trailing slash, or the file assets will get an incorrect path. @@ -39,11 +39,9 @@ if (env.stringified['process.env'].NODE_ENV !== '"production"') { // Note: defined here because it will be used more than once. const cssFilename = 'static/css/[name].[contenthash:8].css'; -// ExtractTextPlugin expects the build output to be flat. -// (See https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/27) -// However, our output is structured with css, js and media folders. -// To have this structure working with relative paths, we have to use custom options. -const extractTextPluginOptions = shouldUseRelativeAssetPaths +// Our output is structured with css, js and media folders. With relative asset +// paths, url()s inside the extracted CSS have to climb back out of static/css. +const miniCssExtractLoaderOptions = shouldUseRelativeAssetPaths ? // Making sure that the publicPath goes back to to build folder. { publicPath: Array(cssFilename.split('/').length).join('../') } : {}; @@ -160,64 +158,55 @@ module.exports = { // The notation here is somewhat confusing. // "postcss" loader applies autoprefixer to our CSS. // "css" loader resolves paths in CSS and adds assets as dependencies. - // "style" loader normally turns CSS into JS modules injecting