Provisioning on-demand macOS virtual machines with Jenkins and Anka Build for iOS CI

Guest Blog Post By Peter Wiesner, Senior Software Engineer @Skyscanner



Every year Apple releases a new version of Xcode. CI systems for iOS application development need to adopt it, so developers can take advantage of the new iOS features. CI systems using Anka Build have a head-start here. For folks not familiar with Anka Build, read more details here.



You only need to create a new tag for the current Anka macOS virtual machine with the new Xcode installed. Anka ships with features helping to automate this process.

In this blog post, I will show you how to do this. We will use the following solutions:

  • anka create to create the macOS virtual machine from a script
  • Jenkins pipeline to run the jobs on demand
  • anka run to execute batch commands on the virtual machines

About the prerequisites:

  • Jenkins with pipeline plugin installed
  • a mac with anka-create label connected to Jenkins. We will use this native node to create and provision the virtual machine
  • Anka Build package version 1.4 current release installed on the mac

Jenkins pipeline

Let’s create a Jenkins pipeline project with following script. The key tricks are:

  • use Jenkins parameters to provide configuration (Xcode version to install)
  • use a separate repo for the actual provisioning files
  • always save the state of the VM on pipeline failure so next iteration is faster
pipeline {

    agent none

    options {

        timeout(time: 240, unit: 'MINUTES')

    }

    environment {

        // Provide credentials here (anka username/pwd, keychain passwords,
        credentials
        for tool packages etc.)

}

parameters {

    //Provide mutable configuration for scripts (anka VM name, anka
    tag,
    Xcode version)

string(name: 'ANKA_VM_NAME', defaultValue: 'anka_VM_node',
    description: 'Name of the Anka VM')

//...
}

stages {

    stage("anka-create") {

        agent {

            node {

                // There is a native mac connected to Jenkins with this
                label
                label 'anka-create'

            }

        }
        steps {

            create_task()

        }

        post {

            always {

                // Create a vm that stores the state of the VM on exiting. When fixing
                failures,
                this can speed up the process a lot

                sh ""
                "#!/bin/bash

                if [\$(anka list | grep current_state_vm | wc - l) == 1]

                then

                anka delete--yes current_state_vm

                fi

                anka stop\ $ANKA_VM_NAME

                anka clone - c\ $ANKA_VM_NAME current_state_vm

                if [\$(anka list | grep\ $ANKA_VM_NAME | wc - l) == 1]

                then

                echo "Deleting Anka VM: \$ANKA_VM_NAME"

                anka delete--yes\ $ANKA_VM_NAME

                fi

                ""
                "

                cleanWs()

            }

        }
    }
}
}

def checkout_provisioning_scripts() {

    // It is useful to decouple the actual provisioning script from this
    pipeline script

    // Easy to do updates and helps reading the pipeline script

    // Clone the provisioning script from a GitHub repo

    timeout(15) {

        checkout([$class: 'GitSCM', branches: [
                [name: 'master']
            ], ,

            extensions: [
                [$class: 'CloneOption', depth: 1, noTags: false,
                    shallow: true, timeout: 30
                ]
            ],
            userRemoteConfigs: [
                [name: 'origin', refspec:
                    '+refs/heads/*:refs/remotes/origin/*', url: 'SSH_GIT_URL'
                ]
            ]
        ])
    }

}

def create_task() {

    cleanWs()

    credentials.withAllCredentials {

        checkout_provisioning_scripts()

        // Once we cloned the repository, print the environment variables
        to see
        if job parameters and variables are correctly passed to this task

        // provision.sh comes from the repo

        sh ""
        "#!/bin/bash

        printenv

        sh. / provision.sh

        ""
        "

    }
}



Creating the VM



The Jenkins pipeline calls out to a provision script. This is responsible to utilize anka for provisioning. The key tricks are:

  • collecting all necessary packages beforehand
  • allowing the script to use previously saved virtual machine as starting point (`anka create` can take up to 20 mins)
#!/bin/bash

# Let's fail on error

set -e

# Retrieve macOS installer and necessary packages

# It's beneficial to store them on a NAS that can be connected to the mac
node

# We can start the process with previous state of vm if we would like to

# This saves a lot of time

if [ "$START_FROM_BEGINNING" -eq 0 ]

then

#Create VM from the Installer on NAS

ANKA_DEFAULT_USER=$USER_NAME anka --debug create --ram-size $VM_RAM
--cpu-count $VM_CPU --disk-size $VM_DISK --app "$PATH_TO_OS_INSTALLER"
$ANKA_VM_NAME

anka modify $ANKA_VM_NAME add port-forwarding --host-port 0 --guest-port
22 --protocol tcp ssh

else

#Clone VM

anka clone current_state_vm $ANKA_VM_NAME

fi

# Iterate on the setup/install scripts

for file in $files_to_execute

do

# Run script as root

anka run --env $ANKA_VM_NAME sudo -E bash $file

# Run script as created anka user

#anka run --env $ANKA_VM_NAME sudo -E -H -u $USER_NAME bash $file

done

# Stop the VM after we are done, start it up and suspend it, so CI can use
the super fast start-up feature of Anka VMs.

anka stop -f $ANKA_VM_NAME

anka run $ANKA_VM_NAME ls

anka suspend $ANKA_VM_NAME

# Save the new baseline virtual machine

anka registry -a $REGISTRY_IP_ADDRESS push $ANKA_VM_NAME -t $ANKA_TAG

Installing necessary tools



The actual provisioning happens in the `for loop`, where we use `anka run`. These scripts install the tools like Xcode, Xcode CLI, Ruby, git or any packages. The content and order of the scripts are company/case specific, but let me list some general tricks. Keep in mind, when running the `anka run` command, the current working directory will be mounted to the VM and the files can be accessed with a relative path (sweet!).



Installing certificates



$P12_PATH contains the path to the exported p12 files location.

echo $USER_PWD | sudo -S security import $P12_PATH -k
"/Library/Keychains/System.keychain" -P "$P12_PASSWORD" -A

Installing Xcode CLI



$XCODE_CLI_PATH contains the path to the Xcode Command Line Tools package location.

MOUNTDIR=$(echo `hdiutil mount $XCODE_CLI_PATH | tail -1 | awk '{$1=$2="";
print $0}'` | xargs -0 echo)

echo $USER_PWD | sudo -S installer -pkg "${MOUNTDIR}/"*.pkg -target /
hdiutil unmount "${MOUNTDIR}"

Installing Xcode



You need to install the xcode-install gem for this. $XCODE_APP_VERSION contains the version number of the Xcode you want to install (like 10.0) $XCODE_REMOTE_URL is an URL from where the Xcode xip can be downloaded (it is worth to download it and upload to a private remote server to have bigger download speed).

xcversion install $XCODE_APP_VERSION --url="$XCODE_REMOTE_URL" --verbose

Share this post

networking-performancev1
Unlocking Superior macOS VM Network Performance: Introducing Anka's new networking mode for Apple Silicon
Large and complex enterprises using Anka have many different demands, and we have worked to continue to develop innovative technology to meet these demands. Enterprise infrastructure hardware is often on the cutting edge, and they need advanced capabilities...
Read More
gitlab-with-anka
Anka Cloud Gitlab Executor
Veertu’s Anka and the new Anka Cloud Gitlab Executor Veertu’s Anka is a suite of software tools built on the macOS virtualization platform. It enables the execution of single or multi-use macOS virtual machines (VMs) in a manner similar to Docker....
Read More
mac-scan-v1
Real-Time CVE Scanning of your macOS Build Systems
It’s common that an organization’s macOS build system will download thousands, sometimes tens of thousands of third-party dependencies every hour. When building and testing iOS applications, it typically downloads and installs third-party...
Read More
anka-on-silicon-v1
The ONLY Fully Automated Apple Silicon macOS VM Creation Solution
Starting in Anka 3.1 we announced that Anka is now able to fully automate the macOS installation processes, disabling SIP, and enabling VNC — all previously manual steps users had to perform inside o the VM. At the time of writing this article,...
Read More
anka_click
Scripting macOS UI User Actions With Anka Click
Starting in Anka 3.2, we’ve introduced a solution for scripting macOS UI user actions. You may ask, “Why would I want to do that?”. Well, often macOS configuration and applications do not have a CLI allowing you to perform certain actions...
Read More
mac-scan-fullscan-shells-v3
Real-time, continuous scan of file downloads on macOS for security vulnerabilities
Today, we are announcing the Beta availability of the Mac Scan solution. Mac Scan software runs on macOS systems (bare metal, virtual, EC2 Mac) and scans downloads in real time for security vulnerabilities. There are multiple scenarios why you would...
Read More
Screen Shot 2022-10-17 at 10.13
Anka 3.1- Fully automated VM macOS installation & The Behavior-Driven macOS UI Automation Framework
We are very happy to announce the General Availability of Anka 3.1 for Apple Silicon / ARM macs. In this release, we are taking our approach to iOS CI automation one step further by introducing a Behavior-Driven macOS UI Automation Framework in Anka,...
Read More
ankam1v2
Migrating from Anka on Intel to Anka on M1 Mac for iOS CI
In this blog, we will cover the key topics for migrating from Anka on Intel to Anka on M1/M2 Macs. Anka is an IaaC solution from Veertu to set up an agile Container like CI for iOS CI using macOS VMs. Anka for Intel uses Apple’s Hypervisor.Framework virtualization...
Read More
ami-scanner-featured-v2
World's first Security Vulnerability scanner for EC2 Mac AMIs
We are excited to announce the General Availability of the world’s first security vulnerability scanner for EC2 Mac AMIs. EC2 Mac AMI Scan scans Intel and Apple Silicon macOS EC2 AMIs, detects security vulnerabilities in third-party packages, dependencies,...
Read More
esxi-to-anka
It's time to migrate your iOS CI from ESXi Virtual Mac Infrastructure to native macOS Virtualization
When VMWare ESXi started officially supporting Apple macOS Virtualization on Mac hardware in late 2012, it opened the doors for the possibility of iOS development to move to a Linux-like, agile, scalable CI infrastructure. Soon enough, many iOS enterprise...
Read More