Skip to content

Commit

Permalink
Add caching of pi-gen artifacts
Browse files Browse the repository at this point in the history
  • Loading branch information
usimd committed Nov 8, 2024
1 parent b771736 commit fb235af
Show file tree
Hide file tree
Showing 16 changed files with 3,952 additions and 6,170 deletions.
1 change: 1 addition & 0 deletions .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ jobs:
pubkey-ssh-first-user: ${{ env.CONFIG_PUBLIC_KEY }}
increase-runner-disk-size: ${{ github.event_name != 'workflow_dispatch' || inputs.increase-runner-disk }}
apt-proxy: http://172.17.0.1:9999
enable-pigen-cache: true

- name: Move APT cache
continue-on-error: true
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/publish-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ jobs:
include: |-
dist/licenses.txt
dist/index.js
dist/pre.js
dist/post.js
README.md
LICENSE
action.yml
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ tries to make sure the stage is respected and its changes are included in the fi
# 'image-noobs-path'.
enable-noobs: false

# Enables caching of pi-gen work artifacts to GitHub action cache to speed up
# repetitive builds.
enable-pigen-cache: false

# Enable SSH access to Pi.
enable-ssh: 0

Expand Down
40 changes: 5 additions & 35 deletions __test__/actions.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import * as core from '@actions/core'
import {DEFAULT_CONFIG} from '../src/pi-gen-config'
import * as actions from '../src/actions'
import {removeContainer} from '../src/remove-container'
import {build} from '../src/build'
import {removeRunnerComponents} from '../src/increase-runner-disk-size'
import * as removeContainer from '../src/remove-container'

jest.mock('../src/configure', () => ({
configure: jest.fn().mockReturnValue(DEFAULT_CONFIG)
Expand All @@ -29,28 +28,11 @@ describe('Actions', () => {
it('should only increase disk space if requested', async () => {
jest.spyOn(core, 'getBooleanInput').mockReturnValueOnce(true)

await actions.piGen()
await actions.main()

expect(removeRunnerComponents).toHaveBeenCalled()
})

it('does not run build function twice but invokes cleanup', async () => {
jest
.spyOn(core, 'getState')
.mockReturnValueOnce('')
.mockReturnValueOnce('true')
.mockReturnValueOnce('true')
process.env['INPUT_INCREASE-RUNNER-DISK-SIZE'] = 'false'

// expect build here
await actions.run()
// expect cleanup here
await actions.run()

expect(build).toHaveBeenCalledTimes(1)
expect(removeContainer).toHaveBeenCalledTimes(1)
})

const errorMessage = 'any error'
it.each([new Error(errorMessage), errorMessage])(
'should catch errors thrown during build and set build safely as failed',
Expand All @@ -59,34 +41,22 @@ describe('Actions', () => {
jest.spyOn(core, 'getInput').mockImplementation((name, options) => {
throw error
})
jest.spyOn(core, 'getBooleanInput').mockReturnValue(false)
jest.spyOn(core, 'setFailed')

await expect(actions.piGen()).resolves.not.toThrow()
await expect(actions.main()).resolves.not.toThrow()
expect(core.setFailed).toHaveBeenLastCalledWith(errorMessage)
}
)

it.each([new Error(errorMessage), errorMessage])(
'should gracefully catch errors thrown during cleanup and emit a warning message',
async error => {
jest.spyOn(core, 'getState').mockImplementation(name => {
throw error
})
jest.spyOn(removeContainer, 'removeContainer').mockRejectedValue(error)
jest.spyOn(core, 'warning')

await expect(actions.cleanup()).resolves.not.toThrow()
expect(core.warning).toHaveBeenLastCalledWith(errorMessage)
}
)

describe('cleanup', () => {
it.each(['', 'true'])(
'tries to remove container only if build has started = %s',
async buildStarted => {
jest.spyOn(core, 'getState').mockReturnValueOnce(buildStarted)
await actions.cleanup()
expect(removeContainer).toHaveBeenCalledTimes(buildStarted ? 1 : 0)
}
)
})
})
24 changes: 16 additions & 8 deletions __test__/pi-gen.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ const mockPiGenDependencies = (
])

jest.spyOn(fs, 'realpathSync').mockImplementationOnce(p => `/${p.toString()}`)

jest.spyOn(fs, 'mkdirSync').mockReturnValueOnce('')
}

describe('PiGen', () => {
Expand Down Expand Up @@ -89,6 +91,7 @@ describe('PiGen', () => {
.spyOn(fs, 'realpathSync')
.mockReturnValueOnce('/any/stage/path')
.mockReturnValueOnce('/pi-gen/stage0')
.mockReturnValueOnce('/pigen-work')

const piGen = await PiGen.getInstance(piGenDir, {
stageList: ['/any/stage/path', '/pi-gen/stage0'],
Expand All @@ -102,8 +105,9 @@ describe('PiGen', () => {
expect.objectContaining({
cwd: piGenDir,
env: expect.objectContaining({
PIGEN_DOCKER_OPTS:
'-v /any/stage/path:/any/stage/path -v /pi-gen/stage0:/pi-gen/stage0 -e DEBIAN_FRONTEND=noninteractive'
PIGEN_DOCKER_OPTS: expect.stringContaining(
'-v /any/stage/path:/any/stage/path -v /pi-gen/stage0:/pi-gen/stage0'
)
})
})
)
Expand All @@ -112,7 +116,10 @@ describe('PiGen', () => {
it('passes custom docker opts', async () => {
const piGenDir = 'pi-gen'
mockPiGenDependencies()
jest.spyOn(fs, 'realpathSync').mockReturnValueOnce('/pi-gen/stage0')
jest
.spyOn(fs, 'realpathSync')
.mockReturnValueOnce('/pi-gen/stage0')
.mockReturnValueOnce('/pigen-work')

const piGen = await PiGen.getInstance(piGenDir, {
stageList: ['/pi-gen/stage0'],
Expand All @@ -126,8 +133,9 @@ describe('PiGen', () => {
expect.objectContaining({
cwd: piGenDir,
env: expect.objectContaining({
PIGEN_DOCKER_OPTS:
'-v /foo:/bar -v /pi-gen/stage0:/pi-gen/stage0 -e DEBIAN_FRONTEND=noninteractive'
PIGEN_DOCKER_OPTS: expect.stringContaining(
'-v /foo:/bar -v /pi-gen/stage0:/pi-gen/stage0'
)
})
})
)
Expand Down Expand Up @@ -156,13 +164,13 @@ describe('PiGen', () => {
})

it('configures NOOBS export for stages that export images', async () => {
const stageList = [tmp.dirSync().name, tmp.dirSync().name]
fs.writeFileSync(`${stageList[0]}/EXPORT_IMAGE`, '')

const piGenDir = 'pi-gen'
mockPiGenDependencies()
jest.spyOn(fs, 'realpathSync').mockReturnValueOnce('/pi-gen/stage0')

const stageList = [tmp.dirSync().name, tmp.dirSync().name]
fs.writeFileSync(`${stageList[0]}/EXPORT_IMAGE`, '')

await PiGen.getInstance(piGenDir, {
stageList: stageList,
enableNoobs: 'true'
Expand Down
10 changes: 9 additions & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ inputs:
If any packages are missing during the build consider adding them to the `extra-host-dependencies` list.
required: false
default: false
enable-pigen-cache:
description: Enables caching of pi-gen work artifacts to GitHub action cache to speed up repetitive builds.
required: false
default: false
pi-gen-dir:
description: Path where selected pi-gen ref will be checked out to. If the path does not yet exist, it will be created (including its parents).
required: false
Expand All @@ -161,6 +165,10 @@ inputs:
description: Token to use for checking out pi-gen repo.
required: false
default: ${{ github.token }}
internal-matrix-context:
description: Used to access matrix parameters for cache key generation purposes. This is an internal parameter and must not be changed.
required: false
default: ${{ toJSON(matrix) }}

outputs:
image-path:
Expand All @@ -171,7 +179,7 @@ outputs:
runs:
using: node20
main: dist/index.js
post: dist/index.js
post: dist/post.js

branding:
icon: box
Expand Down
Loading

0 comments on commit fb235af

Please sign in to comment.