Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ jobs:
requires: ${{ matrix.x.swift }}

xcode:
name: ${{ matrix.platform }} (${{ matrix.action }}, ${{ matrix.xcode }}${{ matrix.codecov && ', cc' || ''}})
name: ${{ matrix.platform }} (${{ matrix.action }}, ${{ matrix.xcode }}${{ matrix.codecov && ', cc' || ''}}${{ matrix.job-name-sufix }})
runs-on: ${{ matrix.os || 'macos-12' }}
needs: [verify-dist]
strategy:
Expand All @@ -259,6 +259,11 @@ jobs:
warnings-as-errors:
- false
include:
- job-name-sufix: ', platform-version ^16'
platform: iOS
platform-version: ^16
os: macos-14
xcode: ^14
- platform: mac-catalyst
xcode: ^13
codecov: false
Expand All @@ -275,6 +280,7 @@ jobs:
- uses: ./
with:
platform: ${{ matrix.platform }}
platform-version: ${{ matrix.platform-version }}
xcode: ${{ matrix.xcode }}
working-directory: fixtures/${{ matrix.platform }}
code-coverage: ${{ matrix.codecov }}
Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,25 @@ jobs:

> † check out https://devhints.io/semver for valid ranges

```yaml
jobs:
build:
strategy:
matrix:
platform:
- iOS
platform-version:
- ^15
- ^16
- ^17
runs-on: macos-latest
steps:
- uses: mxcl/xcodebuild@v3
with:
platform-version: ${{ matrix.platform-version }}
platform: ${{ matrix.platform }}
```

```yaml
jobs:
build:
Expand Down
5 changes: 5 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ inputs:
`mac-catalyst`
Leave unset and `xcodebuild` decides itself.
required: false
platform-version:
description: |
A semantic version range, eg. ^15, ~16.1 or 17.4.1
Leave unset for the latest available on the runner.
required: false
arch:
description: |
Either `arm64` `x86_64 `i386`
Expand Down
4 changes: 2 additions & 2 deletions dist/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ async function main() {

const swiftPM = fs.existsSync('Package.swift')
const platform = getPlatformInput('platform')
const platformVersion = getRangeInput('platform-version')
const arch = getArchInput('arch')
const selected = await xcselect(
getRangeInput('xcode'),
Expand All @@ -42,7 +43,7 @@ async function main() {
const action = getAction(selected, platform)
const configuration = getConfiguration()
const warningsAsErrors = core.getBooleanInput('warnings-as-errors')
const destination = await getDestination(selected, platform)
const destination = await getDestination(selected, platform, platformVersion)
const identity = getIdentity(core.getInput('code-sign-identity'), platform)
const xcpretty = verbosity() == 'xcpretty'
const workspace = core.getInput('workspace')
Expand Down
46 changes: 32 additions & 14 deletions src/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,18 @@ interface Devices {
[key: string]: [
{
udid: string
name: string
}
]
}
}

type DeviceType = 'watchOS' | 'tvOS' | 'iOS' | 'xrOS'
type Destination = { [key: string]: string }
type Destination = {
id: string
name: string | undefined
version: SemVer
}

interface Schemes {
workspace?: {
Expand Down Expand Up @@ -193,7 +198,10 @@ function parseJSON<T>(input: string): T {
}
}

async function destinations(): Promise<Destination> {
async function destination(
deviceType: DeviceType,
version?: Range
): Promise<Destination | undefined> {
const out = await exec('xcrun', [
'simctl',
'list',
Expand All @@ -203,22 +211,23 @@ async function destinations(): Promise<Destination> {
])
const devices = parseJSON<Devices>(out).devices

const rv: { [key: string]: { v: SemVer; id: string } } = {}
// best match
let bm: Destination | undefined
for (const opaqueIdentifier in devices) {
const device = (devices[opaqueIdentifier] ?? [])[0]
if (!device) continue
const [type, v] = parse(opaqueIdentifier)
if (v && (!rv[type] || semver.lt(rv[type].v, v))) {
rv[type] = { v, id: device.udid }
if (
v &&
type === deviceType &&
(!version || version.test(v)) &&
(!bm || semver.lt(bm.version, v))
) {
bm = { id: device.udid, name: device.name, version: v }
}
}

return {
tvOS: rv.tvOS?.id,
watchOS: rv.watchOS?.id,
iOS: rv.iOS?.id,
visionOS: rv.xrOS?.id,
}
return bm

function parse(key: string): [DeviceType, SemVer?] {
const [type, ...vv] = (key.split('.').pop() ?? '').split('-')
Expand Down Expand Up @@ -324,15 +333,24 @@ export function actionIsTestable(action?: string): boolean {

export async function getDestination(
xcodeVersion: SemVer,
platform?: Platform
platform?: Platform,
platformVersion?: Range
): Promise<string[]> {
switch (platform) {
case 'iOS':
case 'tvOS':
case 'watchOS':
case 'visionOS': {
const id = (await destinations())[platform]
return ['-destination', `id=${id}`]
const deviceType: DeviceType = platform === 'visionOS' ? 'xrOS' : platform
const dest = await destination(deviceType, platformVersion)
if (!dest) {
core.error(
`Device not found (platform: ${platform}, version: ${platformVersion})`
)
return []
}
core.info(`Selected device: ${dest.name} (${dest.version})`)
return ['-destination', `id=${dest.id}`]
}
case 'macOS':
return ['-destination', `platform=macOS`]
Expand Down