Allow Running Container as Runner Host User (#600)

- 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
This commit is contained in:
Andrew Kahr 2023-11-24 23:24:16 -08:00 committed by GitHub
parent 8da77ace98
commit 8ca1282c9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 170 additions and 98 deletions

View File

@ -101,6 +101,12 @@ inputs:
required: false required: false
default: '' default: ''
description: '[CloudRunner] GitHub owner name or organization/team name' 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: chownFilesTo:
required: false required: false
default: '' default: ''

View File

@ -1,10 +0,0 @@
{
"m_SettingKeys": [
"VR Device Disabled",
"VR Device User Alert"
],
"m_SettingValues": [
"False",
"False"
]
}

BIN
dist/index.js generated vendored

Binary file not shown.

BIN
dist/index.js.map generated vendored

Binary file not shown.

View File

@ -41,7 +41,7 @@ echo ""
echo "Please note that the exit code is not very descriptive." echo "Please note that the exit code is not very descriptive."
echo "Most likely it will not help you solve the issue." echo "Most likely it will not help you solve the issue."
echo "" 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 "" echo ""
fi; fi;

View File

@ -20,10 +20,6 @@ echo "Requesting activation"
# Store the exit code from the verify command # Store the exit code from the verify command
UNITY_EXIT_CODE=$? 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 # Display information about the result
# #

View File

@ -1,47 +1,76 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# # Ensure machine ID is randomized
# Create directory for license activation dbus-uuidgen > /etc/machine-id && mkdir -p /var/lib/dbus/ && ln -sf /etc/machine-id /var/lib/dbus/machine-id
#
ACTIVATE_LICENSE_PATH="$GITHUB_WORKSPACE/_activate-license~"
mkdir -p "$ACTIVATE_LICENSE_PATH"
# #
# Run steps # Prepare Android SDK, if needed
# # We do this here to ensure it has root permissions
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
#
# Remove license activation directory
# #
rm -r "$ACTIVATE_LICENSE_PATH" fullProjectPath="$GITHUB_WORKSPACE/$PROJECT_PATH"
chmod -R 777 "/BlankProject"
# if [[ "$BUILD_TARGET" == "Android" ]]; then
# Instructions for debugging 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
if [[ $BUILD_EXIT_CODE -gt 0 ]]; then if [[ -n "$ANDROID_SDK_MANAGER_PARAMETERS" ]]; then
echo "" echo "Updating Android SDK with parameters: $ANDROID_SDK_MANAGER_PARAMETERS"
echo "###########################" $SDKMANAGER "$ANDROID_SDK_MANAGER_PARAMETERS"
echo "# Failure #" else
echo "###########################" echo "Updating Android SDK with auto detected target API version"
echo "" # Read the line containing AndroidTargetSdkVersion from the file
echo "Please note that the exit code is not very descriptive." targetAPILine=$(grep 'AndroidTargetSdkVersion' "$fullProjectPath/ProjectSettings/ProjectSettings.asset")
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;
# # Extract the number after the semicolon
# Exit with code from the build step. targetAPI=$(echo "$targetAPILine" | cut -d':' -f2 | tr -d '[:space:]')
#
exit $BUILD_EXIT_CODE $SDKMANAGER "platforms;android-$targetAPI"
fi
echo "Updated Android SDK."
else
echo "Not updating Android SDK."
fi
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 $?

View File

@ -1,31 +1,54 @@
#!/usr/bin/env bash #!/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 if [[ -n "$UNITY_SERIAL" && -n "$UNITY_EMAIL" && -n "$UNITY_PASSWORD" ]]; then
# #
# SERIAL LICENSE MODE # SERIAL LICENSE MODE
# #
# This will activate unity, using the activating process. # This will activate unity, using the serial activation process.
# #
echo "Requesting activation" echo "Requesting activation"
# Activate license # Loop the unity-editor call until the license is activated with exponential backoff and a maximum of 5 retries
unity-editor \ retry_count=0
-logFile /dev/stdout \
-quit \
-serial "$UNITY_SERIAL" \
-username "$UNITY_EMAIL" \
-password "$UNITY_PASSWORD" \
-projectPath "/BlankProject"
# Store the exit code from the verify command # Initialize delay to 15 seconds
UNITY_EXIT_CODE=$? delay=15
if [ ! -f "~/.local/share/unity3d/Unity/Unity_lic.ulf" ]; then # Loop until UNITY_EXIT_CODE is 0 or retry count reaches 5
echo "::error ::There was an error while trying to activate the Unity license." 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 [[ $retry_count -eq 5 ]]
then
echo "Activation failed after 5 retries"
fi fi
elif [[ -n "$UNITY_LICENSING_SERVER" ]]; then elif [[ -n "$UNITY_LICENSING_SERVER" ]]; then
@ -54,8 +77,9 @@ else
echo "Visit https://game.ci/docs/github/getting-started for more" 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 "details on how to set up one of the possible activation strategies."
echo "::error ::No valid license activation strategy could be determined." echo "::error ::No valid license activation strategy could be determined. Make sure to provide UNITY_EMAIL, UNITY_PASSWORD, and either a UNITY_SERIAL \
# Immediately exit as no UNITY_EXIT_CODE can be derrived. or UNITY_LICENSE. Otherwise please use UNITY_LICENSING_SERVER."
# Immediately exit as no UNITY_EXIT_CODE can be derived.
exit 1; exit 1;
fi fi
@ -70,6 +94,7 @@ else
# Activation failed so exit with the code from the license verification step # Activation failed so exit with the code from the license verification step
echo "Unclassified error occured while trying to activate license." echo "Unclassified error occured while trying to activate license."
echo "Exit code was: $UNITY_EXIT_CODE" echo "Exit code was: $UNITY_EXIT_CODE"
echo "::error ::There was an error while trying to activate the Unity license."
exit $UNITY_EXIT_CODE exit $UNITY_EXIT_CODE
fi fi

View File

@ -62,27 +62,6 @@ else
# #
fi 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 # Pre-build debug information
# #

40
dist/platforms/ubuntu/steps/runsteps.sh vendored Normal file
View File

@ -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

View File

@ -13,7 +13,4 @@ Write-Output ""
-projectPath "c:/BlankProject" ` -projectPath "c:/BlankProject" `
-logfile - | Out-Host -logfile - | Out-Host
if(-not(Test-path "C:/ProgramData/Unity/Unity_lic.ulf" -PathType leaf)) $ACTIVATION_EXIT_CODE = $LASTEXITCODE
{
Write-Output "::error ::There was an error while trying to activate the Unity license."
}

View File

@ -1,5 +1,4 @@
Get-Process Get-Process
Start-Sleep -Seconds 3
# Import any necessary registry keys, ie: location of windows 10 sdk # Import any necessary registry keys, ie: location of windows 10 sdk
# No guarantee that there will be any necessary registry keys, ie: tvOS # No guarantee that there will be any necessary registry keys, ie: tvOS
@ -17,13 +16,17 @@ Get-Process -Name regsvr32 | ForEach-Object { Stop-Process -Id $_.Id -Force }
# Activate Unity # Activate Unity
. "c:\steps\activate.ps1" . "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 # Build the project
. "c:\steps\build.ps1" . "c:\steps\build.ps1"
# Free the seat for the activated license # Free the seat for the activated license
. "c:\steps\return_license.ps1" . "c:\steps\return_license.ps1"
Start-Sleep -Seconds 3
Get-Process Get-Process
exit $BUILD_EXIT_CODE exit $BUILD_EXIT_CODE

View File

@ -59,6 +59,7 @@ class BuildParameters {
public kubeVolumeSize!: string; public kubeVolumeSize!: string;
public kubeVolume!: string; public kubeVolume!: string;
public kubeStorageClass!: string; public kubeStorageClass!: string;
public runAsHostUser!: String;
public chownFilesTo!: string; public chownFilesTo!: string;
public commandHooks!: string; public commandHooks!: string;
public pullInputList!: string[]; public pullInputList!: string[];
@ -168,6 +169,7 @@ class BuildParameters {
sshAgent: Input.sshAgent, sshAgent: Input.sshAgent,
sshPublicKeysDirectoryPath: Input.sshPublicKeysDirectoryPath, sshPublicKeysDirectoryPath: Input.sshPublicKeysDirectoryPath,
gitPrivateToken: Input.gitPrivateToken || (await GithubCliReader.GetGitHubAuthToken()), gitPrivateToken: Input.gitPrivateToken || (await GithubCliReader.GetGitHubAuthToken()),
runAsHostUser: Input.runAsHostUser,
chownFilesTo: Input.chownFilesTo, chownFilesTo: Input.chownFilesTo,
dockerCpuLimit: Input.dockerCpuLimit, dockerCpuLimit: Input.dockerCpuLimit,
dockerMemoryLimit: Input.dockerMemoryLimit, dockerMemoryLimit: Input.dockerMemoryLimit,

View File

@ -62,6 +62,7 @@ class ImageEnvironmentFactory {
{ name: 'ANDROID_EXPORT_TYPE', value: parameters.androidExportType }, { name: 'ANDROID_EXPORT_TYPE', value: parameters.androidExportType },
{ name: 'ANDROID_SYMBOL_TYPE', value: parameters.androidSymbolType }, { name: 'ANDROID_SYMBOL_TYPE', value: parameters.androidSymbolType },
{ name: 'CUSTOM_PARAMETERS', value: parameters.customParameters }, { name: 'CUSTOM_PARAMETERS', value: parameters.customParameters },
{ name: 'RUN_AS_HOST_USER', value: parameters.runAsHostUser },
{ name: 'CHOWN_FILES_TO', value: parameters.chownFilesTo }, { name: 'CHOWN_FILES_TO', value: parameters.chownFilesTo },
{ name: 'GITHUB_REF', value: process.env.GITHUB_REF }, { name: 'GITHUB_REF', value: process.env.GITHUB_REF },
{ name: 'GITHUB_SHA', value: process.env.GITHUB_SHA }, { name: 'GITHUB_SHA', value: process.env.GITHUB_SHA },

View File

@ -193,6 +193,10 @@ class Input {
return Input.getInput('gitPrivateToken'); return Input.getInput('gitPrivateToken');
} }
static get runAsHostUser(): string {
return Input.getInput('runAsHostUser') || 'false';
}
static get chownFilesTo() { static get chownFilesTo() {
return Input.getInput('chownFilesTo') || ''; return Input.getInput('chownFilesTo') || '';
} }