Files
Workflow config file is invalid. Please check your config file: model.ReadWorkflow: yaml: unmarshal errors: line 296: mapping key "gdevelop-js-build" already defined at line 87 line 352: mapping key "app-build" already defined at line 143 line 438: mapping key "platform-build" already defined at line 229

599 lines
19 KiB
YAML

# Build workflow for BYOK fork of GDevelop
#
# Multi-job pipeline:
# 1. validate — lint, syntax checks, unit tests
# 2. gdevelop-js-build — compile GDevelop.js (C++ → WASM via Emscripten),
# upload libGD.js + libGD.wasm as CI artifacts
# 3. app-build — compile React frontend (react-app-rewired),
# build GDJS game engine runtime (esbuild),
# assemble Electron preview deps
# (@electron/remote → preview_node_modules),
# upload app-build artifact
# 4. platform-build — Electron platform installer builds (5-ary matrix)
# 5. release — create a GitHub release (pre-release from master
# push, draft release from v* tag, or from
# workflow_dispatch)
#
# Release modes:
# - Push to master → validate → gdevelop-js-build → app-build → platform-build → auto pre-release (with platform artifacts)
# - Push a v* tag → validate → gdevelop-js-build → app-build → platform-build → draft release (with platform artifacts)
# - workflow_dispatch → validate → gdevelop-js-build → app-build → platform-build → release for tag (with platform artifacts)
name: Build
on:
push:
branches:
- master
tags:
- "v*"
workflow_dispatch:
inputs:
tag:
description: "Tag to release (e.g. v1.0.0)"
required: false
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
NODE_VERSION: "18"
jobs:
validate:
name: Validate
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Cache newIDE node_modules
id: cache
uses: actions/cache@v4
with:
path: newIDE/app/node_modules
key: newide-build-v1-${{ hashFiles('newIDE/app/package-lock.json') }}
- name: Install dependencies
if: steps.cache.outputs.cache-hit != 'true'
working-directory: newIDE/app
run: npm ci --ignore-scripts
- name: Lint
working-directory: newIDE/app
run: npm run lint
- name: BYOK syntax check
run: |
for f in newIDE/electron-app/app/byok/*.js \
newIDE/electron-app/app/byok/tests/*.test.js; do
node -c "$f" && echo " ✓ $f" || exit 1
done
- name: BYOK unit tests
working-directory: newIDE/electron-app/app/byok
run: |
node --test \
tests/errors.test.js \
tests/buildSystemPrompt.test.js \
tests/requestStore.test.js \
tests/callLLM.test.js
gdevelop-js-build:
name: GDevelop.js Emscripten build
needs: validate
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Cache GDevelop.js node_modules
id: cache-gdjs
uses: actions/cache@v4
with:
path: GDevelop.js/node_modules
key: gdjs-v1-${{ hashFiles('GDevelop.js/package-lock.json') }}
- name: Install Emscripten SDK
uses: mymindstorm/setup-emsdk@v1
- name: Install GDevelop.js dependencies
working-directory: GDevelop.js
run: npm ci --ignore-scripts
- name: Build GDevelop.js (Emscripten → WASM)
working-directory: GDevelop.js
run: npm run build
- name: Verify build artifacts
run: |
echo "=== GDevelop.js build artifact verification ==="
test -f newIDE/app/public/libGD.js \
|| { echo "❌ libGD.js not found in newIDE/app/public/"; exit 1; }
test -f newIDE/app/public/libGD.wasm \
|| { echo "❌ libGD.wasm not found in newIDE/app/public/"; exit 1; }
JS_SIZE=$(stat -c%s newIDE/app/public/libGD.js 2>/dev/null || echo 0)
WASM_SIZE=$(stat -c%s newIDE/app/public/libGD.wasm 2>/dev/null || echo 0)
MAX_SIZE=2097152
echo "libGD.js: ${JS_SIZE} bytes"
echo "libGD.wasm: ${WASM_SIZE} bytes"
if [ "${JS_SIZE}" -gt "${MAX_SIZE}" ]; then
echo "❌ libGD.js exceeds 2 MiB limit (${JS_SIZE} > ${MAX_SIZE})"
exit 1
fi
echo "✅ libGD.js size check passed (under 2 MiB)"
- uses: actions/upload-artifact@v4
with:
name: gdjs-build
path: newIDE/app/public/
app-build:
name: App + GDJS Runtime Build
needs: [validate, gdevelop-js-build]
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Cache newIDE/app node_modules
id: cache-app
uses: actions/cache@v4
with:
path: newIDE/app/node_modules
key: newide-app-v1-${{ hashFiles('newIDE/app/package-lock.json') }}
- name: Cache GDJS node_modules
id: cache-gdjs-rt
uses: actions/cache@v4
with:
path: GDJS/node_modules
key: gdjs-rt-v1-${{ hashFiles('GDJS/package-lock.json') }}
- name: Cache electron-app node_modules
id: cache-electron
uses: actions/cache@v4
with:
path: newIDE/electron-app/node_modules
key: electron-app-v1-${{ hashFiles('newIDE/electron-app/package-lock.json') }}
- uses: actions/download-artifact@v4
with:
name: gdjs-build
path: newIDE/app/public/
- name: Install app dependencies
working-directory: newIDE/app
run: npm ci
- name: Build React app
working-directory: newIDE/app
run: npm run build
- name: Install Electron dependencies
working-directory: newIDE/electron-app
run: npm ci
- name: Verify outputs
run: |
echo "=== App Build Verification ==="
MISSING=0
if [ -f newIDE/app/build/index.html ]; then
echo "✅ newIDE/app/build/index.html"
else
echo "❌ newIDE/app/build/index.html MISSING"
MISSING=1
fi
if [ -d newIDE/app/resources/GDJS/Runtime ]; then
echo "✅ newIDE/app/resources/GDJS/Runtime/"
else
echo "❌ newIDE/app/resources/GDJS/Runtime/ MISSING"
MISSING=1
fi
if [ -d newIDE/app/resources/preview_node_modules/@electron/remote ]; then
echo "✅ newIDE/app/resources/preview_node_modules/@electron/remote"
else
echo "❌ newIDE/app/resources/preview_node_modules/@electron/remote MISSING"
MISSING=1
fi
if [ "$MISSING" -eq 1 ]; then
echo "❌ Verification FAILED"
exit 1
fi
echo "✅ All outputs verified"
- uses: actions/upload-artifact@v4
with:
name: app-build
path: |
newIDE/app/build/
newIDE/app/resources/GDJS/
newIDE/app/resources/preview_node_modules/
platform-build:
name: Build (${{ matrix.name }})
needs: [app-build]
runs-on: ${{ matrix.os }}
continue-on-error: true
timeout-minutes: 30
strategy:
matrix:
include:
- name: linux-x64
os: ubuntu-latest
builder-args: --linux --x64
- name: linux-arm64
os: ubuntu-latest
builder-args: --linux --arm64
- name: mac-universal
os: macos-latest
builder-args: --mac --universal
- name: win-x64
os: windows-latest
builder-args: --win --x64
- name: win-arm64
os: windows-11-arm64
builder-args: --win --arm64
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Cache electron-app node_modules
uses: actions/cache@v4
with:
path: newIDE/electron-app/node_modules
key: electron-app-v1-${{ hashFiles('newIDE/electron-app/package-lock.json') }}
- name: Install libfuse2 (Linux only)
if: startsWith(matrix.os, 'ubuntu')
run: sudo apt-get install -y libfuse2
- uses: actions/download-artifact@v4
with:
name: gdjs-build
path: newIDE/app/public/
- uses: actions/download-artifact@v4
with:
name: app-build
- name: Install electron-app dependencies
working-directory: newIDE/electron-app
run: npm ci
- name: Copy React bundle to app/www
working-directory: newIDE/electron-app
run: node scripts/app-build.js --skip-app-build
- name: Build platform installer
working-directory: newIDE/electron-app
run: npx electron-builder --config=electron-builder-config.js ${{ matrix.builder-args }} --publish never
- uses: actions/upload-artifact@v4
with:
name: gdevelop-${{ matrix.name }}
path: newIDE/electron-app/dist/
gdevelop-js-build:
name: GDevelop.js Emscripten build
needs: validate
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Cache GDevelop.js node_modules
id: cache-gdjs
uses: actions/cache@v4
with:
path: GDevelop.js/node_modules
key: gdjs-v1-${{ hashFiles('GDevelop.js/package-lock.json') }}
- name: Install Emscripten SDK
uses: mymindstorm/setup-emsdk@v1
- name: Install GDevelop.js dependencies
working-directory: GDevelop.js
run: npm ci --ignore-scripts
- name: Build GDevelop.js (Emscripten → WASM)
working-directory: GDevelop.js
run: npm run build
- name: Verify build artifacts
run: |
echo "=== GDevelop.js build artifact verification ==="
test -f newIDE/app/public/libGD.js \
|| { echo "❌ libGD.js not found in newIDE/app/public/"; exit 1; }
test -f newIDE/app/public/libGD.wasm \
|| { echo "❌ libGD.wasm not found in newIDE/app/public/"; exit 1; }
JS_SIZE=$(stat -c%s newIDE/app/public/libGD.js 2>/dev/null || echo 0)
WASM_SIZE=$(stat -c%s newIDE/app/public/libGD.wasm 2>/dev/null || echo 0)
MAX_SIZE=2097152
echo "libGD.js: ${JS_SIZE} bytes"
echo "libGD.wasm: ${WASM_SIZE} bytes"
if [ "${JS_SIZE}" -gt "${MAX_SIZE}" ]; then
echo "❌ libGD.js exceeds 2 MiB limit (${JS_SIZE} > ${MAX_SIZE})"
exit 1
fi
echo "✅ libGD.js size check passed (under 2 MiB)"
- uses: actions/upload-artifact@v4
with:
name: gdjs-build
path: newIDE/app/public/
app-build:
name: App + GDJS Runtime Build
needs: [validate, gdevelop-js-build]
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Cache newIDE/app node_modules
id: cache-app
uses: actions/cache@v4
with:
path: newIDE/app/node_modules
key: newide-app-v1-${{ hashFiles('newIDE/app/package-lock.json') }}
- name: Cache GDJS node_modules
id: cache-gdjs-rt
uses: actions/cache@v4
with:
path: GDJS/node_modules
key: gdjs-rt-v1-${{ hashFiles('GDJS/package-lock.json') }}
- name: Cache electron-app node_modules
id: cache-electron
uses: actions/cache@v4
with:
path: newIDE/electron-app/node_modules
key: electron-app-v1-${{ hashFiles('newIDE/electron-app/package-lock.json') }}
- uses: actions/download-artifact@v4
with:
name: gdjs-build
path: newIDE/app/public/
- name: Install app dependencies
working-directory: newIDE/app
run: npm ci
- name: Build React app
working-directory: newIDE/app
run: npm run build
- name: Install Electron dependencies
working-directory: newIDE/electron-app
run: npm ci
- name: Verify outputs
run: |
echo "=== App Build Verification ==="
MISSING=0
if [ -f newIDE/app/build/index.html ]; then
echo "✅ newIDE/app/build/index.html"
else
echo "❌ newIDE/app/build/index.html MISSING"
MISSING=1
fi
if [ -d newIDE/app/resources/GDJS/Runtime ]; then
echo "✅ newIDE/app/resources/GDJS/Runtime/"
else
echo "❌ newIDE/app/resources/GDJS/Runtime/ MISSING"
MISSING=1
fi
if [ -d newIDE/app/resources/preview_node_modules/@electron/remote ]; then
echo "✅ newIDE/app/resources/preview_node_modules/@electron/remote"
else
echo "❌ newIDE/app/resources/preview_node_modules/@electron/remote MISSING"
MISSING=1
fi
if [ "$MISSING" -eq 1 ]; then
echo "❌ Verification FAILED"
exit 1
fi
echo "✅ All outputs verified"
- uses: actions/upload-artifact@v4
with:
name: app-build
path: |
newIDE/app/build/
newIDE/app/resources/GDJS/
newIDE/app/resources/preview_node_modules/
platform-build:
name: Build (${{ matrix.name }})
needs: [app-build]
runs-on: ${{ matrix.os }}
continue-on-error: true
timeout-minutes: 30
strategy:
matrix:
include:
- name: linux-x64
os: ubuntu-latest
builder-args: --linux --x64
- name: linux-arm64
os: ubuntu-latest
builder-args: --linux --arm64
- name: mac-universal
os: macos-latest
builder-args: --mac --universal
- name: win-x64
os: windows-latest
builder-args: --win --x64
- name: win-arm64
os: windows-11-arm64
builder-args: --win --arm64
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Cache electron-app node_modules
uses: actions/cache@v4
with:
path: newIDE/electron-app/node_modules
key: electron-app-v1-${{ hashFiles('newIDE/electron-app/package-lock.json') }}
- name: Install libfuse2 (Linux only)
if: startsWith(matrix.os, 'ubuntu')
run: sudo apt-get install -y libfuse2
- uses: actions/download-artifact@v4
with:
name: gdjs-build
path: newIDE/app/public/
- uses: actions/download-artifact@v4
with:
name: app-build
- name: Install electron-app dependencies
working-directory: newIDE/electron-app
run: npm ci
- name: Copy React bundle to app/www
working-directory: newIDE/electron-app
run: node scripts/app-build.js --skip-app-build
- name: Build platform installer
working-directory: newIDE/electron-app
run: npx electron-builder --config=electron-builder-config.js ${{ matrix.builder-args }} --publish never
- uses: actions/upload-artifact@v4
with:
name: gdevelop-${{ matrix.name }}
path: newIDE/electron-app/dist/
release:
name: ${{ github.ref_type == 'tag' && 'Create release' || 'Create pre-release' }}
needs: [validate, gdevelop-js-build, app-build, platform-build]
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
release_url: ${{ steps.create.outputs.url }}
steps:
- uses: actions/checkout@v4
with:
# Full history + tags for changelog generation across the
# previous-to-current-tag range.
fetch-depth: 0
fetch-tags: true
- name: Determine release tag and type
id: info
run: |
IS_PRERELEASE="false"
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
TAG="${{ inputs.tag }}"
elif [[ "${{ github.ref_type }}" == "tag" ]]; then
TAG="${GITHUB_REF_NAME}"
else
# Push to master — auto-generate a unique pre-release tag.
APP_VERSION=$(node -e \
"console.log(require('./newIDE/electron-app/app/package.json').version)")
TS=$(date -u +%Y%m%d%H%M%S)
SHA="${GITHUB_SHA:0:7}"
TAG="v${APP_VERSION}-pre.${TS}.${SHA}"
IS_PRERELEASE="true"
fi
if [ -z "$TAG" ]; then
echo "::error::No tag specified. For workflow_dispatch, provide the 'tag' input."
exit 1
fi
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "is_prerelease=${IS_PRERELEASE}" >> "$GITHUB_OUTPUT"
echo "Release tag: ${TAG} (prerelease: ${IS_PRERELEASE})"
- name: Generate release notes
id: notes
run: |
TAG="${{ steps.info.outputs.tag }}"
IS_PRERELEASE="${{ steps.info.outputs.is_prerelease }}"
PREV_TAG=$(git tag --sort=-creatordate | sed -n '2p')
if [ -n "$PREV_TAG" ]; then
RANGE="${PREV_TAG}..HEAD"
else
RANGE="HEAD"
fi
echo "Changelog range: ${RANGE}"
CHANGELOG=$(git log --oneline --no-merges "$RANGE" 2>/dev/null || \
git log --oneline --no-merges --max-count=30)
{
echo "## ${IS_PRERELEASE:+Pre-release }Changes"
echo
echo '```'
echo "$CHANGELOG"
echo '```'
} > /tmp/release-notes.md
echo "notes_path=/tmp/release-notes.md" >> "$GITHUB_OUTPUT"
- name: Download platform artifacts
uses: actions/download-artifact@v4
with:
pattern: gdevelop-*
merge-multiple: true
- name: Create GitHub Release
id: create
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.info.outputs.tag }}
name: ${{ steps.info.outputs.tag }}
body_path: ${{ steps.notes.outputs.notes_path }}
prerelease: ${{ steps.info.outputs.is_prerelease == 'true' }}
# Pre-releases are published immediately (not draft) so consumers
# can download them. Tagged releases are draft-only for review.
draft: ${{ steps.info.outputs.is_prerelease != 'true' }}
generate_release_notes: false
files: |
*.AppImage
*.deb
*.dmg
*.exe
*.zip