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
53 changes: 52 additions & 1 deletion dist/platforms/mac/steps/return_license.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ UNITY_LICENSE_RETURN_MAX_ATTEMPTS="${UNITY_LICENSE_RETRY_MAX_ATTEMPTS:-4}"
UNITY_LICENSE_RETURN_RETRY_DELAY_SECONDS=20
UNITY_LICENSE_RETURN_TRANSIENT_PATTERN='TimeoutPolicy did not complete|Access token is unavailable|entitlement groups and 0 free entitlements|License activation has failed|No valid Unity Editor license found|License is not active|Serial number unavailable'

# Permanent by construction - see the guards below.
UNITY_LICENSE_RETURN_PERMANENT_PATTERN="Machine bindings don't match"

if [[ "$RETURN_STRATEGY" == "floating" ]]; then
#
# Return any floating license used.
Expand All @@ -52,6 +55,19 @@ if [[ "$RETURN_STRATEGY" == "floating" ]]; then
break
fi

# "Machine bindings don't match" on a RETURN is permanent, exactly as it is
# on an activation: the entitlement is bound to the machine that activated
# it, and no retry rebinds it. It has to be checked separately because the
# same failing return also emits "Access token is unavailable; failed to
# update", which IS in the transient list - so the real reason was masked
# and the run burned all four attempts and ~2.5 minutes of backoff before
# warning anyway. Reported by a user as "we spend an extra 2-3m at the end
# of the test action trying to return a license, which will fail every
# time", and reproduced in this repo's own licensing matrix.
if grep -qF "$UNITY_LICENSE_RETURN_PERMANENT_PATTERN" "$RETURN_LOG"; then
break
fi

if [ "$ATTEMPT" -lt "$UNITY_LICENSE_RETURN_MAX_ATTEMPTS" ] && grep -qE "$UNITY_LICENSE_RETURN_TRANSIENT_PATTERN" "$RETURN_LOG"; then
# Exponential backoff - see mac/steps/activate.sh's matching comment.
UNITY_LICENSE_RETURN_DELAY=$((UNITY_LICENSE_RETURN_RETRY_DELAY_SECONDS * (1 << (ATTEMPT - 1))))
Expand Down Expand Up @@ -84,6 +100,19 @@ elif [[ "$RETURN_STRATEGY" == "personal" ]]; then
break
fi

# "Machine bindings don't match" on a RETURN is permanent, exactly as it is
# on an activation: the entitlement is bound to the machine that activated
# it, and no retry rebinds it. It has to be checked separately because the
# same failing return also emits "Access token is unavailable; failed to
# update", which IS in the transient list - so the real reason was masked
# and the run burned all four attempts and ~2.5 minutes of backoff before
# warning anyway. Reported by a user as "we spend an extra 2-3m at the end
# of the test action trying to return a license, which will fail every
# time", and reproduced in this repo's own licensing matrix.
if grep -qF "$UNITY_LICENSE_RETURN_PERMANENT_PATTERN" "$RETURN_LOG"; then
break
fi

if [ "$ATTEMPT" -lt "$UNITY_LICENSE_RETURN_MAX_ATTEMPTS" ] && grep -qE "$UNITY_LICENSE_RETURN_TRANSIENT_PATTERN" "$RETURN_LOG"; then
# Exponential backoff - see mac/steps/activate.sh's matching comment.
UNITY_LICENSE_RETURN_DELAY=$((UNITY_LICENSE_RETURN_RETRY_DELAY_SECONDS * (1 << (ATTEMPT - 1))))
Expand Down Expand Up @@ -124,6 +153,19 @@ elif [[ "$RETURN_STRATEGY" == "serial" ]]; then
break
fi

# "Machine bindings don't match" on a RETURN is permanent, exactly as it is
# on an activation: the entitlement is bound to the machine that activated
# it, and no retry rebinds it. It has to be checked separately because the
# same failing return also emits "Access token is unavailable; failed to
# update", which IS in the transient list - so the real reason was masked
# and the run burned all four attempts and ~2.5 minutes of backoff before
# warning anyway. Reported by a user as "we spend an extra 2-3m at the end
# of the test action trying to return a license, which will fail every
# time", and reproduced in this repo's own licensing matrix.
if grep -qF "$UNITY_LICENSE_RETURN_PERMANENT_PATTERN" "$RETURN_LOG"; then
break
fi

if [ "$ATTEMPT" -lt "$UNITY_LICENSE_RETURN_MAX_ATTEMPTS" ] && grep -qE "$UNITY_LICENSE_RETURN_TRANSIENT_PATTERN" "$RETURN_LOG"; then
# Exponential backoff - see mac/steps/activate.sh's matching comment.
UNITY_LICENSE_RETURN_DELAY=$((UNITY_LICENSE_RETURN_RETRY_DELAY_SECONDS * (1 << (ATTEMPT - 1))))
Expand All @@ -135,7 +177,16 @@ elif [[ "$RETURN_STRATEGY" == "serial" ]]; then
break
done
if [ "$RETURN_EXIT_CODE" -ne 0 ]; then
echo "##[warning] Failed to return the Unity license after $UNITY_LICENSE_RETURN_MAX_ATTEMPTS attempts - this seat may still be held by Unity's license server."
if grep -qF "$UNITY_LICENSE_RETURN_PERMANENT_PATTERN" "$RETURN_LOG"; then
# Naming the cause matters: "this seat may still be held" sends people
# hunting a leak on their Unity account, when the licence was bound to a
# machine that no longer exists.
echo "##[warning] Could not return the Unity license: it is bound to a different machine than the one returning it."
echo "##[warning] This is expected when activation and return happen on different machines or containers."
echo "##[warning] If activations later run out, release them at https://id.unity.com."
else
echo "##[warning] Failed to return the Unity license after $UNITY_LICENSE_RETURN_MAX_ATTEMPTS attempts - this seat may still be held by Unity's license server."
fi
fi
rm -f "$RETURN_LOG"
fi
Expand Down
53 changes: 52 additions & 1 deletion dist/platforms/ubuntu/steps/return_license.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ UNITY_LICENSE_RETURN_MAX_ATTEMPTS="${UNITY_LICENSE_RETRY_MAX_ATTEMPTS:-4}"
UNITY_LICENSE_RETURN_RETRY_DELAY_SECONDS=20
UNITY_LICENSE_RETURN_TRANSIENT_PATTERN='TimeoutPolicy did not complete|Access token is unavailable|entitlement groups and 0 free entitlements|License activation has failed|No valid Unity Editor license found|License is not active|Serial number unavailable'

# Permanent by construction - see the guards below.
UNITY_LICENSE_RETURN_PERMANENT_PATTERN="Machine bindings don't match"

if [[ "$RETURN_STRATEGY" == "floating" ]]; then
#
# Return any floating license used.
Expand All @@ -43,6 +46,19 @@ if [[ "$RETURN_STRATEGY" == "floating" ]]; then
break
fi

# "Machine bindings don't match" on a RETURN is permanent, exactly as it is
# on an activation: the entitlement is bound to the machine that activated
# it, and no retry rebinds it. It has to be checked separately because the
# same failing return also emits "Access token is unavailable; failed to
# update", which IS in the transient list - so the real reason was masked
# and the run burned all four attempts and ~2.5 minutes of backoff before
# warning anyway. Reported by a user as "we spend an extra 2-3m at the end
# of the test action trying to return a license, which will fail every
# time", and reproduced in this repo's own licensing matrix.
if grep -qF "$UNITY_LICENSE_RETURN_PERMANENT_PATTERN" "$RETURN_LOG"; then
break
fi

if [ "$ATTEMPT" -lt "$UNITY_LICENSE_RETURN_MAX_ATTEMPTS" ] && grep -qE "$UNITY_LICENSE_RETURN_TRANSIENT_PATTERN" "$RETURN_LOG"; then
# Exponential backoff - see mac/steps/activate.sh's matching comment.
UNITY_LICENSE_RETURN_DELAY=$((UNITY_LICENSE_RETURN_RETRY_DELAY_SECONDS * (1 << (ATTEMPT - 1))))
Expand Down Expand Up @@ -79,6 +95,19 @@ elif [[ "$RETURN_STRATEGY" == "personal" ]]; then
break
fi

# "Machine bindings don't match" on a RETURN is permanent, exactly as it is
# on an activation: the entitlement is bound to the machine that activated
# it, and no retry rebinds it. It has to be checked separately because the
# same failing return also emits "Access token is unavailable; failed to
# update", which IS in the transient list - so the real reason was masked
# and the run burned all four attempts and ~2.5 minutes of backoff before
# warning anyway. Reported by a user as "we spend an extra 2-3m at the end
# of the test action trying to return a license, which will fail every
# time", and reproduced in this repo's own licensing matrix.
if grep -qF "$UNITY_LICENSE_RETURN_PERMANENT_PATTERN" "$RETURN_LOG"; then
break
fi

if [ "$ATTEMPT" -lt "$UNITY_LICENSE_RETURN_MAX_ATTEMPTS" ] && grep -qE "$UNITY_LICENSE_RETURN_TRANSIENT_PATTERN" "$RETURN_LOG"; then
# Exponential backoff - see mac/steps/activate.sh's matching comment.
UNITY_LICENSE_RETURN_DELAY=$((UNITY_LICENSE_RETURN_RETRY_DELAY_SECONDS * (1 << (ATTEMPT - 1))))
Expand Down Expand Up @@ -119,6 +148,19 @@ elif [[ "$RETURN_STRATEGY" == "serial" ]]; then
break
fi

# "Machine bindings don't match" on a RETURN is permanent, exactly as it is
# on an activation: the entitlement is bound to the machine that activated
# it, and no retry rebinds it. It has to be checked separately because the
# same failing return also emits "Access token is unavailable; failed to
# update", which IS in the transient list - so the real reason was masked
# and the run burned all four attempts and ~2.5 minutes of backoff before
# warning anyway. Reported by a user as "we spend an extra 2-3m at the end
# of the test action trying to return a license, which will fail every
# time", and reproduced in this repo's own licensing matrix.
if grep -qF "$UNITY_LICENSE_RETURN_PERMANENT_PATTERN" "$RETURN_LOG"; then
break
fi

if [ "$ATTEMPT" -lt "$UNITY_LICENSE_RETURN_MAX_ATTEMPTS" ] && grep -qE "$UNITY_LICENSE_RETURN_TRANSIENT_PATTERN" "$RETURN_LOG"; then
# Exponential backoff - see mac/steps/activate.sh's matching comment.
UNITY_LICENSE_RETURN_DELAY=$((UNITY_LICENSE_RETURN_RETRY_DELAY_SECONDS * (1 << (ATTEMPT - 1))))
Expand All @@ -130,7 +172,16 @@ elif [[ "$RETURN_STRATEGY" == "serial" ]]; then
break
done
if [ "$RETURN_EXIT_CODE" -ne 0 ]; then
echo "##[warning] Failed to return the Unity license after $UNITY_LICENSE_RETURN_MAX_ATTEMPTS attempts - this seat may still be held by Unity's license server."
if grep -qF "$UNITY_LICENSE_RETURN_PERMANENT_PATTERN" "$RETURN_LOG"; then
# Naming the cause matters: "this seat may still be held" sends people
# hunting a leak on their Unity account, when the licence was bound to a
# machine that no longer exists.
echo "##[warning] Could not return the Unity license: it is bound to a different machine than the one returning it."
echo "##[warning] This is expected when activation and return happen on different machines or containers."
echo "##[warning] If activations later run out, release them at https://id.unity.com."
else
echo "##[warning] Failed to return the Unity license after $UNITY_LICENSE_RETURN_MAX_ATTEMPTS attempts - this seat may still be held by Unity's license server."
fi
fi
rm -f "$RETURN_LOG"
fi
Expand Down
17 changes: 16 additions & 1 deletion dist/platforms/windows/return_license.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ $MaxAttempts = if ($Env:UNITY_LICENSE_RETRY_MAX_ATTEMPTS) { [int]$Env:UNITY_LICE
$RetryDelaySeconds = 20
$TransientPattern = 'TimeoutPolicy did not complete|Access token is unavailable|entitlement groups and 0 free entitlements|License activation has failed|No valid Unity Editor license found|License is not active|Serial number unavailable'

# Permanent by construction: the entitlement is bound to the machine that
# activated it, so no retry rebinds it. Checked separately because the same
# failing return also emits "Access token is unavailable", which IS in the
# transient list above - see ubuntu/steps/return_license.sh.
$PermanentPattern = "Machine bindings don't match"

if ($ReturnStrategy -eq 'floating') {
#
# Return any floating license used.
Expand All @@ -46,6 +52,7 @@ if ($ReturnStrategy -eq 'floating') {

if ($ReturnExitCode -eq 0) { break }

if ($ReturnText -match $PermanentPattern) { break }
if ($Attempt -lt $MaxAttempts -and $ReturnText -match $TransientPattern) {
# Exponential backoff - see mac/steps/activate.sh's matching comment.
$CurrentRetryDelay = $RetryDelaySeconds * [math]::Pow(2, $Attempt - 1)
Expand Down Expand Up @@ -78,6 +85,7 @@ elseif ($ReturnStrategy -eq 'personal') {

if ($ReturnExitCode -eq 0) { break }

if ($ReturnText -match $PermanentPattern) { break }
if ($Attempt -lt $MaxAttempts -and $ReturnText -match $TransientPattern) {
# Exponential backoff - see mac/steps/activate.sh's matching comment.
$CurrentRetryDelay = $RetryDelaySeconds * [math]::Pow(2, $Attempt - 1)
Expand Down Expand Up @@ -115,6 +123,7 @@ elseif ($ReturnStrategy -eq 'serial') {

if ($ReturnExitCode -eq 0) { break }

if ($LogContent -match $PermanentPattern) { break }
if ($Attempt -lt $MaxAttempts -and $LogContent -match $TransientPattern) {
# Exponential backoff - see mac/steps/activate.sh's matching comment.
$CurrentRetryDelay = $RetryDelaySeconds * [math]::Pow(2, $Attempt - 1)
Expand All @@ -125,7 +134,13 @@ elseif ($ReturnStrategy -eq 'serial') {
break
}
if ($ReturnExitCode -ne 0) {
Write-Host "##[warning] Failed to return the Unity license after $MaxAttempts attempts - this seat may still be held by Unity's license server."
if ($LogContent -match $PermanentPattern) {
Write-Host "##[warning] Could not return the Unity license: it is bound to a different machine than the one returning it."
Write-Host "##[warning] This is expected when activation and return happen on different machines or containers."
Write-Host "##[warning] If activations later run out, release them at https://id.unity.com."
} else {
Write-Host "##[warning] Failed to return the Unity license after $MaxAttempts attempts - this seat may still be held by Unity's license server."
}
}
}

Expand Down
17 changes: 16 additions & 1 deletion dist/platforms/windows/steps/return_license.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ $MaxAttempts = if ($Env:UNITY_LICENSE_RETRY_MAX_ATTEMPTS) { [int]$Env:UNITY_LICE
$RetryDelaySeconds = 20
$TransientPattern = 'TimeoutPolicy did not complete|Access token is unavailable|entitlement groups and 0 free entitlements|License activation has failed|No valid Unity Editor license found|License is not active|Serial number unavailable'

# Permanent by construction: the entitlement is bound to the machine that
# activated it, so no retry rebinds it. Checked separately because the same
# failing return also emits "Access token is unavailable", which IS in the
# transient list above - see ubuntu/steps/return_license.sh.
$PermanentPattern = "Machine bindings don't match"

try {
if ($ReturnStrategy -eq 'floating') {
#
Expand All @@ -42,6 +48,7 @@ try {

if ($ReturnExitCode -eq 0) { break }

if ($ReturnText -match $PermanentPattern) { break }
if ($Attempt -lt $MaxAttempts -and $ReturnText -match $TransientPattern) {
# Exponential backoff - see mac/steps/activate.sh's matching comment.
$CurrentRetryDelay = $RetryDelaySeconds * [math]::Pow(2, $Attempt - 1)
Expand Down Expand Up @@ -76,6 +83,7 @@ try {

if ($ReturnExitCode -eq 0) { break }

if ($ReturnText -match $PermanentPattern) { break }
if ($Attempt -lt $MaxAttempts -and $ReturnText -match $TransientPattern) {
# Exponential backoff - see mac/steps/activate.sh's matching comment.
$CurrentRetryDelay = $RetryDelaySeconds * [math]::Pow(2, $Attempt - 1)
Expand Down Expand Up @@ -116,6 +124,7 @@ try {

if ($ReturnExitCode -eq 0) { break }

if ($LogContent -match $PermanentPattern) { break }
if ($Attempt -lt $MaxAttempts -and $LogContent -match $TransientPattern) {
# Exponential backoff - see mac/steps/activate.sh's matching comment.
$CurrentRetryDelay = $RetryDelaySeconds * [math]::Pow(2, $Attempt - 1)
Expand All @@ -126,7 +135,13 @@ try {
break
}
if ($ReturnExitCode -ne 0) {
Write-Host "##[warning] Failed to return the Unity license after $MaxAttempts attempts - this seat may still be held by Unity's license server."
if ($LogContent -match $PermanentPattern) {
Write-Host "##[warning] Could not return the Unity license: it is bound to a different machine than the one returning it."
Write-Host "##[warning] This is expected when activation and return happen on different machines or containers."
Write-Host "##[warning] If activations later run out, release them at https://id.unity.com."
} else {
Write-Host "##[warning] Failed to return the Unity license after $MaxAttempts attempts - this seat may still be held by Unity's license server."
}
}
}
} catch {
Expand Down
34 changes: 34 additions & 0 deletions scripts/test-licensing-steps.sh
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ chmod +x "$WORK/Unity.Licensing.Client"
cat > "$WORK/unity-editor" <<'STUB'
#!/usr/bin/env bash
echo "EDITOR $*" >> "$ARGV_LOG"
# STUB_OUTPUT lets a case drive the editor's output the same way it can the
# licensing client's - needed for the return paths, which run the editor.
if [ -n "${STUB_OUTPUT:-}" ]; then
echo "$STUB_OUTPUT"
exit "${STUB_EXIT:-0}"
fi
echo "LICENSE SYSTEM [CI stub] Next license update check is after 2099-01-01T00:00:00"
# Account credentials with no serial is the personal-activation route. The
# real editor answers it with an entitlement resolution and a Personal serial
Expand Down Expand Up @@ -511,6 +517,34 @@ OUT=$(run_step UNITY_EMAIL="ci@example.com" UNITY_PASSWORD="pw123456" \
bash -c 'source "$STEPS_DIR/activate.sh"' 2>&1)
check "a 2FA challenge is named" "$OUT" "second factor"

# Earlier cases in this file redefine the editor stub, so this one defines the
# behaviour it needs rather than inheriting whatever ran last.
cat > "$WORK/unity-editor" <<'STUB'
#!/usr/bin/env bash
echo "EDITOR $*" >> "$ARGV_LOG"
echo "[Licensing::Module] Error: Access token is unavailable; failed to update"
echo "[Licensing::Module] Error: Failed to return entitlement license"
echo "[Licensing::Client] An error occurred attempting to return the ULF license (status code: 1400, message: \"Machine bindings don't match\")"
exit 1
STUB
chmod +x "$WORK/unity-editor"

# A machine-binding mismatch on RETURN is permanent - the entitlement is bound
# to the machine that activated it. It was being retried because the same
# failing return also emits "Access token is unavailable; failed to update",
# which IS a transient signature, so the real reason was masked and the run
# burned all four attempts (~2.5 minutes) before warning anyway. Reported by a
# user, and reproduced in this repo's own licensing capability matrix.
: > "$ARGV_LOG"
OUT=$(run_step UNITY_EMAIL="ci\example.com" UNITY_PASSWORD="pw123456" UNITY_SERIAL="F4-XXXX-XXXX-XXXX-XXXX-XXXX" \
UNITY_LICENSE_RETRY_MAX_ATTEMPTS=4 \
bash -c 'source "$STEPS_DIR/return_license.sh"' 2>&1)
refute "does not retry a machine-binding mismatch on return" "$OUT" \
"known-transient licensing error (attempt 1/4)"
check "and names the real cause instead of implying a leaked seat" "$OUT" \
"bound to a different machine"
refute "and does not send the user hunting a leak" "$OUT" "may still be held"

echo "Seat return on every exit path"
# A steps directory of the real licensing scripts plus a build.sh that hard-
# exits the way a crashed Unity does. Before runsteps.sh armed an EXIT trap,
Expand Down
Loading
Loading