ci: run Swift core tests on available Docker runner

This commit is contained in:
casona 2026-10-26 10:04:29 +03:00
parent 7325c10469
commit d4e033e97e
6 changed files with 149 additions and 51 deletions

73
.forgejo/workflows/ci.yml Normal file
View file

@ -0,0 +1,73 @@
name: macoc CI (Docker)
on:
push:
branches:
- main
tags:
- 'v*'
workflow_dispatch:
jobs:
swift-core:
# This is the ONLY currently available runner label.
runs-on: docker
# The runner may have a Node/Alpine default image. Override it with
# official Swift on Linux so we can run the real platform-neutral XCTest.
container:
image: swift:6.2-noble
steps:
- name: Check out commit without Node.js actions
env:
MACOC_SERVER_URL: "${{ forgejo.server_url }}"
MACOC_REPOSITORY: "${{ forgejo.repository }}"
MACOC_SHA: "${{ forgejo.sha }}"
MACOC_TOKEN: "${{ forgejo.token }}"
run: |
set -eu
test "$MACOC_REPOSITORY" = "catsona/macoc"
case "$MACOC_SHA" in
''|*[!0-9a-f]*) echo "Invalid Forgejo commit SHA" >&2; exit 1 ;;
esac
# Checkout actions depend on Node.js, which the official Swift
# image does not provide. Forgejo's own archive API returns an
# archive for the exact triggering commit (also for private repos).
workspace="${FORGEJO_WORKSPACE:-${GITHUB_WORKSPACE:-$PWD}}"
mkdir -p "$workspace"
archive="$(mktemp)"
config="$(mktemp)"
chmod 600 "$config"
trap 'rm -f "$archive" "$config"' EXIT
printf 'header = "Authorization: token %s"\n' "$MACOC_TOKEN" > "$config"
curl --config "$config" --fail --silent --show-error \
"$MACOC_SERVER_URL/api/v1/repos/$MACOC_REPOSITORY/archive/$MACOC_SHA.tar.gz" \
--output "$archive"
tar -xzf "$archive" --strip-components=1 -C "$workspace"
cd "$workspace"
# The API tarball has no .git directory. Create a temporary local
# index so git diff --cached --check still catches whitespace errors.
git init -q
git config --global --add safe.directory "$workspace"
git add -A
- name: Lint and run Linux SwiftCore XCTest
env:
MACOC_REQUIRE_SWIFT: '1'
run: |
swift --version
bash scripts/verify.sh
- name: Verify release tag matches Info.plist
if: startsWith(forgejo.ref, 'refs/tags/v')
env:
MACOC_TAG: "${{ forgejo.ref_name }}"
run: |
set -eu
version="$(sed -n '/<key>CFBundleShortVersionString<\/key>/{n;s/.*<string>\([^<]*\)<\/string>.*/\1/p;}' Resources/Info.plist)"
if [ -z "$version" ] || [ "$MACOC_TAG" != "v$version" ]; then
echo "Tag $MACOC_TAG does not match app version v$version" >&2
exit 1
fi
echo "Validated source tag $MACOC_TAG (no macOS binary built in Docker)."

View file

@ -1,37 +0,0 @@
name: macoc macOS
on:
push:
branches:
- main
tags:
- 'v*'
workflow_dispatch:
jobs:
build:
# A dedicated macOS Forgejo runner is required; register label macos:host.
# It must have Command Line Tools. There is no Linux/macOS cross-build.
runs-on: macos
steps:
- name: Checkout
uses: https://code.forgejo.org/actions/checkout@v4
with:
persist-credentials: false
- name: Run tests and static checks
run: bash scripts/verify.sh
- name: Build and package macOS app
run: bash scripts/package.sh
env:
MACOC_CODESIGN_IDENTITY: "${{ vars.MACOC_CODESIGN_IDENTITY }}"
- name: Publish Forgejo release on version tags
if: startsWith(forgejo.ref, 'refs/tags/v')
env:
FORGEJO_TOKEN: "${{ secrets.FORGEJO_TOKEN }}"
FORGEJO_SERVER_URL: "${{ forgejo.server_url }}"
FORGEJO_REPOSITORY: "${{ forgejo.repository }}"
FORGEJO_REF_NAME: "${{ forgejo.ref_name }}"
run: bash scripts/publish-release.sh

View file

@ -102,17 +102,23 @@ assigned its normal modifier role rather than No Action.
## Forgejo CI and releases
The workflow at .forgejo/workflows/macos.yml builds and tests on pushes to
main. A pushed tag such as v0.3.0 also creates a Forgejo release with a
macOS .app ZIP and SHA-256 checksum, through the built-in scoped token.
CI workflow: .forgejo/workflows/ci.yml. It uses the **existing Docker
runner** and the official Linux Swift image. Pushes to main run shell
validation and **actual XCTest for pure Swift core logic**. On version
tags it also checks the tag against the application version.
**A real Mac Forgejo runner labelled macos is required.** The Linux server
cannot build AppKit or IOKit. See [CI setup](docs/ci.md). Until a Mac runner
is registered and its job succeeds, no release build has been produced.
**Linux Docker cannot compile macOS .app binaries** because Apple AppKit,
IOKit and macOS SDK are unavailable there. Docker CI does not publish
nonexistent macOS binaries.
A release archive signed ad-hoc is intended for local evaluation.
To distribute broadly without Gatekeeper warnings, use Developer ID
signing and Apple's notarization service (not yet configured here).
For a release archive, check out the tag on a real Mac and run
bash scripts/package.sh (or task package), then execute
bash scripts/publish-release.sh with a repository-scoped Forgejo token.
See [CI and release instructions](docs/ci.md). Fully automated macOS
binary releases require a real Mac runner or another Mac build service.
Ad-hoc artifacts are suited to local evaluation; public distribution
requires Developer ID signing and Apple notarization.
## Diagnostics and recovery

View file

@ -18,8 +18,8 @@ grep -q 'TISSelectInputSource' Sources/CapswitchApp/InputSourceManager.swift
grep -q 'IOHIDServiceClientSetProperty' Sources/CapswitchHID/CapswitchRemapping.c
grep -q '/usr/bin/open' Sources/CapswitchApp/ApplicationRelauncher.swift
grep -q 'SMAppService.mainApp' Sources/CapswitchApp/LoginItemManager.swift
test -f .forgejo/workflows/macos.yml
for name in build bundle test install doctor clean lint verify icon sign package publish-release setup-signing; do
test -f .forgejo/workflows/ci.yml
for name in build bundle test test-linux-core install doctor clean lint verify icon sign package publish-release setup-signing; do
bash -n "scripts/$name.sh"
done
echo "lint: PASS"

47
scripts/test-linux-core.sh Executable file
View file

@ -0,0 +1,47 @@
#!/usr/bin/env bash
source "$(dirname "$0")/common.sh"
if [[ "$IS_MACOS" == 1 ]]; then
echo "Linux-only test fixture must not be used on macOS." >&2
exit 1
fi
if ! command -v swift >/dev/null 2>&1; then
echo "Swift toolchain is required for Linux core tests." >&2
exit 1
fi
# The normal Package.swift targets AppKit, Carbon and IOKit. None of those
# frameworks exist in Linux containers. Build only the pure Swift core and
# its real XCTest suite in a temporary, independent SwiftPM package.
temporary_package="$(mktemp -d)"
trap 'rm -rf "$temporary_package"' EXIT
mkdir -p \
"$temporary_package/Sources/CapswitchCore" \
"$temporary_package/Tests/CapswitchCoreTests"
cp "$ROOT"/Sources/Capswitch/*.swift \
"$temporary_package/Sources/CapswitchCore/"
cp "$ROOT"/Tests/CapswitchCoreTests/*.swift \
"$temporary_package/Tests/CapswitchCoreTests/"
cat > "$temporary_package/Package.swift" <<'SWIFT'
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "macoc-LinuxCoreTests",
products: [
.library(name: "CapswitchCore", targets: ["CapswitchCore"]),
],
targets: [
.target(name: "CapswitchCore"),
.testTarget(
name: "CapswitchCoreTests",
dependencies: ["CapswitchCore"]
),
]
)
SWIFT
swift test --package-path "$temporary_package" --jobs 2

View file

@ -1,5 +1,14 @@
#!/usr/bin/env bash
source "$(dirname "$0")/common.sh"
macos_only
if [[ "$IS_MACOS" == 1 ]]; then
cd "$ROOT"
swift test
elif command -v swift >/dev/null 2>&1; then
bash "$ROOT/scripts/test-linux-core.sh"
elif [[ "${MACOC_REQUIRE_SWIFT:-0}" == 1 ]]; then
echo "ERROR: the CI requires Swift for Linux XCTest." >&2
exit 1
else
echo "SKIP: Swift is not installed on this Linux development host."
fi