321 lines
12 KiB
YAML
321 lines
12 KiB
YAML
name: CI
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
pull_request:
|
|
|
|
concurrency:
|
|
group: ci-${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
SWIFT_VERSION: 6.3.3
|
|
SWIFTLY_VERSION: 1.1.3
|
|
SWIFTLY_SIGNING_FINGERPRINT: E813C892820A6FA13755B268F167DF1ACF9CE069
|
|
|
|
jobs:
|
|
changes:
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 5
|
|
outputs:
|
|
macos-tests: ${{ steps.macos-tests.outputs.macos-tests }}
|
|
macos-tests-reason: ${{ steps.macos-tests.outputs.macos-tests-reason }}
|
|
changed-path-count: ${{ steps.macos-tests.outputs.changed-path-count }}
|
|
steps:
|
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Detect macOS test impact
|
|
id: macos-tests
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
|
base_sha="${{ github.event.pull_request.base.sha }}"
|
|
else
|
|
base_sha="${{ github.event.before }}"
|
|
fi
|
|
|
|
changed_paths="${RUNNER_TEMP}/changed-paths.txt"
|
|
if [[ -z "$base_sha" || "$base_sha" =~ ^0+$ ]]; then
|
|
git ls-files | awk '{ print "A\t" $0 }' > "$changed_paths"
|
|
elif ! git cat-file -e "${base_sha}^{commit}" 2>/dev/null; then
|
|
echo "Base commit ${base_sha} is unavailable; running macOS tests conservatively."
|
|
git ls-files | awk '{ print "A\t" $0 }' > "$changed_paths"
|
|
else
|
|
git diff --name-status "$base_sha" "$GITHUB_SHA" > "$changed_paths"
|
|
fi
|
|
|
|
printf 'Changed paths:\n'
|
|
sed 's/^/- /' "$changed_paths"
|
|
./Scripts/ci_macos_test_gate.sh "$changed_paths"
|
|
|
|
- name: Summarize macOS test gate
|
|
if: ${{ always() }}
|
|
shell: bash
|
|
env:
|
|
MACOS_TESTS: ${{ steps.macos-tests.outputs.macos-tests }}
|
|
MACOS_TESTS_REASON: ${{ steps.macos-tests.outputs.macos-tests-reason }}
|
|
CHANGED_PATH_COUNT: ${{ steps.macos-tests.outputs.changed-path-count }}
|
|
run: |
|
|
set -euo pipefail
|
|
reason="${MACOS_TESTS_REASON:-<unset>}"
|
|
reason="${reason//|/\\|}"
|
|
{
|
|
printf '### macOS test gate\n\n'
|
|
printf '| Field | Value |\n'
|
|
printf '| --- | --- |\n'
|
|
printf '| macOS Swift tests required | `%s` |\n' "${MACOS_TESTS:-<unset>}"
|
|
printf '| Reason | %s |\n' "$reason"
|
|
printf '| Changed path entries | `%s` |\n' "${CHANGED_PATH_COUNT:-<unset>}"
|
|
} >> "$GITHUB_STEP_SUMMARY"
|
|
|
|
lint:
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 10
|
|
steps:
|
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
|
|
- name: Install lint tools
|
|
run: ./Scripts/install_lint_tools.sh swiftlint
|
|
|
|
- name: Lint
|
|
run: ./Scripts/lint.sh lint-linux
|
|
|
|
swift-test-macos:
|
|
needs: changes
|
|
if: ${{ needs.changes.outputs.macos-tests == 'true' }}
|
|
runs-on: macos-15-intel
|
|
timeout-minutes: 60
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
# Two shards amortize SwiftPM's per-shard discovery/cold build.
|
|
# Each shard remains within the 50-minute Swift Test timeout.
|
|
shard-index: [0, 1]
|
|
shard-count: [2]
|
|
steps:
|
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
|
|
- name: Select Xcode 26.3 or 26.2
|
|
run: |
|
|
set -euo pipefail
|
|
# Both versions are part of the official macOS 15 runner image.
|
|
for candidate in /Applications/Xcode_26.3.app /Applications/Xcode_26.2.app; do
|
|
if [[ -d "$candidate" ]]; then
|
|
sudo xcode-select -s "${candidate}/Contents/Developer"
|
|
echo "DEVELOPER_DIR=${candidate}/Contents/Developer" >> "$GITHUB_ENV"
|
|
break
|
|
fi
|
|
done
|
|
[[ "$(/usr/bin/xcodebuild -version)" == Xcode\ 26.* ]]
|
|
/usr/bin/xcodebuild -version
|
|
|
|
- name: Swift toolchain version
|
|
run: |
|
|
set -euo pipefail
|
|
swift --version
|
|
swift package --version
|
|
|
|
- name: Check app locales and Swift formatting
|
|
if: ${{ matrix.shard-index == 0 }}
|
|
run: ./Scripts/lint.sh lint-macos
|
|
|
|
- name: Swift Test
|
|
# Keep small batches to reduce SwiftPM process overhead without returning to one aggregate run.
|
|
timeout-minutes: 50
|
|
run: |
|
|
CODEXBAR_TEST_GROUP_SIZE=4 \
|
|
CODEXBAR_TEST_SUITE_TIMEOUT=120 \
|
|
CODEXBAR_TEST_RETRY_NON_TIMEOUT_FAILURES=0 \
|
|
CODEXBAR_TEST_SHARD_INDEX=${{ matrix.shard-index }} \
|
|
CODEXBAR_TEST_SHARD_COUNT=${{ matrix.shard-count }} \
|
|
./Scripts/test.sh
|
|
|
|
- name: Summarize macOS shard
|
|
if: ${{ always() }}
|
|
shell: bash
|
|
env:
|
|
SHARD_INDEX: ${{ matrix.shard-index }}
|
|
SHARD_COUNT: ${{ matrix.shard-count }}
|
|
RUNS_LINT_MACOS: ${{ matrix.shard-index == 0 }}
|
|
run: |
|
|
set -euo pipefail
|
|
display_shard_index=$((SHARD_INDEX + 1))
|
|
xcode_version="$(/usr/bin/xcodebuild -version 2>/dev/null | tr '\n' ' ' || true)"
|
|
{
|
|
printf '### macOS Swift shard\n\n'
|
|
printf '| Field | Value |\n'
|
|
printf '| --- | --- |\n'
|
|
printf '| Shard | `%s / %s` |\n' "$display_shard_index" "$SHARD_COUNT"
|
|
printf '| Runner | `%s` |\n' "${RUNNER_NAME:-unknown}"
|
|
printf '| Xcode | `%s` |\n' "${xcode_version:-unknown}"
|
|
printf '| Runs lint-macos | `%s` |\n' "$RUNS_LINT_MACOS"
|
|
} >> "$GITHUB_STEP_SUMMARY"
|
|
|
|
lint-build-test:
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 5
|
|
needs:
|
|
- changes
|
|
- lint
|
|
- swift-test-macos
|
|
if: ${{ always() && !cancelled() }}
|
|
steps:
|
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
|
|
- name: Verify lint and macOS test jobs
|
|
run: |
|
|
./Scripts/ci_verify_test_jobs.sh \
|
|
"${{ needs.lint.result }}" \
|
|
"${{ needs.changes.result }}" \
|
|
"${{ needs.changes.outputs.macos-tests }}" \
|
|
"${{ needs.swift-test-macos.result }}"
|
|
|
|
- name: Summarize aggregate CI gate
|
|
if: ${{ always() }}
|
|
shell: bash
|
|
env:
|
|
LINT_RESULT: ${{ needs.lint.result }}
|
|
CHANGES_RESULT: ${{ needs.changes.result }}
|
|
MACOS_TESTS: ${{ needs.changes.outputs.macos-tests }}
|
|
MACOS_TESTS_REASON: ${{ needs.changes.outputs.macos-tests-reason }}
|
|
MACOS_RESULT: ${{ needs.swift-test-macos.result }}
|
|
run: |
|
|
set -euo pipefail
|
|
reason="${MACOS_TESTS_REASON:-<unset>}"
|
|
reason="${reason//|/\\|}"
|
|
{
|
|
printf '### Aggregate CI gate\n\n'
|
|
printf '| Field | Value |\n'
|
|
printf '| --- | --- |\n'
|
|
printf '| lint result | `%s` |\n' "$LINT_RESULT"
|
|
printf '| changes result | `%s` |\n' "$CHANGES_RESULT"
|
|
printf '| macOS Swift tests required | `%s` |\n' "${MACOS_TESTS:-<unset>}"
|
|
printf '| macOS gate reason | %s |\n' "$reason"
|
|
printf '| swift-test-macos result | `%s` |\n' "$MACOS_RESULT"
|
|
} >> "$GITHUB_STEP_SUMMARY"
|
|
|
|
build-linux-cli:
|
|
timeout-minutes: 20
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- name: linux-x64
|
|
runs-on: ubuntu-24.04
|
|
- name: linux-arm64
|
|
runs-on: ubuntu-24.04-arm
|
|
runs-on: ${{ matrix.runs-on }}
|
|
steps:
|
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
|
|
- name: Runner info
|
|
run: |
|
|
set -euo pipefail
|
|
uname -a
|
|
uname -m
|
|
|
|
- name: Restore Swift toolchain cache
|
|
id: swift-toolchain-cache
|
|
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ~/.local/share/swiftly
|
|
key: swift-${{ runner.os }}-${{ runner.arch }}-${{ env.SWIFT_VERSION }}-swiftly-${{ env.SWIFTLY_VERSION }}
|
|
|
|
- name: Install Swift ${{ env.SWIFT_VERSION }} via swiftly
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
missing_packages=()
|
|
for package in ca-certificates gpg libcurl4-openssl-dev; do
|
|
if ! dpkg-query -W -f='${Status}' "$package" 2>/dev/null | grep -q "install ok installed"; then
|
|
missing_packages+=("$package")
|
|
fi
|
|
done
|
|
if [[ "${#missing_packages[@]}" -gt 0 ]]; then
|
|
sudo apt-get update
|
|
sudo apt-get install -y "${missing_packages[@]}"
|
|
fi
|
|
|
|
SWIFTLY_ARCH="$(uname -m)"
|
|
SWIFTLY_ARCHIVE="$RUNNER_TEMP/swiftly-${SWIFTLY_VERSION}-${SWIFTLY_ARCH}.tar.gz"
|
|
SWIFTLY_SIGNATURE="${SWIFTLY_ARCHIVE}.sig"
|
|
SWIFTLY_HOME_DIR="$HOME/.local/share/swiftly"
|
|
SWIFTLY_BIN_DIR="$HOME/.local/bin"
|
|
SWIFT_GNUPGHOME="$(mktemp -d)"
|
|
SWIFT_KEYS="$RUNNER_TEMP/swift-signing-keys.asc"
|
|
POST_INSTALL_SCRIPT="$(mktemp)"
|
|
|
|
mkdir -p "$SWIFTLY_BIN_DIR"
|
|
chmod 700 "$SWIFT_GNUPGHOME"
|
|
echo "Swift toolchain cache hit: ${{ steps.swift-toolchain-cache.outputs.cache-hit }}"
|
|
curl -fsSL "https://download.swift.org/swiftly/linux/swiftly-${SWIFTLY_VERSION}-${SWIFTLY_ARCH}.tar.gz" -o "$SWIFTLY_ARCHIVE"
|
|
curl -fsSL "https://download.swift.org/swiftly/linux/swiftly-${SWIFTLY_VERSION}-${SWIFTLY_ARCH}.tar.gz.sig" -o "$SWIFTLY_SIGNATURE"
|
|
curl -fsSL --compressed "https://www.swift.org/keys/all-keys.asc" -o "$SWIFT_KEYS"
|
|
GNUPGHOME="$SWIFT_GNUPGHOME" gpg --batch --import "$SWIFT_KEYS"
|
|
SIGNATURE_STATUS="$(GNUPGHOME="$SWIFT_GNUPGHOME" gpg --batch --status-fd=1 \
|
|
--verify "$SWIFTLY_SIGNATURE" "$SWIFTLY_ARCHIVE" 2>&1)"
|
|
printf '%s\n' "$SIGNATURE_STATUS"
|
|
grep -Fq "[GNUPG:] VALIDSIG ${SWIFTLY_SIGNING_FINGERPRINT} " <<< "$SIGNATURE_STATUS"
|
|
tar -xzf "$SWIFTLY_ARCHIVE" -C /tmp
|
|
/tmp/swiftly init --assume-yes --skip-install
|
|
|
|
. "$SWIFTLY_HOME_DIR/env.sh"
|
|
echo "$SWIFTLY_BIN_DIR" >> "$GITHUB_PATH"
|
|
echo "SWIFTLY_HOME_DIR=$SWIFTLY_HOME_DIR" >> "$GITHUB_ENV"
|
|
echo "SWIFTLY_BIN_DIR=$SWIFTLY_BIN_DIR" >> "$GITHUB_ENV"
|
|
|
|
swiftly install "$SWIFT_VERSION" --use --assume-yes --verify --post-install-file "$POST_INSTALL_SCRIPT"
|
|
if [[ -s "$POST_INSTALL_SCRIPT" ]]; then
|
|
sudo apt-get update
|
|
sudo bash "$POST_INSTALL_SCRIPT"
|
|
fi
|
|
|
|
hash -r
|
|
swift --version
|
|
|
|
- name: Build CodexBarCLI (release, static Swift stdlib)
|
|
run: swift build -c release --product CodexBarCLI --static-swift-stdlib
|
|
|
|
- name: Swift Test (Linux only)
|
|
run: swift test --parallel
|
|
|
|
- name: Smoke test CodexBarCLI
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
BIN_DIR="$(swift build -c release --product CodexBarCLI --static-swift-stdlib --show-bin-path)"
|
|
BIN="$BIN_DIR/CodexBarCLI"
|
|
"$BIN" --help >/dev/null
|
|
"$BIN" --version >/dev/null
|
|
if "$BIN" usage --provider codex --web >/dev/null 2>&1; then
|
|
echo "Expected --web to fail on Linux"
|
|
exit 1
|
|
fi
|
|
"$BIN" usage --provider codex --web 2>&1 | tee /tmp/codexbarcli-stderr.txt >/dev/null || true
|
|
grep -q "macOS" /tmp/codexbarcli-stderr.txt
|
|
|
|
- name: Summarize Linux CLI build
|
|
if: ${{ always() }}
|
|
shell: bash
|
|
env:
|
|
MATRIX_NAME: ${{ matrix.name }}
|
|
MATRIX_RUNS_ON: ${{ matrix.runs-on }}
|
|
SWIFT_CACHE_HIT: ${{ steps.swift-toolchain-cache.outputs.cache-hit }}
|
|
run: |
|
|
set -euo pipefail
|
|
{
|
|
printf '### Linux CLI build\n\n'
|
|
printf '| Field | Value |\n'
|
|
printf '| --- | --- |\n'
|
|
printf '| Matrix | `%s` |\n' "$MATRIX_NAME"
|
|
printf '| Runner label | `%s` |\n' "$MATRIX_RUNS_ON"
|
|
printf '| Swift version | `%s` |\n' "$SWIFT_VERSION"
|
|
printf '| Swift toolchain cache hit | `%s` |\n' "${SWIFT_CACHE_HIT:-false}"
|
|
} >> "$GITHUB_STEP_SUMMARY"
|