Skip to content

Commit

Permalink
Add store-credentials and environment support
Browse files Browse the repository at this point in the history
Signed-off-by: Dani Llewellyn <[email protected]>
  • Loading branch information
Dani Llewellyn committed Mar 2, 2023
1 parent 9e8d933 commit bd4fba8
Show file tree
Hide file tree
Showing 8 changed files with 370 additions and 207 deletions.
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ notes
-----

* `s390x` is broken at the moment.
* `core20` & `core22` builds do not support `i386` architecture because Ubuntu has dropped support for `i386` in Ubuntu 20.04 and later.
* Builds for `core20`, and later, do not support `i386` architecture because Ubuntu has dropped support for `i386` in Ubuntu 20.04 and later.
* `core` builds do not support `s390x` architecture because Ubuntu does not have support for `s390x` before Ubuntu 18.04.

## Action inputs
Expand Down Expand Up @@ -143,3 +143,27 @@ to indicate an alternative architecture from any of those supported by
the `snapcraft` utility. At the time of writing the supported
architectures are `amd64`, `i386`, `arm64`, `armhf`, `ppc64el` and `s390x`.
This is most-useful when used with GitHub Actions' `matrix` feature.

### `environment`

Add environment variables to the Snapcraft build context. Each
variable needs to be specified on a separate line. For example:

```yaml
with:
environment: |
FOO=bar
BAZ=qux
```
### `store-auth`

Set the `SNAPCRAFT_STORE_CREDENTIALS` environment variable. This
is useful when using the `snapcraft push` command.

You should not save the token into the yaml file directly, but use
the GitHub Actions secrets feature:

```yaml
with:
store-auth: ${{ secrets.STORE_AUTH }}
```
173 changes: 154 additions & 19 deletions __tests__/build.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as exec from '@actions/exec'
import * as build from '../src/build'
import * as tools from '../src/tools'

const default_base = 'core20'
const default_base = 'core22'

afterEach(() => {
jest.restoreAllMocks()
Expand All @@ -21,8 +21,9 @@ test('SnapcraftBuilder expands tilde in project root', () => {
'stable',
'',
'',
'',
false
[],
false,
''
)
expect(builder.projectRoot).toBe(os.homedir())

Expand All @@ -32,8 +33,9 @@ test('SnapcraftBuilder expands tilde in project root', () => {
'stable',
'',
'',
'',
false
[],
false,
''
)
expect(builder.projectRoot).toBe(path.join(os.homedir(), 'foo/bar'))
})
Expand Down Expand Up @@ -65,7 +67,7 @@ for (const base of ['core', 'core18', 'core20', 'core22']) {
}
for (const [base, arch, channel] of matrix) {
test(`SnapcraftBuilder.build runs a snap build using Docker with base: ${base}; and arch: ${arch}`, async () => {
expect.assertions(3)
expect.assertions(4)

const ensureDockerExperimentalMock = jest
.spyOn(tools, 'ensureDockerExperimental')
Expand All @@ -75,6 +77,9 @@ for (const [base, arch, channel] of matrix) {
.mockImplementation(
async (projectRoot: string): Promise<string> => Promise.resolve(base)
)
const detectCGroupsV1Mock = jest
.spyOn(tools, 'detectCGroupsV1')
.mockImplementation(async (): Promise<boolean> => Promise.resolve(true))
const execMock = jest
.spyOn(exec, 'exec')
.mockImplementation(
Expand All @@ -90,8 +95,9 @@ for (const [base, arch, channel] of matrix) {
'stable',
'',
arch,
'',
false
[],
false,
''
)
await builder.build()

Expand All @@ -102,6 +108,11 @@ for (const [base, arch, channel] of matrix) {

expect(ensureDockerExperimentalMock).toHaveBeenCalled()
expect(detectBaseMock).toHaveBeenCalled()
if (base === 'core') {
expect(detectCGroupsV1Mock).toHaveBeenCalled()
} else {
expect(detectCGroupsV1Mock).not.toHaveBeenCalled()
}
expect(execMock).toHaveBeenCalledWith(
'docker',
[
Expand Down Expand Up @@ -130,8 +141,7 @@ for (const [base, arch, channel] of matrix) {
})

test(`SnapcraftBuilder.build runs a snap build using Podman with base: ${base}; and arch: ${arch}`, async () => {
expect.assertions(3)

expect.assertions(4)
const ensureDockerExperimentalMock = jest
.spyOn(tools, 'ensureDockerExperimental')
.mockImplementation(async (): Promise<void> => Promise.resolve())
Expand All @@ -140,6 +150,9 @@ for (const [base, arch, channel] of matrix) {
.mockImplementation(
async (projectRoot: string): Promise<string> => Promise.resolve(base)
)
const detectCGroupsV1Mock = jest
.spyOn(tools, 'detectCGroupsV1')
.mockImplementation(async (): Promise<boolean> => Promise.resolve(true))
const execMock = jest
.spyOn(exec, 'exec')
.mockImplementation(
Expand All @@ -155,13 +168,19 @@ for (const [base, arch, channel] of matrix) {
'stable',
'',
arch,
'',
true
[],
true,
''
)
await builder.build()

expect(ensureDockerExperimentalMock).not.toHaveBeenCalled()
expect(detectBaseMock).toHaveBeenCalled()
if (base === 'core') {
expect(detectCGroupsV1Mock).toHaveBeenCalled()
} else {
expect(detectCGroupsV1Mock).not.toHaveBeenCalled()
}
expect(execMock).toHaveBeenCalledWith(
'sudo podman',
[
Expand Down Expand Up @@ -214,8 +233,9 @@ test('SnapcraftBuilder.build can disable build info', async () => {
'stable',
'',
'',
'',
false
[],
false,
''
)
await builder.build()

Expand Down Expand Up @@ -266,8 +286,62 @@ test('SnapcraftBuilder.build can pass additional arguments', async () => {
'stable',
'--foo --bar',
'',
[],
false,
''
)
await builder.build()

expect(execMock).toHaveBeenCalledWith(
'docker',
[
'run',
'--rm',
'--tty',
'--privileged',
'--volume',
`${process.cwd()}:/data`,
'--workdir',
'/data',
'--env',
`SNAPCRAFT_IMAGE_INFO={"build_url":"https://github.com/user/repo/actions/runs/42"}`,
'--env',
'USE_SNAPCRAFT_CHANNEL=stable',
`diddledani/snapcraft:${default_base}`,
'snapcraft',
'--foo',
'--bar'
],
expect.anything()
)
})

test('SnapcraftBuilder.build can pass extra environment variables', async () => {
expect.assertions(1)

const ensureDockerExperimentalMock = jest
.spyOn(tools, 'ensureDockerExperimental')
.mockImplementation(async (): Promise<void> => Promise.resolve())
const detectBaseMock = jest
.spyOn(tools, 'detectBase')
.mockImplementation(
async (projectRoot: string): Promise<string> => default_base
)
const execMock = jest
.spyOn(exec, 'exec')
.mockImplementation(
async (program: string, args?: string[]): Promise<number> => 0
)

const builder = new build.SnapcraftBuilder(
'.',
false,
'stable',
'--foo --bar',
'',
false
['FOO=bar', 'BAZ=qux'],
false,
''
)
await builder.build()

Expand All @@ -283,6 +357,10 @@ test('SnapcraftBuilder.build can pass additional arguments', async () => {
'--workdir',
'/data',
'--env',
'FOO=bar',
'--env',
'BAZ=qux',
'--env',
`SNAPCRAFT_IMAGE_INFO={"build_url":"https://github.com/user/repo/actions/runs/42"}`,
'--env',
'USE_SNAPCRAFT_CHANNEL=stable',
Expand All @@ -295,6 +373,61 @@ test('SnapcraftBuilder.build can pass additional arguments', async () => {
)
})

test('SnapcraftBuilder.build adds store credentials', async () => {
expect.assertions(1)

const ensureDockerExperimentalMock = jest
.spyOn(tools, 'ensureDockerExperimental')
.mockImplementation(async (): Promise<void> => Promise.resolve())
const detectBaseMock = jest
.spyOn(tools, 'detectBase')
.mockImplementation(
async (projectRoot: string): Promise<string> => default_base
)
const execMock = jest
.spyOn(exec, 'exec')
.mockImplementation(
async (program: string, args?: string[]): Promise<number> => 0
)

const builder = new build.SnapcraftBuilder(
'.',
false,
'stable',
'--foo --bar',
'',
[],
false,
'TEST_STORE_CREDENTIALS'
)
await builder.build()

expect(execMock).toHaveBeenCalledWith(
'docker',
[
'run',
'--rm',
'--tty',
'--privileged',
'--volume',
`${process.cwd()}:/data`,
'--workdir',
'/data',
'--env',
`SNAPCRAFT_IMAGE_INFO={"build_url":"https://github.com/user/repo/actions/runs/42"}`,
'--env',
'USE_SNAPCRAFT_CHANNEL=stable',
'--env',
'SNAPCRAFT_STORE_CREDENTIALS=TEST_STORE_CREDENTIALS',
`diddledani/snapcraft:${default_base}`,
'snapcraft',
'--foo',
'--bar'
],
expect.anything()
)
})

test('SnapcraftBuilder.outputSnap fails if there are no snaps', async () => {
expect.assertions(2)

Expand All @@ -305,8 +438,9 @@ test('SnapcraftBuilder.outputSnap fails if there are no snaps', async () => {
'stable',
'',
'',
'',
false
[],
false,
''
)

const readdir = jest
Expand All @@ -331,8 +465,9 @@ test('SnapcraftBuilder.outputSnap returns the first snap', async () => {
'stable',
'',
'',
'',
false
[],
false,
''
)

const readdir = jest
Expand Down
19 changes: 19 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,25 @@ inputs:
combine this with the build matrix feature of GitHub Actions.
default: 'amd64'
required: true
environment:
description: >
Environment to pass to Snapcraft
Add environment variables to the Snapcraft build context. Each
variable needs to be specified on a separate line. For example:
environment: |
FOO=bar
BAZ=qux
required: false
store-auth:
description: >
The Snap Store authentication token
This token is used to authenticate with the Snap Store when
uploading the snap. It can be obtained by running `snapcraft
export-login --snaps <snap-name> -` and copying the output.
required: false
outputs:
snap:
description: 'The file name of the resulting snap.'
Expand Down
Loading

0 comments on commit bd4fba8

Please sign in to comment.