Skip to content

test: de-flake TestFutureWaitingAnotherFuture cancel-while-loading (#42) #10

test: de-flake TestFutureWaitingAnotherFuture cancel-while-loading (#42)

test: de-flake TestFutureWaitingAnotherFuture cancel-while-loading (#42) #10

Workflow file for this run

name: release
on:
push:
branches:
- main
jobs:
setup:
runs-on: ubuntu-latest
outputs:
UnityProjectPath: ${{ steps.vars.outputs.UnityProjectPath }}
UnityPackageRoot: ${{ steps.vars.outputs.UnityPackageRoot }}
InstallerProjectPath: ${{ steps.vars.outputs.InstallerProjectPath }}
InstallerFileName: ${{ steps.vars.outputs.InstallerFileName }}
InstallerExportMethod: ${{ steps.vars.outputs.InstallerExportMethod }}
InstallerUnityVersion: ${{ steps.vars.outputs.InstallerUnityVersion }}
steps:
- id: vars
run: |
echo "UnityProjectPath=./Unity-Package" >> $GITHUB_OUTPUT
echo "UnityPackageRoot=./Assets/root" >> $GITHUB_OUTPUT
echo "InstallerProjectPath=./Installer" >> $GITHUB_OUTPUT
echo "InstallerFileName=ImageLoader-Installer" >> $GITHUB_OUTPUT
echo "InstallerExportMethod=com.IvanMurzak.Unity.ImageLoader.Installer.PackageExporter.ExportPackage" >> $GITHUB_OUTPUT
echo "InstallerUnityVersion=2019.4.40f1" >> $GITHUB_OUTPUT
check-version-tag:
needs: setup
runs-on: ubuntu-latest
outputs:
version: ${{ steps.get_version.outputs.current-version }}
prev_tag: ${{ steps.prev_tag.outputs.tag }}
tag_exists: ${{ steps.tag_exists.outputs.exists }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Get version from package.json
id: get_version
uses: martinbeentjes/npm-get-version-action@v1.3.1
with:
path: ${{ needs.setup.outputs.UnityProjectPath }}/${{ needs.setup.outputs.UnityPackageRoot }}
- name: Find previous version tag
id: prev_tag
uses: WyriHaximus/github-action-get-previous-tag@v1
- name: Check if tag exists
id: tag_exists
uses: mukunku/tag-exists-action@v1.6.0
with:
tag: ${{ steps.get_version.outputs.current-version }}
# Generates release notes (release.md) in parallel with tests/builds.
# The artifact is consumed by release-unity-plugin's atomic publish step.
prepare-release-notes:
runs-on: ubuntu-latest
needs: [check-version-tag]
if: needs.check-version-tag.outputs.tag_exists == 'false'
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Generate release description
env:
GH_TOKEN: ${{ github.token }}
run: |
set -e
version=${{ needs.check-version-tag.outputs.version }}
prev_tag=${{ needs.check-version-tag.outputs.prev_tag }}
repo_url="https://github.com/${GITHUB_REPOSITORY}"
today=$(date +'%B %e, %Y')
echo "repo_url: $repo_url"
echo "today: $today"
echo "# Unity ImageLoader $version" > release.md
echo "**Released:** *$today*" >> release.md
echo "" >> release.md
echo "---" >> release.md
echo "" >> release.md
if [ -n "$prev_tag" ]; then
echo "## Comparison" >> release.md
echo "See every change: [Compare $prev_tag...$version]($repo_url/compare/$prev_tag...$version)" >> release.md
echo "" >> release.md
echo "---" >> release.md
echo "" >> release.md
echo "## Commit Summary (Newest → Oldest)" >> release.md
for sha in $(git log --pretty=format:'%H' $prev_tag..HEAD); do
username=$(gh api repos/${GITHUB_REPOSITORY}/commits/$sha --jq '.author.login // .commit.author.name' 2>/dev/null || true)
if [ -z "$username" ]; then
username=$(git log -1 --pretty=format:'%an' $sha)
fi
message=$(git log -1 --pretty=format:'%s' $sha)
short_sha=$(git log -1 --pretty=format:'%h' $sha)
echo "- [\`$short_sha\`]($repo_url/commit/$sha) — $message by @$username" >> release.md
done
fi
- name: Upload release notes as artifact
uses: actions/upload-artifact@v4
with:
name: release-notes
path: ./release.md
# Builds the signed UPM package (.tgz with package/.attestation.p7m) in parallel
# with tests/builds and uploads it as a `signed-upm-package` artifact for the
# atomic release publish in release-unity-plugin.
#
# HARD-GATE: this job is NOT continue-on-error. Missing UPM signing secrets fail
# fast and the release pipeline halts — no GitHub Release is created without a
# signed UPM tarball.
#
# Required repo secrets:
# - UPM_SERVICE_ACCOUNT_KEY_ID
# - UPM_SERVICE_ACCOUNT_KEY_SECRET
# - UPM_ORG_ID
# See https://openupm.com/docs/signing-upm-packages.html for the procedure.
build-signed-upm-package:
runs-on: ubuntu-latest
needs: [check-version-tag]
if: needs.check-version-tag.outputs.tag_exists == 'false'
env:
UPM_SERVICE_ACCOUNT_KEY_ID: ${{ secrets.UPM_SERVICE_ACCOUNT_KEY_ID }}
UPM_SERVICE_ACCOUNT_KEY_SECRET: ${{ secrets.UPM_SERVICE_ACCOUNT_KEY_SECRET }}
UPM_ORG_ID: ${{ secrets.UPM_ORG_ID }}
PACKAGE_DIR: Unity-Package/Assets/root
DIST_DIR: /tmp/signed-upm-dist
steps:
- name: Verify signing secrets are configured
run: |
missing=()
[ -z "$UPM_SERVICE_ACCOUNT_KEY_ID" ] && missing+=("UPM_SERVICE_ACCOUNT_KEY_ID")
[ -z "$UPM_SERVICE_ACCOUNT_KEY_SECRET" ] && missing+=("UPM_SERVICE_ACCOUNT_KEY_SECRET")
[ -z "$UPM_ORG_ID" ] && missing+=("UPM_ORG_ID")
if [ "${#missing[@]}" -ne 0 ]; then
printf '::error::UPM signing secrets are not configured (%s). The release pipeline is hard-gated on signing.\n' "${missing[*]}"
exit 1
fi
- name: Checkout repository
uses: actions/checkout@v4
- name: Log package metadata
run: |
package_name="$(jq -r '.name' "$PACKAGE_DIR/package.json")"
package_version="$(jq -r '.version' "$PACKAGE_DIR/package.json")"
printf 'Package name: %s\n' "$package_name"
printf 'Package version: %s\n' "$package_version"
- name: Install Unity UPM CLI
run: |
curl -fsSL https://cdn.packages.unity.com/upm-cli/install.sh -o install.sh
bash install.sh
echo "$HOME/.upm/bin" >> "$GITHUB_PATH"
- name: Verify Unity UPM CLI
run: upm --help
- name: Sign package
run: |
mkdir -p "$DIST_DIR"
upm pack "./$PACKAGE_DIR" --organization-id "$UPM_ORG_ID" --destination "$DIST_DIR"
- name: Verify signed package contains attestation
run: |
shopt -s nullglob
archives=("$DIST_DIR"/*.tgz "$DIST_DIR"/*.tar.gz)
if [ "${#archives[@]}" -ne 1 ]; then
printf 'Expected exactly one signed package archive, found %s: %s\n' "${#archives[@]}" "${archives[*]:-<none>}" >&2
exit 1
fi
archive="${archives[0]}"
archive_basename="$(basename "$archive")"
# OpenUPM consumes the asset via the githubReleaseAssetName: 'extensions.unity.imageloader-' prefix.
# Enforce the contract here so a future upm pack naming change fails CI loudly.
if [[ "$archive_basename" != extensions.unity.imageloader-* ]]; then
printf 'Signed archive basename %q does not begin with the OpenUPM-expected prefix extensions.unity.imageloader-\n' "$archive_basename" >&2
exit 1
fi
archive_entries="$(tar -tzf "$archive")"
grep -qx 'package/package.json' <<<"$archive_entries"
grep -qx 'package/.attestation.p7m' <<<"$archive_entries"
printf 'Signed archive: %s\n' "$archive_basename"
tar -xOzf "$archive" package/package.json | jq '{name, version}'
- name: Upload signed UPM package as artifact
uses: actions/upload-artifact@v4
with:
name: signed-upm-package
path: /tmp/signed-upm-dist/*.tgz
build-unity-installer:
runs-on: [ubuntu-latest]
needs: [setup, check-version-tag]
if: needs.check-version-tag.outputs.tag_exists == 'false'
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Cache Unity Library
uses: actions/cache@v4
with:
path: ${{ needs.setup.outputs.InstallerProjectPath }}/Library
key: Library-Unity-Installer
- name: Test Unity Installer (EditMode)
uses: game-ci/unity-test-runner@v4
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
with:
projectPath: ${{ needs.setup.outputs.InstallerProjectPath }}
unityVersion: ${{ needs.setup.outputs.InstallerUnityVersion }}
customImage: 'unityci/editor:ubuntu-${{ needs.setup.outputs.InstallerUnityVersion }}-base-3'
testMode: editmode
githubToken: ${{ secrets.GITHUB_TOKEN }}
checkName: Unity Installer EditMode Test Results
artifactsPath: artifacts-installer-editmode
customParameters: -CI true -GITHUB_ACTIONS true
- name: Clean Unity artifacts and reset git state
run: |
# Force remove Unity generated files with restricted permissions
sudo rm -rf ${{ needs.setup.outputs.InstallerProjectPath }}/Logs/ || true
sudo rm -rf ${{ needs.setup.outputs.InstallerProjectPath }}/Temp/ || true
sudo rm -rf ./artifacts-installer-editmode/ || true
# Reset only tracked files to their committed state
git reset --hard HEAD
echo "Cleaned Unity artifacts and reset tracked files"
- name: Export Unity Package
uses: game-ci/unity-builder@v5
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
with:
projectPath: ${{ needs.setup.outputs.InstallerProjectPath }}
unityVersion: ${{ needs.setup.outputs.InstallerUnityVersion }}
customImage: 'unityci/editor:ubuntu-${{ needs.setup.outputs.InstallerUnityVersion }}-base-3'
buildName: ${{ needs.setup.outputs.InstallerFileName }}
buildsPath: build
buildMethod: ${{ needs.setup.outputs.InstallerExportMethod }}
customParameters: -CI true -GITHUB_ACTIONS true
# unity-builder@v5 refuses to base semantic versioning on a dirty tree;
# the export needs no versioning so skip that gate.
allowDirtyBuild: true
- name: Upload Unity Package as artifact
uses: actions/upload-artifact@v4
with:
name: unity-installer-package
path: ${{ needs.setup.outputs.InstallerProjectPath }}/build/${{ needs.setup.outputs.InstallerFileName }}.unitypackage
# --- UNITY TESTS ---
# -------------------
# --- EDIT MODE ---
test-unity-2019-4-40f1-editmode:
needs: [setup, build-unity-installer]
uses: ./.github/workflows/test_unity_plugin.yml
with:
projectPath: ${{ needs.setup.outputs.UnityProjectPath }}
unityVersion: '2019.4.40f1'
testMode: 'editmode'
secrets: inherit
test-unity-2020-3-48f1-editmode:
needs: [setup, build-unity-installer]
uses: ./.github/workflows/test_unity_plugin.yml
with:
projectPath: ${{ needs.setup.outputs.UnityProjectPath }}
unityVersion: '2020.3.48f1'
testMode: 'editmode'
secrets: inherit
test-unity-2021-3-45f1-editmode:
needs: [setup, build-unity-installer]
uses: ./.github/workflows/test_unity_plugin.yml
with:
projectPath: ${{ needs.setup.outputs.UnityProjectPath }}
unityVersion: '2021.3.45f1'
testMode: 'editmode'
secrets: inherit
test-unity-2022-3-57f1-editmode:
needs: [setup, build-unity-installer]
uses: ./.github/workflows/test_unity_plugin.yml
with:
projectPath: ${{ needs.setup.outputs.UnityProjectPath }}
unityVersion: '2022.3.57f1'
testMode: 'editmode'
secrets: inherit
test-unity-2023-1-20f1-editmode:
needs: [setup, build-unity-installer]
uses: ./.github/workflows/test_unity_plugin.yml
with:
projectPath: ${{ needs.setup.outputs.UnityProjectPath }}
unityVersion: '2023.1.20f1'
testMode: 'editmode'
secrets: inherit
test-unity-2023-2-20f1-editmode:
needs: [setup, build-unity-installer]
uses: ./.github/workflows/test_unity_plugin.yml
with:
projectPath: ${{ needs.setup.outputs.UnityProjectPath }}
unityVersion: '2023.2.20f1'
testMode: 'editmode'
secrets: inherit
test-unity-6000-0-37f1-editmode:
needs: [setup, build-unity-installer]
uses: ./.github/workflows/test_unity_plugin.yml
with:
projectPath: ${{ needs.setup.outputs.UnityProjectPath }}
unityVersion: '6000.0.37f1'
testMode: 'editmode'
secrets: inherit
# --- PLAY MODE ---
test-unity-2019-4-40f1-playmode:
needs: [setup, build-unity-installer]
uses: ./.github/workflows/test_unity_plugin.yml
with:
projectPath: ${{ needs.setup.outputs.UnityProjectPath }}
unityVersion: '2019.4.40f1'
testMode: 'playmode'
secrets: inherit
test-unity-2020-3-48f1-playmode:
needs: [setup, build-unity-installer]
uses: ./.github/workflows/test_unity_plugin.yml
with:
projectPath: ${{ needs.setup.outputs.UnityProjectPath }}
unityVersion: '2020.3.48f1'
testMode: 'playmode'
secrets: inherit
test-unity-2021-3-45f1-playmode:
needs: [setup, build-unity-installer]
uses: ./.github/workflows/test_unity_plugin.yml
with:
projectPath: ${{ needs.setup.outputs.UnityProjectPath }}
unityVersion: '2021.3.45f1'
testMode: 'playmode'
secrets: inherit
test-unity-2022-3-57f1-playmode:
needs: [setup, build-unity-installer]
uses: ./.github/workflows/test_unity_plugin.yml
with:
projectPath: ${{ needs.setup.outputs.UnityProjectPath }}
unityVersion: '2022.3.57f1'
testMode: 'playmode'
secrets: inherit
test-unity-2023-1-20f1-playmode:
needs: [setup, build-unity-installer]
uses: ./.github/workflows/test_unity_plugin.yml
with:
projectPath: ${{ needs.setup.outputs.UnityProjectPath }}
unityVersion: '2023.1.20f1'
testMode: 'playmode'
secrets: inherit
test-unity-2023-2-20f1-playmode:
needs: [setup, build-unity-installer]
uses: ./.github/workflows/test_unity_plugin.yml
with:
projectPath: ${{ needs.setup.outputs.UnityProjectPath }}
unityVersion: '2023.2.20f1'
testMode: 'playmode'
secrets: inherit
test-unity-6000-0-37f1-playmode:
needs: [setup, build-unity-installer]
uses: ./.github/workflows/test_unity_plugin.yml
with:
projectPath: ${{ needs.setup.outputs.UnityProjectPath }}
unityVersion: '6000.0.37f1'
testMode: 'playmode'
secrets: inherit
# --- STANDALONE ---
test-unity-2019-4-40f1-standalone:
needs: [setup, build-unity-installer]
uses: ./.github/workflows/test_unity_plugin.yml
with:
projectPath: ${{ needs.setup.outputs.UnityProjectPath }}
unityVersion: '2019.4.40f1'
testMode: 'standalone'
secrets: inherit
test-unity-2020-3-48f1-standalone:
needs: [setup, build-unity-installer]
uses: ./.github/workflows/test_unity_plugin.yml
with:
projectPath: ${{ needs.setup.outputs.UnityProjectPath }}
unityVersion: '2020.3.48f1'
testMode: 'standalone'
secrets: inherit
test-unity-2021-3-45f1-standalone:
needs: [setup, build-unity-installer]
uses: ./.github/workflows/test_unity_plugin.yml
with:
projectPath: ${{ needs.setup.outputs.UnityProjectPath }}
unityVersion: '2021.3.45f1'
testMode: 'standalone'
secrets: inherit
test-unity-2022-3-57f1-standalone:
needs: [setup, build-unity-installer]
uses: ./.github/workflows/test_unity_plugin.yml
with:
projectPath: ${{ needs.setup.outputs.UnityProjectPath }}
unityVersion: '2022.3.57f1'
testMode: 'standalone'
secrets: inherit
test-unity-2023-1-20f1-standalone:
needs: [setup, build-unity-installer]
uses: ./.github/workflows/test_unity_plugin.yml
with:
projectPath: ${{ needs.setup.outputs.UnityProjectPath }}
unityVersion: '2023.1.20f1'
testMode: 'standalone'
secrets: inherit
test-unity-2023-2-20f1-standalone:
needs: [setup, build-unity-installer]
uses: ./.github/workflows/test_unity_plugin.yml
with:
projectPath: ${{ needs.setup.outputs.UnityProjectPath }}
unityVersion: '2023.2.20f1'
testMode: 'standalone'
secrets: inherit
test-unity-6000-0-37f1-standalone:
needs: [setup, build-unity-installer]
uses: ./.github/workflows/test_unity_plugin.yml
with:
projectPath: ${{ needs.setup.outputs.UnityProjectPath }}
unityVersion: '6000.0.37f1'
testMode: 'standalone'
secrets: inherit
# -------------------
# Atomic publish point: gated on EVERY prerequisite (tests, installer build,
# signed UPM package, release notes). Downloads all asset artifacts and creates
# the GitHub Release + tag with the full asset set in a SINGLE
# softprops/action-gh-release@v2 call so a failed upload cannot strand the
# release with incomplete assets. Signing failure -> no release (hard gate).
release-unity-plugin:
runs-on: ubuntu-latest
needs: [
setup,
check-version-tag,
prepare-release-notes,
build-unity-installer,
build-signed-upm-package,
test-unity-2019-4-40f1-editmode,
test-unity-2019-4-40f1-playmode,
test-unity-2019-4-40f1-standalone,
test-unity-2020-3-48f1-editmode,
test-unity-2020-3-48f1-playmode,
test-unity-2020-3-48f1-standalone,
test-unity-2021-3-45f1-editmode,
test-unity-2021-3-45f1-playmode,
test-unity-2021-3-45f1-standalone,
test-unity-2022-3-57f1-editmode,
test-unity-2022-3-57f1-playmode,
test-unity-2022-3-57f1-standalone,
test-unity-2023-1-20f1-editmode,
test-unity-2023-1-20f1-playmode,
test-unity-2023-1-20f1-standalone,
test-unity-2023-2-20f1-editmode,
test-unity-2023-2-20f1-playmode,
test-unity-2023-2-20f1-standalone,
test-unity-6000-0-37f1-editmode,
test-unity-6000-0-37f1-playmode,
test-unity-6000-0-37f1-standalone
]
if: needs.check-version-tag.outputs.tag_exists == 'false'
outputs:
version: ${{ needs.check-version-tag.outputs.version }}
steps:
- name: Download release notes artifact
uses: actions/download-artifact@v4
with:
name: release-notes
path: ./release-notes
- name: Download Unity installer artifact
uses: actions/download-artifact@v4
with:
name: unity-installer-package
path: ./assets
- name: Download signed UPM package artifact
uses: actions/download-artifact@v4
with:
name: signed-upm-package
path: ./assets
- name: List assembled release assets
run: |
set -e
echo "Release notes:"
ls -la ./release-notes
echo ""
echo "Release assets:"
ls -la ./assets
- name: Create Tag and Release with all assets
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.check-version-tag.outputs.version }}
name: ${{ needs.check-version-tag.outputs.version }}
body_path: ./release-notes/release.md
draft: false
prerelease: false
fail_on_unmatched_files: true
files: |
./assets/${{ needs.setup.outputs.InstallerFileName }}.unitypackage
./assets/extensions.unity.imageloader-*.tgz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Cleanup job to remove build artifacts after the atomic publish.
cleanup-artifacts:
runs-on: ubuntu-latest
needs: [release-unity-plugin]
if: always()
steps:
- name: Delete Unity Package artifacts
uses: geekyeggo/delete-artifact@v5
with:
name: unity-installer-package
failOnError: false
continue-on-error: true
- name: Delete signed UPM package artifacts
uses: geekyeggo/delete-artifact@v5
with:
name: signed-upm-package
failOnError: false
continue-on-error: true
- name: Delete release notes artifacts
uses: geekyeggo/delete-artifact@v5
with:
name: release-notes
failOnError: false
continue-on-error: true