Skip to content

Commit

Permalink
Allow Running Container as Runner Host User (#600)
Browse files Browse the repository at this point in the history
- Added `runAsHostUser` to allow running the container as the same user as the host system. This fixes most permissions issues on self-hosted runners.
- Perform android sdk setup during entrypoint.sh to ensure it has root permissions if the user switches to a non-root user
- Automatically detect android sdk target version if parameters are not already provided to configure the sdk
- Generate a new uuid for machineID to ensure separate containers are unique to reduce license activation errors
- Add exponential retry strategy for Ubuntu license activations
  • Loading branch information
AndrewKahr authored Nov 25, 2023
1 parent 8da77ac commit 8ca1282
Show file tree
Hide file tree
Showing 15 changed files with 174 additions and 97 deletions.
6 changes: 6 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ inputs:
required: false
default: ''
description: '[CloudRunner] GitHub owner name or organization/team name'
runAsHostUser:
required: false
default: 'false'
description:
'Whether to run as a user that matches the host system or the default root container user. Only applicable to
Linux hosts and containers. This is useful for fixing permission errors on Self-Hosted runners.'
chownFilesTo:
required: false
default: ''
Expand Down
10 changes: 0 additions & 10 deletions dist/BlankProject/ProjectSettings/XRSettings.asset

This file was deleted.

5 changes: 5 additions & 0 deletions dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/platforms/mac/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ echo ""
echo "Please note that the exit code is not very descriptive."
echo "Most likely it will not help you solve the issue."
echo ""
echo "To find the reason for failure: please search for errors in the log above."
echo "To find the reason for failure: please search for errors in the log above and check for annotations in the summary view."
echo ""
fi;

Expand Down
4 changes: 0 additions & 4 deletions dist/platforms/mac/steps/activate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,6 @@ echo "Requesting activation"
# Store the exit code from the verify command
UNITY_EXIT_CODE=$?

if [ ! -f "/Library/Application Support/Unity/Unity_lic.ulf" ]; then
echo "::error ::There was an error while trying to activate the Unity license."
fi

#
# Display information about the result
#
Expand Down
99 changes: 64 additions & 35 deletions dist/platforms/ubuntu/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,47 +1,76 @@
#!/usr/bin/env bash

# Ensure machine ID is randomized
dbus-uuidgen > /etc/machine-id && mkdir -p /var/lib/dbus/ && ln -sf /etc/machine-id /var/lib/dbus/machine-id

#
# Create directory for license activation
# Prepare Android SDK, if needed
# We do this here to ensure it has root permissions
#

ACTIVATE_LICENSE_PATH="$GITHUB_WORKSPACE/_activate-license~"
mkdir -p "$ACTIVATE_LICENSE_PATH"
fullProjectPath="$GITHUB_WORKSPACE/$PROJECT_PATH"

#
# Run steps
#
source /steps/set_extra_git_configs.sh
source /steps/set_gitcredential.sh
source /steps/activate.sh
source /steps/build.sh
source /steps/return_license.sh
if [[ "$BUILD_TARGET" == "Android" ]]; then
export JAVA_HOME="$(awk -F'=' '/JAVA_HOME=/{print $2}' /usr/bin/unity-editor.d/*)"
ANDROID_HOME_DIRECTORY="$(awk -F'=' '/ANDROID_HOME=/{print $2}' /usr/bin/unity-editor.d/*)"
SDKMANAGER=$(find $ANDROID_HOME_DIRECTORY/cmdline-tools -name sdkmanager)
if [ -z "${SDKMANAGER}" ]
then
echo "No sdkmanager found"
exit 1
fi

#
# Remove license activation directory
#
if [[ -n "$ANDROID_SDK_MANAGER_PARAMETERS" ]]; then
echo "Updating Android SDK with parameters: $ANDROID_SDK_MANAGER_PARAMETERS"
$SDKMANAGER "$ANDROID_SDK_MANAGER_PARAMETERS"
else
echo "Updating Android SDK with auto detected target API version"
# Read the line containing AndroidTargetSdkVersion from the file
targetAPILine=$(grep 'AndroidTargetSdkVersion' "$fullProjectPath/ProjectSettings/ProjectSettings.asset")

rm -r "$ACTIVATE_LICENSE_PATH"
chmod -R 777 "/BlankProject"
# Extract the number after the semicolon
targetAPI=$(echo "$targetAPILine" | cut -d':' -f2 | tr -d '[:space:]')

#
# Instructions for debugging
#
$SDKMANAGER "platforms;android-$targetAPI"
fi

if [[ $BUILD_EXIT_CODE -gt 0 ]]; then
echo ""
echo "###########################"
echo "# Failure #"
echo "###########################"
echo ""
echo "Please note that the exit code is not very descriptive."
echo "Most likely it will not help you solve the issue."
echo ""
echo "To find the reason for failure: please search for errors in the log above."
echo ""
fi;
echo "Updated Android SDK."
else
echo "Not updating Android SDK."
fi

#
# Exit with code from the build step.
#
if [[ "$RUN_AS_HOST_USER" == "true" ]]; then
echo "Running as host user"

# Stop on error if we can't set up the user
set -e

# Get host user/group info so we create files with the correct ownership
USERNAME=$(stat -c '%U' "$fullProjectPath")
USERID=$(stat -c '%u' "$fullProjectPath")
GROUPNAME=$(stat -c '%G' "$fullProjectPath")
GROUPID=$(stat -c '%g' "$fullProjectPath")

groupadd -g $GROUPID $GROUPNAME
useradd -u $USERID -g $GROUPID $USERNAME
usermod -aG $GROUPNAME $USERNAME
mkdir -p "/home/$USERNAME"
chown $USERNAME:$GROUPNAME "/home/$USERNAME"

# Normally need root permissions to access when using su
chmod 777 /dev/stdout
chmod 777 /dev/stderr

# Don't stop on error when running our scripts as error handling is baked in
set +e

# Switch to the host user so we can create files with the correct ownership
su $USERNAME -c "$SHELL -c 'source /steps/runsteps.sh'"
else
echo "Running as root"

# Run as root
source /steps/runsteps.sh
fi

exit $BUILD_EXIT_CODE
exit $?
63 changes: 44 additions & 19 deletions dist/platforms/ubuntu/steps/activate.sh
Original file line number Diff line number Diff line change
@@ -1,31 +1,54 @@
#!/usr/bin/env bash

# Run in ACTIVATE_LICENSE_PATH directory
echo "Changing to \"$ACTIVATE_LICENSE_PATH\" directory."
pushd "$ACTIVATE_LICENSE_PATH"

if [[ -n "$UNITY_SERIAL" && -n "$UNITY_EMAIL" && -n "$UNITY_PASSWORD" ]]; then
#
# SERIAL LICENSE MODE
#
# This will activate unity, using the activating process.
# This will activate unity, using the serial activation process.
#
echo "Requesting activation"

# Activate license
unity-editor \
-logFile /dev/stdout \
-quit \
-serial "$UNITY_SERIAL" \
-username "$UNITY_EMAIL" \
-password "$UNITY_PASSWORD" \
-projectPath "/BlankProject"
# Loop the unity-editor call until the license is activated with exponential backoff and a maximum of 5 retries
retry_count=0

# Store the exit code from the verify command
UNITY_EXIT_CODE=$?
# Initialize delay to 15 seconds
delay=15

# Loop until UNITY_EXIT_CODE is 0 or retry count reaches 5
while [[ $retry_count -lt 5 ]]
do
# Activate license
unity-editor \
-logFile /dev/stdout \
-quit \
-serial "$UNITY_SERIAL" \
-username "$UNITY_EMAIL" \
-password "$UNITY_PASSWORD" \
-projectPath "/BlankProject"

# Store the exit code from the verify command
UNITY_EXIT_CODE=$?

# Check if UNITY_EXIT_CODE is 0
if [[ $UNITY_EXIT_CODE -eq 0 ]]
then
echo "Activation successful"
break
else
echo "Activation failed, retrying in $delay seconds..."
sleep $delay

# Increment retry count
((retry_count++))

# Double the delay for the next iteration
delay=$((delay * 2))
fi
done

if [ ! -f "~/.local/share/unity3d/Unity/Unity_lic.ulf" ]; then
echo "::error ::There was an error while trying to activate the Unity license."
if [[ $retry_count -eq 5 ]]
then
echo "Activation failed after 5 retries"
fi

elif [[ -n "$UNITY_LICENSING_SERVER" ]]; then
Expand Down Expand Up @@ -54,8 +77,9 @@ else
echo "Visit https://game.ci/docs/github/getting-started for more"
echo "details on how to set up one of the possible activation strategies."

echo "::error ::No valid license activation strategy could be determined."
# Immediately exit as no UNITY_EXIT_CODE can be derrived.
echo "::error ::No valid license activation strategy could be determined. Make sure to provide UNITY_EMAIL, UNITY_PASSWORD, and either a UNITY_SERIAL \
or UNITY_LICENSE. Otherwise please use UNITY_LICENSING_SERVER."
# Immediately exit as no UNITY_EXIT_CODE can be derived.
exit 1;

fi
Expand All @@ -70,6 +94,7 @@ else
# Activation failed so exit with the code from the license verification step
echo "Unclassified error occured while trying to activate license."
echo "Exit code was: $UNITY_EXIT_CODE"
echo "::error ::There was an error while trying to activate the Unity license."
exit $UNITY_EXIT_CODE
fi

Expand Down
21 changes: 0 additions & 21 deletions dist/platforms/ubuntu/steps/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -62,27 +62,6 @@ else
#
fi

#
# Prepare Android SDK, if needed
#

if [[ "$BUILD_TARGET" == "Android" && -n "$ANDROID_SDK_MANAGER_PARAMETERS" ]]; then
echo "Updating Android SDK with parameters: $ANDROID_SDK_MANAGER_PARAMETERS"
export JAVA_HOME="$(awk -F'=' '/JAVA_HOME=/{print $2}' /usr/bin/unity-editor.d/*)"
ANDROID_HOME_DIRECTORY="$(awk -F'=' '/ANDROID_HOME=/{print $2}' /usr/bin/unity-editor.d/*)"
SDKMANAGER=$(find $ANDROID_HOME_DIRECTORY/cmdline-tools -name sdkmanager)
if [ -z "${SDKMANAGER}" ]
then
echo "No sdkmanager found"
exit 1
fi

$SDKMANAGER "$ANDROID_SDK_MANAGER_PARAMETERS"
echo "Updated Android SDK."
else
echo "Not updating Android SDK."
fi

#
# Pre-build debug information
#
Expand Down
40 changes: 40 additions & 0 deletions dist/platforms/ubuntu/steps/runsteps.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env bash

#
# Run steps
#
source /steps/set_extra_git_configs.sh
source /steps/set_gitcredential.sh
source /steps/activate.sh

# If we didn't activate successfully, exit with the exit code from the activation step.
if [[ $UNITY_EXIT_CODE -ne 0 ]]; then
exit $UNITY_EXIT_CODE
fi

source /steps/build.sh
source /steps/return_license.sh

#
# Instructions for debugging
#

if [[ $BUILD_EXIT_CODE -gt 0 ]]; then
echo ""
echo "###########################"
echo "# Failure #"
echo "###########################"
echo ""
echo "Please note that the exit code is not very descriptive."
echo "Most likely it will not help you solve the issue."
echo ""
echo "To find the reason for failure: please search for errors in the log above and check for annotations in the summary view."
echo ""
fi;

#
# Exit with code from the build step.
#

# Exiting su
exit $BUILD_EXIT_CODE
5 changes: 1 addition & 4 deletions dist/platforms/windows/activate.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,4 @@ Write-Output ""
-projectPath "c:/BlankProject" `
-logfile - | Out-Host

if(-not(Test-path "C:/ProgramData/Unity/Unity_lic.ulf" -PathType leaf))
{
Write-Output "::error ::There was an error while trying to activate the Unity license."
}
$ACTIVATION_EXIT_CODE = $LASTEXITCODE
7 changes: 5 additions & 2 deletions dist/platforms/windows/entrypoint.ps1
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
Get-Process
Start-Sleep -Seconds 3

# Import any necessary registry keys, ie: location of windows 10 sdk
# No guarantee that there will be any necessary registry keys, ie: tvOS
Expand All @@ -17,13 +16,17 @@ Get-Process -Name regsvr32 | ForEach-Object { Stop-Process -Id $_.Id -Force }
# Activate Unity
. "c:\steps\activate.ps1"

# If we didn't activate successfully, exit with the exit code from the activation step.
if ($ACTIVATION_EXIT_CODE -ne 0) {
exit $ACTIVATION_EXIT_CODE
}

# Build the project
. "c:\steps\build.ps1"

# Free the seat for the activated license
. "c:\steps\return_license.ps1"

Start-Sleep -Seconds 3
Get-Process

exit $BUILD_EXIT_CODE
2 changes: 2 additions & 0 deletions src/model/build-parameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class BuildParameters {
public kubeVolumeSize!: string;
public kubeVolume!: string;
public kubeStorageClass!: string;
public runAsHostUser!: String;
public chownFilesTo!: string;
public commandHooks!: string;
public pullInputList!: string[];
Expand Down Expand Up @@ -168,6 +169,7 @@ class BuildParameters {
sshAgent: Input.sshAgent,
sshPublicKeysDirectoryPath: Input.sshPublicKeysDirectoryPath,
gitPrivateToken: Input.gitPrivateToken || (await GithubCliReader.GetGitHubAuthToken()),
runAsHostUser: Input.runAsHostUser,
chownFilesTo: Input.chownFilesTo,
dockerCpuLimit: Input.dockerCpuLimit,
dockerMemoryLimit: Input.dockerMemoryLimit,
Expand Down
Loading

0 comments on commit 8ca1282

Please sign in to comment.