Skip to content

Commit

Permalink
Support build arguments for Dockerfile and builder scenario
Browse files Browse the repository at this point in the history
  • Loading branch information
cormacpayne committed Oct 30, 2023
1 parent f5079b5 commit dfae556
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 16 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ For more information on the structure of the YAML configuration file, please vis
| `containerAppEnvironment` | No | The name of the Container App environment to use with the application. If not provided, an existing environment in the resource group of the Container App will be used, otherwise, an environment will be created in the formation `<container-app-name>-env`. |
| `runtimeStack` | No | The platform version stack used in the final runnable application image that is deployed to the Container App. The value should be provided in the formation `<platform>:<version>`. If not provided, this value is determined by Oryx based on the contents of the provided application. Please refer to [this document](https://github.com/microsoft/Oryx/blob/main/doc/supportedRuntimeVersions.md) for more information on supported runtime stacks for Oryx. |
| `builderStack` | No | The stack (OS) that should be used to build the provided application source and produce the runnable application image. You can provide a specific image tag for the stack, such as "debian-bookworm-20231004.1", or you can provide a supported stack name, such as "debian-bookworm" or "debian-bullseye", and the latest supported image tag for that stack will be used. If no stack is provided, this action will attempt to build the provided application source with each supported stack until there's a successful build. |
| `buildArguments` | No | A list of build arguments provided as KEY=VALUE pairings and are comma-separated. If the a Dockerfile has been provided or is discovered in the application source, each build argument will be passed to the `docker build` command via the `--build-arg` flag. If the Oryx++ builder is used to create a runnable application image, each build argument will be passed to the `pack build` command via the `--env` flag. |
| `targetPort` | No | The designated port for the application to run on. If no value is provided and the builder is used to build the runnable application image, the target port will be set to 80 for Python applications and 8080 for all other platform applications. If no value is provided when creating a Container App, the target port will default to 80. Note: when using this action to update a Container App, the target port may be updated if not provided based on changes to the ingress property. |
| `location` | No | The location that the Container App (and other created resources) will be deployed to. To view locations suitable for creating the Container App in, please run the following: `az provider show -n Microsoft.App --query "resourceTypes[?resourceType=='containerApps'].locations"` |
| `environmentVariables` | No | A list of environment variable(s) for the container. Space-separated values in 'key=value' format. Empty string to clear existing values. Prefix value with 'secretref:' to reference a secret. |
Expand Down
7 changes: 7 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ inputs:
for that stack will be used. If no stack is provided, this action will attempt to build the provided application
source with each supported stack until there's a successful build.'
required: false
buildArguments:
description: |
'A list of build arguments provided as KEY=VALUE pairings and are comma-separated. If the a Dockerfile has been
provided or is discovered in the application source, each build argument will be passed to the "docker build"
command via the --build-arg flag. If the Oryx++ builder is used to create a runnable application image, each
build argument will be passed to the "pack build" command via the --env flag.'
required: false
targetPort:
description: |
'The designated port for the application to run on. If no value is provided and the builder is used to build the
Expand Down
28 changes: 23 additions & 5 deletions azurecontainerapps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,13 @@ export class azurecontainerapps {
this.toolHelper.writeInfo(`Default image to deploy: ${this.imageToDeploy}`);
}

// Set up the build arguments to pass to the Dockerfile or builder
let buildArguments: string[] = [];
const buildArgumentsValue = this.toolHelper.getInput('buildArguments', false);
if (!this.util.isNullOrEmpty(buildArgumentsValue)) {
buildArguments = buildArgumentsValue.split(',');
}

// Get Dockerfile to build, if provided, or check if one exists at the root of the provided application
let dockerfilePath: string = this.toolHelper.getInput('dockerfilePath', false);
if (this.util.isNullOrEmpty(dockerfilePath)) {
Expand All @@ -371,15 +378,15 @@ export class azurecontainerapps {
dockerfilePath = rootDockerfilePath;
} else {
// No Dockerfile found or provided, build the image using the builder
await this.buildImageFromBuilderAsync(this.appSourcePath, this.imageToBuild);
await this.buildImageFromBuilderAsync(this.appSourcePath, this.imageToBuild, buildArguments);
}
} else {
dockerfilePath = path.join(this.appSourcePath, dockerfilePath);
}

if (!this.util.isNullOrEmpty(dockerfilePath)) {
// Build the image from the provided/discovered Dockerfile
await this.buildImageFromDockerfile(this.appSourcePath, dockerfilePath, this.imageToBuild);
await this.buildImageFromDockerfile(this.appSourcePath, dockerfilePath, this.imageToBuild, buildArguments);
}

// Push the image to the Container Registry
Expand All @@ -390,8 +397,9 @@ export class azurecontainerapps {
* Builds a runnable application image using the builder.
* @param appSourcePath - The path to the application source code.
* @param imageToBuild - The name of the image to build.
* @param buildArguments - The build arguments to pass to the builder.
*/
private static async buildImageFromBuilderAsync(appSourcePath: string, imageToBuild: string) {
private static async buildImageFromBuilderAsync(appSourcePath: string, imageToBuild: string, buildArguments: string[]) {
// Install the pack CLI
await this.appHelper.installPackCliAsync();
this.toolHelper.writeInfo(`Successfully installed the pack CLI.`);
Expand Down Expand Up @@ -421,6 +429,11 @@ export class azurecontainerapps {
environmentVariables.push(`ORYX_RUNTIME_PORT=${this.targetPort}`);
}

// Provide any additional build arguments to the builder
if (buildArguments.length > 0) {
environmentVariables = environmentVariables.concat(buildArguments);
}

this.toolHelper.writeInfo(`Building image "${imageToBuild}" using the Oryx++ Builder`);

// Set the Oryx++ Builder as the default builder locally
Expand All @@ -438,10 +451,15 @@ export class azurecontainerapps {
* @param appSourcePath - The path to the application source code.
* @param dockerfilePath - The path to the Dockerfile to build.
* @param imageToBuild - The name of the image to build.
* @param buildArguments - The build arguments to pass to the Dockerfile.
*/
private static async buildImageFromDockerfile(appSourcePath: string, dockerfilePath: string, imageToBuild: string) {
private static async buildImageFromDockerfile(
appSourcePath: string,
dockerfilePath: string,
imageToBuild: string,
buildArguments: string[]) {
this.toolHelper.writeInfo(`Building image "${imageToBuild}" using the provided Dockerfile`);
await this.appHelper.createRunnableAppImageFromDockerfile(imageToBuild, appSourcePath, dockerfilePath);
await this.appHelper.createRunnableAppImageFromDockerfile(imageToBuild, appSourcePath, dockerfilePath, buildArguments);

// If telemetry is enabled, log that the Dockerfile scenario was targeted for this task
this.telemetryHelper.setDockerfileScenario();
Expand Down
37 changes: 27 additions & 10 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ var azurecontainerapps = /** @class */ (function () {
*/
azurecontainerapps.buildAndPushImageAsync = function () {
return __awaiter(this, void 0, void 0, function () {
var imageRepository, dockerfilePath, rootDockerfilePath;
var imageRepository, buildArguments, buildArgumentsValue, dockerfilePath, rootDockerfilePath;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
Expand All @@ -432,6 +432,11 @@ var azurecontainerapps = /** @class */ (function () {
this.imageToDeploy = this.imageToBuild;
this.toolHelper.writeInfo("Default image to deploy: ".concat(this.imageToDeploy));
}
buildArguments = [];
buildArgumentsValue = this.toolHelper.getInput('buildArguments', false);
if (!this.util.isNullOrEmpty(buildArgumentsValue)) {
buildArguments = buildArgumentsValue.split(',');
}
dockerfilePath = this.toolHelper.getInput('dockerfilePath', false);
if (!this.util.isNullOrEmpty(dockerfilePath)) return [3 /*break*/, 4];
this.toolHelper.writeInfo("No Dockerfile path provided; checking for Dockerfile at root of application source.");
Expand All @@ -442,7 +447,7 @@ var azurecontainerapps = /** @class */ (function () {
return [3 /*break*/, 3];
case 1:
// No Dockerfile found or provided, build the image using the builder
return [4 /*yield*/, this.buildImageFromBuilderAsync(this.appSourcePath, this.imageToBuild)];
return [4 /*yield*/, this.buildImageFromBuilderAsync(this.appSourcePath, this.imageToBuild, buildArguments)];
case 2:
// No Dockerfile found or provided, build the image using the builder
_a.sent();
Expand All @@ -454,7 +459,7 @@ var azurecontainerapps = /** @class */ (function () {
case 5:
if (!!this.util.isNullOrEmpty(dockerfilePath)) return [3 /*break*/, 7];
// Build the image from the provided/discovered Dockerfile
return [4 /*yield*/, this.buildImageFromDockerfile(this.appSourcePath, dockerfilePath, this.imageToBuild)];
return [4 /*yield*/, this.buildImageFromDockerfile(this.appSourcePath, dockerfilePath, this.imageToBuild, buildArguments)];
case 6:
// Build the image from the provided/discovered Dockerfile
_a.sent();
Expand All @@ -474,8 +479,9 @@ var azurecontainerapps = /** @class */ (function () {
* Builds a runnable application image using the builder.
* @param appSourcePath - The path to the application source code.
* @param imageToBuild - The name of the image to build.
* @param buildArguments - The build arguments to pass to the builder.
*/
azurecontainerapps.buildImageFromBuilderAsync = function (appSourcePath, imageToBuild) {
azurecontainerapps.buildImageFromBuilderAsync = function (appSourcePath, imageToBuild, buildArguments) {
return __awaiter(this, void 0, void 0, function () {
var environmentVariables, runtimeStack, runtimeStackSplit, platformName, platformVersion, builderStack;
return __generator(this, function (_a) {
Expand Down Expand Up @@ -507,6 +513,10 @@ var azurecontainerapps = /** @class */ (function () {
if (!this.util.isNullOrEmpty(this.targetPort)) {
environmentVariables.push("ORYX_RUNTIME_PORT=".concat(this.targetPort));
}
// Provide any additional build arguments to the builder
if (buildArguments.length > 0) {
environmentVariables = environmentVariables.concat(buildArguments);
}
this.toolHelper.writeInfo("Building image \"".concat(imageToBuild, "\" using the Oryx++ Builder"));
// Set the Oryx++ Builder as the default builder locally
return [4 /*yield*/, this.appHelper.setDefaultBuilder()];
Expand All @@ -530,14 +540,15 @@ var azurecontainerapps = /** @class */ (function () {
* @param appSourcePath - The path to the application source code.
* @param dockerfilePath - The path to the Dockerfile to build.
* @param imageToBuild - The name of the image to build.
* @param buildArguments - The build arguments to pass to the Dockerfile.
*/
azurecontainerapps.buildImageFromDockerfile = function (appSourcePath, dockerfilePath, imageToBuild) {
azurecontainerapps.buildImageFromDockerfile = function (appSourcePath, dockerfilePath, imageToBuild, buildArguments) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
this.toolHelper.writeInfo("Building image \"".concat(imageToBuild, "\" using the provided Dockerfile"));
return [4 /*yield*/, this.appHelper.createRunnableAppImageFromDockerfile(imageToBuild, appSourcePath, dockerfilePath)];
return [4 /*yield*/, this.appHelper.createRunnableAppImageFromDockerfile(imageToBuild, appSourcePath, dockerfilePath, buildArguments)];
case 1:
_a.sent();
// If telemetry is enabled, log that the Dockerfile scenario was targeted for this task
Expand Down Expand Up @@ -5215,18 +5226,24 @@ var ContainerAppHelper = /** @class */ (function () {
* @param appSourcePath - the path to the application source on the machine
* @param dockerfilePath - the path to the Dockerfile to build and tag with the provided image name
*/
ContainerAppHelper.prototype.createRunnableAppImageFromDockerfile = function (imageToDeploy, appSourcePath, dockerfilePath) {
ContainerAppHelper.prototype.createRunnableAppImageFromDockerfile = function (imageToDeploy, appSourcePath, dockerfilePath, buildArguments) {
return __awaiter(this, void 0, void 0, function () {
var command, err_16;
var command_5, err_16;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
toolHelper.writeDebug("Attempting to create a runnable application image from the provided/found Dockerfile \"".concat(dockerfilePath, "\" with image name \"").concat(imageToDeploy, "\""));
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
command = "docker build --file ".concat(dockerfilePath, " ").concat(appSourcePath, " --tag ").concat(imageToDeploy);
return [4 /*yield*/, util.execute(command)];
command_5 = "docker build --file ".concat(dockerfilePath, " ").concat(appSourcePath, " --tag ").concat(imageToDeploy);
// If build arguments were provided, append them to the command
if (buildArguments.length > 0) {
buildArguments.forEach(function (buildArg) {
command_5 += " --build-arg ".concat(buildArg);
});
}
return [4 /*yield*/, util.execute(command_5)];
case 2:
_a.sent();
toolHelper.writeDebug("Successfully created runnable application image from the provided/found Dockerfile \"".concat(dockerfilePath, "\" with image name \"").concat(imageToDeploy, "\""));
Expand Down
11 changes: 10 additions & 1 deletion src/ContainerAppHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,10 +367,19 @@ export class ContainerAppHelper {
public async createRunnableAppImageFromDockerfile(
imageToDeploy: string,
appSourcePath: string,
dockerfilePath: string) {
dockerfilePath: string,
buildArguments: string[]) {
toolHelper.writeDebug(`Attempting to create a runnable application image from the provided/found Dockerfile "${dockerfilePath}" with image name "${imageToDeploy}"`);
try {
let command = `docker build --file ${dockerfilePath} ${appSourcePath} --tag ${imageToDeploy}`;

// If build arguments were provided, append them to the command
if (buildArguments.length > 0) {
buildArguments.forEach(function (buildArg: string) {
command += ` --build-arg ${buildArg}`;
});
}

await util.execute(command);
toolHelper.writeDebug(`Successfully created runnable application image from the provided/found Dockerfile "${dockerfilePath}" with image name "${imageToDeploy}"`);
} catch (err) {
Expand Down

0 comments on commit dfae556

Please sign in to comment.