Skip to content

Commit 2aa5a34

Browse files
brhopcraBrandonHopcraftshigupt202rohit-batraAnatoly Bolshakov
authored
Switched AzureStaticWebApp to new swa client api and added custom build 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]>
1 parent 27a985a commit 2aa5a34

File tree

6 files changed

+756
-65
lines changed

6 files changed

+756
-65
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
[
2+
"LANG",
3+
"ORYX_PATH",
4+
"HOSTNAME",
5+
"VERBOSE",
6+
"BASE_BRANCH",
7+
"DOTNET_SKIP_FIRST_TIME_EXPERIENCE",
8+
"BUILD_TIMEOUT_IN_MINUTES",
9+
"BRANCH",
10+
"ORYX_SDK_STORAGE_BASE_URL",
11+
"NUGET_XMLDOC_MODE",
12+
"DEPLOYMENT_PROVIDER",
13+
"ORYX_PATHS",
14+
"PWD",
15+
"HOME",
16+
"ENABLE_DYNAMIC_INSTALL",
17+
"APP_LOCATION",
18+
"REPOSITORY_URL",
19+
"API_LOCATION",
20+
"DEPLOYMENT_TOKEN",
21+
"REPOSITORY_BASE",
22+
"API_BUILD_COMMAND",
23+
"DEBIAN_FLAVOR",
24+
"SKIP_APP_BUILD",
25+
"ORYX_BUILDIMAGE_TYPE",
26+
"IS_PULL_REQUEST",
27+
"OUTPUT_LOCATION",
28+
"SHLVL",
29+
"ORYX_AI_INSTRUMENTATION_KEY",
30+
"CONFIG_FILE_LOCATION",
31+
"APP_BUILD_COMMAND",
32+
"PATH",
33+
"GITHUB_WORKSPACE",
34+
"ROUTES_LOCATION",
35+
"ORIGINAL_PATH",
36+
"DEPLOYMENT_ACTION",
37+
"NUGET_PACKAGES"
38+
]

Tasks/AzureStaticWebAppV0/index.ts

+125-25
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,21 @@
11
import path = require('path');
22
import tl = require('azure-pipelines-task-lib/task');
33
import trm = require('azure-pipelines-task-lib/toolrunner');
4+
import fs = require('fs');
5+
6+
const appLocationInputName = 'app_location';
7+
const appBuildCommandInputName = 'app_build_command';
8+
const outputLocationInputName = 'output_location';
9+
const apiLocationInputName = 'api_location';
10+
const apiBuildCommandInputName = 'api_build_command';
11+
const routesLocationInputName = 'routes_location';
12+
const buildTimeoutInMinutesInputName = 'build_timeout_in_minutes';
13+
const configFileLocationInputName = 'config_file_location';
14+
const apiTokenInputName = 'azure_static_web_apps_api_token';
415

516
async function run() {
17+
const envVarFilePath: string = path.join(__dirname, 'env.list');
18+
619
try {
720
tl.setResourcePath(path.join(__dirname, 'task.json'));
821

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

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

22-
const deploymentClient = "mcr.microsoft.com/appsvc/staticappsclient:stable";
23-
24-
const workingDirectory: string = tl.getInput('cwd', false) || "";
25-
const appLocation: string = tl.getInput('app_location', false) || "";
26-
const appBuildCommand: string = tl.getInput('app_build_command', false) || "";
27-
const outputLoction: string = tl.getInput('output_location', false) || "";
28-
const apiLocation: string = tl.getInput('api_location', false) || "";
29-
const apiBuildCommand: string = tl.getInput('api_build_command', false) || "";
30-
const routesLocation: string = tl.getInput('routes_location', false) || "";
31-
const skipAppBuild: boolean = tl.getBoolInput('skip_app_build', false);
32-
const apiToken: string = process.env['azure_static_web_apps_api_token'] || tl.getInput('azure_static_web_apps_api_token', false) || "";
33-
34-
process.env['SWA_WORKING_DIR'] = workingDirectory;
35-
process.env['SWA_APP_LOCATION'] = appLocation;
36-
process.env['SWA_APP_BUILD_COMMAND'] = appBuildCommand;
37-
process.env['SWA_OUTPUT_LOCATION'] = outputLoction;
38-
process.env['SWA_API_LOCATION'] = apiLocation;
39-
process.env['SWA_API_BUILD_COMMAND'] = apiBuildCommand;
40-
process.env['SWA_ROUTES_LOCATION'] = routesLocation;
41-
process.env['SWA_DEPLOYMENT_CLIENT'] = deploymentClient;
42-
process.env['SWA_SKIP_APP_BUILD'] = skipAppBuild.toString();
43-
process.env['SWA_API_TOKEN'] = apiToken;
44-
35+
await createDockerEnvVarFile(envVarFilePath);
36+
4537
const options = {
4638
failOnStdErr: false
4739
};
4840

4941
await bash.exec(<any>options);
5042
tl.setResult(tl.TaskResult.Succeeded, null);
43+
} catch (err) {
44+
tl.setResult(tl.TaskResult.Failed, err);
45+
} finally {
46+
await fs.promises.unlink(envVarFilePath).catch(() => tl.warning("Unable to delete env file"));
47+
}
48+
}
49+
50+
async function createDockerEnvVarFile(envVarFilePath: string) {
51+
var variableString: string = ""
52+
53+
const systemVariableNames: Set<string> = new Set<string>();
54+
55+
const addVariableToString = (envVarName: string, envVarValue: string) => variableString += envVarName + "=" + envVarValue + "\n"
56+
57+
const addSystemVariableToString = (envVarName: string, envVarValue: string) => {
58+
addVariableToString(envVarName, envVarValue)
59+
systemVariableNames.add(envVarName)
60+
}
61+
62+
const addInputStringToString = (envVarName: string, envVarValue: string, inputName: string, ) => {
63+
if (envVarValue.includes("\n")) {
64+
throw "Input "+inputName+" is a multiline string and cannot be added to the build environment.";
65+
}
66+
67+
addSystemVariableToString(envVarName, envVarValue)
68+
}
69+
70+
const workingDirectory: string = tl.getInput('cwd', false) || process.env.SYSTEM_DEFAULTWORKINGDIRECTORY;
71+
const appLocation: string = tl.getInput(appLocationInputName, false) || "";
72+
const appBuildCommand: string = tl.getInput(appBuildCommandInputName, false) || "";
73+
const outputLocation: string = tl.getInput(outputLocationInputName, false) || "";
74+
const apiLocation: string = tl.getInput(apiLocationInputName, false) || "";
75+
const apiBuildCommand: string = tl.getInput(apiBuildCommandInputName, false) || "";
76+
const routesLocation: string = tl.getInput(routesLocationInputName, false) || "";
77+
const buildTimeoutInMinutes: string = tl.getInput(buildTimeoutInMinutesInputName, false) || "";
78+
const configFileLocation: string = tl.getInput(configFileLocationInputName, false) || "";
79+
80+
const skipAppBuild: boolean = tl.getBoolInput('skip_app_build', false);
81+
const apiToken: string = process.env[apiTokenInputName] || tl.getInput(apiTokenInputName, false) || "";
82+
83+
const systemVerbose = getNullableBooleanFromString(process.env['SYSTEM_DEBUG']);
84+
const inputVerbose = getNullableBooleanFromString(tl.getInput('verbose', false));
85+
86+
const verbose = inputVerbose === true ? true : (inputVerbose === false ? false : systemVerbose === true);
87+
88+
const deploymentClient = "mcr.microsoft.com/appsvc/staticappsclient:stable";
89+
const containerWorkingDir = "/working_dir";
90+
91+
addInputStringToString("APP_LOCATION", appLocation, appLocationInputName);
92+
addInputStringToString("APP_BUILD_COMMAND", appBuildCommand, appBuildCommandInputName);
93+
addInputStringToString("OUTPUT_LOCATION", outputLocation, outputLocationInputName);
94+
addInputStringToString("API_LOCATION", apiLocation, apiLocationInputName);
95+
addInputStringToString("API_BUILD_COMMAND", apiBuildCommand, apiBuildCommandInputName);
96+
addInputStringToString("ROUTES_LOCATION", routesLocation, routesLocationInputName);
97+
addInputStringToString("BUILD_TIMEOUT_IN_MINUTES", buildTimeoutInMinutes, buildTimeoutInMinutesInputName);
98+
addInputStringToString("CONFIG_FILE_LOCATION", configFileLocation, configFileLocationInputName);
99+
100+
addSystemVariableToString("SKIP_APP_BUILD", skipAppBuild.toString());
101+
addSystemVariableToString("VERBOSE", verbose.toString());
102+
103+
addInputStringToString("DEPLOYMENT_TOKEN", apiToken, apiTokenInputName);
104+
105+
process.env['SWA_DEPLOYMENT_CLIENT'] = deploymentClient;
106+
process.env['SWA_WORKING_DIR'] = workingDirectory;
107+
process.env['SWA_WORKSPACE_DIR'] = containerWorkingDir;
108+
109+
addSystemVariableToString("GITHUB_WORKSPACE", "");
110+
addSystemVariableToString("DEPLOYMENT_PROVIDER", "DevOps");
111+
addSystemVariableToString("REPOSITORY_URL", process.env.BUILD_REPOSITORY_URI || "");
112+
addSystemVariableToString("IS_PULL_REQUEST", "");
113+
addSystemVariableToString("BASE_BRANCH", "");
114+
addSystemVariableToString("REPOSITORY_BASE", containerWorkingDir);
115+
addSystemVariableToString("BRANCH", process.env.BUILD_SOURCEBRANCHNAME || "");
116+
addSystemVariableToString("DEPLOYMENT_ACTION", "upload");
117+
118+
const denylistString = await fs.promises.readFile(path.join(__dirname, 'envVarDenylist.json'), 'utf8');
119+
const denylist = JSON.parse(denylistString);
120+
121+
Object.keys(process.env).forEach( (envVarKey: string) => {
122+
const envVarValue = process.env[envVarKey];
123+
124+
if (envVarValue.includes("\n")) {
125+
tl.warning("Environment variable "+envVarKey+" is a multiline string and cannot be added to the build environment.");
126+
return;
127+
}
128+
129+
if (systemVariableNames.has(envVarKey)) {
130+
tl.warning("custom variable overlapping with reserved SWA variable: " + envVarKey);
131+
return;
132+
}
133+
134+
if(!denylist.includes(envVarKey.toUpperCase())) {
135+
addVariableToString(envVarKey, envVarValue);
136+
}
137+
});
138+
139+
await fs.promises.writeFile(envVarFilePath, variableString);
140+
}
141+
142+
function getNullableBooleanFromString(boolString:string): boolean {
143+
if (boolString == null) return null;
144+
boolString = boolString.toLowerCase();
145+
146+
if(boolString === "true") {
147+
return true;
51148
}
52-
catch (err) {
53-
tl.setResult(tl.TaskResult.Failed, null);
149+
150+
if(boolString === "false") {
151+
return false;
54152
}
153+
154+
return null;
55155
}
56156

57157
run();
+3-24
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,5 @@
1-
workspace="/working_dir"
2-
mount_dir=$SYSTEM_DEFAULTWORKINGDIRECTORY
3-
[[ -n "$SWA_WORKING_DIR" ]] && mount_dir=$SWA_WORKING_DIR
4-
5-
params=()
6-
7-
[[ ! -z "$SWA_APP_LOCATION" ]] && params+=(-e "INPUT_APP_LOCATION=$SWA_APP_LOCATION")
8-
[[ ! -z "$SWA_APP_BUILD_COMMAND" ]] && params+=(-e "INPUT_APP_BUILD_COMMAND=$SWA_APP_BUILD_COMMAND")
9-
[[ ! -z "$SWA_OUTPUT_LOCATION" ]] && params+=(-e "INPUT_OUTPUT_LOCATION=$SWA_OUTPUT_LOCATION")
10-
[[ ! -z "$SWA_API_LOCATION" ]] && params+=(-e "INPUT_API_LOCATION=$SWA_API_LOCATION")
11-
[[ ! -z "$SWA_API_BUILD_COMMAND" ]] && params+=(-e "INPUT_API_BUILD_COMMAND=$SWA_API_BUILD_COMMAND")
12-
[[ ! -z "$SWA_ROUTES_LOCATION" ]] && params+=(-e "INPUT_ROUTES_LOCATION=$SWA_ROUTES_LOCATION")
13-
14-
params+=(-e "INPUT_SKIP_APP_BUILD=$SWA_SKIP_APP_BUILD")
15-
161
docker run \
17-
-e INPUT_AZURE_STATIC_WEB_APPS_API_TOKEN="$SWA_API_TOKEN" \
18-
-e GITHUB_WORKSPACE=$workspace \
19-
-e DEPLOYMENT_PROVIDER=DevOps \
20-
-e REPOSITORY_URL="$BUILD_REPOSITORY_URI" \
21-
-e IS_PULL_REQUEST=false \
22-
-e BASE_BRANCH="$BUILD_SOURCEBRANCHNAME" \
23-
"${params[@]}" \
24-
-v "$mount_dir:$workspace" \
2+
--env-file ./env.list \
3+
-v "$SWA_WORKING_DIR:$SWA_WORKSPACE_DIR" \
254
"$SWA_DEPLOYMENT_CLIENT" \
26-
./bin/staticsites/StaticSitesClient upload
5+
./bin/staticsites/StaticSitesClient run

Tasks/AzureStaticWebAppV0/make.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"cp": [
3+
{
4+
"source": "envVarDenylist.json"
5+
}
6+
]
7+
}

0 commit comments

Comments
 (0)