Files
GDevelop-Heretek/.github/workflows/ci.yml
T
John Smith 48ed4e38d0 chore(maintenance): make fork divergence and static-analysis scope deliberate
Two problems made this fork expensive to maintain: scanner noise nobody
could act on, and divergence from upstream that nothing tracked.

Code scanning reported 3,518 open alerts. 3,449 were fallow findings and
2,399 of those sat in vendored bundles `.fallow.toml` never listed
(jolt-physics.wasm.js 436, box2djs/box2d.js 328, the Firebase SDK 655,
Recast, PeerJS, draco). Listing them plus raising the CRAP thresholds from
the defaults to evidence-based values takes fallow from 3,449 findings to
155, all in maintained code. The remaining 69 were CodeQL: 8 third-party
actions are now pinned to full commit SHAs with `# vN` comments (so
Dependabot keeps updating them) and all 10 workflows declare an explicit
least-privilege `permissions:` block.

SonarCloud's 2,845 open issues are mostly not this fork's code — 2,526 sit
in files byte-identical to upstream. The real defect was configuration:
~200 lines of `sonar.issue.ignore.multicriteria.e1..e88` in
.sonarcloud.properties, plus scripts/sonarcloud-apply-suppressions.sh, were
inert. That key is a Project Settings UI setting and cannot be read from
that file under automatic analysis, so two phases of remediation work
chased config the server never saw. Both are deleted; the exclusions that
do work are kept and extended. `sonar.test.inclusions` no longer declares
production source directories as test inclusions.

Divergence is now bounded and checked. `fork-divergence.json` is a
checked-in allowlist of the 111 modified, 51 fork-only and 7
upstream-missing paths, and `scripts/check-fork-divergence.js` fails CI on
divergence that is not listed or on stale entries. It reads upstream
through the GitHub trees API because 4ian/GDevelop is ~1.7GB and this runs
on every PR. Restoring Extensions/Physics3DBehavior/tests/
Physics3DRuntimeBehavior.spec.js — deleted by an earlier SonarCloud pass to
silence one rule — removes one entry each.

Repairs found along the way: every `// nosonar` marker sat on the line
before the statement SonarCloud flags, where the directive does nothing;
each is now on the exact reported line. Four garbled comment fragments left
in multiplayertools.ts by the same pass are gone. A two-line re-export shim
with no importers (AiGeneration/CustomAIClient.js) is deleted.

`scripts/sonarcloud-drift.js` now writes its outputs to $GITHUB_OUTPUT and
seeds its own baseline, so the weekly drift job's ceiling can actually fire;
it previously ran behind `continue-on-error` against a threshold that
predated the exclusions and was unreachable.

Root documentation collapses to MAINTENANCE.md. The three SonarCloud phase
plans and the Dependabot plan described steps that were proven impossible
and are deleted; AI_GAME_STUDIO_ASSESSMENT.md is a design record and stays.

Verified: newIDE/app 176/176 suites and 1,922 tests pass; GDevelop.js 33/33
suites and 1,177 tests pass; GDJS Karma 1,287 pass with the same 9
pre-existing failures (Firebase network tests) reproduced on a pristine
tree. check-format lists 37 files against the documented 41-file baseline,
so no new violations. Lockfile, drift and divergence guards all exit 0.
2026-09-22 20:01:28 -04:00

291 lines
10 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Continuous Integration for the JS/TS portions of this fork.
#
# What this runs:
# 1. Lockfile security guard (scripts/security/check-lockfiles.js).
# 2. Fork-divergence guard (scripts/check-fork-divergence.js).
# 3. libGD.js (Emscripten) build, once, then shared via artifact.
# 4. Jest test suites in newIDE/app and GDevelop.js, both consuming the
# build output.
# 5. Lint + Flow type checks for newIDE/app.
#
# Karma/Mocha tests for GDJS and SharedLibs/TileMapHelper require a
# headless browser and are run in the dedicated CI jobs in
# .circleci/config.yml (kept from upstream).
name: CI
on:
push:
branches: [master]
pull_request:
branches: [master]
# Read-only token: checkout + report test results.
# `actions/upload-artifact` and `download-artifact` are still available
# with `contents: read` because they use the OIDC token, not the GITHUB_TOKEN.
permissions:
contents: read
# Cancel superseded runs on the same branch — saves CI minutes.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
lockfile-guard:
name: Lockfile security guard
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version: "20"
- name: Run guard
run: node scripts/security/check-lockfiles.js
fork-divergence:
name: Fork divergence guard
# Keeps the fork's divergence from 4ian/GDevelop bounded and deliberate.
# `fork-divergence.json` is the checked-in allowlist; this job fails when a
# path diverges that is not listed (new, undecided divergence) or when a
# listed path no longer diverges (stale entry). Without it, divergence only
# surfaces as a merge conflict on the next upstream sync.
#
# The upstream tree is read through the GitHub trees API rather than a
# clone: 4ian/GDevelop is ~1.7 GB and this job runs on every PR.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version: "20"
- name: Compare against upstream 4ian/GDevelop
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: node scripts/check-fork-divergence.js
lockfile-sync:
name: Lockfile sync (all manifests)
# `npm ci` hard-fails when package.json and package-lock.json disagree, but
# only the directories that a later job happens to install are ever checked.
# That gap let THREE lockfiles rot on master unnoticed (GDJS,
# newIDE/electron-app/app, SharedLibs/TileMapHelper) — each one only failed
# the moment someone tried to install there. This job checks every manifest
# so drift surfaces on the PR that causes it.
#
# Runs as one job over all directories rather than a 10-way matrix: each
# entry is a dry-run resolve taking a few seconds, so a matrix costs 10
# runners to save ~30s of wall clock, and the queue contention starves the
# slower build/test jobs.
#
# `--ignore-scripts` keeps postinstall hooks (patch-package,
# import-resources) out of it: this job only asserts that the lockfile
# resolves. All directories are checked even after a failure so one run
# reports every drifted manifest.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version: "20"
- name: Assert every lockfile is in sync with its package.json
run: |
set -uo pipefail
dirs=(
GDevelop.js
GDJS
GDJS/tests
newIDE/app
newIDE/electron-app
newIDE/electron-app/app
newIDE/web-app
newIDE/visual-tests
SharedLibs/TileMapHelper
SharedLibs/ThreeAddons
)
failed=0
for d in "${dirs[@]}"; do
if [ ! -f "$d/package-lock.json" ]; then
echo "::notice::$d has no lockfile; skipping"
continue
fi
# `npm ci` writes its diagnostics to stdout, so capture both.
if out=$(cd "$d" && npm ci --dry-run --no-audit --no-fund --ignore-scripts 2>&1); then
echo "✅ $d"
else
echo "❌ $d"
echo "$out" | grep -E 'Missing: |Invalid: |npm error code' | sed 's/^/ /' || true
failed=1
fi
done
if [ "$failed" -ne 0 ]; then
echo "::error::One or more lockfiles are out of sync with package.json. Run 'npm install --package-lock-only' in the failing directory."
exit 1
fi
echo "All lockfiles in sync."
build-libgd:
name: Build libGD.js (Emscripten)
# Slow step (~5–10 min on cold cache). Builds the C++ core via
# grunt + emcmake + emmake and uploads the output so the two test
# jobs can consume it without each doing the build themselves.
runs-on: ubuntu-latest
needs: lockfile-guard
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
cache-dependency-path: GDevelop.js/package-lock.json
- name: Install Emscripten SDK
# We install 3.1.21 from the original juj/emsdk fork (the version
# upstream CircleCI uses). The emscripten-core consolidation no
# longer ships the 3.x line as tagged releases.
run: |
git clone https://github.com/juj/emsdk.git /tmp/emsdk
cd /tmp/emsdk
./emsdk install 3.1.21
./emsdk activate 3.1.21
shell: bash
- name: Install GDevelop.js deps
working-directory: GDevelop.js
run: npm ci --no-audit --no-fund
- name: Build libGD.js
# Source emsdk env so emcmake/emmake/cmake are on PATH.
working-directory: GDevelop.js
run: |
source /tmp/emsdk/emsdk_env.sh
npm run build
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: libgd-build
path: Binaries/embuild/GDevelop.js/
if-no-files-found: error
retention-days: 1
test-newide-app:
name: newIDE/app (Jest)
runs-on: ubuntu-latest
needs: build-libgd
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
cache-dependency-path: newIDE/app/package-lock.json
- name: Download libGD.js build
uses: actions/download-artifact@v4
with:
name: libgd-build
path: Binaries/embuild/GDevelop.js/
- name: Install
working-directory: newIDE/app
run: npm ci --no-audit --no-fund --ignore-scripts
- name: Audit production dependencies
working-directory: newIDE/app
run: npm audit --omit=dev --audit-level=high
- name: Copy libGD.js into newIDE/app
# The Jest setup pulls in a pre-built C++ core compiled to JS via
# Emscripten. The build-libgd job produced libGD.js/libGD.wasm
# and uploaded them as an artifact; we copy them straight into
# node_modules/libGD.js-for-tests-only so setupTests.js can
# require() them. Using a direct cp here is simpler than invoking
# scripts/import-libGD.js (which prefers to download from S3 when
# it can't see the build output for any reason).
run: |
mkdir -p newIDE/app/node_modules/libGD.js-for-tests-only
cp Binaries/embuild/GDevelop.js/libGD.js \
newIDE/app/node_modules/libGD.js-for-tests-only/index.js
cp Binaries/embuild/GDevelop.js/libGD.wasm \
newIDE/app/node_modules/libGD.js-for-tests-only/libGD.wasm
ls -la newIDE/app/node_modules/libGD.js-for-tests-only/
- name: Generate build artifacts
# Several Jest test suites require generated files that are
# normally produced by `npm run import-resources` (the local-dev
# path). We run only the generators we need to keep CI fast:
# - make-version-metadata.js produces src/Version/VersionMetadata.js
# (required by src/Version/index.js)
# - build-theme-resources.js produces the UI Theme variable .js/.json
# files (required by src/UI/Theme/DefaultLightTheme/index.js).
# We do NOT need import-libGD.js, import-GDJS-Runtime.js,
# import-monaco-editor.js, import-zipped-external-editors.js, etc.
# at test time, so we skip them.
run: |
cd newIDE/app/scripts
node make-version-metadata.js
node build-theme-resources.js
- name: Test
working-directory: newIDE/app
env:
CI: "true"
run: npm test -- --watchAll=false --ci
test-gdevelopjs:
name: GDevelop.js (Jest)
runs-on: ubuntu-latest
needs: build-libgd
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
cache-dependency-path: GDevelop.js/package-lock.json
- name: Download libGD.js build
uses: actions/download-artifact@v4
with:
name: libgd-build
path: Binaries/embuild/GDevelop.js/
- name: Install
working-directory: GDevelop.js
run: npm ci --no-audit --no-fund
- name: Audit production dependencies
working-directory: GDevelop.js
run: npm audit --omit=dev --audit-level=high
- name: Test
working-directory: GDevelop.js
env:
CI: "true"
run: npm test
lint-newide-app:
name: newIDE/app (lint + format)
runs-on: ubuntu-latest
needs: lockfile-guard
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
cache-dependency-path: newIDE/app/package-lock.json
- name: Install
working-directory: newIDE/app
run: npm ci --no-audit --no-fund --ignore-scripts
- name: Lint
working-directory: newIDE/app
run: npm run lint
- name: Format check
working-directory: newIDE/app
run: npm run check-format
- name: TypeScript type check
working-directory: newIDE/app
run: npm run check-types