diff --git a/__tests__/buildx/build.test.ts b/__tests__/buildx/build.test.ts index 66593664..c21c5f4a 100644 --- a/__tests__/buildx/build.test.ts +++ b/__tests__/buildx/build.test.ts @@ -23,6 +23,8 @@ import * as rimraf from 'rimraf'; import {Context} from '../../src/context'; import {Build} from '../../src/buildx/build'; +import {GitContextFormat} from '../../src/types/buildx/build'; + const fixturesDir = path.join(__dirname, '..', '.fixtures'); const tmpDir = fs.mkdtempSync(path.join(process.env.TEMP || os.tmpdir(), 'buildx-build-')); const tmpName = path.join(tmpDir, '.tmpname-jest'); @@ -41,6 +43,45 @@ afterEach(() => { rimraf.sync(tmpDir); }); +describe('gitContext', () => { + const originalEnv = process.env; + beforeEach(() => { + jest.resetModules(); + process.env = { + ...originalEnv, + DOCKER_GIT_CONTEXT_PR_HEAD_REF: '' + }; + }); + afterEach(() => { + process.env = originalEnv; + }); + // prettier-ignore + test.each([ + // no format set + ['refs/heads/master', undefined, false, 'https://github.com/docker/actions-toolkit.git?ref=refs/heads/master&checksum=860c1904a1ce19322e91ac35af1ab07466440c37'], + ['master', undefined, false, 'https://github.com/docker/actions-toolkit.git?ref=refs/heads/master&checksum=860c1904a1ce19322e91ac35af1ab07466440c37'], + ['refs/pull/15/merge', undefined, false, 'https://github.com/docker/actions-toolkit.git?ref=refs/pull/15/merge&checksum=860c1904a1ce19322e91ac35af1ab07466440c37'], + ['refs/tags/v1.0.0', undefined, false, 'https://github.com/docker/actions-toolkit.git?ref=refs/tags/v1.0.0&checksum=860c1904a1ce19322e91ac35af1ab07466440c37'], + ['refs/pull/15/merge', undefined, true, 'https://github.com/docker/actions-toolkit.git?ref=refs/pull/15/head&checksum=860c1904a1ce19322e91ac35af1ab07466440c37'], + // query format + ['refs/heads/master', 'query', false, 'https://github.com/docker/actions-toolkit.git?ref=refs/heads/master&checksum=860c1904a1ce19322e91ac35af1ab07466440c37'], + ['master', 'query', false, 'https://github.com/docker/actions-toolkit.git?ref=refs/heads/master&checksum=860c1904a1ce19322e91ac35af1ab07466440c37'], + ['refs/pull/15/merge', 'query', false, 'https://github.com/docker/actions-toolkit.git?ref=refs/pull/15/merge&checksum=860c1904a1ce19322e91ac35af1ab07466440c37'], + ['refs/tags/v1.0.0', 'query', false, 'https://github.com/docker/actions-toolkit.git?ref=refs/tags/v1.0.0&checksum=860c1904a1ce19322e91ac35af1ab07466440c37'], + ['refs/pull/15/merge', 'query', true, 'https://github.com/docker/actions-toolkit.git?ref=refs/pull/15/head&checksum=860c1904a1ce19322e91ac35af1ab07466440c37'], + // fragment format + ['refs/heads/master', 'fragment', false, 'https://github.com/docker/actions-toolkit.git#860c1904a1ce19322e91ac35af1ab07466440c37'], + ['master', 'fragment', false, 'https://github.com/docker/actions-toolkit.git#860c1904a1ce19322e91ac35af1ab07466440c37'], + ['refs/pull/15/merge', 'fragment', false, 'https://github.com/docker/actions-toolkit.git#refs/pull/15/merge'], + ['refs/tags/v1.0.0', 'fragment', false, 'https://github.com/docker/actions-toolkit.git#860c1904a1ce19322e91ac35af1ab07466440c37'], + ['refs/pull/15/merge', 'fragment', true, 'https://github.com/docker/actions-toolkit.git#refs/pull/15/head'], + ])('given %p and %p, should return %p', async (ref: string, format: string | undefined, prHeadRef: boolean, expected: string) => { + process.env.DOCKER_DEFAULT_GIT_CONTEXT_PR_HEAD_REF = prHeadRef ? 'true' : ''; + const build = new Build(); + expect(await build.gitContext(ref, '860c1904a1ce19322e91ac35af1ab07466440c37', format as GitContextFormat)).toEqual(expected); + }); +}); + describe('resolveImageID', () => { it('matches', async () => { const imageID = 'sha256:bfb45ab72e46908183546477a08f8867fc40cebadd00af54b071b097aed127a9'; diff --git a/__tests__/context.test.ts b/__tests__/context.test.ts deleted file mode 100644 index 98bfcf6a..00000000 --- a/__tests__/context.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright 2023 actions-toolkit authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import {describe, expect, jest, it, afterEach, beforeEach, test} from '@jest/globals'; -import fs from 'fs'; -import os from 'os'; -import path from 'path'; -import * as rimraf from 'rimraf'; - -import {Context} from '../src/context'; - -const tmpDir = fs.mkdtempSync(path.join(process.env.TEMP || os.tmpdir(), 'context-')); -const tmpName = path.join(tmpDir, '.tmpname-jest'); - -jest.spyOn(Context, 'tmpDir').mockImplementation((): string => { - fs.mkdirSync(tmpDir, {recursive: true}); - return tmpDir; -}); - -jest.spyOn(Context, 'tmpName').mockImplementation((): string => { - return tmpName; -}); - -afterEach(() => { - rimraf.sync(tmpDir); -}); - -describe('gitRef', () => { - it('returns refs/heads/master', async () => { - expect(Context.gitRef()).toEqual('refs/heads/master'); - }); -}); - -describe('parseGitRef', () => { - const originalEnv = process.env; - beforeEach(() => { - jest.resetModules(); - process.env = { - ...originalEnv, - DOCKER_GIT_CONTEXT_PR_HEAD_REF: '' - }; - }); - afterEach(() => { - process.env = originalEnv; - }); - // prettier-ignore - test.each([ - ['refs/heads/master', '860c1904a1ce19322e91ac35af1ab07466440c37', false, '860c1904a1ce19322e91ac35af1ab07466440c37'], - ['master', '860c1904a1ce19322e91ac35af1ab07466440c37', false, '860c1904a1ce19322e91ac35af1ab07466440c37'], - ['refs/pull/15/merge', '860c1904a1ce19322e91ac35af1ab07466440c37', false, 'refs/pull/15/merge'], - ['refs/heads/master', '', false, 'refs/heads/master'], - ['master', '', false, 'master'], - ['refs/tags/v1.0.0', '', false, 'refs/tags/v1.0.0'], - ['refs/pull/15/merge', '', false, 'refs/pull/15/merge'], - ['refs/pull/15/merge', '', true, 'refs/pull/15/head'], - ])('given %p and %p, should return %p', async (ref: string, sha: string, prHeadRef: boolean, expected: string) => { - process.env.DOCKER_DEFAULT_GIT_CONTEXT_PR_HEAD_REF = prHeadRef ? 'true' : ''; - expect(Context.parseGitRef(ref, sha)).toEqual(expected); - }); -}); - -describe('gitContext', () => { - it('returns refs/heads/master', async () => { - expect(Context.gitContext()).toEqual('https://github.com/docker/actions-toolkit.git#refs/heads/master'); - }); -}); diff --git a/src/buildx/build.ts b/src/buildx/build.ts index 65c8b33b..35d89412 100644 --- a/src/buildx/build.ts +++ b/src/buildx/build.ts @@ -17,6 +17,7 @@ import fs from 'fs'; import path from 'path'; import * as core from '@actions/core'; +import * as github from '@actions/github'; import {parse} from 'csv-parse/sync'; import {Buildx} from './buildx'; @@ -24,7 +25,7 @@ import {Context} from '../context'; import {GitHub} from '../github'; import {Util} from '../util'; -import {BuildMetadata} from '../types/buildx/build'; +import {BuildMetadata, GitContextFormat} from '../types/buildx/build'; import {VertexWarning} from '../types/buildkit/client'; import {ProvenancePredicate} from '../types/intoto/slsa_provenance/v0.2/provenance'; @@ -48,6 +49,32 @@ export class Build { this.metadataFilename = `build-metadata-${Util.generateRandomString()}.json`; } + public async gitContext(ref?: string, sha?: string, format?: GitContextFormat): Promise { + const setPullRequestHeadRef: boolean = !!(process.env.DOCKER_DEFAULT_GIT_CONTEXT_PR_HEAD_REF && process.env.DOCKER_DEFAULT_GIT_CONTEXT_PR_HEAD_REF === 'true'); + ref = ref || github.context.ref; + sha = sha || github.context.sha; + if (!ref.startsWith('refs/')) { + ref = `refs/heads/${ref}`; + } else if (ref.startsWith(`refs/pull/`) && setPullRequestHeadRef) { + ref = ref.replace(/\/merge$/g, '/head'); + } + const baseURL = `${GitHub.serverURL}/${github.context.repo.owner}/${github.context.repo.repo}.git`; + if (!format) { + if (await this.buildx.versionSatisfies('>=0.29.0')) { + format = 'query'; + } else { + format = 'fragment'; + } + } + if (format === 'query') { + return `${baseURL}?ref=${ref}${sha !== '' ? `&checksum=${sha}` : ''}`; + } + if (sha && !ref.startsWith(`refs/pull/`)) { + return `${baseURL}#${sha}`; + } + return `${baseURL}#${ref}`; + } + public getImageIDFilePath(): string { return path.join(Context.tmpDir(), this.iidFilename); } diff --git a/src/context.ts b/src/context.ts index 502bb3e9..8b583cd9 100644 --- a/src/context.ts +++ b/src/context.ts @@ -18,9 +18,6 @@ import fs from 'fs'; import os from 'os'; import path from 'path'; import * as tmp from 'tmp'; -import * as github from '@actions/github'; - -import {GitHub} from './github'; export class Context { private static readonly _tmpDir = fs.mkdtempSync(path.join(Context.ensureDirExists(process.env.RUNNER_TEMP || os.tmpdir()), 'docker-actions-toolkit-')); @@ -37,25 +34,4 @@ export class Context { public static tmpName(options?: tmp.TmpNameOptions): string { return tmp.tmpNameSync(options); } - - public static gitRef(): string { - return Context.parseGitRef(github.context.ref, github.context.sha); - } - - public static parseGitRef(ref: string, sha: string): string { - const setPullRequestHeadRef: boolean = !!(process.env.DOCKER_DEFAULT_GIT_CONTEXT_PR_HEAD_REF && process.env.DOCKER_DEFAULT_GIT_CONTEXT_PR_HEAD_REF === 'true'); - if (sha && ref && !ref.startsWith('refs/')) { - ref = `refs/heads/${ref}`; - } - if (sha && !ref.startsWith(`refs/pull/`)) { - ref = sha; - } else if (ref.startsWith(`refs/pull/`) && setPullRequestHeadRef) { - ref = ref.replace(/\/merge$/g, '/head'); - } - return ref; - } - - public static gitContext(): string { - return `${GitHub.serverURL}/${github.context.repo.owner}/${github.context.repo.repo}.git#${Context.gitRef()}`; - } } diff --git a/src/types/buildx/build.ts b/src/types/buildx/build.ts index e31470c2..d12214ec 100644 --- a/src/types/buildx/build.ts +++ b/src/types/buildx/build.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +export type GitContextFormat = 'fragment' | 'query'; + export type BuildMetadata = { // eslint-disable-next-line @typescript-eslint/no-explicit-any [key: string]: any;