teleport/build.assets/build-package.sh
Cam Hutchison d3eed17856
build: Support arm64 and universal binaries for MacOS (#25460)
* build: Support ARM64 (cross)builds of fido2 et al

Add support for building/cross-building the fido2 libraries (cbor,
openssl and fido2), supporting ARM64 builds. This is done by adding the
appropriate flags to the library builds in `build-fido2-macos.sh` based
on the `C_ARCH` environment variable. If unset then the host
architecture is used. The `Makefile` defined `C_ARCH` based on the
`ARCH` variable, mapping it to an appropriate value for the C compiler.

Building the libraries should now be done through the new `build-fido2`
target, and getting the pkg-config path should be done with the
`print-fido2-pkg-path`. This is instead of calling the
`build-fido2-macos.sh` script directly as the `Makefile` takes care of
setting the `C_ARCH` environment variable appropriately.

* build: Add make target to install rust cross toolchain

Add the `rustup-set-target-toolchain` target to the Makefile to ensure
the right rust toolchain is installed for the version of Rust we use as
well as the target architecture we wish to generate code for, based on
the `ARCH` variable. This is intended to be used by CI jobs to ensure
they build with the correct toolchain.

* build: Support building MacOS packages for ARM64

Remove the restriction that allows only AMD64 packages to be built on
MacOS for the teleport and tsh packages. This is via the existing `-a`
flag to `build-package.sh` and a newly added `-a` flag to
`build-pkg-tsh.sh`.

This adds the architecture to the filename of the package to distinguish
the packages for different architectures.

Update the comments in the Makefile mentioning that `arch` is ignored.

build: add architecture to package names

* build: Build Teleport Connect with target architecture

When packaging Teleport Connect with electron-builder, pass an
architecture flag so that we can cross-build Teleport Connect. This will
allow us to build MacOS ARM64 binaries on the AMD64 runners.

Add the architecture to the `dmg` filename via the electron-builder
config, so that the filenames for different architectures don't clash.

* build: Copy Mac release artifacts to release directory

Copy the Mac release artifacts to a release artifact directory so that
the CI scripts do not have to. This makes it clearer what is and is not
a release artifact and puts the logic in the Makefile instead of the CI
yaml, so it can more easily be tested locally and to make it easier to
migrate to the next CI system.

This will also be useful for building universal binaries for Mac as the
CI system can put the architecture-specific binaries from a previous
workflow job into a common location.

We should look at copying all release artifacts for the other builds
(Linux tarballs and packages, etc) into this directory too. It may help
with unifying the GitHub Actions release workflows.

* build: Add MacOS universal builds

Add support for ARCH=universal on Darwin to produce universal (fat)
binaries from pre-built arm64 and amd64 binaries.

Packages (pkg) and disk images (dmg) for containing universal binaries
are named without an architecture in the filename, as that is the
current naming for the current AMD64-only releases. These universal ones
will replace those AMD64-only ones providing a single release artifact
working across architectures.

Co-authored-by: Grzegorz Zdunek <grzegorz.zdunek@goteleport.com>

* build: Do not clean before release-darwin

Remove the `clean` prerequisite from the `release-darwin-unsigned`
target as it is not needed when building on GitHub Actions, as it starts
with a fresh slate each run. We do not make releases manually so we
don't need to ensure a clean working directory there either.

Not doing a clean makes it easier to build a MacOS universal release as
it depends on the architecture-specific tarballs from a previous release
build. We would need to manually save the tarballs from the first
architecture release build as they would get deleted by the `clean` from
the second. So just stop cleaning as it is not needed.

---------

Co-authored-by: Grzegorz Zdunek <grzegorz.zdunek@goteleport.com>
2023-05-04 20:47:49 +00:00

376 lines
13 KiB
Bash
Executable file

#!/bin/bash
set -e
usage() {
echo "Usage: $(basename $0) [-t <oss/ent>] [-v <version>] [-p <package type>] [-b <bundle id>] <-a [amd64/x86_64]|[386/i386]|arm|arm64> <-r fips> <-s tarball source dir>" 1>&2
exit 1
}
# Don't follow sourced script.
#shellcheck disable=SC1090
#shellcheck disable=SC1091
. "$(dirname "$0")/build-common.sh"
while getopts ":t:v:p:a:r:s:b:n" o; do
case "${o}" in
t)
t=${OPTARG}
if [[ ${t} != "oss" && ${t} != "ent" ]]; then usage; fi
;;
v)
v=${OPTARG}
;;
p)
p=${OPTARG}
if [[ ${p} != "rpm" && ${p} != "deb" && ${p} != "pkg" ]]; then usage; fi
;;
a)
a=${OPTARG}
if [[ ${a} != "amd64" && ${a} != "x86_64" && ${a} != "386" && ${a} != "i386" && ${a} != "arm" && ${a} != "arm64" && ${a} != "universal" ]]; then usage; fi
;;
r)
r=${OPTARG}
if [[ ${r} != "fips" ]]; then usage; fi
;;
s)
s=${OPTARG}
;;
b)
b=${OPTARG}
;;
n)
# Dry-run mode.
# Only affects parts of the script, use at your own peril!
DRY_RUN_PREFIX='echo + '
;;
*)
usage
;;
esac
done
shift $((OPTIND-1))
if [ -z "${t}" ] || [ -z "${v}" ] || [ -z "${p}" ]; then
usage
fi
TELEPORT_TYPE="$t"
TELEPORT_VERSION="$v"
PACKAGE_TYPE="$p"
ARCH="$a"
RUNTIME="$r"
TARBALL_DIRECTORY="$s"
GNUPG_DIR=${GNUPG_DIR:-/tmp/gnupg}
# linux package configuration
LINUX_BINARY_DIR=/usr/local/bin
LINUX_SYSTEMD_DIR=/lib/systemd/system
LINUX_CONFIG_DIR=/etc
LINUX_DATA_DIR=/var/lib/teleport
# extra package information for linux
MAINTAINER="info@goteleport.com"
LICENSE="Apache-2.0"
VENDOR="Gravitational"
DESCRIPTION="Teleport is a gateway for managing access to clusters of Linux servers via SSH or the Kubernetes API"
DOCS_URL="https://goteleport.com/docs"
# check that curl is installed
if [[ ! $(type curl) ]]; then
echo "curl must be installed"
exit 2
fi
# check that tar is installed
if [[ ! $(type tar) ]]; then
echo "tar must be installed"
exit 11
fi
# check that docker is installed when fpm is needed to build
if [[ "${PACKAGE_TYPE}" != "pkg" ]]; then
if [[ ! $(type docker) ]]; then
echo "docker must be installed to build non-OSX packages"
exit 3
fi
fi
# check that pkgbuild is installed if building for OS X and set variables appropriately
if [[ "${PACKAGE_TYPE}" == "pkg" ]]; then
if ! uname | grep -q Darwin; then
echo "You must be running on OS X to build .pkg files"
exit 4
fi
if [[ "${RUNTIME}" != "" ]]; then
echo "runtime parameter is ignored when building for OS X"
unset RUNTIME
fi
PLATFORM="darwin"
if [[ ! $(type pkgbuild) ]]; then
echo "You need to install pkgbuild"
echo "Run: xcode-select --install"
exit 5
fi
else
PLATFORM="linux"
# if arch isn't set for other package types, throw an error
if [[ "${ARCH}" == "" ]]; then
usage
fi
if [[ -n "${b:-}" ]]; then
echo "bundle ID parameter can only be used for OS X packages"
exit 6
fi
# set docker image appropriately
if [[ "${PACKAGE_TYPE}" == "deb" ]]; then
DOCKER_IMAGE="public.ecr.aws/gravitational/fpm:debian8"
elif [[ "${PACKAGE_TYPE}" == "rpm" ]]; then
DOCKER_IMAGE="public.ecr.aws/gravitational/fpm:centos8"
fi
fi
PACKAGE_ARCH=""
# handle differences between 'gravitational' arch and system arch
if [[ "${ARCH}" == "386" || "${ARCH}" == "i386" ]]; then
TEXT_ARCH="32-bit x86"
TARBALL_ARCH="386"
DEB_PACKAGE_ARCH="i386"
DEB_OUTPUT_ARCH="i386"
RPM_PACKAGE_ARCH="i386"
RPM_OUTPUT_ARCH="i386"
elif [[ "${ARCH}" == "amd64" || "${ARCH}" == "x86_64" ]]; then
TEXT_ARCH="64-bit x86"
TARBALL_ARCH="amd64"
PACKAGE_ARCH="amd64"
DEB_PACKAGE_ARCH="amd64"
DEB_OUTPUT_ARCH="amd64"
RPM_PACKAGE_ARCH="x86_64"
RPM_OUTPUT_ARCH="x86_64"
elif [[ "${ARCH}" == "arm" ]]; then
TEXT_ARCH="32-bit ARM"
TARBALL_ARCH="arm"
# 32-bit arm can be hardfloat and softfloat, and we build for linux-gnueabihf
DEB_PACKAGE_ARCH="armhf"
DEB_OUTPUT_ARCH="arm" # backwards compatibility
RPM_PACKAGE_ARCH="armv7hl"
RPM_OUTPUT_ARCH="arm" # backwards compatibility
elif [[ "${ARCH}" == "arm64" ]]; then
TEXT_ARCH="64-bit ARM"
TARBALL_ARCH="arm64"
PACKAGE_ARCH="arm64"
DEB_PACKAGE_ARCH="arm64"
DEB_OUTPUT_ARCH="arm64"
RPM_PACKAGE_ARCH="aarch64"
RPM_OUTPUT_ARCH="arm64" # backwards compatibility
elif [[ "${ARCH}" == "universal" ]]; then
TARBALL_ARCH="universal"
PACKAGE_ARCH="universal"
fi
# amd64 RPMs should use CentOS 7 compatible artifacts
if [[ "${PACKAGE_TYPE}" == "rpm" && "${RPM_PACKAGE_ARCH}" == "x86_64" ]]; then
OPTIONAL_TARBALL_SECTION+="-centos7"
fi
# set optional runtime section for filename
if [[ "${RUNTIME}" == "fips" ]]; then
OPTIONAL_RUNTIME_SECTION+="-fips"
fi
# set variables appropriately depending on type of package being built
if [[ "${TELEPORT_TYPE}" == "ent" ]]; then
TARBALL_FILENAME="teleport-ent-v${TELEPORT_VERSION}-${PLATFORM}-${TARBALL_ARCH}${OPTIONAL_TARBALL_SECTION}${OPTIONAL_RUNTIME_SECTION}-bin.tar.gz"
TAR_PATH="teleport-ent"
RPM_NAME="teleport-ent"
if [[ "${RUNTIME}" == "fips" ]]; then
TYPE_DESCRIPTION="[${TEXT_ARCH} Enterprise edition, built with FIPS support]"
RPM_NAME="teleport-ent-fips"
else
TYPE_DESCRIPTION="[${TEXT_ARCH} Enterprise edition]"
fi
else
TARBALL_FILENAME="teleport-v${TELEPORT_VERSION}-${PLATFORM}-${TARBALL_ARCH}${OPTIONAL_TARBALL_SECTION}${OPTIONAL_RUNTIME_SECTION}-bin.tar.gz"
TAR_PATH="teleport"
RPM_NAME="teleport"
if [[ "${RUNTIME}" == "fips" ]]; then
TYPE_DESCRIPTION="[${TEXT_ARCH} Open source edition, built with FIPS support]"
RPM_NAME="teleport-fips"
else
TYPE_DESCRIPTION="[${TEXT_ARCH} Open source edition]"
fi
fi
# set file list
if [[ "${PACKAGE_TYPE}" == "pkg" ]]; then
if [[ -z "${PACKAGE_ARCH}" ]]; then
echo "Unsupported architecture: ${ARCH}"
exit 1
fi
# No architecture tag on package filename for universal (multi-arch) binaries.
ARCH_TAG=""
if [[ "${PACKAGE_ARCH}" != "universal" ]]; then
ARCH_TAG="-${PACKAGE_ARCH}"
fi
SIGN_PKG="true"
FILE_LIST="${TAR_PATH}/tsh ${TAR_PATH}/tctl ${TAR_PATH}/teleport ${TAR_PATH}/tbot"
BUNDLE_ID="${b:-com.gravitational.teleport}"
if [[ "${TELEPORT_TYPE}" == "ent" ]]; then
PKG_FILENAME="teleport-ent-${TELEPORT_VERSION}${ARCH_TAG}.${PACKAGE_TYPE}"
else
PKG_FILENAME="teleport-${TELEPORT_VERSION}${ARCH_TAG}.${PACKAGE_TYPE}"
fi
else
FILE_LIST="${TAR_PATH}/tsh ${TAR_PATH}/tctl ${TAR_PATH}/teleport ${TAR_PATH}/tbot ${TAR_PATH}/examples/systemd/teleport.service"
LINUX_BINARY_FILE_LIST="${TAR_PATH}/tsh ${TAR_PATH}/tctl ${TAR_PATH}/tbot ${TAR_PATH}/teleport"
LINUX_SYSTEMD_FILE_LIST="${TAR_PATH}/examples/systemd/teleport.service"
EXTRA_DOCKER_OPTIONS=""
RPM_SIGN_STANZA=""
if [[ "${PACKAGE_TYPE}" == "rpm" ]]; then
PACKAGE_ARCH="${RPM_PACKAGE_ARCH}"
OUTPUT_FILENAME="${TAR_PATH}-${TELEPORT_VERSION}-1${OPTIONAL_RUNTIME_SECTION}.${RPM_OUTPUT_ARCH}.rpm"
FILE_PERMISSIONS_STANZA="--rpm-user root --rpm-group root --rpm-use-file-permissions "
# the rpm/rpmmacros file suppresses the creation of .build-id files (see https://github.com/gravitational/teleport/issues/7040)
EXTRA_DOCKER_OPTIONS="-v $(pwd)/rpm/rpmmacros:/root/.rpmmacros"
# if we set this environment variable, don't sign RPMs (can be useful for building test RPMs
# without having the signing keys)
if [ "${UNSIGNED_RPM}" == "true" ]; then
echo "RPMs will not be signed as requested"
else
# the GNUPG_DIR location here is assumed to contain a complete ~/.gnupg directory structure
# with pubring.kbx and trustdb.gpg files, plus a private-keys-v1.d directory with signing keys
# it needs to contain the "Gravitational, Inc" private key and signing key.
# we also use the rpm-sign/rpmmacros file instead which contains extra directives used for signing.
EXTRA_DOCKER_OPTIONS="-v $(pwd)/rpm-sign/rpmmacros:/root/.rpmmacros -v $(pwd)/rpm-sign/popt-override:/etc/popt.d/rpmsign-override -v ${GNUPG_DIR}:/root/.gnupg"
RPM_SIGN_STANZA="--rpm-sign"
fi
elif [[ "${PACKAGE_TYPE}" == "deb" ]]; then
PACKAGE_ARCH="${DEB_PACKAGE_ARCH}"
OUTPUT_FILENAME="${TAR_PATH}_${TELEPORT_VERSION}${OPTIONAL_RUNTIME_SECTION}_${DEB_OUTPUT_ARCH}.deb"
FILE_PERMISSIONS_STANZA="--deb-user root --deb-group root "
fi
fi
# create a temporary directory and download specified Teleport version
pushd "$(mktemp -d)"
PACKAGE_TEMPDIR=$(pwd)
# automatically clean up on exit
trap 'rm -rf ${PACKAGE_TEMPDIR}' EXIT
mkdir -p ${PACKAGE_TEMPDIR}/buildroot
# Find or download tarball to the local file cache.
tarname="$TARBALL_FILENAME"
[[ -n "$TARBALL_DIRECTORY" ]] && tarname="$TARBALL_DIRECTORY/$TARBALL_FILENAME"
tarout='' # find_or_fetch_tarball writes to this
find_or_fetch_tarball "$tarname" tarout
TARBALL_DIRECTORY="$(dirname "$tarout")"
TARBALL_FILENAME="$(basename "$tarout")" # for consistency, shouldn't change
echo "Found ${TARBALL_DIRECTORY}/${TARBALL_FILENAME} - using it"
# extract necessary files from tarball
tar -C "$(pwd)" -xvzf ${TARBALL_DIRECTORY}/${TARBALL_FILENAME} ${FILE_LIST}
# move files into correct locations before building the package
if [[ "${PACKAGE_TYPE}" != "pkg" ]]; then
if [[ "${LINUX_BINARY_FILE_LIST}" != "" ]]; then
mkdir -p ${PACKAGE_TEMPDIR}/buildroot${LINUX_BINARY_DIR}
mv -v ${LINUX_BINARY_FILE_LIST} ${PACKAGE_TEMPDIR}/buildroot${LINUX_BINARY_DIR}
fi
if [[ "${LINUX_SYSTEMD_FILE_LIST}" != "" ]]; then
mkdir -p ${PACKAGE_TEMPDIR}/buildroot${LINUX_SYSTEMD_DIR}
mv -v ${LINUX_SYSTEMD_FILE_LIST} ${PACKAGE_TEMPDIR}/buildroot${LINUX_SYSTEMD_DIR}
fi
if [[ "${LINUX_CONFIG_FILE}" != "" ]]; then
mkdir -p ${PACKAGE_TEMPDIR}/buildroot${LINUX_CONFIG_DIR}
mv -v ${LINUX_CONFIG_FILE} ${PACKAGE_TEMPDIR}/buildroot${LINUX_CONFIG_DIR}
CONFIG_FILE_STANZA="--config-files /src/buildroot${LINUX_CONFIG_DIR}/${LINUX_CONFIG_FILE} "
fi
# /var/lib/teleport
# shellcheck disable=SC2174
mkdir -p -m0700 ${PACKAGE_TEMPDIR}/buildroot${LINUX_DATA_DIR}
fi
popd
if [[ "${PACKAGE_TYPE}" == "pkg" ]]; then
# erase any existing versions of the package in the output directory first
rm -f ${PKG_FILENAME}
if [[ "${SIGN_PKG}" == "true" ]]; then
# run codesign to sign binaries
for FILE in ${FILE_LIST}; do
$DRY_RUN_PREFIX codesign -s "${DEVELOPER_ID_APPLICATION}" \
-f \
-v \
--timestamp \
--options runtime \
${PACKAGE_TEMPDIR}/${FILE}
done
fi
# build the package for OS X
pkgbuild \
--root ${PACKAGE_TEMPDIR}/${TAR_PATH} \
--identifier ${BUNDLE_ID} \
--version ${TELEPORT_VERSION} \
--install-location /usr/local/bin \
${PKG_FILENAME}
if [[ "${SIGN_PKG}" == "true" ]]; then
# mark package as unsigned first
mv ${PKG_FILENAME} ${PKG_FILENAME}.unsigned
# run productsign to sign package
$DRY_RUN_PREFIX productsign \
--sign "${DEVELOPER_ID_INSTALLER}" \
--timestamp \
${PKG_FILENAME}.unsigned \
${PKG_FILENAME}
[[ -n "$DRY_RUN_PREFIX" ]] && cp "$PKG_FILENAME.unsigned" "$PKG_FILENAME"
# remove unsigned package after successful signing
rm -f ${PKG_FILENAME}.unsigned
notarize "$PKG_FILENAME" "$TEAMID" "$BUNDLE_ID"
fi
# checksum created packages
for PACKAGE in *."${PACKAGE_TYPE}"; do
shasum -a 256 ${PACKAGE} > ${PACKAGE}.sha256
done
else
# erase any existing packages of the same type/version/arch in the output directory first
rm -vf ${OUTPUT_FILENAME}
# build for other platforms
docker run -v ${PACKAGE_TEMPDIR}:/src --rm ${EXTRA_DOCKER_OPTIONS} ${DOCKER_IMAGE} \
fpm \
--input-type dir \
--output-type ${PACKAGE_TYPE} \
--name ${RPM_NAME} \
--version "${TELEPORT_VERSION}" \
--maintainer "${MAINTAINER}" \
--url "${DOCS_URL}" \
--license "${LICENSE}" \
--vendor "${VENDOR}" \
--description "${DESCRIPTION} ${TYPE_DESCRIPTION}" \
--architecture ${PACKAGE_ARCH} \
--package ${OUTPUT_FILENAME} \
--chdir /src/buildroot \
--directories ${LINUX_DATA_DIR} \
--provides teleport \
--prefix / \
--verbose \
${CONFIG_FILE_STANZA} \
${FILE_PERMISSIONS_STANZA} \
${RPM_SIGN_STANZA} .
# copy created package back to current directory
cp ${PACKAGE_TEMPDIR}/*."${PACKAGE_TYPE}" .
# checksum created packages
for FILE in *."${PACKAGE_TYPE}"; do
sha256sum ${FILE} > ${FILE}.sha256
done
fi