Release #104
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: Release | |
| on: | |
| schedule: | |
| - cron: "40 23 * * *" | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Release version (e.g. 0.1.0). Leave empty for nightly." | |
| required: false | |
| type: string | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: release-${{ github.event.inputs.version || 'nightly' }} | |
| cancel-in-progress: true | |
| env: | |
| DOTNET_VERSION: "10.0.x" | |
| NODE_VERSION: "24" | |
| NIGHTLY_BADGE_BRANCH: "badges" | |
| jobs: | |
| prepare: | |
| name: Prepare metadata | |
| runs-on: ubuntu-latest | |
| outputs: | |
| is_nightly: ${{ steps.meta.outputs.is_nightly }} | |
| version: ${{ steps.meta.outputs.version }} | |
| tag: ${{ steps.meta.outputs.tag }} | |
| ref: ${{ steps.meta.outputs.ref }} | |
| steps: | |
| - name: Resolve release mode | |
| id: meta | |
| shell: bash | |
| env: | |
| INPUT_VERSION: ${{ github.event.inputs.version || '' }} | |
| run: | | |
| set -euo pipefail | |
| VERSION="${INPUT_VERSION}" | |
| if [ -z "${VERSION}" ]; then | |
| echo "is_nightly=true" >> "$GITHUB_OUTPUT" | |
| echo "version=nightly" >> "$GITHUB_OUTPUT" | |
| echo "tag=nightly" >> "$GITHUB_OUTPUT" | |
| echo "ref=${GITHUB_SHA}" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| if [[ ! "${VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then | |
| echo "Invalid version: ${VERSION}" >&2 | |
| exit 1 | |
| fi | |
| echo "is_nightly=false" >> "$GITHUB_OUTPUT" | |
| echo "version=${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "tag=v${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "ref=${GITHUB_SHA}" >> "$GITHUB_OUTPUT" | |
| prepare-nightly-metadata: | |
| name: Prepare nightly metadata | |
| needs: prepare | |
| if: needs.prepare.outputs.is_nightly == 'true' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| should_run: ${{ steps.detect.outputs.should_run }} | |
| steps: | |
| - name: Compare current SHA with published nightly SHA | |
| id: detect | |
| shell: bash | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| GH_TOKEN: ${{ github.token }} | |
| REPO: ${{ github.repository }} | |
| BRANCH: ${{ env.NIGHTLY_BADGE_BRANCH }} | |
| SHA: ${{ needs.prepare.outputs.ref }} | |
| run: | | |
| set -euo pipefail | |
| if [ "${EVENT_NAME}" = "workflow_dispatch" ]; then | |
| echo "Manual nightly trigger detected; forcing rebuild." | |
| echo "should_run=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| mkdir -p prior-meta | |
| PUBLISHED_SHA="" | |
| if gh api "repos/${REPO}/contents/nightly-meta.json?ref=${BRANCH}" --jq '.content' > prior-meta/nightly-meta.base64 2>/dev/null; then | |
| tr -d '\n' < prior-meta/nightly-meta.base64 | base64 -d > prior-meta/nightly-meta.json | |
| PUBLISHED_SHA="$(jq -r '.current_artifact.published_sha // ""' prior-meta/nightly-meta.json)" | |
| fi | |
| SHOULD_RUN="true" | |
| if [ -n "${PUBLISHED_SHA}" ] && [ "${PUBLISHED_SHA}" = "${SHA}" ]; then | |
| SHOULD_RUN="false" | |
| echo "Nightly release already published for ${SHA}; skipping rebuild." | |
| fi | |
| echo "should_run=${SHOULD_RUN}" >> "$GITHUB_OUTPUT" | |
| - name: Checkout repository | |
| if: steps.detect.outputs.should_run == 'true' | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 1 | |
| ref: ${{ needs.prepare.outputs.ref }} | |
| - name: Render nightly status files | |
| if: steps.detect.outputs.should_run == 'true' | |
| uses: ./.github/actions/render-nightly-status | |
| with: | |
| github_token: ${{ github.token }} | |
| repo: ${{ github.repository }} | |
| branch: ${{ env.NIGHTLY_BADGE_BRANCH }} | |
| run_id: ${{ github.run_id }} | |
| event_name: ${{ github.event_name }} | |
| sha: ${{ needs.prepare.outputs.ref }} | |
| phase: start | |
| - name: Update nightly release | |
| if: steps.detect.outputs.should_run == 'true' | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| REPO: ${{ github.repository }} | |
| SHA: ${{ needs.prepare.outputs.ref }} | |
| run: | | |
| set -euo pipefail | |
| if gh release view nightly -R "${REPO}" >/dev/null 2>&1; then | |
| gh release edit nightly -R "${REPO}" \ | |
| --title "nightly" \ | |
| --prerelease \ | |
| --notes-file out/release-notes.md | |
| else | |
| gh release create nightly -R "${REPO}" \ | |
| --target "${SHA}" \ | |
| --title "nightly" \ | |
| --prerelease \ | |
| --notes-file out/release-notes.md | |
| fi | |
| - name: Publish nightly metadata branch | |
| if: steps.detect.outputs.should_run == 'true' | |
| uses: ./.github/actions/publish-nightly-metadata | |
| with: | |
| github_token: ${{ github.token }} | |
| repo: ${{ github.repository }} | |
| branch: ${{ env.NIGHTLY_BADGE_BRANCH }} | |
| bump-version: | |
| name: Bump version, tag, and push | |
| needs: prepare | |
| if: needs.prepare.outputs.is_nightly == 'false' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| ref: ${{ steps.push.outputs.ref }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node and Yarn | |
| uses: ./.github/actions/setup-node-yarn | |
| with: | |
| node_version: ${{ env.NODE_VERSION }} | |
| - name: Setup Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Bump Cargo version | |
| run: yarn build:bump --version "${{ needs.prepare.outputs.version }}" | |
| - name: Update Cargo.lock | |
| run: cargo update | |
| - name: Commit, tag, and push | |
| id: push | |
| shell: bash | |
| env: | |
| VERSION: ${{ needs.prepare.outputs.version }} | |
| TAG: ${{ needs.prepare.outputs.tag }} | |
| run: | | |
| set -euo pipefail | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add Cargo.toml Cargo.lock | |
| if git diff --cached --quiet; then | |
| echo "No version changes to commit." >&2 | |
| exit 1 | |
| fi | |
| git commit -m "build: bump version to v${VERSION}" | |
| git tag "${TAG}" | |
| git push origin HEAD | |
| git push origin "${TAG}" | |
| echo "ref=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" | |
| build: | |
| name: Build ${{ matrix.name }} | |
| needs: | |
| - prepare | |
| - prepare-nightly-metadata | |
| - bump-version | |
| if: always() && needs.prepare.result == 'success' && ((needs.prepare.outputs.is_nightly == 'true' && needs.prepare-nightly-metadata.result == 'success' && needs.prepare-nightly-metadata.outputs.should_run == 'true') || (needs.prepare.outputs.is_nightly == 'false' && needs.bump-version.result == 'success')) | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - name: linux-x64 | |
| os: ubuntu-latest | |
| rust-target: x86_64-unknown-linux-gnu | |
| dotnet-rid: linux-x64 | |
| rust-binary: udon-decompiler | |
| dumper-binary: UdonProgramDumper | |
| - name: windows-x64 | |
| os: windows-latest | |
| rust-target: x86_64-pc-windows-msvc | |
| dotnet-rid: win-x64 | |
| rust-binary: udon-decompiler.exe | |
| dumper-binary: UdonProgramDumper.exe | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.bump-version.outputs.ref || needs.prepare.outputs.ref }} | |
| - name: Setup Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.rust-target }} | |
| - name: Setup Node and Yarn | |
| uses: ./.github/actions/setup-node-yarn | |
| with: | |
| node_version: ${{ env.NODE_VERSION }} | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| dotnet-version: ${{ env.DOTNET_VERSION }} | |
| - name: Create output directory | |
| shell: bash | |
| run: mkdir -p dist/${{ matrix.name }} | |
| - name: Copy clang-format | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| CLANG_FORMAT_PATH="$(node -e "process.stdout.write(require('clang-format-node').clangFormatPath)")" | |
| LLVM_LICENSE_URL="https://raw.githubusercontent.com/llvm/llvm-project/main/LICENSE.TXT" | |
| TARGET_DIR="dist/${{ matrix.name }}" | |
| cp "${CLANG_FORMAT_PATH}" "${TARGET_DIR}/$(basename "${CLANG_FORMAT_PATH}")" | |
| curl -fL \ | |
| --retry 6 \ | |
| --retry-delay 2 \ | |
| --retry-max-time 120 \ | |
| --retry-all-errors \ | |
| "${LLVM_LICENSE_URL}" \ | |
| -o "${TARGET_DIR}/LLVM_LICENSE" | |
| - name: Build udon-decompiler | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| TARGET_DIR="dist/${{ matrix.name }}" | |
| cargo build --release --locked --target ${{ matrix.rust-target }} --bin udon-decompiler | |
| cp "target/${{ matrix.rust-target }}/release/${{ matrix.rust-binary }}" "${TARGET_DIR}/${{ matrix.rust-binary }}" | |
| cp LICENSE "${TARGET_DIR}/LICENSE" | |
| - name: Build UdonProgramDumper | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| TARGET_DIR="dist/${{ matrix.name }}" | |
| dotnet publish tools/UdonProgramDumper/UdonProgramDumper.csproj \ | |
| -c Release \ | |
| -r ${{ matrix.dotnet-rid }} \ | |
| -p:PublishAot=true | |
| cp "tools/UdonProgramDumper/bin/Release/net10.0/${{ matrix.dotnet-rid }}/publish/${{ matrix.dumper-binary }}" "${TARGET_DIR}/${{ matrix.dumper-binary }}" | |
| - name: Upload artifact directory | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: ${{ matrix.name }} | |
| path: dist/${{ matrix.name }} | |
| if-no-files-found: error | |
| retention-days: 14 | |
| package-editor-scripts: | |
| name: Package editor scripts | |
| needs: | |
| - prepare | |
| - prepare-nightly-metadata | |
| - bump-version | |
| if: always() && needs.prepare.result == 'success' && ((needs.prepare.outputs.is_nightly == 'true' && needs.prepare-nightly-metadata.result == 'success' && needs.prepare-nightly-metadata.outputs.should_run == 'true') || (needs.prepare.outputs.is_nightly == 'false' && needs.bump-version.result == 'success')) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.bump-version.outputs.ref || needs.prepare.outputs.ref }} | |
| - name: Create output directory | |
| shell: bash | |
| run: mkdir -p dist/editor-scripts | |
| - name: Copy editor scripts | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| cp -r tools/Editor/. dist/editor-scripts/ | |
| cp LICENSE dist/editor-scripts/LICENSE | |
| - name: Upload artifact directory | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: editor-scripts | |
| path: dist/editor-scripts | |
| if-no-files-found: error | |
| retention-days: 14 | |
| publish-nightly: | |
| name: Publish nightly GitHub release | |
| needs: | |
| - prepare | |
| - prepare-nightly-metadata | |
| - build | |
| - package-editor-scripts | |
| if: always() && needs.prepare.outputs.is_nightly == 'true' && needs.prepare-nightly-metadata.result == 'success' && needs.prepare-nightly-metadata.outputs.should_run == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 1 | |
| ref: ${{ needs.prepare.outputs.ref }} | |
| - name: Render nightly status files | |
| id: nightly-status | |
| uses: ./.github/actions/render-nightly-status | |
| with: | |
| github_token: ${{ github.token }} | |
| repo: ${{ github.repository }} | |
| branch: ${{ env.NIGHTLY_BADGE_BRANCH }} | |
| run_id: ${{ github.run_id }} | |
| event_name: ${{ github.event_name }} | |
| sha: ${{ needs.prepare.outputs.ref }} | |
| phase: finalize | |
| build_result: ${{ needs.build.result }} | |
| package_result: ${{ needs.package-editor-scripts.result }} | |
| - name: Download build artifacts | |
| if: steps.nightly-status.outputs.result == 'success' | |
| uses: actions/download-artifact@v8 | |
| with: | |
| path: release-assets | |
| - name: Package nightly assets | |
| if: steps.nightly-status.outputs.result == 'success' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| cd release-assets | |
| for dir in */; do | |
| [ -d "$dir" ] || continue | |
| name="${dir%/}" | |
| zip -r "${name}.zip" "$name" | |
| done | |
| - name: Checkout repository | |
| if: steps.nightly-status.outputs.result == 'success' | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| path: repo | |
| ref: ${{ needs.prepare.outputs.ref }} | |
| - name: Move nightly tag to current commit | |
| if: steps.nightly-status.outputs.result == 'success' | |
| shell: bash | |
| env: | |
| SHA: ${{ needs.prepare.outputs.ref }} | |
| run: | | |
| set -euo pipefail | |
| git -C repo config user.name "github-actions[bot]" | |
| git -C repo config user.email "github-actions[bot]@users.noreply.github.com" | |
| git -C repo tag -f nightly "${SHA}" | |
| git -C repo push origin refs/tags/nightly --force | |
| - name: Update nightly release | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| REPO: ${{ github.repository }} | |
| SHA: ${{ needs.prepare.outputs.ref }} | |
| RESULT: ${{ steps.nightly-status.outputs.result }} | |
| run: | | |
| set -euo pipefail | |
| if gh release view nightly -R "${REPO}" >/dev/null 2>&1; then | |
| gh release edit nightly -R "${REPO}" \ | |
| --title "nightly" \ | |
| --prerelease \ | |
| --notes-file out/release-notes.md | |
| else | |
| gh release create nightly -R "${REPO}" \ | |
| --target "${SHA}" \ | |
| --title "nightly" \ | |
| --prerelease \ | |
| --notes-file out/release-notes.md | |
| fi | |
| if [ "${RESULT}" = "success" ]; then | |
| gh release upload nightly -R "${REPO}" release-assets/*.zip --clobber | |
| fi | |
| - name: Publish nightly metadata branch | |
| uses: ./.github/actions/publish-nightly-metadata | |
| with: | |
| github_token: ${{ github.token }} | |
| repo: ${{ github.repository }} | |
| branch: ${{ env.NIGHTLY_BADGE_BRANCH }} | |
| draft-release: | |
| name: Create draft GitHub release | |
| needs: | |
| - prepare | |
| - build | |
| - package-editor-scripts | |
| if: needs.prepare.outputs.is_nightly == 'false' && needs.build.result == 'success' && needs.package-editor-scripts.result == 'success' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@v8 | |
| with: | |
| path: release-assets | |
| - name: Package release assets | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| cd release-assets | |
| for dir in */; do | |
| [ -d "$dir" ] || continue | |
| name="${dir%/}" | |
| zip -r "${name}.zip" "$name" | |
| done | |
| - name: Create draft release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ needs.prepare.outputs.tag }} | |
| name: ${{ needs.prepare.outputs.tag }} | |
| draft: true | |
| generate_release_notes: true | |
| fail_on_unmatched_files: true | |
| files: release-assets/*.zip |