merge workdir into source input
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
This commit is contained in:
54
.github/workflows/ci.yml
vendored
54
.github/workflows/ci.yml
vendored
@@ -121,6 +121,27 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
error-source:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
-
|
||||||
|
name: Build
|
||||||
|
id: bake
|
||||||
|
continue-on-error: true
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
source: ./does-not-exist
|
||||||
|
-
|
||||||
|
name: Check
|
||||||
|
run: |
|
||||||
|
if [ "${{ steps.bake.outcome }}" != "failure" ] || [ "${{ steps.bake.conclusion }}" != "success" ]; then
|
||||||
|
echo "::error::Should have failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
standalone:
|
standalone:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
@@ -190,8 +211,7 @@ jobs:
|
|||||||
name: Build
|
name: Build
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
workdir: ./test/go
|
source: ./test/go
|
||||||
source: .
|
|
||||||
targets: binary
|
targets: binary
|
||||||
provenance: ${{ matrix.attrs }}
|
provenance: ${{ matrix.attrs }}
|
||||||
set: |
|
set: |
|
||||||
@@ -232,8 +252,7 @@ jobs:
|
|||||||
name: Build
|
name: Build
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
workdir: ./test/go
|
source: ./test/go
|
||||||
source: .
|
|
||||||
targets: ${{ matrix.target }}
|
targets: ${{ matrix.target }}
|
||||||
sbom: true
|
sbom: true
|
||||||
set: |
|
set: |
|
||||||
@@ -279,8 +298,7 @@ jobs:
|
|||||||
name: Build
|
name: Build
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
workdir: ./test/go
|
source: ./test/go
|
||||||
source: .
|
|
||||||
set: |
|
set: |
|
||||||
*.platform=linux/amd64
|
*.platform=linux/amd64
|
||||||
*.output=type=image,"name=localhost:5000/name/app:v1.0.0,localhost:5000/name/app:latest",push=true
|
*.output=type=image,"name=localhost:5000/name/app:v1.0.0,localhost:5000/name/app:latest",push=true
|
||||||
@@ -309,8 +327,7 @@ jobs:
|
|||||||
name: Build and push
|
name: Build and push
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
workdir: ./test/group
|
source: ./test/group
|
||||||
source: .
|
|
||||||
push: true
|
push: true
|
||||||
set: |
|
set: |
|
||||||
t1.tags=localhost:5000/name/app:t1
|
t1.tags=localhost:5000/name/app:t1
|
||||||
@@ -472,8 +489,7 @@ jobs:
|
|||||||
name: Build and push
|
name: Build and push
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
workdir: ./test/go
|
source: ./test/go
|
||||||
source: .
|
|
||||||
set: |
|
set: |
|
||||||
*.output=type=image,name=localhost:5000/name/app:latest,push=true
|
*.output=type=image,name=localhost:5000/name/app:latest,push=true
|
||||||
*.output=type=docker,name=app:local
|
*.output=type=docker,name=app:local
|
||||||
@@ -516,8 +532,7 @@ jobs:
|
|||||||
name: Build and push
|
name: Build and push
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
workdir: ./test/go
|
source: ./test/go
|
||||||
source: .
|
|
||||||
targets: image
|
targets: image
|
||||||
load: true
|
load: true
|
||||||
push: true
|
push: true
|
||||||
@@ -651,8 +666,7 @@ jobs:
|
|||||||
name: Build
|
name: Build
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
workdir: ./test
|
source: ./test
|
||||||
source: .
|
|
||||||
files: |
|
files: |
|
||||||
./lint.hcl
|
./lint.hcl
|
||||||
|
|
||||||
@@ -673,8 +687,7 @@ jobs:
|
|||||||
name: Build
|
name: Build
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
workdir: ./test
|
source: ./test
|
||||||
source: .
|
|
||||||
files: |
|
files: |
|
||||||
./lint.hcl
|
./lint.hcl
|
||||||
env:
|
env:
|
||||||
@@ -745,8 +758,7 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
workdir: ./test
|
source: ./test
|
||||||
source: .
|
|
||||||
files: |
|
files: |
|
||||||
./lint.hcl
|
./lint.hcl
|
||||||
call: check
|
call: check
|
||||||
@@ -778,8 +790,7 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
workdir: ./test
|
source: ./test
|
||||||
source: .
|
|
||||||
files: |
|
files: |
|
||||||
./lint.hcl
|
./lint.hcl
|
||||||
call: check
|
call: check
|
||||||
@@ -832,5 +843,4 @@ jobs:
|
|||||||
name: Build and push
|
name: Build and push
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
workdir: ./test/attest
|
source: ./test/attest
|
||||||
source: .
|
|
||||||
|
|||||||
77
README.md
77
README.md
@@ -23,6 +23,8 @@ ___
|
|||||||
* [environment variables](#environment-variables)
|
* [environment variables](#environment-variables)
|
||||||
* [Subactions](#subactions)
|
* [Subactions](#subactions)
|
||||||
* [`matrix`](subaction/matrix)
|
* [`matrix`](subaction/matrix)
|
||||||
|
* [Notes](#notes)
|
||||||
|
* [Source semantics](#source-semantics)
|
||||||
* [Contributing](#contributing)
|
* [Contributing](#contributing)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@@ -143,6 +145,31 @@ jobs:
|
|||||||
*.tags=user/app:latest
|
*.tags=user/app:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you point `source` to a subdirectory, relative paths are resolved from that
|
||||||
|
subdirectory:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
-
|
||||||
|
name: Build and push
|
||||||
|
uses: docker/bake-action@v6
|
||||||
|
with:
|
||||||
|
source: ./subdir
|
||||||
|
files: ./docker-bake.hcl
|
||||||
|
```
|
||||||
|
|
||||||
|
For example, if `./subdir/docker-bake.hcl` contains:
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
target "default" {
|
||||||
|
output = ["type=local,dest=./artifacts"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The output will be written to `./subdir/artifacts` in the workspace.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> More info about `source` semantics in the [Source semantics](#source-semantics) section.
|
||||||
|
|
||||||
## Summaries
|
## Summaries
|
||||||
|
|
||||||
This action generates a [job summary](https://github.blog/2022-05-09-supercharging-github-actions-with-job-summaries/)
|
This action generates a [job summary](https://github.blog/2022-05-09-supercharging-github-actions-with-job-summaries/)
|
||||||
@@ -197,23 +224,22 @@ The following inputs can be used as `step.with` keys
|
|||||||
> targets: default,release
|
> targets: default,release
|
||||||
> ```
|
> ```
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
|----------------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|----------------|-------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `builder` | String | Builder instance (see [setup-buildx](https://github.com/docker/setup-buildx-action) action) |
|
| `builder` | String | Builder instance (see [setup-buildx](https://github.com/docker/setup-buildx-action) action) |
|
||||||
| `workdir` | String | Working directory of execution |
|
| `allow` | List/CSV | Allow build to access specified resources (e.g., `network.host`) |
|
||||||
| `source` | String | Context to build from. Can be either local (`.`) or a [remote bake definition](https://docs.docker.com/build/bake/remote-definition/) |
|
| `call` | String | Set method for evaluating build (e.g., check) |
|
||||||
| `allow` | List/CSV | Allow build to access specified resources (e.g., `network.host`) |
|
| `files` | List/CSV | List of [bake definition files](https://docs.docker.com/build/customize/bake/file-definition/) |
|
||||||
| `call` | String | Set method for evaluating build (e.g., check) |
|
| `no-cache` | Bool | Do not use cache when building the image (default `false`) |
|
||||||
| `files` | List/CSV | List of [bake definition files](https://docs.docker.com/build/customize/bake/file-definition/) |
|
| `pull` | Bool | Always attempt to pull a newer version of the image (default `false`) |
|
||||||
| `no-cache` | Bool | Do not use cache when building the image (default `false`) |
|
| `load` | Bool | Load is a shorthand for `--set=*.output=type=docker` (default `false`) |
|
||||||
| `pull` | Bool | Always attempt to pull a newer version of the image (default `false`) |
|
| `provenance` | Bool/String | [Provenance](https://docs.docker.com/build/attestations/slsa-provenance/) is a shorthand for `--set=*.attest=type=provenance` |
|
||||||
| `load` | Bool | Load is a shorthand for `--set=*.output=type=docker` (default `false`) |
|
| `push` | Bool | Push is a shorthand for `--set=*.output=type=registry` (default `false`) |
|
||||||
| `provenance` | Bool/String | [Provenance](https://docs.docker.com/build/attestations/slsa-provenance/) is a shorthand for `--set=*.attest=type=provenance` |
|
| `sbom` | Bool/String | [SBOM](https://docs.docker.com/build/attestations/sbom/) is a shorthand for `--set=*.attest=type=sbom` |
|
||||||
| `push` | Bool | Push is a shorthand for `--set=*.output=type=registry` (default `false`) |
|
| `set` | List | List of [targets values to override](https://docs.docker.com/engine/reference/commandline/buildx_bake/#set) (e.g., `targetpattern.key=value`) |
|
||||||
| `sbom` | Bool/String | [SBOM](https://docs.docker.com/build/attestations/sbom/) is a shorthand for `--set=*.attest=type=sbom` |
|
| `source` | String | Build source to use. Supports local path and [remote bake definition](https://docs.docker.com/build/bake/remote-definition/). With a local path, Bake runs from that directory, so all relative paths are resolved from it. See [Source semantics](#source-semantics). |
|
||||||
| `set` | List | List of [targets values to override](https://docs.docker.com/engine/reference/commandline/buildx_bake/#set) (e.g., `targetpattern.key=value`) |
|
| `targets` | List/CSV | List of bake targets (`default` target used if empty) |
|
||||||
| `targets` | List/CSV | List of bake targets (`default` target used if empty) |
|
| `github-token` | String | API token used to authenticate to a Git repository for [remote definitions](https://docs.docker.com/build/bake/remote-definition/) (default `${{ github.token }}`) |
|
||||||
| `github-token` | String | API token used to authenticate to a Git repository for [remote definitions](https://docs.docker.com/build/bake/remote-definition/) (default `${{ github.token }}`) |
|
|
||||||
|
|
||||||
### outputs
|
### outputs
|
||||||
|
|
||||||
@@ -236,6 +262,23 @@ The following outputs are available
|
|||||||
|
|
||||||
* [`matrix`](subaction/matrix)
|
* [`matrix`](subaction/matrix)
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
### Source semantics
|
||||||
|
|
||||||
|
`source` accepts either a Git/remote bake definition (for example `{{defaultContext}}` or `{{defaultContext}}:subdir`)
|
||||||
|
or a local path (for example `.` or `./subdir`). When `source` is a local path,
|
||||||
|
the action runs Bake from that directory (equivalent to `cd <path> && docker buildx bake`).
|
||||||
|
|
||||||
|
This local path mode affects all relative paths resolved by Bake, not only
|
||||||
|
target `context` fields. This includes paths used by local outputs, cache
|
||||||
|
import/export, and `cwd://` references.
|
||||||
|
|
||||||
|
| `source` | Behavior |
|
||||||
|
|-----------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
|
||||||
|
| Git/remote (`{{defaultContext}}`, `https://...git#ref`, `...:subdir`) | Uses [remote bake definition](https://docs.docker.com/build/bake/remote-definition/) behavior. |
|
||||||
|
| Local path (`.`, `./subdir`) | Changes Bake working directory to that path before invoking Bake. |
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Want to contribute? Awesome! You can find information about contributing to
|
Want to contribute? Awesome! You can find information about contributing to
|
||||||
|
|||||||
@@ -368,11 +368,11 @@ describe('getArgs', () => {
|
|||||||
provenance: inp.provenance,
|
provenance: inp.provenance,
|
||||||
push: inp.push,
|
push: inp.push,
|
||||||
sbom: inp.sbom,
|
sbom: inp.sbom,
|
||||||
source: inp.source,
|
source: inp.source.remoteRef,
|
||||||
targets: inp.targets
|
targets: inp.targets
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
cwd: inp.workdir
|
cwd: inp.source.workdir,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const res = await context.getArgs(inp, definition, toolkit);
|
const res = await context.getArgs(inp, definition, toolkit);
|
||||||
|
|||||||
10
action.yml
10
action.yml
@@ -10,13 +10,6 @@ inputs:
|
|||||||
builder:
|
builder:
|
||||||
description: "Builder instance"
|
description: "Builder instance"
|
||||||
required: false
|
required: false
|
||||||
workdir:
|
|
||||||
description: "Working directory of bake execution"
|
|
||||||
required: false
|
|
||||||
default: '.'
|
|
||||||
source:
|
|
||||||
description: "Context to build from. Can be either local or a remote bake definition"
|
|
||||||
required: false
|
|
||||||
allow:
|
allow:
|
||||||
description: "Allow build to access specified resources (e.g., network.host)"
|
description: "Allow build to access specified resources (e.g., network.host)"
|
||||||
required: false
|
required: false
|
||||||
@@ -51,6 +44,9 @@ inputs:
|
|||||||
set:
|
set:
|
||||||
description: "List of targets values to override (eg. targetpattern.key=value)"
|
description: "List of targets values to override (eg. targetpattern.key=value)"
|
||||||
required: false
|
required: false
|
||||||
|
source:
|
||||||
|
description: "Context to build from. Can be either local to specify the working directory or a remote bake definition"
|
||||||
|
required: false
|
||||||
targets:
|
targets:
|
||||||
description: "List of bake targets"
|
description: "List of bake targets"
|
||||||
required: false
|
required: false
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import * as fs from 'fs';
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import * as handlebars from 'handlebars';
|
import * as handlebars from 'handlebars';
|
||||||
|
|
||||||
@@ -10,10 +11,13 @@ import {Util} from '@docker/actions-toolkit/lib/util.js';
|
|||||||
|
|
||||||
import {BakeDefinition} from '@docker/actions-toolkit/lib/types/buildx/bake.js';
|
import {BakeDefinition} from '@docker/actions-toolkit/lib/types/buildx/bake.js';
|
||||||
|
|
||||||
|
export interface BakeContext {
|
||||||
|
remoteRef?: string;
|
||||||
|
workdir?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Inputs {
|
export interface Inputs {
|
||||||
builder: string;
|
builder: string;
|
||||||
workdir: string;
|
|
||||||
source: string;
|
|
||||||
allow: string[];
|
allow: string[];
|
||||||
call: string;
|
call: string;
|
||||||
files: string[];
|
files: string[];
|
||||||
@@ -24,6 +28,7 @@ export interface Inputs {
|
|||||||
push: boolean;
|
push: boolean;
|
||||||
sbom: string;
|
sbom: string;
|
||||||
set: string[];
|
set: string[];
|
||||||
|
source: BakeContext;
|
||||||
targets: string[];
|
targets: string[];
|
||||||
'github-token': string;
|
'github-token': string;
|
||||||
}
|
}
|
||||||
@@ -31,8 +36,6 @@ export interface Inputs {
|
|||||||
export async function getInputs(): Promise<Inputs> {
|
export async function getInputs(): Promise<Inputs> {
|
||||||
return {
|
return {
|
||||||
builder: core.getInput('builder'),
|
builder: core.getInput('builder'),
|
||||||
workdir: core.getInput('workdir') || '.',
|
|
||||||
source: getSourceInput('source'),
|
|
||||||
allow: Util.getInputList('allow'),
|
allow: Util.getInputList('allow'),
|
||||||
call: core.getInput('call'),
|
call: core.getInput('call'),
|
||||||
files: Util.getInputList('files'),
|
files: Util.getInputList('files'),
|
||||||
@@ -43,6 +46,7 @@ export async function getInputs(): Promise<Inputs> {
|
|||||||
push: core.getBooleanInput('push'),
|
push: core.getBooleanInput('push'),
|
||||||
sbom: core.getInput('sbom'),
|
sbom: core.getInput('sbom'),
|
||||||
set: Util.getInputList('set', {ignoreComma: true, quote: false}),
|
set: Util.getInputList('set', {ignoreComma: true, quote: false}),
|
||||||
|
source: getBakeContext(core.getInput('source')),
|
||||||
targets: Util.getInputList('targets'),
|
targets: Util.getInputList('targets'),
|
||||||
'github-token': core.getInput('github-token')
|
'github-token': core.getInput('github-token')
|
||||||
};
|
};
|
||||||
@@ -59,8 +63,8 @@ export async function getArgs(inputs: Inputs, definition: BakeDefinition, toolki
|
|||||||
|
|
||||||
async function getBakeArgs(inputs: Inputs, definition: BakeDefinition, toolkit: Toolkit): Promise<Array<string>> {
|
async function getBakeArgs(inputs: Inputs, definition: BakeDefinition, toolkit: Toolkit): Promise<Array<string>> {
|
||||||
const args: Array<string> = ['bake'];
|
const args: Array<string> = ['bake'];
|
||||||
if (inputs.source) {
|
if (inputs.source.remoteRef) {
|
||||||
args.push(inputs.source);
|
args.push(inputs.source.remoteRef);
|
||||||
}
|
}
|
||||||
if (await toolkit.buildx.versionSatisfies('>=0.17.0')) {
|
if (await toolkit.buildx.versionSatisfies('>=0.17.0')) {
|
||||||
if (await toolkit.buildx.versionSatisfies('>=0.18.0')) {
|
if (await toolkit.buildx.versionSatisfies('>=0.18.0')) {
|
||||||
@@ -135,17 +139,26 @@ async function getCommonArgs(inputs: Inputs): Promise<Array<string>> {
|
|||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSourceInput(name: string): string {
|
function getBakeContext(sourceInput: string): BakeContext {
|
||||||
let source = handlebars.compile(core.getInput(name))({
|
let bakeContext = handlebars.compile(sourceInput)({
|
||||||
defaultContext: Context.gitContext()
|
defaultContext: Context.gitContext()
|
||||||
});
|
});
|
||||||
if (!source) {
|
if (!bakeContext) {
|
||||||
source = Context.gitContext();
|
bakeContext = Context.gitContext();
|
||||||
}
|
}
|
||||||
if (source === '.') {
|
if (Util.isValidRef(bakeContext)) {
|
||||||
source = '';
|
return {
|
||||||
|
remoteRef: bakeContext
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return source;
|
try {
|
||||||
|
fs.statSync(sourceInput).isDirectory();
|
||||||
|
} catch {
|
||||||
|
throw new Error(`Invalid source: ${sourceInput} does not exist or is not a directory.`);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
workdir: bakeContext
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function noDefaultAttestations(): boolean {
|
function noDefaultAttestations(): boolean {
|
||||||
|
|||||||
@@ -107,12 +107,12 @@ actionsToolkit.run(
|
|||||||
provenance: inputs.provenance,
|
provenance: inputs.provenance,
|
||||||
push: inputs.push,
|
push: inputs.push,
|
||||||
sbom: inputs.sbom,
|
sbom: inputs.sbom,
|
||||||
source: inputs.source,
|
source: inputs.source.remoteRef,
|
||||||
targets: inputs.targets,
|
targets: inputs.targets,
|
||||||
githubToken: gitAuthToken
|
githubToken: gitAuthToken
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
cwd: inputs.workdir
|
cwd: inputs.source.workdir
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -132,7 +132,7 @@ actionsToolkit.run(
|
|||||||
|
|
||||||
await core.group(`Bake definition`, async () => {
|
await core.group(`Bake definition`, async () => {
|
||||||
await Exec.getExecOutput(buildCmd.command, [...buildCmd.args, '--print'], {
|
await Exec.getExecOutput(buildCmd.command, [...buildCmd.args, '--print'], {
|
||||||
cwd: inputs.workdir,
|
cwd: inputs.source.workdir,
|
||||||
env: buildEnv,
|
env: buildEnv,
|
||||||
ignoreReturnCode: true
|
ignoreReturnCode: true
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
@@ -144,7 +144,7 @@ actionsToolkit.run(
|
|||||||
|
|
||||||
let err: Error | undefined;
|
let err: Error | undefined;
|
||||||
await Exec.getExecOutput(buildCmd.command, buildCmd.args, {
|
await Exec.getExecOutput(buildCmd.command, buildCmd.args, {
|
||||||
cwd: inputs.workdir,
|
cwd: inputs.source.workdir,
|
||||||
env: buildEnv,
|
env: buildEnv,
|
||||||
ignoreReturnCode: true
|
ignoreReturnCode: true
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
|
|||||||
@@ -40,8 +40,11 @@ export function setSummarySupported() {
|
|||||||
|
|
||||||
export function setSummaryInputs(inputs: Inputs) {
|
export function setSummaryInputs(inputs: Inputs) {
|
||||||
const res = {};
|
const res = {};
|
||||||
|
if (inputs.source.remoteRef || inputs.source.workdir) {
|
||||||
|
res['source'] = inputs.source.remoteRef || inputs.source.workdir;
|
||||||
|
}
|
||||||
for (const key of Object.keys(inputs)) {
|
for (const key of Object.keys(inputs)) {
|
||||||
if (key === 'github-token') {
|
if (key === 'source' || key === 'github-token') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const value: string | string[] | boolean = inputs[key];
|
const value: string | string[] | boolean = inputs[key];
|
||||||
|
|||||||
Reference in New Issue
Block a user