Skip to content

Commit

Permalink
Switched AzureStaticWebApp to new swa client api and added custom bui…
Browse files Browse the repository at this point in the history
…ld variable functionality (#15353)

* added Azure Static Web App task

* accidental submodule fixed

* changed guid

* altered dependencies to fit with build restrictions

* added task to make-options and added task owners to codeowners

* added compatibility with api token as task input

* fixed task version

* added skip_app_build input

* switching to the new swa api

* small task.json capitilization change

* _

* merged task variables development branch

* updated version

* added config_file_location, added system.debug compatibility, added logic to delete the env file, and fixed a typo

* implemented and modified Anthony's changes to get build vars from the task environment

* changed wording

* fixed make script bug and other small changes

* minor helptext change

* updated task version

Co-authored-by: BrandonHopcraft <[email protected]>
Co-authored-by: shigupt202 <[email protected]>
Co-authored-by: Rohit Batra <[email protected]>
Co-authored-by: Anatoly Bolshakov <[email protected]>
Co-authored-by: Bishal Prasad <[email protected]>
  • Loading branch information
6 people authored Oct 27, 2021
1 parent 27a985a commit 2aa5a34
Show file tree
Hide file tree
Showing 6 changed files with 756 additions and 65 deletions.
38 changes: 38 additions & 0 deletions Tasks/AzureStaticWebAppV0/envVarDenylist.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[
"LANG",
"ORYX_PATH",
"HOSTNAME",
"VERBOSE",
"BASE_BRANCH",
"DOTNET_SKIP_FIRST_TIME_EXPERIENCE",
"BUILD_TIMEOUT_IN_MINUTES",
"BRANCH",
"ORYX_SDK_STORAGE_BASE_URL",
"NUGET_XMLDOC_MODE",
"DEPLOYMENT_PROVIDER",
"ORYX_PATHS",
"PWD",
"HOME",
"ENABLE_DYNAMIC_INSTALL",
"APP_LOCATION",
"REPOSITORY_URL",
"API_LOCATION",
"DEPLOYMENT_TOKEN",
"REPOSITORY_BASE",
"API_BUILD_COMMAND",
"DEBIAN_FLAVOR",
"SKIP_APP_BUILD",
"ORYX_BUILDIMAGE_TYPE",
"IS_PULL_REQUEST",
"OUTPUT_LOCATION",
"SHLVL",
"ORYX_AI_INSTRUMENTATION_KEY",
"CONFIG_FILE_LOCATION",
"APP_BUILD_COMMAND",
"PATH",
"GITHUB_WORKSPACE",
"ROUTES_LOCATION",
"ORIGINAL_PATH",
"DEPLOYMENT_ACTION",
"NUGET_PACKAGES"
]
150 changes: 125 additions & 25 deletions Tasks/AzureStaticWebAppV0/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
import path = require('path');
import tl = require('azure-pipelines-task-lib/task');
import trm = require('azure-pipelines-task-lib/toolrunner');
import fs = require('fs');

const appLocationInputName = 'app_location';
const appBuildCommandInputName = 'app_build_command';
const outputLocationInputName = 'output_location';
const apiLocationInputName = 'api_location';
const apiBuildCommandInputName = 'api_build_command';
const routesLocationInputName = 'routes_location';
const buildTimeoutInMinutesInputName = 'build_timeout_in_minutes';
const configFileLocationInputName = 'config_file_location';
const apiTokenInputName = 'azure_static_web_apps_api_token';

async function run() {
const envVarFilePath: string = path.join(__dirname, 'env.list');

try {
tl.setResourcePath(path.join(__dirname, 'task.json'));

Expand All @@ -19,39 +32,126 @@ async function run() {

bash.line(tl.getInput('args', false));

const deploymentClient = "mcr.microsoft.com/appsvc/staticappsclient:stable";

const workingDirectory: string = tl.getInput('cwd', false) || "";
const appLocation: string = tl.getInput('app_location', false) || "";
const appBuildCommand: string = tl.getInput('app_build_command', false) || "";
const outputLoction: string = tl.getInput('output_location', false) || "";
const apiLocation: string = tl.getInput('api_location', false) || "";
const apiBuildCommand: string = tl.getInput('api_build_command', false) || "";
const routesLocation: string = tl.getInput('routes_location', false) || "";
const skipAppBuild: boolean = tl.getBoolInput('skip_app_build', false);
const apiToken: string = process.env['azure_static_web_apps_api_token'] || tl.getInput('azure_static_web_apps_api_token', false) || "";

process.env['SWA_WORKING_DIR'] = workingDirectory;
process.env['SWA_APP_LOCATION'] = appLocation;
process.env['SWA_APP_BUILD_COMMAND'] = appBuildCommand;
process.env['SWA_OUTPUT_LOCATION'] = outputLoction;
process.env['SWA_API_LOCATION'] = apiLocation;
process.env['SWA_API_BUILD_COMMAND'] = apiBuildCommand;
process.env['SWA_ROUTES_LOCATION'] = routesLocation;
process.env['SWA_DEPLOYMENT_CLIENT'] = deploymentClient;
process.env['SWA_SKIP_APP_BUILD'] = skipAppBuild.toString();
process.env['SWA_API_TOKEN'] = apiToken;

await createDockerEnvVarFile(envVarFilePath);

const options = {
failOnStdErr: false
};

await bash.exec(<any>options);
tl.setResult(tl.TaskResult.Succeeded, null);
} catch (err) {
tl.setResult(tl.TaskResult.Failed, err);
} finally {
await fs.promises.unlink(envVarFilePath).catch(() => tl.warning("Unable to delete env file"));
}
}

async function createDockerEnvVarFile(envVarFilePath: string) {
var variableString: string = ""

const systemVariableNames: Set<string> = new Set<string>();

const addVariableToString = (envVarName: string, envVarValue: string) => variableString += envVarName + "=" + envVarValue + "\n"

const addSystemVariableToString = (envVarName: string, envVarValue: string) => {
addVariableToString(envVarName, envVarValue)
systemVariableNames.add(envVarName)
}

const addInputStringToString = (envVarName: string, envVarValue: string, inputName: string, ) => {
if (envVarValue.includes("\n")) {
throw "Input "+inputName+" is a multiline string and cannot be added to the build environment.";
}

addSystemVariableToString(envVarName, envVarValue)
}

const workingDirectory: string = tl.getInput('cwd', false) || process.env.SYSTEM_DEFAULTWORKINGDIRECTORY;
const appLocation: string = tl.getInput(appLocationInputName, false) || "";
const appBuildCommand: string = tl.getInput(appBuildCommandInputName, false) || "";
const outputLocation: string = tl.getInput(outputLocationInputName, false) || "";
const apiLocation: string = tl.getInput(apiLocationInputName, false) || "";
const apiBuildCommand: string = tl.getInput(apiBuildCommandInputName, false) || "";
const routesLocation: string = tl.getInput(routesLocationInputName, false) || "";
const buildTimeoutInMinutes: string = tl.getInput(buildTimeoutInMinutesInputName, false) || "";
const configFileLocation: string = tl.getInput(configFileLocationInputName, false) || "";

const skipAppBuild: boolean = tl.getBoolInput('skip_app_build', false);
const apiToken: string = process.env[apiTokenInputName] || tl.getInput(apiTokenInputName, false) || "";

const systemVerbose = getNullableBooleanFromString(process.env['SYSTEM_DEBUG']);
const inputVerbose = getNullableBooleanFromString(tl.getInput('verbose', false));

const verbose = inputVerbose === true ? true : (inputVerbose === false ? false : systemVerbose === true);

const deploymentClient = "mcr.microsoft.com/appsvc/staticappsclient:stable";
const containerWorkingDir = "/working_dir";

addInputStringToString("APP_LOCATION", appLocation, appLocationInputName);
addInputStringToString("APP_BUILD_COMMAND", appBuildCommand, appBuildCommandInputName);
addInputStringToString("OUTPUT_LOCATION", outputLocation, outputLocationInputName);
addInputStringToString("API_LOCATION", apiLocation, apiLocationInputName);
addInputStringToString("API_BUILD_COMMAND", apiBuildCommand, apiBuildCommandInputName);
addInputStringToString("ROUTES_LOCATION", routesLocation, routesLocationInputName);
addInputStringToString("BUILD_TIMEOUT_IN_MINUTES", buildTimeoutInMinutes, buildTimeoutInMinutesInputName);
addInputStringToString("CONFIG_FILE_LOCATION", configFileLocation, configFileLocationInputName);

addSystemVariableToString("SKIP_APP_BUILD", skipAppBuild.toString());
addSystemVariableToString("VERBOSE", verbose.toString());

addInputStringToString("DEPLOYMENT_TOKEN", apiToken, apiTokenInputName);

process.env['SWA_DEPLOYMENT_CLIENT'] = deploymentClient;
process.env['SWA_WORKING_DIR'] = workingDirectory;
process.env['SWA_WORKSPACE_DIR'] = containerWorkingDir;

addSystemVariableToString("GITHUB_WORKSPACE", "");
addSystemVariableToString("DEPLOYMENT_PROVIDER", "DevOps");
addSystemVariableToString("REPOSITORY_URL", process.env.BUILD_REPOSITORY_URI || "");
addSystemVariableToString("IS_PULL_REQUEST", "");
addSystemVariableToString("BASE_BRANCH", "");
addSystemVariableToString("REPOSITORY_BASE", containerWorkingDir);
addSystemVariableToString("BRANCH", process.env.BUILD_SOURCEBRANCHNAME || "");
addSystemVariableToString("DEPLOYMENT_ACTION", "upload");

const denylistString = await fs.promises.readFile(path.join(__dirname, 'envVarDenylist.json'), 'utf8');
const denylist = JSON.parse(denylistString);

Object.keys(process.env).forEach( (envVarKey: string) => {
const envVarValue = process.env[envVarKey];

if (envVarValue.includes("\n")) {
tl.warning("Environment variable "+envVarKey+" is a multiline string and cannot be added to the build environment.");
return;
}

if (systemVariableNames.has(envVarKey)) {
tl.warning("custom variable overlapping with reserved SWA variable: " + envVarKey);
return;
}

if(!denylist.includes(envVarKey.toUpperCase())) {
addVariableToString(envVarKey, envVarValue);
}
});

await fs.promises.writeFile(envVarFilePath, variableString);
}

function getNullableBooleanFromString(boolString:string): boolean {
if (boolString == null) return null;
boolString = boolString.toLowerCase();

if(boolString === "true") {
return true;
}
catch (err) {
tl.setResult(tl.TaskResult.Failed, null);

if(boolString === "false") {
return false;
}

return null;
}

run();
27 changes: 3 additions & 24 deletions Tasks/AzureStaticWebAppV0/launch-docker.sh
Original file line number Diff line number Diff line change
@@ -1,26 +1,5 @@
workspace="/working_dir"
mount_dir=$SYSTEM_DEFAULTWORKINGDIRECTORY
[[ -n "$SWA_WORKING_DIR" ]] && mount_dir=$SWA_WORKING_DIR

params=()

[[ ! -z "$SWA_APP_LOCATION" ]] && params+=(-e "INPUT_APP_LOCATION=$SWA_APP_LOCATION")
[[ ! -z "$SWA_APP_BUILD_COMMAND" ]] && params+=(-e "INPUT_APP_BUILD_COMMAND=$SWA_APP_BUILD_COMMAND")
[[ ! -z "$SWA_OUTPUT_LOCATION" ]] && params+=(-e "INPUT_OUTPUT_LOCATION=$SWA_OUTPUT_LOCATION")
[[ ! -z "$SWA_API_LOCATION" ]] && params+=(-e "INPUT_API_LOCATION=$SWA_API_LOCATION")
[[ ! -z "$SWA_API_BUILD_COMMAND" ]] && params+=(-e "INPUT_API_BUILD_COMMAND=$SWA_API_BUILD_COMMAND")
[[ ! -z "$SWA_ROUTES_LOCATION" ]] && params+=(-e "INPUT_ROUTES_LOCATION=$SWA_ROUTES_LOCATION")

params+=(-e "INPUT_SKIP_APP_BUILD=$SWA_SKIP_APP_BUILD")

docker run \
-e INPUT_AZURE_STATIC_WEB_APPS_API_TOKEN="$SWA_API_TOKEN" \
-e GITHUB_WORKSPACE=$workspace \
-e DEPLOYMENT_PROVIDER=DevOps \
-e REPOSITORY_URL="$BUILD_REPOSITORY_URI" \
-e IS_PULL_REQUEST=false \
-e BASE_BRANCH="$BUILD_SOURCEBRANCHNAME" \
"${params[@]}" \
-v "$mount_dir:$workspace" \
--env-file ./env.list \
-v "$SWA_WORKING_DIR:$SWA_WORKSPACE_DIR" \
"$SWA_DEPLOYMENT_CLIENT" \
./bin/staticsites/StaticSitesClient upload
./bin/staticsites/StaticSitesClient run
7 changes: 7 additions & 0 deletions Tasks/AzureStaticWebAppV0/make.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"cp": [
{
"source": "envVarDenylist.json"
}
]
}
Loading

0 comments on commit 2aa5a34

Please sign in to comment.