Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Deployment pipelines for your iOS / tvOS apps with Amazon EC2 Mac Instances

Deployment pipelines for your iOS / tvOS apps with Amazon EC2 Mac Instances

Continuous Integration and Continuous Deployment (CI/CD) allows development teams be agile and respond to rapid business changes. It is one of the mechanisms used to increase software quality. In the cloud, AWS customers are typically using managed services such AWS Code Pipeline and AWS CodeBuild to automate their CI/CD pipelines. But what about developers on the Mac platforms ? Those building iOS, tvOS or watchOS applications ? These typically require access to Apple machines running macOS to build, test and deploy the apps. In this talk you will learn how to combine these two worlds: orchestrate and automate your builds and test using AWS Code* services, while delegating the actual build and test to Amazon EC2 instances running macOS.

More Decks by Sébastien Stormacq - AWS Developer Advocate

Other Decks in Technology

Transcript

  1. D E V D AY

    View Slide

  2. © 2021, Amazon Web Services, Inc. or its affiliates. All rights reserved.
    Deployment pipelines
    for your iOS / tvOS apps
    with Amazon EC2 Mac Instances
    Sébastien Stormacq
    Principal Seveloper Advocate, AWS
    @sebsto

    View Slide

  3. ✅ ✅ ✅ ✅

    View Slide

  4. View Slide

  5. ⁉ ‼ 💔

    View Slide

  6. View Slide

  7. CI/CD Challenges for iOS

    View Slide

  8. Typical Solution #1 : Xcode Server

    View Slide

  9. View Slide

  10. View Slide

  11. View Slide

  12. View Slide

  13. Typical Solution #2 : macOS Workers nodes

    View Slide

  14. Automate the build process
    Have a macOS machine part of the workflow
    CI/CD Challenges for iOS

    View Slide

  15. Provisioning the hardware 1

    View Slide

  16. View Slide

  17. Start a Mac EC2 Instance
    ➜ ~ aws --region $REGION ec2 allocate-hosts \
    --instance-type mac1.metal \
    --availability-zone us-east-2b \
    --quantity 1
    {
    "HostIds": [
    "h-0bab92ca090bfec3f"
    ]
    }

    View Slide

  18. Start a Mac EC2 Instance
    ➜ ~ aws --region $REGION ec2 run-instances \
    --instance-type mac1.metal \
    --key-name your_key \
    --placement HostId=h-0bab92ca090bfec3f \
    --security-group-ids sg-01000000000000032 \
    --image-id AWS_Or_Your_AMI_Id
    {
    "Groups": [],
    "Instances": [
    {
    "AmiLaunchIndex": 0,
    "ImageId": "ami-019fd52485934f8bd",
    "InstanceId": "i-0883f109e7f9b025c",
    "InstanceType": "mac1.metal",
    "KeyName": "sst-aws",
    "LaunchTime": "2021-03-08T16:47:39+00:00",
    "Monitoring": {
    "State": "disabled"
    },

    View Slide

  19. AWS Cloud
    mac1 instance
    Amazon EC2
    👩💻

    View Slide

  20. mac1 Pricing

    View Slide

  21. mac1 Savings Plans

    View Slide

  22. sudo passwd ec2-user
    sudo /System/Library/CoreServices/RemoteManagement/\
    ARDAgent.app/Contents/Resources/kickstart \
    -activate -configure -access -on \
    -restart -agent -privs –all
    ssh -L 5900:localhost:5900 –C –N -i /path/my-key-pair.pem \
    ec2-user@my-instance-public-dns-name
    open vnc://localhost:5900
    mac1 EC2 Tips – VNC (optional)
    mac1 instance

    View Slide

  23. PDISK=$(diskutil list physical external | \
    head -n1 | cut -d" " -f1)
    APFSCONT=$(diskutil list physical external | \
    grep "Apple_APFS" | tr -s " " | \
    cut -d" " -f8)
    yes | sudo diskutil repairDisk $PDISK
    sudo diskutil apfs resizeContainer $APFSCONT 0
    mac1 EC2 Tips - EBS
    mac1 instance

    View Slide

  24. # Download and install Xcode from your private web site (or S3 bucket)
    curl -o xcode.xip https://your_private_web_server/apple/Xcode_12.4.xip
    xip --expand xcode.xip
    sudo mv Xcode.app /Applications
    # Download and install Xcode CLI (use your own S3 bucket / CloudFront distribution, the below will stop working
    at some point)
    curl -o xcode-cli.dmg https://your_private_web_server/apple/Command_Line_Tools_for_Xcode_12.4.dmg
    hdiutil mount ./xcode-cli.dmg
    sudo installer -pkg /Volumes/Command\ Line\ Developer\ Tools/Command\ Line\ Tools.pkg -target /
    hdiutil unmount /Volumes/Command\ Line\ Developer\ Tools/
    sudo installer -pkg /Applications/Xcode.app/Contents/Resources/Packages/XcodeSystemResources.pkg -target /
    sudo installer -pkg /Applications/Xcode.app/Contents/Resources/Packages/CoreTypes.pkg -target /
    sudo installer -pkg /Applications/Xcode.app/Contents/Resources/Packages/MobileDevice.pkg -target /
    sudo installer -pkg /Applications/Xcode.app/Contents/Resources/Packages/MobileDeviceDevelopment.pkg -target /
    # it might take several minutes to display the license / to return. Try until it works
    sudo xcodebuild -license accept
    xcode-select -p
    mac1 EC2 Tips – CLI Install Xcode
    mac1 instance

    View Slide

  25. Mac1 EC2 Tips – Create Golden AMI
    REGION=us-east-2
    # REPLACE THE IP ADDRESS IN THE COMMAND BELOW
    # Use the EC2 mac1 Instance Public IP
    EBS_VOLUME_ID=$(aws ec2 --region $REGION describe-instances --query
    'Reservations[].Instances[?PublicIpAddress==`18.191.179.58`].BlockDeviceMappings[][]
    .Ebs.VolumeId' --output text)
    aws ec2 create-snapshot --region $REGION --volume-id $EBS_VOLUME_ID --description
    "macOS Big Sur Xcode"
    # AT THIS STAGE COPY THE SNAPSHOT_ID RETURNED BY THE PREVIOUS COMMAND
    # WAIT FOR THE SNAPSHOT TO COMPLETE, THIS CAN TAKES SEVERAL MINUTES
    SNAPSHOT_ID=
    aws ec2 register-image --region=$REGION --name "GOLD_macOS_BigSur_Xcode" --
    description "macOS Big Sur Xcode Gold Image" --architecture x86_64_mac --
    virtualization-type hvm --block-device-mappings
    DeviceName="/dev/sda1",Ebs=\{SnapshotId=$SNAPSHOT_ID,VolumeType=gp3\} --root-device-
    name "/dev/sda1"

    View Slide

  26. 2
    Automated Builds

    View Slide

  27. Build and Archive
    xcodebuild clean build archive \
    -workspace "$WORKSPACE" \
    -scheme "$SCHEME" \
    -archivePath "$ARCHIVE_PATH" \
    -configuration "$CONFIGURATION"

    View Slide

  28. Code Signing

    View Slide

  29. Code Signing
    security create-keychain -p "${PASSWORD}" "${NAME}"
    security unlock-keychain -p "${PASSWORD}" "${NAME}"
    security list-keychains -s "${NAME}" "${OLD_KEYCHAIN_NAMES[@]}"

    View Slide

  30. Code Signing
    echo $S3_APPLE_DISTRIBUTION_CERT | base64 -d > $DIST_CERT
    security import "${DIST_CERT}" \
    -P "${APPLE_DISTRIBUTION_KEY_PASSWORD}" \
    -k "${KEYCHAIN_NAME}" \
    -T /usr/bin/codesign \
    -T /usr/bin/xcodebuild

    View Slide

  31. Code Signing

    View Slide

  32. Code Signing
    curl -o ~/AppleWWDRCA.cer \
    https://developer.apple.com/certificationauthority/AppleWWDRCA.cer
    security import ~/AppleWWDRCA.cer -t cert -k "${KEYCHAIN_NAME}" \
    -T /usr/bin/codesign -T /usr/bin/xcodebuild
    curl -o ~/AppleWWDRCAG3.cer \
    https://www.apple.com/certificateauthority/AppleWWDRCAG3.cer
    security import ~/AppleWWDRCAG3.cer -t cert -k "${KEYCHAIN_NAME}" \
    -T /usr/bin/codesign -T /usr/bin/xcodebuild
    curl -o ~/DevAuthCA.cer \
    https://www.apple.com/certificateauthority/DevAuthCA.cer
    security import ~/DevAuthCA.cer -t cert -k "${KEYCHAIN_NAME}” \
    -T /usr/bin/codesign -T /usr/bin/xcodebuild

    View Slide

  33. Code Signing
    security set-key-partition-list \
    -S apple-tool:,apple: \
    -s \
    -k "${PASSWORD}" "${NAME}"

    View Slide

  34. Code Signing
    echo $S3_MOBILE_PROFILE | base64 -d > mobile.profile
    UUID=$(security cms -D -i mobile.profile \
    -k "${KEYCHAIN_NAME}" |
    plutil -extract UUID xml1 -o - - |
    xmllint --xpath "//string/text()" -)
    mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles"
    cp mobile.profile "$HOME/Library/MobileDevice/Provisioning
    Profiles/${UUID}.mobileprovision"

    View Slide

  35. Other Build Steps

    View Slide

  36. Amplify
    R E - C R E A T E A M P L I F Y C O N F I G U R A T I O N . J S O N
    /usr/local/bin/amplify pull \
    --amplify $AMPLIFY \
    --frontend $FRONTEND \
    --providers $PROVIDERS \
    --yes --region eu-central-1

    View Slide

  37. +1
    Other Build Steps

    View Slide

  38. Increase Build Number +1
    BUILD_NUMBER=`date +%Y%m%d%H%M%S`
    plutil -replace CFBundleVersion \
    -string $BUILD_NUMBER \
    "./getting started/Info.plist"

    View Slide

  39. Other Build Steps

    View Slide

  40. Upload to iTunes Connect
    xcrun altool \
    --upload-app \
    -f "$(pwd)/build/$SCHEME.ipa" \
    -t ios \
    -u $APPLE_ID \
    -p @env:APPLE_SECRET

    View Slide

  41. 3
    Integrate in the pipeline

    View Slide

  42. AWS CodeBuild
    Swift Amazon Linux 2
    AWS Cloud
    macOS + Xcode
    Gold AMI
    GitHub
    Xcode
    mac1 Instance
    👩💻
    Amazon EC2
    ?

    View Slide

  43. Command Message
    (build.sh)
    Command Queue
    Amazon Simple Queue
    Service
    AWS CodeBuild
    Swift Amazon Linux 2
    SQSProducer
    AWS Cloud
    macOS + Xcode
    Gold AMI
    GitHub
    Xcode
    Response Queue Result Message
    (stdout / stderr)
    mac1 Instance
    SQS Build Agent
    👩💻
    Amazon EC2
    ./SQSProducer https://command.queue.url \
    https://response.queue.url command.sh
    ./SQSBuildAgent https://command.queue.url

    View Slide

  44. SQS Build Agent
    S O U R C E C O D E

    View Slide

  45. 4
    Integrate AWS Services

    View Slide

  46. IAM Permissions attached
    {
    "Sid": "VisualEditor0",
    "Effect": "Allow",
    "Action": [
    "cloudformation:List*",
    "cloudformation:Describe*",
    "cloudformation:Get*",
    "cloudformation:Validate*",
    "cloudformation:Detect*",
    "amplify:List*",
    "amplify:Get*",
    "sqs:Receive*",
    "sqs:Send*",
    "sqs:DeleteMessage"
    ],
    "Resource": "*"
    },
    {
    "Sid": "VisualEditor1",
    "Effect": "Allow",
    "Action":
    "secretsmanager:GetSecretValue",
    "Resource": [
    "arn:aws:secretsmanager:REGION:ACCOU
    NT_ID:secret:amplify*",
    "arn:aws:secretsmanager:REGION:ACCOU
    NT_ID:secret:apple*"
    ]
    }

    View Slide

  47. Store your build secrets
    A W S S E C R E T S M A N A G E R
    aws secretsmanager create-secret \
    --name apple-dist-certificatee \
    --secret-binary fileb://./apple-dist.p12
    aws secretsmanager create-secret \
    --name apple-id \
    --secret-string [email protected]

    View Slide

  48. Retrieve your build secrets
    AW S S E C R E T S M A N A G E R
    export APPLE_SECRET=$($AWS_CLI secretsmanager get-secret-value \
    --secret-id $APPLE_SECRET_SECRET \
    --query SecretString --output text)
    # These are base64 values, we will need to decode to a file when
    needed
    APPLE_CERT=$($AWS_CLI secretsmanager get-secret-value \
    --secret-id $APPLE_DISTRIBUTION_CERT_SECRET \
    --query SecretBinary --output text)

    View Slide

  49. Retrieve your build secrets
    A W S S E C R E T S M A N A G E R
    echo $APPLE_CERT | base64 -d > $DIST_CERT
    security import "${DIST_CERT}" \
    -P "${KEY_PASSWORD}" \
    -k "${KEYCHAIN_NAME}" \
    -T /usr/bin/codesign \
    -T /usr/bin/xcodebuild

    View Slide

  50. © 2021, Amazon Web Services, Inc. or its affiliates. All rights reserved.
    End-to-End Demo

    View Slide

  51. ✅ ✅ ✅ ✅

    View Slide

  52. 1

    View Slide

  53. https://github.com/sebsto/amplify-ios-getting-started/tree/main/code

    View Slide

  54. 2

    View Slide

  55. 3

    View Slide

  56. 4

    View Slide

  57. View Slide

  58. Your imagination 🧠 is the limit

    View Slide

  59. Go build !

    View Slide

  60. Thank you!
    © 2021, Amazon Web Services, Inc. or its affiliates. All rights reserved.
    Sébastien Stormacq
    @sebsto

    View Slide

  61. Please complete
    the session survey
    © 2021, Amazon Web Services, Inc. or its affiliates. All rights reserved.

    View Slide