Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Dynamically download Az module
Pre-baking of Az modules in runners will be refactored such that:
1. Only 1 latest Az module will be available as folder
2. Some high usage module versions will be available as zip based on platform
3. Other versions dynamically downloaded from Azure/az-ps-module-versions repo's releases
4. PSGallery will act as a fallback in case download fail from github releases

So to load the correct module from correct source the changes are done.
  • Loading branch information
amit-avit authored Mar 9, 2021
commit 46f5fd064d87934bde00d9923f9c080f9791c9cc
3 changes: 3 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ inputs:
description: 'If this is true, this task will fail if any errors are written to the error pipeline, or if any data is written to the Standard Error stream.'
required: false
default: 'false'
githubToken:
description: Used to pull Az module from Azure/az-ps-module-versions. Since there's a default, this is typically not supplied by the user.
default: ${{ github.token }}
branding:
icon: 'login.svg'
color: 'blue'
Expand Down
168 changes: 168 additions & 0 deletions lib/AzModuleInstaller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(require("@actions/core"));
const tc = __importStar(require("@actions/tool-cache"));
const os = __importStar(require("os"));
const ArchiveTools_1 = require("./Utilities/ArchiveTools");
const FileUtils_1 = __importDefault(require("./Utilities/FileUtils"));
const Utils_1 = __importDefault(require("./Utilities/Utils"));
class AzModuleInstaller {
constructor(version, githubAuth) {
var _a;
this.isWin = false;
this.version = version;
this.githubAuth = githubAuth;
this.installResult = {
moduleSource: "Others",
isInstalled: false
};
const platform = (_a = (process.env.RUNNER_OS || os.type())) === null || _a === void 0 ? void 0 : _a.toLowerCase();
switch (platform) {
case "windows":
case "windows_nt":
this.isWin = true;
this.moduleContainerPath = "C:\\Modules";
this.modulePath = `${this.moduleContainerPath}\\az_${this.version}`;
break;
case "linux":
this.moduleContainerPath = "/usr/share";
this.modulePath = `${this.moduleContainerPath}/az_${this.version}`;
break;
default:
throw `OS ${platform} not supported`;
}
this.moduleZipPath = `${this.modulePath}.zip`;
}
install() {
return __awaiter(this, void 0, void 0, function* () {
if (Utils_1.default.isHostedAgent(this.moduleContainerPath)) {
yield this.tryInstallingLatest();
yield this.tryInstallFromFolder();
yield this.tryInstallFromZip();
yield this.tryInstallFromGHRelease();
yield this.tryInstallFromPSGallery();
}
else {
core.debug("File layout is not like hosted agent, skippig module install.");
this.installResult = {
isInstalled: false,
moduleSource: "privateAgent"
};
}
return this.installResult;
});
}
tryInstallingLatest() {
return __awaiter(this, void 0, void 0, function* () {
if (this.installResult.isInstalled) {
return;
}
if (this.version === "latest") {
core.debug("Latest selected, will use latest Az module available in agent as folder.");
this.installResult = {
isInstalled: true,
moduleSource: "hostedAgentFolder"
};
}
});
}
tryInstallFromFolder() {
return __awaiter(this, void 0, void 0, function* () {
if (this.installResult.isInstalled) {
return;
}
if (FileUtils_1.default.pathExists(this.modulePath)) {
core.debug(`Az ${this.version} present at ${this.modulePath} as folder.`);
this.installResult = {
isInstalled: true,
moduleSource: "hostedAgentFolder"
};
}
});
}
tryInstallFromZip() {
return __awaiter(this, void 0, void 0, function* () {
if (this.installResult.isInstalled) {
return;
}
if (FileUtils_1.default.pathExists(this.moduleZipPath)) {
core.debug(`Az ${this.version} present at ${this.moduleZipPath} as zip, expanding it.`);
yield new ArchiveTools_1.ArchiveTools(this.isWin).unzip(this.moduleZipPath, this.moduleContainerPath);
yield FileUtils_1.default.deleteFile(this.moduleZipPath);
this.installResult = {
isInstalled: true,
moduleSource: "hostedAgentZip"
};
}
});
}
tryInstallFromGHRelease() {
return __awaiter(this, void 0, void 0, function* () {
if (this.installResult.isInstalled) {
return;
}
try {
const downloadUrl = yield this.getDownloadUrlFromGHRelease();
core.debug(`Downloading Az ${this.version} from GHRelease using url ${downloadUrl}`);
yield tc.downloadTool(downloadUrl, this.moduleZipPath, this.githubAuth);
core.debug(`Expanding Az ${this.version} downloaded at ${this.moduleZipPath} as zip.`);
yield new ArchiveTools_1.ArchiveTools(this.isWin).unzip(this.moduleZipPath, this.moduleContainerPath);
yield FileUtils_1.default.deleteFile(this.moduleZipPath);
this.installResult = {
isInstalled: true,
moduleSource: "hostedAgentGHRelease"
};
}
catch (err) {
core.debug(err);
console.log("Download from GHRelease failed, will fallback to PSGallery");
}
});
}
tryInstallFromPSGallery() {
return __awaiter(this, void 0, void 0, function* () {
if (this.installResult.isInstalled) {
return;
}
yield Utils_1.default.saveAzModule(this.version, this.modulePath);
this.installResult = {
isInstalled: true,
moduleSource: "hostedAgentPSGallery"
};
});
}
getDownloadUrlFromGHRelease() {
var _a;
return __awaiter(this, void 0, void 0, function* () {
core.debug("Getting versions manifest from GHRelease.");
const releases = yield tc.getManifestFromRepo("Azure", "az-ps-module-versions", this.githubAuth, "main");
core.debug(JSON.stringify(releases));
const releaseInfo = (_a = releases.filter(release => release.version === this.version)) === null || _a === void 0 ? void 0 : _a[0];
let downloadUrl = null;
if (releaseInfo && releaseInfo.files.length > 0) {
downloadUrl = releaseInfo.files[0].download_url;
}
return downloadUrl;
});
}
}
exports.AzModuleInstaller = AzModuleInstaller;
56 changes: 56 additions & 0 deletions lib/Utilities/ArchiveTools.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const exec_1 = require("@actions/exec");
const io_1 = require("@actions/io");
const PowerShellToolRunner_1 = __importDefault(require("./PowerShellToolRunner"));
class ArchiveTools {
constructor(use7Zip = false) {
this.use7Zip = use7Zip;
}
unzip(zipPath, destination) {
return __awaiter(this, void 0, void 0, function* () {
if (this.use7Zip) {
yield this.unzipUsing7Zip(zipPath, destination);
}
else {
yield this.unzipUsingPowerShell(zipPath, destination);
}
});
}
unzipUsing7Zip(zipPath, destination) {
return __awaiter(this, void 0, void 0, function* () {
const path7Zip = yield io_1.which("7z.exe", true);
const exitCode = yield exec_1.exec(`${path7Zip} x -o${destination} ${zipPath}`);
if (exitCode != 0) {
throw new Error(`Extraction using 7zip failed from ${zipPath} to ${destination}`);
}
});
}
unzipUsingPowerShell(zipPath, destination) {
return __awaiter(this, void 0, void 0, function* () {
const script = `
$prevProgressPref = $ProgressPreference
$ProgressPreference = 'SilentlyContinue'
Expand-Archive -Path ${zipPath} -DestinationPath ${destination}
$ProgressPreference = $prevProgressPref`;
PowerShellToolRunner_1.default.init();
const exitCode = yield PowerShellToolRunner_1.default.executePowerShellScriptBlock(script);
if (exitCode != 0) {
throw new Error(`Extraction using Expand-Archive cmdlet failed from ${zipPath} to ${destination}`);
}
});
}
}
exports.ArchiveTools = ArchiveTools;
3 changes: 3 additions & 0 deletions lib/Utilities/FileUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ class FileUtils {
}
});
}
static pathExists(path) {
return fs.existsSync(path);
}
}
exports.default = FileUtils;
FileUtils.tempDirectory = process.env.RUNNER_TEMP || os.tmpdir();
34 changes: 34 additions & 0 deletions lib/Utilities/Utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,5 +102,39 @@ class Utils {
static isValidVersion(version) {
return !!version.match(Constants_1.default.versionPattern);
}
static isHostedAgent(moduleContainerPath) {
return __awaiter(this, void 0, void 0, function* () {
const script = `Test-Path (Join-Path "${moduleContainerPath}" "az_*")`;
let output = "";
const options = {
listeners: {
stdout: (data) => {
output += data.toString();
}
}
};
yield PowerShellToolRunner_1.default.init();
yield PowerShellToolRunner_1.default.executePowerShellCommand(script, options);
return output.trim().toLowerCase() === "true";
});
}
static isGhes() {
const ghUrl = new URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com');
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM';
}
static saveAzModule(version, modulePath) {
return __awaiter(this, void 0, void 0, function* () {
const script = `
$prevProgressPref = $ProgressPreference
$ProgressPreference = 'SilentlyContinue'
Save-Module -Path ${modulePath} -Name Az -RequiredVersion ${version} -Force -ErrorAction Stop
$ProgressPreference = $prevProgressPref`;
yield PowerShellToolRunner_1.default.init();
const exitCode = yield PowerShellToolRunner_1.default.executePowerShellScriptBlock(script);
if (exitCode != 0) {
throw new Error(`Download from PSGallery failed for Az ${version} to ${modulePath}`);
}
});
}
}
exports.default = Utils;
5 changes: 5 additions & 0 deletions lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const Utils_1 = __importDefault(require("./Utilities/Utils"));
const FileUtils_1 = __importDefault(require("./Utilities/FileUtils"));
const ScriptRunner_1 = __importDefault(require("./ScriptRunner"));
const InitializeAzure_1 = __importDefault(require("./InitializeAzure"));
const AzModuleInstaller_1 = require("./AzModuleInstaller");
const errorActionPrefValues = new Set(['STOP', 'CONTINUE', 'SILENTLYCONTINUE']);
let azPSVersion;
let userAgentPrefix = !!process.env.AZURE_HTTP_USER_AGENT ? `${process.env.AZURE_HTTP_USER_AGENT}` : "";
Expand All @@ -40,8 +41,12 @@ function main() {
azPSVersion = core.getInput('azPSVersion', { required: true }).trim().toLowerCase();
const errorActionPreference = core.getInput('errorActionPreference');
const failOnStandardError = core.getInput('failOnStandardError').trim().toLowerCase() === "true";
const githubToken = core.getInput('githubToken');
console.log(`Validating inputs`);
validateInputs(inlineScript, errorActionPreference);
const githubAuth = !githubToken || Utils_1.default.isGhes() ? undefined : `token ${githubToken}`;
const installResult = yield new AzModuleInstaller_1.AzModuleInstaller(azPSVersion, githubAuth).install();
console.log(`Module Az ${azPSVersion} installed from ${installResult.moduleSource}`);
console.log(`Initializing Az Module`);
yield InitializeAzure_1.default.importAzModule(azPSVersion);
console.log(`Initializing Az Module Complete`);
Expand Down
36 changes: 34 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@actions/core": "^1.2.2",
"@actions/exec": "^1.0.3",
"@actions/io": "^1.0.2",
"@actions/tool-cache": "^1.6.1",
"fs": "0.0.1-security",
"os": "^0.1.1",
"path": "^0.12.7",
Expand Down
Loading