GitHub Actions Runner

This commit is contained in:
Tingluo Huang
2019-10-10 00:52:42 -04:00
commit c8afc84840
1255 changed files with 198670 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
#!/usr/bin/env node
// Copyright (c) GitHub. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
var childProcess = require("child_process");
var path = require("path")
var supported = ['linux', 'darwin']
if (supported.indexOf(process.platform) == -1) {
console.log('Unsupported platform: ' + process.platform);
console.log('Supported platforms are: ' + supported.toString());
process.exit(1);
}
var stopping = false;
var listener = null;
var runService = function() {
var listenerExePath = path.join(__dirname, '../bin/Runner.Listener');
var interactive = process.argv[2] === "interactive";
if(!stopping) {
try {
if (interactive) {
console.log('Starting Runner listener interactively');
listener = childProcess.spawn(listenerExePath, ['run'], { env: process.env });
} else {
console.log('Starting Runner listener with startup type: service');
listener = childProcess.spawn(listenerExePath, ['run', '--startuptype', 'service'], { env: process.env });
}
console.log('Started listener process');
listener.stdout.on('data', (data) => {
process.stdout.write(data.toString('utf8'));
});
listener.stderr.on('data', (data) => {
process.stdout.write(data.toString('utf8'));
});
listener.on('close', (code) => {
console.log(`Runner listener exited with error code ${code}`);
if (code === 0) {
console.log('Runner listener exit with 0 return code, stop the service, no retry needed.');
stopping = true;
} else if (code === 1) {
console.log('Runner listener exit with terminated error, stop the service, no retry needed.');
stopping = true;
} else if (code === 2) {
console.log('Runner listener exit with retryable error, re-launch runner in 5 seconds.');
} else if (code === 3) {
console.log('Runner listener exit because of updating, re-launch runner in 5 seconds.');
} else {
console.log('Runner listener exit with undefined return code, re-launch runner in 5 seconds.');
}
if(!stopping) {
setTimeout(runService, 5000);
}
});
} catch(ex) {
console.log(ex);
}
}
}
runService();
console.log('Started running service');
var gracefulShutdown = function(code) {
console.log('Shutting down runner listener');
stopping = true;
if (listener) {
console.log('Sending SIGINT to runner listener to stop');
listener.kill('SIGINT');
// TODO wait for 30 seconds and send a SIGKILL
}
}
process.on('SIGINT', () => {
gracefulShutdown(0);
});
process.on('SIGTERM', () => {
gracefulShutdown(0);
});

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>{{SvcName}}</string>
<key>ProgramArguments</key>
<array>
<string>{{RunnerRoot}}/runsvc.sh</string>
</array>
<key>UserName</key>
<string>{{User}}</string>
<key>WorkingDirectory</key>
<string>{{RunnerRoot}}</string>
<key>RunAtLoad</key>
<true/>
<key>StandardOutPath</key>
<string>{{UserHome}}/Library/Logs/{{SvcName}}/stdout.log</string>
<key>StandardErrorPath</key>
<string>{{UserHome}}/Library/Logs/{{SvcName}}/stderr.log</string>
<key>EnvironmentVariables</key>
<dict>
<key>ACTIONS_RUNNER_SVC</key>
<string>1</string>
</dict>
</dict>
</plist>

View File

@@ -0,0 +1,14 @@
[Unit]
Description={{Description}}
After=network.target
[Service]
ExecStart={{RunnerRoot}}/runsvc.sh
User={{User}}
WorkingDirectory={{RunnerRoot}}
KillMode=process
KillSignal=SIGTERM
TimeoutStopSec=5min
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,135 @@
#!/bin/bash
SVC_NAME="{{SvcNameVar}}"
SVC_DESCRIPTION="{{SvcDescription}}"
user_id=`id -u`
# launchctl should not run as sudo for launch runners
if [ $user_id -eq 0 ]; then
echo "Must not run with sudo"
exit 1
fi
SVC_CMD=$1
RUNNER_ROOT=`pwd`
LAUNCH_PATH="${HOME}/Library/LaunchAgents"
PLIST_PATH="${LAUNCH_PATH}/${SVC_NAME}.plist"
TEMPLATE_PATH=./bin/actions.runner.plist.template
TEMP_PATH=./bin/actions.runner.plist.temp
CONFIG_PATH=.service
function failed()
{
local error=${1:-Undefined error}
echo "Failed: $error" >&2
exit 1
}
if [ ! -f "${TEMPLATE_PATH}" ]; then
failed "Must run from runner root or install is corrupt"
fi
function install()
{
echo "Creating launch runner in ${PLIST_PATH}"
if [ ! -d "${LAUNCH_PATH}" ]; then
mkdir ${LAUNCH_PATH}
fi
if [ -f "${PLIST_PATH}" ]; then
failed "error: exists ${PLIST_PATH}"
fi
if [ -f "${TEMP_PATH}" ]; then
rm "${TEMP_PATH}" || failed "failed to delete ${TEMP_PATH}"
fi
log_path="${HOME}/Library/Logs/${SVC_NAME}"
echo "Creating ${log_path}"
mkdir -p "${log_path}" || failed "failed to create ${log_path}"
echo Creating ${PLIST_PATH}
sed "s/{{User}}/${SUDO_USER:-$USER}/g; s/{{SvcName}}/$SVC_NAME/g; s@{{RunnerRoot}}@${RUNNER_ROOT}@g; s@{{UserHome}}@$HOME@g;" "${TEMPLATE_PATH}" > "${TEMP_PATH}" || failed "failed to create replacement temp file"
mv "${TEMP_PATH}" "${PLIST_PATH}" || failed "failed to copy plist"
# Since we started with sudo, runsvc.sh will be owned by root. Change this to current login user.
echo Creating runsvc.sh
cp ./bin/runsvc.sh ./runsvc.sh || failed "failed to copy runsvc.sh"
chmod u+x ./runsvc.sh || failed "failed to set permission for runsvc.sh"
echo Creating ${CONFIG_PATH}
echo "${PLIST_PATH}" > ${CONFIG_PATH} || failed "failed to create .Service file"
echo "svc install complete"
}
function start()
{
echo "starting ${SVC_NAME}"
launchctl load -w "${PLIST_PATH}" || failed "failed to load ${PLIST_PATH}"
status
}
function stop()
{
echo "stopping ${SVC_NAME}"
launchctl unload "${PLIST_PATH}" || failed "failed to unload ${PLIST_PATH}"
status
}
function uninstall()
{
echo "uninstalling ${SVC_NAME}"
stop
rm "${PLIST_PATH}" || failed "failed to delete ${PLIST_PATH}"
if [ -f "${CONFIG_PATH}" ]; then
rm "${CONFIG_PATH}" || failed "failed to delete ${CONFIG_PATH}"
fi
}
function status()
{
echo "status ${SVC_NAME}:"
if [ -f "${PLIST_PATH}" ]; then
echo
echo "${PLIST_PATH}"
else
echo
echo "not installed"
echo
return
fi
echo
status_out=`launchctl list | grep "${SVC_NAME}"`
if [ ! -z "$status_out" ]; then
echo Started:
echo $status_out
echo
else
echo Stopped
echo
fi
}
function usage()
{
echo
echo Usage:
echo "./svc.sh [install, start, stop, status, uninstall]"
echo
}
case $SVC_CMD in
"install") install;;
"status") status;;
"uninstall") uninstall;;
"start") start;;
"stop") stop;;
*) usage;;
esac
exit 0

View File

@@ -0,0 +1,298 @@
#!/bin/bash
user_id=`id -u`
if [ $user_id -ne 0 ]; then
echo "Need to run with sudo privilege"
exit 1
fi
# Determine OS type
# Debian based OS (Debian, Ubuntu, Linux Mint) has /etc/debian_version
# Fedora based OS (Fedora, Redhat, Centos, Oracle Linux 7) has /etc/redhat-release
# SUSE based OS (OpenSUSE, SUSE Enterprise) has ID_LIKE=suse in /etc/os-release
function print_errormessage()
{
echo "Can't install dotnet core dependencies."
echo "You can manually install all required dependencies based on following documentation"
echo "https://docs.microsoft.com/en-us/dotnet/core/linux-prerequisites?tabs=netcore2x"
}
function print_rhel6message()
{
echo "We did our best effort to install dotnet core dependencies"
echo "However, there are some dependencies which require manual installation"
echo "You can install all remaining required dependencies based on the following documentation"
echo "https://github.com/dotnet/core/blob/master/Documentation/build-and-install-rhel6-prerequisites.md"
}
function print_rhel6errormessage()
{
echo "We couldn't install dotnet core dependencies"
echo "You can manually install all required dependencies based on following documentation"
echo "https://docs.microsoft.com/en-us/dotnet/core/linux-prerequisites?tabs=netcore2x"
echo "In addition, there are some dependencies which require manual installation. Please follow this documentation"
echo "https://github.com/dotnet/core/blob/master/Documentation/build-and-install-rhel6-prerequisites.md"
}
if [ -e /etc/os-release ]
then
echo "--------OS Information--------"
cat /etc/os-release
echo "------------------------------"
if [ -e /etc/debian_version ]
then
echo "The current OS is Debian based"
echo "--------Debian Version--------"
cat /etc/debian_version
echo "------------------------------"
# prefer apt over apt-get
command -v apt
if [ $? -eq 0 ]
then
apt update && apt install -y liblttng-ust0 libkrb5-3 zlib1g
if [ $? -ne 0 ]
then
echo "'apt' failed with exit code '$?'"
print_errormessage
exit 1
fi
# ubuntu 18 uses libcurl4
# ubuntu 14, 16 and other linux use libcurl3
apt install -y libcurl3 || apt install -y libcurl4
if [ $? -ne 0 ]
then
echo "'apt' failed with exit code '$?'"
print_errormessage
exit 1
fi
# debian 9 use libssl1.0.2
# other debian linux use libssl1.0.0
apt install -y libssl1.0.0 || apt install -y libssl1.0.2
if [ $? -ne 0 ]
then
echo "'apt' failed with exit code '$?'"
print_errormessage
exit 1
fi
# libicu version prefer: libicu52 -> libicu55 -> libicu57 -> libicu60
apt install -y libicu52 || apt install -y libicu55 || apt install -y libicu57 || apt install -y libicu60
if [ $? -ne 0 ]
then
echo "'apt' failed with exit code '$?'"
print_errormessage
exit 1
fi
else
command -v apt-get
if [ $? -eq 0 ]
then
apt-get update && apt-get install -y liblttng-ust0 libkrb5-3 zlib1g
if [ $? -ne 0 ]
then
echo "'apt-get' failed with exit code '$?'"
print_errormessage
exit 1
fi
# ubuntu 18 uses libcurl4
# ubuntu 14, 16 and other linux use libcurl3
apt-get install -y libcurl3 || apt-get install -y libcurl4
if [ $? -ne 0 ]
then
echo "'apt-get' failed with exit code '$?'"
print_errormessage
exit 1
fi
# debian 9 use libssl1.0.2
# other debian linux use libssl1.0.0
apt-get install -y libssl1.0.0 || apt install -y libssl1.0.2
if [ $? -ne 0 ]
then
echo "'apt-get' failed with exit code '$?'"
print_errormessage
exit 1
fi
# libicu version prefer: libicu52 -> libicu55 -> libicu57 -> libicu60
apt-get install -y libicu52 || apt install -y libicu55 || apt install -y libicu57 || apt install -y libicu60
if [ $? -ne 0 ]
then
echo "'apt-get' failed with exit code '$?'"
print_errormessage
exit 1
fi
else
echo "Can not find 'apt' or 'apt-get'"
print_errormessage
exit 1
fi
fi
elif [ -e /etc/redhat-release ]
then
echo "The current OS is Fedora based"
echo "--------Redhat Version--------"
cat /etc/redhat-release
echo "------------------------------"
# use dnf on fedora
# use yum on centos and redhat
if [ -e /etc/fedora-release ]
then
command -v dnf
if [ $? -eq 0 ]
then
useCompatSsl=0
grep -i 'fedora release 28' /etc/fedora-release
if [ $? -eq 0 ]
then
useCompatSsl=1
else
grep -i 'fedora release 27' /etc/fedora-release
if [ $? -eq 0 ]
then
useCompatSsl=1
else
grep -i 'fedora release 26' /etc/fedora-release
if [ $? -eq 0 ]
then
useCompatSsl=1
fi
fi
fi
if [ $useCompatSsl -eq 1 ]
then
echo "Use compat-openssl10-devel instead of openssl-devel for Fedora 27/28 (dotnet core requires openssl 1.0.x)"
dnf install -y compat-openssl10
if [ $? -ne 0 ]
then
echo "'dnf' failed with exit code '$?'"
print_errormessage
exit 1
fi
else
dnf install -y openssl-libs
if [ $? -ne 0 ]
then
echo "'dnf' failed with exit code '$?'"
print_errormessage
exit 1
fi
fi
dnf install -y lttng-ust libcurl krb5-libs zlib libicu
if [ $? -ne 0 ]
then
echo "'dnf' failed with exit code '$?'"
print_errormessage
exit 1
fi
else
echo "Can not find 'dnf'"
print_errormessage
exit 1
fi
else
command -v yum
if [ $? -eq 0 ]
then
yum install -y openssl-libs libcurl krb5-libs zlib libicu
if [ $? -ne 0 ]
then
echo "'yum' failed with exit code '$?'"
print_errormessage
exit 1
fi
# install lttng-ust separately since it's not part of offical package repository
yum install -y wget && wget -P /etc/yum.repos.d/ https://packages.efficios.com/repo.files/EfficiOS-RHEL7-x86-64.repo && rpmkeys --import https://packages.efficios.com/rhel/repo.key && yum updateinfo && yum install -y lttng-ust
if [ $? -ne 0 ]
then
echo "'lttng-ust' installation failed with exit code '$?'"
print_errormessage
exit 1
fi
else
echo "Can not find 'yum'"
print_errormessage
exit 1
fi
fi
else
# we might on OpenSUSE
OSTYPE=$(grep ID_LIKE /etc/os-release | cut -f2 -d=)
echo $OSTYPE
if [ $OSTYPE == '"suse"' ]
then
echo "The current OS is SUSE based"
command -v zypper
if [ $? -eq 0 ]
then
zypper -n install lttng-ust libopenssl1_0_0 libcurl4 krb5 zlib libicu52_1
if [ $? -ne 0 ]
then
echo "'zypper' failed with exit code '$?'"
print_errormessage
exit 1
fi
else
echo "Can not find 'zypper'"
print_errormessage
exit 1
fi
else
echo "Can't detect current OS type based on /etc/os-release."
print_errormessage
exit 1
fi
fi
elif [ -e /etc/redhat-release ]
# RHEL6 doesn't have an os-release file defined, read redhat-release instead
then
redhatRelease=$(</etc/redhat-release)
if [[ $redhatRelease == "CentOS release 6."* || $redhatRelease == "Red Hat Enterprise Linux Server release 6."* ]]
then
echo "The current OS is Red Hat Enterprise Linux 6 or Centos 6"
# Install known dependencies, as a best effort.
# The remaining dependencies are covered by the GitHub doc that will be shown by `print_rhel6message`
command -v yum
if [ $? -eq 0 ]
then
yum install -y openssl krb5-libs zlib
if [ $? -ne 0 ]
then
echo "'yum' failed with exit code '$?'"
print_rhel6errormessage
exit 1
fi
else
echo "Can not find 'yum'"
print_rhel6errormessage
exit 1
fi
print_rhel6message
exit 1
else
echo "Unknown RHEL OS version"
print_errormessage
exit 1
fi
else
echo "Unknown OS version"
print_errormessage
exit 1
fi
echo "-----------------------------"
echo " Finish Install Dependencies"
echo "-----------------------------"

20
src/Misc/layoutbin/runsvc.sh Executable file
View File

@@ -0,0 +1,20 @@
#!/bin/bash
# convert SIGTERM signal to SIGINT
# for more info on how to propagate SIGTERM to a child process see: http://veithen.github.io/2014/11/16/sigterm-propagation.html
trap 'kill -INT $PID' TERM INT
if [ -f ".path" ]; then
# configure
export PATH=`cat .path`
echo ".path=${PATH}"
fi
# insert anything to setup env when running as a service
# run the host process which keep the listener alive
./externals/node12/bin/node ./bin/RunnerService.js &
PID=$!
wait $PID
trap - TERM INT
wait $PID

View File

@@ -0,0 +1,143 @@
#!/bin/bash
SVC_NAME="{{SvcNameVar}}"
SVC_DESCRIPTION="{{SvcDescription}}"
SVC_CMD=$1
arg_2=${2}
RUNNER_ROOT=`pwd`
UNIT_PATH=/etc/systemd/system/${SVC_NAME}
TEMPLATE_PATH=./bin/actions.runner.service.template
TEMP_PATH=./bin/actions.runner.service.temp
CONFIG_PATH=.service
user_id=`id -u`
# systemctl must run as sudo
# this script is a convenience wrapper around systemctl
if [ $user_id -ne 0 ]; then
echo "Must run as sudo"
exit 1
fi
function failed()
{
local error=${1:-Undefined error}
echo "Failed: $error" >&2
exit 1
}
if [ ! -f "${TEMPLATE_PATH}" ]; then
failed "Must run from runner root or install is corrupt"
fi
#check if we run as root
if [[ $(id -u) != "0" ]]; then
echo "Failed: This script requires to run with sudo." >&2
exit 1
fi
function install()
{
echo "Creating launch runner in ${UNIT_PATH}"
if [ -f "${UNIT_PATH}" ]; then
failed "error: exists ${UNIT_PATH}"
fi
if [ -f "${TEMP_PATH}" ]; then
rm "${TEMP_PATH}" || failed "failed to delete ${TEMP_PATH}"
fi
# can optionally use username supplied
run_as_user=${arg_2:-$SUDO_USER}
echo "Run as user: ${run_as_user}"
run_as_uid=$(id -u ${run_as_user}) || failed "User does not exist"
echo "Run as uid: ${run_as_uid}"
run_as_gid=$(id -g ${run_as_user}) || failed "Group not available"
echo "gid: ${run_as_gid}"
sed "s/{{User}}/${run_as_user}/g; s/{{Description}}/$(echo ${SVC_DESCRIPTION} | sed -e 's/[\/&]/\\&/g')/g; s/{{RunnerRoot}}/$(echo ${RUNNER_ROOT} | sed -e 's/[\/&]/\\&/g')/g;" "${TEMPLATE_PATH}" > "${TEMP_PATH}" || failed "failed to create replacement temp file"
mv "${TEMP_PATH}" "${UNIT_PATH}" || failed "failed to copy unit file"
# unit file should not be executable and world writable
chmod 664 ${UNIT_PATH} || failed "failed to set permissions on ${UNIT_PATH}"
systemctl daemon-reload || failed "failed to reload daemons"
# Since we started with sudo, runsvc.sh will be owned by root. Change this to current login user.
cp ./bin/runsvc.sh ./runsvc.sh || failed "failed to copy runsvc.sh"
chown ${run_as_uid}:${run_as_gid} ./runsvc.sh || failed "failed to set owner for runsvc.sh"
chmod 755 ./runsvc.sh || failed "failed to set permission for runsvc.sh"
systemctl enable ${SVC_NAME} || failed "failed to enable ${SVC_NAME}"
echo "${SVC_NAME}" > ${CONFIG_PATH} || failed "failed to create .service file"
chown ${run_as_uid}:${run_as_gid} ${CONFIG_PATH} || failed "failed to set permission for ${CONFIG_PATH}"
}
function start()
{
systemctl start ${SVC_NAME} || failed "failed to start ${SVC_NAME}"
status
}
function stop()
{
systemctl stop ${SVC_NAME} || failed "failed to stop ${SVC_NAME}"
status
}
function uninstall()
{
stop
systemctl disable ${SVC_NAME} || failed "failed to disable ${SVC_NAME}"
rm "${UNIT_PATH}" || failed "failed to delete ${UNIT_PATH}"
if [ -f "${CONFIG_PATH}" ]; then
rm "${CONFIG_PATH}" || failed "failed to delete ${CONFIG_PATH}"
fi
systemctl daemon-reload || failed "failed to reload daemons"
}
function status()
{
if [ -f "${UNIT_PATH}" ]; then
echo
echo "${UNIT_PATH}"
else
echo
echo "not installed"
echo
return
fi
systemctl --no-pager status ${SVC_NAME}
}
function usage()
{
echo
echo Usage:
echo "./svc.sh [install, start, stop, status, uninstall]"
echo "Commands:"
echo " install [user]: Install runner service as Root or specified user."
echo " start: Manually start the runner service."
echo " stop: Manually stop the runner service."
echo " status: Display status of runner service."
echo " uninstall: Uninstall runner service."
echo
}
case $SVC_CMD in
"install") install;;
"status") status;;
"uninstall") uninstall;;
"start") start;;
"stop") stop;;
"status") status;;
*) usage;;
esac
exit 0

View File

@@ -0,0 +1,143 @@
@echo off
rem runner will replace key words in the template and generate a batch script to run.
rem Keywords:
rem PROCESSID = pid
rem RUNNERPROCESSNAME = Runner.Listener[.exe]
rem ROOTFOLDER = ./
rem EXISTRUNNERVERSION = 2.100.0
rem DOWNLOADRUNNERVERSION = 2.101.0
rem UPDATELOG = _diag/SelfUpdate-UTC.log
rem RESTARTINTERACTIVERUNNER = 0/1
setlocal
set runnerpid=_PROCESS_ID_
set runnerprocessname=_RUNNER_PROCESS_NAME_
set rootfolder=_ROOT_FOLDER_
set existrunnerversion=_EXIST_RUNNER_VERSION_
set downloadrunnerversion=_DOWNLOAD_RUNNER_VERSION_
set logfile=_UPDATE_LOG_
set restartinteractiverunner=_RESTART_INTERACTIVE_RUNNER_
rem log user who run the script
echo [%date% %time%] --------whoami-------- >> "%logfile%" 2>&1
whoami >> "%logfile%" 2>&1
echo [%date% %time%] --------whoami-------- >> "%logfile%" 2>&1
rem wait for runner process to exit.
echo [%date% %time%] Waiting for %runnerprocessname% (%runnerpid%) to complete >> "%logfile%" 2>&1
:loop
tasklist /fi "pid eq %runnerpid%" | find /I "%runnerprocessname%" >> "%logfile%" 2>&1
if ERRORLEVEL 1 (
goto copy
)
echo [%date% %time%] Process %runnerpid% still running, check again after 1 second. >> "%logfile%" 2>&1
ping -n 2 127.0.0.1 >nul
goto loop
rem start re-organize folders
:copy
echo [%date% %time%] Process %runnerpid% finished running >> "%logfile%" 2>&1
echo [%date% %time%] Sleep 1 more second to make sure process exited >> "%logfile%" 2>&1
ping -n 2 127.0.0.1 >nul
echo [%date% %time%] Re-organize folders >> "%logfile%" 2>&1
rem the folder structure under runner root will be
rem ./bin -> bin.2.100.0 (junction folder)
rem ./externals -> externals.2.100.0 (junction folder)
rem ./bin.2.100.0
rem ./externals.2.100.0
rem ./bin.2.99.0
rem ./externals.2.99.0
rem by using the juction folder we can avoid file in use problem.
rem if the bin/externals junction point already exist, we just need to delete the juction point then re-create to point to new bin/externals folder.
rem if the bin/externals still are real folders, we need to rename the existing folder to bin.version format then create junction point to new bin/externals folder.
rem check bin folder
rem we do findstr /C:" bin" since in migration mode, we create a junction folder from runner to bin.
rem as result, dir /AL | findstr "bin" will return the runner folder. output looks like (07/27/2016 05:21 PM <JUNCTION> runner [E:\bin])
dir "%rootfolder%" /AL 2>&1 | findstr /C:" bin" >> "%logfile%" 2>&1
if ERRORLEVEL 1 (
rem return code 1 means it can't find a bin folder that is a junction folder
rem so we need to move the current bin folder to bin.2.99.0 folder.
echo [%date% %time%] move "%rootfolder%\bin" "%rootfolder%\bin.%existrunnerversion%" >> "%logfile%" 2>&1
move "%rootfolder%\bin" "%rootfolder%\bin.%existrunnerversion%" >> "%logfile%" 2>&1
if ERRORLEVEL 1 (
echo [%date% %time%] Can't move "%rootfolder%\bin" to "%rootfolder%\bin.%existrunnerversion%" >> "%logfile%" 2>&1
goto fail
)
) else (
rem otherwise it find a bin folder that is a junction folder
rem we just need to delete the junction point.
echo [%date% %time%] Delete existing junction bin folder >> "%logfile%" 2>&1
rmdir "%rootfolder%\bin" >> "%logfile%" 2>&1
if ERRORLEVEL 1 (
echo [%date% %time%] Can't delete existing junction bin folder >> "%logfile%" 2>&1
goto fail
)
)
rem check externals folder
dir "%rootfolder%" /AL 2>&1 | findstr "externals" >> "%logfile%" 2>&1
if ERRORLEVEL 1 (
rem return code 1 means it can't find a externals folder that is a junction folder
rem so we need to move the current externals folder to externals.2.99.0 folder.
echo [%date% %time%] move "%rootfolder%\externals" "%rootfolder%\externals.%existrunnerversion%" >> "%logfile%" 2>&1
move "%rootfolder%\externals" "%rootfolder%\externals.%existrunnerversion%" >> "%logfile%" 2>&1
if ERRORLEVEL 1 (
echo [%date% %time%] Can't move "%rootfolder%\externals" to "%rootfolder%\externals.%existrunnerversion%" >> "%logfile%" 2>&1
goto fail
)
) else (
rem otherwise it find a externals folder that is a junction folder
rem we just need to delete the junction point.
echo [%date% %time%] Delete existing junction externals folder >> "%logfile%" 2>&1
rmdir "%rootfolder%\externals" >> "%logfile%" 2>&1
if ERRORLEVEL 1 (
echo [%date% %time%] Can't delete existing junction externals folder >> "%logfile%" 2>&1
goto fail
)
)
rem create junction bin folder
echo [%date% %time%] Create junction bin folder >> "%logfile%" 2>&1
mklink /J "%rootfolder%\bin" "%rootfolder%\bin.%downloadrunnerversion%" >> "%logfile%" 2>&1
if ERRORLEVEL 1 (
echo [%date% %time%] Can't create junction bin folder >> "%logfile%" 2>&1
goto fail
)
rem create junction externals folder
echo [%date% %time%] Create junction externals folder >> "%logfile%" 2>&1
mklink /J "%rootfolder%\externals" "%rootfolder%\externals.%downloadrunnerversion%" >> "%logfile%" 2>&1
if ERRORLEVEL 1 (
echo [%date% %time%] Can't create junction externals folder >> "%logfile%" 2>&1
goto fail
)
echo [%date% %time%] Update succeed >> "%logfile%" 2>&1
rem rename the update log file with %logfile%.succeed/.failed/succeedneedrestart
rem runner service host can base on the log file name determin the result of the runner update
echo [%date% %time%] Rename "%logfile%" to be "%logfile%.succeed" >> "%logfile%" 2>&1
move "%logfile%" "%logfile%.succeed" >nul
rem restart interactive runner if needed
if %restartinteractiverunner% equ 1 (
echo [%date% %time%] Restart interactive runner >> "%logfile%.succeed" 2>&1
endlocal
start "Actions Runner" cmd.exe /k "_ROOT_FOLDER_\run.cmd"
) else (
endlocal
)
goto :eof
:fail
echo [%date% %time%] Rename "%logfile%" to be "%logfile%.failed" >> "%logfile%" 2>&1
move "%logfile%" "%logfile%.failed" >nul
goto :eof

View File

@@ -0,0 +1,133 @@
#!/bin/bash
# runner will replace key words in the template and generate a batch script to run.
# Keywords:
# PROCESSID = pid
# RUNNERPROCESSNAME = Runner.Listener[.exe]
# ROOTFOLDER = ./
# EXISTRUNNERVERSION = 2.100.0
# DOWNLOADRUNNERVERSION = 2.101.0
# UPDATELOG = _diag/SelfUpdate-UTC.log
# RESTARTINTERACTIVERUNNER = 0/1
runnerpid=_PROCESS_ID_
runnerprocessname=_RUNNER_PROCESS_NAME_
rootfolder="_ROOT_FOLDER_"
existrunnerversion=_EXIST_RUNNER_VERSION_
downloadrunnerversion=_DOWNLOAD_RUNNER_VERSION_
logfile="_UPDATE_LOG_"
restartinteractiverunner=_RESTART_INTERACTIVE_RUNNER_
# log user who run the script
date "+[%F %T-%4N] --------whoami--------" >> "$logfile" 2>&1
whoami >> "$logfile" 2>&1
date "+[%F %T-%4N] --------whoami--------" >> "$logfile" 2>&1
# wait for runner process to exit.
date "+[%F %T-%4N] Waiting for $runnerprocessname ($runnerpid) to complete" >> "$logfile" 2>&1
while [ -e /proc/$runnerpid ]
do
date "+[%F %T-%4N] Process $runnerpid still running" >> "$logfile" 2>&1
ping -c 2 127.0.0.1 >nul
done
date "+[%F %T-%4N] Process $runnerpid finished running" >> "$logfile" 2>&1
# start re-organize folders
date "+[%F %T-%4N] Sleep 1 more second to make sure process exited" >> "$logfile" 2>&1
ping -c 2 127.0.0.1 >nul
# the folder structure under runner root will be
# ./bin -> bin.2.100.0 (junction folder)
# ./externals -> externals.2.100.0 (junction folder)
# ./bin.2.100.0
# ./externals.2.100.0
# ./bin.2.99.0
# ./externals.2.99.0
# by using the juction folder we can avoid file in use problem.
# if the bin/externals junction point already exist, we just need to delete the juction point then re-create to point to new bin/externals folder.
# if the bin/externals still are real folders, we need to rename the existing folder to bin.version format then create junction point to new bin/externals folder.
# check bin folder
if [[ -L "$rootfolder/bin" && -d "$rootfolder/bin" ]]
then
# return code 0 means it find a bin folder that is a junction folder
# we just need to delete the junction point.
date "+[%F %T-%4N] Delete existing junction bin folder" >> "$logfile"
rm "$rootfolder/bin" >> "$logfile"
if [ $? -ne 0 ]
then
date "+[%F %T-%4N] Can't delete existing junction bin folder" >> "$logfile"
mv -fv "$logfile" "$logfile.failed"
exit 1
fi
else
# otherwise, we need to move the current bin folder to bin.2.99.0 folder.
date "+[%F %T-%4N] move $rootfolder/bin $rootfolder/bin.$existrunnerversion" >> "$logfile" 2>&1
mv -fv "$rootfolder/bin" "$rootfolder/bin.$existrunnerversion" >> "$logfile" 2>&1
if [ $? -ne 0 ]
then
date "+[%F %T-%4N] Can't move $rootfolder/bin to $rootfolder/bin.$existrunnerversion" >> "$logfile" 2>&1
mv -fv "$logfile" "$logfile.failed"
exit 1
fi
fi
# check externals folder
if [[ -L "$rootfolder/externals" && -d "$rootfolder/externals" ]]
then
# the externals folder is already a junction folder
# we just need to delete the junction point.
date "+[%F %T-%4N] Delete existing junction externals folder" >> "$logfile"
rm "$rootfolder/externals" >> "$logfile"
if [ $? -ne 0 ]
then
date "+[%F %T-%4N] Can't delete existing junction externals folder" >> "$logfile"
mv -fv "$logfile" "$logfile.failed"
exit 1
fi
else
# otherwise, we need to move the current externals folder to externals.2.99.0 folder.
date "+[%F %T-%4N] move $rootfolder/externals $rootfolder/externals.$existrunnerversion" >> "$logfile" 2>&1
mv -fv "$rootfolder/externals" "$rootfolder/externals.$existrunnerversion" >> "$logfile" 2>&1
if [ $? -ne 0 ]
then
date "+[%F %T-%4N] Can't move $rootfolder/externals to $rootfolder/externals.$existrunnerversion" >> "$logfile" 2>&1
mv -fv "$logfile" "$logfile.failed"
exit 1
fi
fi
# create junction bin folder
date "+[%F %T-%4N] Create junction bin folder" >> "$logfile" 2>&1
ln -s "$rootfolder/bin.$downloadrunnerversion" "$rootfolder/bin" >> "$logfile" 2>&1
if [ $? -ne 0 ]
then
date "+[%F %T-%4N] Can't create junction bin folder" >> "$logfile" 2>&1
mv -fv "$logfile" "$logfile.failed"
exit 1
fi
# create junction externals folder
date "+[%F %T-%4N] Create junction externals folder" >> "$logfile" 2>&1
ln -s "$rootfolder/externals.$downloadrunnerversion" "$rootfolder/externals" >> "$logfile" 2>&1
if [ $? -ne 0 ]
then
date "+[%F %T-%4N] Can't create junction externals folder" >> "$logfile" 2>&1
mv -fv "$logfile" "$logfile.failed"
exit 1
fi
date "+[%F %T-%4N] Update succeed" >> "$logfile"
# rename the update log file with %logfile%.succeed/.failed/succeedneedrestart
# runner service host can base on the log file name determin the result of the runner update
date "+[%F %T-%4N] Rename $logfile to be $logfile.succeed" >> "$logfile" 2>&1
mv -fv "$logfile" "$logfile.succeed" >> "$logfile" 2>&1
# restart interactive runner if needed
if [ $restartinteractiverunner -ne 0 ]
then
date "+[%F %T-%4N] Restarting interactive runner" >> "$logfile.succeed" 2>&1
"$rootfolder/run.sh" &
fi