mirror of
https://github.com/docker/build-push-action.git
synced 2024-12-25 00:02:13 -05:00
Merge pull request #296 from crazy-max/secret-file
Allow to use secret file mount
This commit is contained in:
commit
af867d4937
9 changed files with 122 additions and 33 deletions
|
@ -1,4 +1,4 @@
|
||||||
#syntax=docker/dockerfile:1.1-experimental
|
#syntax=docker/dockerfile:1.2
|
||||||
|
|
||||||
FROM node:12 AS deps
|
FROM node:12 AS deps
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
|
@ -471,9 +471,6 @@ using [actions/cache](https://github.com/actions/cache) with this action:
|
||||||
```
|
```
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
> If you want to [export layers for all stages](https://github.com/docker/buildx#--cache-tonametypetypekeyvalue),
|
|
||||||
> you have to specify `mode=max` attribute in `cache-to`.
|
|
||||||
|
|
||||||
### Handle tags and labels
|
### Handle tags and labels
|
||||||
|
|
||||||
If you come from [`v1`](https://github.com/docker/build-push-action/tree/releases/v1#readme) and want an
|
If you come from [`v1`](https://github.com/docker/build-push-action/tree/releases/v1#readme) and want an
|
||||||
|
@ -622,7 +619,8 @@ Following inputs can be used as `step.with` keys
|
||||||
| `outputs` | List | List of [output destinations](https://github.com/docker/buildx#-o---outputpath-typetypekeyvalue) (format: `type=local,dest=path`) |
|
| `outputs` | List | List of [output destinations](https://github.com/docker/buildx#-o---outputpath-typetypekeyvalue) (format: `type=local,dest=path`) |
|
||||||
| `cache-from` | List | List of [external cache sources](https://github.com/docker/buildx#--cache-fromnametypetypekeyvalue) (eg. `type=local,src=path/to/dir`) |
|
| `cache-from` | List | List of [external cache sources](https://github.com/docker/buildx#--cache-fromnametypetypekeyvalue) (eg. `type=local,src=path/to/dir`) |
|
||||||
| `cache-to` | List | List of [cache export destinations](https://github.com/docker/buildx#--cache-tonametypetypekeyvalue) (eg. `type=local,dest=path/to/dir`) |
|
| `cache-to` | List | List of [cache export destinations](https://github.com/docker/buildx#--cache-tonametypetypekeyvalue) (eg. `type=local,dest=path/to/dir`) |
|
||||||
| `secrets` | List | List of secrets to expose to the build (eg. `key=value`, `GIT_AUTH_TOKEN=mytoken`) |
|
| `secrets` | List | List of secrets to expose to the build (eg. `key=string`, `GIT_AUTH_TOKEN=mytoken`) |
|
||||||
|
| `secret-files` | List | List of secret files to expose to the build (eg. `key=filename`, `MY_SECRET=./secret.txt`) |
|
||||||
| `ssh` | List | List of SSH agent socket or keys to expose to the build |
|
| `ssh` | List | List of SSH agent socket or keys to expose to the build |
|
||||||
|
|
||||||
### outputs
|
### outputs
|
||||||
|
|
|
@ -119,21 +119,34 @@ describe('parseVersion', () => {
|
||||||
|
|
||||||
describe('getSecret', () => {
|
describe('getSecret', () => {
|
||||||
test.each([
|
test.each([
|
||||||
['A_SECRET=abcdef0123456789', 'A_SECRET', 'abcdef0123456789', false],
|
['A_SECRET=abcdef0123456789', false, 'A_SECRET', 'abcdef0123456789', false],
|
||||||
['GIT_AUTH_TOKEN=abcdefghijklmno=0123456789', 'GIT_AUTH_TOKEN', 'abcdefghijklmno=0123456789', false],
|
['GIT_AUTH_TOKEN=abcdefghijklmno=0123456789', false, 'GIT_AUTH_TOKEN', 'abcdefghijklmno=0123456789', false],
|
||||||
['MY_KEY=c3RyaW5nLXdpdGgtZXF1YWxzCg==', 'MY_KEY', 'c3RyaW5nLXdpdGgtZXF1YWxzCg==', false],
|
['MY_KEY=c3RyaW5nLXdpdGgtZXF1YWxzCg==', false, 'MY_KEY', 'c3RyaW5nLXdpdGgtZXF1YWxzCg==', false],
|
||||||
['aaaaaaaa', '', '', true],
|
['aaaaaaaa', false, '', '', true],
|
||||||
['aaaaaaaa=', '', '', true],
|
['aaaaaaaa=', false, '', '', true],
|
||||||
['=bbbbbbb', '', '', true]
|
['=bbbbbbb', false, '', '', true],
|
||||||
])('given %p key and %p secret', async (kvp, key, secret, invalid) => {
|
[
|
||||||
|
`foo=${path.join(__dirname, 'fixtures', 'secret.txt').split(path.sep).join(path.posix.sep)}`,
|
||||||
|
true,
|
||||||
|
'foo',
|
||||||
|
'bar',
|
||||||
|
false
|
||||||
|
],
|
||||||
|
[`notfound=secret`, true, '', '', true]
|
||||||
|
])('given %p key and %p secret', async (kvp, file, exKey, exValue, invalid) => {
|
||||||
try {
|
try {
|
||||||
const secretArgs = await buildx.getSecret(kvp);
|
let secret: string;
|
||||||
|
if (file) {
|
||||||
|
secret = await buildx.getSecretFile(kvp);
|
||||||
|
} else {
|
||||||
|
secret = await buildx.getSecretString(kvp);
|
||||||
|
}
|
||||||
expect(true).toBe(!invalid);
|
expect(true).toBe(!invalid);
|
||||||
console.log(`secretArgs: ${secretArgs}`);
|
console.log(`secret: ${secret}`);
|
||||||
expect(secretArgs).toEqual(`id=${key},src=${tmpNameSync}`);
|
expect(secret).toEqual(`id=${exKey},src=${tmpNameSync}`);
|
||||||
const secretContent = await fs.readFileSync(tmpNameSync, 'utf-8');
|
const secretValue = await fs.readFileSync(tmpNameSync, 'utf-8');
|
||||||
console.log(`secretValue: ${secretContent}`);
|
console.log(`secretValue: ${secretValue}`);
|
||||||
expect(secretContent).toEqual(secret);
|
expect(secretValue).toEqual(exValue);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(true).toBe(invalid);
|
expect(true).toBe(invalid);
|
||||||
}
|
}
|
||||||
|
|
|
@ -337,6 +337,27 @@ ccc`],
|
||||||
'--push',
|
'--push',
|
||||||
'https://github.com/docker/build-push-action.git#heads/master'
|
'https://github.com/docker/build-push-action.git#heads/master'
|
||||||
]
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'0.5.1',
|
||||||
|
new Map<string, string>([
|
||||||
|
['context', 'https://github.com/docker/build-push-action.git#heads/master'],
|
||||||
|
['tag', 'localhost:5000/name/app:latest'],
|
||||||
|
['secret-files', `MY_SECRET=${path.join(__dirname, 'fixtures', 'secret.txt').split(path.sep).join(path.posix.sep)}`],
|
||||||
|
['file', './test/Dockerfile'],
|
||||||
|
['builder', 'builder-git-context-2'],
|
||||||
|
['push', 'true']
|
||||||
|
]),
|
||||||
|
[
|
||||||
|
'buildx',
|
||||||
|
'build',
|
||||||
|
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
|
||||||
|
'--secret', 'id=MY_SECRET,src=/tmp/.docker-build-push-jest/.tmpname-jest',
|
||||||
|
'--file', './test/Dockerfile',
|
||||||
|
'--builder', 'builder-git-context-2',
|
||||||
|
'--push',
|
||||||
|
'https://github.com/docker/build-push-action.git#heads/master'
|
||||||
|
]
|
||||||
]
|
]
|
||||||
])(
|
])(
|
||||||
'given %p with %p as inputs, returns %p',
|
'given %p with %p as inputs, returns %p',
|
||||||
|
|
1
__tests__/fixtures/secret.txt
Normal file
1
__tests__/fixtures/secret.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
bar
|
11
action.yml
11
action.yml
|
@ -60,15 +60,18 @@ inputs:
|
||||||
description: "List of cache export destinations for buildx (eg. user/app:cache, type=local,dest=path/to/dir)"
|
description: "List of cache export destinations for buildx (eg. user/app:cache, type=local,dest=path/to/dir)"
|
||||||
required: false
|
required: false
|
||||||
secrets:
|
secrets:
|
||||||
description: "List of secrets to expose to the build (eg. key=value, GIT_AUTH_TOKEN=mytoken)"
|
description: "List of secrets to expose to the build (eg. key=string, GIT_AUTH_TOKEN=mytoken)"
|
||||||
|
required: false
|
||||||
|
secret-files:
|
||||||
|
description: "List of secret files to expose to the build (eg. key=filename, MY_SECRET=./secret.txt)"
|
||||||
|
required: false
|
||||||
|
ssh:
|
||||||
|
description: "List of SSH agent socket or keys to expose to the build"
|
||||||
required: false
|
required: false
|
||||||
github-token:
|
github-token:
|
||||||
description: "GitHub Token used to authenticate against a repository for Git context"
|
description: "GitHub Token used to authenticate against a repository for Git context"
|
||||||
default: ${{ github.token }}
|
default: ${{ github.token }}
|
||||||
required: false
|
required: false
|
||||||
ssh:
|
|
||||||
description: "List of SSH agent socket or keys to expose to the build"
|
|
||||||
required: false
|
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
digest:
|
digest:
|
||||||
|
|
39
dist/index.js
generated
vendored
39
dist/index.js
generated
vendored
|
@ -4581,7 +4581,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.parseVersion = exports.getVersion = exports.isAvailable = exports.hasGitAuthToken = exports.isLocalOrTarExporter = exports.getSecret = exports.getImageID = exports.getImageIDFile = void 0;
|
exports.parseVersion = exports.getVersion = exports.isAvailable = exports.hasGitAuthToken = exports.isLocalOrTarExporter = exports.getSecret = exports.getSecretFile = exports.getSecretString = exports.getImageID = exports.getImageIDFile = void 0;
|
||||||
const sync_1 = __importDefault(__webpack_require__(750));
|
const sync_1 = __importDefault(__webpack_require__(750));
|
||||||
const fs_1 = __importDefault(__webpack_require__(747));
|
const fs_1 = __importDefault(__webpack_require__(747));
|
||||||
const path_1 = __importDefault(__webpack_require__(622));
|
const path_1 = __importDefault(__webpack_require__(622));
|
||||||
|
@ -4604,18 +4604,36 @@ function getImageID() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
exports.getImageID = getImageID;
|
exports.getImageID = getImageID;
|
||||||
function getSecret(kvp) {
|
function getSecretString(kvp) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
return getSecret(kvp, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports.getSecretString = getSecretString;
|
||||||
|
function getSecretFile(kvp) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
return getSecret(kvp, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports.getSecretFile = getSecretFile;
|
||||||
|
function getSecret(kvp, file) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const delimiterIndex = kvp.indexOf('=');
|
const delimiterIndex = kvp.indexOf('=');
|
||||||
const key = kvp.substring(0, delimiterIndex);
|
const key = kvp.substring(0, delimiterIndex);
|
||||||
const value = kvp.substring(delimiterIndex + 1);
|
let value = kvp.substring(delimiterIndex + 1);
|
||||||
if (key.length == 0 || value.length == 0) {
|
if (key.length == 0 || value.length == 0) {
|
||||||
throw new Error(`${kvp} is not a valid secret`);
|
throw new Error(`${kvp} is not a valid secret`);
|
||||||
}
|
}
|
||||||
|
if (file) {
|
||||||
|
if (!fs_1.default.existsSync(value)) {
|
||||||
|
throw new Error(`secret file ${value} not found`);
|
||||||
|
}
|
||||||
|
value = fs_1.default.readFileSync(value, { encoding: 'utf-8' });
|
||||||
|
}
|
||||||
const secretFile = context.tmpNameSync({
|
const secretFile = context.tmpNameSync({
|
||||||
tmpdir: context.tmpDir()
|
tmpdir: context.tmpDir()
|
||||||
});
|
});
|
||||||
yield fs_1.default.writeFileSync(secretFile, value);
|
fs_1.default.writeFileSync(secretFile, value);
|
||||||
return `id=${key},src=${secretFile}`;
|
return `id=${key},src=${secretFile}`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -13032,6 +13050,7 @@ function getInputs(defaultContext) {
|
||||||
cacheFrom: yield getInputList('cache-from', true),
|
cacheFrom: yield getInputList('cache-from', true),
|
||||||
cacheTo: yield getInputList('cache-to', true),
|
cacheTo: yield getInputList('cache-to', true),
|
||||||
secrets: yield getInputList('secrets', true),
|
secrets: yield getInputList('secrets', true),
|
||||||
|
secretFiles: yield getInputList('secret-files', true),
|
||||||
githubToken: core.getInput('github-token'),
|
githubToken: core.getInput('github-token'),
|
||||||
ssh: yield getInputList('ssh')
|
ssh: yield getInputList('ssh')
|
||||||
};
|
};
|
||||||
|
@ -13084,14 +13103,22 @@ function getBuildArgs(inputs, defaultContext, buildxVersion) {
|
||||||
}));
|
}));
|
||||||
yield exports.asyncForEach(inputs.secrets, (secret) => __awaiter(this, void 0, void 0, function* () {
|
yield exports.asyncForEach(inputs.secrets, (secret) => __awaiter(this, void 0, void 0, function* () {
|
||||||
try {
|
try {
|
||||||
args.push('--secret', yield buildx.getSecret(secret));
|
args.push('--secret', yield buildx.getSecretString(secret));
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
core.warning(err.message);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
yield exports.asyncForEach(inputs.secretFiles, (secretFile) => __awaiter(this, void 0, void 0, function* () {
|
||||||
|
try {
|
||||||
|
args.push('--secret', yield buildx.getSecretFile(secretFile));
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
core.warning(err.message);
|
core.warning(err.message);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
if (inputs.githubToken && !buildx.hasGitAuthToken(inputs.secrets) && inputs.context == defaultContext) {
|
if (inputs.githubToken && !buildx.hasGitAuthToken(inputs.secrets) && inputs.context == defaultContext) {
|
||||||
args.push('--secret', yield buildx.getSecret(`GIT_AUTH_TOKEN=${inputs.githubToken}`));
|
args.push('--secret', yield buildx.getSecretString(`GIT_AUTH_TOKEN=${inputs.githubToken}`));
|
||||||
}
|
}
|
||||||
yield exports.asyncForEach(inputs.ssh, (ssh) => __awaiter(this, void 0, void 0, function* () {
|
yield exports.asyncForEach(inputs.ssh, (ssh) => __awaiter(this, void 0, void 0, function* () {
|
||||||
args.push('--ssh', ssh);
|
args.push('--ssh', ssh);
|
||||||
|
|
|
@ -18,17 +18,34 @@ export async function getImageID(): Promise<string | undefined> {
|
||||||
return fs.readFileSync(iidFile, {encoding: 'utf-8'});
|
return fs.readFileSync(iidFile, {encoding: 'utf-8'});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getSecret(kvp: string): Promise<string> {
|
export async function getSecretString(kvp: string): Promise<string> {
|
||||||
|
return getSecret(kvp, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSecretFile(kvp: string): Promise<string> {
|
||||||
|
return getSecret(kvp, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSecret(kvp: string, file: boolean): Promise<string> {
|
||||||
const delimiterIndex = kvp.indexOf('=');
|
const delimiterIndex = kvp.indexOf('=');
|
||||||
const key = kvp.substring(0, delimiterIndex);
|
const key = kvp.substring(0, delimiterIndex);
|
||||||
const value = kvp.substring(delimiterIndex + 1);
|
let value = kvp.substring(delimiterIndex + 1);
|
||||||
if (key.length == 0 || value.length == 0) {
|
if (key.length == 0 || value.length == 0) {
|
||||||
throw new Error(`${kvp} is not a valid secret`);
|
throw new Error(`${kvp} is not a valid secret`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
if (!fs.existsSync(value)) {
|
||||||
|
throw new Error(`secret file ${value} not found`);
|
||||||
|
}
|
||||||
|
value = fs.readFileSync(value, {encoding: 'utf-8'});
|
||||||
|
}
|
||||||
|
|
||||||
const secretFile = context.tmpNameSync({
|
const secretFile = context.tmpNameSync({
|
||||||
tmpdir: context.tmpDir()
|
tmpdir: context.tmpDir()
|
||||||
});
|
});
|
||||||
await fs.writeFileSync(secretFile, value);
|
fs.writeFileSync(secretFile, value);
|
||||||
|
|
||||||
return `id=${key},src=${secretFile}`;
|
return `id=${key},src=${secretFile}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ export interface Inputs {
|
||||||
cacheFrom: string[];
|
cacheFrom: string[];
|
||||||
cacheTo: string[];
|
cacheTo: string[];
|
||||||
secrets: string[];
|
secrets: string[];
|
||||||
|
secretFiles: string[];
|
||||||
githubToken: string;
|
githubToken: string;
|
||||||
ssh: string[];
|
ssh: string[];
|
||||||
}
|
}
|
||||||
|
@ -73,6 +74,7 @@ export async function getInputs(defaultContext: string): Promise<Inputs> {
|
||||||
cacheFrom: await getInputList('cache-from', true),
|
cacheFrom: await getInputList('cache-from', true),
|
||||||
cacheTo: await getInputList('cache-to', true),
|
cacheTo: await getInputList('cache-to', true),
|
||||||
secrets: await getInputList('secrets', true),
|
secrets: await getInputList('secrets', true),
|
||||||
|
secretFiles: await getInputList('secret-files', true),
|
||||||
githubToken: core.getInput('github-token'),
|
githubToken: core.getInput('github-token'),
|
||||||
ssh: await getInputList('ssh')
|
ssh: await getInputList('ssh')
|
||||||
};
|
};
|
||||||
|
@ -123,13 +125,20 @@ async function getBuildArgs(inputs: Inputs, defaultContext: string, buildxVersio
|
||||||
});
|
});
|
||||||
await asyncForEach(inputs.secrets, async secret => {
|
await asyncForEach(inputs.secrets, async secret => {
|
||||||
try {
|
try {
|
||||||
args.push('--secret', await buildx.getSecret(secret));
|
args.push('--secret', await buildx.getSecretString(secret));
|
||||||
|
} catch (err) {
|
||||||
|
core.warning(err.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await asyncForEach(inputs.secretFiles, async secretFile => {
|
||||||
|
try {
|
||||||
|
args.push('--secret', await buildx.getSecretFile(secretFile));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
core.warning(err.message);
|
core.warning(err.message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (inputs.githubToken && !buildx.hasGitAuthToken(inputs.secrets) && inputs.context == defaultContext) {
|
if (inputs.githubToken && !buildx.hasGitAuthToken(inputs.secrets) && inputs.context == defaultContext) {
|
||||||
args.push('--secret', await buildx.getSecret(`GIT_AUTH_TOKEN=${inputs.githubToken}`));
|
args.push('--secret', await buildx.getSecretString(`GIT_AUTH_TOKEN=${inputs.githubToken}`));
|
||||||
}
|
}
|
||||||
await asyncForEach(inputs.ssh, async ssh => {
|
await asyncForEach(inputs.ssh, async ssh => {
|
||||||
args.push('--ssh', ssh);
|
args.push('--ssh', ssh);
|
||||||
|
|
Loading…
Reference in a new issue