Skip to content

chore: support building Coder Desktop .dylib #15512

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 94 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -806,10 +806,91 @@ jobs:

echo "Required checks have passed"

# Builds the dylibs and upload it as an artifact so it can be embedded in the main build
build-dylib:
needs: changes
# We always build the dylibs on Go changes to verify we're not merging unbuildable code,
# but they need only be signed and uploaded on coder/coder main.
if: needs.changes.outputs.docs-only == 'false' || github.ref == 'refs/heads/main'
runs-on: ${{ github.repository_owner == 'coder' && 'depot-macos-latest' || 'macos-latest' }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
with:
egress-policy: audit

- name: Checkout
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
with:
fetch-depth: 0

- name: Setup build tools
run: |
brew install bash gnu-getopt make
echo "$(brew --prefix bash)/bin" >> $GITHUB_PATH
echo "$(brew --prefix gnu-getopt)/bin" >> $GITHUB_PATH
echo "$(brew --prefix make)/libexec/gnubin" >> $GITHUB_PATH

- name: Setup Go
uses: ./.github/actions/setup-go

- name: Install rcodesign
if: ${{ github.repository_owner == 'coder' && github.ref == 'refs/heads/main' }}
run: |
set -euo pipefail
wget -O /tmp/rcodesign.tar.gz https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign%2F0.22.0/apple-codesign-0.22.0-macos-universal.tar.gz
sudo tar -xzf /tmp/rcodesign.tar.gz \
-C /usr/local/bin \
--strip-components=1 \
apple-codesign-0.22.0-macos-universal/rcodesign
rm /tmp/rcodesign.tar.gz

- name: Setup Apple Developer certificate and API key
if: ${{ github.repository_owner == 'coder' && github.ref == 'refs/heads/main' }}
run: |
set -euo pipefail
touch /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8}
chmod 600 /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8}
echo "$AC_CERTIFICATE_P12_BASE64" | base64 -d > /tmp/apple_cert.p12
echo "$AC_CERTIFICATE_PASSWORD" > /tmp/apple_cert_password.txt
echo "$AC_APIKEY_P8_BASE64" | base64 -d > /tmp/apple_apikey.p8
env:
AC_CERTIFICATE_P12_BASE64: ${{ secrets.AC_CERTIFICATE_P12_BASE64 }}
AC_CERTIFICATE_PASSWORD: ${{ secrets.AC_CERTIFICATE_PASSWORD }}
AC_APIKEY_P8_BASE64: ${{ secrets.AC_APIKEY_P8_BASE64 }}

- name: Build dylibs
run: |
set -euxo pipefail
go mod download

make gen/mark-fresh
make build/coder-dylib
env:
CODER_SIGN_DARWIN: ${{ github.ref == 'refs/heads/main' && '1' || '0' }}
AC_CERTIFICATE_FILE: /tmp/apple_cert.p12
AC_CERTIFICATE_PASSWORD_FILE: /tmp/apple_cert_password.txt

- name: Upload build artifacts
if: ${{ github.repository_owner == 'coder' && github.ref == 'refs/heads/main' }}
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
with:
name: dylibs
path: |
./build/*.h
./build/*.dylib
retention-days: 7

- name: Delete Apple Developer certificate and API key
if: ${{ github.repository_owner == 'coder' && github.ref == 'refs/heads/main' }}
run: rm -f /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8}

build:
# This builds and publishes ghcr.io/coder/coder-preview:main for each commit
# to main branch.
needs: changes
needs:
- changes
- build-dylib
if: github.ref == 'refs/heads/main' && needs.changes.outputs.docs-only == 'false' && !github.event.pull_request.head.repo.fork
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }}
permissions:
Expand Down Expand Up @@ -848,6 +929,18 @@ jobs:
- name: Install zstd
run: sudo apt-get install -y zstd

- name: Download dylibs
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: dylibs
path: ./build

- name: Insert dylibs
run: |
mv ./build/*amd64.dylib ./site/out/bin/coder-vpn-darwin-amd64.dylib
mv ./build/*arm64.dylib ./site/out/bin/coder-vpn-darwin-arm64.dylib
mv ./build/*arm64.h ./site/out/bin/coder-vpn-darwin-dylib.h

- name: Build
run: |
set -euxo pipefail
Expand Down
93 changes: 89 additions & 4 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,80 @@ env:
CODER_RELEASE_NOTES: ${{ inputs.release_notes }}

jobs:
# build-dylib is a separate job to build the dylib on macOS.
build-dylib:
runs-on: ${{ github.repository_owner == 'coder' && 'depot-macos-latest' || 'macos-latest' }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
with:
egress-policy: audit

- name: Checkout
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
with:
fetch-depth: 0

- name: Setup build tools
run: |
brew install bash gnu-getopt make
echo "$(brew --prefix bash)/bin" >> $GITHUB_PATH
echo "$(brew --prefix gnu-getopt)/bin" >> $GITHUB_PATH
echo "$(brew --prefix make)/libexec/gnubin" >> $GITHUB_PATH

- name: Setup Go
uses: ./.github/actions/setup-go

- name: Install rcodesign
run: |
set -euo pipefail
wget -O /tmp/rcodesign.tar.gz https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign%2F0.22.0/apple-codesign-0.22.0-macos-universal.tar.gz
sudo tar -xzf /tmp/rcodesign.tar.gz \
-C /usr/local/bin \
--strip-components=1 \
apple-codesign-0.22.0-macos-universal/rcodesign
rm /tmp/rcodesign.tar.gz

- name: Setup Apple Developer certificate and API key
run: |
set -euo pipefail
touch /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8}
chmod 600 /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8}
echo "$AC_CERTIFICATE_P12_BASE64" | base64 -d > /tmp/apple_cert.p12
echo "$AC_CERTIFICATE_PASSWORD" > /tmp/apple_cert_password.txt
echo "$AC_APIKEY_P8_BASE64" | base64 -d > /tmp/apple_apikey.p8
env:
AC_CERTIFICATE_P12_BASE64: ${{ secrets.AC_CERTIFICATE_P12_BASE64 }}
AC_CERTIFICATE_PASSWORD: ${{ secrets.AC_CERTIFICATE_PASSWORD }}
AC_APIKEY_P8_BASE64: ${{ secrets.AC_APIKEY_P8_BASE64 }}

- name: Build dylibs
run: |
set -euxo pipefail
go mod download

make gen/mark-fresh
make build/coder-dylib
env:
CODER_SIGN_DARWIN: 1
AC_CERTIFICATE_FILE: /tmp/apple_cert.p12
AC_CERTIFICATE_PASSWORD_FILE: /tmp/apple_cert_password.txt

- name: Upload build artifacts
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
with:
name: dylibs
path: |
./build/*.h
./build/*.dylib
retention-days: 7

- name: Delete Apple Developer certificate and API key
run: rm -f /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8}

release:
name: Build and publish
needs: build-dylib
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }}
permissions:
# Required to publish a release
Expand Down Expand Up @@ -145,6 +217,18 @@ jobs:
- name: Install nsis and zstd
run: sudo apt-get install -y nsis zstd

- name: Download dylibs
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: dylibs
path: ./build

- name: Insert dylibs
run: |
mv ./build/*amd64.dylib ./site/out/bin/coder-vpn-darwin-amd64.dylib
mv ./build/*arm64.dylib ./site/out/bin/coder-vpn-darwin-arm64.dylib
mv ./build/*arm64.h ./site/out/bin/coder-vpn-darwin-dylib.h

- name: Install nfpm
run: |
set -euo pipefail
Expand Down Expand Up @@ -271,6 +355,7 @@ jobs:
${{ steps.image-base-tag.outputs.tag }}

- name: Verify that images are pushed properly
if: steps.image-base-tag.outputs.tag != ''
run: |
# retry 10 times with a 5 second delay as the images may not be
# available immediately
Expand Down Expand Up @@ -303,17 +388,17 @@ jobs:
run: |
set -euxo pipefail

# build Docker images for each architecture
version="$(./scripts/version.sh)"
make build/coder_"$version"_linux_{amd64,arm64,armv7}.tag

# we can't build multi-arch if the images aren't pushed, so quit now
# if dry-running
if [[ "$CODER_RELEASE" != *t* ]]; then
echo Skipping multi-arch docker builds due to dry-run.
exit 0
fi

# build Docker images for each architecture
version="$(./scripts/version.sh)"
make build/coder_"$version"_linux_{amd64,arm64,armv7}.tag

# build and push multi-arch manifest, this depends on the other images
# being pushed so will automatically push them.
make push/build/coder_"$version"_linux.tag
Expand Down
23 changes: 23 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,12 @@ PACKAGE_OS_ARCHES := linux_amd64 linux_armv7 linux_arm64
# All architectures we build Docker images for (Linux only).
DOCKER_ARCHES := amd64 arm64 armv7

# All ${OS}_${ARCH} combos we build the desktop dylib for.
DYLIB_ARCHES := darwin_amd64 darwin_arm64

# Computed variables based on the above.
CODER_SLIM_BINARIES := $(addprefix build/coder-slim_$(VERSION)_,$(OS_ARCHES))
CODER_DYLIBS := $(foreach os_arch, $(DYLIB_ARCHES), build/coder-vpn_$(VERSION)_$(os_arch).dylib)
CODER_FAT_BINARIES := $(addprefix build/coder_$(VERSION)_,$(OS_ARCHES))
CODER_ALL_BINARIES := $(CODER_SLIM_BINARIES) $(CODER_FAT_BINARIES)
CODER_TAR_GZ_ARCHIVES := $(foreach os_arch, $(ARCHIVE_TAR_GZ), build/coder_$(VERSION)_$(os_arch).tar.gz)
Expand Down Expand Up @@ -238,6 +242,25 @@ $(CODER_ALL_BINARIES): go.mod go.sum \
cp "$@" "./site/out/bin/coder-$$os-$$arch$$dot_ext"
fi

# This task builds Coder Desktop dylibs
$(CODER_DYLIBS): go.mod go.sum $(GO_SRC_FILES)
@if [ "$(shell uname)" = "Darwin" ]; then
$(get-mode-os-arch-ext)
./scripts/build_go.sh \
--os "$$os" \
--arch "$$arch" \
--version "$(VERSION)" \
--output "$@" \
--dylib

else
echo "ERROR: Can't build dylib on non-Darwin OS" 1>&2
exit 1
fi

# This task builds both dylibs
build/coder-dylib: $(CODER_DYLIBS)

# This task builds all archives. It parses the target name to get the metadata
# for the build, so it must be specified in this format:
# build/coder_${version}_${os}_${arch}.${format}
Expand Down
30 changes: 26 additions & 4 deletions scripts/build_go.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# This script builds a single Go binary of Coder with the given parameters.
#
# Usage: ./build_go.sh [--version 1.2.3-devel+abcdef] [--os linux] [--arch amd64] [--output path/to/output] [--slim] [--agpl] [--boringcrypto]
# Usage: ./build_go.sh [--version 1.2.3-devel+abcdef] [--os linux] [--arch amd64] [--output path/to/output] [--slim] [--agpl] [--boringcrypto] [--dylib]
#
# Defaults to linux:amd64 with slim disabled, but can be controlled with GOOS,
# GOARCH and CODER_SLIM_BUILD=1. If no version is specified, defaults to the
Expand All @@ -25,6 +25,9 @@
#
# If the --boringcrypto parameter is specified, builds use boringcrypto instead of
# the standard go crypto libraries.
#
# If the --dylib parameter is specified, the Coder Desktop `.dylib` is built
# instead of the standard binary. This is only supported on macOS arm64 & amd64.

set -euo pipefail
# shellcheck source=scripts/lib.sh
Expand All @@ -36,12 +39,14 @@ arch="${GOARCH:-amd64}"
slim="${CODER_SLIM_BUILD:-0}"
sign_darwin="${CODER_SIGN_DARWIN:-0}"
sign_windows="${CODER_SIGN_WINDOWS:-0}"
bin_ident="com.coder.cli"
output_path=""
agpl="${CODER_BUILD_AGPL:-0}"
boringcrypto=${CODER_BUILD_BORINGCRYPTO:-0}
debug=0
dylib=0

args="$(getopt -o "" -l version:,os:,arch:,output:,slim,agpl,sign-darwin,boringcrypto,debug -- "$@")"
args="$(getopt -o "" -l version:,os:,arch:,output:,slim,agpl,sign-darwin,boringcrypto,dylib,debug -- "$@")"
eval set -- "$args"
while true; do
case "$1" in
Expand Down Expand Up @@ -78,6 +83,10 @@ while true; do
boringcrypto=1
shift
;;
--dylib)
dylib=1
shift
;;
--debug)
debug=1
shift
Expand Down Expand Up @@ -168,18 +177,31 @@ if [[ "$agpl" == 1 ]]; then
fi

cgo=0
if [[ "$dylib" == 1 ]]; then
if [[ "$os" != "darwin" ]]; then
error "dylib builds are not supported on $os"
fi
cgo=1
cmd_path="./vpn/dylib/lib.go"
build_args+=("-buildmode=c-shared")
SDKROOT="$(xcrun --sdk macosx --show-sdk-path)"
export SDKROOT
bin_ident="com.coder.vpn"
fi

goexp=""
if [[ "$boringcrypto" == 1 ]]; then
cgo=1
goexp="boringcrypto"
fi

GOEXPERIMENT="$goexp" CGO_ENABLED="$cgo" GOOS="$os" GOARCH="$arch" GOARM="$arm_version" go build \
GOEXPERIMENT="$goexp" CGO_ENABLED="$cgo" GOOS="$os" GOARCH="$arch" GOARM="$arm_version" \
go build \
"${build_args[@]}" \
"$cmd_path" 1>&2

if [[ "$sign_darwin" == 1 ]] && [[ "$os" == "darwin" ]]; then
execrelative ./sign_darwin.sh "$output_path" 1>&2
execrelative ./sign_darwin.sh "$output_path" "$bin_ident" 1>&2
fi

if [[ "$sign_windows" == 1 ]] && [[ "$os" == "windows" ]]; then
Expand Down
11 changes: 7 additions & 4 deletions scripts/release/publish.sh
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,13 @@ if [[ "$stable" == 1 ]]; then
fi

target_commitish=main # This is the default.
release_branch_refname=$(git branch --remotes --contains "${new_tag}" --format '%(refname)' '*/release/*')
if [[ -n "${release_branch_refname}" ]]; then
# refs/remotes/origin/release/2.9 -> release/2.9
target_commitish="release/${release_branch_refname#*release/}"
# Skip during dry-runs
if [[ "$dry_run" == 0 ]]; then
release_branch_refname=$(git branch --remotes --contains "${new_tag}" --format '%(refname)' '*/release/*')
if [[ -n "${release_branch_refname}" ]]; then
# refs/remotes/origin/release/2.9 -> release/2.9
target_commitish="release/${release_branch_refname#*release/}"
fi
fi

# We pipe `true` into `gh` so that it never tries to be interactive.
Expand Down
Loading
Loading