cloudflare/cloudflared
Publicmirrored from https://github.com/cloudflare/cloudflaredAvailable
.ci/scripts/mac/build.sh
228lines · modecode
| 1 | #!/bin/bash |
| 2 | |
| 3 | set -exo pipefail |
| 4 | |
| 5 | if [[ "$(uname)" != "Darwin" ]] ; then |
| 6 | echo "This should be run on macOS" |
| 7 | exit 1 |
| 8 | fi |
| 9 | |
| 10 | if [[ "amd64" != "${TARGET_ARCH}" && "arm64" != "${TARGET_ARCH}" ]] |
| 11 | then |
| 12 | echo "TARGET_ARCH must be amd64 or arm64" |
| 13 | exit 1 |
| 14 | fi |
| 15 | |
| 16 | go version |
| 17 | export GO111MODULE=on |
| 18 | |
| 19 | # build 'cloudflared-darwin-amd64.tgz' |
| 20 | mkdir -p artifacts |
| 21 | TARGET_DIRECTORY=".build" |
| 22 | BINARY_NAME="cloudflared" |
| 23 | VERSION=$(git describe --tags --always --dirty="-dev") |
| 24 | PRODUCT="cloudflared" |
| 25 | APPLE_CA_CERT="apple_dev_ca.cert" |
| 26 | CODE_SIGN_PRIV="code_sign.p12" |
| 27 | CODE_SIGN_CERT="code_sign.cer" |
| 28 | INSTALLER_PRIV="installer.p12" |
| 29 | INSTALLER_CERT="installer.cer" |
| 30 | BUNDLE_ID="com.cloudflare.cloudflared" |
| 31 | SEC_DUP_MSG="security: SecKeychainItemImport: The specified item already exists in the keychain." |
| 32 | export PATH="$PATH:/usr/local/bin" |
| 33 | FILENAME="$(pwd)/artifacts/cloudflared-darwin-$TARGET_ARCH.tgz" |
| 34 | PKGNAME="$(pwd)/artifacts/cloudflared-$TARGET_ARCH.pkg" |
| 35 | mkdir -p ../src/github.com/cloudflare/ |
| 36 | cp -r . ../src/github.com/cloudflare/cloudflared |
| 37 | cd ../src/github.com/cloudflare/cloudflared |
| 38 | |
| 39 | # Imports certificates to the Apple KeyChain |
| 40 | import_certificate() { |
| 41 | local CERTIFICATE_NAME=$1 |
| 42 | local CERTIFICATE_ENV_VAR=$2 |
| 43 | local CERTIFICATE_FILE_NAME=$3 |
| 44 | |
| 45 | echo "Importing $CERTIFICATE_NAME" |
| 46 | |
| 47 | if [[ ! -z "$CERTIFICATE_ENV_VAR" ]]; then |
| 48 | # write certificate to disk and then import it keychain |
| 49 | echo -n -e ${CERTIFICATE_ENV_VAR} | base64 -D > ${CERTIFICATE_FILE_NAME} |
| 50 | # we set || true here and for every `security import invoke` because the "duplicate SecKeychainItemImport" error |
| 51 | # will cause set -e to exit 1. It is okay we do this because we deliberately handle this error in the lines below. |
| 52 | local out=$(security import ${CERTIFICATE_FILE_NAME} -T /usr/bin/pkgbuild -A 2>&1) || true |
| 53 | local exitcode=$? |
| 54 | # delete the certificate from disk |
| 55 | rm -rf ${CERTIFICATE_FILE_NAME} |
| 56 | if [ -n "$out" ]; then |
| 57 | if [ $exitcode -eq 0 ]; then |
| 58 | echo "$out" |
| 59 | else |
| 60 | if [ "$out" != "${SEC_DUP_MSG}" ]; then |
| 61 | echo "$out" >&2 |
| 62 | exit $exitcode |
| 63 | else |
| 64 | echo "already imported code signing certificate" |
| 65 | fi |
| 66 | fi |
| 67 | fi |
| 68 | fi |
| 69 | } |
| 70 | |
| 71 | create_cloudflared_build_keychain() { |
| 72 | # Reusing the private key password as the keychain key |
| 73 | local PRIVATE_KEY_PASS=$1 |
| 74 | |
| 75 | # Create keychain only if it doesn't already exist |
| 76 | if [ ! -f "$HOME/Library/Keychains/cloudflared_build_keychain.keychain-db" ]; then |
| 77 | security create-keychain -p "$PRIVATE_KEY_PASS" cloudflared_build_keychain |
| 78 | else |
| 79 | echo "Keychain already exists: cloudflared_build_keychain" |
| 80 | fi |
| 81 | |
| 82 | # Append temp keychain to the user domain |
| 83 | security list-keychains -d user -s cloudflared_build_keychain $(security list-keychains -d user | sed s/\"//g) |
| 84 | |
| 85 | # Remove relock timeout |
| 86 | security set-keychain-settings cloudflared_build_keychain |
| 87 | |
| 88 | # Unlock keychain so it doesn't require password |
| 89 | security unlock-keychain -p "$PRIVATE_KEY_PASS" cloudflared_build_keychain |
| 90 | |
| 91 | } |
| 92 | |
| 93 | # Imports private keys to the Apple KeyChain |
| 94 | import_private_keys() { |
| 95 | local PRIVATE_KEY_NAME=$1 |
| 96 | local PRIVATE_KEY_ENV_VAR=$2 |
| 97 | local PRIVATE_KEY_FILE_NAME=$3 |
| 98 | local PRIVATE_KEY_PASS=$4 |
| 99 | |
| 100 | echo "Importing $PRIVATE_KEY_NAME" |
| 101 | |
| 102 | if [[ ! -z "$PRIVATE_KEY_ENV_VAR" ]]; then |
| 103 | if [[ ! -z "$PRIVATE_KEY_PASS" ]]; then |
| 104 | # write private key to disk and then import it keychain |
| 105 | echo -n -e ${PRIVATE_KEY_ENV_VAR} | base64 -D > ${PRIVATE_KEY_FILE_NAME} |
| 106 | # we set || true here and for every `security import invoke` because the "duplicate SecKeychainItemImport" error |
| 107 | # will cause set -e to exit 1. It is okay we do this because we deliberately handle this error in the lines below. |
| 108 | local out=$(security import ${PRIVATE_KEY_FILE_NAME} -k cloudflared_build_keychain -P "$PRIVATE_KEY_PASS" -T /usr/bin/pkgbuild -A -P "${PRIVATE_KEY_PASS}" 2>&1) || true |
| 109 | local exitcode=$? |
| 110 | rm -rf ${PRIVATE_KEY_FILE_NAME} |
| 111 | if [ -n "$out" ]; then |
| 112 | if [ $exitcode -eq 0 ]; then |
| 113 | echo "$out" |
| 114 | else |
| 115 | if [ "$out" != "${SEC_DUP_MSG}" ]; then |
| 116 | echo "$out" >&2 |
| 117 | exit $exitcode |
| 118 | fi |
| 119 | fi |
| 120 | fi |
| 121 | fi |
| 122 | fi |
| 123 | } |
| 124 | |
| 125 | # Create temp keychain only for this build |
| 126 | create_cloudflared_build_keychain "${CFD_CODE_SIGN_PASS}" |
| 127 | |
| 128 | # Add Apple Root Developer certificate to the key chain |
| 129 | import_certificate "Apple Developer CA" "${APPLE_DEV_CA_CERT}" "${APPLE_CA_CERT}" |
| 130 | |
| 131 | # Add code signing private key to the key chain |
| 132 | import_private_keys "Developer ID Application" "${CFD_CODE_SIGN_KEY}" "${CODE_SIGN_PRIV}" "${CFD_CODE_SIGN_PASS}" |
| 133 | |
| 134 | # Add code signing certificate to the key chain |
| 135 | import_certificate "Developer ID Application" "${CFD_CODE_SIGN_CERT}" "${CODE_SIGN_CERT}" |
| 136 | |
| 137 | # Add package signing private key to the key chain |
| 138 | import_private_keys "Developer ID Installer" "${CFD_INSTALLER_KEY}" "${INSTALLER_PRIV}" "${CFD_INSTALLER_PASS}" |
| 139 | |
| 140 | # Add package signing certificate to the key chain |
| 141 | import_certificate "Developer ID Installer" "${CFD_INSTALLER_CERT}" "${INSTALLER_CERT}" |
| 142 | |
| 143 | # get the code signing certificate name |
| 144 | if [[ ! -z "$CFD_CODE_SIGN_NAME" ]]; then |
| 145 | CODE_SIGN_NAME="${CFD_CODE_SIGN_NAME}" |
| 146 | else |
| 147 | if [[ -n "$(security find-certificate -c "Developer ID Application" cloudflared_build_keychain | cut -d'"' -f 4 -s | grep "Developer ID Application:" | head -1)" ]]; then |
| 148 | CODE_SIGN_NAME=$(security find-certificate -c "Developer ID Application" cloudflared_build_keychain | cut -d'"' -f 4 -s | grep "Developer ID Application:" | head -1) |
| 149 | else |
| 150 | CODE_SIGN_NAME="" |
| 151 | fi |
| 152 | fi |
| 153 | |
| 154 | # get the package signing certificate name |
| 155 | if [[ ! -z "$CFD_INSTALLER_NAME" ]]; then |
| 156 | PKG_SIGN_NAME="${CFD_INSTALLER_NAME}" |
| 157 | else |
| 158 | if [[ -n "$(security find-certificate -c "Developer ID Installer" cloudflared_build_keychain | cut -d'"' -f 4 -s | grep "Developer ID Installer:" | head -1)" ]]; then |
| 159 | PKG_SIGN_NAME=$(security find-certificate -c "Developer ID Installer" cloudflared_build_keychain | cut -d'"' -f 4 -s | grep "Developer ID Installer:" | head -1) |
| 160 | else |
| 161 | PKG_SIGN_NAME="" |
| 162 | fi |
| 163 | fi |
| 164 | |
| 165 | # cleanup the build directory because the previous execution might have failed without cleaning up. |
| 166 | rm -rf "${TARGET_DIRECTORY}" |
| 167 | export TARGET_OS="darwin" |
| 168 | GOCACHE="$PWD/../../../../" GOPATH="$PWD/../../../../" CGO_ENABLED=1 make cloudflared |
| 169 | |
| 170 | |
| 171 | # This allows apple tools to use the certificates in the keychain without requiring password input. |
| 172 | # This command always needs to run after the certificates have been loaded into the keychain |
| 173 | if [[ ! -z "$CFD_CODE_SIGN_PASS" ]]; then |
| 174 | security set-key-partition-list -S apple-tool:,apple: -s -k "${CFD_CODE_SIGN_PASS}" cloudflared_build_keychain |
| 175 | fi |
| 176 | |
| 177 | # sign the cloudflared binary |
| 178 | if [[ ! -z "$CODE_SIGN_NAME" ]]; then |
| 179 | codesign --keychain $HOME/Library/Keychains/cloudflared_build_keychain.keychain-db -s "${CODE_SIGN_NAME}" -fv --options runtime --timestamp ${BINARY_NAME} |
| 180 | |
| 181 | # notarize the binary |
| 182 | # TODO: TUN-5789 |
| 183 | fi |
| 184 | |
| 185 | ARCH_TARGET_DIRECTORY="${TARGET_DIRECTORY}/${TARGET_ARCH}-build" |
| 186 | # creating build directory |
| 187 | rm -rf $ARCH_TARGET_DIRECTORY |
| 188 | mkdir -p "${ARCH_TARGET_DIRECTORY}" |
| 189 | mkdir -p "${ARCH_TARGET_DIRECTORY}/contents" |
| 190 | cp -r ".mac_resources/scripts" "${ARCH_TARGET_DIRECTORY}/scripts" |
| 191 | |
| 192 | # copy cloudflared into the build directory |
| 193 | cp ${BINARY_NAME} "${ARCH_TARGET_DIRECTORY}/contents/${PRODUCT}" |
| 194 | |
| 195 | # compress cloudflared into a tar and gzipped file |
| 196 | tar czf "$FILENAME" "${BINARY_NAME}" |
| 197 | |
| 198 | # build the installer package |
| 199 | if [[ ! -z "$PKG_SIGN_NAME" ]]; then |
| 200 | |
| 201 | pkgbuild --identifier com.cloudflare.${PRODUCT} \ |
| 202 | --version ${VERSION} \ |
| 203 | --scripts ${ARCH_TARGET_DIRECTORY}/scripts \ |
| 204 | --root ${ARCH_TARGET_DIRECTORY}/contents \ |
| 205 | --install-location /usr/local/bin \ |
| 206 | --keychain cloudflared_build_keychain \ |
| 207 | --sign "${PKG_SIGN_NAME}" \ |
| 208 | ${PKGNAME} |
| 209 | |
| 210 | # notarize the package |
| 211 | # TODO: TUN-5789 |
| 212 | else |
| 213 | pkgbuild --identifier com.cloudflare.${PRODUCT} \ |
| 214 | --version ${VERSION} \ |
| 215 | --scripts ${ARCH_TARGET_DIRECTORY}/scripts \ |
| 216 | --root ${ARCH_TARGET_DIRECTORY}/contents \ |
| 217 | --install-location /usr/local/bin \ |
| 218 | ${PKGNAME} |
| 219 | fi |
| 220 | |
| 221 | # cleanup build directory because this script is not ran within containers, |
| 222 | # which might lead to future issues in subsequent runs. |
| 223 | rm -rf "${TARGET_DIRECTORY}" |
| 224 | |
| 225 | # cleanup the keychain |
| 226 | security default-keychain -d user -s login.keychain-db |
| 227 | security list-keychains -d user -s login.keychain-db |
| 228 | security delete-keychain cloudflared_build_keychain |
| 229 | |