mirror of
https://github.com/pepaskopec-blip/Graduately.git
synced 2026-10-27 15:14:35 +00:00
Ship only the native Swift app on macOS.
GTK packaging stays for Linux and Windows; the Mac installer and updates use the Swift zip. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
58e3a75f21
commit
011bc3c685
7 changed files with 71 additions and 524 deletions
71
.github/workflows/build.yml
vendored
71
.github/workflows/build.yml
vendored
|
|
@ -86,14 +86,10 @@ jobs:
|
||||||
|
|
||||||
macos:
|
macos:
|
||||||
name: macOS app
|
name: macOS app
|
||||||
runs-on: macos-14
|
runs-on: macos-26
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
brew install gtk4 pkg-config
|
|
||||||
|
|
||||||
# Developer ID lives in repo secrets. Without it the bundle is only
|
# Developer ID lives in repo secrets. Without it the bundle is only
|
||||||
# ad-hoc signed and Gatekeeper still blocks downloaded copies.
|
# ad-hoc signed and Gatekeeper still blocks downloaded copies.
|
||||||
# Do not use `secrets` in `if:` — GitHub rejects the whole workflow.
|
# Do not use `secrets` in `if:` — GitHub rejects the whole workflow.
|
||||||
|
|
@ -123,9 +119,10 @@ jobs:
|
||||||
$(security list-keychains -d user | tr -d '"')
|
$(security list-keychains -d user | tr -d '"')
|
||||||
rm -f "$cert"
|
rm -f "$cert"
|
||||||
|
|
||||||
- name: Build .app zip
|
- name: Build Swift .app zip
|
||||||
env:
|
env:
|
||||||
COMMIT: ${{ github.sha }}
|
COMMIT: ${{ github.sha }}
|
||||||
|
DEVELOPER_DIR: /Applications/Xcode.app/Contents/Developer
|
||||||
MACOS_SIGN_IDENTITY: ${{ secrets.MACOS_SIGN_IDENTITY }}
|
MACOS_SIGN_IDENTITY: ${{ secrets.MACOS_SIGN_IDENTITY }}
|
||||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||||
|
|
@ -134,8 +131,8 @@ jobs:
|
||||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||||
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
|
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
|
||||||
run: |
|
run: |
|
||||||
chmod +x scripts/bundle-macos.sh
|
chmod +x scripts/bundle-macos-swift.sh
|
||||||
./scripts/bundle-macos.sh
|
./scripts/bundle-macos-swift.sh
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
|
@ -215,62 +212,9 @@ jobs:
|
||||||
name: maturita-ios
|
name: maturita-ios
|
||||||
path: dist-ios/maturita-ios.ipa
|
path: dist-ios/maturita-ios.ipa
|
||||||
|
|
||||||
macos-swift:
|
|
||||||
name: macOS Swift app
|
|
||||||
runs-on: macos-26
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Import Developer ID certificate
|
|
||||||
env:
|
|
||||||
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
|
|
||||||
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
if [ -z "${MACOS_CERTIFICATE:-}" ]; then
|
|
||||||
echo "No Developer ID certificate; the bundle will be ad-hoc signed."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
cert="$RUNNER_TEMP/certificate.p12"
|
|
||||||
keychain="$RUNNER_TEMP/signing.keychain-db"
|
|
||||||
pass="$(openssl rand -base64 32)"
|
|
||||||
echo "$MACOS_CERTIFICATE" | base64 --decode > "$cert"
|
|
||||||
security create-keychain -p "$pass" "$keychain"
|
|
||||||
security set-keychain-settings -lut 21600 "$keychain"
|
|
||||||
security unlock-keychain -p "$pass" "$keychain"
|
|
||||||
security import "$cert" -k "$keychain" \
|
|
||||||
-P "$MACOS_CERTIFICATE_PASSWORD" \
|
|
||||||
-T /usr/bin/codesign -T /usr/bin/security
|
|
||||||
security set-key-partition-list -S apple-tool:,apple: -s \
|
|
||||||
-k "$pass" "$keychain"
|
|
||||||
security list-keychains -d user -s "$keychain" \
|
|
||||||
$(security list-keychains -d user | tr -d '"')
|
|
||||||
rm -f "$cert"
|
|
||||||
|
|
||||||
- name: Build Swift .app zip
|
|
||||||
env:
|
|
||||||
COMMIT: ${{ github.sha }}
|
|
||||||
DEVELOPER_DIR: /Applications/Xcode.app/Contents/Developer
|
|
||||||
MACOS_SIGN_IDENTITY: ${{ secrets.MACOS_SIGN_IDENTITY }}
|
|
||||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
|
||||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
|
||||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
|
||||||
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
|
|
||||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
|
||||||
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
|
|
||||||
run: |
|
|
||||||
chmod +x scripts/bundle-macos-swift.sh
|
|
||||||
./scripts/bundle-macos-swift.sh
|
|
||||||
|
|
||||||
- name: Upload artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: maturita-macos-swift
|
|
||||||
path: dist/maturita-macos-swift-*.zip
|
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
name: Publish to builds branch
|
name: Publish to builds branch
|
||||||
needs: [linux, windows, macos, android, ios, macos-swift]
|
needs: [linux, windows, macos, android, ios]
|
||||||
# One broken platform must not freeze the others: publish whatever was
|
# One broken platform must not freeze the others: publish whatever was
|
||||||
# built and keep the previous package for anything that failed.
|
# built and keep the previous package for anything that failed.
|
||||||
if: ${{ !cancelled() && contains(needs.*.result, 'success') }}
|
if: ${{ !cancelled() && contains(needs.*.result, 'success') }}
|
||||||
|
|
@ -296,6 +240,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
cp -f fresh/* staged/ 2>/dev/null || true
|
cp -f fresh/* staged/ 2>/dev/null || true
|
||||||
|
rm -f staged/maturita-macos-swift-*.zip staged/VERSION-macos-swift
|
||||||
chmod +x installers/pack-installer-zips.sh
|
chmod +x installers/pack-installer-zips.sh
|
||||||
./installers/pack-installer-zips.sh
|
./installers/pack-installer-zips.sh
|
||||||
cp installers/maturita-installer-linux.sh staged/
|
cp installers/maturita-installer-linux.sh staged/
|
||||||
|
|
@ -307,7 +252,7 @@ jobs:
|
||||||
# here, so a platform that failed this run is not offered as new.
|
# here, so a platform that failed this run is not offered as new.
|
||||||
[ -f fresh/maturita-android.apk ] && echo "${{ github.sha }}" > staged/VERSION-android
|
[ -f fresh/maturita-android.apk ] && echo "${{ github.sha }}" > staged/VERSION-android
|
||||||
[ -f fresh/maturita-ios.ipa ] && echo "${{ github.sha }}" > staged/VERSION-ios
|
[ -f fresh/maturita-ios.ipa ] && echo "${{ github.sha }}" > staged/VERSION-ios
|
||||||
ls fresh/maturita-macos-swift-*.zip >/dev/null 2>&1 && echo "${{ github.sha }}" > staged/VERSION-macos-swift
|
ls fresh/maturita-macos-*.zip >/dev/null 2>&1 && echo "${{ github.sha }}" > staged/VERSION-macos
|
||||||
ls fresh staged
|
ls fresh staged
|
||||||
|
|
||||||
# The branch is rewritten from scratch every time rather than added to,
|
# The branch is rewritten from scratch every time rather than added to,
|
||||||
|
|
|
||||||
4
Makefile
4
Makefile
|
|
@ -99,8 +99,8 @@ bundle: $(TARGET)
|
||||||
@echo "Bundled into dist/ - run dist/$(TARGET)"
|
@echo "Bundled into dist/ - run dist/$(TARGET)"
|
||||||
else ifeq ($(UNAME_S),Darwin)
|
else ifeq ($(UNAME_S),Darwin)
|
||||||
bundle:
|
bundle:
|
||||||
@chmod +x scripts/bundle-macos.sh
|
@chmod +x scripts/bundle-macos-swift.sh
|
||||||
@./scripts/bundle-macos.sh
|
@./scripts/bundle-macos-swift.sh
|
||||||
else
|
else
|
||||||
bundle:
|
bundle:
|
||||||
@chmod +x scripts/bundle-linux.sh
|
@chmod +x scripts/bundle-linux.sh
|
||||||
|
|
|
||||||
89
README.md
89
README.md
|
|
@ -1,6 +1,6 @@
|
||||||
# maturita.c
|
# maturita.c
|
||||||
|
|
||||||
Vzdělávací GTK 4 aplikace k přípravě na maturitu. / An educational GTK 4 app for the Czech maturita exam.
|
Vzdělávací aplikace k přípravě na maturitu. / An educational app for the Czech maturita exam.
|
||||||
|
|
||||||
**[Česky](#česky)** · **[English](#english)**
|
**[Česky](#česky)** · **[English](#english)**
|
||||||
|
|
||||||
|
|
@ -53,10 +53,9 @@ spolehlivější instalátor výše, nebo stažení přímo z té stránky větv
|
||||||
- [maturita-linux-x86_64.AppImage](https://github.com/pepaskopec-blip/maturita.c/raw/builds/maturita-linux-x86_64.AppImage)
|
- [maturita-linux-x86_64.AppImage](https://github.com/pepaskopec-blip/maturita.c/raw/builds/maturita-linux-x86_64.AppImage)
|
||||||
- [maturita-android.apk](https://github.com/pepaskopec-blip/maturita.c/raw/refs/heads/builds/maturita-android.apk)
|
- [maturita-android.apk](https://github.com/pepaskopec-blip/maturita.c/raw/refs/heads/builds/maturita-android.apk)
|
||||||
- [maturita-ios.ipa](https://github.com/pepaskopec-blip/maturita.c/raw/refs/heads/builds/maturita-ios.ipa)
|
- [maturita-ios.ipa](https://github.com/pepaskopec-blip/maturita.c/raw/refs/heads/builds/maturita-ios.ipa)
|
||||||
- [maturita-macos-swift-arm64.zip](https://github.com/pepaskopec-blip/maturita.c/raw/builds/maturita-macos-swift-arm64.zip)
|
|
||||||
|
|
||||||
Postup a nastavení se ukládají vedle `.app` / složky / AppImage do `progress/`.
|
Postup a nastavení se ukládají vedle `.app` / složky / AppImage do `progress/`.
|
||||||
Na Androidu a iOS do úložiště aplikace (stejné statistiky, témata a jazyk).
|
Na Androidu, iOS a macOS do úložiště aplikace (stejné statistiky, témata a jazyk).
|
||||||
|
|
||||||
Intel Mac, jiná architektura nebo úpravy kódu → [sestavení ze zdroje](#sestavení-ze-zdroje).
|
Intel Mac, jiná architektura nebo úpravy kódu → [sestavení ze zdroje](#sestavení-ze-zdroje).
|
||||||
|
|
||||||
|
|
@ -81,9 +80,12 @@ Intel Mac, jiná architektura nebo úpravy kódu → [sestavení ze zdroje](#ses
|
||||||
|
|
||||||
### Sestavení ze zdroje
|
### Sestavení ze zdroje
|
||||||
|
|
||||||
|
macOS je nativní SwiftUI aplikace (Xcode 26, macOS 26+). Linux a Windows
|
||||||
|
zůstávají GTK 4.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# macOS
|
# macOS
|
||||||
brew install gtk4 gcc make pkg-config
|
# Xcode 26 z App Storu; žádné GTK
|
||||||
|
|
||||||
# Debian / Ubuntu
|
# Debian / Ubuntu
|
||||||
sudo apt install build-essential pkg-config libgtk-4-dev
|
sudo apt install build-essential pkg-config libgtk-4-dev
|
||||||
|
|
@ -98,17 +100,28 @@ sudo pacman -S base-devel pkgconf gtk4
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/pepaskopec-blip/maturita.c.git
|
git clone https://github.com/pepaskopec-blip/maturita.c.git
|
||||||
cd maturita.c
|
cd maturita.c
|
||||||
make
|
|
||||||
make run # nebo ./maturita
|
|
||||||
make bundle # AppImage / .app zip / Windows složka s DLL
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Volitelně CMake: `cmake -S . -B build && cmake --build build`.
|
Na Macu:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
open Maturita.xcworkspace # scheme Maturita z macos/Maturita.xcodeproj
|
||||||
|
# nebo: make bundle # zip v dist/maturita-macos-arm64.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
Na Linuxu a Windows:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make
|
||||||
|
make run # nebo ./maturita
|
||||||
|
make bundle # AppImage / Windows složka s DLL
|
||||||
|
```
|
||||||
|
|
||||||
|
Volitelně CMake (GTK): `cmake -S . -B build && cmake --build build`.
|
||||||
|
|
||||||
Na macOS `make bundle` automaticky vloží aktuální Git commit, aby fungovala
|
Na macOS `make bundle` automaticky vloží aktuální Git commit, aby fungovala
|
||||||
kontrola aktualizací. Při balení ze zdrojů bez `.git` zadejte
|
kontrola aktualizací. Při balení ze zdrojů bez `.git` zadejte
|
||||||
`COMMIT=<zdrojový-commit> make bundle`. Samotné `make` nadále vytváří vývojový
|
`COMMIT=<zdrojový-commit> make bundle`.
|
||||||
build bez aktualizací.
|
|
||||||
|
|
||||||
Aby stažená aplikace na macOS šla otevřít bez potvrzení v Nastavení, CI
|
Aby stažená aplikace na macOS šla otevřít bez potvrzení v Nastavení, CI
|
||||||
potřebuje **Apple Developer Program** a GitHub Secrets:
|
potřebuje **Apple Developer Program** a GitHub Secrets:
|
||||||
|
|
@ -139,7 +152,7 @@ make bundle # dist/maturita.exe jde spustit i z Průzkumník
|
||||||
### Struktura
|
### Struktura
|
||||||
|
|
||||||
```
|
```
|
||||||
src/ C zdroje a maturita.h
|
src/ C zdroje (GTK přehrávač pro Linux a Windows)
|
||||||
android/ Jetpack Compose přehrávač (stejný obsah)
|
android/ Jetpack Compose přehrávač (stejný obsah)
|
||||||
ios/ SwiftUI přehrávač (stejný obsah)
|
ios/ SwiftUI přehrávač (stejný obsah)
|
||||||
macos/ nativní SwiftUI aplikace pro Mac (stejný obsah)
|
macos/ nativní SwiftUI aplikace pro Mac (stejný obsah)
|
||||||
|
|
@ -177,17 +190,9 @@ open Maturita.xcworkspace # v kořeni repozitáře
|
||||||
# nebo: make ios # unsigned IPA v dist-ios/maturita-ios.ipa
|
# nebo: make ios # unsigned IPA v dist-ios/maturita-ios.ipa
|
||||||
```
|
```
|
||||||
|
|
||||||
Nativní macOS ze zdroje (Xcode 26, macOS 26+):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
open Maturita.xcworkspace # scheme Maturita z macos/Maturita.xcodeproj
|
|
||||||
# nebo: make macos # zip v dist/maturita-macos-swift-arm64.zip
|
|
||||||
```
|
|
||||||
|
|
||||||
Workspace odkazuje na `ios/Maturita.xcodeproj` a `macos/Maturita.xcodeproj`.
|
Workspace odkazuje na `ios/Maturita.xcodeproj` a `macos/Maturita.xcodeproj`.
|
||||||
Projekty nepřesouvejte – build čte `../src` a `../scripts` a v kořeni by
|
Projekty nepřesouvejte – build čte `../src` a `../scripts` a v kořeni by
|
||||||
`Maturita/` kolidovalo se zkompilovaným binárem `maturita`. Nativní Mac
|
`Maturita/` kolidovalo se zkompilovaným binárem `maturita`.
|
||||||
aplikace sdílí cvičení s iOS přehrávačem; GTK verze (`make bundle`) zůstává.
|
|
||||||
|
|
||||||
Licence: [GPL-3.0](LICENSE).
|
Licence: [GPL-3.0](LICENSE).
|
||||||
|
|
||||||
|
|
@ -240,10 +245,9 @@ above, or download from that branch page:
|
||||||
- [maturita-linux-x86_64.AppImage](https://github.com/pepaskopec-blip/maturita.c/raw/builds/maturita-linux-x86_64.AppImage)
|
- [maturita-linux-x86_64.AppImage](https://github.com/pepaskopec-blip/maturita.c/raw/builds/maturita-linux-x86_64.AppImage)
|
||||||
- [maturita-android.apk](https://github.com/pepaskopec-blip/maturita.c/raw/refs/heads/builds/maturita-android.apk)
|
- [maturita-android.apk](https://github.com/pepaskopec-blip/maturita.c/raw/refs/heads/builds/maturita-android.apk)
|
||||||
- [maturita-ios.ipa](https://github.com/pepaskopec-blip/maturita.c/raw/refs/heads/builds/maturita-ios.ipa)
|
- [maturita-ios.ipa](https://github.com/pepaskopec-blip/maturita.c/raw/refs/heads/builds/maturita-ios.ipa)
|
||||||
- [maturita-macos-swift-arm64.zip](https://github.com/pepaskopec-blip/maturita.c/raw/builds/maturita-macos-swift-arm64.zip)
|
|
||||||
|
|
||||||
Progress and settings live in `progress/` next to the `.app` / folder /
|
Progress and settings live in `progress/` next to the `.app` / folder /
|
||||||
AppImage. On Android and iOS they stay in app storage (same stats, themes, language).
|
AppImage. On Android, iOS and macOS they stay in app storage (same stats, themes, language).
|
||||||
|
|
||||||
Intel Mac, another architecture, or hacking on the code →
|
Intel Mac, another architecture, or hacking on the code →
|
||||||
[build from source](#build-from-source).
|
[build from source](#build-from-source).
|
||||||
|
|
@ -269,9 +273,12 @@ Intel Mac, another architecture, or hacking on the code →
|
||||||
|
|
||||||
### Build from source
|
### Build from source
|
||||||
|
|
||||||
|
macOS is a native SwiftUI app (Xcode 26, macOS 26+). Linux and Windows
|
||||||
|
stay on GTK 4.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# macOS
|
# macOS
|
||||||
brew install gtk4 gcc make pkg-config
|
# Xcode 26 from the App Store; no GTK
|
||||||
|
|
||||||
# Debian / Ubuntu
|
# Debian / Ubuntu
|
||||||
sudo apt install build-essential pkg-config libgtk-4-dev
|
sudo apt install build-essential pkg-config libgtk-4-dev
|
||||||
|
|
@ -286,17 +293,28 @@ sudo pacman -S base-devel pkgconf gtk4
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/pepaskopec-blip/maturita.c.git
|
git clone https://github.com/pepaskopec-blip/maturita.c.git
|
||||||
cd maturita.c
|
cd maturita.c
|
||||||
make
|
|
||||||
make run # or ./maturita
|
|
||||||
make bundle # AppImage / .app zip / Windows DLL folder
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Optional CMake: `cmake -S . -B build && cmake --build build`.
|
On a Mac:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
open Maturita.xcworkspace # scheme Maturita from macos/Maturita.xcodeproj
|
||||||
|
# or: make bundle # zip at dist/maturita-macos-arm64.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
On Linux and Windows:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make
|
||||||
|
make run # or ./maturita
|
||||||
|
make bundle # AppImage / Windows DLL folder
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional CMake (GTK): `cmake -S . -B build && cmake --build build`.
|
||||||
|
|
||||||
On macOS, `make bundle` embeds the current Git commit so the installed app
|
On macOS, `make bundle` embeds the current Git commit so the installed app
|
||||||
can check for updates. When packaging a source export without `.git`, use
|
can check for updates. When packaging a source export without `.git`, use
|
||||||
`COMMIT=<source-commit> make bundle`. Plain `make` still produces a development
|
`COMMIT=<source-commit> make bundle`.
|
||||||
build without updates.
|
|
||||||
|
|
||||||
Skipping Gatekeeper on a downloaded macOS build needs an
|
Skipping Gatekeeper on a downloaded macOS build needs an
|
||||||
**Apple Developer Program** membership and these GitHub Secrets:
|
**Apple Developer Program** membership and these GitHub Secrets:
|
||||||
|
|
@ -327,7 +345,7 @@ make bundle # dist/maturita.exe can be double-clicked
|
||||||
### Layout
|
### Layout
|
||||||
|
|
||||||
```
|
```
|
||||||
src/ C sources and maturita.h
|
src/ C sources (GTK player for Linux and Windows)
|
||||||
android/ Jetpack Compose player (same content)
|
android/ Jetpack Compose player (same content)
|
||||||
ios/ SwiftUI player (same content)
|
ios/ SwiftUI player (same content)
|
||||||
macos/ native SwiftUI Mac app (same content)
|
macos/ native SwiftUI Mac app (same content)
|
||||||
|
|
@ -365,17 +383,8 @@ open Maturita.xcworkspace # at the repo root
|
||||||
# or: make ios # unsigned IPA at dist-ios/maturita-ios.ipa
|
# or: make ios # unsigned IPA at dist-ios/maturita-ios.ipa
|
||||||
```
|
```
|
||||||
|
|
||||||
Native macOS from source (Xcode 26, macOS 26+):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
open Maturita.xcworkspace # scheme Maturita from macos/Maturita.xcodeproj
|
|
||||||
# or: make macos # zip at dist/maturita-macos-swift-arm64.zip
|
|
||||||
```
|
|
||||||
|
|
||||||
The workspace points at `ios/Maturita.xcodeproj` and `macos/Maturita.xcodeproj`.
|
The workspace points at `ios/Maturita.xcodeproj` and `macos/Maturita.xcodeproj`.
|
||||||
Do not move the projects – the build reads `../src` and `../scripts`, and at
|
Do not move the projects – the build reads `../src` and `../scripts`, and at
|
||||||
the root `Maturita/` would collide with the compiled `maturita` binary. The
|
the root `Maturita/` would collide with the compiled `maturita` binary.
|
||||||
native Mac app shares exercises with the iOS player; the GTK build
|
|
||||||
(`make bundle`) stays.
|
|
||||||
|
|
||||||
License: [GPL-3.0](LICENSE).
|
License: [GPL-3.0](LICENSE).
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ final class Updater {
|
||||||
let atom = try self.get("https://github.com/\(self.repo)/commits/\(self.branch).atom")
|
let atom = try self.get("https://github.com/\(self.repo)/commits/\(self.branch).atom")
|
||||||
let remote: String
|
let remote: String
|
||||||
if let tip = self.atomTip(atom),
|
if let tip = self.atomTip(atom),
|
||||||
let marker = try? self.get("https://raw.githubusercontent.com/\(self.repo)/\(tip)/VERSION-macos-swift")
|
let marker = try? self.get("https://raw.githubusercontent.com/\(self.repo)/\(tip)/VERSION-macos")
|
||||||
.trimmingCharacters(in: .whitespacesAndNewlines),
|
.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||||
marker.count >= 7 {
|
marker.count >= 7 {
|
||||||
remote = marker
|
remote = marker
|
||||||
|
|
@ -90,9 +90,9 @@ final class Updater {
|
||||||
|
|
||||||
private static func assetName() -> String {
|
private static func assetName() -> String {
|
||||||
#if arch(arm64)
|
#if arch(arm64)
|
||||||
return "maturita-macos-swift-arm64.zip"
|
return "maturita-macos-arm64.zip"
|
||||||
#else
|
#else
|
||||||
return "maturita-macos-swift-x86_64.zip"
|
return "maturita-macos-x86_64.zip"
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ case "$arch" in
|
||||||
esac
|
esac
|
||||||
|
|
||||||
dd="$root/macos/DerivedData"
|
dd="$root/macos/DerivedData"
|
||||||
out_zip="maturita-macos-swift-${arch_tag}.zip"
|
out_zip="maturita-macos-${arch_tag}.zip"
|
||||||
mkdir -p "$dd" "$root/dist"
|
mkdir -p "$dd" "$root/dist"
|
||||||
|
|
||||||
xcodebuild \
|
xcodebuild \
|
||||||
|
|
|
||||||
|
|
@ -1,407 +1,5 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Build a self-contained macOS .app with GTK dylibs relocated into the bundle.
|
# Official macOS package is the native Swift app.
|
||||||
# Requires: make, pkg-config, GTK4 (Homebrew).
|
# The GTK bundler used to live here; Linux/Windows still use GTK.
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
exec "$(cd "$(dirname "$0")" && pwd)/bundle-macos-swift.sh" "$@"
|
||||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
||||||
cd "$ROOT"
|
|
||||||
|
|
||||||
# An installed .app needs a source identity for the updater, including when
|
|
||||||
# packaged locally. CI can still supply COMMIT explicitly. Plain `make`
|
|
||||||
# builds keep their "dev" default.
|
|
||||||
if [[ -z "${COMMIT:-}" ]]; then
|
|
||||||
if [[ ! -e "$ROOT/.git" ]] ||
|
|
||||||
! COMMIT="$(git -C "$ROOT" rev-parse --verify HEAD 2>/dev/null)"; then
|
|
||||||
echo "error: cannot determine the source commit; set COMMIT when bundling without Git metadata" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
export COMMIT
|
|
||||||
|
|
||||||
ARCH="$(uname -m)"
|
|
||||||
case "$ARCH" in
|
|
||||||
arm64|aarch64) ARCH_TAG=arm64 ;;
|
|
||||||
x86_64) ARCH_TAG=x86_64 ;;
|
|
||||||
*)
|
|
||||||
echo "Unsupported architecture: $ARCH" >&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
APP_NAME="Maturita.app"
|
|
||||||
APP="$ROOT/dist/$APP_NAME"
|
|
||||||
MACOS="$APP/Contents/MacOS"
|
|
||||||
RES="$APP/Contents/Resources"
|
|
||||||
FW="$APP/Contents/Frameworks"
|
|
||||||
ICON_512="$ROOT/data/share/icons/hicolor/512x512/apps/maturita.png"
|
|
||||||
OUT_ZIP="maturita-macos-${ARCH_TAG}.zip"
|
|
||||||
BREW_PREFIX="$(brew --prefix 2>/dev/null || echo /opt/homebrew)"
|
|
||||||
|
|
||||||
echo "==> Building maturita"
|
|
||||||
make -C "$ROOT" clean
|
|
||||||
make -C "$ROOT"
|
|
||||||
|
|
||||||
echo "==> Creating $APP_NAME"
|
|
||||||
rm -rf "$APP"
|
|
||||||
mkdir -p "$MACOS" "$RES/icons/hicolor/512x512/apps" "$RES/share" "$FW"
|
|
||||||
|
|
||||||
cp -f "$ROOT/maturita" "$MACOS/maturita-bin"
|
|
||||||
cp -f "$ROOT/data/style.css" "$RES/style.css"
|
|
||||||
cp -f "$ROOT/data/changelog.txt" "$RES/changelog.txt"
|
|
||||||
cp -f "$ICON_512" "$RES/icons/hicolor/512x512/apps/maturita.png"
|
|
||||||
cp -R "$ROOT/data/share/." "$RES/share/"
|
|
||||||
|
|
||||||
# Resolve an absolute Homebrew-style dependency to a real file on disk.
|
|
||||||
resolve_lib() {
|
|
||||||
local dep="$1"
|
|
||||||
local candidate
|
|
||||||
|
|
||||||
if [[ -f "$dep" ]]; then
|
|
||||||
realpath "$dep"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Try common Homebrew layouts for the basename.
|
|
||||||
local base
|
|
||||||
base="$(basename "$dep")"
|
|
||||||
for candidate in \
|
|
||||||
"$BREW_PREFIX/lib/$base" \
|
|
||||||
"$BREW_PREFIX/opt"/*/lib/"$base" \
|
|
||||||
"$BREW_PREFIX/Cellar"/*/*/lib/"$base"; do
|
|
||||||
if [[ -f "$candidate" ]]; then
|
|
||||||
realpath "$candidate"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Recursively copy non-system dylibs into Frameworks and rewrite load paths.
|
|
||||||
# Unlike dylibbundler, this never prompts interactively and fails loudly.
|
|
||||||
bundle_deps() {
|
|
||||||
local target="$1"
|
|
||||||
local line dep name dest real
|
|
||||||
|
|
||||||
while IFS= read -r line; do
|
|
||||||
dep="$(echo "$line" | awk '{print $1}')"
|
|
||||||
case "$dep" in
|
|
||||||
/System/*|/usr/lib/*|@executable_path/*|@loader_path/*)
|
|
||||||
continue
|
|
||||||
;;
|
|
||||||
@rpath/*)
|
|
||||||
name="$(basename "$dep")"
|
|
||||||
if ! real="$(resolve_lib "$BREW_PREFIX/lib/$name")"; then
|
|
||||||
echo "error: cannot resolve @rpath dependency '$dep' needed by $target" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
/*)
|
|
||||||
name="$(basename "$dep")"
|
|
||||||
case "$name" in
|
|
||||||
libjpeg*.dylib) name="libjpeg.8.dylib" ;;
|
|
||||||
esac
|
|
||||||
if [[ -f "$dep" ]]; then
|
|
||||||
real="$(realpath "$dep")"
|
|
||||||
elif ! real="$(resolve_lib "$dep")"; then
|
|
||||||
echo "error: cannot resolve dependency '$dep' needed by $target" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
continue
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
dest="$FW/$name"
|
|
||||||
|
|
||||||
if [[ ! -f "$dest" ]]; then
|
|
||||||
echo " + $name (from $real)"
|
|
||||||
cp -f "$real" "$dest"
|
|
||||||
chmod u+w "$dest"
|
|
||||||
install_name_tool -id "@executable_path/../Frameworks/$name" "$dest" 2>/dev/null || true
|
|
||||||
codesign --force --sign - "$dest" >/dev/null 2>&1 || true
|
|
||||||
bundle_deps "$dest"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Rewrite matching absolute / @rpath references on the target.
|
|
||||||
while IFS= read -r changed; do
|
|
||||||
changed="$(echo "$changed" | awk '{print $1}')"
|
|
||||||
case "$changed" in
|
|
||||||
/*|@rpath/*)
|
|
||||||
if [[ "$(basename "$changed")" == "$name" ]] || \
|
|
||||||
[[ "$(basename "$changed")" == "$(basename "$real")" ]] || \
|
|
||||||
{ [[ "$name" == "libjpeg.8.dylib" ]] && [[ "$(basename "$changed")" == libjpeg* ]]; }; then
|
|
||||||
install_name_tool -change "$changed" \
|
|
||||||
"@executable_path/../Frameworks/$name" "$target" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done < <(otool -L "$target" | tail -n +2)
|
|
||||||
done < <(otool -L "$target" | tail -n +2)
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "==> Bundling GTK / GLib dylibs into Frameworks"
|
|
||||||
bundle_deps "$MACOS/maturita-bin"
|
|
||||||
codesign --force --sign - "$MACOS/maturita-bin" >/dev/null 2>&1 || true
|
|
||||||
|
|
||||||
echo "==> Verifying Frameworks completeness"
|
|
||||||
MISSING=0
|
|
||||||
check_file_deps() {
|
|
||||||
local file="$1"
|
|
||||||
local line dep name
|
|
||||||
while IFS= read -r line; do
|
|
||||||
dep="$(echo "$line" | awk '{print $1}')"
|
|
||||||
case "$dep" in
|
|
||||||
@executable_path/../Frameworks/*)
|
|
||||||
name="$(basename "$dep")"
|
|
||||||
if [[ ! -f "$FW/$name" ]]; then
|
|
||||||
echo "MISSING: $name (required by $(basename "$file"))" >&2
|
|
||||||
MISSING=1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
@rpath/*)
|
|
||||||
echo "UNRESOLVED @rpath: $dep (in $(basename "$file"))" >&2
|
|
||||||
MISSING=1
|
|
||||||
;;
|
|
||||||
/opt/homebrew/*|/usr/local/*)
|
|
||||||
echo "UNBUNDLED absolute dep still present: $dep (in $(basename "$file"))" >&2
|
|
||||||
MISSING=1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done < <(otool -L "$file" | tail -n +2)
|
|
||||||
}
|
|
||||||
check_file_deps "$MACOS/maturita-bin"
|
|
||||||
for f in "$FW"/*.dylib; do
|
|
||||||
check_file_deps "$f"
|
|
||||||
done
|
|
||||||
if [[ "$MISSING" -ne 0 ]]; then
|
|
||||||
echo "error: macOS bundle is incomplete" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo " Frameworks: $(ls "$FW" | wc -l | tr -d ' ') dylibs"
|
|
||||||
|
|
||||||
# Copy GLib / GTK runtime data (schemas, modules, icon themes).
|
|
||||||
copy_pkg_share() {
|
|
||||||
local prefix
|
|
||||||
prefix="$(pkg-config --variable=prefix "$1" 2>/dev/null || true)"
|
|
||||||
[[ -n "$prefix" && -d "$prefix/share" ]] || return 0
|
|
||||||
mkdir -p "$RES/share"
|
|
||||||
for sub in glib-2.0 gtk-4.0 icons themes locale; do
|
|
||||||
if [[ -d "$prefix/share/$sub" ]]; then
|
|
||||||
rsync -a "$prefix/share/$sub" "$RES/share/" 2>/dev/null \
|
|
||||||
|| cp -R "$prefix/share/$sub" "$RES/share/"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
copy_pkg_share gtk4
|
|
||||||
copy_pkg_share glib-2.0
|
|
||||||
|
|
||||||
if [[ -d "$RES/share/glib-2.0/schemas" ]] && command -v glib-compile-schemas >/dev/null 2>&1; then
|
|
||||||
glib-compile-schemas "$RES/share/glib-2.0/schemas" || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Launcher sets GTK search paths then execs the real binary.
|
|
||||||
cat > "$MACOS/maturita" <<'EOF'
|
|
||||||
#!/bin/bash
|
|
||||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
||||||
export DYLD_LIBRARY_PATH="$ROOT/Frameworks${DYLD_LIBRARY_PATH:+:$DYLD_LIBRARY_PATH}"
|
|
||||||
export XDG_DATA_DIRS="$ROOT/Resources/share${XDG_DATA_DIRS:+:$XDG_DATA_DIRS}"
|
|
||||||
export GSETTINGS_SCHEMA_DIR="$ROOT/Resources/share/glib-2.0/schemas"
|
|
||||||
export GTK_DATA_PREFIX="$ROOT/Resources"
|
|
||||||
export GTK_EXE_PREFIX="$ROOT"
|
|
||||||
export XDG_DATA_HOME="$ROOT/Resources"
|
|
||||||
exec "$ROOT/MacOS/maturita-bin" "$@"
|
|
||||||
EOF
|
|
||||||
chmod +x "$MACOS/maturita"
|
|
||||||
|
|
||||||
cat > "$APP/Contents/Info.plist" <<EOF
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
|
||||||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>maturita.c</string>
|
|
||||||
<key>CFBundleDisplayName</key>
|
|
||||||
<string>maturita.c</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>org.maturita.Maturita</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>${VERSION:-1.0}</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>${VERSION:-1.0}</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>maturita</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>APPL</string>
|
|
||||||
<key>CFBundleIconFile</key>
|
|
||||||
<string>AppIcon</string>
|
|
||||||
<key>LSMinimumSystemVersion</key>
|
|
||||||
<string>12.0</string>
|
|
||||||
<key>NSHighResolutionCapable</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
EOF
|
|
||||||
|
|
||||||
if command -v sips >/dev/null 2>&1 && command -v iconutil >/dev/null 2>&1; then
|
|
||||||
ICONSET="$RES/AppIcon.iconset"
|
|
||||||
mkdir -p "$ICONSET"
|
|
||||||
for sz in 16 32 64 128 256 512; do
|
|
||||||
sips -z "$sz" "$sz" "$ICON_512" --out "$ICONSET/icon_${sz}x${sz}.png" >/dev/null
|
|
||||||
sips -z $((sz * 2)) $((sz * 2)) "$ICON_512" \
|
|
||||||
--out "$ICONSET/icon_${sz}x${sz}@2x.png" >/dev/null
|
|
||||||
done
|
|
||||||
iconutil -c icns -o "$RES/AppIcon.icns" "$ICONSET" && rm -rf "$ICONSET" || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Developer ID + notarization is what lets a downloaded app open without
|
|
||||||
# System Settings. Without a certificate the bundle stays ad-hoc signed, which
|
|
||||||
# is enough for local runs but Gatekeeper still blocks internet downloads.
|
|
||||||
ENTITLEMENTS="$ROOT/data/macos/Maturita.entitlements"
|
|
||||||
|
|
||||||
pick_sign_identity() {
|
|
||||||
if [[ -n "${MACOS_SIGN_IDENTITY:-}" ]]; then
|
|
||||||
printf '%s\n' "$MACOS_SIGN_IDENTITY"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
local found
|
|
||||||
found="$(security find-identity -v -p codesigning 2>/dev/null \
|
|
||||||
| awk -F'"' '/Developer ID Application/ { print $2; exit }')"
|
|
||||||
if [[ -n "$found" ]]; then
|
|
||||||
printf '%s\n' "$found"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
printf '%s\n' "-"
|
|
||||||
}
|
|
||||||
|
|
||||||
sign_one() {
|
|
||||||
local path="$1"
|
|
||||||
local use_entitlements="${2:-}"
|
|
||||||
local args=(--force --sign "$IDENTITY")
|
|
||||||
|
|
||||||
if [[ "$IDENTITY" != "-" ]]; then
|
|
||||||
args+=(--options runtime --timestamp)
|
|
||||||
fi
|
|
||||||
if [[ "$use_entitlements" == "entitlements" && -f "$ENTITLEMENTS" ]]; then
|
|
||||||
args+=(--entitlements "$ENTITLEMENTS")
|
|
||||||
fi
|
|
||||||
codesign "${args[@]}" "$path"
|
|
||||||
}
|
|
||||||
|
|
||||||
sign_bundle() {
|
|
||||||
local f
|
|
||||||
|
|
||||||
echo "==> Signing $APP_NAME"
|
|
||||||
IDENTITY="$(pick_sign_identity)"
|
|
||||||
if [[ "$IDENTITY" == "-" ]]; then
|
|
||||||
echo " identity: ad-hoc (downloaded copies will still need Gatekeeper approval)"
|
|
||||||
else
|
|
||||||
echo " identity: $IDENTITY"
|
|
||||||
fi
|
|
||||||
|
|
||||||
shopt -s nullglob
|
|
||||||
for f in "$FW"/*.dylib; do
|
|
||||||
sign_one "$f"
|
|
||||||
done
|
|
||||||
shopt -u nullglob
|
|
||||||
|
|
||||||
sign_one "$MACOS/maturita-bin" entitlements
|
|
||||||
sign_one "$MACOS/maturita" entitlements
|
|
||||||
sign_one "$APP" entitlements
|
|
||||||
|
|
||||||
codesign --verify --strict --verbose=2 "$APP"
|
|
||||||
}
|
|
||||||
|
|
||||||
can_notarize() {
|
|
||||||
[[ "$IDENTITY" != "-" ]] || return 1
|
|
||||||
if [[ -n "${APPLE_API_KEY:-}" && -n "${APPLE_API_KEY_ID:-}" &&
|
|
||||||
-n "${APPLE_API_ISSUER:-}" ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
if [[ -n "${APPLE_ID:-}" && -n "${APPLE_APP_SPECIFIC_PASSWORD:-}" &&
|
|
||||||
-n "${APPLE_TEAM_ID:-}" ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
notarize_bundle() {
|
|
||||||
local zip notary_args keyfile
|
|
||||||
|
|
||||||
if [[ "$IDENTITY" == "-" ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
if ! can_notarize; then
|
|
||||||
if [[ -n "${GITHUB_ACTIONS:-}${CI:-}" ]]; then
|
|
||||||
echo "error: Developer ID is present but notarization secrets are missing." >&2
|
|
||||||
echo "Set APPLE_API_KEY + APPLE_API_KEY_ID + APPLE_API_ISSUER, or" >&2
|
|
||||||
echo "APPLE_ID + APPLE_APP_SPECIFIC_PASSWORD + APPLE_TEAM_ID." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo " warning: signed, but not notarized — Gatekeeper will still block downloads"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "==> Notarizing $APP_NAME"
|
|
||||||
zip="$ROOT/dist/.notarize-$OUT_ZIP"
|
|
||||||
rm -f "$zip"
|
|
||||||
ditto -c -k --keepParent "$APP" "$zip"
|
|
||||||
|
|
||||||
notary_args=(submit "$zip" --wait)
|
|
||||||
keyfile=""
|
|
||||||
if [[ -n "${APPLE_API_KEY:-}" ]]; then
|
|
||||||
keyfile="$(mktemp)"
|
|
||||||
printf '%s\n' "$APPLE_API_KEY" > "$keyfile"
|
|
||||||
notary_args+=(--key "$keyfile" --key-id "$APPLE_API_KEY_ID"
|
|
||||||
--issuer "$APPLE_API_ISSUER")
|
|
||||||
else
|
|
||||||
notary_args+=(--apple-id "$APPLE_ID"
|
|
||||||
--password "$APPLE_APP_SPECIFIC_PASSWORD"
|
|
||||||
--team-id "$APPLE_TEAM_ID")
|
|
||||||
fi
|
|
||||||
|
|
||||||
xcrun notarytool "${notary_args[@]}"
|
|
||||||
[[ -n "$keyfile" ]] && rm -f "$keyfile"
|
|
||||||
rm -f "$zip"
|
|
||||||
xcrun stapler staple "$APP"
|
|
||||||
echo " stapled notarization ticket"
|
|
||||||
}
|
|
||||||
|
|
||||||
sign_bundle
|
|
||||||
|
|
||||||
echo "==> Smoke-testing launch"
|
|
||||||
if [[ -n "${GITHUB_ACTIONS:-}${CI:-}" ]]; then
|
|
||||||
echo " (CI: skipping GUI smoke test; Frameworks verification already passed)"
|
|
||||||
else
|
|
||||||
SMOKE_LOG="$(mktemp)"
|
|
||||||
"$MACOS/maturita" >"$SMOKE_LOG" 2>&1 &
|
|
||||||
SMOKE_PID=$!
|
|
||||||
sleep 3
|
|
||||||
if ! kill -0 "$SMOKE_PID" 2>/dev/null; then
|
|
||||||
if grep -q "Library not loaded" "$SMOKE_LOG"; then
|
|
||||||
echo "error: Maturita.app missing bundled libraries" >&2
|
|
||||||
cat "$SMOKE_LOG" >&2 || true
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "error: Maturita.app exited during smoke test" >&2
|
|
||||||
cat "$SMOKE_LOG" >&2 || true
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
kill "$SMOKE_PID" 2>/dev/null || true
|
|
||||||
wait "$SMOKE_PID" 2>/dev/null || true
|
|
||||||
rm -f "$SMOKE_LOG"
|
|
||||||
echo " smoke test OK (process stayed up)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
notarize_bundle
|
|
||||||
|
|
||||||
echo "==> Zipping"
|
|
||||||
rm -f "$ROOT/dist/$OUT_ZIP"
|
|
||||||
(
|
|
||||||
cd "$ROOT/dist"
|
|
||||||
zip -qry "$OUT_ZIP" "$APP_NAME"
|
|
||||||
)
|
|
||||||
|
|
||||||
echo "Created dist/$OUT_ZIP"
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Exercise build identity at the real bundler entry point without building GTK.
|
# Exercise build identity at the real bundler entry point without Xcode.
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
|
@ -12,28 +12,23 @@ fail() {
|
||||||
}
|
}
|
||||||
|
|
||||||
mkdir -p "$TMP/bin"
|
mkdir -p "$TMP/bin"
|
||||||
cat > "$TMP/bin/make" <<'EOF'
|
cat > "$TMP/bin/xcodebuild" <<'EOF'
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
printf '%s\n' "${COMMIT-UNSET}" >> "$MATURITA_TEST_MAKE_LOG"
|
printf '%s\n' "${COMMIT-UNSET}" >> "$MATURITA_TEST_MAKE_LOG"
|
||||||
if [[ "$*" == *" clean" ]]; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
# Stop before packaging, after observing the real build's environment.
|
# Stop before packaging, after observing the real build's environment.
|
||||||
exit 97
|
exit 97
|
||||||
EOF
|
EOF
|
||||||
cat > "$TMP/bin/brew" <<'EOF'
|
|
||||||
#!/bin/sh
|
|
||||||
echo /unused-homebrew
|
|
||||||
EOF
|
|
||||||
cat > "$TMP/bin/uname" <<'EOF'
|
cat > "$TMP/bin/uname" <<'EOF'
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
echo arm64
|
echo arm64
|
||||||
EOF
|
EOF
|
||||||
chmod +x "$TMP/bin/make" "$TMP/bin/brew" "$TMP/bin/uname"
|
chmod +x "$TMP/bin/xcodebuild" "$TMP/bin/uname"
|
||||||
|
|
||||||
new_fixture() {
|
new_fixture() {
|
||||||
mkdir -p "$1/scripts"
|
mkdir -p "$1/scripts"
|
||||||
cp "$ROOT/scripts/bundle-macos.sh" "$1/scripts/bundle-macos.sh"
|
cp "$ROOT/scripts/bundle-macos.sh" "$1/scripts/bundle-macos.sh"
|
||||||
|
cp "$ROOT/scripts/bundle-macos-swift.sh" "$1/scripts/bundle-macos-swift.sh"
|
||||||
|
chmod +x "$1/scripts/bundle-macos.sh" "$1/scripts/bundle-macos-swift.sh"
|
||||||
}
|
}
|
||||||
|
|
||||||
run_bundle() {
|
run_bundle() {
|
||||||
|
|
@ -64,15 +59,15 @@ expect_build_commit() {
|
||||||
cat "$OUTPUT_LOG" >&2
|
cat "$OUTPUT_LOG" >&2
|
||||||
fail "expected the stubbed build to run (status $STATUS)"
|
fail "expected the stubbed build to run (status $STATUS)"
|
||||||
}
|
}
|
||||||
[[ -f "$MAKE_LOG" ]] || fail "make was not called"
|
[[ -f "$MAKE_LOG" ]] || fail "xcodebuild was not called"
|
||||||
actual="$(cat "$MAKE_LOG")"
|
actual="$(cat "$MAKE_LOG")"
|
||||||
[[ "$actual" == "$expected"$'\n'"$expected" ]] ||
|
[[ "$actual" == "$expected" ]] ||
|
||||||
fail "clean/build did not both receive COMMIT=$expected (got '$actual')"
|
fail "build did not receive COMMIT=$expected (got '$actual')"
|
||||||
}
|
}
|
||||||
|
|
||||||
expect_missing_identity() {
|
expect_missing_identity() {
|
||||||
[[ "$STATUS" -ne 0 ]] || fail 'missing identity unexpectedly succeeded'
|
[[ "$STATUS" -ne 0 ]] || fail 'missing identity unexpectedly succeeded'
|
||||||
[[ ! -e "$MAKE_LOG" ]] || fail 'missing identity reached make'
|
[[ ! -e "$MAKE_LOG" ]] || fail 'missing identity reached xcodebuild'
|
||||||
grep -q 'COMMIT' "$OUTPUT_LOG" || fail 'missing identity has no COMMIT guidance'
|
grep -q 'COMMIT' "$OUTPUT_LOG" || fail 'missing identity has no COMMIT guidance'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue