Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

With Azure CLI GitHub Action, you can automate your workflow by executing [Azure CLI](https://github.com/Azure/azure-cli) commands to manage Azure resources inside of an Action.

The action executes the Azure CLI Bash script on a user defined Azure CLI version. If the user does not specify a version, the version of Azure CLI installed on the agent is used. If there is no version of Azure CLI found on the agent, the action falls back the version to `latest`.
The action executes the Azure CLI Bash script on a user defined Azure CLI version. If the user does not specify a version, the version of Azure CLI installed on the agent is used. If there is no version of Azure CLI found on the agent, the action falls back the version to `latest` and it runs inside the container.
Read more about various Azure CLI versions [here](https://github.com/Azure/azure-cli/releases).

- `azcliversion` – **Optional** Example: 2.30.0, Default: set to az cli version of the agent.
Expand All @@ -14,7 +14,7 @@ Azure CLI GitHub Action is supported for the Azure public cloud as well as Azure
The definition of this GitHub Action is in [action.yml](https://github.com/Azure/CLI/blob/master/action.yml). The action status is determined by the exit code returned by the script rather than StandardError stream.

> [!NOTE]
> Please note that the action executes Azure CLI script in a docker container. This means that the action is subjected to potential restrictions which arise from containerized execution. For example:
> Please note that the action may execute Azure CLI script in a docker container. This means that the action is subjected to potential restrictions which arise from containerized execution. For example:
> 1. If script sets up an environment variable, it will not take effect in host and hence subsequent actions shouldn't rely on such environment variable.
> 2. There is some restriction on how cross action file read/write is done. `GITHUB_WORKSPACE` directory in host is mapped to working directory inside container. So, if the action wants to create a file, which will be read by subsequent actions, it should do so within current working directory tree.

Expand Down
2 changes: 1 addition & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ inputs:
description: 'Specify the script here'
required: true
azcliversion:
description: 'Azure CLI version to be used to execute the script. If not provided, latest version is used'
description: 'Azure CLI version to be used to execute the script. If not provided, the version on the agent is used. '
required: false
default: 'agentazcliversion'
branding:
Expand Down
2 changes: 1 addition & 1 deletion dist/index.js

Large diffs are not rendered by default.

65 changes: 63 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const cpExec = util.promisify(require('child_process').exec);
import { createScriptFile, TEMP_DIRECTORY, NullOutstreamStringWritable, deleteFile, getCurrentTime, checkIfEnvironmentVariableIsOmitted } from './utils';

const START_SCRIPT_EXECUTION_MARKER: string = "Starting script execution via docker image mcr.microsoft.com/azure-cli:";
const START_SCRIPT_DIRECT_EXECUTION_MARKER: string = "Starting script execution via az in the agent:";
const AZ_CLI_VERSION_DEFAULT_VALUE = 'agentazcliversion'
const prefix = !!process.env.AZURE_HTTP_USER_AGENT ? `${process.env.AZURE_HTTP_USER_AGENT}` : "";

Expand All @@ -21,6 +22,7 @@ export async function main() {

var scriptFileName: string = '';
const CONTAINER_NAME = `MICROSOFT_AZURE_CLI_${getCurrentTime()}_CONTAINER`;
let executingInContainer: boolean = true;
try {
if (process.env.RUNNER_OS != 'Linux') {
throw new Error('Please use Linux-based OS as a runner.');
Expand All @@ -33,6 +35,8 @@ export async function main() {
try {
const { stdout } = await cpExec('az version');
azcliversion = JSON.parse(stdout)["azure-cli"]
executingInContainer = false;
console.log(`az cli version from agent: ${azcliversion}. It will be used for script execution.`);
} catch (err) {
console.log('Failed to fetch az cli version from agent. Reverting back to latest.')
azcliversion = 'latest'
Expand All @@ -48,6 +52,21 @@ export async function main() {
core.error('Please enter a valid script.');
throw new Error('Please enter a valid script.')
}

if (executingInContainer == false) {
try {
inlineScript = ` set -e >&2; echo '${START_SCRIPT_DIRECT_EXECUTION_MARKER}' >&2; ${inlineScript}`;
scriptFileName = await createScriptFile(inlineScript);
let args: string[] = ["--noprofile", "--norc", "-e", `${TEMP_DIRECTORY}/${scriptFileName}`];
console.log(`${START_SCRIPT_DIRECT_EXECUTION_MARKER}`);
await executeBashCommand(args);
console.log("az script ran successfully.");
return;
} catch (err) {
console.log('Failed to fetch az cli version from agent. Reverting back to execution in container.')
}
}

inlineScript = ` set -e >&2; echo '${START_SCRIPT_EXECUTION_MARKER}' >&2; ${inlineScript}`;
scriptFileName = await createScriptFile(inlineScript);

Expand Down Expand Up @@ -84,8 +103,10 @@ export async function main() {
// clean up
const scriptFilePath: string = path.join(TEMP_DIRECTORY, scriptFileName);
await deleteFile(scriptFilePath);
console.log("cleaning up container...");
await executeDockerCommand(["rm", "--force", CONTAINER_NAME], true);
if (executingInContainer) {
console.log("cleaning up container...");
await executeDockerCommand(["rm", "--force", CONTAINER_NAME], true);
}
}
};

Expand Down Expand Up @@ -157,3 +178,43 @@ const executeDockerCommand = async (args: string[], continueOnError: boolean = f
core.warning(errorStream)
}
}

const executeBashCommand = async (args: string[], continueOnError: boolean = false): Promise<void> => {

const bash: string = await io.which("bash", true);
var errorStream: string = '';
var shouldOutputErrorStream: boolean = false;
var execOptions: any = {
outStream: new NullOutstreamStringWritable({ decodeStrings: false }),
listeners: {
stdout: (data: any) => console.log(data.toString()), //to log the script output while the script is running.
errline: (data: string) => {
if (!shouldOutputErrorStream) {
errorStream += data + os.EOL;
}
else {
console.log(data);
}
if (data.trim() === START_SCRIPT_EXECUTION_MARKER) {
shouldOutputErrorStream = true;
errorStream = ''; // Flush the container logs. After this, script error logs will be tracked.
}
}
}
};
var exitCode;
try {
exitCode = await exec.exec(bash, args, execOptions);
} catch (error) {
if (!continueOnError) {
throw error;
}
core.warning(error);
}
finally {
if (exitCode !== 0 && !continueOnError) {
throw new Error(errorStream || 'az cli script failed.');
}
core.warning(errorStream)
}
}