Skip to content

Commit bc11b09

Browse files
author
Denis Nikulin (Akvelon Inc)
committed
Moved the changes to the separated build config.
1 parent 5b052eb commit bc11b09

File tree

125 files changed

+15266
-3111
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

125 files changed

+15266
-3111
lines changed

BuildConfigGen/Program.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@ public record ConfigRecord(string name, string constMappingKey, bool isDefault,
4747
public static readonly ConfigRecord Node20_229_12 = new ConfigRecord(name: nameof(Node20_229_12), constMappingKey: "Node20_229_12", isDefault: false, isNode: true, nodePackageVersion: "^20.3.1", isWif: false, nodeHandler: "Node20_1", preprocessorVariableName: "NODE20", enableBuildConfigOverrides: true, deprecated: false, shouldUpdateTypescript: true, overriddenDirectoryName: "Node20", writeNpmrc: true);
4848
public static readonly ConfigRecord Node20_229_13 = new ConfigRecord(name: nameof(Node20_229_13), constMappingKey: "Node20_229_13", isDefault: false, isNode: true, nodePackageVersion: "^20.3.1", isWif: false, nodeHandler: "Node20_1", preprocessorVariableName: "NODE20", enableBuildConfigOverrides: true, deprecated: false, shouldUpdateTypescript: true, overriddenDirectoryName: "Node20", writeNpmrc: true);
4949
public static readonly ConfigRecord Node20_229_14 = new ConfigRecord(name: nameof(Node20_229_14), constMappingKey: "Node20_229_14", isDefault: false, isNode: true, nodePackageVersion: "^20.3.1", isWif: false, nodeHandler: "Node20_1", preprocessorVariableName: "NODE20", enableBuildConfigOverrides: true, deprecated: false, shouldUpdateTypescript: true, overriddenDirectoryName: "Node20", writeNpmrc: true);
50-
public static readonly ConfigRecord WorkloadIdentityFederation = new ConfigRecord(name: nameof(WorkloadIdentityFederation), constMappingKey: "WorkloadIdentityFederation", isDefault: false, isNode: true, nodePackageVersion: "^16.11.39", isWif: true, nodeHandler: "Node16", preprocessorVariableName: "WORKLOADIDENTITYFEDERATION", enableBuildConfigOverrides: true, deprecated: false, shouldUpdateTypescript: false, writeNpmrc: false);
51-
public static ConfigRecord[] Configs = { Default, Node16, Node16_225, Node20, Node20_228, Node20_229_1, Node20_229_2, Node20_229_3, Node20_229_4, Node20_229_5, Node20_229_6, Node20_229_7, Node20_229_8, Node20_229_9, Node20_229_10, Node20_229_11, Node20_229_12, Node20_229_13, Node20_229_14, WorkloadIdentityFederation };
50+
public static readonly ConfigRecord WorkloadIdentityFederation = new ConfigRecord(name: nameof(WorkloadIdentityFederation), constMappingKey: "WorkloadIdentityFederation", isDefault: false, isNode: false, nodePackageVersion: "^16.11.39", isWif: true, nodeHandler: "Node16", preprocessorVariableName: "WORKLOADIDENTITYFEDERATION", enableBuildConfigOverrides: true, deprecated: false, shouldUpdateTypescript: false, writeNpmrc: false);
51+
public static readonly ConfigRecord IssueSourceEnabled = new ConfigRecord(name: nameof(IssueSourceEnabled), constMappingKey: "", isDefault: false, isNode: false, nodePackageVersion: "", isWif: false, nodeHandler: "", preprocessorVariableName: "ISSUESOURCEENABLED", enableBuildConfigOverrides: false, deprecated: false, shouldUpdateTypescript: false, writeNpmrc: false);
52+
public static ConfigRecord[] Configs = { Default, Node16, Node16_225, Node20, Node20_228, Node20_229_1, Node20_229_2, Node20_229_3, Node20_229_4, Node20_229_5, Node20_229_6, Node20_229_7, Node20_229_8, Node20_229_9, Node20_229_10, Node20_229_11, Node20_229_12, Node20_229_13, Node20_229_14, WorkloadIdentityFederation, IssueSourceEnabled };
5253
}
5354

5455
// ensureUpdateModeVerifier wraps all writes. if writeUpdate=false, it tracks writes that would have occured
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
import { IExecSyncResult } from 'azure-pipelines-task-lib/toolrunner';
2+
import path = require("path");
3+
import tl = require("azure-pipelines-task-lib/task");
4+
import fs = require("fs");
5+
import os = require("os");
6+
import { getHandlerFromToken, WebApi } from "azure-devops-node-api";
7+
import { ITaskApi } from "azure-devops-node-api/TaskApi";
8+
9+
export class azureclitask {
10+
public static checkIfAzurePythonSdkIsInstalled() {
11+
return !!tl.which("az", false);
12+
}
13+
14+
public static async runMain() {
15+
var toolExecutionError = null;
16+
try {
17+
var tool;
18+
if (os.type() != "Windows_NT") {
19+
tool = tl.tool(tl.which("bash", true));
20+
}
21+
22+
var scriptLocation: string = tl.getInput("scriptLocation");
23+
var scriptPath: string = null;
24+
var cwd: string = tl.getPathInput("cwd", true, false);
25+
26+
if (scriptLocation === "scriptPath") {
27+
scriptPath = tl.getPathInput("scriptPath", true, true);
28+
// if user didn"t supply a cwd (advanced), then set cwd to folder script is in.
29+
// All "script" tasks should do this
30+
if (!tl.filePathSupplied("cwd")) {
31+
cwd = path.dirname(scriptPath);
32+
}
33+
}
34+
else {
35+
var tmpDir = tl.getVariable('Agent.TempDirectory') || os.tmpdir();
36+
var script: string = tl.getInput("inlineScript", true);
37+
if (os.type() != "Windows_NT") {
38+
scriptPath = path.join(tmpDir, "azureclitaskscript" + new Date().getTime() + ".sh");
39+
}
40+
else {
41+
scriptPath = path.join(tmpDir, "azureclitaskscript" + new Date().getTime() + ".bat");
42+
}
43+
this.createFile(scriptPath, script);
44+
}
45+
46+
var args = tl.getInput("args", false);
47+
48+
// determines whether output to stderr will fail a task.
49+
// some tools write progress and other warnings to stderr. scripts can also redirect.
50+
var failOnStdErr = tl.getBoolInput("failOnStandardError", false);
51+
52+
tl.mkdirP(cwd);
53+
tl.cd(cwd);
54+
55+
if (os.type() != "Windows_NT") {
56+
tool.arg(scriptPath);
57+
}
58+
else {
59+
tool = tl.tool(tl.which(scriptPath, true));
60+
}
61+
this.throwIfError(tl.execSync("az", "--version"));
62+
// set az cli config dir
63+
this.setConfigDirectory();
64+
this.setAzureCloudBasedOnServiceEndpoint();
65+
var connectedService: string = tl.getInput("connectedServiceNameARM", true);
66+
await this.loginAzureRM(connectedService);
67+
68+
tool.line(args); // additional args should always call line. line() parses quoted arg strings
69+
70+
var addSpnToEnvironment = tl.getBoolInput("addSpnToEnvironment", false);
71+
var authorizationScheme = tl.getEndpointAuthorizationScheme(connectedService, true).toLowerCase();
72+
if (!!addSpnToEnvironment && authorizationScheme == 'serviceprincipal') {
73+
await tool.exec({
74+
failOnStdErr: failOnStdErr,
75+
env: {
76+
...process.env,
77+
...{ servicePrincipalId: this.servicePrincipalId, servicePrincipalKey: this.servicePrincipalKey, tenantId: this.tenantId }
78+
}
79+
});
80+
} else if (!!addSpnToEnvironment && authorizationScheme == 'workloadidentityfederation') {
81+
await tool.exec({
82+
failOnStdErr: failOnStdErr,
83+
env: { ...process.env, ...{ servicePrincipalId: this.servicePrincipalId, idToken: this.federatedToken, tenantId: this.tenantId } }
84+
});
85+
}
86+
else {
87+
await tool.exec({ failOnStdErr: failOnStdErr });
88+
}
89+
}
90+
catch (err) {
91+
if (err.stderr) {
92+
toolExecutionError = err.stderr;
93+
}
94+
else {
95+
toolExecutionError = err;
96+
}
97+
//go to finally and logout of azure and set task result
98+
}
99+
finally {
100+
if (scriptLocation === "inlineScript") {
101+
this.deleteFile(scriptPath);
102+
}
103+
104+
if (this.cliPasswordPath) {
105+
tl.debug('Removing spn certificate file');
106+
tl.rmRF(this.cliPasswordPath);
107+
}
108+
109+
//set the task result to either succeeded or failed based on error was thrown or not
110+
if (toolExecutionError) {
111+
tl.setResult(tl.TaskResult.Failed, tl.loc("ScriptFailed", toolExecutionError));
112+
}
113+
else {
114+
tl.setResult(tl.TaskResult.Succeeded, tl.loc("ScriptReturnCode", 0));
115+
}
116+
117+
//Logout of Azure if logged in
118+
if (this.isLoggedIn) {
119+
this.logoutAzure();
120+
}
121+
}
122+
}
123+
124+
private static isLoggedIn: boolean = false;
125+
private static cliPasswordPath: string = null;
126+
private static servicePrincipalId: string = null;
127+
private static servicePrincipalKey: string = null;
128+
private static federatedToken: string = null;
129+
private static tenantId: string = null;
130+
131+
private static async loginAzureRM(connectedService: string):Promise<void> {
132+
var authScheme: string = tl.getEndpointAuthorizationScheme(connectedService, true);
133+
var subscriptionID: string = tl.getEndpointDataParameter(connectedService, "SubscriptionID", true);
134+
135+
if (authScheme.toLowerCase() == "serviceprincipal") {
136+
let authType: string = tl.getEndpointAuthorizationParameter(connectedService, 'authenticationType', true);
137+
let cliPassword: string = null;
138+
var servicePrincipalId: string = tl.getEndpointAuthorizationParameter(connectedService, "serviceprincipalid", false);
139+
var tenantId: string = tl.getEndpointAuthorizationParameter(connectedService, "tenantid", false);
140+
141+
this.servicePrincipalId = servicePrincipalId;
142+
this.tenantId = tenantId;
143+
144+
if (authType == "spnCertificate") {
145+
tl.debug('certificate based endpoint');
146+
let certificateContent: string = tl.getEndpointAuthorizationParameter(connectedService, "servicePrincipalCertificate", false);
147+
cliPassword = path.join(tl.getVariable('Agent.TempDirectory') || tl.getVariable('system.DefaultWorkingDirectory'), 'spnCert.pem');
148+
fs.writeFileSync(cliPassword, certificateContent);
149+
this.cliPasswordPath = cliPassword;
150+
}
151+
else {
152+
tl.debug('key based endpoint');
153+
cliPassword = tl.getEndpointAuthorizationParameter(connectedService, "serviceprincipalkey", false);
154+
this.servicePrincipalKey = cliPassword;
155+
}
156+
157+
let escapedCliPassword = cliPassword.replace(/"/g, '\\"');
158+
tl.setSecret(escapedCliPassword.replace(/\\/g, '\"'));
159+
//login using svn
160+
this.throwIfError(tl.execSync("az", `login --service-principal -u "${servicePrincipalId}" --password="${escapedCliPassword}" --tenant "${tenantId}"`), tl.loc("LoginFailed"));
161+
}
162+
else if (authScheme.toLowerCase() == "managedserviceidentity") {
163+
//login using msi
164+
this.throwIfError(tl.execSync("az", "login --identity"), tl.loc("MSILoginFailed"));
165+
}
166+
else if (authScheme.toLowerCase() == "workloadidentityfederation") {
167+
var servicePrincipalId: string = tl.getEndpointAuthorizationParameter(connectedService, "serviceprincipalid", false);
168+
var tenantId: string = tl.getEndpointAuthorizationParameter(connectedService, "tenantid", false);
169+
170+
const federatedToken = await this.getIdToken(connectedService);
171+
tl.setSecret(federatedToken);
172+
const args = `login --service-principal -u "${servicePrincipalId}" --tenant "${tenantId}" --allow-no-subscriptions --federated-token "${federatedToken}"`;
173+
174+
//login using OpenID Connect federation
175+
this.throwIfError(tl.execSync("az", args), tl.loc("LoginFailed"));
176+
177+
this.servicePrincipalId = servicePrincipalId;
178+
this.federatedToken = federatedToken;
179+
this.tenantId = tenantId;
180+
}
181+
else{
182+
throw tl.loc('AuthSchemeNotSupported', authScheme);
183+
}
184+
185+
this.isLoggedIn = true;
186+
//set the subscription imported to the current subscription
187+
this.throwIfError(tl.execSync("az", "account set --subscription \"" + subscriptionID + "\""), tl.loc("ErrorInSettingUpSubscription"));
188+
}
189+
190+
private static setConfigDirectory(): void {
191+
if (tl.getBoolInput("useGlobalConfig")) {
192+
return;
193+
}
194+
195+
if (!!tl.getVariable('Agent.TempDirectory')) {
196+
var azCliConfigPath = path.join(tl.getVariable('Agent.TempDirectory'), ".azclitask");
197+
console.log(tl.loc('SettingAzureConfigDir', azCliConfigPath));
198+
process.env['AZURE_CONFIG_DIR'] = azCliConfigPath;
199+
} else {
200+
console.warn(tl.loc('GlobalCliConfigAgentVersionWarning'));
201+
}
202+
}
203+
204+
private static setAzureCloudBasedOnServiceEndpoint(): void {
205+
var connectedService: string = tl.getInput("connectedServiceNameARM", true);
206+
var environment = tl.getEndpointDataParameter(connectedService, 'environment', true);
207+
if(!!environment) {
208+
console.log(tl.loc('SettingAzureCloud', environment));
209+
this.throwIfError(tl.execSync("az", "cloud set -n " + environment));
210+
}
211+
}
212+
213+
private static logoutAzure() {
214+
try {
215+
tl.execSync("az", " account clear");
216+
}
217+
catch (err) {
218+
// task should not fail if logout doesn`t occur
219+
tl.warning(tl.loc("FailedToLogout"), tl.IssueSource.TaskInternal);
220+
}
221+
}
222+
223+
private static throwIfError(resultOfToolExecution: IExecSyncResult, errormsg?: string): void {
224+
if (resultOfToolExecution.code != 0) {
225+
tl.error("Error Code: [" + resultOfToolExecution.code + "]", tl.IssueSource.TaskInternal);
226+
if (errormsg) {
227+
tl.error("Error: " + errormsg, tl.IssueSource.TaskInternal);
228+
}
229+
throw resultOfToolExecution;
230+
}
231+
}
232+
233+
private static createFile(filePath: string, data: string) {
234+
try {
235+
fs.writeFileSync(filePath, data);
236+
}
237+
catch (err) {
238+
this.deleteFile(filePath);
239+
throw err;
240+
}
241+
}
242+
243+
private static deleteFile(filePath: string): void {
244+
if (fs.existsSync(filePath)) {
245+
try {
246+
//delete the publishsetting file created earlier
247+
fs.unlinkSync(filePath);
248+
}
249+
catch (err) {
250+
//error while deleting should not result in task failure
251+
console.error(err.toString());
252+
}
253+
}
254+
}
255+
256+
private static async getIdToken(connectedService: string) : Promise<string> {
257+
const jobId = tl.getVariable("System.JobId");
258+
const planId = tl.getVariable("System.PlanId");
259+
const projectId = tl.getVariable("System.TeamProjectId");
260+
const hub = tl.getVariable("System.HostType");
261+
const uri = tl.getVariable("System.CollectionUri");
262+
const token = this.getSystemAccessToken();
263+
264+
const authHandler = getHandlerFromToken(token);
265+
const connection = new WebApi(uri, authHandler);
266+
const api: ITaskApi = await connection.getTaskApi();
267+
const response = await api.createOidcToken({}, projectId, hub, planId, jobId, connectedService);
268+
if (response == null) {
269+
return null;
270+
}
271+
272+
return response.oidcToken;
273+
}
274+
275+
private static getSystemAccessToken() : string {
276+
tl.debug('Getting credentials for local feeds');
277+
const auth = tl.getEndpointAuthorization('SYSTEMVSSCONNECTION', false);
278+
if (auth.scheme === 'OAuth') {
279+
tl.debug('Got auth token');
280+
return auth.parameters['AccessToken'];
281+
}
282+
else {
283+
tl.warning('Could not determine credentials to use', tl.IssueSource.TaskInternal);
284+
}
285+
}
286+
}
287+
288+
tl.setResourcePath(path.join(__dirname, "task.json"));
289+
290+
if (!azureclitask.checkIfAzurePythonSdkIsInstalled()) {
291+
tl.setResult(tl.TaskResult.Failed, tl.loc("AzureSDKNotFound"));
292+
}
293+
294+
azureclitask.runMain();

0 commit comments

Comments
 (0)