Skip to content

Commit

Permalink
Migrate latest changes from v1 to v2 (#68)
Browse files Browse the repository at this point in the history
* Migrate latest changes from v1 to v2

* Support build arguments for Dockerfile and builder scenario

* Fix typo in docs
  • Loading branch information
cormacpayne authored Nov 7, 2023
1 parent 1c6a07a commit 43f6f95
Show file tree
Hide file tree
Showing 6 changed files with 462 additions and 289 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
# .gitignore
node_modules/
node_modules/
*.js
!dist/index.js
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 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 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
74 changes: 50 additions & 24 deletions azurecontainerapps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ export class azurecontainerapps {

// Miscellaneous properties
private static imageToBuild: string;
private static runtimeStack: string;
private static ingress: string;
private static targetPort: string;
private static shouldUseUpdateCommand: boolean;
Expand Down Expand Up @@ -362,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 @@ -372,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.builderImageFromDockerfile(this.appSourcePath, dockerfilePath, this.imageToBuild);
await this.buildImageFromDockerfile(this.appSourcePath, dockerfilePath, this.imageToBuild, buildArguments);
}

// Push the image to the Container Registry
Expand All @@ -391,17 +397,41 @@ 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.`)
this.toolHelper.writeInfo(`Successfully installed the pack CLI.`);

// Enable experimental features for the pack CLI
await this.appHelper.enablePackCliExperimentalFeaturesAsync();
this.toolHelper.writeInfo(`Successfully enabled experimental features for the pack CLI.`);

// Define the environment variables that should be propagated to the builder
let environmentVariables: string[] = []

// Parse the given runtime stack input and export the platform and version to environment variables
const runtimeStack = this.toolHelper.getInput('runtimeStack', false);
if (!this.util.isNullOrEmpty(runtimeStack)) {
const runtimeStackSplit = runtimeStack.split(':');
const platformName = runtimeStackSplit[0] == "dotnetcore" ? "dotnet" : runtimeStackSplit[0];
const platformVersion = runtimeStackSplit[1];
environmentVariables.push(`ORYX_PLATFORM_NAME=${platformName}`);
environmentVariables.push(`ORYX_PLATFORM_VERSION=${platformVersion}`);
}

// Check if the user provided a builder stack to use
const builderStack = this.toolHelper.getInput('builderStack', false);

// Set the target port on the image produced by the builder
if (!this.util.isNullOrEmpty(this.targetPort)) {
environmentVariables.push(`ORYX_RUNTIME_PORT=${this.targetPort}`);
}

// Get the runtime stack if provided, or determine it using Oryx
this.runtimeStack = this.toolHelper.getInput('runtimeStack', false);
if (this.util.isNullOrEmpty(this.runtimeStack)) {
this.runtimeStack = await this.appHelper.determineRuntimeStackAsync(appSourcePath);
this.toolHelper.writeInfo(`Runtime stack determined to be: ${this.runtimeStack}`);
// 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`);
Expand All @@ -410,7 +440,7 @@ export class azurecontainerapps {
await this.appHelper.setDefaultBuilder();

// Create a runnable application image
await this.appHelper.createRunnableAppImage(imageToBuild, appSourcePath, this.runtimeStack);
await this.appHelper.createRunnableAppImage(imageToBuild, appSourcePath, environmentVariables, builderStack);

// If telemetry is enabled, log that the builder scenario was targeted for this task
this.telemetryHelper.setBuilderScenario();
Expand All @@ -421,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 builderImageFromDockerfile(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 Expand Up @@ -474,19 +509,10 @@ export class azurecontainerapps {

// Handle setup for ingress values when enabled
if (this.ingressEnabled) {
// Get the target port if provided, or determine it based on the application type
// Get the target port if provided, or set it to the default value
this.targetPort = this.toolHelper.getInput('targetPort', false);
if (this.util.isNullOrEmpty(this.targetPort)) {
if (!this.util.isNullOrEmpty(this.runtimeStack) && this.runtimeStack.startsWith('python:')) {
this.targetPort = '80';
} else {
this.targetPort = '8080';
}

this.toolHelper.writeInfo(`Default target port: ${this.targetPort}`);
}

// Set the target port to 80 if it was not provided or determined
// Set the target port to 80 if it was not provided
if (this.util.isNullOrEmpty(this.targetPort)) {
this.targetPort = '80';
this.toolHelper.writeInfo(`Default target port: ${this.targetPort}`);
Expand Down
Loading

0 comments on commit 43f6f95

Please sign in to comment.