Skip to content

Commit a440a66

Browse files
Add darwin support (#175)
1 parent 9c3a0d5 commit a440a66

18 files changed

+966
-5
lines changed
+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
name: Build and Notarize macOS Installer
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
release_version:
7+
description: 'Release number for MW Agent for macOS'
8+
required: true
9+
push:
10+
paths-ignore:
11+
- '.github/**'
12+
tags:
13+
- '[0-9]+.[0-9]+.[0-9]+'
14+
15+
jobs:
16+
build:
17+
strategy:
18+
matrix:
19+
include:
20+
- arch: arm64
21+
image: macos-latest
22+
- arch: amd64
23+
image: macos-latest-large
24+
max-parallel: 1
25+
runs-on: ${{ matrix.image }}
26+
steps:
27+
- name: Checkout Repo
28+
uses: actions/checkout@v4
29+
with:
30+
token: ${{ secrets.GHCR_TOKEN }}
31+
ssh-key: ${{ secrets.CHECK_AGENT_ACCESS }}
32+
submodules: 'recursive'
33+
34+
- name: Set up Git credentials
35+
run: |
36+
git config --global url."https://${{ secrets.GHCR_TOKEN }}:@github.com/".insteadOf "https://github.com/"
37+
env:
38+
GITHUB_TOKEN: ${{ secrets.GHCR_TOKEN }}
39+
40+
- name: Set up Go
41+
uses: actions/setup-go@v5
42+
with:
43+
go-version: '^1.23.1' # The Go version to download (if necessary) and use.
44+
45+
- name: Setting Release Number
46+
run: |
47+
if [ -n "${{ github.event.inputs.release_version }}" ]; then
48+
echo "RELEASE_VERSION=${{ github.event.inputs.release_version }}" >> $GITHUB_ENV
49+
else
50+
echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
51+
fi
52+
53+
- name: Set up signing certificates
54+
run: |
55+
echo "$APPLE_DEVELOPER_ID_APPLICATION_CERTIFICATE" | base64 --decode > signing_certificate_application.p12
56+
echo "$APPLE_DEVELOPER_ID_INSTALLER_CERTIFICATE" | base64 --decode > signing_certificate_installer.p12
57+
security create-keychain -p "$APPLE_KEYCHAIN_PASSWORD" $KEYCHAIN_NAME
58+
security unlock-keychain -p "$APPLE_KEYCHAIN_PASSWORD" $KEYCHAIN_NAME
59+
security import signing_certificate_application.p12 -k $KEYCHAIN_NAME -P "$APPLE_DEVELOPER_ID_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
60+
security import signing_certificate_installer.p12 -k $KEYCHAIN_NAME -P "$APPLE_DEVELOPER_ID_CERTIFICATE_PASSWORD" -T /usr/bin/productbuild
61+
security list-keychains -s $KEYCHAIN_NAME
62+
security set-keychain-settings -t 3600 -u $KEYCHAIN_NAME
63+
security set-key-partition-list -S apple-tool:,apple: -s -k "$APPLE_KEYCHAIN_PASSWORD" $KEYCHAIN_NAME
64+
env:
65+
APPLE_DEVELOPER_ID_APPLICATION_CERTIFICATE: ${{ secrets.APPLE_DEVELOPER_ID_APPLICATION_CERTIFICATE_BASE64 }}
66+
APPLE_DEVELOPER_ID_INSTALLER_CERTIFICATE: ${{ secrets.APPLE_DEVELOPER_ID_INSTALLER_CERTIFICATE_BASE64 }}
67+
APPLE_DEVELOPER_ID_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_DEVELOPER_ID_CERTIFICATE_PASSWORD }}
68+
APPLE_KEYCHAIN_PASSWORD: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
69+
KEYCHAIN_NAME: "build.keychain"
70+
71+
- name: Build and notarize installer
72+
run: |
73+
CGO_ENABLED=1 GOPRIVATE=github.com/middleware-labs GOOS=darwin GOARCH=${{ matrix.arch }} go build -ldflags="-s -w -X main.agentVersion=${RELEASE_VERSION}" -v -a -o build/mw-host-agent cmd/host-agent/main.go
74+
bash package-tooling/darwin/create_installer.sh ${{ env.RELEASE_VERSION }}
75+
env:
76+
APPLE_DEVELOPER_ID_APPLICATION: "Developer ID Application: Middleware Labs Inc (AV4NQ68UX8)"
77+
APPLE_DEVELOPER_ID_INSTALLER: "Developer ID Installer: Middleware Labs Inc (AV4NQ68UX8)"
78+
APPLE_ID: ${{ secrets.APPLE_ID }}
79+
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
80+
APPLE_DEVELOPER_TEAM_ID: ${{ secrets.APPLE_DEVELOPER_TEAM_ID }}
81+
KEYCHAIN_PROFILE: "Middleware MacOS Agent"
82+
KEYCHAIN_NAME: "build.keychain"
83+
RELEASE_VERSION: ${{ env.RELEASE_VERSION }}
84+
ARCH: ${{ matrix.arch }}
85+
86+
- name: Upload installer package
87+
uses: actions/upload-artifact@v4
88+
with:
89+
name: mw-macos-agent-setup-${{ matrix.arch }}.pkg
90+
path: build/mw-macos-agent-setup-${{ matrix.arch }}.pkg

Makefile

+7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ build-windows:
44
GOOS=windows CGO_ENABLED=0 go build -ldflags=${LD_FLAGS} -o build/mw-windows-agent.exe cmd/host-agent/main.go
55
build-linux:
66
GOOS=linux CGO_ENABLED=0 go build -ldflags=${LD_FLAGS} -o build/mw-host-agent cmd/host-agent/main.go
7+
build-darwin-amd64:
8+
GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 go build -ldflags=${LD_FLAGS} -o build/mw-host-agent cmd/host-agent/main.go
9+
build-darwin-arm64:
10+
GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 go build -ldflags=${LD_FLAGS} -o build/mw-host-agent cmd/host-agent/main.go
711
build-linux-amd64:
812
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags=${LD_FLAGS} -o build/mw-host-agent-amd64 cmd/host-agent/main.go
913
build-linux-arm64:
@@ -26,6 +30,9 @@ package-linux: package-linux-deb package-linux-rpm package-linux-docker
2630
package-linux-docker:
2731
Dockerfiles/docker-build.sh build local Dockerfiles/DockerfileLinux
2832

33+
package-darwin: build-darwin-arm64
34+
package-tooling/darwin/create_installer.sh ${RELEASE_VERSION}
35+
2936
package: package-windows package-linux
3037

3138
clean:

cmd/host-agent/main.go

+16-3
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,11 @@ func getFlags(execPath string, cfg *agent.HostConfig) []cli.Flag {
185185
DefaultText: func() string {
186186
switch runtime.GOOS {
187187
case "linux":
188-
return filepath.Join("/etc", "mw-agent", "mw-agent.yaml")
188+
return filepath.Join("/etc", "mw-agent", "agent-config.yaml")
189+
case "darwin":
190+
return filepath.Join("/etc", "mw-agent", "agent-config.yaml")
191+
case "windows":
192+
return filepath.Join(filepath.Dir(execPath), "agent-config.yaml")
189193
}
190194

191195
return ""
@@ -200,6 +204,8 @@ func getFlags(execPath string, cfg *agent.HostConfig) []cli.Flag {
200204
switch runtime.GOOS {
201205
case "linux":
202206
return filepath.Join("/etc", "mw-agent", "otel-config.yaml")
207+
case "darwin":
208+
return filepath.Join("/etc", "mw-agent", "otel-config.yaml")
203209
case "windows":
204210
return filepath.Join(filepath.Dir(execPath), "otel-config.yaml")
205211
}
@@ -210,6 +216,10 @@ func getFlags(execPath string, cfg *agent.HostConfig) []cli.Flag {
210216
switch runtime.GOOS {
211217
case "linux":
212218
return filepath.Join("/etc", "mw-agent", "otel-config.yaml")
219+
case "darwin":
220+
return filepath.Join("/etc", "mw-agent", "otel-config.yaml")
221+
case "windows":
222+
return filepath.Join(filepath.Dir(execPath), "otel-config.yaml")
213223
}
214224

215225
return ""
@@ -305,8 +315,11 @@ func main() {
305315
hostname = "unknown"
306316
}
307317

308-
logger.Info("starting host agent", zap.String("agent location", execPath),
309-
zap.String("hostname", hostname))
318+
logger.Info("starting host agent",
319+
zap.String("agent location", execPath),
320+
zap.String("hostname", hostname),
321+
zap.String("OS", runtime.GOOS),
322+
zap.String("arch", runtime.GOARCH))
310323

311324
logger.Info("host agent config", zap.Stringer("config", cfg),
312325
zap.String("version", agentVersion),

go.mod

+2-2
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ require (
172172
github.com/googleapis/gax-go/v2 v2.12.2 // indirect
173173
github.com/gophercloud/gophercloud v1.8.0 // indirect
174174
github.com/gorilla/websocket v1.5.0 // indirect
175-
github.com/grafana/pyroscope-go/godeltaprof v0.1.6 // indirect
175+
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect
176176
github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd // indirect
177177
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
178178
github.com/hashicorp/consul/api v1.28.3 // indirect
@@ -206,7 +206,7 @@ require (
206206
github.com/jpillora/backoff v1.0.0 // indirect
207207
github.com/json-iterator/go v1.1.12 // indirect
208208
github.com/k0kubun/pp v3.0.1+incompatible // indirect
209-
github.com/klauspost/compress v1.17.8 // indirect
209+
github.com/klauspost/compress v1.17.9 // indirect
210210
github.com/knadh/koanf v1.5.0 // indirect
211211
github.com/knadh/koanf/v2 v2.1.1 // indirect
212212
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect

go.sum

+4
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,8 @@ github.com/grafana/pyroscope-go v1.1.1 h1:PQoUU9oWtO3ve/fgIiklYuGilvsm8qaGhlY4Vw
379379
github.com/grafana/pyroscope-go v1.1.1/go.mod h1:Mw26jU7jsL/KStNSGGuuVYdUq7Qghem5P8aXYXSXG88=
380380
github.com/grafana/pyroscope-go/godeltaprof v0.1.6 h1:nEdZ8louGAplSvIJi1HVp7kWvFvdiiYg3COLlTwJiFo=
381381
github.com/grafana/pyroscope-go/godeltaprof v0.1.6/go.mod h1:Tk376Nbldo4Cha9RgiU7ik8WKFkNpfds98aUzS8omLE=
382+
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg=
383+
github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
382384
github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd h1:PpuIBO5P3e9hpqBD0O/HjhShYuM6XE0i/lbE6J94kww=
383385
github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A=
384386
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
@@ -522,6 +524,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
522524
github.com/klauspost/compress v1.17.3/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
523525
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
524526
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
527+
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
528+
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
525529
github.com/knadh/koanf v1.5.0 h1:q2TSd/3Pyc/5yP9ldIrSdIz26MCcyNQzW0pEAugLPNs=
526530
github.com/knadh/koanf v1.5.0/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs=
527531
github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM=
+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#!/bin/bash
2+
3+
# Check if required environment variables are set
4+
if [ -z "$APPLE_DEVELOPER_ID_APPLICATION" ] || [ -z "$APPLE_DEVELOPER_ID_INSTALLER" ] || [ -z "$APPLE_ID" ] || [ -z "$APPLE_ID_PASSWORD" ] || [ -z "$KEYCHAIN_PROFILE" ] || [ -z "$KEYCHAIN_NAME" ]; then
5+
echo "Error: One or more required environment variables are not set."
6+
echo "Required variables: APPLE_DEVELOPER_ID_APPLICATION, APPLE_DEVELOPER_ID_INSTALLER, APPLE_ID, APPLE_ID_PASSWORD, KEYCHAIN_PROFILE, KEYCHAIN_NAME"
7+
exit 1
8+
fi
9+
10+
# Set variables
11+
REPO_ROOT=$(git rev-parse --show-toplevel)
12+
BASE_DIR="/tmp/mw-agent-pkg"
13+
INSTALLER_DIR="$REPO_ROOT/build"
14+
ROOT_DIR="$BASE_DIR/root"
15+
SCRIPTS_DIR="$BASE_DIR/scripts"
16+
RESOURCE_DIR="$BASE_DIR/resources"
17+
IDENTIFIER="io.middleware.mw-agent"
18+
INSTALLER_NAME="mw-macos-agent-setup-${ARCH}.pkg"
19+
RELEASE_VERSION=$1
20+
21+
# Unlock the keychain
22+
security unlock-keychain -p "$APPLE_KEYCHAIN_PASSWORD" $KEYCHAIN_NAME
23+
# Prepare directories for the installer
24+
mkdir -p $BASE_DIR
25+
mkdir -p $RESOURCE_DIR
26+
mkdir -p $ROOT_DIR/Library/LaunchDaemons
27+
mkdir -p $ROOT_DIR/opt/mw-agent/
28+
mkdir -p $ROOT_DIR/etc/mw-agent/
29+
mkdir -p $SCRIPTS_DIR
30+
sudo cp $REPO_ROOT/build/mw-host-agent $ROOT_DIR/opt/mw-agent/mw-agent
31+
sudo cp $REPO_ROOT/package-tooling/agent-config.yaml.sample $ROOT_DIR/etc/mw-agent/agent-config.yaml
32+
sudo cp -r $REPO_ROOT/package-tooling/darwin/resources/* $RESOURCE_DIR
33+
sudo cp -r $REPO_ROOT/package-tooling/darwin/preinstall $SCRIPTS_DIR
34+
sudo chmod +x $SCRIPTS_DIR/preinstall
35+
36+
sudo cp -r $REPO_ROOT/package-tooling/darwin/prompt_user.applescript $SCRIPTS_DIR
37+
38+
sudo cp -r $REPO_ROOT/package-tooling/darwin/postinstall $SCRIPTS_DIR
39+
sudo chmod +x $SCRIPTS_DIR/postinstall
40+
41+
sudo cp -r $REPO_ROOT/package-tooling/darwin/uninstall.sh $ROOT_DIR/opt/mw-agent/uninstall.sh
42+
sudo chmod +x $ROOT_DIR/opt/mw-agent/uninstall.sh
43+
44+
sudo cp -r $REPO_ROOT/package-tooling/darwin/io.middleware.mw-agent.plist $ROOT_DIR/Library/LaunchDaemons/
45+
46+
sudo cp -r $REPO_ROOT/package-tooling/darwin/distribution.xml $BASE_DIR/distribution.xml
47+
48+
echo "Signing the mw-agent binary with hardened runtime"
49+
sudo codesign --sign "$APPLE_DEVELOPER_ID_APPLICATION" --options runtime --timestamp "$ROOT_DIR/opt/mw-agent/mw-agent"
50+
# Create component package for the installer
51+
sudo pkgbuild --root $ROOT_DIR \
52+
--identifier $IDENTIFIER \
53+
--version $RELEASE_VERSION \
54+
--install-location / \
55+
--scripts $SCRIPTS_DIR \
56+
$BASE_DIR/middleware_agent.pkg
57+
58+
# Check if pkgbuild command failed
59+
if [ $? -ne 0 ]; then
60+
echo "Error: pkgbuild command failed"
61+
exit 1
62+
fi
63+
64+
# Create and sign the installer package
65+
echo "Signing the installer package"
66+
sudo productbuild --distribution $BASE_DIR/distribution.xml \
67+
--package-path $BASE_DIR \
68+
--resources $RESOURCE_DIR \
69+
--sign "$APPLE_DEVELOPER_ID_INSTALLER" \
70+
--keychain $KEYCHAIN_NAME \
71+
$INSTALLER_DIR/$INSTALLER_NAME
72+
73+
# Check if productbuild command failed
74+
if [ $? -ne 0 ]; then
75+
echo "Error: productbuild command failed"
76+
exit 1
77+
fi
78+
79+
echo "Notarizing the installer package, team id: $APPLE_DEVELOPER_TEAM_ID"
80+
xcrun notarytool store-credentials "$KEYCHAIN_PROFILE" --apple-id $APPLE_ID --password $APPLE_ID_PASSWORD --team-id "$APPLE_DEVELOPER_TEAM_ID"
81+
82+
echo "Submitting the installer package for notarization"
83+
sudo xcrun notarytool submit $INSTALLER_DIR/$INSTALLER_NAME --keychain-profile "$KEYCHAIN_PROFILE" --wait
84+
85+
# Check if notarytool command failed
86+
if [ $? -ne 0 ]; then
87+
echo "Error: notarytool command failed"
88+
exit 1
89+
fi
90+
91+
echo "Stapling the notarization ticket to the installer package"
92+
sudo xcrun stapler staple $INSTALLER_DIR/$INSTALLER_NAME
93+
94+
# Check if stapler command failed
95+
if [ $? -ne 0 ]; then
96+
echo "Error: stapler command failed"
97+
exit 1
98+
fi
99+
echo "Installer package created at $INSTALLER_DIR/$INSTALLER_NAME"
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<installer-gui-script minSpecVersion="1">
3+
<title>Middleware Agent</title>
4+
<welcome mime-type="text/html" file="welcome.html"/>
5+
<license file="LICENSE"/>
6+
<conclusion mime-type="text/html" file="conclusion.html"/>
7+
<background mime-type="image/png" alignment="bottomleft" scaling="proportional" file="logo.png"/>
8+
<background-darkAqua mime-type="image/png" alignment="bottomleft" scaling="proportional" file="logo.png"/>
9+
<options rootVolumeOnly="true" hostArchitectures="arm64"/>
10+
<choices-outline>
11+
<line choice="default"/>
12+
</choices-outline>
13+
<choice id="default" title="Install Middleware Agent">
14+
<pkg-ref id="io.middleware.mw-agent"/>
15+
</choice>
16+
17+
<pkg-ref id="io.middleware.mw-agent" version="1.7.6" auth="root" onConclusion="none">#middleware_agent.pkg</pkg-ref>
18+
</installer-gui-script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.7.4">
4+
<dict>
5+
<key>Label</key>
6+
<string>io.middleware.mw-agent</string>
7+
<key>ProgramArguments</key>
8+
<array>
9+
<string>/opt/mw-agent/mw-agent</string>
10+
<string>start</string>
11+
<string>--config-file=/etc/mw-agent/agent-config.yaml</string>
12+
</array>
13+
<key>UserName</key>
14+
<string>root</string>
15+
<key>GroupName</key>
16+
<string>wheel</string>
17+
<key>RunAtLoad</key>
18+
<true/>
19+
<key>KeepAlive</key>
20+
<true/>
21+
<key>StandardErrorPath</key>
22+
<string>/var/log/mw-agent.log</string>
23+
<key>StandardOutPath</key>
24+
<string>/var/log/mw-agent.log</string>
25+
</dict>
26+
</plist>

0 commit comments

Comments
 (0)