platform files refact

This commit is contained in:
2026-08-06 10:00:34 +02:00
parent 45450b2d0a
commit 265e1092a1
45 changed files with 219 additions and 205 deletions
@@ -29,13 +29,13 @@ module WarpEngine
return render json: { error: "Forbidden" }, status: :forbidden
end
input = WarpEngine::UpdateInputDto.new(
input = WarpEngine::PublishInputDto.new(
platform: params[:platform],
name: params[:name],
version: params[:version]
)
WarpEngine::UpdateService.new.update(input)
WarpEngine::PublishService.new.publish(input)
claim_software_ownership(params[:name])
render json: { published: true, name: params[:name], platform: params[:platform], version: params[:version] }
@@ -1,5 +1,5 @@
module WarpEngine
UpdateInputDto = Struct.new(:platform, :name, :version, keyword_init: true) do
PublishInputDto = Struct.new(:platform, :name, :version, keyword_init: true) do
def initialize(platform:, name:, version: nil)
super
end
@@ -2,7 +2,7 @@ require "zip"
require "fileutils"
module WarpEngine
module SoftwareUpdater
module Platforms
module ArchiveExtraction
private
@@ -1,5 +1,5 @@
module WarpEngine
module SoftwareUpdater
module Platforms
module Builds
module BuildCartridge
extend ActiveSupport::Concern
@@ -1,5 +1,5 @@
module WarpEngine
module SoftwareUpdater
module Platforms
module Builds
module BuildDocs
extend ActiveSupport::Concern
@@ -1,5 +1,5 @@
module WarpEngine
module SoftwareUpdater
module Platforms
module Builds
module BuildLinuxX64
extend ActiveSupport::Concern
@@ -1,5 +1,5 @@
module WarpEngine
module SoftwareUpdater
module Platforms
module Builds
module BuildMacArm64
extend ActiveSupport::Concern
@@ -1,5 +1,5 @@
module WarpEngine
module SoftwareUpdater
module Platforms
module Builds
module BuildMacUniversal
extend ActiveSupport::Concern
@@ -1,5 +1,5 @@
module WarpEngine
module SoftwareUpdater
module Platforms
module Builds
module BuildMacX64
extend ActiveSupport::Concern
@@ -1,5 +1,5 @@
module WarpEngine
module SoftwareUpdater
module Platforms
module Builds
module BuildSource
extend ActiveSupport::Concern
@@ -1,5 +1,5 @@
module WarpEngine
module SoftwareUpdater
module Platforms
module Builds
module BuildWeb
extend ActiveSupport::Concern
@@ -1,5 +1,5 @@
module WarpEngine
module SoftwareUpdater
module Platforms
module Builds
module BuildWinX64
extend ActiveSupport::Concern
@@ -1,5 +1,5 @@
module WarpEngine
module SoftwareUpdater
module Platforms
module Builds
module BuildWinX86
extend ActiveSupport::Concern
@@ -1,7 +1,7 @@
require "json"
module WarpEngine
module SoftwareUpdater
module Platforms
module MetadataParsing
METADATA_KEYS = %i[name title author desc site repo license].freeze
@@ -1,5 +1,5 @@
module WarpEngine
module SoftwareUpdater
module Platforms
module SoftwarePersistence
private
@@ -1,5 +1,5 @@
module WarpEngine
module SoftwareUpdater
module Platforms
module Updatable
extend ActiveSupport::Concern
include ArchiveExtraction
@@ -9,7 +9,7 @@ module WarpEngine
class_methods do
def platform(value = nil)
@platform = value if value
@platform ||= name.demodulize.delete_suffix("Service").downcase
@platform ||= name.deconstantize.demodulize.downcase
end
def label(value = nil)
+2 -2
View File
@@ -2,7 +2,7 @@ module WarpEngine
class BuildsService
def index
platforms = WarpEngine::PlatformLink::SUPPORTED_PLATFORMS.each_with_object({}) do |platform, hash|
service_class = "WarpEngine::SoftwareUpdater::#{platform.camelize}Service".constantize
service_class = "WarpEngine::Platforms::#{platform.camelize}::Service".constantize
hash[platform] = {
label: service_class.label,
kinds: service_class.expected_kinds
@@ -16,7 +16,7 @@ module WarpEngine
def show(name)
software = WarpEngine::Software.find_by!(name: name)
service_class = "WarpEngine::SoftwareUpdater::#{software.platform.camelize}Service".constantize
service_class = "WarpEngine::Platforms::#{software.platform.camelize}::Service".constantize
expected = service_class.expected_kinds
releases = software.releases.includes(:release_assets).order(updated_at: :desc)
@@ -2,8 +2,8 @@ require "erb"
module WarpEngine
# Renders the /build/config platform templates: the pipeline logic lives in
# lib/warp_engine/ci_templates/<platform>.yaml.erb, the per-platform builder
# images come from WarpEngine.config.ci_platforms.
# app/services/warp_engine/platforms/<platform>/pipeline.yaml.erb, the
# per-platform builder images come from WarpEngine.config.ci_platforms.
class CiConfigService
PLATFORM_FORMAT = /\A[a-z0-9_-]+\z/
@@ -15,7 +15,7 @@ module WarpEngine
spec = platform_spec(platform)
return nil if spec.nil?
path = templates_dir.join("#{platform}.yaml.erb")
path = templates_dir.join(platform, "pipeline.yaml.erb")
return nil unless path.exist?
ERB.new(path.read, trim_mode: "-").result_with_hash(
@@ -34,7 +34,7 @@ module WarpEngine
end
def templates_dir
WarpEngine::Engine.root.join("lib", "warp_engine", "ci_templates")
WarpEngine::Engine.root.join("app", "services", "warp_engine", "platforms")
end
end
end
@@ -0,0 +1,103 @@
# Generated pipeline — WarpEngine /build/config (platform: bevy, name: <%= name %>)
steps:
- name: version
image: alpine
commands:
- apk add --no-cache git jq
- |
if [ -f metadata.json ]; then
VERSION=$(jq -r '.version' metadata.json)
else
VERSION=$(git rev-parse --short HEAD)
fi
BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ "$BRANCH" != "main" ] && [ "$BRANCH" != "master" ]; then
VERSION="dev-$VERSION-$BRANCH"
fi
echo "VERSION is: $VERSION"
echo $VERSION > .version
- name: build
image: <%= builder %>
pull: true
commands:
- |
VERSION=$(cat .version)
mkdir -p dist
cargo build --release --target wasm32-unknown-unknown
wasm-bindgen --target web --no-typescript \
--out-dir dist --out-name game target/wasm32-unknown-unknown/release/<%= name %>.wasm
echo "==> Downloading index.html"
curl -sSL https://git.teletypegames.org/tools/bevy-tools/raw/branch/master/web/index.html -o dist/index.html
echo "==> Packaging HTML/WASM for $VERSION"
zip -r "<%= name %>-$VERSION.html.zip" -j dist/game_bg.wasm dist/game.js dist/index.html
echo "==> Cleaning temporary files"
rm -f dist/game_bg.wasm dist/game.js dist/index.html
- name: binaries
image: <%= builder %>
pull: true
commands:
- |
VERSION=$(cat .version)
# Native binaries. linux-x64: glibc build in the debian-based builder
# image; win-x64: mingw-w64 cross-compile (x86_64-pc-windows-gnu).
# Mac needs osxcross, it is not built here.
# The zip gets the assets/ dir too if the project has one — bevy loads
# it at runtime, it is not embedded in the binary.
set -e
pack_binary() {
P_SLUG="$1"; P_BIN="$2"; P_NAME="$3"
PKG_DIR="<%= name %>-$VERSION-$P_SLUG"
rm -rf "$PKG_DIR" "$PKG_DIR.zip"
mkdir -p "$PKG_DIR"
cp "$P_BIN" "$PKG_DIR/$P_NAME"
chmod +x "$PKG_DIR/$P_NAME"
if [ -d assets ]; then cp -r assets "$PKG_DIR/assets"; fi
zip -qr "$PKG_DIR.zip" "$PKG_DIR"
rm -rf "$PKG_DIR"
echo "==> $PKG_DIR.zip kesz"
}
echo "==> Building linux-x64 binary"
cargo build --release
pack_binary "linux-x64" "target/release/<%= name %>" "<%= name %>"
echo "==> Building win-x64 binary"
CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER=x86_64-w64-mingw32-gcc \
cargo build --release --target x86_64-pc-windows-gnu
pack_binary "win-x64" "target/x86_64-pc-windows-gnu/release/<%= name %>.exe" "<%= name %>.exe"
- name: upload
image: alpine
environment:
UPDATE_SERVER: <%= update_server %>
UPDATE_SECRET:
from_secret: application_token
commands:
- apk add --no-cache curl
- |
VERSION=$(cat .version)
FILE="<%= name %>-$VERSION.html.zip"
META_SRC="metadata.json"
META_DST="<%= name %>-$VERSION.metadata.json"
cp $META_SRC $META_DST
BINS=""
for slug in win-x64 linux-x64; do
[ -f "<%= name %>-$VERSION-$slug.zip" ] && BINS="$BINS <%= name %>-$VERSION-$slug.zip"
done
for f in $FILE $META_DST $BINS; do
curl -fsS -H "X-Update-Secret: $UPDATE_SECRET" \
-F "file=@$f" \
"$UPDATE_SERVER/build/upload?name=<%= name %>&version=$VERSION" || exit 1
done
- name: publish
image: alpine
environment:
UPDATE_SERVER: <%= update_server %>
UPDATE_SECRET:
from_secret: application_token
commands:
- apk add --no-cache curl
- |
VERSION=$(cat .version)
curl -fsS -X POST -H "X-Update-Secret: $UPDATE_SECRET" "$UPDATE_SERVER/build/publish?name=<%= name %>&platform=bevy&version=$VERSION"
@@ -0,0 +1,14 @@
module WarpEngine
module Platforms
module Bevy
class Service
include Updatable
include Builds::BuildWeb
include Builds::BuildWinX64
include Builds::BuildLinuxX64
label "Bevy"
end
end
end
end
@@ -0,0 +1,59 @@
# Generated pipeline — WarpEngine /build/config (platform: c64, name: <%= name %>)
steps:
- name: version
image: alpine
commands:
- |
VERSION=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' metadata.json | head -n 1)
if [ -z "$VERSION" ]; then
echo "ERROR: no \"version\" field in metadata.json!"
exit 1
fi
BRANCH=${CI_COMMIT_BRANCH:-${WOODPECKER_BRANCH}}
BRANCH=$(echo "$BRANCH" | tr '/' '-')
if [ "$BRANCH" != "main" ] && [ "$BRANCH" != "master" ] && [ -n "$BRANCH" ]; then
VERSION="dev-$VERSION-$BRANCH"
fi
echo "VERSION is: $VERSION"
echo $VERSION > .version
- name: build
image: <%= builder %>
commands:
- |
VERSION=$(cat .version)
acme -f cbm -o <%= name %>.prg main.asm
echo "==> Creating versioned files for $VERSION"
cp <%= name %>.prg <%= name %>-$VERSION.prg
cp metadata.json <%= name %>-$VERSION.metadata.json
ls -lh <%= name %>-$VERSION.*
- name: artifact
image: alpine
environment:
UPDATE_SERVER: <%= update_server %>
UPDATE_SECRET:
from_secret: application_token
commands:
- apk add --no-cache curl
- |
VERSION=$(cat .version)
echo "==> Uploading artifacts for version $VERSION"
for f in <%= name %>-$VERSION.prg <%= name %>-$VERSION.metadata.json; do
curl -fsS -H "X-Update-Secret: $UPDATE_SECRET" \
-F "file=@$f" \
"$UPDATE_SERVER/build/upload?name=<%= name %>&version=$VERSION" || exit 1
done
- name: publish
image: alpine
environment:
UPDATE_SERVER: <%= update_server %>
UPDATE_SECRET:
from_secret: application_token
commands:
- apk add --no-cache curl
- |
VERSION=$(cat .version)
echo "==> Publishing version $VERSION"
curl -fsS -X POST -H "X-Update-Secret: $UPDATE_SECRET" "$UPDATE_SERVER/build/publish?name=<%= name %>&platform=c64&version=$VERSION"
@@ -0,0 +1,14 @@
module WarpEngine
module Platforms
module C64
class Service
include Updatable
include Builds::BuildCartridge
label "C64"
def cartridge_ext = ".prg"
end
end
end
end
@@ -0,0 +1,95 @@
# Generated pipeline — WarpEngine /build/config (platform: ebitengine, name: <%= name %>)
steps:
- name: version
image: alpine
commands:
- apk add --no-cache git jq
- |
if [ -f metadata.json ]; then
VERSION=$(jq -r '.version' metadata.json)
else
VERSION=$(git rev-parse --short HEAD)
fi
BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ "$BRANCH" != "main" ] && [ "$BRANCH" != "master" ]; then
VERSION="dev-$VERSION-$BRANCH"
fi
echo "VERSION is: $VERSION"
echo $VERSION > .version
- name: build
image: <%= builder %>
pull: true
commands:
- |
VERSION=$(cat .version)
mkdir -p dist
GOOS=js GOARCH=wasm go build -o dist/game.wasm .
cp "$(go env GOROOT)/lib/wasm/wasm_exec.js" dist/wasm_exec.js
echo "==> Downloading index.html"
curl -sSL https://git.teletypegames.org/tools/ebitengine-tools/raw/branch/master/web/index.html -o dist/index.html
echo "==> Packaging HTML/WASM for $VERSION"
zip -r "<%= name %>-$VERSION.html.zip" -j dist/game.wasm dist/wasm_exec.js dist/index.html
echo "==> Cleaning temporary files"
rm -f dist/game.wasm dist/wasm_exec.js dist/index.html
- name: binaries
image: <%= builder %>
pull: true
commands:
- |
VERSION=$(cat .version)
# win-x86 / win-x64: pure Go cross-compile (Windowson nem kell cgo)
# linux-x64: cgo build, linux/amd64 hoston fut (builder image, X11/GL dev libekkel)
# helper: builds one target + zips it with a single root folder
# (unix zip keeps the executable bit)
binary_build() {
B_GOOS="$1"; B_GOARCH="$2"; B_CGO="$3"; B_EXT="$4"; B_TARGET="$5"
PKG_DIR="<%= name %>-$VERSION-$B_TARGET"
echo "==> Building $PKG_DIR"
rm -rf "$PKG_DIR" "$PKG_DIR.zip"
mkdir -p "$PKG_DIR"
CGO_ENABLED=$B_CGO GOOS=$B_GOOS GOARCH=$B_GOARCH go build -o "$PKG_DIR/<%= name %>$B_EXT" .
if [ -f LICENSE ]; then cp LICENSE "$PKG_DIR/"; fi
if [ -f README.md ]; then cp README.md "$PKG_DIR/"; fi
zip -r "$PKG_DIR.zip" "$PKG_DIR" >/dev/null
rm -rf "$PKG_DIR"
echo "==> $PKG_DIR.zip kesz"
}
# CI (linux builder) builds these three:
binary_build "windows" "386" "0" ".exe" "win-x86"
binary_build "windows" "amd64" "0" ".exe" "win-x64"
binary_build "linux" "amd64" "1" "" "linux-x64"
- name: artifact
image: alpine
environment:
UPDATE_SERVER: <%= update_server %>
UPDATE_SECRET:
from_secret: application_token
commands:
- apk add --no-cache curl
- |
VERSION=$(cat .version)
FILE="<%= name %>-$VERSION.html.zip"
META_SRC="metadata.json"
META_DST="<%= name %>-$VERSION.metadata.json"
BINS=$(ls <%= name %>-$VERSION-*.zip 2>/dev/null || true)
cp $META_SRC $META_DST
for f in $FILE $META_DST $BINS; do
curl -fsS -H "X-Update-Secret: $UPDATE_SECRET" \
-F "file=@$f" \
"$UPDATE_SERVER/build/upload?name=<%= name %>&version=$VERSION" || exit 1
done
- name: publish
image: alpine
environment:
UPDATE_SERVER: <%= update_server %>
UPDATE_SECRET:
from_secret: application_token
commands:
- apk add --no-cache curl
- |
VERSION=$(cat .version)
curl -fsS -X POST -H "X-Update-Secret: $UPDATE_SECRET" "$UPDATE_SERVER/build/publish?name=<%= name %>&platform=ebitengine&version=$VERSION"
@@ -0,0 +1,17 @@
module WarpEngine
module Platforms
module Ebitengine
class Service
include Updatable
include Builds::BuildWeb
include Builds::BuildWinX86
include Builds::BuildWinX64
include Builds::BuildLinuxX64
include Builds::BuildMacX64
include Builds::BuildMacArm64
label "Ebitengine"
end
end
end
end
@@ -0,0 +1,100 @@
# Generated pipeline — WarpEngine /build/config (platform: godot, name: <%= name %>)
steps:
- name: version
image: alpine
commands:
- apk add --no-cache git jq
- |
if [ -f metadata.json ]; then
VERSION=$(jq -r '.version' metadata.json)
else
VERSION=$(git rev-parse --short HEAD)
fi
BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ "$BRANCH" != "main" ] && [ "$BRANCH" != "master" ]; then
VERSION="dev-$VERSION-$BRANCH"
fi
echo "VERSION is: $VERSION"
echo $VERSION > .version
- name: build
image: <%= builder %>
commands:
- |
VERSION=$(cat .version)
echo "==> Importing project"
godot --headless --import
echo "==> Exporting web build (Web preset)"
mkdir -p dist/web
godot --headless --export-release "Web" dist/web/index.html
echo "==> Packaging web build for $VERSION"
(cd dist/web && zip -r "../../<%= name %>-$VERSION.html.zip" .)
rm -rf dist/web
- |
VERSION=$(cat .version)
# exports a win/linux target + zips it with a single root folder
# (embed_pck makes the export a single executable)
binary_build() {
B_PRESET="$1"; B_EXT="$2"; B_TARGET="$3"
PKG_DIR="<%= name %>-$VERSION-$B_TARGET"
echo "==> Building $PKG_DIR"
rm -rf "$PKG_DIR" "$PKG_DIR.zip"
mkdir -p "$PKG_DIR"
godot --headless --export-release "$B_PRESET" "$(pwd)/$PKG_DIR/<%= name %>$B_EXT"
if [ -f LICENSE ]; then cp LICENSE "$PKG_DIR/"; fi
if [ -f README.md ]; then cp README.md "$PKG_DIR/"; fi
zip -r "$PKG_DIR.zip" "$PKG_DIR" >/dev/null
rm -rf "$PKG_DIR"
echo "==> $PKG_DIR.zip kesz"
}
# mac: from linux Godot can only export macOS into a .zip (holding the
# .app); repackage it to the root-folder convention (zip -ry keeps
# exec bits and symlinks)
binary_build_mac() {
B_PRESET="$1"; B_TARGET="$2"
PKG_DIR="<%= name %>-$VERSION-$B_TARGET"
echo "==> Building $PKG_DIR"
rm -rf "$PKG_DIR" "$PKG_DIR.zip"
mkdir -p "$PKG_DIR"
godot --headless --export-release "$B_PRESET" "$(pwd)/$PKG_DIR/<%= name %>-mac-tmp.zip"
(cd "$PKG_DIR" && unzip -q "<%= name %>-mac-tmp.zip" && rm "<%= name %>-mac-tmp.zip")
if [ -f LICENSE ]; then cp LICENSE "$PKG_DIR/"; fi
if [ -f README.md ]; then cp README.md "$PKG_DIR/"; fi
zip -ry "$PKG_DIR.zip" "$PKG_DIR" >/dev/null
rm -rf "$PKG_DIR"
echo "==> $PKG_DIR.zip kesz"
}
binary_build "Windows x86" ".exe" "win-x86"
binary_build "Windows x64" ".exe" "win-x64"
binary_build "Linux x64" "" "linux-x64"
binary_build_mac "Mac universal" "mac-universal"
- name: upload
image: alpine
environment:
UPDATE_SERVER: <%= update_server %>
UPDATE_SECRET:
from_secret: application_token
commands:
- apk add --no-cache curl
- |
VERSION=$(cat .version)
cp metadata.json "<%= name %>-$VERSION.metadata.json"
BINS=$(ls <%= name %>-$VERSION-*.zip 2>/dev/null || true)
for f in "<%= name %>-$VERSION.html.zip" "<%= name %>-$VERSION.metadata.json" $BINS; do
curl -fsS -H "X-Update-Secret: $UPDATE_SECRET" \
-F "file=@$f" \
"$UPDATE_SERVER/build/upload?name=<%= name %>&version=$VERSION" || exit 1
done
- name: publish
image: alpine
environment:
UPDATE_SERVER: <%= update_server %>
UPDATE_SECRET:
from_secret: application_token
commands:
- apk add --no-cache curl
- |
VERSION=$(cat .version)
curl -fsS -X POST -H "X-Update-Secret: $UPDATE_SECRET" "$UPDATE_SERVER/build/publish?name=<%= name %>&platform=godot&version=$VERSION"
@@ -0,0 +1,16 @@
module WarpEngine
module Platforms
module Godot
class Service
include Updatable
include Builds::BuildWeb
include Builds::BuildWinX86
include Builds::BuildWinX64
include Builds::BuildLinuxX64
include Builds::BuildMacUniversal
label "Godot"
end
end
end
end
@@ -0,0 +1,174 @@
# Generated pipeline — WarpEngine /build/config (platform: love, name: <%= name %>)
steps:
- name: version
image: alpine
commands:
- apk add --no-cache git jq
- |
if [ -f metadata.json ]; then
VERSION=$(jq -r '.version' metadata.json)
else
VERSION=$(git rev-parse --short HEAD)
fi
BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ "$BRANCH" != "main" ] && [ "$BRANCH" != "master" ]; then
VERSION="dev-$VERSION-$BRANCH"
fi
echo "VERSION is: $VERSION"
echo $VERSION > .version
- name: export
image: <%= builder %>
pull: true
commands:
- |
VERSION=$(cat .version)
mkdir -p dist
echo "==> Building .love package"
zip -r dist/<%= name %>.love . \
--exclude "*.git*" \
--exclude "bin/*" \
--exclude "dist/*" \
--exclude "Makefile" \
--exclude ".version" \
--exclude "metadata.json" \
--exclude "*.zip"
mkdir -p dist/web
# The love-builder CI image pre-fetches love.js here; local builds
# fall back to GitHub.
if [ -f /opt/lovejs.zip ]; then
echo "==> Using cached love.js (/opt/lovejs.zip)"
cp /opt/lovejs.zip dist/lovejs.zip
else
echo "==> Downloading love.js (2dengine)"
curl -sSL https://github.com/2dengine/love.js/archive/refs/heads/master.zip -o dist/lovejs.zip
fi
unzip -o dist/lovejs.zip -d dist/lovejs-src
rm -f dist/lovejs.zip
echo "==> Assembling web bundle"
cp -r dist/lovejs-src/*/. dist/web/
rm -rf dist/lovejs-src
cp dist/<%= name %>.love dist/web/<%= name %>.love
echo "==> Patching player.js"
sed -i.bak "s|uri = 'nogame\.love'|uri = '<%= name %>.love'|g" dist/web/player.js && rm dist/web/player.js.bak
echo "==> Patching index.html"
sed -i.bak 's|<base href="/play/">|<base href="/file/<%= name %>-'"$VERSION"'/">|g' dist/web/index.html && rm dist/web/index.html.bak
echo "==> Web build ready in dist/web"
echo "==> Packaging Love2D for $VERSION"
zip -r <%= name %>-$VERSION.love.zip dist/<%= name %>.love
echo "==> Packaging web build for $VERSION"
(cd dist/web && zip -r ../../<%= name %>-$VERSION.html.zip .)
echo "==> Cleaning temporary files"
rm -f dist/<%= name %>.love
rm -rf dist/web
- name: binaries
image: <%= builder %>
pull: true
commands:
- |
VERSION=$(cat .version)
# The export step deleted the .love, rebuild it here (in make the
# binary-* targets' love prerequisite did the same).
mkdir -p dist
zip -r dist/<%= name %>.love . \
--exclude "*.git*" \
--exclude "bin/*" \
--exclude "dist/*" \
--exclude "Makefile" \
--exclude ".version" \
--exclude "metadata.json" \
--exclude "*.zip"
# The love-builder CI image pre-fetches the dist files to
# /opt/love-dist; local builds fall back to GitHub.
fetch_love() {
if [ -f "/opt/love-dist/$1" ]; then
echo "==> Using cached $1"
cp "/opt/love-dist/$1" "dist/$1"
elif [ ! -f "dist/$1" ]; then
echo "==> Downloading $1"
curl -sSL "https://github.com/love2d/love/releases/download/11.5/$1" -o "dist/$1"
fi
}
echo "==> Fusing windows binary"
fetch_love love-11.5-win64.zip
PKG_DIR="<%= name %>-$VERSION-win-x64"
rm -rf "$PKG_DIR" "$PKG_DIR.zip" dist/win64
unzip -q dist/love-11.5-win64.zip -d dist/win64
SRC=$(dirname $(find dist/win64 -name love.exe | head -n 1))
mkdir -p "$PKG_DIR"
cat "$SRC/love.exe" dist/<%= name %>.love > "$PKG_DIR/<%= name %>.exe"
cp "$SRC"/*.dll "$PKG_DIR/"
cp "$SRC/license.txt" "$PKG_DIR/" 2>/dev/null || true
zip -qr "$PKG_DIR.zip" "$PKG_DIR"
rm -rf "$PKG_DIR" dist/win64
echo "==> $PKG_DIR.zip kesz"
echo "==> Fusing macOS app bundle"
fetch_love love-11.5-macos.zip
PKG_DIR="<%= name %>-$VERSION-mac-universal"
rm -rf "$PKG_DIR" "$PKG_DIR.zip" dist/macos
unzip -q dist/love-11.5-macos.zip -d dist/macos
mkdir -p "$PKG_DIR"
mv dist/macos/love.app "$PKG_DIR/<%= name %>.app"
cp dist/<%= name %>.love "$PKG_DIR/<%= name %>.app/Contents/Resources/"
PLIST="$PKG_DIR/<%= name %>.app/Contents/Info.plist"
sed -i.bak "s|<string>LÖVE</string>|<string><%= name %></string>|g" "$PLIST" && rm "$PLIST.bak"
sed -i.bak "s|org\.love2d\.love|org.teletypegames.<%= name %>|g" "$PLIST" && rm "$PLIST.bak"
zip -qry "$PKG_DIR.zip" "$PKG_DIR"
rm -rf "$PKG_DIR" dist/macos
echo "==> $PKG_DIR.zip kesz"
# The AppImage runtime is glibc-dynamic and cannot run on alpine
# (musl), so we do not run the runtime: the offset is computed from
# readelf (shoff + shentsize*shnum) and the squashfs is extracted
# with unsquashfs -o.
echo "==> Fusing linux AppImage"
fetch_love love-11.5-x86_64.AppImage
PKG_DIR="<%= name %>-$VERSION-linux-x64"
APPIMAGE="dist/love-11.5-x86_64.AppImage"
rm -rf "$PKG_DIR" "$PKG_DIR.zip" squashfs-root dist/game.squashfs dist/runtime
OFFSET=$(readelf -h "$APPIMAGE" | awk '/Start of section headers/{o=$5} /Size of section headers/{s=$5} /Number of section headers/{n=$5} END{print o+s*n}')
unsquashfs -q -o $OFFSET -d squashfs-root "$APPIMAGE" >/dev/null
cat squashfs-root/bin/love dist/<%= name %>.love > squashfs-root/bin/love.fused
mv squashfs-root/bin/love.fused squashfs-root/bin/love
chmod +x squashfs-root/bin/love
mksquashfs squashfs-root dist/game.squashfs -root-owned -noappend -quiet -comp gzip
head -c $OFFSET "$APPIMAGE" > dist/runtime
mkdir -p "$PKG_DIR"
cat dist/runtime dist/game.squashfs > "$PKG_DIR/<%= name %>.AppImage"
chmod +x "$PKG_DIR/<%= name %>.AppImage"
zip -qr "$PKG_DIR.zip" "$PKG_DIR"
rm -rf "$PKG_DIR" squashfs-root dist/game.squashfs dist/runtime
echo "==> $PKG_DIR.zip kesz"
- name: upload
image: alpine
environment:
UPDATE_SERVER: <%= update_server %>
UPDATE_SECRET:
from_secret: application_token
commands:
- apk add --no-cache curl
- |
VERSION=$(cat .version)
cp metadata.json "<%= name %>-$VERSION.metadata.json"
BINS=""
for slug in win-x64 mac-universal linux-x64; do
[ -f "<%= name %>-$VERSION-$slug.zip" ] && BINS="$BINS <%= name %>-$VERSION-$slug.zip"
done
for f in "<%= name %>-$VERSION.love.zip" "<%= name %>-$VERSION.html.zip" "<%= name %>-$VERSION.metadata.json" $BINS; do
curl -fsS -H "X-Update-Secret: $UPDATE_SECRET" \
-F "file=@$f" \
"$UPDATE_SERVER/build/upload?name=<%= name %>&version=$VERSION" || exit 1
done
- name: publish
image: alpine
environment:
UPDATE_SERVER: <%= update_server %>
UPDATE_SECRET:
from_secret: application_token
commands:
- apk add --no-cache curl
- |
VERSION=$(cat .version)
curl -fsS -X POST -H "X-Update-Secret: $UPDATE_SECRET" "$UPDATE_SERVER/build/publish?name=<%= name %>&platform=love&version=$VERSION"
@@ -0,0 +1,15 @@
module WarpEngine
module Platforms
module Love
class Service
include Updatable
include Builds::BuildWeb
include Builds::BuildWinX64
include Builds::BuildLinuxX64
include Builds::BuildMacUniversal
label "LÖVE"
end
end
end
end
@@ -0,0 +1,69 @@
# Generated pipeline — WarpEngine /build/config (platform: phaser, name: <%= name %>)
steps:
- name: version
image: alpine
commands:
- apk add --no-cache git jq
- |
if [ -f metadata.json ]; then
VERSION=$(jq -r '.version' metadata.json)
else
VERSION=$(git rev-parse --short HEAD)
fi
BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ "$BRANCH" != "main" ] && [ "$BRANCH" != "master" ]; then
VERSION="dev-$VERSION-$BRANCH"
fi
echo "VERSION is: $VERSION"
echo $VERSION > .version
- name: build
image: <%= builder %>
commands:
- |
VERSION=$(cat .version)
echo "==> Checking JS syntax"
for f in src/*.js; do node --check $f; done
mkdir -p dist/web
echo "==> Downloading Phaser 3.90.0"
curl -sSL https://cdn.jsdelivr.net/npm/phaser@3.90.0/dist/phaser.min.js -o dist/web/phaser.min.js
echo "==> Downloading index.html"
curl -sSL https://git.teletypegames.org/tools/phaser-tools/raw/branch/master/web/index.html -o dist/web/index.html
echo "==> Bundling game sources"
cat src/*.js > dist/web/game.js
echo "==> Packaging web build for $VERSION"
(cd dist/web && zip -r "../../<%= name %>-$VERSION.html.zip" .)
echo "==> Cleaning temporary files"
rm -rf dist/web
- name: upload
image: alpine
environment:
UPDATE_SERVER: <%= update_server %>
UPDATE_SECRET:
from_secret: application_token
commands:
- apk add --no-cache curl
- |
VERSION=$(cat .version)
FILE="<%= name %>-$VERSION.html.zip"
META_SRC="metadata.json"
META_DST="<%= name %>-$VERSION.metadata.json"
cp $META_SRC $META_DST
for f in $FILE $META_DST; do
curl -fsS -H "X-Update-Secret: $UPDATE_SECRET" \
-F "file=@$f" \
"$UPDATE_SERVER/build/upload?name=<%= name %>&version=$VERSION" || exit 1
done
- name: publish
image: alpine
environment:
UPDATE_SERVER: <%= update_server %>
UPDATE_SECRET:
from_secret: application_token
commands:
- apk add --no-cache curl
- |
VERSION=$(cat .version)
curl -fsS -X POST -H "X-Update-Secret: $UPDATE_SECRET" "$UPDATE_SERVER/build/publish?name=<%= name %>&platform=phaser&version=$VERSION"
@@ -0,0 +1,12 @@
module WarpEngine
module Platforms
module Phaser
class Service
include Updatable
include Builds::BuildWeb
label "Phaser"
end
end
end
end
@@ -0,0 +1,199 @@
# Generated pipeline — WarpEngine /build/config (platform: tic80, name: <%= name %>)
# The version comes from the source (inc/meta/meta.header.lua "-- version:"
# comment) — WarpEngine parses tic80 metadata from the Lua header too, hence
# no metadata.json.
steps:
- name: version
image: alpine
commands:
- |
VERSION=$(sed -n "s/^-- version: //p" inc/meta/meta.header.lua | head -n 1 | tr -d "[:space:]")
BRANCH=${CI_COMMIT_BRANCH:-${WOODPECKER_BRANCH}}
BRANCH=$(echo "$BRANCH" | tr '/' '-')
if [ "$BRANCH" != "main" ] && [ "$BRANCH" != "master" ] && [ -n "$BRANCH" ]; then
VERSION=dev-$VERSION-$BRANCH
fi
echo "VERSION is: $VERSION"
echo $VERSION > .version
- name: lint
image: alpine
commands:
- apk add --no-cache lua5.4 lua5.4-dev luarocks gcc musl-dev
- ln -sf /usr/bin/lua5.4 /usr/bin/lua
- ln -sf /usr/bin/luarocks-5.4 /usr/bin/luarocks
- luarocks install luacheck
- |
echo "==> Merging..."
rm -f /tmp/_lint_combined.lua /tmp/_lint_map.txt
touch /tmp/_lint_combined.lua
line=1
while IFS= read -r f || [ -n "$f" ]; do
f=$(printf '%s' "$f" | tr -d '\r')
[ -z "$f" ] && continue
before=$(wc -l < /tmp/_lint_combined.lua)
cat "inc/$f" >> /tmp/_lint_combined.lua
printf '\n' >> /tmp/_lint_combined.lua
after=$(wc -l < /tmp/_lint_combined.lua)
linecount=$((after - before))
echo "$line $linecount inc/$f" >> /tmp/_lint_map.txt
line=$((line + linecount))
done < <%= name %>.inc
echo "==> luacheck..."
LINT_OUTPUT=$(luacheck --no-max-line-length /tmp/_lint_combined.lua 2>&1 | awk -v map=/tmp/_lint_map.txt '
BEGIN {
NR_map = 0;
while ((getline line < map) > 0) {
n = split(line, a, " ");
start[NR_map] = a[1]+0;
count[NR_map] = a[2]+0;
fname[NR_map] = a[3];
NR_map++;
}
}
/^[^:]+:[0-9]+:[0-9]+:/ {
colon1 = index($0, ":");
rest1 = substr($0, colon1+1);
colon2 = index(rest1, ":");
absline = substr(rest1, 1, colon2-1) + 0;
rest2 = substr(rest1, colon2+1);
colon3 = index(rest2, ":");
col = substr(rest2, 1, colon3-1);
rest = substr(rest2, colon3);
found = 0;
for (i = 0; i < NR_map; i++) {
end_line = start[i] + count[i] -1;
if (absline >= start[i] && absline <= end_line) {
relline = absline - start[i] + 1;
print fname[i] ":" relline ":" col ":" rest;
found = 1;
break;
}
}
if (!found) print $0;
next;
}
{ print }
')
echo "$LINT_OUTPUT"
NUM_ISSUES=$(echo "$LINT_OUTPUT" | grep -cE "^[^:]+:[0-9]+:[0-9]+:" || true)
if [ "$NUM_ISSUES" -gt 0 ]; then
echo "Total: $NUM_ISSUES issue(s) found, commit aborted."
exit 1
else
echo "Checking /tmp/_lint_combined.lua OK"
echo "Total: 0 warnings / 0 errors in 1 file"
fi
rm -f /tmp/_lint_combined.lua /tmp/_lint_map.txt
- name: minify
image: alpine
commands:
- apk add --no-cache lua5.4 curl
- ln -sf /usr/bin/lua5.4 /usr/bin/lua
- |
rm -f <%= name %>.lua
sed 's/\r$//' <%= name %>.inc | while read f; do
cat "inc/$f" >> <%= name %>.lua
echo "" >> <%= name %>.lua
done
test -f minify.lua || { echo "==> Downloading minify.lua"; curl -fsSL https://raw.githubusercontent.com/ztimar31/lua-minify-tic80/refs/heads/master/minify.lua -o minify.lua; }
echo "==> Minifying <%= name %>.lua"
cp <%= name %>.lua <%= name %>.original.lua
lua minify.lua minify <%= name %>.original.lua > <%= name %>.lua
- name: docs
image: alpine
commands:
- apk add --no-cache lua5.4 lua5.4-dev luarocks gcc musl-dev zip
- ln -sf /usr/bin/lua5.4 /usr/bin/lua
- ln -sf /usr/bin/luarocks-5.4 /usr/bin/luarocks
- luarocks install ldoc
- |
VERSION=$(cat .version)
echo "==> Generating docs from <%= name %>.original.lua"
ldoc <%= name %>.original.lua -d docs
echo "==> Zipping docs for version $VERSION"
(cd docs && zip -r ../<%= name %>-$VERSION-docs.zip .)
cp <%= name %>-$VERSION-docs.zip <%= name %>-docs.zip
echo "==> Docs zip created"
- name: export
image: <%= builder %>
environment:
XDG_RUNTIME_DIR: /tmp
commands:
- |
VERSION=$(cat .version)
echo "==> Exporting HTML for version $VERSION"
tic80 --cli --skip --fs=. \
--cmd="load <%= name %>.lua & save <%= name %>-$VERSION & export html <%= name %>-$VERSION.html & exit"
if [ -f "<%= name %>-$VERSION.tic" ]; then
cp <%= name %>-$VERSION.tic <%= name %>.tic
fi
if [ -f "<%= name %>-$VERSION.html.zip" ]; then
cp <%= name %>-$VERSION.html.zip <%= name %>.html.zip
fi
echo "==> Generated files:"
ls -lh <%= name %>-$VERSION.* <%= name %>.tic <%= name %>.html.zip 2>/dev/null || true
- name: binaries
image: <%= builder %>
environment:
XDG_RUNTIME_DIR: /tmp
commands:
- |
VERSION=$(cat .version)
echo "==> Exporting native players for version $VERSION"
tic80 --cli --skip --fs=. \
--cmd="load <%= name %>.lua & export win <%= name %>-win & export linux <%= name %>-linux & export mac <%= name %>-mac & exit"
# unix zip preserves the executable bit
pack_binary() {
SLUG="$1"; SRC_FILE="$2"; DST_FILE="$3"
PKG_DIR="<%= name %>-$VERSION-$SLUG"
rm -rf "$PKG_DIR" "$PKG_DIR.zip"
mkdir -p "$PKG_DIR"
mv "$SRC_FILE" "$PKG_DIR/$DST_FILE"
chmod +x "$PKG_DIR/$DST_FILE"
zip -r "$PKG_DIR.zip" "$PKG_DIR" >/dev/null
rm -rf "$PKG_DIR"
echo "==> $PKG_DIR.zip kesz"
}
pack_binary win-x64 <%= name %>-win.exe <%= name %>.exe
pack_binary linux-x64 <%= name %>-linux <%= name %>
pack_binary mac-x64 <%= name %>-mac <%= name %>
- name: upload
image: alpine
environment:
UPDATE_SERVER: <%= update_server %>
UPDATE_SECRET:
from_secret: application_token
commands:
- apk add --no-cache curl
- |
VERSION=$(cat .version)
echo "==> Uploading artifacts for version $VERSION"
cp <%= name %>.lua <%= name %>-$VERSION.lua
BINS=""
for slug in win-x64 linux-x64 mac-x64; do
[ -f "<%= name %>-$VERSION-$slug.zip" ] && BINS="$BINS <%= name %>-$VERSION-$slug.zip"
done
for f in <%= name %>-$VERSION.lua <%= name %>-$VERSION.tic <%= name %>-$VERSION.html.zip <%= name %>-$VERSION-docs.zip $BINS; do
curl -fsS -H "X-Update-Secret: $UPDATE_SECRET" \
-F "file=@$f" \
"$UPDATE_SERVER/build/upload?name=<%= name %>&version=$VERSION" || exit 1
done
- name: publish
image: alpine
environment:
UPDATE_SERVER: <%= update_server %>
UPDATE_SECRET:
from_secret: application_token
commands:
- apk add --no-cache curl
- |
VERSION=$(cat .version)
echo "==> Publishing version $VERSION"
curl -fsS -X POST -H "X-Update-Secret: $UPDATE_SECRET" "$UPDATE_SERVER/build/publish?name=<%= name %>&platform=tic80&version=$VERSION"
@@ -0,0 +1,40 @@
module WarpEngine
module Platforms
module Tic80
class Service
include Updatable
include Builds::BuildCartridge
include Builds::BuildSource
include Builds::BuildWeb
include Builds::BuildDocs
include Builds::BuildWinX64
include Builds::BuildLinuxX64
include Builds::BuildMacX64
label "TIC-80"
def cartridge_ext = ".tic"
def source_ext = ".lua"
private
def parse_metadata(versioned)
parse_lua_metadata(full_path("#{versioned}.lua"))
end
def parse_lua_metadata(source_path)
metadata = {}
File.foreach(source_path) do |line|
break unless line.start_with?("--")
parts = line[2..].split(":", 2)
next if parts.length != 2
key = parts[0].strip.downcase.to_sym
value = parts[1].strip
metadata[key] = value
end
metadata.slice(*MetadataParsing::METADATA_KEYS)
end
end
end
end
end
@@ -1,11 +1,11 @@
module WarpEngine
class UpdateService
def update(input)
class PublishService
def publish(input)
unless WarpEngine::PlatformLink::SUPPORTED_PLATFORMS.include?(input.platform)
raise ArgumentError, "Unsupported platform: #{input.platform}"
end
"WarpEngine::SoftwareUpdater::#{input.platform.camelize}Service".constantize.new.update(input.name, input.version)
"WarpEngine::Platforms::#{input.platform.camelize}::Service".constantize.new.update(input.name, input.version)
end
end
end
@@ -1,12 +0,0 @@
module WarpEngine
module SoftwareUpdater
class BevyService
include Updatable
include Builds::BuildWeb
include Builds::BuildWinX64
include Builds::BuildLinuxX64
label "Bevy"
end
end
end
@@ -1,12 +0,0 @@
module WarpEngine
module SoftwareUpdater
class C64Service
include Updatable
include Builds::BuildCartridge
label "C64"
def cartridge_ext = ".prg"
end
end
end
@@ -1,15 +0,0 @@
module WarpEngine
module SoftwareUpdater
class EbitengineService
include Updatable
include Builds::BuildWeb
include Builds::BuildWinX86
include Builds::BuildWinX64
include Builds::BuildLinuxX64
include Builds::BuildMacX64
include Builds::BuildMacArm64
label "Ebitengine"
end
end
end
@@ -1,14 +0,0 @@
module WarpEngine
module SoftwareUpdater
class GodotService
include Updatable
include Builds::BuildWeb
include Builds::BuildWinX86
include Builds::BuildWinX64
include Builds::BuildLinuxX64
include Builds::BuildMacUniversal
label "Godot"
end
end
end
@@ -1,13 +0,0 @@
module WarpEngine
module SoftwareUpdater
class LoveService
include Updatable
include Builds::BuildWeb
include Builds::BuildWinX64
include Builds::BuildLinuxX64
include Builds::BuildMacUniversal
label "LÖVE"
end
end
end
@@ -1,10 +0,0 @@
module WarpEngine
module SoftwareUpdater
class PhaserService
include Updatable
include Builds::BuildWeb
label "Phaser"
end
end
end
@@ -1,38 +0,0 @@
module WarpEngine
module SoftwareUpdater
class Tic80Service
include Updatable
include Builds::BuildCartridge
include Builds::BuildSource
include Builds::BuildWeb
include Builds::BuildDocs
include Builds::BuildWinX64
include Builds::BuildLinuxX64
include Builds::BuildMacX64
label "TIC-80"
def cartridge_ext = ".tic"
def source_ext = ".lua"
private
def parse_metadata(versioned)
parse_lua_metadata(full_path("#{versioned}.lua"))
end
def parse_lua_metadata(source_path)
metadata = {}
File.foreach(source_path) do |line|
break unless line.start_with?("--")
parts = line[2..].split(":", 2)
next if parts.length != 2
key = parts[0].strip.downcase.to_sym
value = parts[1].strip
metadata[key] = value
end
metadata.slice(*MetadataParsing::METADATA_KEYS)
end
end
end
end