ci: run Swift core tests on available Docker runner
Some checks failed
macoc CI (Docker) / swift-core (push) Failing after 3s
Some checks failed
macoc CI (Docker) / swift-core (push) Failing after 3s
This commit is contained in:
parent
009c7e5a33
commit
1e50fae6c8
7 changed files with 206 additions and 90 deletions
73
.forgejo/workflows/ci.yml
Normal file
73
.forgejo/workflows/ci.yml
Normal 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)."
|
||||||
|
|
@ -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
|
|
||||||
24
README.md
24
README.md
|
|
@ -102,17 +102,23 @@ assigned its normal modifier role rather than No Action.
|
||||||
|
|
||||||
## Forgejo CI and releases
|
## Forgejo CI and releases
|
||||||
|
|
||||||
The workflow at .forgejo/workflows/macos.yml builds and tests on pushes to
|
CI workflow: .forgejo/workflows/ci.yml. It uses the **existing Docker
|
||||||
main. A pushed tag such as v0.3.0 also creates a Forgejo release with a
|
runner** and the official Linux Swift image. Pushes to main run shell
|
||||||
macOS .app ZIP and SHA-256 checksum, through the built-in scoped token.
|
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
|
**Linux Docker cannot compile macOS .app binaries** because Apple AppKit,
|
||||||
cannot build AppKit or IOKit. See [CI setup](docs/ci.md). Until a Mac runner
|
IOKit and macOS SDK are unavailable there. Docker CI does not publish
|
||||||
is registered and its job succeeds, no release build has been produced.
|
nonexistent macOS binaries.
|
||||||
|
|
||||||
A release archive signed ad-hoc is intended for local evaluation.
|
For a release archive, check out the tag on a real Mac and run
|
||||||
To distribute broadly without Gatekeeper warnings, use Developer ID
|
bash scripts/package.sh (or task package), then execute
|
||||||
signing and Apple's notarization service (not yet configured here).
|
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
|
## Diagnostics and recovery
|
||||||
|
|
||||||
|
|
|
||||||
96
docs/ci.md
96
docs/ci.md
|
|
@ -1,48 +1,66 @@
|
||||||
# Forgejo Actions CI and macOS releases
|
# Forgejo Actions CI: existing Docker runner
|
||||||
|
|
||||||
The workflow is in .forgejo/workflows/macos.yml.
|
The only registered Forgejo runner has the **docker** label. Workflow:
|
||||||
|
.forgejo/workflows/ci.yml. It uses runs-on: docker with the official
|
||||||
|
swift:6.2-noble Linux container image.
|
||||||
|
|
||||||
- Push to main: Swift tests, static checks, macOS app build and ZIP archive.
|
## CI checks
|
||||||
- Push a version tag like v0.3.0: same checks plus a Forgejo release with
|
|
||||||
macoc-vX.Y.Z-macos-<arch>.zip and a SHA-256 checksum file.
|
|
||||||
- Manual workflow run: test and build only.
|
|
||||||
|
|
||||||
Artifacts are for the runner's CPU architecture, not universal.
|
- Main branch / manual dispatch: obtain the exact triggering commit, validate
|
||||||
|
source and shell scripts, and run the **real** XCTest suite for the pure
|
||||||
|
Swift CapswitchCore module.
|
||||||
|
- Tags v*: same tests plus Info.plist-version/tag consistency checking.
|
||||||
|
The v0.3.0 tag must match CFBundleShortVersionString 0.3.0.
|
||||||
|
|
||||||
## One-time macOS runner setup
|
scripts/test-linux-core.sh creates a temporary SwiftPM package containing
|
||||||
|
the actual Sources/Capswitch source files and Tests/CapswitchCoreTests suite.
|
||||||
|
The standard Package.swift is macOS-specific and cannot be compiled on Linux.
|
||||||
|
|
||||||
1. Enable Actions under repository Settings -> Units.
|
## Why Docker CI does not produce a macOS binary
|
||||||
2. Register a dedicated runner on a real Mac with Command Line Tools, Swift,
|
|
||||||
iconutil, sips, codesign, ditto, curl, Git, Python 3 and Node for checkout.
|
|
||||||
3. Under Settings -> Actions -> Runners obtain the registration token.
|
|
||||||
4. Configure the runner label macos:host; workflow uses runs-on: macos.
|
|
||||||
5. Push a main-branch commit to check the workflow. Then push an exact version
|
|
||||||
tag matching Resources/Info.plist when the build is green.
|
|
||||||
|
|
||||||
Host runners have no isolation; allow only trusted contributors to edit
|
Linux Docker does not include the Apple macOS SDK, AppKit, Carbon or IOKit.
|
||||||
the workflow, protect release tags, and avoid running arbitrary PR code.
|
It cannot generate a signed functional macoc.app. This workflow does not run
|
||||||
|
scripts/package.sh on Linux and does not create a misleading binary release.
|
||||||
|
|
||||||
Official documentation:
|
For a release with the current infrastructure:
|
||||||
https://forgejo.org/docs/latest/user/actions/overview/
|
|
||||||
|
1. Push tag v0.3.0 (or the version in Info.plist); verify Docker CI passed.
|
||||||
|
2. On a real Mac with Command Line Tools, check out that precise tag.
|
||||||
|
3. Run bash scripts/package.sh (equivalent to task package). This creates
|
||||||
|
dist/macoc-v0.3.0-macos-<arch>.zip and the SHA-256 checksum file.
|
||||||
|
4. Set FORGEJO_SERVER_URL, FORGEJO_REPOSITORY=catsona/macoc,
|
||||||
|
FORGEJO_REF_NAME=v0.3.0, and FORGEJO_TOKEN with repository release write
|
||||||
|
access; then run bash scripts/publish-release.sh to publish the Mac
|
||||||
|
generated ZIP/checksum to Forgejo.
|
||||||
|
|
||||||
|
That is a manual macOS packaging step. For fully automated builds of
|
||||||
|
macOS binaries, a real Mac runner or a trusted Mac build service is needed.
|
||||||
|
|
||||||
|
Code signing / Developer ID notarization is separate; ad-hoc builds may
|
||||||
|
require explicit Gatekeeper approval.
|
||||||
|
|
||||||
|
## No Node checkout dependency
|
||||||
|
|
||||||
|
Forgejo Runner's default Docker image might supply Node, but the official
|
||||||
|
Swift image does not promise Node.js, so actions/checkout is not used.
|
||||||
|
The workflow obtains an authenticated archive at the triggering SHA using
|
||||||
|
Forgejo's own API, with the short-lived repository-scoped forgejo.token.
|
||||||
|
The token is only placed in a mode-0600 temporary curl configuration,
|
||||||
|
not in clone URLs, source files or process arguments.
|
||||||
|
|
||||||
|
As tar archives have no Git index, the job creates a local Git index for
|
||||||
|
git diff --cached --check. No CI commit or push occurs.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
The registered runner must advertise the docker label to this repository.
|
||||||
|
It must be allowed to pull swift:6.2-noble from Docker Hub and reach the
|
||||||
|
Forgejo API. No Docker socket inside the job is necessary.
|
||||||
|
|
||||||
|
Forgejo documentation:
|
||||||
https://forgejo.org/docs/latest/admin/actions/configuration/
|
https://forgejo.org/docs/latest/admin/actions/configuration/
|
||||||
https://forgejo.org/docs/latest/user/actions/basic-concepts/
|
https://forgejo.org/docs/latest/user/actions/reference/
|
||||||
|
https://forgejo.org/docs/latest/user/actions/security/
|
||||||
|
|
||||||
## Signing and publishing
|
Swift Docker:
|
||||||
|
https://www.swift.org/install/linux/docker/
|
||||||
The optional Forgejo repository variable MACOC_CODESIGN_IDENTITY selects
|
|
||||||
a code-signing identity already available in the macOS runner keychain.
|
|
||||||
Otherwise builds use ad-hoc signing. Ad-hoc releases are useful for local
|
|
||||||
testing but are not notarized and may trigger Gatekeeper warnings.
|
|
||||||
|
|
||||||
For public distribution use Developer ID Application signing plus Apple
|
|
||||||
notarization. Do not commit private keys or notarization credentials.
|
|
||||||
Notarization has not been automated in this version.
|
|
||||||
|
|
||||||
scripts/package.sh builds the archive and SHA-256 checksum.
|
|
||||||
scripts/publish-release.sh creates the release and uploads artifacts over
|
|
||||||
Forgejo REST using the workflow's short-lived FORGEJO_TOKEN. No long-lived
|
|
||||||
access token is stored in this repository.
|
|
||||||
|
|
||||||
The workflow requires a separately provisioned macOS runner. It cannot
|
|
||||||
be completed with the current Linux development host alone, and merely
|
|
||||||
committing it does not prove that a Mac build/release has run.
|
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ grep -q 'TISSelectInputSource' Sources/CapswitchApp/InputSourceManager.swift
|
||||||
grep -q 'IOHIDServiceClientSetProperty' Sources/CapswitchHID/CapswitchRemapping.c
|
grep -q 'IOHIDServiceClientSetProperty' Sources/CapswitchHID/CapswitchRemapping.c
|
||||||
grep -q '/usr/bin/open' Sources/CapswitchApp/ApplicationRelauncher.swift
|
grep -q '/usr/bin/open' Sources/CapswitchApp/ApplicationRelauncher.swift
|
||||||
grep -q 'SMAppService.mainApp' Sources/CapswitchApp/LoginItemManager.swift
|
grep -q 'SMAppService.mainApp' Sources/CapswitchApp/LoginItemManager.swift
|
||||||
test -f .forgejo/workflows/macos.yml
|
test -f .forgejo/workflows/ci.yml
|
||||||
for name in build bundle test install doctor clean lint verify icon sign package publish-release setup-signing; do
|
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"
|
bash -n "scripts/$name.sh"
|
||||||
done
|
done
|
||||||
echo "lint: PASS"
|
echo "lint: PASS"
|
||||||
|
|
|
||||||
47
scripts/test-linux-core.sh
Executable file
47
scripts/test-linux-core.sh
Executable 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
|
||||||
|
|
@ -1,5 +1,14 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
source "$(dirname "$0")/common.sh"
|
source "$(dirname "$0")/common.sh"
|
||||||
macos_only
|
|
||||||
|
if [[ "$IS_MACOS" == 1 ]]; then
|
||||||
cd "$ROOT"
|
cd "$ROOT"
|
||||||
swift test
|
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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue