Skip to content

Commit

Permalink
feat: add build arguments to build page
Browse files Browse the repository at this point in the history
### What does this PR do?

Build arguments is already built-in to podman desktop API.

This adds to the UI the ability to pass in build arguments.

### Screenshot / video of UI

<!-- If this PR is changing UI, please include
screenshots or screencasts showing the difference -->

### What issues does this PR fix or reference?

<!-- Include any related issues from Podman Desktop
repository (or from another issue tracker). -->

Closes #7250

### How to test this PR?

<!-- Please explain steps to verify the functionality,
do not forget to provide unit/component tests -->

- [X] Tests are covering the bug fix or the new feature

1. Build a Containerfile with ARG
2. See it fail (normal)
3. In build args, add your arg (ex. foo=bar)
4. It will now build and pass.

Signed-off-by: Charlie Drage <charlie@charliedrage.com>
  • Loading branch information
cdrage committed May 17, 2024
1 parent 3a31932 commit 90c6f0c
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 1 deletion.
2 changes: 2 additions & 0 deletions packages/main/src/plugin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1094,6 +1094,7 @@ export class PluginSystem {
selectedProvider: ProviderContainerConnectionInfo,
onDataCallbacksBuildImageId: number,
cancellableTokenId?: number,
buildargs?: { [key: string]: string },
): Promise<unknown> => {
const abortController = this.createAbortControllerOnCancellationToken(
cancellationTokenRegistry,
Expand All @@ -1115,6 +1116,7 @@ export class PluginSystem {
platform,
provider: selectedProvider,
abortController,
buildargs,
},
);
},
Expand Down
2 changes: 2 additions & 0 deletions packages/preload/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1058,6 +1058,7 @@ export function initExposure(): void {
key: symbol,
eventCollect: (key: symbol, eventName: 'finish' | 'stream' | 'error', data: string) => void,
cancellableTokenId?: number,
buildargs?: { [key: string]: string },
): Promise<unknown> => {
onDataCallbacksBuildImageId++;
onDataCallbacksBuildImage.set(onDataCallbacksBuildImageId, eventCollect);
Expand All @@ -1071,6 +1072,7 @@ export function initExposure(): void {
selectedProvider,
onDataCallbacksBuildImageId,
cancellableTokenId,
buildargs,
);
},
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,3 +355,46 @@ test('Expect recommended extension in case of build error', async () => {
const proposal = screen.getByRole('button', { name: 'Install myExtension.id Extension' });
expect(proposal).toBeInTheDocument();
});

test('Expect build to include build arguments', async () => {
setup();
render(BuildImageFromContainerfile);

const containerFilePath = screen.getByRole('textbox', { name: 'Containerfile Path' });
expect(containerFilePath).toBeInTheDocument();
await userEvent.type(containerFilePath, '/somepath/containerfile');

const buildFolder = screen.getByRole('textbox', { name: 'Build Context Directory' });
expect(buildFolder).toBeInTheDocument();
await userEvent.type(buildFolder, '/somepath');

const containerImageName = screen.getByRole('textbox', { name: 'Image Name' });
expect(containerImageName).toBeInTheDocument();
await userEvent.type(containerImageName, 'foobar');

const addArgButton = screen.getByRole('label', { name: 'Add build argument' });
expect(addArgButton).toBeInTheDocument();
await userEvent.click(addArgButton);

const keyInputs = screen.getAllByPlaceholderText('Key');
await userEvent.type(keyInputs[1], 'ARG_KEY');

const valueInputs = screen.getAllByPlaceholderText('Value');
await userEvent.type(valueInputs[1], 'ARG_VALUE');

const buildButton = screen.getByRole('button', { name: 'Build' });
expect(buildButton).toBeInTheDocument();
expect(buildButton).toBeEnabled();
await userEvent.click(buildButton);

expect(window.buildImage).toHaveBeenCalledWith(
'/somepath',
'containerfile',
'foobar',
'linux/amd64',
expect.anything(),
expect.anything(),
expect.anything(),
expect.objectContaining({ ARG_KEY: 'ARG_VALUE' }),
);
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
/* eslint-disable import/no-duplicates */
// https://github.com/import-js/eslint-plugin-import/issues/1479
import { faCube } from '@fortawesome/free-solid-svg-icons';
import { faCube, faMinusCircle, faPlusCircle } from '@fortawesome/free-solid-svg-icons';
import { Button, Input } from '@podman-desktop/ui-svelte';
import { onDestroy, onMount } from 'svelte';
import { get } from 'svelte/store';
Expand Down Expand Up @@ -55,6 +55,24 @@ interface BuildOutputItem {
type BuildOutput = BuildOutputItem[];
let buildArgs: { key: string; value: string }[] = [{ key: '', value: '' }];
let formattedBuildArgs: Record<string, string> = {};
function addBuildArg() {
buildArgs = [...buildArgs, { key: '', value: '' }];
}
function deleteBuildArg(index: number) {
// Only one item in the list, clear the content
if (buildArgs.length === 1) {
buildArgs[index].key = '';
buildArgs[index].value = '';
} else {
// Remove the item from the array
buildArgs = buildArgs.filter((_, i) => i !== index);
}
}
function getTerminalCallback(): BuildImageCallback {
return {
onStream: function (data: string): void {
Expand Down Expand Up @@ -83,6 +101,14 @@ async function buildContainerImage(): Promise<void> {
buildParentImageName = undefined;
buildError = undefined;
// Create the formatted build arguments that will be used when passing to buildImage
formattedBuildArgs = buildArgs.reduce<Record<string, string>>((acc, { key, value }) => {
if (key && value) {
acc[key] = value;
}
return acc;
}, {});
// Pick if we are building a singular platform (which will just create the image)
// or multiple platforms (which will create the image and then create a manifest)
if (platforms.length === 1) {
Expand Down Expand Up @@ -119,6 +145,7 @@ async function buildSinglePlatformImage(): Promise<void> {
buildImageInfo.buildImageKey,
eventCollect,
cancellableTokenId,
formattedBuildArgs,
);
} catch (error) {
logsTerminal.write(`Error:${error}\r`);
Expand Down Expand Up @@ -167,6 +194,7 @@ async function buildMultiplePlatformImagesAndCreateManifest(): Promise<void> {
buildImageInfo.buildImageKey,
eventCollect,
cancellableTokenId,
formattedBuildArgs,
)) as BuildOutput;
// Extract and store the build ID as this is required for creating the manifest, only if it is available.
Expand Down Expand Up @@ -327,6 +355,21 @@ async function abortBuild() {
</label>
{/if}
</div>
<div hidden="{buildImageInfo?.buildRunning}">
<label for="inputKey" class="block mb-2 text-sm font-bold text-gray-400">Build arguments</label>
{#each buildArgs as buildArg, index}
<div class="flex flex-row items-center space-x-2 mb-2">
<Input bind:value="{buildArg.key}" name="inputKey" placeholder="Key" class="flex-grow" required />
<Input bind:value="{buildArg.value}" placeholder="Value" class="flex-grow" required />
<Button
on:click="{() => deleteBuildArg(index)}"
icon="{faMinusCircle}"
disabled="{buildArgs.length === 1 && buildArg.key === '' && buildArg.value === ''}"
aria-label="Delete build argument" />
<Button on:click="{addBuildArg}" icon="{faPlusCircle}" aria-label="Add build argument" />
</div>
{/each}
</div>

<div hidden="{buildImageInfo?.buildRunning}">
<label for="containerBuildPlatform" class="block mb-2 text-sm font-bold text-gray-400">Platform</label>
Expand Down

0 comments on commit 90c6f0c

Please sign in to comment.