From e5da9b5332c7cb895496570f93050331c06a246a Mon Sep 17 00:00:00 2001 From: Christoph Keller Date: Mon, 3 Oct 2022 19:18:33 +0200 Subject: [PATCH] Initial commit. --- README.md | 15 +++++++++ base.sh | 18 +++++++++++ cleanup.sh | 13 ++++++++ gitlab-runner-example-config.toml | 24 ++++++++++++++ install-ssh-key.sh | 19 +++++++++++ prepare.sh | 54 +++++++++++++++++++++++++++++++ run.sh | 13 ++++++++ 7 files changed, 156 insertions(+) create mode 100644 README.md create mode 100755 base.sh create mode 100755 cleanup.sh create mode 100644 gitlab-runner-example-config.toml create mode 100755 install-ssh-key.sh create mode 100755 prepare.sh create mode 100755 run.sh diff --git a/README.md b/README.md new file mode 100644 index 0000000..9306390 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# Run GitLab Runner jobs with macOS virtualization + +## What is this? +A set of configuration files which allows gitlab-runner to make use of the macOS virtualization framework. +Under the hood, these scripts use the [tart](https://github.com/cirruslabs/tart) commandline tool to provision VMs for jobs. +The macOS virtualization framework allows running two macOS virtual machines in parallel. + +## Installation +Clone this repository onto your build machine and register a `custom` gitlab-runner to your instance. +You need to adjust the paths in `prepare_exec`, `run_exec` and `cleanup_exec` which can also be found in `gitlab-runner-example-config.toml`. + +Install dependencies: `brew install gitlab-runner daemonize tart`. + +## Configurations +- `TART_IMAGE`: Choose a different tart image, e.g. from https://github.com/orgs/cirruslabs/packages?tab=packages&q=macos. The current default is `ghcr.io/cirruslabs/macos-monterey-xcode:14`. diff --git a/base.sh b/base.sh new file mode 100755 index 0000000..9e2cdc0 --- /dev/null +++ b/base.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +VM_ID="runner-$CUSTOM_ENV_CI_RUNNER_ID-project-$CUSTOM_ENV_CI_PROJECT_ID-concurrent-$CUSTOM_ENV_CI_CONCURRENT_PROJECT_ID-job-$CUSTOM_ENV_CI_JOB_ID" +VM_IMAGE="${CUSTOM_ENV_TART_IMAGE:-ghcr.io/cirruslabs/macos-monterey-xcode:14}" +VM_USER="admin" +VM_PASSWORD="admin" + +_get_vm_ip() { + tart ip "$VM_ID" --wait 30 || true +} + +_get_vm_pid() { + ps -A | grep -m1 "tart run $VM_ID" | awk '{print $1}' +} + +_ssh() { + ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$VM_USER"@"$VM_IP" "/bin/zsh --login -c '$@'" >/dev/null +} diff --git a/cleanup.sh b/cleanup.sh new file mode 100755 index 0000000..728828d --- /dev/null +++ b/cleanup.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +currentDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +source ${currentDir}/base.sh # Get variables from base script. + +set -o pipefail + +# Destroy VM. +VM_PID=$(_get_vm_pid) +kill $VM_PID || true + +# Delete VM disk. +tart delete "$VM_ID" diff --git a/gitlab-runner-example-config.toml b/gitlab-runner-example-config.toml new file mode 100644 index 0000000..75888a0 --- /dev/null +++ b/gitlab-runner-example-config.toml @@ -0,0 +1,24 @@ +concurrent = 2 +check_interval = 0 + +[session_server] + session_timeout = 1800 + +[[runners]] + name = "tart-driver" + url = "https://gitlab.com/" + id = 123 + token = "asdf" + token_obtained_at = 2022-10-03T10:27:18Z + token_expires_at = 0001-01-01T00:00:00Z + executor = "custom" + builds_dir = "/Users/admin/builds" + cache_dir = "/Users/admin/cache" + [runners.custom_build_dir] + [runners.cache] + [runners.cache.s3] + [runners.cache.gcs] + [runners.custom] + prepare_exec = "/Users/ci/gitlab-runner-tart/prepare.sh" + run_exec = "/Users/ci/gitlab-runner-tart/run.sh" + cleanup_exec = "/Users/ci/gitlab-runner-tart/cleanup.sh" diff --git a/install-ssh-key.sh b/install-ssh-key.sh new file mode 100755 index 0000000..2f7df19 --- /dev/null +++ b/install-ssh-key.sh @@ -0,0 +1,19 @@ +#!/usr/bin/expect -f +lassign $argv IPSrv Login Passwd + +set timeout 10 + +spawn ssh-copy-id -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=1s $Login@$IPSrv + +expect { + -re ".*assword:.*" { + send "$Passwd\r" + exp_continue + } + + -re ".*ERROR:.*" { + exit 4 + } + + eof {} +} diff --git a/prepare.sh b/prepare.sh new file mode 100755 index 0000000..07d3283 --- /dev/null +++ b/prepare.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash + +currentDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +source ${currentDir}/base.sh # Get variables from base script. + +set -eo pipefail + +# trap any error, and mark it as a system failure. +trap "exit $SYSTEM_FAILURE_EXIT_CODE" ERR + +# Install the VM +tart clone "$VM_IMAGE" "$VM_ID" + +# Update VM configuration +tart set "$VM_ID" --cpu 4 --memory 8192 + +# Run the VM in background +daemonize $(brew --prefix)/bin/tart run "$VM_ID" --no-graphics + +# Wait for VM to get IP +echo 'Waiting for VM to get IP' +VM_IP=$(_get_vm_ip) + +if [ -n "$VM_IP" ]; then + echo "VM got IP: $VM_IP" +else + echo 'Waited 30 seconds for VM to start, exiting...' + # Inform GitLab Runner that this is a system failure, so it + # should be retried. + exit "$SYSTEM_FAILURE_EXIT_CODE" +fi + +# Wait for ssh to become available +echo "Waiting for sshd to be available" +for i in $(seq 1 30); do + if ${currentDir}/install-ssh-key.sh "$VM_IP" "$VM_USER" "$VM_PASSWORD" >/dev/null 2>/dev/null; then + break + fi + + if [ "$i" == "30" ]; then + echo 'Waited 30 seconds for sshd to start, exiting...' + # Inform GitLab Runner that this is a system failure, so it + # should be retried. + exit "$SYSTEM_FAILURE_EXIT_CODE" + fi + + sleep 1 +done + +echo "Updating hostname" +_ssh "sudo scutil --set HostName $VM_ID && sudo scutil --set ComputerName $VM_ID" + +echo "Installing gitlab-runner for artifact uploads" +_ssh "brew install gitlab-runner" diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..eece40a --- /dev/null +++ b/run.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +currentDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +source ${currentDir}/base.sh # Get variables from base script. + +VM_IP=$(_get_vm_ip) + +ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$VM_USER"@"$VM_IP" /bin/zsh --login < "${1}" +if [ $? -ne 0 ]; then + # Exit using the variable, to make the build as failure in GitLab + # CI. + exit "$BUILD_FAILURE_EXIT_CODE" +fi \ No newline at end of file