Skip to content

Release new version #11

Release new version

Release new version #11

name: Release new version
on:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: write
packages: write
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
USERNAME: sshcrack
VCPKG_EXE: ${{ github.workspace }}/vcpkg/vcpkg
FEED_URL: https://nuget.pkg.github.com/sshcrack/index.json
VCPKG_BINARY_SOURCES: "clear;nuget,https://nuget.pkg.github.com/sshcrack/index.json,readwrite"
jobs:
# Job to extract version and do initial validation
prepare:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.VERSION }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: true
- name: Extract version from CMakeLists.txt
id: version
run: |
VERSION=$(grep -Po 'project\([^ ]+ VERSION \K[0-9]+\.[0-9]+\.[0-9]+' CMakeLists.txt)
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
- name: Check if tag or release exists
id: check_tag
run: |
TAG="v${{ steps.version.outputs.VERSION }}"
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "Tag $TAG already exists!" >&2
exit 1
fi
if gh release view "$TAG" >/dev/null 2>&1; then
echo "Release $TAG already exists!" >&2
exit 1
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 10
run_install: false
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: "pnpm"
cache-dependency-path: "react-native/pnpm-lock.yaml"
- name: Install React Native dependencies
working-directory: react-native
run: pnpm install
- name: Build React Native web version
working-directory: react-native
run: pnpm exec expo export --platform web
- name: Upload web build artifact
uses: actions/upload-artifact@v4
with:
name: web-build
path: react-native/dist/
# Build matrix for led-matrix and desktop app
build:
runs-on: ${{ matrix.os }}
needs: prepare
strategy:
fail-fast: true
matrix:
include:
- name: "led-matrix"
os: ubuntu-latest
preset: "cross-compile"
artifact-name: "led-matrix-build"
artifact-path: |
build/led-matrix-${{ needs.prepare.outputs.version }}-arm64.tar.gz
requires-toolchain: true
- name: "desktop-linux"
os: ubuntu-latest
preset: "desktop-linux"
artifact-name: "desktop-linux-build"
artifact-path: desktop_build/led-matrix-desktop-${{ needs.prepare.outputs.version }}-Linux.tar.gz
requires-toolchain: false
- name: "desktop-windows"
os: windows-latest
preset: "desktop-windows"
artifact-name: "desktop-windows-build"
artifact-path: |
desktop_build/led-matrix-desktop-${{ needs.prepare.outputs.version }}-win64.zip
desktop_build/led-matrix-desktop-${{ needs.prepare.outputs.version }}-win64.exe
requires-toolchain: false
env:
BUILD_TYPE: RelWithDebInfo
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: recursive
- uses: awalsh128/cache-apt-pkgs-action@latest
if: runner.os != 'Windows'
with:
packages: python3-jinja2 pkg-config autoconf automake libtool python3 linux-libc-dev curl libltdl-dev libx11-dev libxft-dev libxext-dev libwayland-dev libxkbcommon-dev libegl1-mesa-dev libibus-1.0-dev mono-complete libxrandr-dev libxrandr2 wayland-protocols extra-cmake-modules xorg-dev libxinerama-dev libxcursor-dev libxi-dev libxext-dev libgtk-3-dev libayatana-appindicator3-dev
version: 1.0
- name: Update certificates
if: runner.os != 'Windows'
run: |
sudo apt-get update
sudo apt-get install --reinstall ca-certificates
sudo update-ca-certificates
sudo cert-sync /etc/ssl/certs/ca-certificates.crt
cert-sync --user /etc/ssl/certs/ca-certificates.crt
- uses: lukka/get-cmake@latest
with:
cmakeVersion: "~3.25.0"
- name: Setup anew (or from cache) vcpkg (and does not build any package)
uses: lukka/run-vcpkg@v11
with:
vcpkgConfigurationJsonGlob: "vcpkg-configuration.json"
- name: Add NuGet sources (Linux)
if: runner.os != 'Windows'
shell: bash
env:
VCPKG_EXE: ${{ github.workspace }}/vcpkg/vcpkg
USERNAME: sshcrack
FEED_URL: https://nuget.pkg.github.com/sshcrack/index.json
run: |
mono `${{ env.VCPKG_EXE }} fetch nuget | tail -n 1` \
sources add \
-Source "${{ env.FEED_URL }}" \
-StorePasswordInClearText \
-Name GitHubPackages \
-UserName "${{ env.USERNAME }}" \
-Password "${{ secrets.GH_PACKAGES_TOKEN }}"
mono `${{ env.VCPKG_EXE }} fetch nuget | tail -n 1` \
setapikey "${{ secrets.GH_PACKAGES_TOKEN }}" \
-Source "${{ env.FEED_URL }}"
- name: Add NuGet sources (Windows)
if: runner.os == 'Windows'
shell: pwsh
env:
VCPKG_EXE: ${{ github.workspace }}/vcpkg/vcpkg.exe
USERNAME: sshcrack
FEED_URL: https://nuget.pkg.github.com/sshcrack/index.json
run: |
.$(${{ env.VCPKG_EXE }} fetch nuget) `
sources add `
-Source "${{ env.FEED_URL }}" `
-StorePasswordInClearText `
-Name GitHubPackages `
-UserName "${{ env.USERNAME }}" `
-Password "${{ secrets.GH_PACKAGES_TOKEN }}"
.$(${{ env.VCPKG_EXE }} fetch nuget) `
setapikey "${{ secrets.GH_PACKAGES_TOKEN }}" `
-Source "${{ env.FEED_URL }}"
- name: Download web build artifact
uses: actions/download-artifact@v4
with:
name: web-build
path: react-native/dist/
- name: Cache cross-compile toolchain
if: matrix.requires-toolchain
id: cache-toolchain
uses: actions/cache@v4
with:
path: ${{ runner.temp }}/cross-compile
key: cross-compile-toolchain-v0.0.1-beta
- name: Download and extract cross-compile toolchain
if: matrix.requires-toolchain && steps.cache-toolchain.outputs.cache-hit != 'true'
run: |
mkdir -p ${{ runner.temp }}/cross-compile
cd ${{ runner.temp }}/cross-compile
wget -O cross-compile.tar.xz "https://github.com/sshcrack/led-matrix/releases/download/v0.0.1-beta/cross-compile.tar.xz"
tar -xvf cross-compile.tar.xz
rm cross-compile.tar.xz
- name: Build ${{ matrix.name }}
uses: lukka/run-cmake@v10
with:
configurePreset: ${{ matrix.preset }}
configurePresetAdditionalArgs: "['-DSKIP_WEB_BUILD=ON']"
buildPreset: ${{ matrix.preset }}
buildPresetAdditionalArgs: ${{ runner.os == 'Windows' && '[''--target'', ''package'', ''--config'', ''RelWithDebInfo'']' || '[''--target'', ''package'']' }}
env:
VCPKG_BINARY_SOURCES: "clear;nuget,https://nuget.pkg.github.com/sshcrack/index.json,readwrite"
CROSS_COMPILE_ROOT: ${{ matrix.requires-toolchain && runner.temp }}/cross-compile
- name: Upload ${{ matrix.name }} artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact-name }}
path: ${{ matrix.artifact-path }}
# Build React Native APK
build-react-native:
runs-on: ubuntu-latest
needs: prepare
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: true
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 10
run_install: false
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: "pnpm"
cache-dependency-path: "react-native/pnpm-lock.yaml"
- name: Install dependencies
working-directory: react-native
run: pnpm install
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
# Install Android SDK build-tools for apksigner
- name: Setup Android SDK
uses: android-actions/setup-android@v3
- name: Install Android SDK build-tools
run: |
echo "y" | sdkmanager --install "build-tools;34.0.0" "platform-tools"
echo "$ANDROID_HOME/build-tools/34.0.0" >> $GITHUB_PATH
- name: 🏗 Setup EAS
uses: expo/expo-github-action@v8
with:
eas-version: latest
packager: pnpm
token: ${{ secrets.EXPO_TOKEN }}
# Cache bundletool to avoid downloading it every time
- name: Cache bundletool
id: cache-bundletool
uses: actions/cache@v4
with:
path: ${{ runner.temp }}/bundletool
key: bundletool-1.18.1
- name: Install bundletool
if: steps.cache-bundletool.outputs.cache-hit != 'true'
run: |
mkdir -p ${{ runner.temp }}/bundletool
curl -L -o ${{ runner.temp }}/bundletool/bundletool.jar https://github.com/google/bundletool/releases/download/1.18.1/bundletool-all-1.18.1.jar
# Decode release keystore from GitHub Secrets
- name: Decode release keystore
run: |
mkdir -p ${{ runner.temp }}/keystore
echo "${{ secrets.RELEASE_KEYSTORE_BASE64 }}" | base64 -d > ${{ runner.temp }}/keystore/release.keystore
- name: Build React Native AAB
working-directory: react-native
env:
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
run: |
pnpx expo prebuild -p android
eas build -p android --local --non-interactive --output ../app-v${{ needs.prepare.outputs.version }}.aab
- name: Convert AAB to APK with release signing
run: |
# Convert AAB to APKS (signed with release key)
java -jar ${{ runner.temp }}/bundletool/bundletool.jar build-apks \
--bundle=app-v${{ needs.prepare.outputs.version }}.aab \
--output=${{ runner.temp }}/app.apks \
--mode=universal \
--ks=${{ runner.temp }}/keystore/release.keystore \
--ks-pass=pass:"${{ secrets.KEYSTORE_PASSWORD }}" \
--ks-key-alias="${{ secrets.KEY_ALIAS }}" \
--key-pass=pass:"${{ secrets.KEY_PASSWORD }}"
# Extract the universal APK
unzip -p ${{ runner.temp }}/app.apks universal.apk > app-v${{ needs.prepare.outputs.version }}.apk
# Verify APK signature with apksigner (now guaranteed to be available)
echo "Verifying APK signature with apksigner..."
apksigner verify --verbose app-v${{ needs.prepare.outputs.version }}.apk
# Show detailed certificate information
echo "APK certificate information:"
apksigner verify --print-certs app-v${{ needs.prepare.outputs.version }}.apk
# Clean up sensitive keystore file
- name: Clean up keystore
if: always()
run: |
rm -f ${{ runner.temp }}/keystore/release.keystore
- name: Upload React Native APK
uses: actions/upload-artifact@v4
with:
name: react-native-apk
path: app-v${{ needs.prepare.outputs.version }}.apk
# Create release and upload all artifacts
create-release:
runs-on: ubuntu-latest
needs: [prepare, build, build-react-native]
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: true
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Create release
id: create_release
run: |
TAG="v${{ needs.prepare.outputs.version }}"
# Get previous tag (if any)
PREV_TAG=$(git tag --sort=-creatordate | grep -v "$TAG" | head -n1 || true)
if [ -n "$PREV_TAG" ]; then
# Fetch merged PRs between previous and current tag, with author
PRS=$(gh pr list --state merged --search "merged:>=$(git log -1 --format=%aI $PREV_TAG)" --json number,title,author,mergedAt,mergeCommit --jq '.[] | select(.mergeCommit != null) | "- #\(.number) (\(.title)) - (@\(.author.login))"')
FULL_CHANGELOG_LINK="[**Full Changelog**](https://github.com/${{ github.repository }}/compare/$PREV_TAG...$TAG)"
if [ -n "$PRS" ]; then
# Use template with PRs
CHANGELOG=$(cat .github/changelog-template.md | sed "s|{{PRS}}|$PRS|g" | sed "s|{{FULL_CHANGELOG_LINK}}|$FULL_CHANGELOG_LINK|g")
else
# Use template for no PRs
CHANGELOG=$(cat .github/changelog-no-prs-template.md | sed "s|{{FULL_CHANGELOG_LINK}}|$FULL_CHANGELOG_LINK|g")
fi
else
# Fallback for edge case (no previous tag but not initial release)
CHANGELOG="Release v${{ needs.prepare.outputs.version }}"
fi
gh release create "$TAG" \
--title "$TAG" \
--notes "$CHANGELOG"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload all artifacts to release
run: |
TAG="v${{ needs.prepare.outputs.version }}"
# Upload led-matrix (cross-compile)
gh release upload "$TAG" artifacts/led-matrix-build/led-matrix-${{ needs.prepare.outputs.version }}-arm64.tar.gz
# Upload desktop-linux
gh release upload "$TAG" artifacts/desktop-linux-build/led-matrix-desktop-${{ needs.prepare.outputs.version }}-Linux.tar.gz
# Upload desktop-windows
gh release upload "$TAG" artifacts/desktop-windows-build/led-matrix-desktop-${{ needs.prepare.outputs.version }}-win64.zip
gh release upload "$TAG" artifacts/desktop-windows-build/led-matrix-desktop-${{ needs.prepare.outputs.version }}-win64.exe
# Upload React Native APK
gh release upload "$TAG" artifacts/react-native-apk/app-v${{ needs.prepare.outputs.version }}.apk
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}