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
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,33 @@ directory.

This behavior cannot currently be disabled, PR welcome.

## Code Signing

```yaml
jobs:
build:
runs-on: macos-latest
steps:
- use: mxcl/xcodebuild@v1
with:
code-sign-certificate: ${{ secrets.CERTIFICATE_BASE64 }}
code-sign-certificate-passphrase: ${{ secrets.CERTIFICATE_PASSPHRASE}}
```

> This feature requires macOS.

A code signing certificate can be installed to the macOS Keychain. It is
automatically removed from the Keychain in a post action.

To export your certificate from Xcode and Base64 encode it, follow
[these instructions][export]. Store any secrets, including certificates and
passphrases, in GitHub as [Encrypted Secrets][secrets].

You may specify a `code-sign-identity` to override any `CODE_SIGN_IDENTITY`
specified by your project.

To disable code signing, you can specify `code-sign-identity: '-'`.
Comment thread
chris-araman marked this conversation as resolved.

## Caveats

* The selected Xcode remains the default Xcode for the image for the duration of
Expand Down Expand Up @@ -229,8 +256,9 @@ This action does not support Linux.
1. Run `npm run prepare`
1. Create a [Pull Request](https://github.com/mxcl/xcodebuild/compare)


[automated-list]: https://flatgithub.com/mxcl/.github/?filename=versions.json
[gha-xcode-list]: https://github.com/actions/virtual-environments/blob/main/images/macos/macos-10.15-Readme.md#xcode
[sinoru/actions-setup-xcode]: https://github.com/sinoru/actions-setup-xcode
[img]: https://raw.githubusercontent.com/mxcl/xcodebuild/gh-pages/XCResult.png
[secrets]: https://docs.github.com/en/actions/reference/encrypted-secrets
[export]: https://docs.github.com/en/actions/guides/installing-an-apple-certificate-on-macos-runners-for-xcode-development#creating-secrets-for-your-certificate-and-provisioning-profile
27 changes: 25 additions & 2 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,26 @@ inputs:
code-coverage:
description: Enables code coverage
required: false
default: false
default: 'false'
code-sign-certificate:
description: |
A Base64-encoded certificate to be installed to the macOS Keychain for
code signing. It is removed from the keychain in the post action. This
certificate should correspond to the `CODE_SIGN_IDENTITY` specified in
your project or to the `code-sign-identity` input. Pass this in as a
GitHub Encrypted Secret. Requires macOS and
`code-sign-certificate-passphrase`.
required: false
code-sign-certificate-passphrase:
description: |
The passphrase used to protect the code signing certificate. Pass this in
as a GitHub Encrypted Secret.
required: false
code-sign-identity:
description: |
Identity to be used for code signing. If your project specifies a
`CODE_SIGN_IDENTITY`, this will override it.
required: false
working-directory:
description: '…'
required: false
Expand All @@ -48,16 +67,20 @@ inputs:
warnings-as-errors:
description: Fails the build if any warnings in *non test targets*.
required: false
default: false
default: 'false'
Comment thread
chris-araman marked this conversation as resolved.
verbosity:
description: One of `xcpretty`, `quiet` or `verbose`.
default: xcpretty
required: false
upload-logs:
description: |
One of `always` or `on-failure`.
Consider `always` if you want access to your coverage-reports.
Beware that artifacts count against your GitHub Actions storage limits.
default: on-failure
required: false
runs:
using: 'node12'
main: 'dist/index.js'
post: 'dist/index.js'
post-if: runner.os == 'macOS'
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.

14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"semver": "^6.3.0"
},
"devDependencies": {
"@types/node": "^14.14.9",
"@types/node": "^12.20.18",
"@types/semver": "^7.3.6",
"@vercel/ncc": "^0.28.6",
"typescript": "^4.1.3"
Expand Down
79 changes: 63 additions & 16 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { scheme as libGetScheme, spawn, xcselect, getConfiguration, actionIsTestable, getAction, Platform, getDestination, verbosity } from './lib'
import { scheme as libGetScheme, spawn, xcselect, getConfiguration, actionIsTestable, getAction, Platform, getDestination, getIdentity, createKeychain, deleteKeychain, verbosity } from './lib'
import xcodebuildX from './xcodebuild'
const artifact = require('@actions/artifact');
import * as core from '@actions/core'
Expand All @@ -10,7 +10,7 @@ import { basename, extname, join } from 'path'
//TODO we also need to set the right flags for other languages
const warningsAsErrorsFlags = 'OTHER_SWIFT_FLAGS=-warnings-as-errors'

async function run() {
async function main() {
const cwd = core.getInput('working-directory')
if (cwd) {
process.chdir(cwd)
Expand All @@ -23,6 +23,7 @@ async function run() {
const configuration = getConfiguration()
const warningsAsErrors = core.getBooleanInput('warnings-as-errors')
const destination = await getDestination(platform, selected)
const identity = getIdentity(core.getInput('code-sign-identity'), platform)
const xcpretty = verbosity() == 'xcpretty'

core.info(`» Selected Xcode ${selected}`)
Expand All @@ -32,6 +33,8 @@ async function run() {
generateXcodeproj(reason)
}

await configureKeychain()

await build(await getScheme())

if (core.getInput('upload-logs') == 'always') {
Expand Down Expand Up @@ -66,6 +69,27 @@ async function run() {
}
}

async function configureKeychain() {
const certificate = core.getInput('code-sign-certificate')
if (!certificate) return

if (process.env.RUNNER_OS != 'macOS') {
throw new Error('code-sign-certificate requires macOS.')
}

const passphrase = core.getInput('code-sign-certificate-passphrase')
if (!passphrase) {
throw new Error('code-sign-certificate requires code-sign-certificate-passphrase.')
}

await core.group(
'Configuring code signing',
async function() {
await createKeychain(certificate, passphrase)
}
)
}

async function build(scheme: string | undefined) {
if (warningsAsErrors && actionIsTestable(action)) {
await xcodebuild('build', scheme)
Expand All @@ -83,6 +107,7 @@ async function run() {
core.startGroup(`\`${title}\``)
let args = destination
if (scheme) args = args.concat(['-scheme', scheme])
if (identity) args = args.concat(identity)
if (verbosity() == 'quiet') args.push('-quiet')
if (configuration) args = args.concat(['-configuration', configuration])

Expand Down Expand Up @@ -121,26 +146,48 @@ async function run() {
}
}

async function post() {
await deleteKeychain()
}

async function run() {
// We use the same entry point for `main` and `post` in action.yml in order to
// avoid duplicating common logic. To differentiate at runtime, we set some
// state in `main` for `post` to read.
const isPost = Boolean(core.getState('isPost'))
if (isPost) {
return await post()
} else {
core.saveState('isPost', true)
Comment thread
chris-araman marked this conversation as resolved.
}

try {
await main()
} catch (error) {
await uploadLogs()

const id = `${process.env.GITHUB_RUN_ID}`
const slug = process.env.GITHUB_REPOSITORY
const href = `https://github.com/${slug}/actions/runs/${id}#artifact`

core.warning(`
We feel you.
CI failures suck.
Download the \`.xcresult\` files we just artifact’d.
They *really* help diagnose what went wrong!
${href}
`.replace(/\s+/g, ' '))

throw error
}
}

run().catch(async e => {
core.setFailed(e)

if (e instanceof SyntaxError && e.stack) {
core.error(e.stack)
}

await uploadLogs()

const id = `${process.env.GITHUB_RUN_ID}`
const slug = process.env.GITHUB_REPOSITORY
const href = `https://github.com/${slug}/actions/runs/${id}#artifact`

core.warning(`
We feel you.
CI failures suck.
Download the \`.xcresult\` files we just artifact’d.
They *really* help diagnose what went wrong!
${href}
`.replace(/\s+/g, ' '))
})

async function uploadLogs() {
Expand Down
Loading