chore: initial bootstrap — 5 reusable workflows + scaffolding

Public-readable Tier-1 exception (alongside sporel-meta) for the
practical reason that community pipelines need to call into these
workflows without per-repo deploy-token setup.

5 reusable workflows moved from sporel-meta/.gitea/workflows/ to here
(Plan-B.1: privacy-fix for sporel-meta which stays private with the
proprietary design docs).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Axel Meyer
2026-04-29 23:37:38 +02:00
commit 506c1797a8
8 changed files with 454 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
# Reusable workflow: C-Lib (Cat 2 per ADR-0025)
# Build C-Lib gegen Engine-Release-Headers + Import-Lib → on-tag zip
name: C-Lib CI
on:
workflow_call:
inputs:
engine_release_tag:
description: "Engine release to build against (e.g. 'v0.1.0' or 'latest')"
type: string
default: "latest"
build_type:
type: string
default: "Release"
jobs:
build:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download engine release (headers + import lib)
shell: bash
env:
GITEA_API: "https://git.davoryn.de/api/v1"
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
run: |
set -euo pipefail
OS_TAG="${{ matrix.os }}"
OS_TAG="${OS_TAG//-latest/}"
TAG="${{ inputs.engine_release_tag }}"
if [ "$TAG" = "latest" ]; then
TAG=$(curl -sH "Authorization: token ${GITEA_TOKEN}" \
"${GITEA_API}/repos/calic/sporel-engine/releases/latest" | \
grep -oP '"tag_name":"\K[^"]+')
fi
ARTEFACT="sporel-engine-${TAG}-${OS_TAG}.zip"
mkdir -p engine-release
curl -sL -H "Authorization: token ${GITEA_TOKEN}" \
-o "engine-release/${ARTEFACT}" \
"https://git.davoryn.de/calic/sporel-engine/releases/download/${TAG}/${ARTEFACT}"
cd engine-release && unzip "${ARTEFACT}" && rm "${ARTEFACT}"
echo "SPOREL_ROOT=${{ github.workspace }}/engine-release" >> "$GITHUB_ENV"
- name: Manifest validation (lib.json)
shell: bash
run: |
if [ -f lib.json ]; then
python3 -c "import json,sys; d=json.load(open('lib.json'));
assert d.get('id'), 'missing id'; assert d.get('version'), 'missing version';
assert d.get('api'), 'missing api field (per ADR-0022)';
print('lib.json OK:', d['id'], d['version'])"
else
echo "::warning::no lib.json found"
fi
- name: README presence (per ADR-0026)
shell: bash
run: |
test -s README.md || (echo "::error::README.md missing or empty" && exit 1)
- name: Configure CMake
run: cmake -B build -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DSPOREL_ROOT=${{ env.SPOREL_ROOT }}
- name: Build
run: cmake --build build --config ${{ inputs.build_type }} --parallel
- name: Package lib (on tag)
if: startsWith(github.ref, 'refs/tags/')
shell: bash
run: |
OS_TAG="${{ matrix.os }}"
OS_TAG="${OS_TAG//-latest/}"
ARTEFACT_NAME="${GITHUB_REPOSITORY##*/}-${GITHUB_REF_NAME}-${OS_TAG}.zip"
cmake --install build --prefix=${{ github.workspace }}/lib-install --config ${{ inputs.build_type }}
cd lib-install && zip -r "../${ARTEFACT_NAME}" . && cd ..
echo "ARTEFACT_NAME=${ARTEFACT_NAME}" >> "$GITHUB_ENV"
- name: Upload to Gitea Release (on tag)
if: startsWith(github.ref, 'refs/tags/')
uses: akkuman/gitea-release-action@v1
with:
files: ${{ env.ARTEFACT_NAME }}

View File

@@ -0,0 +1,43 @@
# Reusable workflow: Meta / Docs / Tool (Cat 5 per ADR-0025)
# Markdown lint + internal link check
name: Docs CI
on:
workflow_call:
inputs:
lint_paths:
description: "Glob of paths to lint"
type: string
default: "**/*.md"
ignore_paths:
description: "Glob of paths to skip (e.g. archive)"
type: string
default: "**/archive/**"
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Node + markdownlint-cli2
run: |
sudo apt-get update -qq
sudo apt-get install -y nodejs npm
sudo npm install -g markdownlint-cli2
- name: Run markdownlint
run: |
markdownlint-cli2 "${{ inputs.lint_paths }}" "!${{ inputs.ignore_paths }}" || \
(echo "::warning::markdown lint warnings (non-blocking)" && true)
- name: Internal link check (lychee)
uses: lycheeverse/lychee-action@v2
with:
args: --offline --no-progress --include-fragments "**/*.md" --exclude-path "**/archive/**"
fail: true
- name: README presence (root)
run: |
test -s README.md || (echo "::warning::no README.md at repo root" && true)

View File

@@ -0,0 +1,59 @@
# Reusable workflow: Engine + Launcher (Cat 1 per ADR-0025)
# C-Build (Windows + Linux) → smoke tests → on-tag: release artefacts
name: Engine + Launcher CI
on:
workflow_call:
inputs:
build_type:
description: "CMake build type"
type: string
default: "Release"
smoke_test_script:
description: "Path to smoke-test script (relative to repo root); empty = skip"
type: string
default: "tests/smoke.sh"
jobs:
build:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Configure CMake
run: cmake -B build -DCMAKE_BUILD_TYPE=${{ inputs.build_type }}
- name: Build
run: cmake --build build --config ${{ inputs.build_type }} --parallel
- name: Smoke tests
if: hashFiles(inputs.smoke_test_script) != ''
shell: bash
run: |
chmod +x "${{ inputs.smoke_test_script }}" || true
"${{ inputs.smoke_test_script }}"
- name: Install (on tag)
if: startsWith(github.ref, 'refs/tags/')
run: cmake --install build --prefix=${{ github.workspace }}/install --config ${{ inputs.build_type }}
- name: Package release artefacts (on tag)
if: startsWith(github.ref, 'refs/tags/')
shell: bash
run: |
OS_TAG="${{ matrix.os }}"
OS_TAG="${OS_TAG//-latest/}"
ARTEFACT_NAME="${GITHUB_REPOSITORY##*/}-${GITHUB_REF_NAME}-${OS_TAG}.zip"
cd install && zip -r "../${ARTEFACT_NAME}" . && cd ..
echo "ARTEFACT_NAME=${ARTEFACT_NAME}" >> "$GITHUB_ENV"
- name: Upload to Gitea Release (on tag)
if: startsWith(github.ref, 'refs/tags/')
uses: akkuman/gitea-release-action@v1
with:
files: ${{ env.ARTEFACT_NAME }}

View File

@@ -0,0 +1,83 @@
# Reusable workflow: Hybrid C+Lua Module (Cat 4 per ADR-0025)
# Combines Cat 2 (C-build) + Cat 3 (Lua-lint) for modules with native code.
name: Hybrid Module CI
on:
workflow_call:
inputs:
engine_release_tag:
type: string
default: "latest"
build_type:
type: string
default: "Release"
lint_target:
type: string
default: "lua/ scripts/ init.lua"
jobs:
lint-lua:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install luacheck
run: sudo apt-get install -y luarocks && sudo luarocks install luacheck
- name: Run luacheck
run: luacheck ${{ inputs.lint_target }} --no-unused-args --no-self
- name: Manifest + README + LICENSE
run: |
for f in manifest.module manifest.core lib.json dependencies.json; do
[ -f "$f" ] && python3 -c "import json; json.load(open('$f'))"
done
test -s README.md && test -s LICENSE
build-c:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Download engine release
shell: bash
env:
GITEA_API: "https://git.davoryn.de/api/v1"
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
run: |
set -euo pipefail
OS_TAG="${{ matrix.os }}"
OS_TAG="${OS_TAG//-latest/}"
TAG="${{ inputs.engine_release_tag }}"
if [ "$TAG" = "latest" ]; then
TAG=$(curl -sH "Authorization: token ${GITEA_TOKEN}" \
"${GITEA_API}/repos/calic/sporel-engine/releases/latest" | \
grep -oP '"tag_name":"\K[^"]+')
fi
ARTEFACT="sporel-engine-${TAG}-${OS_TAG}.zip"
mkdir -p engine-release
curl -sL -H "Authorization: token ${GITEA_TOKEN}" \
-o "engine-release/${ARTEFACT}" \
"https://git.davoryn.de/calic/sporel-engine/releases/download/${TAG}/${ARTEFACT}"
cd engine-release && unzip "${ARTEFACT}"
echo "SPOREL_ROOT=${{ github.workspace }}/engine-release" >> "$GITHUB_ENV"
- name: Build
run: |
cmake -B build -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DSPOREL_ROOT=${{ env.SPOREL_ROOT }}
cmake --build build --config ${{ inputs.build_type }} --parallel
package:
needs: [lint-lua, build-c]
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Package as zip (multi-OS-pre-built)
run: |
ARTEFACT="${GITHUB_REPOSITORY##*/}-${GITHUB_REF_NAME}.zip"
zip -r "$ARTEFACT" . -x "*.git/*" -x "*.gitea/*" -x "tests/*" -x "build/*"
echo "ARTEFACT_NAME=$ARTEFACT" >> "$GITHUB_ENV"
- name: Upload to Gitea Release
uses: akkuman/gitea-release-action@v1
with:
files: ${{ env.ARTEFACT_NAME }}

View File

@@ -0,0 +1,70 @@
# Reusable workflow: Lua-Lib / Module (Cat 3 per ADR-0025)
# luacheck + JSON validation + on-tag zip
name: Lua CI
on:
workflow_call:
inputs:
lint_target:
description: "Path glob to lint (default: lua/ + init.lua + scripts/)"
type: string
default: "lua/ scripts/ init.lua"
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install luacheck
run: |
sudo apt-get update -qq
sudo apt-get install -y luarocks
sudo luarocks install luacheck
- name: Run luacheck
run: |
luacheck ${{ inputs.lint_target }} --no-unused-args --no-self || \
(echo "::error::luacheck failures" && exit 1)
- name: Manifest validation
shell: bash
run: |
for manifest in manifest.module manifest.core lib.json; do
if [ -f "$manifest" ]; then
python3 -c "import json; json.load(open('$manifest'))" || \
(echo "::error::$manifest invalid JSON" && exit 1)
echo "$manifest: valid JSON"
fi
done
if [ -f dependencies.json ]; then
python3 -c "import json; d=json.load(open('dependencies.json')); assert 'required' in d or 'suggests' in d"
fi
- name: README presence (per ADR-0026)
run: |
test -s README.md || (echo "::error::README.md missing or empty" && exit 1)
- name: LICENSE presence (per ADR-0027)
run: |
test -s LICENSE || (echo "::error::LICENSE missing or empty" && exit 1)
package:
needs: lint
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Package as zip
run: |
ARTEFACT="${GITHUB_REPOSITORY##*/}-${GITHUB_REF_NAME}.zip"
zip -r "$ARTEFACT" . -x "*.git/*" -x "*.gitea/*" -x "tests/*"
echo "ARTEFACT_NAME=$ARTEFACT" >> "$GITHUB_ENV"
- name: Upload to Gitea Release
uses: akkuman/gitea-release-action@v1
with:
files: ${{ env.ARTEFACT_NAME }}