Skip to content

Commit

Permalink
support for optionally adding pac cli to PATH environment variable (#570
Browse files Browse the repository at this point in the history
)
  • Loading branch information
devkeydet authored May 9, 2024
1 parent b726964 commit 9571e81
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 5 deletions.
4 changes: 4 additions & 0 deletions actions-install/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ inputs:
description: 'Path to a pac.exe (windows) or pac (linux/mac) already installed on the action runner.'
required: false

add-tools-to-path:
description: 'Enables you to use pac cli from script tasks without needing to set up the path manually.'
required: false

runs:
using: 'node16'
main: '../dist/actions/actions-install/index.js'
Expand Down
36 changes: 31 additions & 5 deletions src/actions/actions-install/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,17 @@ const argName = {
nugetFeedOverride: 'nuget-feed-override',
nugetFeedUsername: 'nuget-feed-username',
nugetFeedPassword: 'nuget-feed-password',
localPacPath: 'use-preinstalled-pac'
localPacPath: 'use-preinstalled-pac',
addToolsToPath: 'add-tools-to-path'
};

let args: {
nugetFeedPassword: string;
localPacPath: string;
nugetFeedOverride: string;
versionOverride: string;
nugetFeedUsername: string;
addToolsToPath: boolean;
};

class MutuallyExclusiveArgsError extends Error {
Expand All @@ -38,11 +48,12 @@ class MutuallyExclusiveArgsError extends Error {
export async function main(): Promise<void> {
core.startGroup('actions-install:');

const args = {
args = {
versionOverride: core.getInput(argName.versionOverride, { required: false }),
nugetFeedOverride: core.getInput(argName.nugetFeedOverride, { required: false }),
nugetFeedUsername: core.getInput(argName.nugetFeedUsername, { required: false }),
nugetFeedPassword: core.getInput(argName.nugetFeedPassword, { required: false }),
addToolsToPath: core.getInput(argName.addToolsToPath, { required: false }) === 'true',
localPacPath: core.getInput(argName.localPacPath, { required: false })
};

Expand Down Expand Up @@ -83,6 +94,10 @@ export async function main(): Promise<void> {
core.endGroup();
}

function removePacFromPath(path:string): string {
return path.replace(/(\/|\\)(pac.exe|pac)$/, '');
}

async function usingPreinstalledPac(localPacPath: string): Promise<void> {
const absolutePath = path.resolve(localPacPath);
core.info(`Using preinstalled pac from ${absolutePath}`)
Expand All @@ -100,6 +115,9 @@ async function usingPreinstalledPac(localPacPath: string): Promise<void> {

core.exportVariable(PacInstalledEnvVarName, 'true');
core.exportVariable(PacPathEnvVarName, absolutePath);
if (args.addToolsToPath) {
core.addPath(removePacFromPath(absolutePath));
}
core.warning(`Actions built targetting PAC ${PacInfo.PacPackageVersion}, so Action and PAC parameters might not match if preinstalled pac is a different version.`);
}

Expand All @@ -125,9 +143,13 @@ async function nugetInstall(packageName: string, packageVersion: string, nugetFe

await exec.getExecOutput('nuget', installArgs);

const pacPath = resolve(toolpath, packageName + '.' + packageVersion, 'tools', 'pac.exe');
const pacPathWithoutExecutable = resolve(toolpath, packageName + '.' + packageVersion, 'tools');
const pacPath = resolve(pacPathWithoutExecutable, 'pac.exe');
core.exportVariable(PacInstalledEnvVarName, 'true');
core.exportVariable(PacPathEnvVarName, pacPath);
if (args.addToolsToPath) {
core.addPath(pacPathWithoutExecutable);
}
} finally {
if (nugetConfigFile) {
await fs.rm(nugetConfigFile);
Expand All @@ -153,7 +175,11 @@ async function dotnetInstall(packageName: string, packageVersion: string, nugetF
await exec.getExecOutput('dotnet', installArgs);

core.exportVariable(PacInstalledEnvVarName, 'true');
core.exportVariable(PacPathEnvVarName, path.join(toolpath, os.platform() === 'win32' ? 'pac.exe' : 'pac'));
const pacPath = path.join(toolpath, os.platform() === 'win32' ? 'pac.exe' : 'pac');
core.exportVariable(PacPathEnvVarName, pacPath);
if (args.addToolsToPath) {
core.addPath(removePacFromPath(toolpath));
}
core.info(`pac installed to ${process.env[PacPathEnvVarName]}`);
} finally {
if (nugetConfigFile) {
Expand Down Expand Up @@ -185,7 +211,7 @@ async function createNugetConfigViaDotnet(toolDirectory: string, nugetFeedOverri
await exec.getExecOutput('dotnet', ['new', 'nugetconfig', '-o', toolDirectory]);
await exec.getExecOutput('dotnet', ['nuget', 'remove', 'source', 'nuget', '--configfile', filename]);

const nugetSourceArgs = ['nuget', 'add', 'source', nugetFeedOverride, '--name', 'pacNugetFeed', '--configfile', filename ];
const nugetSourceArgs = ['nuget', 'add', 'source', nugetFeedOverride, '--name', 'pacNugetFeed', '--configfile', filename];
nugetFeedUsername && nugetSourceArgs.push('--username', nugetFeedUsername);
nugetFeedPassword && nugetSourceArgs.push('--password', nugetFeedPassword);

Expand Down
115 changes: 115 additions & 0 deletions src/test/actionsInstall.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { assert, should, use } from "chai";
import * as sinonChai from "sinon-chai";
import rewiremock from "./rewiremock";
import { restore, stub } from "sinon";
import { runnerParameters } from "../lib/runnerParameters";
import Sinon = require("sinon");
import * as os from 'os';

should();
use(sinonChai);

describe("actions-install tests", () => {
let pathValue: string;
const addToolsToPath = 'add-tools-to-path';
const usePreinstalledPac = 'use-preinstalled-pac';
let inputs: { [key: string]: string };
const actionsInstallStub: Sinon.SinonStub<unknown[], unknown> = stub();
let osPlatformStub: Sinon.SinonStub<unknown[], unknown> = stub();

beforeEach(() => {
pathValue = '';
inputs = {};
osPlatformStub = Sinon.stub(os, 'platform');
});
afterEach(() => {
osPlatformStub.restore();
restore();
});

async function callActionWithMocks(): Promise<void> {
const toolInstaller = await rewiremock.around(
() => import("../actions/actions-install/index"),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(mock: any) => {
mock(() => import("@microsoft/powerplatform-cli-wrapper/dist/actions")).with({
actionsInstall: actionsInstallStub
});
mock(() => import("@actions/core")).with({
getInput: (name: string) => inputs[name],
startGroup: () => undefined,
endGroup: () => undefined,
info: () => undefined,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
exportVariable: (name: string, val: string) => undefined,
addPath: (path: string) => pathValue = path,
warning: () => undefined
});
mock(() => import("@actions/io")).with({
which: () => Promise.resolve("path/to/pac")
});
mock(() => import("@actions/exec")).with({
getExecOutput: () => undefined
});
mock(() => import("../lib/runnerParameters")).with({
runnerParameters: runnerParameters
});
});
await toolInstaller.main();
}

function setupTest(platform: string, addTools: string, preinstalledPac?: string) {
osPlatformStub.returns(platform);
inputs[addToolsToPath] = addTools;
if (preinstalledPac) {
inputs[usePreinstalledPac] = preinstalledPac;
}
}

function assertPathNotEmpty() {
assert.isNotEmpty(pathValue);
assert.isTrue(!pathValue.endsWith('pac') && !pathValue.endsWith('pac.exe'));
}

function assertPathEmpty() {
assert.isEmpty(pathValue);
}

it("calls actions-install with add-tools-to-path true and calls addPath", async function () {
setupTest('win32', 'true');
await callActionWithMocks();
assertPathNotEmpty();
});

it("calls actions-install with add-tools-to-path false and does not call addPath", async function () {
setupTest('win32', 'false');
await callActionWithMocks();
assertPathEmpty();
});

it("calls actions-install with add-tools-to-path true + use-preinstalled-pac value and calls addPath", async function () {
setupTest('win32', 'true', 'out/pac/tools/pac.exe');
await callActionWithMocks();
assertPathNotEmpty();
});

it("calls actions-install with add-tools-to-path false + use-preinstalled-pac value and does not call addPath", async function () {
setupTest('win32', 'false', 'out/pac/tools/pac.exe');
await callActionWithMocks();
assertPathEmpty();
});

it("calls actions-install using dotnetInstall with add-tools-to-path true and calls addPath", async function () {
setupTest('linux', 'true');
await callActionWithMocks();
assertPathNotEmpty();
});

it("calls actions-install using dotnetInstall with add-tools-to-path false and does nots addPath", async function () {
setupTest('linux', 'false');
await callActionWithMocks();
assertPathEmpty();
});
});

0 comments on commit 9571e81

Please sign in to comment.