From 6888b86df94810e2b2defd80f95993da12e9748c Mon Sep 17 00:00:00 2001 From: Cristovao Cordeiro Date: Tue, 14 Nov 2023 14:09:06 +0100 Subject: [PATCH 1/2] feat: allow revision pinning for rockcraft --- .github/workflows/test.yml | 10 ++++- dist/rockcraft-pack-action/index.js | 21 ++++++----- lib/tools.js | 6 +-- rockcraft-pack/action.yml | 6 +++ src/rockcraft-pack-action.ts | 4 +- src/rockcraft-pack.ts | 5 ++- src/tools.ts | 9 +++-- tests/rockcraft-pack.test.ts | 57 ++++++++++++++++++++++++----- tests/tools.test.ts | 20 ++++++++-- 9 files changed, 107 insertions(+), 31 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 70fbe09..b29126a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,11 +26,12 @@ jobs: run: npm test - name: Try to package code run: npm run pack - + run-rockcraft-pack-action: strategy: matrix: os: [ubuntu-22.04, ubuntu-20.04] + revision: ['1206', ''] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 @@ -40,6 +41,13 @@ jobs: with: path: tests verbosity: debug + revision: ${{ matrix.revision }} + - name: Assert revision number + if: matrix.revision != '' + run: | + set -ex + installed_rev="$(snap info rockcraft | tail -1 | awk '{print $(NF-2)}')" + [ "$installed_rev" == "(${{ matrix.revision }})" ] - name: Upload ROCK uses: actions/upload-artifact@v3 with: diff --git a/dist/rockcraft-pack-action/index.js b/dist/rockcraft-pack-action/index.js index ac5d8eb..7025727 100644 --- a/dist/rockcraft-pack-action/index.js +++ b/dist/rockcraft-pack-action/index.js @@ -4133,7 +4133,7 @@ var external_fs_ = __nccwpck_require__(147); var external_path_ = __nccwpck_require__(17); // EXTERNAL MODULE: external "os" var external_os_ = __nccwpck_require__(37); -;// CONCATENATED MODULE: ./src/tools.ts +;// CONCATENATED MODULE: ./lib/tools.js // -*- mode: javascript; js-indent-level: 2 -*- @@ -4219,20 +4219,20 @@ async function ensureLXD() { await exec.exec('sudo', ['lxd', 'init', '--auto']); await ensureLXDNetwork(); } -async function ensureRockcraft(channel) { +async function ensureRockcraft(channel, revision) { const haveRockcraft = await haveExecutable('/snap/bin/rockcraft'); core.info('Installing Rockcraft...'); await exec.exec('sudo', [ 'snap', haveRockcraft ? 'refresh' : 'install', - '--channel', - channel, + revision.length > 0 ? '--revision' : '--channel', + revision.length > 0 ? revision : channel, '--classic', 'rockcraft' ]); } -;// CONCATENATED MODULE: ./src/rockcraft-pack.ts +;// CONCATENATED MODULE: ./lib/rockcraft-pack.js // -*- mode: javascript; js-indent-level: 2 -*- @@ -4244,6 +4244,7 @@ class RockcraftBuilder { constructor(options) { this.projectRoot = expandHome(options.projectRoot); this.rockcraftChannel = options.rockcraftChannel; + this.rockcraftRevision = options.rockcraftRevision; if (allowedVerbosity.includes(options.rockcraftPackVerbosity)) { this.rockcraftPackVerbosity = options.rockcraftPackVerbosity; } @@ -4256,7 +4257,7 @@ class RockcraftBuilder { core.startGroup('Installing Rockcraft plus dependencies'); await ensureSnapd(); await ensureLXD(); - await ensureRockcraft(this.rockcraftChannel); + await ensureRockcraft(this.rockcraftChannel, this.rockcraftRevision); core.endGroup(); let rockcraft = 'rockcraft pack'; let rockcraftPackArgs = ''; @@ -4286,7 +4287,7 @@ class RockcraftBuilder { } } -;// CONCATENATED MODULE: ./src/rockcraft-pack-action.ts +;// CONCATENATED MODULE: ./lib/rockcraft-pack-action.js // -*- mode: javascript; js-indent-level: 2 -*- @@ -4294,12 +4295,14 @@ async function run() { try { const projectRoot = core.getInput('path'); core.info(`Building ROCK in "${projectRoot}"...`); - const rockcraftChannel = core.getInput('rockcraft-channel') || 'edge'; + const rockcraftChannel = core.getInput('rockcraft-channel') || 'stable'; const rockcraftPackVerbosity = core.getInput('verbosity'); + const rockcraftRevision = core.getInput('revision'); const builder = new RockcraftBuilder({ projectRoot, rockcraftChannel, - rockcraftPackVerbosity + rockcraftPackVerbosity, + rockcraftRevision }); await builder.pack(); const rock = await builder.outputRock(); diff --git a/lib/tools.js b/lib/tools.js index fbb7dff..ba07713 100644 --- a/lib/tools.js +++ b/lib/tools.js @@ -83,14 +83,14 @@ export async function ensureLXD() { await exec.exec('sudo', ['lxd', 'init', '--auto']); await ensureLXDNetwork(); } -export async function ensureRockcraft(channel) { +export async function ensureRockcraft(channel, revision) { const haveRockcraft = await haveExecutable('/snap/bin/rockcraft'); core.info('Installing Rockcraft...'); await exec.exec('sudo', [ 'snap', haveRockcraft ? 'refresh' : 'install', - '--channel', - channel, + revision.length > 0 ? '--revision' : '--channel', + revision.length > 0 ? revision : channel, '--classic', 'rockcraft' ]); diff --git a/rockcraft-pack/action.yml b/rockcraft-pack/action.yml index 5703f8e..7745acf 100644 --- a/rockcraft-pack/action.yml +++ b/rockcraft-pack/action.yml @@ -14,6 +14,12 @@ inputs: The default is 'trace'. default: 'trace' + revision: + description: > + Pin the snap revision to install. + + If not provided, it defaults to whatever revision is in latest/stable. + default: '' outputs: rock: description: 'The file name of the resulting ROCK.' diff --git a/src/rockcraft-pack-action.ts b/src/rockcraft-pack-action.ts index c981933..1731f77 100644 --- a/src/rockcraft-pack-action.ts +++ b/src/rockcraft-pack-action.ts @@ -9,11 +9,13 @@ async function run(): Promise { core.info(`Building ROCK in "${projectRoot}"...`) const rockcraftChannel = core.getInput('rockcraft-channel') || 'stable' const rockcraftPackVerbosity = core.getInput('verbosity') + const rockcraftRevision = core.getInput('revision') const builder = new RockcraftBuilder({ projectRoot, rockcraftChannel, - rockcraftPackVerbosity + rockcraftPackVerbosity, + rockcraftRevision }) await builder.pack() const rock = await builder.outputRock() diff --git a/src/rockcraft-pack.ts b/src/rockcraft-pack.ts index 4d011fa..ce2c68d 100644 --- a/src/rockcraft-pack.ts +++ b/src/rockcraft-pack.ts @@ -12,16 +12,19 @@ interface RockcraftBuilderOptions { projectRoot: string rockcraftChannel: string rockcraftPackVerbosity: string + rockcraftRevision: string } export class RockcraftBuilder { projectRoot: string rockcraftChannel: string rockcraftPackVerbosity: string + rockcraftRevision: string constructor(options: RockcraftBuilderOptions) { this.projectRoot = tools.expandHome(options.projectRoot) this.rockcraftChannel = options.rockcraftChannel + this.rockcraftRevision = options.rockcraftRevision if (allowedVerbosity.includes(options.rockcraftPackVerbosity)) { this.rockcraftPackVerbosity = options.rockcraftPackVerbosity } else { @@ -36,7 +39,7 @@ export class RockcraftBuilder { core.startGroup('Installing Rockcraft plus dependencies') await tools.ensureSnapd() await tools.ensureLXD() - await tools.ensureRockcraft(this.rockcraftChannel) + await tools.ensureRockcraft(this.rockcraftChannel, this.rockcraftRevision) core.endGroup() let rockcraft = 'rockcraft pack' diff --git a/src/tools.ts b/src/tools.ts index 4b02ba1..db949ab 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -94,14 +94,17 @@ export async function ensureLXD(): Promise { await ensureLXDNetwork() } -export async function ensureRockcraft(channel: string): Promise { +export async function ensureRockcraft( + channel: string, + revision: string +): Promise { const haveRockcraft = await haveExecutable('/snap/bin/rockcraft') core.info('Installing Rockcraft...') await exec.exec('sudo', [ 'snap', haveRockcraft ? 'refresh' : 'install', - '--channel', - channel, + revision.length > 0 ? '--revision' : '--channel', + revision.length > 0 ? revision : channel, '--classic', 'rockcraft' ]) diff --git a/tests/rockcraft-pack.test.ts b/tests/rockcraft-pack.test.ts index 97f97d9..e95d9d4 100644 --- a/tests/rockcraft-pack.test.ts +++ b/tests/rockcraft-pack.test.ts @@ -14,14 +14,16 @@ test('RockcraftBuilder expands tilde in project root', () => { let builder = new build.RockcraftBuilder({ projectRoot: '~', rockcraftChannel: 'edge', - rockcraftPackVerbosity: 'trace' + rockcraftPackVerbosity: 'trace', + rockcraftRevision: '1' }) expect(builder.projectRoot).toBe(os.homedir()) builder = new build.RockcraftBuilder({ projectRoot: '~/foo/bar', rockcraftChannel: 'stable', - rockcraftPackVerbosity: 'trace' + rockcraftPackVerbosity: 'trace', + rockcraftRevision: '1' }) expect(builder.projectRoot).toBe(path.join(os.homedir(), 'foo/bar')) }) @@ -50,7 +52,8 @@ test('RockcraftBuilder.pack runs a ROCK build', async () => { const builder = new build.RockcraftBuilder({ projectRoot: projectDir, rockcraftChannel: 'stable', - rockcraftPackVerbosity: 'debug' + rockcraftPackVerbosity: 'debug', + rockcraftRevision: '1' }) await builder.pack() @@ -89,11 +92,43 @@ test('RockcraftBuilder.build can set the Rockcraft channel', async () => { const builder = new build.RockcraftBuilder({ projectRoot: '.', rockcraftChannel: 'test-channel', - rockcraftPackVerbosity: 'trace' + rockcraftPackVerbosity: 'trace', + rockcraftRevision: '' }) await builder.pack() - expect(ensureRockcraft).toHaveBeenCalledWith('test-channel') + expect(ensureRockcraft).toHaveBeenCalledWith('test-channel', '') +}) + +test('RockcraftBuilder.build can set the Rockcraft revision', async () => { + expect.assertions(1) + + const ensureSnapd = jest + .spyOn(tools, 'ensureSnapd') + .mockImplementation(async (): Promise => {}) + const ensureLXD = jest + .spyOn(tools, 'ensureLXD') + .mockImplementation(async (): Promise => {}) + const ensureRockcraft = jest + .spyOn(tools, 'ensureRockcraft') + .mockImplementation(async (channel): Promise => {}) + const execMock = jest + .spyOn(exec, 'exec') + .mockImplementation( + async (program: string, args?: string[]): Promise => { + return 0 + } + ) + + const builder = new build.RockcraftBuilder({ + projectRoot: '.', + rockcraftChannel: 'channel', + rockcraftPackVerbosity: 'trace', + rockcraftRevision: '123' + }) + await builder.pack() + + expect(ensureRockcraft).toHaveBeenCalledWith('channel', '123') }) test('RockcraftBuilder.build can pass known verbosity', async () => { @@ -119,7 +154,8 @@ test('RockcraftBuilder.build can pass known verbosity', async () => { const builder = new build.RockcraftBuilder({ projectRoot: '.', rockcraftChannel: 'stable', - rockcraftPackVerbosity: 'trace' + rockcraftPackVerbosity: 'trace', + rockcraftRevision: '1' }) await builder.pack() @@ -133,7 +169,8 @@ test('RockcraftBuilder.build can pass known verbosity', async () => { new build.RockcraftBuilder({ projectRoot: '.', rockcraftChannel: 'stable', - rockcraftPackVerbosity: 'fake-verbosity' + rockcraftPackVerbosity: 'fake-verbosity', + rockcraftRevision: '1' }) } expect(badBuilder).toThrowError() @@ -146,7 +183,8 @@ test('RockcraftBuilder.outputRock fails if there are no ROCKs', async () => { const builder = new build.RockcraftBuilder({ projectRoot: projectDir, rockcraftChannel: 'stable', - rockcraftPackVerbosity: 'trace' + rockcraftPackVerbosity: 'trace', + rockcraftRevision: '1' }) const readdir = jest @@ -168,7 +206,8 @@ test('RockcraftBuilder.outputRock returns the first ROCK', async () => { const builder = new build.RockcraftBuilder({ projectRoot: projectDir, rockcraftChannel: 'stable', - rockcraftPackVerbosity: 'trace' + rockcraftPackVerbosity: 'trace', + rockcraftRevision: '1' }) const readdir = jest diff --git a/tests/tools.test.ts b/tests/tools.test.ts index 1ab60de..1ec75e8 100644 --- a/tests/tools.test.ts +++ b/tests/tools.test.ts @@ -254,8 +254,8 @@ test('ensureLXD still calls "lxd init" if LXD is installed', async () => { expect(execMock).toHaveBeenNthCalledWith(4, 'sudo', ['lxd', 'init', '--auto']) }) -test('ensureSnapcraft installs Snapcraft if needed', async () => { - expect.assertions(2) +test('ensureRockcraft installs Rockcraft if needed', async () => { + expect.assertions(4) const accessMock = jest .spyOn(fs.promises, 'access') @@ -275,7 +275,7 @@ test('ensureSnapcraft installs Snapcraft if needed', async () => { } ) - await tools.ensureRockcraft('edge') + await tools.ensureRockcraft('edge', '') expect(accessMock).toHaveBeenCalled() expect(execMock).toHaveBeenNthCalledWith(1, 'sudo', [ @@ -286,6 +286,18 @@ test('ensureSnapcraft installs Snapcraft if needed', async () => { '--classic', 'rockcraft' ]) + + await tools.ensureRockcraft('stable', '1234') + + expect(accessMock).toHaveBeenCalled() + expect(execMock).toHaveBeenNthCalledWith(2, 'sudo', [ + 'snap', + 'install', + '--revision', + '1234', + '--classic', + 'rockcraft' + ]) }) test('ensureRockcraft refreshes if Rockcraft is installed', async () => { @@ -309,7 +321,7 @@ test('ensureRockcraft refreshes if Rockcraft is installed', async () => { } ) - await tools.ensureRockcraft('edge') + await tools.ensureRockcraft('edge', '') expect(accessMock).toHaveBeenCalled() expect(execMock).toHaveBeenNthCalledWith(1, 'sudo', [ From d9c1181a4a201ed841e773f1ebb5a18825ffaa71 Mon Sep 17 00:00:00 2001 From: Cristovao Cordeiro Date: Tue, 12 Dec 2023 14:45:59 +0100 Subject: [PATCH 2/2] feat: issue warn msg when revision is not used --- .github/workflows/test.yml | 2 +- dist/rockcraft-pack-action/index.js | 5 ++++- src/rockcraft-pack-action.ts | 7 ++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b29126a..ca94e92 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,7 +26,7 @@ jobs: run: npm test - name: Try to package code run: npm run pack - + run-rockcraft-pack-action: strategy: matrix: diff --git a/dist/rockcraft-pack-action/index.js b/dist/rockcraft-pack-action/index.js index 7025727..573599b 100644 --- a/dist/rockcraft-pack-action/index.js +++ b/dist/rockcraft-pack-action/index.js @@ -4295,9 +4295,12 @@ async function run() { try { const projectRoot = core.getInput('path'); core.info(`Building ROCK in "${projectRoot}"...`); + const rockcraftRevision = core.getInput('revision'); const rockcraftChannel = core.getInput('rockcraft-channel') || 'stable'; + if (rockcraftRevision.length < 1) { + core.warning(`Rockcraft revision not provided. Installing from ${rockcraftChannel}`); + } const rockcraftPackVerbosity = core.getInput('verbosity'); - const rockcraftRevision = core.getInput('revision'); const builder = new RockcraftBuilder({ projectRoot, rockcraftChannel, diff --git a/src/rockcraft-pack-action.ts b/src/rockcraft-pack-action.ts index 1731f77..7651b44 100644 --- a/src/rockcraft-pack-action.ts +++ b/src/rockcraft-pack-action.ts @@ -7,9 +7,14 @@ async function run(): Promise { try { const projectRoot = core.getInput('path') core.info(`Building ROCK in "${projectRoot}"...`) + const rockcraftRevision = core.getInput('revision') const rockcraftChannel = core.getInput('rockcraft-channel') || 'stable' + if (rockcraftRevision.length < 1) { + core.warning( + `Rockcraft revision not provided. Installing from ${rockcraftChannel}` + ) + } const rockcraftPackVerbosity = core.getInput('verbosity') - const rockcraftRevision = core.getInput('revision') const builder = new RockcraftBuilder({ projectRoot,