name: Techmino CI on: push: branches: [main, ci*] tags: [pre*, v*] pull_request: branches: [main] env: BUILD_ASSETS_FOLDER: ./.github/build BUILD_TYPE: ${{ fromJSON('["dev", "release"]')[startsWith(github.ref, 'refs/tags/v')] }} COLD_CLEAR_DOWNLOAD_URL: https://github.com/26F-Studio/cold_clear_ai_love2d_wrapper/releases/download/11.5 CORE_LOVE_ARTIFACT_NAME: core_love_package CORE_LOVE_PACKAGE_PATH: ./core.love jobs: get-info: runs-on: ubuntu-latest outputs: app-name: ${{ steps.app-info.outputs.app-name }} version-name: ${{ steps.app-info.outputs.version-name }} version-string: ${{ steps.app-info.outputs.version-string }} version-code: ${{ steps.app-info.outputs.version-code }} update-title: ${{ steps.app-info.outputs.update-title }} update-note: ${{ steps.app-info.outputs.update-note }} commit-hash: ${{ steps.git-info.outputs.commit-hash }} base-name: ${{ steps.assemble-base-name.outputs.base-name }} steps: - uses: actions/checkout@v4 - name: Install lua run: | sudo apt-get install lua5.3 -y - name: Get app info id: app-info shell: lua {0} run: | local version = require "version" os.execute('echo "app-name=Techmino" >> $GITHUB_OUTPUT') os.execute('echo "version-name=' .. version.name .. '" >> $GITHUB_OUTPUT') os.execute('echo "version-string=' .. version.string:gsub("%a", "") .. '" >> $GITHUB_OUTPUT') os.execute('echo "version-code=' .. tostring(version.code) .. '" >> $GITHUB_OUTPUT') local f = io.open("updateLog.txt", 'r') local note = f:read("*a") f:close() local p1 = note:find("\n%d") + 1 local p2 = note:find("\n", p1) - 1 os.execute('echo "update-title=' .. note:sub(p1, p2) .. '" >> $GITHUB_OUTPUT') local p3 = note:find("\n", note:find("\n%d") + 1) + 1 local p4 = note:find("\n%d", p3 + 1) updateNote = note:sub(p3, p4 - 2) :gsub(" ", "- ") :gsub(" ", "# ") os.execute('echo "update-note<> $GITHUB_OUTPUT') os.execute('echo "' .. updateNote .. '" >> $GITHUB_OUTPUT') os.execute('echo "EOF" >> $GITHUB_OUTPUT') - name: Get git info id: git-info shell: bash run: | COMMIT_HASH=$(git rev-parse --short ${{ GITHUB.SHA }}) echo "commit-hash=$COMMIT_HASH" >> $GITHUB_OUTPUT - name: Assemble package base name id: assemble-base-name shell: bash run: | BASE_NAME=Techmino_${{ steps.app-info.outputs.version-string }}_${{ steps.git-info.outputs.commit-hash }}_#${{ GITHUB.RUN_NUMBER }} echo "base-name=$BASE_NAME" >> $GITHUB_OUTPUT build-core: runs-on: ubuntu-latest needs: get-info env: OUTPUT_FOLDER: ./build RELEASE_FOLDER: ./release steps: - uses: actions/checkout@v4 with: submodules: recursive - name: Process app name id: process-app-name shell: python3 {0} run: | import os import re with open(os.getenv('GITHUB_OUTPUT'), 'a') as f: f.write('product-name=' + re.sub(r'[^A-Za-z0-9]+', '_', '${{ needs.get-info.outputs.app-name }}') + '\n') - uses: ./.github/actions/update-version if: ${{ !startsWith(github.ref, 'refs/tags/v') }} with: commit: ${{ needs.get-info.outputs.commit-hash }} type: snapshot - name: Build core love package uses: love-actions/love-actions-core@v1 with: build-list: ./media/ ./parts/ ./Zframework/ ./conf.lua ./main.lua ./version.lua ./legals.md ./license.txt package-path: ${{ env.CORE_LOVE_PACKAGE_PATH }} - name: Upload core love package uses: actions/upload-artifact@v4 with: name: ${{ env.CORE_LOVE_ARTIFACT_NAME }} path: ${{ env.CORE_LOVE_PACKAGE_PATH }} - name: Add icon to love package run: | cp ${{ env.BUILD_ASSETS_FOLDER }}/linux/${{ env.BUILD_TYPE }}/icon.png media/image/icon.png zip -u ${{ env.CORE_LOVE_PACKAGE_PATH }} media/image/icon.png rm media/image/icon.png - name: Rename love package run: | mkdir -p ${{ env.OUTPUT_FOLDER }} mv ${{ env.CORE_LOVE_PACKAGE_PATH }} ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.love - name: Upload artifact uses: actions/upload-artifact@v4 with: name: ${{ needs.get-info.outputs.base-name }}_Core_love path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.love - name: Prepare for release if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }} shell: bash run: | mkdir -p ${{ env.RELEASE_FOLDER }} cp ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.love ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Bare.love - name: Upload release if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }} uses: ncipollo/release-action@v1 with: allowUpdates: true artifacts: ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Bare.love body: ${{ needs.get-info.outputs.update-note }} name: ${{ needs.get-info.outputs.update-title }} prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }} auto-test: runs-on: ubuntu-22.04 needs: build-core env: APPIMAGE_PATH: ./love.AppImage steps: - uses: actions/checkout@v4 with: submodules: recursive - name: Love actions for testing uses: love-actions/love-actions-test@v1 with: font-path: ./parts/fonts/proportional.otf language-folder: ./parts/language - name: Download core love package uses: actions/download-artifact@v4 with: name: ${{ env.CORE_LOVE_ARTIFACT_NAME }} - name: Download love shell: bash run: | curl --retry 5 https://github.com/love2d/love/releases/download/11.5/love-11.5-x86_64.AppImage -o ${{ env.APPIMAGE_PATH }} chmod +x ${{ env.APPIMAGE_PATH }} - name: Install dependencies shell: bash run: | sudo apt-get update sudo apt-get install alsa-oss alsa-utils libfuse2 pavucontrol pulseaudio pulseaudio-utils x11-xserver-utils xvfb -y - name: Run automated test shell: bash run: | xvfb-run --auto-servernum ${{ env.APPIMAGE_PATH }} ${{ env.CORE_LOVE_PACKAGE_PATH }} --test build-android: runs-on: ubuntu-latest needs: [get-info, build-core, auto-test] if: github.event_name != 'pull_request' env: COLD_CLEAR_FOLDER: ./libAndroid OUTPUT_FOLDER: ./build RELEASE_FOLDER: ./release steps: - uses: actions/checkout@v4 with: submodules: recursive - name: Process app name id: process-app-name shell: python3 {0} run: | import os import re with open(os.getenv('GITHUB_OUTPUT'), 'a') as f: if "${{ env.BUILD_TYPE }}" == "dev": f.write('bundle-id=org.f26_studio.' + re.sub(r'[^A-Za-z0-9]+', '_', '${{ needs.get-info.outputs.app-name }}') + '.snapshot\n') f.write('product-name=' + re.sub(r'[^A-Za-z0-9]+', '-', '${{ needs.get-info.outputs.app-name }}') + '_Snapshot\n') else: f.write('bundle-id=org.f26_studio.' + re.sub(r'[^A-Za-z0-9]+', '_', '${{ needs.get-info.outputs.app-name }}') + '\n') f.write('product-name=' + re.sub(r'[^A-Za-z0-9]+', '-', '${{ needs.get-info.outputs.app-name }}') + '\n') - name: Download core love package uses: actions/download-artifact@v4 with: name: ${{ env.CORE_LOVE_ARTIFACT_NAME }} - name: Download ColdClear uses: ./.github/actions/get-unzip with: url: ${{ env.COLD_CLEAR_DOWNLOAD_URL }}/Android.zip dir: ${{ env.COLD_CLEAR_FOLDER }} - name: Build Android packages id: build-packages uses: love-actions/love-actions-android@v2 with: app-name: ${{ needs.get-info.outputs.app-name }} bundle-id: ${{ steps.process-app-name.outputs.bundle-id }} icon-specifier: "@mipmap/icon" keystore-alias: ${{ secrets.ANDROID_KEYSTORE_ALIAS }} keystore-base64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} keystore-key-password: ${{ secrets.ANDROID_KEYSTORE_KEYPASSWORD }} keystore-store-password: ${{ secrets.ANDROID_KEYSTORE_STOREPASSWORD }} love-package: ${{ env.CORE_LOVE_PACKAGE_PATH }} resource-path: ${{ env.BUILD_ASSETS_FOLDER }}/android/${{ env.BUILD_TYPE }}/res extra-assets: ${{ env.COLD_CLEAR_FOLDER }} custom-scheme: studio26f://oauth product-name: ${{ steps.process-app-name.outputs.product-name }} version-string: ${{ needs.get-info.outputs.version-string }} version-code: ${{ needs.get-info.outputs.version-code }} output-folder: ${{ env.OUTPUT_FOLDER }} - name: Upload artifact uses: actions/upload-artifact@v4 with: name: ${{ needs.get-info.outputs.base-name }}_Android_release path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}-release.apk - name: Prepare for release if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }} shell: bash run: | mkdir -p ${{ env.RELEASE_FOLDER }} cp ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}-release.apk ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Android.apk - name: Upload release if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }} uses: ncipollo/release-action@v1 with: allowUpdates: true artifacts: ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Android.apk body: ${{ needs.get-info.outputs.update-note }} name: ${{ needs.get-info.outputs.update-title }} prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }} build-linux: runs-on: ubuntu-latest needs: [get-info, build-core, auto-test] env: COLD_CLEAR_FOLDER: ./ColdClear OUTPUT_FOLDER: ./build RELEASE_FOLDER: ./release steps: - uses: actions/checkout@v4 with: submodules: recursive - name: Process app name id: process-app-name shell: python3 {0} run: | import os import re product_name = re.sub(r'[^A-Za-z0-9]+', '-', '${{ needs.get-info.outputs.app-name }}').strip('-').lower() with open(os.getenv('GITHUB_OUTPUT'), 'a') as f: f.write('bundle-id=org.26f-studio.' + product_name + '\n') f.write('product-name=' + product_name + '\n') - name: Download core love package uses: actions/download-artifact@v4 with: name: ${{ env.CORE_LOVE_ARTIFACT_NAME }} - name: Add icon to love package run: | cp ${{ env.BUILD_ASSETS_FOLDER }}/linux/${{ env.BUILD_TYPE }}/icon.png media/image/icon.png zip -u ${{ env.CORE_LOVE_PACKAGE_PATH }} media/image/icon.png rm media/image/icon.png - name: Download ColdClear uses: ./.github/actions/get-unzip with: url: ${{ env.COLD_CLEAR_DOWNLOAD_URL }}/Linux.zip dir: ${{ env.COLD_CLEAR_FOLDER }} - name: Process ColdClear shell: bash run: | cd ${{ env.COLD_CLEAR_FOLDER }} mkdir -p ./lib/lua/5.1 mv ./x64/CCloader.so ./lib/lua/5.1 - name: Build Linux packages id: build-packages uses: love-actions/love-actions-linux@v2 with: app-name: ${{ needs.get-info.outputs.app-name }} bundle-id: ${{ steps.process-app-name.outputs.bundle-id }} description: Techmino is fun! version-string: ${{ needs.get-info.outputs.version-string }} icon-path: ${{ env.BUILD_ASSETS_FOLDER }}/linux/${{ env.BUILD_TYPE }}/icon.png love-package: ${{ env.CORE_LOVE_PACKAGE_PATH }} lib-path: ${{ env.COLD_CLEAR_FOLDER }}/lib product-name: ${{ steps.process-app-name.outputs.product-name }} output-folder: ${{ env.OUTPUT_FOLDER }} - name: Upload AppImage artifact uses: actions/upload-artifact@v4 with: name: ${{ needs.get-info.outputs.base-name }}_Linux_AppImage path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.AppImage - name: Upload Debian artifact uses: actions/upload-artifact@v4 with: name: ${{ needs.get-info.outputs.base-name }}_Linux_Debian path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.deb - name: Prepare for release if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }} shell: bash run: | mkdir -p ${{ env.RELEASE_FOLDER }} cp ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.AppImage ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Linux.AppImage cp ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.deb ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Linux.deb - name: Upload release if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }} uses: ncipollo/release-action@v1 with: allowUpdates: true artifacts: | ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Linux.AppImage ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Linux.deb body: ${{ needs.get-info.outputs.update-note }} name: ${{ needs.get-info.outputs.update-title }} prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }} build-web-compat: runs-on: ubuntu-latest needs: [get-info, build-core, auto-test] env: MEMORY_LIMIT: 128000000 OUTPUT_FOLDER: ./build steps: - uses: actions/checkout@v4 with: submodules: recursive - name: Download core love package uses: actions/download-artifact@v4 with: name: ${{ env.CORE_LOVE_ARTIFACT_NAME }} - name: Build web packages run: | npx love.js ${{ env.CORE_LOVE_PACKAGE_PATH }} ${{ env.OUTPUT_FOLDER }} -t "${{ needs.get-info.outputs.app-name }}" -m ${{ env.MEMORY_LIMIT }} -c - name: Move assets run: | mv ${{ env.BUILD_ASSETS_FOLDER }}/web/${{ env.BUILD_TYPE }}/* ${{ env.OUTPUT_FOLDER }} - name: Initialize Love.js Api Player run: | pushd ${{ env.OUTPUT_FOLDER }} wget https://raw.githubusercontent.com/MrcSnm/Love.js-Api-Player/refs/heads/master/consolewrapper.js wget https://raw.githubusercontent.com/MrcSnm/Love.js-Api-Player/refs/heads/master/globalizeFS.js wget https://raw.githubusercontent.com/MrcSnm/Love.js-Api-Player/refs/heads/master/webdb.js node globalizeFS.js rm globalizeFS.js popd - name: Upload artifact uses: actions/upload-artifact@v4 with: name: ${{ needs.get-info.outputs.base-name }}_Web path: ${{ env.OUTPUT_FOLDER }} - name: Deploy to GitHub Pages uses: crazy-max/ghaction-github-pages@v4 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: build_dir: ${{ env.OUTPUT_FOLDER }} keep_history: false target_branch: gh-pages build-windows: runs-on: windows-latest needs: [get-info, build-core, auto-test] env: COLD_CLEAR_FOLDER: ./ColdClear OUTPUT_FOLDER: ./build RELEASE_FOLDER: ./release steps: - uses: actions/checkout@v4 with: submodules: recursive - name: Process app name id: process-app-name shell: python3 {0} run: | import os import re with open(os.getenv('GITHUB_OUTPUT'), 'a') as f: f.write('product-name=' + re.sub(r'[^A-Za-z0-9]+', '_', '${{ needs.get-info.outputs.app-name }}') + '\n') - name: Download core love package uses: actions/download-artifact@v4 with: name: ${{ env.CORE_LOVE_ARTIFACT_NAME }} - name: Download ColdClear uses: ./.github/actions/get-unzip with: url: ${{ env.COLD_CLEAR_DOWNLOAD_URL }}/Windows.zip dir: ${{ env.COLD_CLEAR_FOLDER }} - name: Update Windows template shell: python3 {0} run: | version_string = "${{ needs.get-info.outputs.version-string }}" file_version = (f"{version_string.replace('.', ',')},0") with open("${{ env.BUILD_ASSETS_FOLDER }}/windows/${{ env.BUILD_TYPE }}/template.rc", "r+", encoding="utf8") as file: data = file.read() data = data\ .replace("@Version", version_string)\ .replace("@FileVersion", file_version) file.seek(0) file.truncate() file.write(data) - name: Build Windows packages id: build-packages uses: love-actions/love-actions-windows@v2 with: icon-path: ${{ env.BUILD_ASSETS_FOLDER }}/windows/${{ env.BUILD_TYPE }}/icon.ico rc-path: ${{ env.BUILD_ASSETS_FOLDER }}/windows/${{ env.BUILD_TYPE }}/template.rc love-package: ${{ env.CORE_LOVE_PACKAGE_PATH }} extra-assets-x86: ${{ env.COLD_CLEAR_FOLDER }}/x86/CCloader.dll ${{ env.COLD_CLEAR_FOLDER }}/x86/cold_clear.dll ${{ env.BUILD_ASSETS_FOLDER }}/extraLibs/Windows_x64/discord-rpc.dll extra-assets-x64: ${{ env.COLD_CLEAR_FOLDER }}/x64/CCloader.dll ${{ env.COLD_CLEAR_FOLDER }}/x64/cold_clear.dll ${{ env.BUILD_ASSETS_FOLDER }}/extraLibs/Windows_x86/discord-rpc.dll product-name: ${{ steps.process-app-name.outputs.product-name }} app-id: ${{ secrets.WINDOWS_APP_ID }} project-website: https://www.studio26f.org/ installer-languages: ChineseSimplified.isl ChineseTraditional.isl English.isl Spanish.isl French.isl Indonesian.isl Japanese.isl Portuguese.isl output-folder: ${{ env.OUTPUT_FOLDER }} - name: Upload 32-bit artifact uses: actions/upload-artifact@v4 with: name: ${{ needs.get-info.outputs.base-name }}_Windows_x86 path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_x86.zip - name: Upload 64-bit artifact uses: actions/upload-artifact@v4 with: name: ${{ needs.get-info.outputs.base-name }}_Windows_x64 path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_x64.zip - name: Upload installer artifact uses: actions/upload-artifact@v4 with: name: ${{ needs.get-info.outputs.base-name }}_Windows_installer path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_installer.exe - name: Prepare for release if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }} shell: bash run: | mkdir -p ${{ env.RELEASE_FOLDER }} cp ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_x86.zip ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Windows_x86.zip cp ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_x64.zip ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Windows_x64.zip cp ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_installer.exe ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Windows_installer.exe - name: Upload release if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }} uses: ncipollo/release-action@v1 with: allowUpdates: true artifacts: | ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Windows_x86.zip ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Windows_x64.zip ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Windows_installer.exe body: ${{ needs.get-info.outputs.update-note }} name: ${{ needs.get-info.outputs.update-title }} prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }} post-build: runs-on: ubuntu-latest if: ${{ always() }} needs: [ get-info, auto-test, build-core, build-android, build-linux, build-web-compat, build-windows, ] env: ACTION_TYPE: ${{ fromJSON('[["Development", "Pre-release"], ["Release", "Release"]]')[startsWith(github.ref, 'refs/tags/v')][startsWith(github.ref, 'refs/tags/pre')] }} steps: - name: Cleanup uses: geekyeggo/delete-artifact@v5 with: name: ${{ env.CORE_LOVE_ARTIFACT_NAME }} - name: Send Discord message if: github.ref == 'refs/heads/main' shell: python run: | import requests import json headers = { 'Content-Type': 'application/json', } payload = json.dumps({ "content": "Github Actions for **${{ github.repository }}**.", "embeds": [ { "author": { "name": "${{ needs.get-info.outputs.app-name }} [${{ env.ACTION_TYPE }}]", "url": "https://github.com/${{ github.repository }}" }, "title": "${{ needs.get-info.outputs.app-name }} (${{ needs.get-info.outputs.version-name }}) Build Result", "description": "CI triggered at ${{ needs.get-info.outputs.commit-hash }}", "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", "thumbnail": { "url": "https://raw.githubusercontent.com/${{ github.repository }}/main/.github/build/linux/${{ env.BUILD_TYPE }}/icon.png" }, "color": 36863, "fields": [ { "name": "Version", "value": "${{ needs.get-info.outputs.version-string }}", "inline": True }, { "name": "Package Name", "value": "${{ needs.get-info.outputs.base-name }}", "inline": True }, { "name": "Status", "value": "**Automatic Test:** ${{ needs.auto-test.result }}\n**Core:** ${{ needs.build-core.result }}\n**Android:** ${{ needs.build-android.result }}\n**Linux:** ${{ needs.build-linux.result }}\n**Web compatible:** ${{ needs.build-web-compat.result }}\n**Windows:** ${{ needs.build-windows.result }}" } ] } ] }) requests.request("POST", "${{ secrets.DISCORD_WEBHOOK }}", headers=headers, data=payload)