Skip to content
Open
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
31 changes: 31 additions & 0 deletions .github/workflows/macos-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: macOS build

on:
push:
branches: [master, feature/apple-silicon-macos]
tags: ['v*']
pull_request:
branches: [master]

jobs:
build:
runs-on: macos-14
steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- name: Install dependencies
run: brew install qt go

- name: Build
run: ./scripts/macos/build-local.sh

- name: Verify architecture
run: |
file build/Moolticute.app/Contents/MacOS/moolticute
lipo -info build/Moolticute.app/Contents/MacOS/moolticute

- name: Run tests
working-directory: build
run: ./tests/tests
69 changes: 69 additions & 0 deletions .github/workflows/macos-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: macOS release

on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
version:
description: 'Release version (e.g. v1.04.0-arm64)'
required: true
default: 'v1.04.0-arm64'

permissions:
contents: write

jobs:
build-and-release:
runs-on: macos-14
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0

- name: Install dependencies
run: brew install qt go

- name: Build and package app
run: |
./scripts/macos/build-local.sh --package
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
./scripts/macos/package-release.sh "${{ github.event.inputs.version }}"
else
./scripts/macos/package-release.sh "${GITHUB_REF_NAME}"
fi

- name: Run tests
working-directory: build
run: ./tests/tests

- name: Verify native architecture
run: |
file build/Moolticute.app/Contents/MacOS/moolticute
lipo -info build/Moolticute.app/Contents/MacOS/moolticute

- name: Publish GitHub release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version || github.ref_name }}
name: Moolticute macOS Apple Silicon (${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version || github.ref_name }})
body: |
Native **arm64** Moolticute build for Apple Silicon Macs (no Rosetta required).

### Install
1. Download `Moolticute-*-macos-arm64.dmg` or `.zip`
2. Open the DMG and drag **Moolticute** to Applications
3. On first launch, right-click → **Open** if macOS blocks an unsigned app

### Includes
- Moolticute GUI + `moolticuted` daemon
- `mc-agent` and `mc-cli` (arm64)

Built from commit ${{ github.sha }}.
files: |
build/Moolticute-*-macos-*.dmg
build/Moolticute-*-macos-*.zip
draft: false
prerelease: true
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,21 @@ git clone https://github.com/mooltipass/moolticute.git --recurse-submodules
- qt-network
- qt-websockets

##### macOS (Apple Silicon)

Official release DMGs are currently built for Intel (`x86_64`). To run natively on Apple Silicon without Rosetta, build from source:

```bash
brew install qt go
git clone --recurse-submodules https://github.com/mooltipass/moolticute.git
cd moolticute
./scripts/macos/build-local.sh --package
```

See [documentation/macos_apple_silicon.md](documentation/macos_apple_silicon.md) for details.

**Community Apple Silicon builds** (unsigned): [GitHub Releases](https://github.com/poitee/moolticute/releases) on the `poitee/moolticute` fork.

##### Linux
- Requires the qt-dbus module
- Requires to install [udev rule](https://github.com/mooltipass/mooltipass-udev) for it
Expand Down
37 changes: 37 additions & 0 deletions documentation/macos_apple_silicon.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Building Moolticute on macOS (Apple Silicon)

Official release DMGs are currently built for Intel (`x86_64`). On Apple Silicon Macs the app runs through Rosetta today, but native `arm64` builds are supported from source.

## Quick build

```bash
brew install qt go
git clone --recurse-submodules https://github.com/mooltipass/moolticute.git
cd moolticute
./scripts/macos/build-local.sh
open build/Moolticute.app
```

To bundle the daemon and CLI tools into the app:

```bash
./scripts/macos/build-local.sh --package
```

## What changes on Apple Silicon

- Qt is taken from Homebrew (`/opt/homebrew/opt/qt`) when available.
- `mc-agent` and `mc-cli` are compiled from source because the prebuilt Intel binaries hosted for releases are not compatible with `arm64`.
- The resulting `Moolticute.app` runs natively without Rosetta.

## Verify the build

```bash
file build/Moolticute.app/Contents/MacOS/moolticute
# Expected on Apple Silicon: Mach-O 64-bit executable arm64
```

## Related issues

- https://github.com/mooltipass/moolticute/issues/1254
- https://github.com/mooltipass/moolticute/issues/929
12 changes: 6 additions & 6 deletions scripts/ci/osx/after_success.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ set -ev

SCRIPTDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
source $SCRIPTDIR/../funcs.sh
source $SCRIPTDIR/env.sh

VERSION="$(get_version .)"

Expand All @@ -17,7 +18,8 @@ if [ "$(git rev-list -n 1 $VERSION)" != "$(git rev-parse HEAD)" ]; then
exit 0
fi

QTDIR="/Users/travis/Qt/6.2.4/macos"
detect_qtdir
MACDEPLOYQT_BIN="$(macdeployqt_bin)"
APP=Moolticute
# this directory name will also be shown in the title when the DMG is mounted
TEMPDIR=build/$APP
Expand All @@ -37,15 +39,13 @@ cat build/$APP.app/Contents/Info.plist
# Copy daemon to bundle
cp build/moolticuted build/$APP.app/Contents/MacOS/

#Get 3rd party tools
# Bundle architecture-matching CLI tools
mkdir -p build/$APP.app/Contents/MacOS/cli
wget_retry https://calaos.fr/mooltipass/tools/macos/mc-agent -O build/$APP.app/Contents/MacOS/cli/mc-agent
wget_retry https://calaos.fr/mooltipass/tools/macos/mc-cli -O build/$APP.app/Contents/MacOS/cli/mc-cli
chmod +x build/$APP.app/Contents/MacOS/cli/mc-agent build/$APP.app/Contents/MacOS/cli/mc-cli
bundle_mc_cli_tools build/$APP.app/Contents/MacOS/cli

# use macdeployqt to deploy the application
echo "Calling macdeployqt"
$QTDIR/bin/macdeployqt build/$APP.app
$MACDEPLOYQT_BIN build/$APP.app
if [ "$?" -ne "0" ]; then
echo "Failed to run macdeployqt"
exit 1
Expand Down
101 changes: 101 additions & 0 deletions scripts/ci/osx/env.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#!/bin/bash
# Shared macOS build environment for Intel and Apple Silicon hosts.

set -e

OSX_SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

export MACOS_ARCH="$(uname -m)"

case "$MACOS_ARCH" in
arm64)
export GOARCH=arm64
export MC_CLI_TOOLS_ARCH=arm64
;;
x86_64)
export GOARCH=amd64
export MC_CLI_TOOLS_ARCH=amd64
;;
*)
echo "Unsupported macOS architecture: $MACOS_ARCH"
exit 1
;;
esac

detect_qtdir() {
if [ -n "${QTDIR:-}" ] && { [ -x "$QTDIR/bin/qmake" ] || [ -x "$QTDIR/bin/qmake6" ]; }; then
return 0
fi

for candidate in \
"/opt/homebrew/opt/qt" \
"/usr/local/opt/qt" \
"/Users/travis/Qt/6.2.4/macos" \
"$HOME/Qt/6.2.4/macos"
do
if [ -x "$candidate/bin/qmake" ] || [ -x "$candidate/bin/qmake6" ]; then
export QTDIR="$candidate"
return 0
fi
done

echo "Qt not found. Install with Homebrew (brew install qt) or set QTDIR."
return 1
}

qmake_bin() {
if [ -x "$QTDIR/bin/qmake6" ]; then
echo "$QTDIR/bin/qmake6"
else
echo "$QTDIR/bin/qmake"
fi
}

macdeployqt_bin() {
if [ -x "$QTDIR/bin/macdeployqt6" ]; then
echo "$QTDIR/bin/macdeployqt6"
else
echo "$QTDIR/bin/macdeployqt"
fi
}

build_mc_cli_tools() {
local dest="$1"

if ! command -v go >/dev/null 2>&1; then
echo "Go is required to build mc-agent and mc-cli for $MACOS_ARCH"
return 1
fi

mkdir -p "$dest"
local tmp
tmp="$(mktemp -d)"

for tool in mc-agent mc-cli; do
rm -rf "$tmp/$tool"
git clone --depth 1 "https://github.com/raoulh/$tool.git" "$tmp/$tool"
(
cd "$tmp/$tool"
GOOS=darwin GOARCH="$GOARCH" go build -o "$dest/$tool" .
) || return 1
[ -f "$dest/$tool" ] || return 1
chmod +x "$dest/$tool"
done

rm -rf "$tmp"
}

bundle_mc_cli_tools() {
local dest="$1"

if [ "$MC_CLI_TOOLS_ARCH" = "amd64" ]; then
# shellcheck source=../funcs.sh
source "$OSX_SCRIPTDIR/../funcs.sh"
wget_retry "https://calaos.fr/mooltipass/tools/macos/mc-agent" -O "$dest/mc-agent"
wget_retry "https://calaos.fr/mooltipass/tools/macos/mc-cli" -O "$dest/mc-cli"
chmod +x "$dest/mc-agent" "$dest/mc-cli"
return 0
fi

build_mc_cli_tools "$dest"
}
29 changes: 20 additions & 9 deletions scripts/ci/osx/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,25 @@ set -ev
echo "install.sh"

brew upgrade openssl
brew install jq lftp
brew uninstall wget
brew install jq lftp go
brew uninstall wget || true
brew install wget

SCRIPTDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
source $SCRIPTDIR/env.sh

echo "Current PATH: $PATH"
#pip3 install --break-system-packages --upgrade pip
#pip3 install --break-system-packages aqtinstall -t /Users/travis/aqt
pip3 install --upgrade pip
pip3 install aqtinstall -t /Users/travis/aqt
export PYTHONPATH=$PYTHONPATH:/Users/travis/aqt
/Users/travis/aqt/bin/aqt install-qt mac desktop 6.2.4 clang_64 -m all -O /Users/travis/Qt
networksetup -setv6off Ethernet
echo "Target architecture: $MACOS_ARCH"

if [ "$MACOS_ARCH" = "arm64" ]; then
brew install qt
detect_qtdir
echo "Using Homebrew Qt at $QTDIR for Apple Silicon"
else
pip3 install --upgrade pip
pip3 install aqtinstall -t /Users/travis/aqt
export PYTHONPATH=$PYTHONPATH:/Users/travis/aqt
/Users/travis/aqt/bin/aqt install-qt mac desktop 6.2.4 clang_64 -m all -O /Users/travis/Qt
export QTDIR="/Users/travis/Qt/6.2.4/macos"
networksetup -setv6off Ethernet || true
fi
7 changes: 4 additions & 3 deletions scripts/ci/osx/script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@
set -ev

SCRIPTDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
source $SCRIPTDIR/../funcs.sh
source $SCRIPTDIR/env.sh

mkdir build
pushd build

QTDIR="/Users/travis/Qt/6.2.4/macos"
detect_qtdir
QMAKE_BIN="$(qmake_bin)"
PATH="$QTDIR/bin:$PATH"
LDFLAGS=-L$QTDIR/lib
CPPFLAGS=-I$QTDIR/include

make_version .. macos

qmake ../Moolticute.pro
"$QMAKE_BIN" ../Moolticute.pro

# Compund exit codes of make and the tests.
make && ./tests/tests
Expand Down
Loading