Compare commits

..

No commits in common. "d3b9cdfa580156c018ca5e745c1bf653d8ea46b5" and "eed250920367120910219051b8653a1d846f7f03" have entirely different histories.

10 changed files with 43 additions and 101 deletions

View File

@ -1,3 +0,0 @@
FROM docker:29.3@sha256:4d90f1f6c400315c2dba96d3ec93c01e64198395cbba04f79d12adce4f737029
ENTRYPOINT ["docker"]

View File

@ -1,15 +0,0 @@
name: Pull Docker image
description: Pull an image from a Docker container action
inputs:
image:
description: Image reference to pull
required: true
runs:
using: docker
image: Dockerfile
args:
- pull
- ${{ inputs.image }}

View File

@ -106,10 +106,11 @@ jobs:
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- -
name: Pull test image from Docker CLI container action name: DinD
uses: ./.github/actions/docker-pull uses: docker://docker:29.3@sha256:4d90f1f6c400315c2dba96d3ec93c01e64198395cbba04f79d12adce4f737029
with: with:
image: ${{ env.GHCR_TEST_IMAGE }} entrypoint: docker
args: pull ${{ env.GHCR_TEST_IMAGE }}
- -
name: Pull test image name: Pull test image
run: | run: |

View File

@ -35,12 +35,12 @@ jobs:
node-version: ${{ env.NODE_VERSION }} node-version: ${{ env.NODE_VERSION }}
- -
name: Initialize CodeQL name: Initialize CodeQL
uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8 uses: github/codeql-action/init@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
with: with:
languages: javascript-typescript languages: javascript-typescript
build-mode: none build-mode: none
- -
name: Perform CodeQL Analysis name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8 uses: github/codeql-action/analyze@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
with: with:
category: "/language:javascript-typescript" category: "/language:javascript-typescript"

View File

@ -168,23 +168,3 @@ test('getAuthList masks registry-auth password when present', async () => {
expect(stdoutWriteSpy.mock.calls.map(call => call[0]).join('')).toContain('::add-mask::groundcontrol'); expect(stdoutWriteSpy.mock.calls.map(call => call[0]).join('')).toContain('::add-mask::groundcontrol');
}); });
// prettier-ignore
test.each([
['false', 'false'],
['"false"', 'false'],
['true', 'true'],
['auto', 'auto'],
['""', 'auto']
])('getAuthList normalizes registry-auth ecr: %s', (ecr, expected) => {
const [auth] = getAuthList({
registry: '',
username: '',
password: '',
scope: '',
ecr: '',
logout: true,
registryAuth: `- registry: 012345678910.dkr.ecr.eu-west-3.amazonaws.com\n username: AWS\n password: token\n ecr: ${ecr}\n`
});
expect(auth.ecr).toBe(expected);
});

View File

@ -5,7 +5,6 @@ import {beforeEach, describe, expect, test, vi} from 'vitest';
import * as dockerhub from '../src/dockerhub.js'; import * as dockerhub from '../src/dockerhub.js';
vi.mock('@actions/core', () => ({ vi.mock('@actions/core', () => ({
debug: vi.fn(),
getIDToken: vi.fn(), getIDToken: vi.fn(),
info: vi.fn(), info: vi.fn(),
setSecret: vi.fn() setSecret: vi.fn()
@ -84,15 +83,6 @@ describe('getOIDCToken', () => {
expect(body.get('connection_id')).toBe(validConnectionID); expect(body.get('connection_id')).toBe(validConnectionID);
expect(body.get('expires_in')).toBe('300'); expect(body.get('expires_in')).toBe('300');
expect(setSecretMock).toHaveBeenCalledWith('hub-token'); expect(setSecretMock).toHaveBeenCalledWith('hub-token');
expect(core.info).toHaveBeenCalledWith('Docker Hub OIDC detected for docker.io');
expect(core.info).toHaveBeenCalledWith('Retrieving GitHub OIDC token for Docker Hub');
expect(core.info).toHaveBeenCalledWith('Exchanging GitHub OIDC token for Docker Hub token');
expect(core.info).toHaveBeenCalledWith('Docker Hub OIDC token exchange succeeded');
expect(core.debug).toHaveBeenCalledWith('Docker Hub OIDC token audience: https://identity.docker.com');
expect(core.debug).toHaveBeenCalledWith('Docker Hub OIDC token expiration: 300s');
expect(core.debug).toHaveBeenCalledWith('Sending Docker Hub OIDC token request to https://identity.docker.com/oauth/token');
expect(core.debug).toHaveBeenCalledWith('Docker Hub OIDC token request returned status code 200');
expect(core.debug).toHaveBeenCalledWith('Docker Hub OIDC token response status code: 200');
}); });
test('uses custom token expiration', async () => { test('uses custom token expiration', async () => {

66
dist/index.cjs generated vendored

File diff suppressed because one or more lines are too long

6
dist/index.cjs.map generated vendored

File diff suppressed because one or more lines are too long

View File

@ -62,7 +62,7 @@ export function getAuthList(inputs: Inputs): Array<Auth> {
username: auth.username, username: auth.username,
password: auth.password, password: auth.password,
scope: auth.scope, scope: auth.scope,
ecr: String(auth.ecr ?? '') || 'auto', ecr: auth.ecr || 'auto',
configDir: scopeToConfigDir(registry, auth.scope) configDir: scopeToConfigDir(registry, auth.scope)
}; };
}); });

View File

@ -35,10 +35,6 @@ export const getOIDCToken = async (registry: string, username: string): Promise<
const expiresIn = getExpiresIn(); const expiresIn = getExpiresIn();
const identityHost = registry === 'registry-1-stage.docker.io' ? 'identity-stage.docker.com' : 'identity.docker.com'; const identityHost = registry === 'registry-1-stage.docker.io' ? 'identity-stage.docker.com' : 'identity.docker.com';
const audience = `https://${identityHost}`; const audience = `https://${identityHost}`;
core.info(`Docker Hub OIDC detected for ${registry || 'docker.io'}`);
core.debug(`Docker Hub OIDC token audience: ${audience}`);
core.debug(`Docker Hub OIDC token expiration: ${expiresIn}s`);
core.info(`Retrieving GitHub OIDC token for Docker Hub`);
const idToken = await core.getIDToken(audience); const idToken = await core.getIDToken(audience);
const http: httpm.HttpClient = new httpm.HttpClient('github.com/docker/login-action', [], { const http: httpm.HttpClient = new httpm.HttpClient('github.com/docker/login-action', [], {
headers: { headers: {
@ -53,12 +49,10 @@ export const getOIDCToken = async (registry: string, username: string): Promise<
data.set('connection_id', connectionID); data.set('connection_id', connectionID);
data.set('expires_in', expiresIn.toString()); data.set('expires_in', expiresIn.toString());
core.info(`Exchanging GitHub OIDC token for Docker Hub token`);
const resp = await postWithRetry(http, `https://${identityHost}/oauth/token`, data.toString()); const resp = await postWithRetry(http, `https://${identityHost}/oauth/token`, data.toString());
const tokenResp = <OIDCTokenResponse>JSON.parse(await handleResponse(resp)); const tokenResp = <OIDCTokenResponse>JSON.parse(await handleResponse(resp));
core.setSecret(tokenResp.access_token); core.setSecret(tokenResp.access_token);
core.info(`Docker Hub OIDC token exchange succeeded`);
return { return {
username, username,
@ -76,20 +70,16 @@ const getExpiresIn = (): number => {
}; };
const postWithRetry = async (http: httpm.HttpClient, url: string, data: string): Promise<httpm.HttpClientResponse> => { const postWithRetry = async (http: httpm.HttpClient, url: string, data: string): Promise<httpm.HttpClientResponse> => {
core.debug(`Sending Docker Hub OIDC token request to ${url}`);
let resp = await http.post(url, data); let resp = await http.post(url, data);
core.debug(`Docker Hub OIDC token request returned status code ${resp.message.statusCode || HttpCodes.InternalServerError}`);
for (let attempt = 0; (resp.message.statusCode || HttpCodes.InternalServerError) === HttpCodes.TooManyRequests && attempt < maxRetries; attempt++) { for (let attempt = 0; (resp.message.statusCode || HttpCodes.InternalServerError) === HttpCodes.TooManyRequests && attempt < maxRetries; attempt++) {
const delay = parseRetryAfter(resp.message.headers['retry-after']); const delay = parseRetryAfter(resp.message.headers['retry-after']);
if (delay === null) { if (delay === null) {
core.debug(`Docker Hub OIDC token request rate limited without retry-after header`);
break; break;
} }
await resp.readBody(); await resp.readBody();
core.info(`Docker Hub OIDC token request rate limited, retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries})`); core.info(`Docker Hub OIDC token request rate limited, retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries})`);
await new Promise(resolve => setTimeout(resolve, delay)); await new Promise(resolve => setTimeout(resolve, delay));
resp = await http.post(url, data); resp = await http.post(url, data);
core.debug(`Docker Hub OIDC token request returned status code ${resp.message.statusCode || HttpCodes.InternalServerError}`);
} }
return resp; return resp;
}; };
@ -111,7 +101,6 @@ const parseRetryAfter = (value: string | string[] | undefined): number | null =>
const handleResponse = async (resp: httpm.HttpClientResponse): Promise<string> => { const handleResponse = async (resp: httpm.HttpClientResponse): Promise<string> => {
const body = await resp.readBody(); const body = await resp.readBody();
const statusCode = resp.message.statusCode || HttpCodes.InternalServerError; const statusCode = resp.message.statusCode || HttpCodes.InternalServerError;
core.debug(`Docker Hub OIDC token response status code: ${statusCode}`);
if (statusCode < HttpCodes.OK || statusCode >= HttpCodes.MultipleChoices) { if (statusCode < HttpCodes.OK || statusCode >= HttpCodes.MultipleChoices) {
throw parseError(statusCode, body); throw parseError(statusCode, body);
} }