mirror of
https://github.com/docker/login-action.git
synced 2025-08-14 04:23:55 +08:00
Compare commits
22 Commits
v1.12.0
...
releases/v
Author | SHA1 | Date | |
---|---|---|---|
|
dd4fa0671b | ||
|
4e3538592e | ||
|
bb984efc56 | ||
|
722888132b | ||
|
17780b56b7 | ||
|
39857b3b45 | ||
|
5fcc728422 | ||
|
9fb8721eb9 | ||
|
4e3c9375bb | ||
|
4b59a429db | ||
|
6af3c118c8 | ||
|
caca3368ce | ||
|
17f28ab24d | ||
|
a875dd0e21 | ||
|
7948fffc49 | ||
|
5fcefb941d | ||
|
3bb2d084df | ||
|
242fb9a356 | ||
|
fa72313bc3 | ||
|
088f62a4f2 | ||
|
7929ac7647 | ||
|
9376d24995 |
14
.github/workflows/ci.yml
vendored
14
.github/workflows/ci.yml
vendored
@@ -76,6 +76,20 @@ jobs:
|
||||
docker image prune -a -f >/dev/null 2>&1
|
||||
docker pull ghcr.io/docker-ghactiontest/test
|
||||
|
||||
acr:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
-
|
||||
name: Login to ACR
|
||||
uses: ./
|
||||
with:
|
||||
registry: ${{ secrets.AZURE_REGISTRY_NAME }}.azurecr.io
|
||||
username: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
password: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||
|
||||
dockerhub:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
|
104
README.md
104
README.md
@@ -39,7 +39,7 @@ name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: master
|
||||
branches: main
|
||||
|
||||
jobs:
|
||||
login:
|
||||
@@ -64,7 +64,7 @@ name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: master
|
||||
branches: main
|
||||
|
||||
jobs:
|
||||
login:
|
||||
@@ -92,7 +92,7 @@ name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: master
|
||||
branches: main
|
||||
|
||||
jobs:
|
||||
login:
|
||||
@@ -118,7 +118,7 @@ name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: master
|
||||
branches: main
|
||||
|
||||
jobs:
|
||||
login:
|
||||
@@ -142,6 +142,45 @@ jobs:
|
||||
> Google Container Registry, use the information [on this page](https://cloud.google.com/artifact-registry/docs/transition/transition-from-gcr)
|
||||
> to learn about transitioning to Google Artifact Registry.
|
||||
|
||||
You can use either workload identity federation based keyless authentication or service account based authentication.
|
||||
|
||||
#### Workload identity federation based authentication
|
||||
|
||||
Configure the workload identity federation for github actions in gcloud (for steps, [refer here](https://github.com/google-github-actions/auth#setting-up-workload-identity-federation)). In the steps, your service account should the ability to push to GCR. Then use google-github-actions/auth action for authentication using workload identity like below:
|
||||
|
||||
```yaml
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: main
|
||||
|
||||
jobs:
|
||||
login:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: 'auth'
|
||||
name: 'Authenticate to Google Cloud'
|
||||
uses: 'google-github-actions/auth@v0'
|
||||
with:
|
||||
token_format: 'access_token'
|
||||
workload_identity_provider: '<workload_identity_provider>'
|
||||
service_account: '<service_account>'
|
||||
|
||||
- name: Login to GCR
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: gcr.io
|
||||
username: oauth2accesstoken
|
||||
password: ${{ steps.auth.outputs.access_token }}
|
||||
```
|
||||
|
||||
> Replace `<workload_identity_provider>` with configured workload identity provider. For steps to configure, [refer here](https://github.com/google-github-actions/auth#setting-up-workload-identity-federation).
|
||||
|
||||
> Replace `<service_account>` with configured service account in workload identity provider which has access to push to GCR
|
||||
|
||||
#### Service account based authentication
|
||||
|
||||
Use a service account with the ability to push to GCR and [configure access control](https://cloud.google.com/container-registry/docs/access-control).
|
||||
Then create and download the JSON key for this service account and save content of `.json` file
|
||||
[as a secret](https://docs.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets#creating-encrypted-secrets-for-a-repository)
|
||||
@@ -153,7 +192,7 @@ name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: master
|
||||
branches: main
|
||||
|
||||
jobs:
|
||||
login:
|
||||
@@ -170,6 +209,47 @@ jobs:
|
||||
|
||||
### Google Artifact Registry (GAR)
|
||||
|
||||
You can use either workload identity federation based keyless authentication or service account based authentication.
|
||||
|
||||
#### Workload identity federation based authentication
|
||||
|
||||
Configure the workload identity federation for github actions in gcloud (for steps, [refer here](https://github.com/google-github-actions/auth#setting-up-workload-identity-federation)). In the steps, your service account should the ability to push to GAR. Then use google-github-actions/auth action for authentication using workload identity like below:
|
||||
|
||||
```yaml
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: main
|
||||
|
||||
jobs:
|
||||
login:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: 'auth'
|
||||
name: 'Authenticate to Google Cloud'
|
||||
uses: 'google-github-actions/auth@v0'
|
||||
with:
|
||||
token_format: 'access_token'
|
||||
workload_identity_provider: '<workload_identity_provider>'
|
||||
service_account: '<service_account>'
|
||||
|
||||
- name: Login to GAR
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: <location>-docker.pkg.dev
|
||||
username: oauth2accesstoken
|
||||
password: ${{ steps.auth.outputs.access_token }}
|
||||
```
|
||||
> Replace `<workload_identity_provider>` with configured workload identity provider
|
||||
|
||||
> Replace `<service_account>` with configured service account in workload identity provider which has access to push to GCR
|
||||
|
||||
> Replace `<location>` with the regional or multi-regional [location](https://cloud.google.com/artifact-registry/docs/repo-organize#locations)
|
||||
> of the repository where the image is stored.
|
||||
|
||||
#### Service account based authentication
|
||||
|
||||
Use a service account with the ability to push to GAR and [configure access control](https://cloud.google.com/artifact-registry/docs/access-control).
|
||||
Then create and download the JSON key for this service account and save content of `.json` file
|
||||
[as a secret](https://docs.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets#creating-encrypted-secrets-for-a-repository)
|
||||
@@ -181,7 +261,7 @@ name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: master
|
||||
branches: main
|
||||
|
||||
jobs:
|
||||
login:
|
||||
@@ -210,7 +290,7 @@ name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: master
|
||||
branches: main
|
||||
|
||||
jobs:
|
||||
login:
|
||||
@@ -233,7 +313,7 @@ name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: master
|
||||
branches: main
|
||||
|
||||
jobs:
|
||||
login:
|
||||
@@ -260,7 +340,7 @@ name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: master
|
||||
branches: main
|
||||
|
||||
jobs:
|
||||
login:
|
||||
@@ -293,7 +373,7 @@ name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: master
|
||||
branches: main
|
||||
|
||||
jobs:
|
||||
login:
|
||||
@@ -327,7 +407,7 @@ name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: master
|
||||
branches: main
|
||||
|
||||
jobs:
|
||||
login:
|
||||
@@ -353,7 +433,7 @@ name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: master
|
||||
branches: main
|
||||
|
||||
jobs:
|
||||
login:
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import {beforeEach, describe, expect, jest, test} from '@jest/globals';
|
||||
import {AuthorizationData} from '@aws-sdk/client-ecr';
|
||||
import * as aws from '../src/aws';
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import {expect, test} from '@jest/globals';
|
||||
import {getInputs} from '../src/context';
|
||||
|
||||
test('with password and username getInputs does not throw error', async () => {
|
||||
|
@@ -1,20 +1,19 @@
|
||||
import {expect, jest, test} from '@jest/globals';
|
||||
import {loginStandard, logout} from '../src/docker';
|
||||
|
||||
import * as path from 'path';
|
||||
|
||||
import * as exec from '@actions/exec';
|
||||
|
||||
process.env['RUNNER_TEMP'] = path.join(__dirname, 'runner');
|
||||
|
||||
test('loginStandard calls exec', async () => {
|
||||
const execSpy: jest.SpyInstance = jest.spyOn(exec, 'getExecOutput');
|
||||
execSpy.mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
// @ts-ignore
|
||||
const execSpy = jest.spyOn(exec, 'getExecOutput').mockImplementation(async () => {
|
||||
return {
|
||||
exitCode: expect.any(Number),
|
||||
stdout: expect.any(Function),
|
||||
stderr: expect.any(Function)
|
||||
})
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
const username: string = 'dbowie';
|
||||
const password: string = 'groundcontrol';
|
||||
@@ -30,14 +29,14 @@ test('loginStandard calls exec', async () => {
|
||||
});
|
||||
|
||||
test('logout calls exec', async () => {
|
||||
const execSpy: jest.SpyInstance = jest.spyOn(exec, 'getExecOutput');
|
||||
execSpy.mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
// @ts-ignore
|
||||
const execSpy = jest.spyOn(exec, 'getExecOutput').mockImplementation(async () => {
|
||||
return {
|
||||
exitCode: expect.any(Number),
|
||||
stdout: expect.any(Function),
|
||||
stderr: expect.any(Function)
|
||||
})
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
const registry: string = 'https://ghcr.io';
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import {expect, jest, test} from '@jest/globals';
|
||||
import osm = require('os');
|
||||
|
||||
import {run} from '../src/main';
|
||||
@@ -7,26 +8,20 @@ import * as stateHelper from '../src/state-helper';
|
||||
import * as core from '@actions/core';
|
||||
|
||||
test('errors without username and password', async () => {
|
||||
const platSpy = jest.spyOn(osm, 'platform');
|
||||
platSpy.mockImplementation(() => 'linux');
|
||||
const platSpy = jest.spyOn(osm, 'platform').mockImplementation(() => 'linux');
|
||||
|
||||
process.env['INPUT_LOGOUT'] = 'true'; // default value
|
||||
|
||||
const coreSpy: jest.SpyInstance = jest.spyOn(core, 'setFailed');
|
||||
const coreSpy = jest.spyOn(core, 'setFailed');
|
||||
|
||||
await run();
|
||||
|
||||
expect(coreSpy).toHaveBeenCalledWith('Username and password required');
|
||||
});
|
||||
|
||||
test('successful with username and password', async () => {
|
||||
const platSpy = jest.spyOn(osm, 'platform');
|
||||
platSpy.mockImplementation(() => 'linux');
|
||||
|
||||
const setRegistrySpy: jest.SpyInstance = jest.spyOn(stateHelper, 'setRegistry');
|
||||
const setLogoutSpy: jest.SpyInstance = jest.spyOn(stateHelper, 'setLogout');
|
||||
const dockerSpy: jest.SpyInstance = jest.spyOn(docker, 'login');
|
||||
dockerSpy.mockImplementation(() => {});
|
||||
const platSpy = jest.spyOn(osm, 'platform').mockImplementation(() => 'linux');
|
||||
const setRegistrySpy = jest.spyOn(stateHelper, 'setRegistry');
|
||||
const setLogoutSpy = jest.spyOn(stateHelper, 'setLogout');
|
||||
const dockerSpy = jest.spyOn(docker, 'login').mockImplementation(jest.fn());
|
||||
|
||||
const username: string = 'dbowie';
|
||||
process.env[`INPUT_USERNAME`] = username;
|
||||
@@ -48,13 +43,11 @@ test('successful with username and password', async () => {
|
||||
});
|
||||
|
||||
test('calls docker login', async () => {
|
||||
const platSpy = jest.spyOn(osm, 'platform');
|
||||
platSpy.mockImplementation(() => 'linux');
|
||||
|
||||
const setRegistrySpy: jest.SpyInstance = jest.spyOn(stateHelper, 'setRegistry');
|
||||
const setLogoutSpy: jest.SpyInstance = jest.spyOn(stateHelper, 'setLogout');
|
||||
const dockerSpy: jest.SpyInstance = jest.spyOn(docker, 'login');
|
||||
dockerSpy.mockImplementation(() => {});
|
||||
const platSpy = jest.spyOn(osm, 'platform').mockImplementation(() => 'linux');
|
||||
const setRegistrySpy = jest.spyOn(stateHelper, 'setRegistry');
|
||||
const setLogoutSpy = jest.spyOn(stateHelper, 'setLogout');
|
||||
const dockerSpy = jest.spyOn(docker, 'login');
|
||||
dockerSpy.mockImplementation(jest.fn());
|
||||
|
||||
const username: string = 'dbowie';
|
||||
process.env[`INPUT_USERNAME`] = username;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1.3-labs
|
||||
|
||||
ARG NODE_VERSION
|
||||
ARG NODE_VERSION=12
|
||||
ARG DOCKER_VERSION=20.10.10
|
||||
ARG BUILDX_VERSION=0.7.0
|
||||
|
9259
dist/index.js
generated
vendored
9259
dist/index.js
generated
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,3 @@
|
||||
variable "NODE_VERSION" {
|
||||
default = "12"
|
||||
}
|
||||
|
||||
target "node-version" {
|
||||
args = {
|
||||
NODE_VERSION = NODE_VERSION
|
||||
}
|
||||
}
|
||||
|
||||
group "default" {
|
||||
targets = ["build"]
|
||||
}
|
||||
@@ -21,50 +11,43 @@ group "validate" {
|
||||
}
|
||||
|
||||
target "build" {
|
||||
inherits = ["node-version"]
|
||||
dockerfile = "./hack/build.Dockerfile"
|
||||
dockerfile = "dev.Dockerfile"
|
||||
target = "build-update"
|
||||
output = ["."]
|
||||
}
|
||||
|
||||
target "build-validate" {
|
||||
inherits = ["node-version"]
|
||||
dockerfile = "./hack/build.Dockerfile"
|
||||
dockerfile = "dev.Dockerfile"
|
||||
target = "build-validate"
|
||||
output = ["type=cacheonly"]
|
||||
}
|
||||
|
||||
target "format" {
|
||||
inherits = ["node-version"]
|
||||
dockerfile = "./hack/build.Dockerfile"
|
||||
dockerfile = "dev.Dockerfile"
|
||||
target = "format-update"
|
||||
output = ["."]
|
||||
}
|
||||
|
||||
target "format-validate" {
|
||||
inherits = ["node-version"]
|
||||
dockerfile = "./hack/build.Dockerfile"
|
||||
dockerfile = "dev.Dockerfile"
|
||||
target = "format-validate"
|
||||
output = ["type=cacheonly"]
|
||||
}
|
||||
|
||||
target "vendor-update" {
|
||||
inherits = ["node-version"]
|
||||
dockerfile = "./hack/build.Dockerfile"
|
||||
dockerfile = "dev.Dockerfile"
|
||||
target = "vendor-update"
|
||||
output = ["."]
|
||||
}
|
||||
|
||||
target "vendor-validate" {
|
||||
inherits = ["node-version"]
|
||||
dockerfile = "./hack/build.Dockerfile"
|
||||
dockerfile = "dev.Dockerfile"
|
||||
target = "vendor-validate"
|
||||
output = ["type=cacheonly"]
|
||||
}
|
||||
|
||||
target "test" {
|
||||
inherits = ["node-version"]
|
||||
dockerfile = "./hack/build.Dockerfile"
|
||||
dockerfile = "dev.Dockerfile"
|
||||
target = "test-coverage"
|
||||
output = ["./coverage"]
|
||||
}
|
||||
|
@@ -2,9 +2,7 @@ module.exports = {
|
||||
clearMocks: true,
|
||||
moduleFileExtensions: ['js', 'ts'],
|
||||
setupFiles: ["dotenv/config"],
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/*.test.ts'],
|
||||
testRunner: 'jest-circus/runner',
|
||||
transform: {
|
||||
'^.+\\.ts$': 'ts-jest'
|
||||
},
|
||||
|
28
package.json
28
package.json
@@ -4,8 +4,8 @@
|
||||
"main": "lib/main.js",
|
||||
"scripts": {
|
||||
"build": "tsc && ncc build",
|
||||
"format": "prettier --write **/*.ts",
|
||||
"format-check": "prettier --check **/*.ts",
|
||||
"format": "prettier --write '**/*.ts'",
|
||||
"format-check": "prettier --check '**/*.ts'",
|
||||
"test": "jest --coverage",
|
||||
"pre-checkin": "yarn run format && yarn run build"
|
||||
},
|
||||
@@ -30,20 +30,18 @@
|
||||
"@actions/core": "^1.6.0",
|
||||
"@actions/exec": "^1.1.0",
|
||||
"@actions/io": "^1.1.1",
|
||||
"@aws-sdk/client-ecr": "^3.44.0",
|
||||
"@aws-sdk/client-ecr-public": "^3.43.0"
|
||||
"@aws-sdk/client-ecr": "^3.53.0",
|
||||
"@aws-sdk/client-ecr-public": "^3.53.0",
|
||||
"http-proxy-agent": "^5.0.0",
|
||||
"https-proxy-agent": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^26.0.23",
|
||||
"@types/node": "^14.17.4",
|
||||
"@vercel/ncc": "^0.28.6",
|
||||
"dotenv": "^8.6.0",
|
||||
"jest": "^26.6.3",
|
||||
"jest-circus": "^26.6.3",
|
||||
"jest-runtime": "^26.6.3",
|
||||
"prettier": "^2.3.2",
|
||||
"ts-jest": "^26.5.6",
|
||||
"typescript": "^3.9.10",
|
||||
"typescript-formatter": "^7.2.2"
|
||||
"@types/node": "^16.11.26",
|
||||
"@vercel/ncc": "^0.33.3",
|
||||
"dotenv": "^16.0.0",
|
||||
"jest": "^27.2.5",
|
||||
"prettier": "^2.5.1",
|
||||
"ts-jest": "^27.1.2",
|
||||
"typescript": "^4.4.4"
|
||||
}
|
||||
}
|
||||
|
29
src/aws.ts
29
src/aws.ts
@@ -1,6 +1,9 @@
|
||||
import * as core from '@actions/core';
|
||||
import {ECR} from '@aws-sdk/client-ecr';
|
||||
import {ECRPUBLIC} from '@aws-sdk/client-ecr-public';
|
||||
import {NodeHttpHandler} from '@aws-sdk/node-http-handler';
|
||||
import {HttpProxyAgent} from 'http-proxy-agent';
|
||||
import {HttpsProxyAgent} from 'https-proxy-agent';
|
||||
|
||||
const ecrRegistryRegex = /^(([0-9]{12})\.dkr\.ecr\.(.+)\.amazonaws\.com(.cn)?)(\/([^:]+)(:.+)?)?$/;
|
||||
|
||||
@@ -54,6 +57,20 @@ export const getRegistriesData = async (registry: string, username?: string, pas
|
||||
authTokenRequest['registryIds'] = accountIDs;
|
||||
}
|
||||
|
||||
let httpProxyAgent: any = null;
|
||||
const httpProxy = process.env.http_proxy || process.env.HTTP_PROXY || '';
|
||||
if (httpProxy) {
|
||||
core.debug(`Using http proxy ${httpProxy}`);
|
||||
httpProxyAgent = new HttpProxyAgent(httpProxy);
|
||||
}
|
||||
|
||||
let httpsProxyAgent: any = null;
|
||||
const httpsProxy = process.env.https_proxy || process.env.HTTPS_PROXY || '';
|
||||
if (httpsProxy) {
|
||||
core.debug(`Using https proxy ${httpsProxy}`);
|
||||
httpsProxyAgent = new HttpsProxyAgent(httpsProxy);
|
||||
}
|
||||
|
||||
const credentials =
|
||||
username && password
|
||||
? {
|
||||
@@ -67,7 +84,11 @@ export const getRegistriesData = async (registry: string, username?: string, pas
|
||||
const ecrPublic = new ECRPUBLIC({
|
||||
customUserAgent: 'docker-login-action',
|
||||
credentials,
|
||||
region: region
|
||||
region: region,
|
||||
requestHandler: new NodeHttpHandler({
|
||||
httpAgent: httpProxyAgent,
|
||||
httpsAgent: httpsProxyAgent
|
||||
})
|
||||
});
|
||||
const authTokenResponse = await ecrPublic.getAuthorizationToken(authTokenRequest);
|
||||
if (!authTokenResponse.authorizationData || !authTokenResponse.authorizationData.authorizationToken) {
|
||||
@@ -87,7 +108,11 @@ export const getRegistriesData = async (registry: string, username?: string, pas
|
||||
const ecr = new ECR({
|
||||
customUserAgent: 'docker-login-action',
|
||||
credentials,
|
||||
region: region
|
||||
region: region,
|
||||
requestHandler: new NodeHttpHandler({
|
||||
httpAgent: httpProxyAgent,
|
||||
httpsAgent: httpsProxyAgent
|
||||
})
|
||||
});
|
||||
const authTokenResponse = await ecr.getAuthorizationToken(authTokenRequest);
|
||||
if (!Array.isArray(authTokenResponse.authorizationData) || !authTokenResponse.authorizationData.length) {
|
||||
|
@@ -9,7 +9,7 @@ export async function run(): Promise<void> {
|
||||
stateHelper.setRegistry(input.registry);
|
||||
stateHelper.setLogout(input.logout);
|
||||
await docker.login(input.registry, input.username, input.password, input.ecr);
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
core.setFailed(error.message);
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"target": "esnext",
|
||||
"module": "commonjs",
|
||||
"lib": [
|
||||
"es6",
|
||||
"dom"
|
||||
],
|
||||
"newLine": "lf",
|
||||
"outDir": "./lib",
|
||||
"rootDir": "./src",
|
||||
|
Reference in New Issue
Block a user