Skip to content

Commit 8e54dfe

Browse files
committed
buildx(build): stabilize pull request git contexts
Signed-off-by: CrazyMax <[email protected]>
1 parent d046dce commit 8e54dfe

2 files changed

Lines changed: 52 additions & 28 deletions

File tree

__tests__/buildx/build.test.ts

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {afterEach, beforeEach, describe, expect, it, vi, test} from 'vitest';
1818
import fs from 'fs';
1919
import os from 'os';
2020
import path from 'path';
21+
import * as github from '@actions/github';
2122
import * as rimraf from 'rimraf';
2223

2324
import {Context} from '../../src/context.js';
@@ -46,16 +47,26 @@ afterEach(() => {
4647

4748
describe('gitContext', () => {
4849
const originalEnv = process.env;
50+
const githubContextSha = '860c1904a1ce19322e91ac35af1ab07466440c37';
51+
const pullRequestHeadSha = 'f11797113e5a9b86bd976329c5dbb8a8bfdfadfa';
4952
beforeEach(() => {
5053
vi.resetModules();
5154
process.env = {
5255
...originalEnv,
5356
DOCKER_DEFAULT_GIT_CONTEXT_PR_HEAD_REF: '',
5457
BUILDX_SEND_GIT_QUERY_AS_INPUT: ''
5558
};
59+
github.context.sha = githubContextSha;
60+
github.context.payload.pull_request = {
61+
number: 15,
62+
head: {
63+
sha: pullRequestHeadSha
64+
}
65+
};
5666
});
5767
afterEach(() => {
5868
process.env = originalEnv;
69+
delete github.context.payload.pull_request;
5970
});
6071

6172
type GitContextTestCase = {
@@ -75,13 +86,13 @@ describe('gitContext', () => {
7586
// no format set (defaults to fragment)
7687
[{ref: 'refs/heads/master', format: undefined, prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git#860c1904a1ce19322e91ac35af1ab07466440c37'],
7788
[{ref: 'master', format: undefined, prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git#860c1904a1ce19322e91ac35af1ab07466440c37'],
78-
[{ref: 'refs/pull/15/merge', format: undefined, prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git#refs/pull/15/merge'],
89+
[{ref: 'refs/pull/15/merge', checksum: undefined, format: undefined, prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git#860c1904a1ce19322e91ac35af1ab07466440c37'],
7990
[{ref: 'refs/tags/v1.0.0', format: undefined, prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git#860c1904a1ce19322e91ac35af1ab07466440c37'],
80-
[{ref: 'refs/pull/15/merge', format: undefined, prHeadRef: true, sendGitQueryAsInput: false, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git#refs/pull/15/head'],
91+
[{ref: 'refs/pull/15/merge', checksum: undefined, format: undefined, prHeadRef: true, sendGitQueryAsInput: false, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git#f11797113e5a9b86bd976329c5dbb8a8bfdfadfa'],
8192
// no format set (defaults to query only when client-side query resolution is enabled and supported)
8293
[{ref: 'refs/heads/master', format: undefined, prHeadRef: false, sendGitQueryAsInput: true, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git?ref=refs/heads/master&checksum=860c1904a1ce19322e91ac35af1ab07466440c37'],
83-
[{ref: 'refs/pull/15/merge', format: undefined, prHeadRef: false, sendGitQueryAsInput: true, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git?ref=refs/pull/15/merge&checksum=860c1904a1ce19322e91ac35af1ab07466440c37'],
84-
[{ref: 'refs/pull/15/merge', format: undefined, prHeadRef: true, sendGitQueryAsInput: true, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git?ref=refs/pull/15/head&checksum=860c1904a1ce19322e91ac35af1ab07466440c37'],
94+
[{ref: 'refs/pull/15/merge', checksum: undefined, format: undefined, prHeadRef: false, sendGitQueryAsInput: true, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git?ref=860c1904a1ce19322e91ac35af1ab07466440c37'],
95+
[{ref: 'refs/pull/15/merge', checksum: undefined, format: undefined, prHeadRef: true, sendGitQueryAsInput: true, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git?ref=f11797113e5a9b86bd976329c5dbb8a8bfdfadfa'],
8596
[{ref: 'refs/heads/master', format: undefined, prHeadRef: false, sendGitQueryAsInput: true, buildxQuerySupport: false}, 'https://github.com/docker/actions-toolkit.git#860c1904a1ce19322e91ac35af1ab07466440c37'],
8697
[{ref: 'refs/heads/master', format: undefined, prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true, attrs: {}}, 'https://github.com/docker/actions-toolkit.git#860c1904a1ce19322e91ac35af1ab07466440c37'],
8798
[{ref: 'refs/heads/master', checksum: undefined, format: undefined, prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true, attrs: {checksum: 'cafebabe'}}, 'https://github.com/docker/actions-toolkit.git#cafebabe'],
@@ -95,9 +106,10 @@ describe('gitContext', () => {
95106
// query format
96107
[{ref: 'refs/heads/master', format: 'query', prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git?ref=refs/heads/master&checksum=860c1904a1ce19322e91ac35af1ab07466440c37'],
97108
[{ref: 'master', format: 'query', prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git?ref=refs/heads/master&checksum=860c1904a1ce19322e91ac35af1ab07466440c37'],
98-
[{ref: 'refs/pull/15/merge', format: 'query', prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git?ref=refs/pull/15/merge&checksum=860c1904a1ce19322e91ac35af1ab07466440c37'],
109+
[{ref: 'refs/pull/15/merge', checksum: undefined, format: 'query', prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git?ref=860c1904a1ce19322e91ac35af1ab07466440c37'],
99110
[{ref: 'refs/tags/v1.0.0', format: 'query', prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git?ref=refs/tags/v1.0.0&checksum=860c1904a1ce19322e91ac35af1ab07466440c37'],
100-
[{ref: 'refs/pull/15/merge', format: 'query', prHeadRef: true, sendGitQueryAsInput: false, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git?ref=refs/pull/15/head&checksum=860c1904a1ce19322e91ac35af1ab07466440c37'],
111+
[{ref: 'refs/pull/15/merge', checksum: undefined, format: 'query', prHeadRef: true, sendGitQueryAsInput: false, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git?ref=f11797113e5a9b86bd976329c5dbb8a8bfdfadfa'],
112+
[{ref: 'refs/pull/15/merge', checksum: undefined, format: 'query', prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true, attrs: {checksum: 'cafebabe'}}, 'https://github.com/docker/actions-toolkit.git?ref=refs/pull/15/merge&checksum=cafebabe'],
101113
[{ref: 'refs/heads/master', format: 'query', prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true, subdir: 'subdir'}, 'https://github.com/docker/actions-toolkit.git?ref=refs/heads/master&checksum=860c1904a1ce19322e91ac35af1ab07466440c37&subdir=subdir'],
102114
[{ref: 'refs/heads/master', format: 'query', prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true, subdir: '.'}, 'https://github.com/docker/actions-toolkit.git?ref=refs/heads/master&checksum=860c1904a1ce19322e91ac35af1ab07466440c37'],
103115
[{ref: 'refs/heads/master', checksum: undefined, format: 'query', prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true, attrs: {ref: 'refs/tags/v1.0.0', checksum: 'cafebabe', subdir: 'subdir', submodules: 'false'}}, 'https://github.com/docker/actions-toolkit.git?ref=refs/heads/master&checksum=cafebabe&subdir=subdir&submodules=false'],
@@ -108,14 +120,15 @@ describe('gitContext', () => {
108120
// fragment format
109121
[{ref: 'refs/heads/master', format: 'fragment', prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git#860c1904a1ce19322e91ac35af1ab07466440c37'],
110122
[{ref: 'master', format: 'fragment', prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git#860c1904a1ce19322e91ac35af1ab07466440c37'],
111-
[{ref: 'refs/pull/15/merge', format: 'fragment', prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git#refs/pull/15/merge'],
123+
[{ref: 'refs/pull/15/merge', checksum: undefined, format: 'fragment', prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git#860c1904a1ce19322e91ac35af1ab07466440c37'],
112124
[{ref: 'refs/tags/v1.0.0', format: 'fragment', prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git#860c1904a1ce19322e91ac35af1ab07466440c37'],
113-
[{ref: 'refs/pull/15/merge', format: 'fragment', prHeadRef: true, sendGitQueryAsInput: false, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git#refs/pull/15/head'],
125+
[{ref: 'refs/pull/15/merge', checksum: undefined, format: 'fragment', prHeadRef: true, sendGitQueryAsInput: false, buildxQuerySupport: true}, 'https://github.com/docker/actions-toolkit.git#f11797113e5a9b86bd976329c5dbb8a8bfdfadfa'],
126+
[{ref: 'refs/pull/15/merge', checksum: undefined, format: 'fragment', prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true, attrs: {checksum: 'cafebabe'}}, 'https://github.com/docker/actions-toolkit.git#refs/pull/15/merge'],
114127
[{ref: 'refs/heads/master', checksum: undefined, format: 'fragment', prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true, attrs: {checksum: 'cafebabe', subdir: 'subdir', ref: 'refs/tags/v1.0.0'}}, 'https://github.com/docker/actions-toolkit.git#cafebabe:subdir'],
115128
[{ref: 'refs/heads/master', format: 'fragment', prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true, attrs: {'keep-git-dir': 'true'}}, 'https://github.com/docker/actions-toolkit.git#860c1904a1ce19322e91ac35af1ab07466440c37'],
116129
[{ref: 'refs/heads/master', format: 'fragment', prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true, subdir: 'subdir'}, 'https://github.com/docker/actions-toolkit.git#860c1904a1ce19322e91ac35af1ab07466440c37:subdir'],
117130
[{ref: 'refs/heads/master', format: 'fragment', prHeadRef: false, sendGitQueryAsInput: false, buildxQuerySupport: true, subdir: '.'}, 'https://github.com/docker/actions-toolkit.git#860c1904a1ce19322e91ac35af1ab07466440c37'],
118-
[{ref: 'refs/pull/15/merge', format: 'fragment', prHeadRef: true, sendGitQueryAsInput: false, buildxQuerySupport: true, subdir: 'subdir'}, 'https://github.com/docker/actions-toolkit.git#refs/pull/15/head:subdir'],
131+
[{ref: 'refs/pull/15/merge', checksum: undefined, format: 'fragment', prHeadRef: true, sendGitQueryAsInput: false, buildxQuerySupport: true, subdir: 'subdir'}, 'https://github.com/docker/actions-toolkit.git#f11797113e5a9b86bd976329c5dbb8a8bfdfadfa:subdir'],
119132
];
120133

121134
test.each(gitContextCases)('given %o should return %o', async (input: GitContextTestCase, expected: string) => {

src/buildx/build.ts

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -59,28 +59,37 @@ export class Build {
5959

6060
public async gitContext(opts?: GitContextOpts): Promise<string> {
6161
const gitContextCommonAttrs = new Set(['ref', 'checksum', 'subdir']);
62-
const setPullRequestHeadRef = Util.parseBoolOrDefault(process.env.DOCKER_DEFAULT_GIT_CONTEXT_PR_HEAD_REF);
63-
const commonAttrs = {
64-
ref: opts?.attrs?.ref,
65-
checksum: opts?.attrs?.checksum,
66-
subdir: opts?.attrs?.subdir
67-
};
68-
69-
const gitChecksum = opts?.checksum || commonAttrs.checksum || github.context.sha;
62+
const commonAttrs = opts?.attrs || {};
63+
const extraAttrs = Object.entries(commonAttrs).filter(([name]) => !gitContextCommonAttrs.has(name));
64+
7065
let ref = opts?.ref || commonAttrs.ref || github.context.ref;
71-
const subdir = opts?.subdir || commonAttrs.subdir;
72-
const attrs = Object.entries(opts?.attrs || {}).filter(([name]) => !gitContextCommonAttrs.has(name));
7366
if (!ref.startsWith('refs/')) {
7467
ref = `refs/heads/${ref}`;
75-
} else if (ref.startsWith(`refs/pull/`) && setPullRequestHeadRef) {
68+
} else if (ref.startsWith(`refs/pull/`) && Util.parseBoolOrDefault(process.env.DOCKER_DEFAULT_GIT_CONTEXT_PR_HEAD_REF)) {
7669
ref = ref.replace(/\/merge$/g, '/head');
7770
}
7871

72+
const inputChecksum = opts?.checksum || commonAttrs.checksum;
73+
const inputSubdir = opts?.subdir || commonAttrs.subdir;
74+
const checksum = inputChecksum || (ref.startsWith(`refs/pull/`) ? undefined : github.context.sha);
75+
76+
// BuildKit resolves PR refs remotely at build time, so mutable refs like
77+
// refs/pull/*/{merge,head} can drift away from the event SHA. actions/checkout
78+
// avoids that by fetching the exact commit into a local PR ref; here we do the
79+
// equivalent for implicit PR contexts by rewriting them to the event's commit SHA.
80+
if (!inputChecksum && ref.startsWith(`refs/pull/`)) {
81+
if (ref.endsWith('/merge')) {
82+
ref = github.context.sha;
83+
} else if (ref.endsWith('/head') && typeof github.context.payload.pull_request?.head?.sha === 'string') {
84+
ref = github.context.payload.pull_request.head.sha;
85+
}
86+
}
87+
7988
const baseURL = `${GitHub.serverURL}/${github.context.repo.owner}/${github.context.repo.repo}.git`;
8089
let format = opts?.format;
8190
if (!format) {
8291
format = 'fragment';
83-
if (attrs.length > 0) {
92+
if (extraAttrs.length > 0) {
8493
format = 'query';
8594
} else if (Util.parseBoolOrDefault(process.env.BUILDX_SEND_GIT_QUERY_AS_INPUT)) {
8695
try {
@@ -92,21 +101,23 @@ export class Build {
92101
}
93102
}
94103
}
104+
95105
if (format === 'query') {
96106
const query = [`ref=${ref}`];
97-
if (gitChecksum) {
98-
query.push(`checksum=${gitChecksum}`);
107+
if (checksum) {
108+
query.push(`checksum=${checksum}`);
99109
}
100-
if (subdir && subdir !== '.') {
101-
query.push(`subdir=${subdir}`);
110+
if (inputSubdir && inputSubdir !== '.') {
111+
query.push(`subdir=${inputSubdir}`);
102112
}
103-
for (const [name, value] of attrs) {
113+
for (const [name, value] of extraAttrs) {
104114
query.push(`${name}=${value}`);
105115
}
106116
return `${baseURL}?${query.join('&')}`;
107117
}
108-
const fragmentRef = gitChecksum && !ref.startsWith(`refs/pull/`) ? gitChecksum : ref;
109-
return `${baseURL}#${fragmentRef}${subdir && subdir !== '.' ? `:${subdir}` : ''}`;
118+
119+
const fragmentRef = inputChecksum && ref.startsWith(`refs/pull/`) ? ref : (checksum ?? ref);
120+
return `${baseURL}#${fragmentRef}${inputSubdir && inputSubdir !== '.' ? `:${inputSubdir}` : ''}`;
110121
}
111122

112123
public getImageIDFilePath(): string {

0 commit comments

Comments
 (0)