Create Release #112
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Create Release | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Version number (e.g., v4.6.1.0 or v4.6.0.0-beta.1)' | |
| required: true | |
| type: string | |
| changelog: | |
| description: 'Changelog message for this release' | |
| required: true | |
| type: string | |
| beta: | |
| description: 'Beta release (skips pak.json commit, marks as prerelease)' | |
| required: false | |
| type: boolean | |
| default: false | |
| permissions: | |
| contents: write | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| jobs: | |
| prepare: | |
| runs-on: ubuntu-22.04-arm | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Set version | |
| id: version | |
| run: | | |
| VERSION=$(echo "${{ inputs.version }}" | xargs) | |
| echo "value=$VERSION" >> $GITHUB_OUTPUT | |
| - name: Update pak.json files | |
| run: | | |
| # Check current version | |
| CURRENT_VERSION=$(jq -r '.version' pak.json) | |
| if [ "$CURRENT_VERSION" != "${{ steps.version.outputs.value }}" ]; then | |
| echo "Updating pak.json from $CURRENT_VERSION to ${{ steps.version.outputs.value }}" | |
| # Update root pak.json | |
| jq --arg version "${{ steps.version.outputs.value }}" \ | |
| --arg changelog "${{ inputs.changelog }}" \ | |
| '.version = $version | .changelog[$version] = $changelog' \ | |
| pak.json > pak.json.tmp && mv pak.json.tmp pak.json | |
| else | |
| echo "Version is already ${{ steps.version.outputs.value }}, skipping pak.json update" | |
| fi | |
| - name: Commit pak.json changes | |
| if: ${{ inputs.beta == false }} | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| if git diff --quiet pak.json; then | |
| echo "No changes to commit" | |
| else | |
| git add pak.json | |
| git commit -m "Update pak.json to ${{ steps.version.outputs.value }}" | |
| git push | |
| fi | |
| outputs: | |
| version: ${{ steps.version.outputs.value }} | |
| build-arm64: | |
| needs: prepare | |
| runs-on: ubuntu-22.04-arm | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.ref }} | |
| - name: Install Task | |
| uses: arduino/setup-task@v2 | |
| with: | |
| version: 3.x | |
| repo-token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Build and extract ARM64 | |
| run: task build:arm64 | |
| env: | |
| GROUT_VERSION: ${{ needs.prepare.outputs.version }} | |
| - name: Package ARM64 platforms | |
| run: task package:next package:muos package:knulli package:rocknix package:arkos package:trimui package:batocera | |
| - name: Create distributions | |
| run: | | |
| cd dist/Grout.pak && zip -r ../Grout.pak.zip . && cd ../.. | |
| cd dist/muOS && zip -r Grout.muxapp Grout && mv Grout.muxapp ../Grout.muxapp && cd ../.. | |
| cd dist/Knulli && zip -r ../Grout-Knulli.zip Grout && cd ../.. | |
| cd dist/ROCKNIX && zip -r ../Grout-ROCKNIX.zip Grout.sh Grout && cd ../.. | |
| cd dist/ArkOS && zip -r ../Grout-ArkOS.zip Grout.sh Grout && cd ../.. | |
| cd dist/Trimui && zip -r ../Grout-Trimui.zip Grout && cd ../.. | |
| cd dist/Batocera-arm64 && zip -r ../Grout-Batocera-arm64.zip Grout.sh Grout && cd ../.. | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: arm64-artifacts | |
| path: | | |
| dist/Grout.pak.zip | |
| dist/Grout.muxapp | |
| dist/Grout-Knulli.zip | |
| dist/Grout-ROCKNIX.zip | |
| dist/Grout-ArkOS.zip | |
| dist/Grout-Trimui.zip | |
| dist/Grout-Batocera-arm64.zip | |
| build64/grout | |
| build64/lib/** | |
| build-amd64: | |
| needs: prepare | |
| runs-on: ubuntu-22.04 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.ref }} | |
| - name: Install Task | |
| uses: arduino/setup-task@v2 | |
| with: | |
| version: 3.x | |
| repo-token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Build and extract AMD64 | |
| run: task build:amd64 | |
| env: | |
| GROUT_VERSION: ${{ needs.prepare.outputs.version }} | |
| - name: Package Batocera AMD64 | |
| run: task package:batocera-amd64 | |
| - name: Create distribution | |
| run: | | |
| cd dist/Batocera-amd64 && zip -r ../Grout-Batocera-amd64.zip Grout.sh Grout | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: amd64-artifacts | |
| path: | | |
| dist/Grout-Batocera-amd64.zip | |
| build-x86: | |
| needs: prepare | |
| runs-on: ubuntu-22.04 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.ref }} | |
| - name: Install Task | |
| uses: arduino/setup-task@v2 | |
| with: | |
| version: 3.x | |
| repo-token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Build and extract x86 | |
| run: task build:x86 | |
| env: | |
| GROUT_VERSION: ${{ needs.prepare.outputs.version }} | |
| - name: Package Batocera x86 | |
| run: task package:batocera-x86 | |
| - name: Create distribution | |
| run: | | |
| cd dist/Batocera-x86 && zip -r ../Grout-Batocera-x86.zip Grout.sh Grout | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: x86-artifacts | |
| path: | | |
| dist/Grout-Batocera-x86.zip | |
| build-arm32: | |
| needs: prepare | |
| runs-on: ubuntu-22.04-arm | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.ref }} | |
| - name: Install Task | |
| uses: arduino/setup-task@v2 | |
| with: | |
| version: 3.x | |
| repo-token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Build and extract ARM32 | |
| run: task build:arm32 | |
| env: | |
| GROUT_VERSION: ${{ needs.prepare.outputs.version }} | |
| - name: Package ARM32 platforms | |
| run: task package:allium package:onion package:koriki | |
| - name: Create distributions | |
| run: | | |
| cd dist/Allium && zip -r ../Grout-Allium.zip Grout.pak && cd ../.. | |
| cd dist/Onion && zip -r ../Grout-Onion.zip Grout && cd ../.. | |
| cd dist/Koriki && zip -r ../Grout-Koriki.zip .simplemenu App && cd ../.. | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: arm32-artifacts | |
| path: | | |
| dist/Grout-Allium.zip | |
| dist/Grout-Onion.zip | |
| dist/Grout-Koriki.zip | |
| build32/grout | |
| package-arm-universal: | |
| needs: [prepare, build-arm64, build-arm32] | |
| runs-on: ubuntu-22.04 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.ref }} | |
| - name: Install Task | |
| uses: arduino/setup-task@v2 | |
| with: | |
| version: 3.x | |
| repo-token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: arm*-artifacts | |
| merge-multiple: true | |
| - name: Package universal assets | |
| run: task package:spruce package:minui | |
| - name: Create distributions | |
| run: | | |
| cd dist/Spruce && zip -r Grout.spruce.zip Grout && mv Grout.spruce.zip ../Grout.spruce.zip && cd ../.. | |
| cd dist/MinUI && zip -r ../Grout-MinUI.zip Grout.pak && cd ../.. | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: arm-universal-artifacts | |
| path: | | |
| dist/Grout.spruce.zip | |
| dist/Grout-MinUI.zip | |
| release: | |
| needs: [prepare, build-arm64, build-amd64, build-x86, build-arm32, package-arm-universal] | |
| runs-on: ubuntu-22.04 | |
| steps: | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| merge-multiple: true | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ needs.prepare.outputs.version }} | |
| name: ${{ needs.prepare.outputs.version }} | |
| body: | | |
| ${{ inputs.beta && '⚠️ **This is a beta release.** It may contain bugs or incomplete features.' || '' }} | |
| ${{ inputs.changelog }} | |
| ${{ inputs.beta && format('Built from branch: `{0}`', github.ref_name) || '' }} | |
| files: | | |
| **/Grout.pak.zip | |
| **/Grout.muxapp | |
| **/Grout.spruce.zip | |
| **/Grout-Allium.zip | |
| **/Grout-Onion.zip | |
| **/Grout-Koriki.zip | |
| **/Grout-Knulli.zip | |
| **/Grout-ArkOS.zip | |
| **/Grout-ROCKNIX.zip | |
| **/Grout-Trimui.zip | |
| **/Grout-MinUI.zip | |
| **/Grout-Batocera-arm64.zip | |
| **/Grout-Batocera-x86.zip | |
| **/Grout-Batocera-amd64.zip | |
| **/grout | |
| draft: false | |
| prerelease: ${{ inputs.beta }} | |
| update-versions: | |
| needs: [prepare, release] | |
| runs-on: ubuntu-22.04 | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| token: ${{ secrets.RELEASE_PAT }} | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| merge-multiple: true | |
| - name: Update versions.json | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| VERSION: ${{ needs.prepare.outputs.version }} | |
| IS_BETA: ${{ inputs.beta }} | |
| CHANGELOG: ${{ inputs.changelog }} | |
| run: | | |
| REPO="rommapp/grout" | |
| DOWNLOAD_BASE="https://github.com/${REPO}/releases/download/${VERSION}" | |
| # Hash local build artifacts and get their sizes | |
| get_asset_info() { | |
| local path="$1" | |
| if [ -f "$path" ]; then | |
| local size=$(stat --format=%s "$path") | |
| local sha256=$(sha256sum "$path" | cut -d' ' -f1) | |
| echo "${size} ${sha256}" | |
| else | |
| echo "0 " | |
| fi | |
| } | |
| # Verify uploaded release assets match local build artifacts | |
| verify_upload() { | |
| local name="$1" | |
| local expected_sha256="$2" | |
| if [ -z "$expected_sha256" ]; then return; fi | |
| local url="${DOWNLOAD_BASE}/${name}" | |
| if curl -sfL -o "/tmp/${name}" "$url"; then | |
| local actual_sha256=$(sha256sum "/tmp/${name}" | cut -d' ' -f1) | |
| rm -f "/tmp/${name}" | |
| if [ "$actual_sha256" != "$expected_sha256" ]; then | |
| echo "::error::Upload verification failed for ${name}: expected ${expected_sha256}, got ${actual_sha256}" | |
| exit 1 | |
| fi | |
| echo "Verified ${name}: ${actual_sha256}" | |
| else | |
| echo "::warning::Could not download ${name} for verification" | |
| fi | |
| } | |
| # Distribution zip assets (name only — path is resolved via find) | |
| DIST_NAMES=( | |
| "Grout.pak.zip" | |
| "Grout.muxapp" | |
| "Grout-Knulli.zip" | |
| "Grout.spruce.zip" | |
| "Grout-ROCKNIX.zip" | |
| "Grout-Trimui.zip" | |
| "Grout-Allium.zip" | |
| "Grout-Onion.zip" | |
| "Grout-Koriki.zip" | |
| "Grout-ArkOS.zip" | |
| "Grout-MinUI.zip" | |
| "Grout-Batocera-arm64.zip" | |
| "Grout-Batocera-amd64.zip" | |
| "Grout-Batocera-x86.zip" | |
| ) | |
| # Build the assets JSON object | |
| ASSETS_JSON="{}" | |
| for name in "${DIST_NAMES[@]}"; do | |
| path=$(find . -name "$name" -type f | head -1) | |
| if [ -z "$path" ]; then | |
| echo "::warning::Asset not found: ${name}" | |
| continue | |
| fi | |
| read size sha256 <<< $(get_asset_info "$path") | |
| if [ "$size" = "0" ]; then | |
| echo "::warning::Asset not found: ${path}" | |
| continue | |
| fi | |
| verify_upload "$name" "$sha256" | |
| ASSETS_JSON=$(echo "$ASSETS_JSON" | jq \ | |
| --arg name "$name" \ | |
| --arg url "${DOWNLOAD_BASE}/${name}" \ | |
| --argjson size "${size}" \ | |
| --arg sha256 "${sha256}" \ | |
| '.[$name] = { url: $url, size: $size, sha256: $sha256 }') | |
| done | |
| # Build the release entry | |
| RELEASE_ENTRY=$(jq -n \ | |
| --arg version "$VERSION" \ | |
| --arg notes "$CHANGELOG" \ | |
| --argjson assets "$ASSETS_JSON" \ | |
| '{ version: $version, notes: $notes, assets: $assets }') | |
| # Parse major.minor.patch for the romm key | |
| ROMM_KEY=$(echo "$VERSION" | sed 's/^v//' | grep -oE '^[0-9]+\.[0-9]+\.[0-9]+') | |
| # Initialize versions.json if it doesn't exist | |
| if [ ! -f docs/versions.json ]; then | |
| mkdir -p docs | |
| echo '{"stable":null,"beta":null,"romm":{}}' > docs/versions.json | |
| fi | |
| # Update versions.json | |
| if [ "$IS_BETA" = "true" ]; then | |
| jq --argjson entry "$RELEASE_ENTRY" --arg key "$ROMM_KEY" \ | |
| '.beta = $entry | .romm[$key] = $entry' \ | |
| docs/versions.json > docs/versions.json.tmp | |
| else | |
| jq --argjson entry "$RELEASE_ENTRY" --arg key "$ROMM_KEY" \ | |
| '.stable = $entry | .romm[$key] = $entry' \ | |
| docs/versions.json > docs/versions.json.tmp | |
| fi | |
| mv docs/versions.json.tmp docs/versions.json | |
| - name: Commit and push versions.json | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| if git diff --quiet docs/versions.json; then | |
| echo "No changes to versions.json" | |
| else | |
| git add docs/versions.json | |
| git commit -m "Update versions.json for ${{ needs.prepare.outputs.version }}" | |
| git push | |
| fi |