Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions .github/actions/setup-unity/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
name: Set up Unity
description: Install Unity CLI and the Editor, then activate a license on this runner.

inputs:
editor-version:
description: Exact Unity Editor version to install.
required: true
email:
description: Unity account email address.
required: true
password:
description: Unity account password.
required: true

runs:
using: composite
steps:
- name: Check Unity credentials
shell: bash
env:
UNITY_EMAIL: ${{ inputs.email }}
UNITY_PASSWORD: ${{ inputs.password }}
run: |
if [[ -z "$UNITY_EMAIL" || -z "$UNITY_PASSWORD" ]]; then
echo "::error::UNITY_EMAIL and UNITY_PASSWORD are required. GitHub does not provide repository secrets to pull requests from forks."
exit 1
fi

- name: Install Linux dependencies
shell: bash
run: |
sudo apt-get update
sudo apt-get install --yes --no-install-recommends \
libasound2 libgbm1 libgconf-2-4 libglu1-mesa libgtk-3-0 \
libncurses5 libnotify4 libnss3 libxss1 libxtst6

# Unity 2021's .NET tooling requires OpenSSL 1.1, absent from Ubuntu 22.04.
- name: Install OpenSSL 1.1 for Unity 2021
if: startsWith(inputs.editor-version, '2021.')
shell: bash
run: |
curl --fail --silent --show-error --location \
https://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2.24_amd64.deb \
--output "$RUNNER_TEMP/libssl1.1.deb"
echo "7cf39d70a639017d1dd7c8d36daa2258063608688e449fddf40ffdd46f992a78 $RUNNER_TEMP/libssl1.1.deb" | sha256sum --check
sudo apt-get install --yes --no-install-recommends "$RUNNER_TEMP/libssl1.1.deb"

- name: Install Unity CLI
shell: bash
env:
UNITY_CLI_CHANNEL: beta
UNITY_CLI_VERSION: 1.0.0-beta.12
UNITY_CLI_HOME: ${{ runner.temp }}/unity-cli
run: |
curl --fail --silent --show-error --location \
https://public-cdn.cloud.unity3d.com/hub/prod/cli/install.sh \
--output "$RUNNER_TEMP/install-unity-cli.sh"
bash "$RUNNER_TEMP/install-unity-cli.sh"
echo "$UNITY_CLI_HOME/bin" >> "$GITHUB_PATH"
"$UNITY_CLI_HOME/bin/unity" --version

- name: Install Unity Editor
shell: bash
env:
UNITY_EDITOR_VERSION: ${{ inputs.editor-version }}
run: unity --non-interactive install "$UNITY_EDITOR_VERSION" --yes --accept-eula

# Unity CLI cannot sign in with email/password. The bundled client can
# activate a license for this runner instead of importing a desktop's ULF.
- name: Activate Unity license
shell: bash
env:
UNITY_EDITOR_VERSION: ${{ inputs.editor-version }}
UNITY_EMAIL: ${{ inputs.email }}
UNITY_PASSWORD: ${{ inputs.password }}
run: |
editor_root="$(unity --non-interactive editors path "$UNITY_EDITOR_VERSION")"
licensing_client="$editor_root/Editor/Data/Resources/Licensing/Client/Unity.Licensing.Client"
test -x "$licensing_client"

umask 077
activation_log="$(mktemp "$RUNNER_TEMP/unity-activation-XXXXXX")"
trap 'rm -f "$activation_log"' EXIT
# Arm cleanup before activation, including a partially failed request.
echo "UNITY_LICENSE_ACTIVATION_ATTEMPTED=true" >> "$GITHUB_ENV"
result=0
timeout 120 "$licensing_client" \
--activate-all --include-personal \
--username "$UNITY_EMAIL" --password "$UNITY_PASSWORD" \
> "$activation_log" 2>&1 || result=$?

if [[ "$result" != 0 ]]; then
if grep -qiE 'no (available )?seats|seat is not available|LICENSE_LIMIT|maximum number of' "$activation_log"; then
echo "::error::Unity reports no available license seats for this account."
elif grep -qiE 'two.?factor|2fa|verification code|unauthorized|invalid (username|password|credentials)' "$activation_log"; then
echo "::error::Unity rejected the credentials or requires interactive account verification."
else
echo "::error::Unity account activation failed (exit $result)."
fi
exit 1
fi

# The client can exit zero without granting a seat. Check license state.
if ! unity --non-interactive license status > "$activation_log" 2>&1; then
echo "::error::Unity account activation did not produce an active license."
exit 1
fi
echo "Unity license is active on this runner."
38 changes: 38 additions & 0 deletions .github/actions/setup-unity/return-license.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -euo pipefail

editor_root="$(unity --non-interactive editors path "$UNITY_EDITOR_VERSION")"
licensing_client="$editor_root/Editor/Data/Resources/Licensing/Client/Unity.Licensing.Client"

umask 077
return_directory="$(mktemp -d "$RUNNER_TEMP/unity-license-return-XXXXXX")"
return_log="$return_directory/return.log"
trap 'rm -rf "$return_directory"' EXIT

result=0
timeout 120 "$licensing_client" --return-ulf \
--username "$UNITY_EMAIL" --password "$UNITY_PASSWORD" \
> "$return_log" 2>&1 || result=$?

# Entitlement licenses have no ULF file; the Editor returns those with the
# account credentials. A scratch project keeps cleanup out of the real project.
if [[ "$result" != 0 ]] && grep -qi 'Ulf license file not found' "$return_log"; then
result=0
timeout 120 "$editor_root/Editor/Unity" \
-batchmode -nographics -quit -returnlicense \
-username "$UNITY_EMAIL" -password "$UNITY_PASSWORD" \
-projectPath "$return_directory/project" -logFile /dev/stdout \
> "$return_log" 2>&1 || result=$?
fi

# The Editor may return the entitlement and then exit nonzero while also
# attempting a ULF return. Its explicit success message confirms the return.
if grep -qE 'Successfully returned ULF license|Successfully returned the entitlement license|License has been returned' "$return_log"; then
result=0
fi

if [[ "$result" != 0 ]]; then
echo "::error::Unity license return failed (exit $result)."
exit 1
fi
echo "Unity license returned."
67 changes: 67 additions & 0 deletions .github/scripts/release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/env bash
set -euo pipefail

operation="${1:?Expected prepare or create}"
case "$operation" in
prepare|create) ;;
*) echo "::error::Expected prepare or create."; exit 1 ;;
esac

# Package versions follow SemVer, including prerelease and build identifiers.
number='(0|[1-9][0-9]*)'
prerelease='(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)'
if [[ ! "$VERSION" =~ ^${number}\.${number}\.${number}(-${prerelease}(\.${prerelease})*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$ ]]; then
echo "::error::Release version must follow SemVer (for example 1.7.1 or 1.8.0-preview.1)."
exit 1
fi
git check-ref-format "refs/tags/$VERSION"

if [[ "$operation" == prepare ]]; then
if [[ "$RELEASE_REF" != refs/heads/* ]]; then
echo "::error::Dispatch Release from a branch."
exit 1
fi

source_sha="$(git rev-parse HEAD)"
current_version="$(jq -er '.version | select(type == "string" and length > 0)' "$TARGET_FILE")"
if [[ "$current_version" != "$VERSION" ]]; then
manifest="$(mktemp "$RUNNER_TEMP/release-package-XXXXXX")"
trap 'rm -f "$manifest"' EXIT
jq --arg version "$VERSION" '.version = $version' "$TARGET_FILE" > "$manifest"
cat "$manifest" > "$TARGET_FILE"
git add -- "$TARGET_FILE"
git commit --only -m "chore(release): update package version to $VERSION" -- "$TARGET_FILE"
fi
RELEASE_SHA="$(git rev-parse HEAD)"
fi

remote_tags="$(git ls-remote --tags origin "refs/tags/$VERSION" "refs/tags/$VERSION^{}")"
tag_commit=""
while read -r object ref; do
case "$ref" in
"refs/tags/$VERSION") tag_commit="$object" ;;
# Annotated tags identify a tag object; compare the peeled commit instead.
"refs/tags/$VERSION^{}") tag_commit="$object"; break ;;
esac
done <<< "$remote_tags"

if [[ -n "$tag_commit" && "$tag_commit" != "$RELEASE_SHA" ]]; then
echo "::error::Tag $VERSION belongs to a different commit. Choose a new release version."
exit 1
fi

if [[ "$operation" == prepare ]]; then
if [[ "$current_version" != "$VERSION" ]]; then
# Require a fast-forward from the checked-out commit and an unchanged remote
# branch. The lease also rejects deletion or rollback during preparation.
git merge-base --is-ancestor "$source_sha" "$RELEASE_SHA"
git push --no-follow-tags --recurse-submodules=no \
--force-with-lease="$RELEASE_REF:$source_sha" origin "$RELEASE_SHA:$RELEASE_REF"
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "sha=$RELEASE_SHA" >> "$GITHUB_OUTPUT"
elif [[ -z "$tag_commit" ]]; then
git tag -- "$VERSION" "$RELEASE_SHA"
# A non-forced tag push rejects a concurrent release for a different commit.
git push --no-follow-tags --recurse-submodules=no origin "refs/tags/$VERSION"
fi
49 changes: 0 additions & 49 deletions .github/workflows/build.yaml

This file was deleted.

45 changes: 0 additions & 45 deletions .github/workflows/package.yaml

This file was deleted.

Loading
Loading