mirror of
https://github.com/Heretek-AI/GDevelop-Heretek.git
synced 2026-10-12 02:02:42 +00:00
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.
195 lines
8.8 KiB
YAML
195 lines
8.8 KiB
YAML
name: Upstream Sync (PR-Only)
|
|
|
|
on:
|
|
schedule:
|
|
- cron: '0 6 * * *' # Daily at 06:00 UTC
|
|
workflow_dispatch:
|
|
|
|
# Minimal least-privilege permissions: strictly cannot mint OIDC tokens, cannot modify packages
|
|
permissions:
|
|
contents: write
|
|
pull-requests: write
|
|
issues: write # conflict path calls `gh issue create`
|
|
|
|
jobs:
|
|
prepare-sync-pr:
|
|
name: Sync Upstream & Create PR
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Hardened Checkout
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
token: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Configure Git Identity
|
|
run: |
|
|
git config user.name "heretek-sync-bot[bot]"
|
|
git config user.email "[email protected]"
|
|
|
|
- name: Fetch Upstream
|
|
run: |
|
|
git remote add upstream https://github.com/4ian/GDevelop.git || true
|
|
git fetch upstream master --tags
|
|
|
|
- name: Check for New Upstream Commits
|
|
id: check_diff
|
|
run: |
|
|
git checkout master
|
|
UPSTREAM_COMMITS=$(git rev-list master..upstream/master --count)
|
|
echo "Upstream commits ahead: $UPSTREAM_COMMITS"
|
|
echo "commits_count=$UPSTREAM_COMMITS" >> $GITHUB_OUTPUT
|
|
|
|
- name: Create Isolated Sync Branch
|
|
if: steps.check_diff.outputs.commits_count != '0'
|
|
id: sync_branch
|
|
run: |
|
|
SYNC_BRANCH="upstream-sync-$(date +%Y-%m-%d)"
|
|
git checkout -B "$SYNC_BRANCH" master
|
|
|
|
# Attempt merge
|
|
MERGE_SUCCESS=true
|
|
if ! git merge upstream/master -m "chore(sync): sync upstream 4ian/GDevelop changes"; then
|
|
echo "Merge conflicts detected."
|
|
MERGE_SUCCESS=false
|
|
git merge --abort
|
|
fi
|
|
|
|
# SECURITY: Restore and lock local workflows to prevent upstream workflow tampering (e.g. Shai-Hulud)
|
|
echo "Locking local .github/workflows to prevent upstream workflow tampering..."
|
|
git checkout master -- .github/workflows
|
|
|
|
# INTEGRITY CHECK: Ensure BYOK core files were not deleted or stripped
|
|
REQUIRED_BYOK_FILES=(
|
|
"newIDE/app/src/AI/CustomAIClient.js"
|
|
"newIDE/app/src/AI/CustomAIClient.spec.js"
|
|
"newIDE/app/src/AiGeneration/AiConfiguration.js"
|
|
"newIDE/app/src/MainFrame/Preferences/PreferencesDialog.js"
|
|
"newIDE/app/src/MainFrame/Preferences/PreferencesProvider.js"
|
|
)
|
|
|
|
for file in "${REQUIRED_BYOK_FILES[@]}"; do
|
|
if [ ! -f "$file" ]; then
|
|
echo "CRITICAL: Required BYOK file $file is missing after merge! Restoring from master..."
|
|
git checkout master -- "$file"
|
|
fi
|
|
done
|
|
|
|
git commit -am "chore(sync): lock workflows and verify BYOK integrity" || true
|
|
git push origin "$SYNC_BRANCH" --force
|
|
|
|
echo "sync_branch=$SYNC_BRANCH" >> $GITHUB_OUTPUT
|
|
echo "merge_success=$MERGE_SUCCESS" >> $GITHUB_OUTPUT
|
|
|
|
- name: Verify fork divergence is unchanged by the sync
|
|
# A sync merge can silently re-introduce upstream copies of files the
|
|
# fork deliberately deletes or modifies (it is how
|
|
# Extensions/Physics3DBehavior/tests/Physics3DRuntimeBehavior.spec.js
|
|
# was deleted by hand and how the obsolete wavesurfer patch keeps
|
|
# coming back). `fork-divergence.json` is the checked-in allowlist of
|
|
# intended divergence; this step reports any drift instead of letting
|
|
# it reach a reviewer as a mystery diff. Informational only: the
|
|
# reviewer decides whether to record the drift or revert it.
|
|
if: steps.check_diff.outputs.commits_count != '0'
|
|
continue-on-error: true
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
if node scripts/check-fork-divergence.js; then
|
|
echo "Divergence matches fork-divergence.json."
|
|
else
|
|
{
|
|
echo "### Fork divergence changed in this sync"
|
|
echo ""
|
|
echo "The sync introduced or removed divergence not recorded in"
|
|
echo "\`fork-divergence.json\`. Review the list above, then either"
|
|
echo "record it (\`node scripts/check-fork-divergence.js --update\`)"
|
|
echo "or revert the accidental part before merging."
|
|
} >> "$GITHUB_STEP_SUMMARY"
|
|
echo "::warning::Fork divergence changed; see the job summary."
|
|
fi
|
|
|
|
- name: Open or Update Sync PR
|
|
if: steps.check_diff.outputs.commits_count != '0'
|
|
run: |
|
|
BRANCH="${{ steps.sync_branch.outputs.sync_branch }}"
|
|
MERGE_OK="${{ steps.sync_branch.outputs.merge_success }}"
|
|
|
|
PR_TITLE="[Upstream Sync] 4ian/GDevelop updates ($(date +%Y-%m-%d))"
|
|
|
|
PR_BODY="### Automated Upstream Synchronization
|
|
|
|
- **Upstream Source**: \`4ian/GDevelop:master\`
|
|
- **Target**: \`Heretek-AI/GDevelop-Heretek:master\`
|
|
- **Branch**: \`$BRANCH\`
|
|
- **Clean Merge**: \`$MERGE_OK\`
|
|
|
|
---
|
|
|
|
### Security & Integrity Controls
|
|
- **No Direct Push**: \`master\` is never modified automatically.
|
|
- **Workflow Poisoning Protection**: \`.github/workflows/\` were locked to local \`master\` to prevent malicious workflow injection (Shai-Hulud vectors).
|
|
- **BYOK File Integrity**: Core BYOK modules and test suites verified before PR creation.
|
|
|
|
*Please review the diff and run tests before merging into master.*"
|
|
|
|
# Check if an open PR for this branch already exists
|
|
if [ "$MERGE_OK" != "true" ]; then
|
|
echo "Upstream merge has conflicts that need manual resolution."
|
|
gh issue list --search "upstream sync conflicts in:title state:open" --json number -q 'map(.number) | length' > /tmp/conflict_issues.txt 2>/dev/null || echo 0 > /tmp/conflict_issues.txt
|
|
if [ "$(cat /tmp/conflict_issues.txt)" = "0" ]; then
|
|
gh issue create \
|
|
--title "Upstream sync needs manual conflict resolution ($(date +%Y-%m-%d))" \
|
|
--body "The automated upstream sync from \`4ian/GDevelop:master\` hit merge conflicts. Please run a manual \`git fetch upstream && git merge upstream/master\` on a branch, resolve conflicts, and open a PR. Workflow/BYOK integrity guards still apply after resolving." || true
|
|
fi
|
|
exit 0
|
|
fi
|
|
EXISTING_PR=$(gh pr list --head "$BRANCH" --json number -q '.[0].number' 2>/dev/null || echo "")
|
|
if [ -n "$EXISTING_PR" ]; then
|
|
echo "PR #$EXISTING_PR already exists for $BRANCH."
|
|
exit 0
|
|
fi
|
|
|
|
# `gh pr create` from this workflow fails with "GraphQL: Resource not
|
|
# accessible by integration (createPullRequest)". The cause is NOT a
|
|
# read-only token - that was an earlier misdiagnosis. Verified by
|
|
# probe against this repo:
|
|
#
|
|
# REST POST /pulls -> OK (created a PR)
|
|
# gh pr create -> OK (created a PR)
|
|
# gh pr create on an
|
|
# upstream-sync-* branch -> OK (created a PR)
|
|
#
|
|
# all as app/github-actions with secrets.GITHUB_TOKEN, while
|
|
# default_workflow_permissions = write
|
|
# can_approve_pull_request_reviews = true
|
|
# runtime grant = Contents:write + PullRequests:write
|
|
# git push with the same token = succeeds
|
|
# SYNC_PAT = not set
|
|
#
|
|
# So the token can create PRs here. The branch HAS been pushed, so
|
|
# degrade to a ready-to-click compare URL rather than failing the run.
|
|
if gh pr create \
|
|
--base master \
|
|
--head "$BRANCH" \
|
|
--title "$PR_TITLE" \
|
|
--body "$PR_BODY"; then
|
|
echo "Sync PR created."
|
|
else
|
|
COMPARE_URL="https://github.com/${GITHUB_REPOSITORY}/compare/master...${BRANCH}?expand=1"
|
|
{
|
|
echo "### Upstream sync branch is ready — no PR was opened"
|
|
echo ""
|
|
echo "The sync branch \`$BRANCH\` was pushed successfully, but this"
|
|
echo "workflow could not open the pull request. \`gh pr create\`"
|
|
echo "returned a permissions error even though the same token"
|
|
echo "creates PRs successfully from other workflows; the cause is"
|
|
echo "still unidentified (see the comment in this step)."
|
|
echo ""
|
|
echo "Open it manually: $COMPARE_URL"
|
|
} >> "$GITHUB_STEP_SUMMARY"
|
|
echo "::warning::Could not open the sync PR. Open manually: $COMPARE_URL"
|
|
fi
|
|
env:
|
|
GH_TOKEN: ${{ secrets.SYNC_PAT != '' && secrets.SYNC_PAT || secrets.GITHUB_TOKEN }}
|