From 8ca1282c9eabf4b053576f63611d21ee855b80db Mon Sep 17 00:00:00 2001 From: Andrew Kahr <22359829+AndrewKahr@users.noreply.github.com> Date: Fri, 24 Nov 2023 23:24:16 -0800 Subject: [PATCH] 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 --- action.yml | 6 + .../ProjectSettings/XRSettings.asset | 10 -- dist/index.js | Bin 22177358 -> 22177589 bytes dist/index.js.map | Bin 14648317 -> 14648588 bytes dist/platforms/mac/entrypoint.sh | 2 +- dist/platforms/mac/steps/activate.sh | 4 - dist/platforms/ubuntu/entrypoint.sh | 103 +++++++++++------- dist/platforms/ubuntu/steps/activate.sh | 63 +++++++---- dist/platforms/ubuntu/steps/build.sh | 21 ---- dist/platforms/ubuntu/steps/runsteps.sh | 40 +++++++ dist/platforms/windows/activate.ps1 | 5 +- dist/platforms/windows/entrypoint.ps1 | 7 +- src/model/build-parameters.ts | 2 + src/model/image-environment-factory.ts | 1 + src/model/input.ts | 4 + 15 files changed, 170 insertions(+), 98 deletions(-) delete mode 100644 dist/BlankProject/ProjectSettings/XRSettings.asset create mode 100644 dist/platforms/ubuntu/steps/runsteps.sh diff --git a/action.yml b/action.yml index 989c2569..121174df 100644 --- a/action.yml +++ b/action.yml @@ -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: '' diff --git a/dist/BlankProject/ProjectSettings/XRSettings.asset b/dist/BlankProject/ProjectSettings/XRSettings.asset deleted file mode 100644 index 482590c1..00000000 --- a/dist/BlankProject/ProjectSettings/XRSettings.asset +++ /dev/null @@ -1,10 +0,0 @@ -{ - "m_SettingKeys": [ - "VR Device Disabled", - "VR Device User Alert" - ], - "m_SettingValues": [ - "False", - "False" - ] -} \ No newline at end of file diff --git a/dist/index.js b/dist/index.js index dc3aac42a703b775526b6293e055ff2f7be75896..f55c9a5754ad0fa61144fc31a46181f3f2597b72 100644 GIT binary patch delta 1594 zcmY+^XM7BH9LMojxEP6WMkdZ#iA-W0F@q2*A#qxSOH@)J4RVRq)F$@k?7bzLR5dYT zwP?^(wPss;&(>MwXo9A&pc$GY3@y+StDtoWS=e!bzOMY5ag<{D?F731{&$e!)4M z#|2!(C0xd@_zl0~3a;WBuHy!7;udb>4({R}?&AR-;t?L>37+B){E26Hju&`|SNIEm z;~%`nzxWUT;|<=(n@Kqv{BkB;YvHDz0yI6P&z7|lt`ts(naa2bW^%3 QQA)JZ!|4$JiPMvucWvM8%m4rY delta 1457 zcmWmEXI~8f0EO}2CCSYw4Jx5jq(bu=722i9-r3oE?=56y@4aV6lD)~u-h1!8x97b0 zKOf+{I2k$F#+)3p?1O2#Uf1j&MRTIHNd9pd?)2ic)ZcJ3LStW#Eaj@PapdP!8o$ z0lx5qKLQYlAec}Ql~5T~5R4FnqAIGPI>Hc+2t*Z1V~q7fQH+crfrG)EFzpe0(NHQJyp+94V3(E%ON37ydeUC|BQ(E~lv3%$_?ebEp7 zF#rQG2!k;MLop1)F#;no3ZpRwV=)dX7>@~4_=<1%jvx4mU-*qb_=|t3(OHJ6 zP;x1`6*DD|Vy@&>ER=kTQL$9=D+QE-N+HEcu~rHzHj1rcr`Rh+l%k4*;;1+&#S~|y zxKctXskkVvN-4!naaTN)(n=Y{Qz@%>Dc*{YQcfwaR8V{sKgC}OPy&@8#iUeJDk+tf yDoU^tqJ%0{m1;_LB}@rdB9uraN{LotlvpKBsiDLx2}(_+mXc^Vz@Hf2Zu$?%v7Rgd diff --git a/dist/index.js.map b/dist/index.js.map index 6f16cb34d7228988949499d3aa0c00a6b3e1ff10..c72ad08ff8d9e7cbba7b326cb543e4bc810fb100 100644 GIT binary patch delta 1209 zcmY+-Syaqn7{~E5V+#GJW~NjkW`t7qeWws*YgA*6l**PtQ-o||86`AYUVHZJhVW-> zSt1l7`+DJmJI-;z1;=+>IJ)?p^S;mdJ-_Fd?YDb=-fwq@siEEIR%}p7Vt0t@M`3}& zL;N_UzUg|%r9U;nit>+@pOk;UzS&-blqEIZpM|zsp(;<;eotXnXfJt7@(DM;^GZ>X z?Jo(hDxV?>gX~5VYmyiot-EYD8pVQKe@U)+<*Qb>#^vT^wJN(&Bf{gglF(Vy?M1;C zPo1ceydD3dVXml$DEp@A#icb4tIc6mK#2xW!42+ch(>4(4@gkM6JF3j3mv@SgC_8W zAN&+9jv8J(! z5pju8F>&#Urntyh5$@Fcdj8jznjv&feV|wh zea#F8TV;StD+Hl6+Mq3h5rTFIMHt$n13ID;I-?7^q8qxS2YR9xdZQ2eq96KW00v?Z z1|u917=ob~hDZ#@2t*+oBQXl2F$QB110%*E7IBD&3F9#V6EO*sF$D>jifKs1bR;1e zGmwH*q+upzVK(MqF48d%X3WO|EJOw@$V3(vVKK6?1WU0DIarPrScz3w4J+1QE!JT@ zY}kNYn+(sqt;4Z3g5BKo^)u_Qk iJi=qt;t8Ij4$tr$FYpqt@EULM7Vq$0t_=9lXU-pU;u;$O delta 1048 zcmW;HS4@)u6o%n+*xDAP6l%2&oZvtOH%@SG>p*d1#f1pC!Bs746|uI0{LZ=;iWIB< zDn+dmH(IwS7wV-6ns}j!#J5~L$(NHa=j7z&)++wlY8CE8WxhkA7RpunKZOZ8jo`}A z`2?*rTq6cs4Jx@%6D!e(7QOtJq|_8G33NS~+TSUGAi8{pAgR+-BqWE*H9}NHSb3A2gIIJ$H*`l2^h7W8MjZN}FZ!WB24EoKk$^!M zj3F3`VHl1P7>Q9BjWHODaTpI1CSW2aVKNdi1yhlPX_$@~n2A{k{5}WCmYNW)U3BLkUOhUHj+l~{$Aq4({R}?&AR-q5_Zb7*FsN&+r^CP>Gj# c1wUTn4c_7%-s1y4;uEUy8DB)du{tjGKmHu&WdHyG diff --git a/dist/platforms/mac/entrypoint.sh b/dist/platforms/mac/entrypoint.sh index 80ec6d09..bea8f36a 100755 --- a/dist/platforms/mac/entrypoint.sh +++ b/dist/platforms/mac/entrypoint.sh @@ -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; diff --git a/dist/platforms/mac/steps/activate.sh b/dist/platforms/mac/steps/activate.sh index 2d3339ef..3f749f04 100755 --- a/dist/platforms/mac/steps/activate.sh +++ b/dist/platforms/mac/steps/activate.sh @@ -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 # diff --git a/dist/platforms/ubuntu/entrypoint.sh b/dist/platforms/ubuntu/entrypoint.sh index af7a4ea2..0847d0c9 100755 --- a/dist/platforms/ubuntu/entrypoint.sh +++ b/dist/platforms/ubuntu/entrypoint.sh @@ -1,47 +1,76 @@ #!/usr/bin/env bash -# -# Create directory for license activation -# - -ACTIVATE_LICENSE_PATH="$GITHUB_WORKSPACE/_activate-license~" -mkdir -p "$ACTIVATE_LICENSE_PATH" +# 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 # -# 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 - -# -# Remove license activation directory +# Prepare Android SDK, if needed +# We do this here to ensure it has root permissions # -rm -r "$ACTIVATE_LICENSE_PATH" -chmod -R 777 "/BlankProject" +fullProjectPath="$GITHUB_WORKSPACE/$PROJECT_PATH" -# -# Instructions for debugging -# +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 -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; + 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") -# -# Exit with code from the build step. -# + # Extract the number after the semicolon + 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 $? diff --git a/dist/platforms/ubuntu/steps/activate.sh b/dist/platforms/ubuntu/steps/activate.sh index 4af50212..61c649d7 100755 --- a/dist/platforms/ubuntu/steps/activate.sh +++ b/dist/platforms/ubuntu/steps/activate.sh @@ -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 - if [ ! -f "~/.local/share/unity3d/Unity/Unity_lic.ulf" ]; then - echo "::error ::There was an error while trying to activate the Unity license." + # 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 [[ $retry_count -eq 5 ]] + then + echo "Activation failed after 5 retries" fi elif [[ -n "$UNITY_LICENSING_SERVER" ]]; then @@ -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 @@ -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 diff --git a/dist/platforms/ubuntu/steps/build.sh b/dist/platforms/ubuntu/steps/build.sh index 7a202bab..4cdb3e89 100755 --- a/dist/platforms/ubuntu/steps/build.sh +++ b/dist/platforms/ubuntu/steps/build.sh @@ -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 # diff --git a/dist/platforms/ubuntu/steps/runsteps.sh b/dist/platforms/ubuntu/steps/runsteps.sh new file mode 100644 index 00000000..e8376051 --- /dev/null +++ b/dist/platforms/ubuntu/steps/runsteps.sh @@ -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 diff --git a/dist/platforms/windows/activate.ps1 b/dist/platforms/windows/activate.ps1 index 5e505b46..897ec17e 100644 --- a/dist/platforms/windows/activate.ps1 +++ b/dist/platforms/windows/activate.ps1 @@ -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 diff --git a/dist/platforms/windows/entrypoint.ps1 b/dist/platforms/windows/entrypoint.ps1 index 07cbb070..fac75b74 100644 --- a/dist/platforms/windows/entrypoint.ps1 +++ b/dist/platforms/windows/entrypoint.ps1 @@ -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 @@ -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 diff --git a/src/model/build-parameters.ts b/src/model/build-parameters.ts index a91e165e..6710a4da 100644 --- a/src/model/build-parameters.ts +++ b/src/model/build-parameters.ts @@ -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[]; @@ -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, diff --git a/src/model/image-environment-factory.ts b/src/model/image-environment-factory.ts index 6162b50b..2df52f1a 100644 --- a/src/model/image-environment-factory.ts +++ b/src/model/image-environment-factory.ts @@ -62,6 +62,7 @@ class ImageEnvironmentFactory { { name: 'ANDROID_EXPORT_TYPE', value: parameters.androidExportType }, { name: 'ANDROID_SYMBOL_TYPE', value: parameters.androidSymbolType }, { name: 'CUSTOM_PARAMETERS', value: parameters.customParameters }, + { name: 'RUN_AS_HOST_USER', value: parameters.runAsHostUser }, { name: 'CHOWN_FILES_TO', value: parameters.chownFilesTo }, { name: 'GITHUB_REF', value: process.env.GITHUB_REF }, { name: 'GITHUB_SHA', value: process.env.GITHUB_SHA }, diff --git a/src/model/input.ts b/src/model/input.ts index 85f45733..c12abe00 100644 --- a/src/model/input.ts +++ b/src/model/input.ts @@ -193,6 +193,10 @@ class Input { return Input.getInput('gitPrivateToken'); } + static get runAsHostUser(): string { + return Input.getInput('runAsHostUser') || 'false'; + } + static get chownFilesTo() { return Input.getInput('chownFilesTo') || ''; }