Skip to content

Merge pull request #5 from RealDyllon/codex/optional-tool-docs-tests #6

Merge pull request #5 from RealDyllon/codex/optional-tool-docs-tests

Merge pull request #5 from RealDyllon/codex/optional-tool-docs-tests #6

Workflow file for this run

name: Build and Release App
on:
pull_request:
paths:
- ColimaStack/**
- ColimaStackTests/**
- ColimaStackUITests/**
- ColimaStack.xcodeproj/**
- .github/workflows/app-release.yml
push:
branches: [main]
tags:
- 'v*'
paths:
- ColimaStack/**
- ColimaStackTests/**
- ColimaStackUITests/**
- ColimaStack.xcodeproj/**
- .github/workflows/app-release.yml
workflow_dispatch:
inputs:
version:
description: Release version, without the leading v.
required: true
default: '0.0.1'
build_number:
description: Optional CFBundleVersion override. Defaults to the workflow run number.
required: false
permissions:
contents: read
concurrency:
group: app-${{ github.ref }}
cancel-in-progress: true
env:
APP_NAME: ColimaStack
PROJECT_PATH: ColimaStack.xcodeproj
SCHEME: ColimaStack
CONFIGURATION: Release
DERIVED_DATA_PATH: build/DerivedData
EXPORT_PATH: build/export
jobs:
app-smoke-tests:
name: App Smoke Tests (${{ matrix.runner }})
runs-on: ${{ matrix.runner }}
needs: build
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
runner: [macos-14, macos-15, macos-26]
steps:
- name: Show runner platform
run: |
sw_vers
- name: Download app artifact
uses: actions/download-artifact@v4
with:
name: ${{ needs.build.outputs.artifact_name }}
path: smoke-assets
- name: Expand app artifact
shell: bash
run: |
set -euo pipefail
archive_path="smoke-assets/${{ needs.build.outputs.artifact_name }}.zip"
if [[ ! -f "$archive_path" ]]; then
echo "Expected app archive not found at $archive_path" >&2
exit 1
fi
mkdir -p smoke-app
ditto -x -k "$archive_path" smoke-app
- name: Launch app
shell: bash
run: |
set -euo pipefail
app_path="smoke-app/${APP_NAME}.app"
executable="$app_path/Contents/MacOS/${APP_NAME}"
log_path="$RUNNER_TEMP/${APP_NAME}-${{ matrix.runner }}.log"
if [[ ! -x "$executable" ]]; then
echo "Expected app executable not found at $executable" >&2
exit 1
fi
"$executable" --mock-data >"$log_path" 2>&1 &
app_pid="$!"
cleanup() {
kill "$app_pid" >/dev/null 2>&1 || true
wait "$app_pid" >/dev/null 2>&1 || true
}
trap cleanup EXIT
for _ in {1..15}; do
if ! kill -0 "$app_pid" >/dev/null 2>&1; then
echo "App exited before the smoke test completed." >&2
cat "$log_path" >&2 || true
exit 1
fi
sleep 1
done
echo "App stayed running during the smoke test."
build:
name: Build and Test
runs-on: macos-15
outputs:
version: ${{ steps.metadata.outputs.version }}
build_number: ${{ steps.metadata.outputs.build_number }}
artifact_name: ${{ steps.metadata.outputs.artifact_name }}
release_tag: ${{ steps.metadata.outputs.release_tag }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Resolve version metadata
id: metadata
shell: bash
env:
EVENT_NAME: ${{ github.event_name }}
INPUT_BUILD_NUMBER: ${{ github.event.inputs.build_number }}
INPUT_VERSION: ${{ github.event.inputs.version }}
REF_NAME: ${{ github.ref_name }}
REF_TYPE: ${{ github.ref_type }}
RUN_NUMBER: ${{ github.run_number }}
run: |
set -euo pipefail
project_version="$(
xcodebuild -showBuildSettings \
-project "$PROJECT_PATH" \
-scheme "$SCHEME" \
-configuration "$CONFIGURATION" \
-destination 'generic/platform=macOS' \
-derivedDataPath "$DERIVED_DATA_PATH" \
| awk -F '= ' '/MARKETING_VERSION/ { print $2; exit }'
)"
if [[ -z "$project_version" ]]; then
echo "Unable to resolve MARKETING_VERSION from Xcode build settings." >&2
exit 1
fi
version="$project_version"
if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
version="$INPUT_VERSION"
elif [[ "$REF_TYPE" == "tag" ]]; then
version="${REF_NAME#v}"
fi
build_number="$RUN_NUMBER"
if [[ "$EVENT_NAME" == "workflow_dispatch" && -n "$INPUT_BUILD_NUMBER" ]]; then
build_number="$INPUT_BUILD_NUMBER"
fi
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Version must use x.y.z format, for example 0.0.1." >&2
exit 1
fi
if [[ ! "$build_number" =~ ^[0-9]+$ ]]; then
echo "Build number must be numeric." >&2
exit 1
fi
release_tag="v${version}"
artifact_name="${APP_NAME}-${version}-${build_number}"
{
echo "version=${version}"
echo "build_number=${build_number}"
echo "release_tag=${release_tag}"
echo "artifact_name=${artifact_name}"
} >> "$GITHUB_OUTPUT"
- name: Run tests
run: |
xcodebuild test \
-project "$PROJECT_PATH" \
-scheme "$SCHEME" \
-configuration Debug \
-destination 'platform=macOS' \
-skip-testing:ColimaStackUITests \
-derivedDataPath "$DERIVED_DATA_PATH" \
-parallel-testing-enabled NO \
CODE_SIGNING_ALLOWED=NO
- name: Build app
run: |
xcodebuild build \
-project "$PROJECT_PATH" \
-scheme "$SCHEME" \
-configuration "$CONFIGURATION" \
-destination 'generic/platform=macOS' \
-derivedDataPath "$DERIVED_DATA_PATH" \
MARKETING_VERSION="${{ steps.metadata.outputs.version }}" \
CURRENT_PROJECT_VERSION="${{ steps.metadata.outputs.build_number }}" \
CODE_SIGNING_ALLOWED=NO \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGN_IDENTITY=""
- name: Package app
shell: bash
run: |
set -euo pipefail
app_path="$DERIVED_DATA_PATH/Build/Products/$CONFIGURATION/$APP_NAME.app"
if [[ ! -d "$app_path" ]]; then
echo "Expected app bundle not found at $app_path" >&2
exit 1
fi
mkdir -p "$EXPORT_PATH"
ditto -c -k --keepParent "$app_path" "$EXPORT_PATH/${{ steps.metadata.outputs.artifact_name }}.zip"
shasum -a 256 "$EXPORT_PATH/${{ steps.metadata.outputs.artifact_name }}.zip" \
> "$EXPORT_PATH/${{ steps.metadata.outputs.artifact_name }}.zip.sha256"
- name: Upload workflow artifact
uses: actions/upload-artifact@v4
with:
name: ${{ steps.metadata.outputs.artifact_name }}
path: |
${{ env.EXPORT_PATH }}/${{ steps.metadata.outputs.artifact_name }}.zip
${{ env.EXPORT_PATH }}/${{ steps.metadata.outputs.artifact_name }}.zip.sha256
if-no-files-found: error
release:
name: Publish GitHub Release
runs-on: ubuntu-latest
needs:
- build
- app-smoke-tests
if: github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v')
permissions:
contents: write
steps:
- name: Download app artifact
uses: actions/download-artifact@v4
with:
name: ${{ needs.build.outputs.artifact_name }}
path: release-assets
- name: Create or update release
env:
GH_TOKEN: ${{ github.token }}
REPOSITORY: ${{ github.repository }}
TAG: ${{ needs.build.outputs.release_tag }}
VERSION: ${{ needs.build.outputs.version }}
BUILD_NUMBER: ${{ needs.build.outputs.build_number }}
ARTIFACT_NAME: ${{ needs.build.outputs.artifact_name }}
run: |
set -euo pipefail
notes="ColimaStack ${VERSION} (${BUILD_NUMBER})"
if gh release view "$TAG" --repo "$REPOSITORY" >/dev/null 2>&1; then
gh release upload "$TAG" release-assets/* --repo "$REPOSITORY" --clobber
else
gh release create "$TAG" release-assets/* \
--repo "$REPOSITORY" \
--title "ColimaStack ${VERSION}" \
--notes "$notes"
fi