Allow to use secret file mount

Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
CrazyMax 2021-02-15 10:08:19 +01:00
parent e5f26cdae4
commit 080cadd33e
No known key found for this signature in database
GPG Key ID: 3248E46B6BB8C7F7
8 changed files with 115 additions and 29 deletions

View File

@ -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

View File

@ -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

View File

@ -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);
} }

View File

@ -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',

View File

@ -0,0 +1 @@
bar

39
dist/index.js generated vendored
View File

@ -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}`;
}); });
} }
@ -13003,6 +13021,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')
}; };
@ -13055,14 +13074,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);

View File

@ -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}`;
} }

View File

@ -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);