Skip to content

Commit

Permalink
Initial draft of Github Action
Browse files Browse the repository at this point in the history
  • Loading branch information
orj committed Jan 23, 2020
1 parent d582e7e commit 1dc5a66
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 54 deletions.
9 changes: 1 addition & 8 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,9 @@ on: # rebuild any PRs and main branch changes

jobs:
build: # make sure build/ci work properly
runs-on: ubuntu-latest
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1
- run: |
npm install
npm run all
test: # make sure the action works on a clean machine without building
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: ./
with:
milliseconds: 1000
2 changes: 2 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@

The MIT License (MIT)

Copyright (c) 2014 Microsoft Corporation
Copyright (c) 2018 GitHub, Inc. and contributors
Copyright (c) 2020 Itty Bitty Apps Pty Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
36 changes: 13 additions & 23 deletions __tests__/main.test.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,17 @@
import {wait} from '../src/wait'
import * as process from 'process'
import * as cp from 'child_process'
import * as path from 'path'
import * as security from '../src/security'

test('throws invalid number', async () => {
const input = parseInt('foo', 10)
await expect(wait(input)).rejects.toThrow('milliseconds not a number')
})
test('imports p12 file', async () => {
const keychain: string = "signing_temp"
const keychainPassword: string = Math.random().toString(36)
const p12FilePath: string = "Certificates.p12"
const p12Password: string = "password"

test('wait 500 ms', async () => {
const start = new Date()
await wait(500)
const end = new Date()
var delta = Math.abs(end.getTime() - start.getTime())
expect(delta).toBeGreaterThan(450)
})
await security.installCertInTemporaryKeychain(
keychain,
true,
keychainPassword,
p12FilePath,
p12Password)

// shows how the runner will run a javascript action with env / stdout protocol
test('test runs', () => {
process.env['INPUT_MILLISECONDS'] = '500'
const ip = path.join(__dirname, '..', 'lib', 'main.js')
const options: cp.ExecSyncOptions = {
env: process.env
}
console.log(cp.execSync(`node ${ip}`, options).toString())
await security.deleteKeychain(keychain)
})
31 changes: 25 additions & 6 deletions action.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
name: 'Your name here'
description: 'Provide a description here'
author: 'Your name or organization here'
name: 'Import Code-Signing Certificates'
description: 'Imports a PKCS12 certificate and private key into a macOS Keychain.'
author: 'Itty Bitty Apps Pty Ltd'
inputs:
myInput: # change this
description: 'input description here'
default: 'default value if applicable'
keychain:
description: 'The name of the keychain to import into.'
required: true
default: 'signing_temp'
create-keychain:
description: 'A boolean indicating whether to create the keychain.'
required: true
default: 'true'
keychain-password:
description: 'The password to use with the keychain. Gets auto-generated if keychain is "signing_temp".'
required: false
p12-filepath:
description: 'The path to the PKCS12 file to import.'
required: true
p12-password:
description: 'The password used to import the PKCS12 file.'
required: false
outputs:
keychain-password:
description: 'The password for temporary keychain.'
security-response:
description: 'The output of the security commands.'
runs:
using: 'node12'
main: 'dist/index.js'
13 changes: 13 additions & 0 deletions package-lock.json

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

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"author": "YourNameOrOrganization",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.2.0"
"@actions/core": "^1.2.0",
"@actions/exec": "latest"
},
"devDependencies": {
"@types/jest": "^24.0.23",
Expand Down
32 changes: 25 additions & 7 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
import * as core from '@actions/core'
import {wait} from './wait'
import * as os from 'os'
import * as security from './security'

async function run(): Promise<void> {
try {
const ms: string = core.getInput('milliseconds')
core.debug(`Waiting ${ms} milliseconds ...`)
if (os.platform() !== 'darwin') {
throw new Error('Action requires macOS agent.')
}

core.debug(new Date().toTimeString())
await wait(parseInt(ms, 10))
core.debug(new Date().toTimeString())
const keychain: string = core.getInput('keychain')
const createKeychain: boolean = core.getInput('create-keychain') === 'true'
let keychainPassword: string = core.getInput('keychain-password')
const p12File: string = core.getInput('p12-file')
const p12Password: string = core.getInput('p12-password')

core.setOutput('time', new Date().toTimeString())
if (keychainPassword === '') {
// generate a keychain password for the temporary keychain
keychainPassword = Math.random().toString(36)
}

core.setOutput('keychain-password', keychainPassword)
core.setSecret(keychainPassword)

await security.installCertInTemporaryKeychain(
keychain,
createKeychain,
keychainPassword,
p12File,
p12Password
)
} catch (error) {
core.setFailed(error.message)
}
Expand Down
103 changes: 103 additions & 0 deletions src/security.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import * as core from '@actions/core'
import * as exec from '@actions/exec'
import {ExecOptions} from '@actions/exec/lib/interfaces'

export async function installCertInTemporaryKeychain(
keychain: string,
setupKeychain: boolean,
keychainPassword: string,
p12FilePath: string,
p12Password: string
): Promise<void> {
let output = ''
const options: ExecOptions = {}
options.listeners = {
stdout: (data: Buffer) => {
output += data.toString()
}
}

const tempKeychain = `${keychain}.keychain`
if (setupKeychain) {
await createKeychain(tempKeychain, keychainPassword, options)
}
await unlockKeychain(tempKeychain, keychainPassword, options)
await importPkcs12(tempKeychain, p12FilePath, p12Password, options)

core.debug(output)
core.setOutput('security-response', output)
}

/**
* Delete the specified keychain
* @param keychain
* @param options Execution options (optional)
*/
export async function deleteKeychain(
keychain: string,
options?: ExecOptions
): Promise<void> {
await exec.exec(
'security',
['delete-keychain', `${keychain}.keychain`],
options
)
}

/**
* Import a PKCS12 file into the keychain
* @param keychain The name of the keychain to import the P12 file into.
* @param p12FilePath The path to the .p12 file
* @param p12Password The password used to decrypt the .p12 file.
* @param options Execution options (optional)
*/
async function importPkcs12(
keychain: string,
p12FilePath: string,
p12Password: string,
options?: ExecOptions
): Promise<void> {
const importArgs: string[] = [
'import',
p12FilePath,
'-k',
keychain,
'-f',
'pkcs12',
// This option allows any application to read keys.
// This would be insecure if the keychain was retained but GitHub action
// VMs are thrown away after use.
'-A',
'-P',
p12Password
]

await exec.exec('security', importArgs, options)
}

async function unlockKeychain(
keychain: string,
password: string,
options: ExecOptions
): Promise<void> {
const args: string[] = ['unlock-keychain', '-p', password, keychain]
await exec.exec('security', args, options)
}

async function createKeychain(
keychain: string,
password: string,
options: ExecOptions
): Promise<void> {
const createArgs: string[] = ['create-keychain', '-p', password, keychain]
await exec.exec('security', createArgs, options)

// Set automatic keychain lock timeout to 6 hours.
const setSettingsArgs: string[] = [
'set-keychain-settings',
'-lut',
'21600',
keychain
]
await exec.exec('security', setSettingsArgs, options)
}
9 changes: 0 additions & 9 deletions src/wait.ts

This file was deleted.

0 comments on commit 1dc5a66

Please sign in to comment.