Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support for optionally adding pac cli to PATH environment variable #570

Merged
merged 3 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
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
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();
});
});
Loading