mirror of
https://github.com/RetroDECK/RetroVERSE.git
synced 2026-10-17 01:26:38 +00:00
Compare commits
8 commits
e8c9aedd2d
...
aa9b6913d6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa9b6913d6 | ||
|
|
f04a209c3b | ||
|
|
908f16e1a3 | ||
|
|
5764a1e939 | ||
|
|
b05ff6e6e5 | ||
|
|
e99b1ae942 | ||
|
|
82d64ca312 | ||
|
|
512ed1821c |
370
.github/workflows/retroverse-build.yml
vendored
370
.github/workflows/retroverse-build.yml
vendored
|
|
@ -9,7 +9,7 @@ on:
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
dry_run:
|
dry_run:
|
||||||
description: 'Dry run - build and validate but do not create release'
|
description: 'Dry run: build and validate but do not create release'
|
||||||
required: false
|
required: false
|
||||||
type: boolean
|
type: boolean
|
||||||
default: true
|
default: true
|
||||||
|
|
@ -25,6 +25,21 @@ permissions:
|
||||||
env:
|
env:
|
||||||
SOURCE_ID: official
|
SOURCE_ID: official
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# The Alchemist is not vendored here. It is checked out from the components
|
||||||
|
# repo at a ref pinned in alchemist/alchemist_ref, so the store repo carries
|
||||||
|
# only its own version_policy.sh (and optional version_pins.sh), which are
|
||||||
|
# overlaid onto the checkout.
|
||||||
|
#
|
||||||
|
# The ref is pinned rather than floating on main: an unpinned tool could
|
||||||
|
# change build output with no commit to this repo, and reuse would not notice
|
||||||
|
# because the fingerprint covers item folders only. A release could then mix
|
||||||
|
# artifacts built by two different tool versions.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
ALCHEMIST_REPO: RetroDECK/components
|
||||||
|
ALCHEMIST_PATH: .alchemist
|
||||||
|
ALCHEMIST_DIR: .alchemist/automation-tools/alchemist
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
# Check whether this event should trigger a build
|
# Check whether this event should trigger a build
|
||||||
|
|
@ -62,7 +77,7 @@ jobs:
|
||||||
|
|
||||||
# PR merge commits always trigger a build
|
# PR merge commits always trigger a build
|
||||||
if echo "$COMMITS" | grep -qiE "^Merge pull request #"; then
|
if echo "$COMMITS" | grep -qiE "^Merge pull request #"; then
|
||||||
echo "PR merge detected - auto-building"
|
echo "PR merge detected: auto-building"
|
||||||
echo "should_build=true" >> $GITHUB_OUTPUT
|
echo "should_build=true" >> $GITHUB_OUTPUT
|
||||||
echo "is_dry_run=false" >> $GITHUB_OUTPUT
|
echo "is_dry_run=false" >> $GITHUB_OUTPUT
|
||||||
exit 0
|
exit 0
|
||||||
|
|
@ -82,7 +97,7 @@ jobs:
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "No build trigger found in commit messages - skipping"
|
echo "No build trigger found in commit messages: skipping"
|
||||||
echo "should_build=false" >> $GITHUB_OUTPUT
|
echo "should_build=false" >> $GITHUB_OUTPUT
|
||||||
echo "is_dry_run=false" >> $GITHUB_OUTPUT
|
echo "is_dry_run=false" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
|
@ -97,6 +112,7 @@ jobs:
|
||||||
outputs:
|
outputs:
|
||||||
branch: ${{ steps.config.outputs.branch }}
|
branch: ${{ steps.config.outputs.branch }}
|
||||||
is_dry_run: ${{ needs.check-trigger.outputs.is_dry_run }}
|
is_dry_run: ${{ needs.check-trigger.outputs.is_dry_run }}
|
||||||
|
alchemist_ref: ${{ steps.alchemist-ref.outputs.ref }}
|
||||||
steps:
|
steps:
|
||||||
- name: Determine build parameters
|
- name: Determine build parameters
|
||||||
id: config
|
id: config
|
||||||
|
|
@ -123,6 +139,48 @@ jobs:
|
||||||
with:
|
with:
|
||||||
ref: ${{ steps.config.outputs.branch }}
|
ref: ${{ steps.config.outputs.branch }}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# The pinned Alchemist ref. A tag or full commit SHA.
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
- name: Read pinned Alchemist ref
|
||||||
|
id: alchemist-ref
|
||||||
|
run: |
|
||||||
|
REF_FILE="alchemist/alchemist_ref"
|
||||||
|
|
||||||
|
if [[ ! -f "$REF_FILE" ]]; then
|
||||||
|
echo "ERROR: $REF_FILE not found: the Alchemist ref must be pinned in this repo"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
REF=$(grep -vE '^\s*(#|$)' "$REF_FILE" | head -n1 | tr -d '[:space:]')
|
||||||
|
|
||||||
|
if [[ -z "$REF" ]]; then
|
||||||
|
echo "ERROR: $REF_FILE is empty"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$REF" in
|
||||||
|
main|cooker|epicure|HEAD)
|
||||||
|
echo "ERROR: '$REF' is a branch, not a pin. Use a tag or a full commit SHA."
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "ref=$REF" >> $GITHUB_OUTPUT
|
||||||
|
echo "Alchemist pinned to: $REF"
|
||||||
|
|
||||||
|
- name: Validate version policy is present
|
||||||
|
run: |
|
||||||
|
# load_versions.sh checks for version_policy.sh at source time, before any recipe is read, and exits if it is missing. This repo therefore carries its own even if it holds only data items.
|
||||||
|
# A comment-only file is enough.
|
||||||
|
if [[ ! -f "alchemist/version_policy.sh" ]]; then
|
||||||
|
echo "ERROR: alchemist/version_policy.sh not found"
|
||||||
|
echo "The Alchemist exits at source time without it, even when no item needs a policy."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Version policy present"
|
||||||
|
|
||||||
- name: Validate Bash scripts
|
- name: Validate Bash scripts
|
||||||
run: |
|
run: |
|
||||||
echo "Validating Bash scripts..."
|
echo "Validating Bash scripts..."
|
||||||
|
|
@ -219,7 +277,7 @@ jobs:
|
||||||
;;
|
;;
|
||||||
component)
|
component)
|
||||||
if [[ "$file_count" -ne 0 ]]; then
|
if [[ "$file_count" -ne 0 ]]; then
|
||||||
fail "class 'component' must not declare files[] - deployment belongs to the external component installer"
|
fail "class 'component' must not declare files[]: deployment belongs to the external component installer"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Store version is read from retroverse_manifest.json for both classes so the aggregator needs no class-specific logic.
|
# Store version is read from retroverse_manifest.json for both classes so the aggregator needs no class-specific logic.
|
||||||
|
|
@ -327,6 +385,7 @@ jobs:
|
||||||
outputs:
|
outputs:
|
||||||
items-json: ${{ steps.collect.outputs.items }}
|
items-json: ${{ steps.collect.outputs.items }}
|
||||||
prev-json: ${{ steps.get-prev.outputs.prev }}
|
prev-json: ${{ steps.get-prev.outputs.prev }}
|
||||||
|
prev-alchemist-ref: ${{ steps.get-prev.outputs.prev_alchemist_ref }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
|
|
@ -334,6 +393,41 @@ jobs:
|
||||||
ref: ${{ needs.configure.outputs.branch }}
|
ref: ${{ needs.configure.outputs.branch }}
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# Alchemist checkout. sparse-checkout keeps the rest of the components
|
||||||
|
# repo out of the workspace, and the nested .git is harmless: alchemist.sh
|
||||||
|
# resolves REPO_ROOT from its working directory, which stays the store
|
||||||
|
# repo root, so $REPO_ROOT/$COMPONENT_NAME still points at the item folder.
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
- name: Checkout Alchemist
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
repository: ${{ env.ALCHEMIST_REPO }}
|
||||||
|
ref: ${{ needs.configure.outputs.alchemist_ref }}
|
||||||
|
path: ${{ env.ALCHEMIST_PATH }}
|
||||||
|
sparse-checkout: automation-tools/alchemist
|
||||||
|
sparse-checkout-cone-mode: true
|
||||||
|
|
||||||
|
- name: Overlay store version policy
|
||||||
|
run: |
|
||||||
|
# version_policy.sh and version_pins.sh are read from the Alchemist's
|
||||||
|
# own SCRIPT_DIR, so the components repo's copies arrive with the
|
||||||
|
# checkout and must be replaced by this repo's.
|
||||||
|
if [[ ! -x "$ALCHEMIST_DIR/alchemist.sh" ]]; then
|
||||||
|
echo "ERROR: alchemist.sh not found or not executable at $ALCHEMIST_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$ALCHEMIST_DIR/version_policy.sh" "$ALCHEMIST_DIR/version_pins.sh"
|
||||||
|
cp alchemist/version_policy.sh "$ALCHEMIST_DIR/version_policy.sh"
|
||||||
|
|
||||||
|
if [[ -f alchemist/version_pins.sh ]]; then
|
||||||
|
cp alchemist/version_pins.sh "$ALCHEMIST_DIR/version_pins.sh"
|
||||||
|
echo "Version pins overlaid"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Alchemist ready at ${{ needs.configure.outputs.alchemist_ref }}"
|
||||||
|
|
||||||
- name: Get previous release metadata
|
- name: Get previous release metadata
|
||||||
id: get-prev
|
id: get-prev
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -347,6 +441,7 @@ jobs:
|
||||||
if [[ -z "$PREV_RELEASE" || "$PREV_RELEASE" == "null" ]]; then
|
if [[ -z "$PREV_RELEASE" || "$PREV_RELEASE" == "null" ]]; then
|
||||||
echo "No previous release found for branch ${BRANCH_NAME}"
|
echo "No previous release found for branch ${BRANCH_NAME}"
|
||||||
echo "prev={}" >> $GITHUB_OUTPUT
|
echo "prev={}" >> $GITHUB_OUTPUT
|
||||||
|
echo "prev_alchemist_ref=" >> $GITHUB_OUTPUT
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -357,9 +452,15 @@ jobs:
|
||||||
PREV=$(jq -c '.items | map({(.id): .}) | add // {}' retroverse.json)
|
PREV=$(jq -c '.items | map({(.id): .}) | add // {}' retroverse.json)
|
||||||
echo "prev=$PREV" >> $GITHUB_OUTPUT
|
echo "prev=$PREV" >> $GITHUB_OUTPUT
|
||||||
echo "Previous inventory: $(jq 'keys | length' <<< "$PREV") items"
|
echo "Previous inventory: $(jq 'keys | length' <<< "$PREV") items"
|
||||||
|
|
||||||
|
# Tool version the previous release was built with. A change to it invalidates every reuse (see setup-matrix)
|
||||||
|
PREV_ALCHEMIST=$(jq -r '.alchemist_ref // ""' retroverse.json)
|
||||||
|
echo "prev_alchemist_ref=$PREV_ALCHEMIST" >> $GITHUB_OUTPUT
|
||||||
|
echo "Previous Alchemist ref: ${PREV_ALCHEMIST:-<unrecorded>}"
|
||||||
else
|
else
|
||||||
echo "No retroverse.json found in previous release"
|
echo "No retroverse.json found in previous release"
|
||||||
echo "prev={}" >> $GITHUB_OUTPUT
|
echo "prev={}" >> $GITHUB_OUTPUT
|
||||||
|
echo "prev_alchemist_ref=" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Check for component items
|
- name: Check for component items
|
||||||
|
|
@ -369,7 +470,7 @@ jobs:
|
||||||
echo "found=true" >> $GITHUB_OUTPUT
|
echo "found=true" >> $GITHUB_OUTPUT
|
||||||
else
|
else
|
||||||
echo "found=false" >> $GITHUB_OUTPUT
|
echo "found=false" >> $GITHUB_OUTPUT
|
||||||
echo "No component items - skipping build dependency install"
|
echo "No component items: skipping build dependency install"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Install build dependencies
|
- name: Install build dependencies
|
||||||
|
|
@ -386,13 +487,13 @@ jobs:
|
||||||
apt)
|
apt)
|
||||||
sudo add-apt-repository -y ppa:flatpak/stable
|
sudo add-apt-repository -y ppa:flatpak/stable
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt install -y flatpak flatpak-builder p7zip-full xmlstarlet bzip2 curl jq unzip zstd
|
sudo apt install -y flatpak flatpak-builder p7zip-full xmlstarlet bzip2 curl jq unzip
|
||||||
;;
|
;;
|
||||||
pacman)
|
pacman)
|
||||||
sudo pacman -Sy --noconfirm flatpak flatpak-builder p7zip xmlstarlet bzip2 curl jq unzip zstd
|
sudo pacman -Sy --noconfirm flatpak flatpak-builder p7zip xmlstarlet bzip2 curl jq unzip
|
||||||
;;
|
;;
|
||||||
dnf)
|
dnf)
|
||||||
sudo dnf install -y flatpak flatpak-builder p7zip p7zip-plugins xmlstarlet bzip2 curl jq unzip zstd
|
sudo dnf install -y flatpak flatpak-builder p7zip p7zip-plugins xmlstarlet bzip2 curl jq unzip
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "ERROR: No supported package manager found (apt, pacman, dnf)"
|
echo "ERROR: No supported package manager found (apt, pacman, dnf)"
|
||||||
|
|
@ -427,7 +528,9 @@ jobs:
|
||||||
|
|
||||||
# -----------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
# Fingerprint covers only what the Alchemist consumes. The store
|
# Fingerprint covers only what the Alchemist consumes. The store
|
||||||
# manifest and artwork are excluded.
|
# manifest and artwork are excluded: they are republished from
|
||||||
|
# source every release, so a description or thumbnail edit must not
|
||||||
|
# invalidate an otherwise identical artifact.
|
||||||
# -----------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
fingerprint=$(git ls-tree -r HEAD -- "$item_dir" \
|
fingerprint=$(git ls-tree -r HEAD -- "$item_dir" \
|
||||||
| grep -v 'retroverse_manifest\.json$' \
|
| grep -v 'retroverse_manifest\.json$' \
|
||||||
|
|
@ -440,7 +543,7 @@ jobs:
|
||||||
# -----------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
upstream=""
|
upstream=""
|
||||||
if [[ "$class" == "component" ]]; then
|
if [[ "$class" == "component" ]]; then
|
||||||
upstream=$(automation-tools/alchemist/alchemist.sh \
|
upstream=$("$ALCHEMIST_DIR/alchemist.sh" \
|
||||||
-f "$item_dir/component_recipe.json" --resolve-versions)
|
-f "$item_dir/component_recipe.json" --resolve-versions)
|
||||||
|
|
||||||
if [[ -z "$upstream" ]]; then
|
if [[ -z "$upstream" ]]; then
|
||||||
|
|
@ -495,7 +598,7 @@ jobs:
|
||||||
PREV: ${{ steps.get-prev.outputs.prev }}
|
PREV: ${{ steps.get-prev.outputs.prev }}
|
||||||
run: |
|
run: |
|
||||||
if [[ "$PREV" == "{}" ]]; then
|
if [[ "$PREV" == "{}" ]]; then
|
||||||
echo "No previous release - skipping bump check"
|
echo "No previous release: skipping bump check"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -512,7 +615,7 @@ jobs:
|
||||||
prev_fp=$(jq -r --arg i "$item" '.[$i].fingerprint // empty' <<< "$PREV")
|
prev_fp=$(jq -r --arg i "$item" '.[$i].fingerprint // empty' <<< "$PREV")
|
||||||
prev_up=$(jq -r --arg i "$item" '.[$i].upstream_version // ""' <<< "$PREV")
|
prev_up=$(jq -r --arg i "$item" '.[$i].upstream_version // ""' <<< "$PREV")
|
||||||
|
|
||||||
# New item - nothing to compare against
|
# New item: nothing to compare against
|
||||||
if [[ -z "$prev_version" ]]; then
|
if [[ -z "$prev_version" ]]; then
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
@ -551,12 +654,42 @@ jobs:
|
||||||
env:
|
env:
|
||||||
CURRENT: ${{ needs.collect-metadata.outputs.items-json }}
|
CURRENT: ${{ needs.collect-metadata.outputs.items-json }}
|
||||||
PREV: ${{ needs.collect-metadata.outputs.prev-json }}
|
PREV: ${{ needs.collect-metadata.outputs.prev-json }}
|
||||||
|
ALCHEMIST_REF: ${{ needs.configure.outputs.alchemist_ref }}
|
||||||
|
PREV_ALCHEMIST_REF: ${{ needs.collect-metadata.outputs.prev-alchemist-ref }}
|
||||||
run: |
|
run: |
|
||||||
echo "Current: $(jq 'keys | length' <<< "$CURRENT") items"
|
echo "Current: $(jq 'keys | length' <<< "$CURRENT") items"
|
||||||
echo "Previous: $(jq 'keys | length' <<< "$PREV") items"
|
echo "Previous: $(jq 'keys | length' <<< "$PREV") items"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
entries=$(jq -c --argjson prev "$PREV" '
|
# ---------------------------------------------------------------
|
||||||
|
# A change to the pinned Alchemist invalidates every reuse.
|
||||||
|
#
|
||||||
|
# The tool is not covered by any item's fingerprint, so without
|
||||||
|
# this a release could carry some artifacts built by the new
|
||||||
|
# Alchemist and some by the old one. Rebuilding everything keeps a
|
||||||
|
# release internally consistent, at the cost of one full rebuild
|
||||||
|
# per Alchemist bump.
|
||||||
|
#
|
||||||
|
# This does NOT force version bumps: the bump check compares
|
||||||
|
# fingerprint and upstream version, neither of which the tool ref
|
||||||
|
# touches. A tool change rebuilds without demanding a bump on
|
||||||
|
# every item in the store.
|
||||||
|
# ---------------------------------------------------------------
|
||||||
|
ALCHEMIST_CHANGED=false
|
||||||
|
if [[ -n "$PREV_ALCHEMIST_REF" && "$PREV_ALCHEMIST_REF" != "$ALCHEMIST_REF" ]]; then
|
||||||
|
ALCHEMIST_CHANGED=true
|
||||||
|
echo "Alchemist ref changed: $PREV_ALCHEMIST_REF -> $ALCHEMIST_REF"
|
||||||
|
echo "Reuse disabled for this release: rebuilding every item"
|
||||||
|
echo ""
|
||||||
|
elif [[ -z "$PREV_ALCHEMIST_REF" && "$PREV" != "{}" ]]; then
|
||||||
|
ALCHEMIST_CHANGED=true
|
||||||
|
echo "Previous release recorded no Alchemist ref: rebuilding every item"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
entries=$(jq -c \
|
||||||
|
--argjson prev "$PREV" \
|
||||||
|
--argjson alchemist_changed "$ALCHEMIST_CHANGED" '
|
||||||
to_entries | map(
|
to_entries | map(
|
||||||
.value as $c |
|
.value as $c |
|
||||||
($prev[.key] // {}) as $p |
|
($prev[.key] // {}) as $p |
|
||||||
|
|
@ -566,6 +699,7 @@ jobs:
|
||||||
class: $c.class,
|
class: $c.class,
|
||||||
version: $c.version,
|
version: $c.version,
|
||||||
can_reuse: (
|
can_reuse: (
|
||||||
|
($alchemist_changed | not) and
|
||||||
($p.version // "") == $c.version and
|
($p.version // "") == $c.version and
|
||||||
($p.fingerprint // "") == $c.fingerprint and
|
($p.fingerprint // "") == $c.fingerprint and
|
||||||
($p.upstream_version // "") == ($c.upstream_version // "")
|
($p.upstream_version // "") == ($c.upstream_version // "")
|
||||||
|
|
@ -599,6 +733,9 @@ jobs:
|
||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
# Build each item (or reuse from previous release)
|
# Build each item (or reuse from previous release)
|
||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
|
# fail-fast is false: a store may hold hundreds of items and one malformed
|
||||||
|
# manifest should surface every other failure in the same run.
|
||||||
|
# ===========================================================================
|
||||||
build-items:
|
build-items:
|
||||||
name: Build ${{ matrix.item }}
|
name: Build ${{ matrix.item }}
|
||||||
needs: [configure, collect-metadata, setup-matrix]
|
needs: [configure, collect-metadata, setup-matrix]
|
||||||
|
|
@ -617,6 +754,33 @@ jobs:
|
||||||
ref: ${{ needs.configure.outputs.branch }}
|
ref: ${{ needs.configure.outputs.branch }}
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Checkout Alchemist
|
||||||
|
if: matrix.can_reuse == false
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
repository: ${{ env.ALCHEMIST_REPO }}
|
||||||
|
ref: ${{ needs.configure.outputs.alchemist_ref }}
|
||||||
|
path: ${{ env.ALCHEMIST_PATH }}
|
||||||
|
sparse-checkout: automation-tools/alchemist
|
||||||
|
sparse-checkout-cone-mode: true
|
||||||
|
|
||||||
|
- name: Overlay store version policy
|
||||||
|
if: matrix.can_reuse == false
|
||||||
|
run: |
|
||||||
|
if [[ ! -x "$ALCHEMIST_DIR/alchemist.sh" ]]; then
|
||||||
|
echo "ERROR: alchemist.sh not found or not executable at $ALCHEMIST_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$ALCHEMIST_DIR/version_policy.sh" "$ALCHEMIST_DIR/version_pins.sh"
|
||||||
|
cp alchemist/version_policy.sh "$ALCHEMIST_DIR/version_policy.sh"
|
||||||
|
|
||||||
|
if [[ -f alchemist/version_pins.sh ]]; then
|
||||||
|
cp alchemist/version_pins.sh "$ALCHEMIST_DIR/version_pins.sh"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Alchemist ready at ${{ needs.configure.outputs.alchemist_ref }}"
|
||||||
|
|
||||||
- name: Install build dependencies
|
- name: Install build dependencies
|
||||||
if: matrix.can_reuse == false
|
if: matrix.can_reuse == false
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -631,13 +795,13 @@ jobs:
|
||||||
apt)
|
apt)
|
||||||
sudo add-apt-repository -y ppa:flatpak/stable
|
sudo add-apt-repository -y ppa:flatpak/stable
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt install -y flatpak flatpak-builder p7zip-full xmlstarlet bzip2 curl jq unzip zstd
|
sudo apt install -y flatpak flatpak-builder p7zip-full xmlstarlet bzip2 curl jq unzip
|
||||||
;;
|
;;
|
||||||
pacman)
|
pacman)
|
||||||
sudo pacman -Sy --noconfirm flatpak flatpak-builder p7zip xmlstarlet bzip2 curl jq unzip zstd
|
sudo pacman -Sy --noconfirm flatpak flatpak-builder p7zip xmlstarlet bzip2 curl jq unzip
|
||||||
;;
|
;;
|
||||||
dnf)
|
dnf)
|
||||||
sudo dnf install -y flatpak flatpak-builder p7zip p7zip-plugins xmlstarlet bzip2 curl jq unzip zstd
|
sudo dnf install -y flatpak flatpak-builder p7zip p7zip-plugins xmlstarlet bzip2 curl jq unzip
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "ERROR: No supported package manager found (apt, pacman, dnf)"
|
echo "ERROR: No supported package manager found (apt, pacman, dnf)"
|
||||||
|
|
@ -676,7 +840,8 @@ jobs:
|
||||||
echo "Reused assets downloaded"
|
echo "Reused assets downloaded"
|
||||||
|
|
||||||
# ---------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# Component items run through the Alchemist unchanged.
|
# Component items run through the Alchemist unchanged, so their archive
|
||||||
|
# is byte-for-byte the shape the external component installer expects.
|
||||||
# ---------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
- name: Build component item
|
- name: Build component item
|
||||||
if: matrix.can_reuse == false && matrix.class == 'component'
|
if: matrix.can_reuse == false && matrix.class == 'component'
|
||||||
|
|
@ -685,7 +850,7 @@ jobs:
|
||||||
|
|
||||||
echo "Building component item $ITEM..."
|
echo "Building component item $ITEM..."
|
||||||
|
|
||||||
automation-tools/alchemist/alchemist.sh -f "${{ matrix.dir }}/component_recipe.json"
|
"$ALCHEMIST_DIR/alchemist.sh" -f "${{ matrix.dir }}/component_recipe.json"
|
||||||
|
|
||||||
echo "Build complete"
|
echo "Build complete"
|
||||||
|
|
||||||
|
|
@ -693,6 +858,10 @@ jobs:
|
||||||
# Data items stop before compression so each deployed file can be hashed
|
# Data items stop before compression so each deployed file can be hashed
|
||||||
# while still uncompressed. The workflow then compresses exactly as the
|
# while still uncompressed. The workflow then compresses exactly as the
|
||||||
# Alchemist would, so collection and verification stay class-agnostic.
|
# Alchemist would, so collection and verification stay class-agnostic.
|
||||||
|
#
|
||||||
|
# The output directory MUST exist before invoking with -o: the Alchemist
|
||||||
|
# runs realpath on it, and export masks the failure from set -e, leaving
|
||||||
|
# WORKDIR empty and the artifact root at the filesystem root.
|
||||||
# ---------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
- name: Build data item
|
- name: Build data item
|
||||||
if: matrix.can_reuse == false && matrix.class == 'data'
|
if: matrix.can_reuse == false && matrix.class == 'data'
|
||||||
|
|
@ -704,7 +873,7 @@ jobs:
|
||||||
ITEM_WORKDIR="$GITHUB_WORKSPACE/.build/$ITEM"
|
ITEM_WORKDIR="$GITHUB_WORKSPACE/.build/$ITEM"
|
||||||
mkdir -p "$ITEM_WORKDIR"
|
mkdir -p "$ITEM_WORKDIR"
|
||||||
|
|
||||||
automation-tools/alchemist/alchemist.sh \
|
"$ALCHEMIST_DIR/alchemist.sh" \
|
||||||
-f "${{ matrix.dir }}/component_recipe.json" \
|
-f "${{ matrix.dir }}/component_recipe.json" \
|
||||||
--dry-run \
|
--dry-run \
|
||||||
-o "$ITEM_WORKDIR"
|
-o "$ITEM_WORKDIR"
|
||||||
|
|
@ -756,7 +925,7 @@ jobs:
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${{ matrix.class }}" != "data" ]]; then
|
if [[ "${{ matrix.class }}" != "data" ]]; then
|
||||||
echo "Component item - no files[] to cross-check"
|
echo "Component item: no files[] to cross-check"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -830,19 +999,42 @@ jobs:
|
||||||
ARCHIVE_SIZE=$(du -h "$ITEM/artifacts/$ITEM.tar.gz" | cut -f1)
|
ARCHIVE_SIZE=$(du -h "$ITEM/artifacts/$ITEM.tar.gz" | cut -f1)
|
||||||
echo "Artifacts verified ($ARCHIVE_SIZE)"
|
echo "Artifacts verified ($ARCHIVE_SIZE)"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
# Artwork is staged as a single item-named file rather than an artwork/
|
||||||
|
# directory, so collection in create-release cannot collide no matter how
|
||||||
|
# download-artifact lays the tree out. It runs for reused items too:
|
||||||
|
# artwork sits outside the fingerprint and is republished every release.
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
- name: Stage artwork
|
- name: Stage artwork
|
||||||
run: |
|
run: |
|
||||||
ITEM="${{ matrix.item }}"
|
ITEM="${{ matrix.item }}"
|
||||||
ART_SRC="${{ matrix.dir }}/artwork"
|
MANIFEST="${{ matrix.dir }}/retroverse_manifest.json"
|
||||||
|
|
||||||
if [[ -d "$ART_SRC" ]]; then
|
ART_REL=$(jq -r '.artwork // empty' "$MANIFEST")
|
||||||
mkdir -p "$ITEM/artifacts/artwork"
|
|
||||||
cp -r "$ART_SRC/." "$ITEM/artifacts/artwork/"
|
if [[ -z "$ART_REL" ]]; then
|
||||||
echo "Artwork staged"
|
echo "No artwork declared for $ITEM"
|
||||||
else
|
exit 0
|
||||||
echo "No artwork for $ITEM"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
ART_SRC="${{ matrix.dir }}/$ART_REL"
|
||||||
|
|
||||||
|
if [[ ! -f "$ART_SRC" ]]; then
|
||||||
|
echo "ERROR: artwork declared as '$ART_REL' but not found at $ART_SRC"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
EXT="${ART_SRC##*.}"
|
||||||
|
if [[ "$EXT" == "$ART_SRC" ]]; then
|
||||||
|
echo "ERROR: artwork '$ART_REL' has no file extension"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$ITEM/artifacts"
|
||||||
|
cp "$ART_SRC" "$ITEM/artifacts/$ITEM.artwork.$EXT"
|
||||||
|
|
||||||
|
echo "Artwork staged as $ITEM.artwork.$EXT"
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
|
|
@ -869,9 +1061,6 @@ jobs:
|
||||||
with:
|
with:
|
||||||
ref: ${{ needs.configure.outputs.branch }}
|
ref: ${{ needs.configure.outputs.branch }}
|
||||||
|
|
||||||
- name: Install zstd
|
|
||||||
run: sudo apt update && sudo apt install -y zstd
|
|
||||||
|
|
||||||
- name: Generate release tag
|
- name: Generate release tag
|
||||||
id: tag
|
id: tag
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -915,27 +1104,52 @@ jobs:
|
||||||
env:
|
env:
|
||||||
CURRENT: ${{ needs.collect-metadata.outputs.items-json }}
|
CURRENT: ${{ needs.collect-metadata.outputs.items-json }}
|
||||||
run: |
|
run: |
|
||||||
|
# nullglob: without it, an unmatched glob passes through literally and
|
||||||
|
# the checks below report a missing SHA for a file called "*.tar.gz"
|
||||||
|
# instead of saying nothing was collected.
|
||||||
|
shopt -s nullglob
|
||||||
|
|
||||||
mkdir -p release-assets artwork-staging
|
mkdir -p release-assets artwork-staging
|
||||||
|
|
||||||
for dir in downloaded-artifacts/*-artifacts; do
|
echo "Downloaded artifact tree:"
|
||||||
[[ -d "$dir" ]] || continue
|
find downloaded-artifacts/ -maxdepth 3 | sort | sed 's/^/ /'
|
||||||
item=$(basename "$dir" -artifacts)
|
echo ""
|
||||||
|
|
||||||
cp "$dir/$item.tar.gz" release-assets/
|
# -------------------------------------------------------------------
|
||||||
cp "$dir/$item.tar.gz.sha" release-assets/
|
# Collection is layout-agnostic on purpose.
|
||||||
|
#
|
||||||
|
# download-artifact may or may not place each artifact in its own
|
||||||
|
# subdirectory depending on version and pattern handling, so nothing
|
||||||
|
# here depends on the tree shape. Every file an item contributes is
|
||||||
|
# already named after that item, so a flattened download and a nested
|
||||||
|
# one both collect correctly.
|
||||||
|
# -------------------------------------------------------------------
|
||||||
|
while IFS= read -r file; do
|
||||||
|
cp "$file" release-assets/
|
||||||
|
done < <(find downloaded-artifacts/ -type f \
|
||||||
|
\( -name '*.tar.gz' -o -name '*.tar.gz.sha' -o -name '*.file_hashes.txt' \))
|
||||||
|
|
||||||
if [[ -f "$dir/$item.file_hashes.txt" ]]; then
|
while IFS= read -r art; do
|
||||||
cp "$dir/$item.file_hashes.txt" release-assets/
|
cp "$art" artwork-staging/
|
||||||
fi
|
done < <(find downloaded-artifacts/ -type f -name '*.artwork.*')
|
||||||
|
|
||||||
if [[ -d "$dir/artwork" ]]; then
|
# Item archives only. The artwork bundle is also a .tar.gz and is
|
||||||
mkdir -p "artwork-staging/$item"
|
# built later in this job; excluding it by name keeps the inventory
|
||||||
cp -r "$dir/artwork/." "artwork-staging/$item/"
|
# check correct if these steps are ever reordered.
|
||||||
fi
|
ARCHIVES=()
|
||||||
|
for candidate in release-assets/*.tar.gz; do
|
||||||
|
[[ "$(basename "$candidate")" == "artwork.tar.gz" ]] && continue
|
||||||
|
ARCHIVES+=("$candidate")
|
||||||
done
|
done
|
||||||
|
|
||||||
|
if [[ "${#ARCHIVES[@]}" -eq 0 ]]; then
|
||||||
|
echo "ERROR: no artifacts were collected from downloaded-artifacts/"
|
||||||
|
echo "The download step produced nothing matching *.tar.gz - see the tree above."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
MISSING=0
|
MISSING=0
|
||||||
for archive in release-assets/*.tar.gz; do
|
for archive in "${ARCHIVES[@]}"; do
|
||||||
if [[ ! -f "${archive}.sha" ]]; then
|
if [[ ! -f "${archive}.sha" ]]; then
|
||||||
echo "ERROR: Missing SHA file for $(basename "$archive")"
|
echo "ERROR: Missing SHA file for $(basename "$archive")"
|
||||||
MISSING=$((MISSING + 1))
|
MISSING=$((MISSING + 1))
|
||||||
|
|
@ -948,10 +1162,18 @@ jobs:
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# -------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
# Check for inventory completeness.
|
# Inventory completeness.
|
||||||
|
#
|
||||||
|
# A failed matrix leg already fails build-items and skips this job, so
|
||||||
|
# this is not guarding against item build failures. It guards against
|
||||||
|
# an item silently never reaching release-assets. A download pattern
|
||||||
|
# that misses one, a collection bug, a matrix entry that produced no
|
||||||
|
# leg. None of those fail anything on their own, and the result would
|
||||||
|
# be a smaller inventory that every client reads as those items having
|
||||||
|
# been removed from the store.
|
||||||
# -------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
jq -r 'keys[]' <<< "$CURRENT" | sort > /tmp/expected_items
|
jq -r 'keys[]' <<< "$CURRENT" | sort > /tmp/expected_items
|
||||||
ls release-assets/*.tar.gz \
|
printf '%s\n' "${ARCHIVES[@]}" \
|
||||||
| xargs -n1 basename \
|
| xargs -n1 basename \
|
||||||
| sed 's/\.tar\.gz$//' \
|
| sed 's/\.tar\.gz$//' \
|
||||||
| sort > /tmp/collected_items
|
| sort > /tmp/collected_items
|
||||||
|
|
@ -974,7 +1196,7 @@ jobs:
|
||||||
|
|
||||||
if [[ "$INVENTORY_ERRORS" -gt 0 ]]; then
|
if [[ "$INVENTORY_ERRORS" -gt 0 ]]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "ERROR: inventory is incomplete - refusing to publish a partial store"
|
echo "ERROR: inventory is incomplete: refusing to publish a partial store"
|
||||||
echo " Expected: $(wc -l < /tmp/expected_items) items"
|
echo " Expected: $(wc -l < /tmp/expected_items) items"
|
||||||
echo " Collected: $(wc -l < /tmp/collected_items) items"
|
echo " Collected: $(wc -l < /tmp/collected_items) items"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
@ -982,18 +1204,28 @@ jobs:
|
||||||
|
|
||||||
echo "Inventory complete: $(wc -l < /tmp/collected_items) items"
|
echo "Inventory complete: $(wc -l < /tmp/collected_items) items"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
# The bundle is a flat set of <item_id>.artwork.<ext> files, so the client
|
||||||
|
# finds an item's thumbnail by name without walking a directory tree.
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
- name: Build artwork bundle
|
- name: Build artwork bundle
|
||||||
run: |
|
run: |
|
||||||
if [[ -d artwork-staging && -n "$(ls -A artwork-staging)" ]]; then
|
if [[ -d artwork-staging && -n "$(ls -A artwork-staging)" ]]; then
|
||||||
tar -c -C artwork-staging . | zstd -19 -o release-assets/artwork.tar.zst
|
tar -czf release-assets/artwork.tar.gz -C artwork-staging .
|
||||||
echo "Artwork bundle built"
|
( cd release-assets && sha256sum artwork.tar.gz > artwork.tar.gz.sha )
|
||||||
|
echo "Artwork bundle built: $(ls -1 artwork-staging | wc -l) image(s)"
|
||||||
else
|
else
|
||||||
echo "No artwork to bundle"
|
echo "No artwork to bundle"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ---------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# Collect store metadata. Authored fields come from each retroverse_manifest.json;
|
# Aggregation. Authored fields come from each retroverse_manifest.json;
|
||||||
# sha256, payload_size, url and file_hashes are derived here.
|
# sha256, payload_size, url and file_hashes are derived here.
|
||||||
|
#
|
||||||
|
# file_hashes for a REUSED item are carried forward from the previous
|
||||||
|
# release. A reused item is never unpacked and rehashed. Omitting this
|
||||||
|
# would not fail the build; it would silently ship empty hash maps for
|
||||||
|
# the most stable items in the store.
|
||||||
# ---------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
- name: Generate retroverse.json
|
- name: Generate retroverse.json
|
||||||
env:
|
env:
|
||||||
|
|
@ -1051,6 +1283,16 @@ jobs:
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# The authored 'artwork' value is a repo path the client cannot see.
|
||||||
|
# Replace it with the file's name inside artwork.tar.gz, or null
|
||||||
|
# when the item ships none.
|
||||||
|
artwork_file=""
|
||||||
|
for candidate in artwork-staging/"$item".artwork.*; do
|
||||||
|
[[ -f "$candidate" ]] || continue
|
||||||
|
artwork_file=$(basename "$candidate")
|
||||||
|
break
|
||||||
|
done
|
||||||
|
|
||||||
entry=$(jq -c \
|
entry=$(jq -c \
|
||||||
--arg sha "$sha" \
|
--arg sha "$sha" \
|
||||||
--argjson size "$size" \
|
--argjson size "$size" \
|
||||||
|
|
@ -1060,6 +1302,7 @@ jobs:
|
||||||
--arg built_on "$built_on" \
|
--arg built_on "$built_on" \
|
||||||
--arg status "$status" \
|
--arg status "$status" \
|
||||||
--arg source_id "$SOURCE_ID" \
|
--arg source_id "$SOURCE_ID" \
|
||||||
|
--arg artwork "$artwork_file" \
|
||||||
--argjson file_hashes "$file_hashes" \
|
--argjson file_hashes "$file_hashes" \
|
||||||
'. + {
|
'. + {
|
||||||
source_id: $source_id,
|
source_id: $source_id,
|
||||||
|
|
@ -1070,23 +1313,28 @@ jobs:
|
||||||
fingerprint: $fingerprint,
|
fingerprint: $fingerprint,
|
||||||
built_on: $built_on,
|
built_on: $built_on,
|
||||||
status: $status,
|
status: $status,
|
||||||
|
artwork: (if $artwork == "" then null else $artwork end),
|
||||||
file_hashes: $file_hashes
|
file_hashes: $file_hashes
|
||||||
}' <<< "$manifest")
|
}' <<< "$manifest")
|
||||||
|
|
||||||
entries=$(jq -c --argjson e "$entry" '. += [$e]' <<< "$entries")
|
entries=$(jq -c --argjson e "$entry" '. += [$e]' <<< "$entries")
|
||||||
done < <(jq -r 'keys[]' <<< "$CURRENT")
|
done < <(jq -r 'keys[]' <<< "$CURRENT")
|
||||||
|
|
||||||
|
# alchemist_ref is recorded so the next release can tell whether the
|
||||||
|
# tool changed. Without it, reuse cannot be invalidated on a tool bump.
|
||||||
jq -n \
|
jq -n \
|
||||||
--arg schema "1" \
|
--arg schema "1" \
|
||||||
--arg source_id "$SOURCE_ID" \
|
--arg source_id "$SOURCE_ID" \
|
||||||
--arg tag "$RELEASE_TAG" \
|
--arg tag "$RELEASE_TAG" \
|
||||||
--arg built_on "$BUILD_TIMESTAMP" \
|
--arg built_on "$BUILD_TIMESTAMP" \
|
||||||
|
--arg alchemist_ref "${{ needs.configure.outputs.alchemist_ref }}" \
|
||||||
--argjson items "$entries" \
|
--argjson items "$entries" \
|
||||||
'{
|
'{
|
||||||
store_schema_version: ($schema | tonumber),
|
store_schema_version: ($schema | tonumber),
|
||||||
source_id: $source_id,
|
source_id: $source_id,
|
||||||
release_tag: $tag,
|
release_tag: $tag,
|
||||||
built_on: $built_on,
|
built_on: $built_on,
|
||||||
|
alchemist_ref: $alchemist_ref,
|
||||||
items: $items
|
items: $items
|
||||||
}' > release-assets/retroverse.json
|
}' > release-assets/retroverse.json
|
||||||
|
|
||||||
|
|
@ -1169,7 +1417,7 @@ jobs:
|
||||||
echo "Items built/validated:"
|
echo "Items built/validated:"
|
||||||
jq -r 'to_entries[] | " \(.key) [\(.value.class)]: \(.value.version)"' <<< "$CURRENT"
|
jq -r 'to_entries[] | " \(.key) [\(.value.class)]: \(.value.version)"' <<< "$CURRENT"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Dry-run complete - no release was created"
|
echo "Dry-run complete: no release was created"
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
|
|
||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
|
|
@ -1186,10 +1434,15 @@ jobs:
|
||||||
BUILD_MATRIX: ${{ needs.setup-matrix.outputs.build-matrix }}
|
BUILD_MATRIX: ${{ needs.setup-matrix.outputs.build-matrix }}
|
||||||
run: |
|
run: |
|
||||||
IS_DRY_RUN="${{ needs.configure.outputs.is_dry_run }}"
|
IS_DRY_RUN="${{ needs.configure.outputs.is_dry_run }}"
|
||||||
|
RELEASE_RESULT="${{ needs.create-release.result }}"
|
||||||
|
|
||||||
|
# A tag only exists once create-release has actually published. It is
|
||||||
|
# empty on a dry run (the job is skipped) and on a failed release.
|
||||||
RELEASE_TAG="${{ needs.create-release.outputs.release_tag }}"
|
RELEASE_TAG="${{ needs.create-release.outputs.release_tag }}"
|
||||||
if [[ -z "$RELEASE_TAG" ]]; then
|
RELEASE_URL=""
|
||||||
RELEASE_TAG="(not created)"
|
|
||||||
|
if [[ -n "$RELEASE_TAG" && "$RELEASE_RESULT" == "success" ]]; then
|
||||||
|
RELEASE_URL="${{ github.server_url }}/${{ github.repository }}/releases/tag/${RELEASE_TAG}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "## RetroVERSE Build Summary" >> $GITHUB_STEP_SUMMARY
|
echo "## RetroVERSE Build Summary" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
@ -1198,6 +1451,9 @@ jobs:
|
||||||
if [[ "$IS_DRY_RUN" == "true" ]]; then
|
if [[ "$IS_DRY_RUN" == "true" ]]; then
|
||||||
echo "> **Dry run** - no release was created" >> $GITHUB_STEP_SUMMARY
|
echo "> **Dry run** - no release was created" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
elif [[ -n "$RELEASE_URL" ]]; then
|
||||||
|
echo "**[View release ${RELEASE_TAG}](${RELEASE_URL})**" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "### Parameters" >> $GITHUB_STEP_SUMMARY
|
echo "### Parameters" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
@ -1205,7 +1461,15 @@ jobs:
|
||||||
echo "| Parameter | Value |" >> $GITHUB_STEP_SUMMARY
|
echo "| Parameter | Value |" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "|-----------|-------|" >> $GITHUB_STEP_SUMMARY
|
echo "|-----------|-------|" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "| Branch | \`${{ needs.configure.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY
|
echo "| Branch | \`${{ needs.configure.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "| Release tag | \`${RELEASE_TAG}\` |" >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
if [[ -n "$RELEASE_URL" ]]; then
|
||||||
|
echo "| Release | [\`${RELEASE_TAG}\`](${RELEASE_URL}) |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
elif [[ -n "$RELEASE_TAG" ]]; then
|
||||||
|
echo "| Release | \`${RELEASE_TAG}\` (publish failed) |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
else
|
||||||
|
echo "| Release | (not created) |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
if [[ -n "$BUILD_MATRIX" && "$BUILD_MATRIX" != "null" ]]; then
|
if [[ -n "$BUILD_MATRIX" && "$BUILD_MATRIX" != "null" ]]; then
|
||||||
|
|
|
||||||
1
alchemist/alchemist_ref
Normal file
1
alchemist/alchemist_ref
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
28e108e32350d518198857dc91ea2a2cb7b25529
|
||||||
1
alchemist/version_policy.sh
Normal file
1
alchemist/version_policy.sh
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export FREEDOOM_VERSION_POLICY="latest"
|
||||||
BIN
freedoom/artwork/thumb.png
Normal file
BIN
freedoom/artwork/thumb.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
17
freedoom/component_recipe.json
Normal file
17
freedoom/component_recipe.json
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"freedoom": [
|
||||||
|
{
|
||||||
|
"source_url": "https://github.com/freedoom/freedoom/releases/download/v0.13.0/freedm-0.13.0.zip",
|
||||||
|
"source_type": "github_release",
|
||||||
|
"extraction_type": "archive",
|
||||||
|
"version": "$FREEDOOM_DESIRED_VERSION",
|
||||||
|
"assets": [
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"source": "freedm-0.13.0/freedm.wad",
|
||||||
|
"dest": "$COMPONENT_ARTIFACT_ROOT"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
22
freedoom/retroverse_manifest.json
Normal file
22
freedoom/retroverse_manifest.json
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"id": "freedoom",
|
||||||
|
"class": "data",
|
||||||
|
"name": "FreeDOOM",
|
||||||
|
"description": "Freedoom is a complete, free content first person shooter game, based on the Doom engine.",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"tags": ["rom", "freeware"],
|
||||||
|
|
||||||
|
"min_client_schema": 1,
|
||||||
|
"requires": ["component:uzdoom"],
|
||||||
|
|
||||||
|
"artwork": "artwork/thumb.png",
|
||||||
|
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"source": "freedm.wad",
|
||||||
|
"destination": "$roms_folder/doom/freedm.wad",
|
||||||
|
"policy": "replace"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue