From 2bceeb0418f1c62966fded15963e657be03d0f03 Mon Sep 17 00:00:00 2001 From: "Fischer, Louis" Date: Wed, 22 Mar 2023 22:55:37 -0500 Subject: [PATCH 1/8] Added Cake Frosting switch --- .gitignore | 6 +++++ README.md | 30 +++++++++++++++++---- __tests__/cake.test.ts | 59 ++++++++++++++++++++++++++++++++++++++++++ __tests__/main.test.ts | 22 ++++++++++++++++ action.yml | 5 +++- dist/index.js | 36 ++++++++++++++++++++++---- src/action.ts | 2 ++ src/cake.ts | 23 ++++++++++++++++ src/main.ts | 14 +++++++--- 9 files changed, 182 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index a44a138..2b2207c 100644 --- a/.gitignore +++ b/.gitignore @@ -68,3 +68,9 @@ typings/ # Secret files .secrets + +# bin folder +bin/ + +# obj folder +obj/ \ No newline at end of file diff --git a/README.md b/README.md index 54e7a5b..ad954f7 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![GitHub Marketplace](https://img.shields.io/github/v/release/cake-build/cake-action?label=Marketplace&sort=semver)](https://github.com/marketplace/actions/cake-action) [![GitHub Actions Build](https://github.com/cake-build/cake-action/workflows/Build/badge.svg)](https://github.com/cake-build/cake-action/actions?workflow=Build) [![GitHub Actions Tests](https://github.com/cake-build/cake-action/workflows/Tests/badge.svg)](https://github.com/cake-build/cake-action/actions?workflow=Tests) [![Coveralls](https://coveralls.io/repos/github/cake-build/cake-action/badge.svg?branch=master)](https://coveralls.io/github/cake-build/cake-action?branch=master) -This action allows you to run a Cake script from your GitHub Actions workflow without having to use a [bootstrapper](https://github.com/cake-build/resources). +This action allows you to run a Cake script or Cake Frosting project from your GitHub Actions workflow without having to use a [bootstrapper](https://github.com/cake-build/resources). ## Usage @@ -14,7 +14,9 @@ steps: uses: cake-build/cake-action@v2 ``` -The Cake action will look for a script named `build.cake` in your repository's root directory and run it for you using the [Cake Tool](https://www.nuget.org/packages/Cake.Tool/). All output from the Cake script will be automatically redirected to the build log for inspection. +The Cake action will look for a script named `build.cake` in your repository's root directory and run it for you using the [Cake Tool](https://www.nuget.org/packages/Cake.Tool/). If you are using a [Cake Frosting](https://cakebuild.net/docs/running-builds/runners/cake-frosting) project, the `csproj-path` parameter must be specified and the `script-path` is ignored. + +All output from the Cake script or Cake Frosting project will be automatically redirected to the build log for inspection. ## Inputs @@ -30,9 +32,21 @@ steps: script-path: path/to/script.cake ``` +### `csproj-path` + +If you are using [Cake Frosting](https://cakebuild.net/docs/running-builds/runners/cake-frosting), you can specify the path with the `csproj-path` [input parameter](https://help.github.com/en/github/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idstepswith): + +```yml +steps: + - name: Run Cake Frosting + uses: cake-build/cake-action@v2 + with: + csproj-path: path/to/build.csproj +``` + ### `target` -You'll likely want to specify which task to run out of the ones defined in the Cake script. For that, you can use the `target` parameter: +You'll likely want to specify which task to run out of the ones defined in the Cake script or Cake Frosting project. For that, you can use the `target` parameter: ```yml steps: @@ -88,7 +102,11 @@ The arguments are defined in a [multi-line string literal](https://yaml.org/spec ### `cake-version` -By default, the Cake action will run your script using the latest _stable_ version of the [Cake .NET Core Global tool](https://www.nuget.org/packages/Cake.Tool/). However, if for some reason you want to [use a specific version of Cake](https://cakebuild.net/docs/tutorials/pinning-cake-version) (for compatibility with older third-party addins, for example), you can do so by specifying the version number in the `cake-version` parameter: +By default, the Cake action will run your script using the latest _stable_ version of the [Cake .NET Core Global tool](https://www.nuget.org/packages/Cake.Tool/). + +_Ignored_ for Cake Frosting Project. + +If for some reason you want to [use a specific version of Cake](https://cakebuild.net/docs/tutorials/pinning-cake-version) (for compatibility with older third-party addins, for example), you can do so by specifying the version number in the `cake-version` parameter.: ```yml steps: @@ -112,6 +130,8 @@ steps: As of [Cake 1.0.0](https://github.com/cake-build/cake/releases/tag/v1.0.0), any [custom modules](https://cakebuild.net/docs/fundamentals/modules) that you reference in your script are [bootstrapped automatically](https://github.com/cake-build/cake/issues/2833) upon running it. +_Ignored_ for Cake Frosting Project. + If you're using an older version of Cake, however, you need to explicitly [bootstrap](https://cakebuild.net/docs/fundamentals/preprocessor-directives#module-directive) them before running the script. The Cake action can take care of this extra step for you by setting the `cake-bootstrap` parameter to `explicit`: ```yml @@ -137,7 +157,7 @@ The default value is `auto`, which means that the modules will be automatically ## Cross-platform -Since the [Cake Tool](https://www.nuget.org/packages/Cake.Tool/) is built on .NET Core, the Cake action will run on any of the [virtual environments](https://help.github.com/en/github/automating-your-workflow-with-github-actions/software-in-virtual-environments-for-github-actions) supported by GitHub Actions, namely Linux, Windows and macOS. +Since the [Cake Tool](https://www.nuget.org/packages/Cake.Tool/) and [Cake Frosting](https://www.nuget.org/packages/Cake.Frosting) are built on .NET Core, the Cake action will run on any of the [virtual environments](https://help.github.com/en/github/automating-your-workflow-with-github-actions/software-in-virtual-environments-for-github-actions) supported by GitHub Actions, namely Linux, Windows and macOS. This allows you to define your build step exactly _once_ and run it on multiple operating systems _in parallel_ by defining a [build matrix](https://help.github.com/en/github/automating-your-workflow-with-github-actions/configuring-a-workflow#configuring-a-build-matrix): diff --git a/__tests__/cake.test.ts b/__tests__/cake.test.ts index 02b1170..a768c16 100644 --- a/__tests__/cake.test.ts +++ b/__tests__/cake.test.ts @@ -8,7 +8,9 @@ import { CakeArgument, CakeSwitch } from '../src/cakeParameter'; const pathToLocalToolsDirectory = path.join('path', 'to', 'tool'); const pathToLocalTool = path.join(pathToLocalToolsDirectory, 'dotnet-cake'); +const pathToCsprojFile = path.join('build', 'Build.csproj'); const dotnetManifestCake = 'dotnet tool run dotnet-cake'; +const dotnetRun = 'dotnet run'; jest.mock('@actions/exec'); jest.mock('@actions/io'); @@ -190,3 +192,60 @@ describe('When running a script successfully using the tool manifest', () => { ['script.cake', '--switch']); }); }); + +describe('When running a Cake Frosting project successfully', () => { + const fakeExec = exec as jest.MockedFunction; + const fakeToolsDirectory = new ToolsDirectory(); + + beforeAll(async () => { + fakeExec.mockReturnValue(Promise.resolve(0)); + }); + + test('it should run with the default non-required parameters', async () => { + await cake.runProject(pathToCsprojFile, fakeToolsDirectory); + + expect(fakeExec).toHaveBeenCalledWith( + dotnetRun, + [ + '--project', pathToCsprojFile, + '--no-launch-profile', + '--verbosity', 'minimal', + '--configuration', 'Release', + '--', + `--paths_tools="${fakeToolsDirectory.path}"` + ]); + }); + + test('it should run with the specified parameters', async () => { + await cake.runProject( + pathToCsprojFile, + fakeToolsDirectory, + new CakeArgument('param', 'arg'), + new CakeSwitch('switch')); + + expect(fakeExec).toHaveBeenCalledWith( + dotnetRun, + [ + '--project', pathToCsprojFile, + '--no-launch-profile', + '--verbosity', 'minimal', + '--configuration', 'Release', + '--', + `--paths_tools="${fakeToolsDirectory.path}"`, + '--param=arg', + '--switch' + ]); + }); +}); + +describe('When failing to run a Cake Frosting Project', () => { + const fakeExec = exec as jest.MockedFunction; + + beforeAll(() => { + fakeExec.mockReturnValue(Promise.resolve(-21)); + }); + + test('it should throw an error containing the exit code', async () => { + await expect(cake.runProject('', new ToolsDirectory())).rejects.toThrow('-21'); + }); +}); diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts index fb2f2be..de976cd 100644 --- a/__tests__/main.test.ts +++ b/__tests__/main.test.ts @@ -67,6 +67,28 @@ describe('When running the action with the script path input argument', () => { }); }); +describe('When running the action with the csproj path input argument', () => { + const fakeGetInputs = action.getInputs as jest.MockedFunction; + const fakeRunProject = cake.runProject as jest.MockedFunction; + + beforeAll(() => { + fakeGetInputs.mockReturnValue({ + csprojPath: './path/to/build.csproj', + scriptArguments: [] + }); + }); + + test('it should run the Cake Frosting Project', async () => { + await run(); + expect(cake.runProject).toHaveBeenCalled(); + }); + + test('it should run the specified Cake Frosting project', async () => { + await run(); + expect(fakeRunProject.mock.calls[0][0]).toBe('./path/to/build.csproj'); + }); +}); + describe('When running the action with tool-manifest as the Cake version input argument', () => { const fakeGetInputs = action.getInputs as jest.MockedFunction; const fakeInstallCakeTool = cakeTool.install as jest.MockedFunction; diff --git a/action.yml b/action.yml index ff9a130..ab30ddc 100644 --- a/action.yml +++ b/action.yml @@ -1,5 +1,5 @@ name: 'Cake Action' -description: 'Run a Cake script as part of your build.' +description: 'Run a Cake script or Cake Frosting project as part of your build.' author: 'Enrico Campidoglio' branding: icon: 'box' @@ -9,6 +9,9 @@ inputs: description: 'The path of the Cake script to run.' required: false default: 'build.cake' + csproj-path: + description: 'The path of the Cake Frosting Project to run. Takes precedence over script-path' + required: false target: description: 'The name of the task to execute. Note that this argument must be supported by the script.' required: false diff --git a/dist/index.js b/dist/index.js index 5762626..42d106a 100644 --- a/dist/index.js +++ b/dist/index.js @@ -3991,6 +3991,7 @@ const input = __importStar(__nccwpck_require__(6747)); function getInputs() { return { scriptPath: core.getInput('script-path'), + csprojPath: core.getInput('csproj-path'), cakeVersion: getCakeVersionInput(), cakeBootstrap: getCakeBootstrapInput(), scriptArguments: getScriptInputs() @@ -4070,11 +4071,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.bootstrapScript = exports.runScript = void 0; +exports.bootstrapScript = exports.runProject = exports.runScript = void 0; const exec_1 = __nccwpck_require__(1514); const io_1 = __nccwpck_require__(7436); const dotnetCake = 'dotnet-cake'; const dotnetLocalToolCake = 'dotnet tool run dotnet-cake'; +const dotnetRun = 'dotnet run'; function runScript(scriptPath = 'build.cake', cakeToolSettings, ...params) { return __awaiter(this, void 0, void 0, function* () { const cakeToolPath = yield resolveCakeToolPath(cakeToolSettings); @@ -4086,6 +4088,24 @@ function runScript(scriptPath = 'build.cake', cakeToolSettings, ...params) { }); } exports.runScript = runScript; +function runProject(csprojPath, toolsDir, ...params) { + return __awaiter(this, void 0, void 0, function* () { + const cakeParams = formatParameters(params); + const exitCode = yield (0, exec_1.exec)(dotnetRun, [ + '--project', csprojPath, + '--no-launch-profile', + '--verbosity', 'minimal', + '--configuration', 'Release', + '--', + `--paths_tools="${toolsDir}"`, + ...cakeParams + ]); + if (exitCode != 0) { + throw new Error(`Failed to run the csproj. Exit code: ${exitCode}`); + } + }); +} +exports.runProject = runProject; function bootstrapScript(scriptPath = 'build.cake', cakeToolSettings) { return __awaiter(this, void 0, void 0, function* () { const cakeToolPath = yield resolveCakeToolPath(cakeToolSettings); @@ -4515,6 +4535,7 @@ function run() { try { const inputs = action.getInputs(); const scriptPath = inputs.scriptPath; + const csprojPath = inputs.csprojPath; const version = inputs.cakeVersion; const bootstrap = inputs.cakeBootstrap; const toolsDir = new toolsDirectory_1.ToolsDirectory(); @@ -4522,11 +4543,16 @@ function run() { const cakeToolSettings = new cakeToolSettings_1.CakeToolSettings(toolsDir, (version === null || version === void 0 ? void 0 : version.version) === 'tool-manifest'); dotnet.disableTelemetry(); dotnet.disableWelcomeMessage(); - yield cakeTool.install(toolsDir, version); - if (bootstrap === 'explicit') { - yield cake.bootstrapScript(scriptPath, cakeToolSettings); + if (csprojPath) { + yield cake.runProject(csprojPath, toolsDir, ...inputs.scriptArguments); + } + else { + yield cakeTool.install(toolsDir, version); + if (bootstrap === 'explicit') { + yield cake.bootstrapScript(scriptPath, cakeToolSettings); + } + yield cake.runScript(scriptPath, cakeToolSettings, ...inputs.scriptArguments); } - yield cake.runScript(scriptPath, cakeToolSettings, ...inputs.scriptArguments); } catch (error) { if ((0, guards_1.isError)(error)) { diff --git a/src/action.ts b/src/action.ts index 61787e4..319a184 100644 --- a/src/action.ts +++ b/src/action.ts @@ -4,6 +4,7 @@ import * as input from './input'; interface CakeInputs { readonly scriptPath?: string, + readonly csprojPath?: string, readonly cakeVersion?: CakeVersion, readonly cakeBootstrap?: CakeBootstrap; } @@ -35,6 +36,7 @@ export type CakeBootstrap = export function getInputs(): CakeInputs & ScriptInputs { return { scriptPath: core.getInput('script-path'), + csprojPath: core.getInput('csproj-path'), cakeVersion: getCakeVersionInput(), cakeBootstrap: getCakeBootstrapInput(), scriptArguments: getScriptInputs() diff --git a/src/cake.ts b/src/cake.ts index 34e6400..5bf51e5 100644 --- a/src/cake.ts +++ b/src/cake.ts @@ -2,9 +2,11 @@ import { exec } from '@actions/exec'; import { which } from '@actions/io'; import { CakeToolSettings } from './cakeToolSettings'; import { CakeParameter } from './cakeParameter'; +import { ToolsDirectory } from './toolsDirectory'; const dotnetCake = 'dotnet-cake'; const dotnetLocalToolCake = 'dotnet tool run dotnet-cake'; +const dotnetRun = 'dotnet run'; export async function runScript( scriptPath = 'build.cake', @@ -20,6 +22,27 @@ export async function runScript( } } +export async function runProject( + csprojPath: string, + toolsDir: ToolsDirectory, + ...params: CakeParameter[] +) { + const cakeParams = formatParameters(params); + const exitCode = await exec(dotnetRun, [ + '--project', csprojPath, + '--no-launch-profile', + '--verbosity', 'minimal', + '--configuration', 'Release', + '--', + `--paths_tools="${toolsDir}"`, + ...cakeParams + ]); + + if (exitCode != 0) { + throw new Error(`Failed to run the csproj. Exit code: ${exitCode}`); + } +} + export async function bootstrapScript( scriptPath = 'build.cake', cakeToolSettings?: CakeToolSettings diff --git a/src/main.ts b/src/main.ts index 348cdf1..2793181 100644 --- a/src/main.ts +++ b/src/main.ts @@ -11,6 +11,7 @@ export async function run() { try { const inputs = action.getInputs(); const scriptPath = inputs.scriptPath; + const csprojPath = inputs.csprojPath; const version = inputs.cakeVersion; const bootstrap = inputs.cakeBootstrap; @@ -22,13 +23,18 @@ export async function run() { dotnet.disableTelemetry(); dotnet.disableWelcomeMessage(); - await cakeTool.install(toolsDir, version); + if (csprojPath) { + await cake.runProject(csprojPath, toolsDir, ...inputs.scriptArguments); + } else { + await cakeTool.install(toolsDir, version); - if (bootstrap === 'explicit') { - await cake.bootstrapScript(scriptPath, cakeToolSettings); + if (bootstrap === 'explicit') { + await cake.bootstrapScript(scriptPath, cakeToolSettings); + } + + await cake.runScript(scriptPath, cakeToolSettings, ...inputs.scriptArguments); } - await cake.runScript(scriptPath, cakeToolSettings, ...inputs.scriptArguments); } catch (error) { if (isError(error)) { core.setFailed(error.message); From 5917af1044ef312f2e967da96718cbd3af2966a5 Mon Sep 17 00:00:00 2001 From: Enrico Campidoglio Date: Tue, 1 Oct 2024 13:28:03 +0200 Subject: [PATCH 2/8] Makes the `CakeVersion` discriminated union safer It's a good idea to make the discriminator field read-only in a discriminated union to make it more type-safe. --- src/action.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/action.ts b/src/action.ts index 319a184..b0e606e 100644 --- a/src/action.ts +++ b/src/action.ts @@ -18,20 +18,20 @@ export type CakeVersion = | Latest | Specific; type ToolManifest = { - version: 'tool-manifest'; + readonly version: 'tool-manifest'; }; type Latest = { - version: 'latest'; + readonly version: 'latest'; }; type Specific = { - version: 'specific', + readonly version: 'specific', number: string; }; export type CakeBootstrap = | 'auto' | 'explicit' - | 'skip' + | 'skip'; export function getInputs(): CakeInputs & ScriptInputs { return { From e49a4c422de91e7a72a747eb1b166be1dbfccd38 Mon Sep 17 00:00:00 2001 From: Enrico Campidoglio Date: Tue, 1 Oct 2024 14:11:06 +0200 Subject: [PATCH 3/8] Consolidates the parsing of the `script-path` and `csproj-path` input parameters This commit introduces a new type to represent the input file called `File`: ```typescript { type: string, path: string } ``` `File` is a discriminated union type that can either be a `ScriptFile` or a `ProjectFile`. The `path` field is set to either the value of the `script-path` or `csproj-path` input parameter, based on what is provided by the user. This new abstraction makes is clear that only one type of file can be executed at any one time by the Cake Action. Also, it allows us to more clearly express that the `csproj-path` input parameter takes precedence over the `script-path` parameter. --- __tests__/action.test.ts | 79 ++++++++++++++++++++++++++++++++++++---- __tests__/main.test.ts | 17 +++++++-- dist/index.js | 31 ++++++++++++---- src/action.ts | 35 +++++++++++++++--- src/main.ts | 12 +++--- 5 files changed, 142 insertions(+), 32 deletions(-) diff --git a/__tests__/action.test.ts b/__tests__/action.test.ts index 2f366c1..4c215eb 100644 --- a/__tests__/action.test.ts +++ b/__tests__/action.test.ts @@ -5,19 +5,77 @@ import { CakeArgument, CakeSwitch } from '../src/cakeParameter'; jest.mock('@actions/core'); -describe('When getting the Cake input arguments from the action', () => { +describe('When getting the script-path input argument from the action', () => { const fakeGetInput = core.getInput as jest.MockedFunction; beforeAll(() => { when(fakeGetInput).calledWith('script-path').mockReturnValue('path/to/script.cake'); - when(fakeGetInput).calledWith('cake-version').mockReturnValue('the.version.number'); - when(fakeGetInput).calledWith('cake-bootstrap').mockReturnValue('auto'); + when(fakeGetInput).calledWith('csproj-path').mockReturnValue(''); + when(fakeGetInput).calledWith('cake-version').mockReturnValue(''); + when(fakeGetInput).calledWith('cake-bootstrap').mockReturnValue(''); when(fakeGetInput).calledWith('dry-run').mockReturnValue(''); when(fakeGetInput).calledWith('arguments').mockReturnValue(''); }); - test('it should return the argument for the script-path parameter', () => { - expect(action.getInputs().scriptPath).toBe('path/to/script.cake'); + test('it should return \'script\' as the file type parameter', () => { + expect(action.getInputs().file.type).toBe('script'); + }); + + test('it should return the argument for the script-path parameter as the file path parameter', () => { + expect(action.getInputs().file.path).toBe('path/to/script.cake'); + }); +}); + +describe('When getting the csproj-path input argument from the action', () => { + const fakeGetInput = core.getInput as jest.MockedFunction; + + beforeAll(() => { + when(fakeGetInput).calledWith('script-path').mockReturnValue(''); + when(fakeGetInput).calledWith('csproj-path').mockReturnValue('path/to/build.csproj'); + when(fakeGetInput).calledWith('cake-version').mockReturnValue(''); + when(fakeGetInput).calledWith('cake-bootstrap').mockReturnValue(''); + when(fakeGetInput).calledWith('dry-run').mockReturnValue(''); + when(fakeGetInput).calledWith('arguments').mockReturnValue(''); + }); + + test('it should return \'project\' as the file type parameter', () => { + expect(action.getInputs().file.type).toBe('project'); + }); + + test('it should return the argument for the csproj-path parameter as the file path parameter', () => { + expect(action.getInputs().file.path).toBe('path/to/build.csproj'); + }); +}); + +describe('When getting both the script-path and csproj-path input arguments from the action', () => { + const fakeGetInput = core.getInput as jest.MockedFunction; + + beforeAll(() => { + when(fakeGetInput).calledWith('script-path').mockReturnValue('path/to/build.cake'); + when(fakeGetInput).calledWith('csproj-path').mockReturnValue('path/to/build.csproj'); + when(fakeGetInput).calledWith('cake-version').mockReturnValue(''); + when(fakeGetInput).calledWith('cake-bootstrap').mockReturnValue(''); + when(fakeGetInput).calledWith('dry-run').mockReturnValue(''); + when(fakeGetInput).calledWith('arguments').mockReturnValue(''); + }); + + test('it should return \'project\' as the file type parameter', () => { + expect(action.getInputs().file.type).toBe('project'); + }); + + test('it should return the argument for the csproj-path parameter as the file path parameter', () => { + expect(action.getInputs().file.path).toBe('path/to/build.csproj'); + }); +}); + +describe('When getting the Cake tool input arguments from the action', () => { + const fakeGetInput = core.getInput as jest.MockedFunction; + + beforeAll(() => { + when(fakeGetInput).calledWith('cake-version').mockReturnValue('the.version.number'); + when(fakeGetInput).calledWith('cake-bootstrap').mockReturnValue('auto'); + when(fakeGetInput).calledWith('dry-run').mockReturnValue(''); + when(fakeGetInput).calledWith('arguments').mockReturnValue(''); }); test('it should return the argument for the cake-version parameter', () => { @@ -32,7 +90,7 @@ describe('When getting the Cake input arguments from the action', () => { }); }); -describe('When getting the documented script input arguments from the action', () => { +describe('When getting the Cake input arguments from the action', () => { const fakeGetInput = core.getInput as jest.MockedFunction; beforeAll(() => { @@ -163,6 +221,7 @@ describe('When getting no input arguments from the action', () => { beforeAll(() => { when(fakeGetInput).calledWith('script-path').mockReturnValue(''); + when(fakeGetInput).calledWith('csproj-path').mockReturnValue(''); when(fakeGetInput).calledWith('cake-version').mockReturnValue(''); when(fakeGetInput).calledWith('cake-bootstrap').mockReturnValue(''); when(fakeGetInput).calledWith('target').mockReturnValue(''); @@ -171,8 +230,12 @@ describe('When getting no input arguments from the action', () => { when(fakeGetInput).calledWith('arguments').mockReturnValue(''); }); - test('it should return an empty string for the script-path parameter', () => { - expect(action.getInputs().scriptPath).toBe(''); + test('it should return \'script\' as the file type parameter', () => { + expect(action.getInputs().file.type).toBe('script'); + }); + + test('it should return \'build.cake\' as the file path parameter', () => { + expect(action.getInputs().file.path).toBe('build.cake'); }); test('it should return latest for the cake-version parameter', () => { diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts index de976cd..0ece8c6 100644 --- a/__tests__/main.test.ts +++ b/__tests__/main.test.ts @@ -20,6 +20,7 @@ describe('When running the action without any input arguments', () => { beforeAll(() => { fakeGetInputs.mockReturnValue({ + file: { type: 'script', path: 'build.cake' }, scriptArguments: [] }); }); @@ -56,7 +57,7 @@ describe('When running the action with the script path input argument', () => { beforeAll(() => { fakeGetInputs.mockReturnValue({ - scriptPath: 'path/to/script.cake', + file: { type: 'script', path: 'path/to/script.cake' }, scriptArguments: [] }); }); @@ -73,7 +74,7 @@ describe('When running the action with the csproj path input argument', () => { beforeAll(() => { fakeGetInputs.mockReturnValue({ - csprojPath: './path/to/build.csproj', + file: { type: 'project', path: 'path/to/build.csproj' }, scriptArguments: [] }); }); @@ -85,7 +86,7 @@ describe('When running the action with the csproj path input argument', () => { test('it should run the specified Cake Frosting project', async () => { await run(); - expect(fakeRunProject.mock.calls[0][0]).toBe('./path/to/build.csproj'); + expect(fakeRunProject.mock.calls[0][0]).toBe('path/to/build.csproj'); }); }); @@ -95,6 +96,7 @@ describe('When running the action with tool-manifest as the Cake version input a beforeAll(() => { fakeGetInputs.mockReturnValue({ + file: { type: 'script', path: 'build.cake' }, cakeVersion: { version: 'tool-manifest' }, scriptArguments: [] }); @@ -112,6 +114,7 @@ describe('When running the action with latest as the Cake version input argument beforeAll(() => { fakeGetInputs.mockReturnValue({ + file: { type: 'script', path: 'build.cake' }, cakeVersion: { version: 'latest' }, scriptArguments: [] }); @@ -129,6 +132,7 @@ describe('When running the action with a specific version as the Cake version in beforeAll(() => { fakeGetInputs.mockReturnValue({ + file: { type: 'script', path: 'build.cake' }, cakeVersion: { version: 'specific', number: 'the.version.number' }, scriptArguments: [] }); @@ -149,6 +153,7 @@ describe('When running the action with auto as the Cake bootstrap input argument beforeAll(() => { fakeGetInputs.mockReturnValue({ + file: { type: 'script', path: 'build.cake' }, cakeBootstrap: 'auto', scriptArguments: [] }); @@ -166,7 +171,7 @@ describe('When running the action with explicit as the Cake bootstrap input argu beforeAll(() => { fakeGetInputs.mockReturnValue({ - scriptPath: 'custom.cake', + file: { type: 'script', path: 'custom.cake' }, cakeBootstrap: 'explicit', scriptArguments: [] }); @@ -184,6 +189,7 @@ describe('When running the action with skip as the Cake bootstrap input argument beforeAll(() => { fakeGetInputs.mockReturnValue({ + file: { type: 'script', path: 'build.cake' }, cakeBootstrap: 'skip', scriptArguments: [] }); @@ -201,6 +207,7 @@ describe('When running the action with the target input argument', () => { beforeAll(() => { fakeGetInputs.mockReturnValue({ + file: { type: 'script', path: 'build.cake' }, scriptArguments: [new CakeArgument('target', 'Task-To-Run')] }); }); @@ -218,6 +225,7 @@ describe('When running the action with the verbosity input argument', () => { beforeAll(() => { fakeGetInputs.mockReturnValue({ + file: { type: 'script', path: 'build.cake' }, scriptArguments: [new CakeArgument('verbosity', 'Verbosity-Level')] }); }); @@ -235,6 +243,7 @@ describe('When running the action with custom script input arguments', () => { beforeAll(() => { fakeGetInputs.mockReturnValue({ + file: { type: 'script', path: 'build.cake' }, scriptArguments: [ new CakeArgument('string-parameter', '\'value\''), new CakeArgument('numeric-parameter', '3'), diff --git a/dist/index.js b/dist/index.js index 42d106a..d1e23d0 100644 --- a/dist/index.js +++ b/dist/index.js @@ -3990,14 +3990,30 @@ const script = __importStar(__nccwpck_require__(8714)); const input = __importStar(__nccwpck_require__(6747)); function getInputs() { return { - scriptPath: core.getInput('script-path'), - csprojPath: core.getInput('csproj-path'), + file: getFileInput(), cakeVersion: getCakeVersionInput(), cakeBootstrap: getCakeBootstrapInput(), scriptArguments: getScriptInputs() }; } exports.getInputs = getInputs; +function getFileInput() { + const scriptPath = core.getInput('script-path'); + const projectPath = core.getInput('csproj-path'); + // When both script and project paths are specified, + // the project path takes precedence. + // If neither is provided, the default 'build.cake' script + // is used, as per Cake's convention. + if (projectPath) { + return { type: 'project', path: projectPath }; + } + else if (scriptPath) { + return { type: 'script', path: scriptPath }; + } + else { + return { type: 'script', path: 'build.cake' }; + } +} function getCakeVersionInput() { const version = core.getInput('cake-version').toLowerCase(); switch (version) { @@ -4534,8 +4550,7 @@ function run() { return __awaiter(this, void 0, void 0, function* () { try { const inputs = action.getInputs(); - const scriptPath = inputs.scriptPath; - const csprojPath = inputs.csprojPath; + const file = inputs.file; const version = inputs.cakeVersion; const bootstrap = inputs.cakeBootstrap; const toolsDir = new toolsDirectory_1.ToolsDirectory(); @@ -4543,15 +4558,15 @@ function run() { const cakeToolSettings = new cakeToolSettings_1.CakeToolSettings(toolsDir, (version === null || version === void 0 ? void 0 : version.version) === 'tool-manifest'); dotnet.disableTelemetry(); dotnet.disableWelcomeMessage(); - if (csprojPath) { - yield cake.runProject(csprojPath, toolsDir, ...inputs.scriptArguments); + if (file.type === 'project') { + yield cake.runProject(file.path, toolsDir, ...inputs.scriptArguments); } else { yield cakeTool.install(toolsDir, version); if (bootstrap === 'explicit') { - yield cake.bootstrapScript(scriptPath, cakeToolSettings); + yield cake.bootstrapScript(file.path, cakeToolSettings); } - yield cake.runScript(scriptPath, cakeToolSettings, ...inputs.scriptArguments); + yield cake.runScript(file.path, cakeToolSettings, ...inputs.scriptArguments); } } catch (error) { diff --git a/src/action.ts b/src/action.ts index b0e606e..b823918 100644 --- a/src/action.ts +++ b/src/action.ts @@ -3,8 +3,7 @@ import * as script from './cakeParameter'; import * as input from './input'; interface CakeInputs { - readonly scriptPath?: string, - readonly csprojPath?: string, + readonly file: File, readonly cakeVersion?: CakeVersion, readonly cakeBootstrap?: CakeBootstrap; } @@ -13,6 +12,16 @@ interface ScriptInputs { readonly scriptArguments: script.CakeParameter[]; } +export type File = ScriptFile | ProjectFile; +type ScriptFile = { + readonly type: 'script', + path: string; +}; +type ProjectFile = { + readonly type: 'project', + path: string; +}; + export type CakeVersion = | ToolManifest | Latest @@ -35,14 +44,30 @@ export type CakeBootstrap = export function getInputs(): CakeInputs & ScriptInputs { return { - scriptPath: core.getInput('script-path'), - csprojPath: core.getInput('csproj-path'), + file: getFileInput(), cakeVersion: getCakeVersionInput(), cakeBootstrap: getCakeBootstrapInput(), scriptArguments: getScriptInputs() }; } +function getFileInput(): File { + const scriptPath = core.getInput('script-path'); + const projectPath = core.getInput('csproj-path'); + + // When both script and project paths are specified, + // the project path takes precedence. + // If neither is provided, the default 'build.cake' script + // is used, as per Cake's convention. + if (projectPath) { + return { type: 'project', path: projectPath }; + } else if (scriptPath) { + return { type: 'script', path: scriptPath }; + } else { + return { type: 'script', path: 'build.cake' }; + } +} + function getCakeVersionInput(): CakeVersion { const version = core.getInput('cake-version').toLowerCase(); switch (version) { @@ -76,7 +101,7 @@ function getScriptInputs(): script.CakeParameter[] { ]; } -function parseSkipBootstrapSwitch() : script.CakeParameter[] { +function parseSkipBootstrapSwitch(): script.CakeParameter[] { return getCakeBootstrapInput() === 'skip' ? [new script.CakeSwitch('skip-bootstrap')] : []; diff --git a/src/main.ts b/src/main.ts index 2793181..49ec56c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,8 +10,7 @@ import * as action from './action'; export async function run() { try { const inputs = action.getInputs(); - const scriptPath = inputs.scriptPath; - const csprojPath = inputs.csprojPath; + const file = inputs.file; const version = inputs.cakeVersion; const bootstrap = inputs.cakeBootstrap; @@ -23,18 +22,17 @@ export async function run() { dotnet.disableTelemetry(); dotnet.disableWelcomeMessage(); - if (csprojPath) { - await cake.runProject(csprojPath, toolsDir, ...inputs.scriptArguments); + if (file.type === 'project') { + await cake.runProject(file.path, toolsDir, ...inputs.scriptArguments); } else { await cakeTool.install(toolsDir, version); if (bootstrap === 'explicit') { - await cake.bootstrapScript(scriptPath, cakeToolSettings); + await cake.bootstrapScript(file.path, cakeToolSettings); } - await cake.runScript(scriptPath, cakeToolSettings, ...inputs.scriptArguments); + await cake.runScript(file.path, cakeToolSettings, ...inputs.scriptArguments); } - } catch (error) { if (isError(error)) { core.setFailed(error.message); From 94f064d359017f45045c1903c33470a8ad6899cf Mon Sep 17 00:00:00 2001 From: Enrico Campidoglio Date: Tue, 1 Oct 2024 14:21:17 +0200 Subject: [PATCH 4/8] Extracts the functions to execute a Cake Frosting project and a Cake script --- dist/index.js | 95 ++++++++++++++++++++++++++++++++++++++++++--------- src/exec.ts | 30 ++++++++++++++++ src/main.ts | 30 ++++++---------- 3 files changed, 118 insertions(+), 37 deletions(-) create mode 100644 src/exec.ts diff --git a/dist/index.js b/dist/index.js index d1e23d0..9c787e3 100644 --- a/dist/index.js +++ b/dist/index.js @@ -4434,6 +4434,74 @@ function restoreLocalTools() { exports.restoreLocalTools = restoreLocalTools; +/***/ }), + +/***/ 5047: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +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()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.script = exports.project = void 0; +const cakeToolSettings_1 = __nccwpck_require__(6881); +const toolsDirectory_1 = __nccwpck_require__(6745); +const cake = __importStar(__nccwpck_require__(9275)); +const cakeTool = __importStar(__nccwpck_require__(4574)); +function project(path, ...params) { + return __awaiter(this, void 0, void 0, function* () { + const toolsDir = new toolsDirectory_1.ToolsDirectory(); + toolsDir.create(); + yield cake.runProject(path, toolsDir, ...params); + }); +} +exports.project = project; +function script(path, version, bootstrap, ...params) { + return __awaiter(this, void 0, void 0, function* () { + const toolsDir = new toolsDirectory_1.ToolsDirectory(); + toolsDir.create(); + const cakeToolSettings = new cakeToolSettings_1.CakeToolSettings(toolsDir, (version === null || version === void 0 ? void 0 : version.version) === 'tool-manifest'); + yield cakeTool.install(toolsDir, version); + if (bootstrap === 'explicit') { + yield cake.bootstrapScript(path, cakeToolSettings); + } + yield cake.runScript(path, cakeToolSettings, ...params); + }); +} +exports.script = script; + + /***/ }), /***/ 3265: @@ -4539,13 +4607,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge Object.defineProperty(exports, "__esModule", ({ value: true })); exports.run = void 0; const core = __importStar(__nccwpck_require__(2186)); -const toolsDirectory_1 = __nccwpck_require__(6745); -const cakeToolSettings_1 = __nccwpck_require__(6881); -const guards_1 = __nccwpck_require__(3265); -const dotnet = __importStar(__nccwpck_require__(9870)); -const cakeTool = __importStar(__nccwpck_require__(4574)); -const cake = __importStar(__nccwpck_require__(9275)); const action = __importStar(__nccwpck_require__(7672)); +const dotnet = __importStar(__nccwpck_require__(9870)); +const exec = __importStar(__nccwpck_require__(5047)); +const guards_1 = __nccwpck_require__(3265); function run() { return __awaiter(this, void 0, void 0, function* () { try { @@ -4553,20 +4618,16 @@ function run() { const file = inputs.file; const version = inputs.cakeVersion; const bootstrap = inputs.cakeBootstrap; - const toolsDir = new toolsDirectory_1.ToolsDirectory(); - toolsDir.create(); - const cakeToolSettings = new cakeToolSettings_1.CakeToolSettings(toolsDir, (version === null || version === void 0 ? void 0 : version.version) === 'tool-manifest'); dotnet.disableTelemetry(); dotnet.disableWelcomeMessage(); - if (file.type === 'project') { - yield cake.runProject(file.path, toolsDir, ...inputs.scriptArguments); - } - else { - yield cakeTool.install(toolsDir, version); - if (bootstrap === 'explicit') { - yield cake.bootstrapScript(file.path, cakeToolSettings); + switch (file.type) { + case 'project': + yield exec.project(file.path, ...inputs.scriptArguments); + break; + case 'script': { + yield exec.script(file.path, version, bootstrap, ...inputs.scriptArguments); + break; } - yield cake.runScript(file.path, cakeToolSettings, ...inputs.scriptArguments); } } catch (error) { diff --git a/src/exec.ts b/src/exec.ts new file mode 100644 index 0000000..ff4f1f1 --- /dev/null +++ b/src/exec.ts @@ -0,0 +1,30 @@ +import { CakeBootstrap, CakeVersion } from './action'; +import { CakeParameter } from './cakeParameter'; +import { CakeToolSettings } from './cakeToolSettings'; +import { ToolsDirectory } from './toolsDirectory'; +import * as cake from './cake'; +import * as cakeTool from './cakeTool'; + +export async function project(path: string, ...params: CakeParameter[]) { + const toolsDir = new ToolsDirectory(); + toolsDir.create(); + await cake.runProject(path, toolsDir, ...params); +} + +export async function script( + path: string, + version?: CakeVersion, + bootstrap?: CakeBootstrap, + ...params: CakeParameter[]) { + const toolsDir = new ToolsDirectory(); + const cakeToolSettings = new CakeToolSettings(toolsDir, version?.version === 'tool-manifest'); + + toolsDir.create(); + await cakeTool.install(toolsDir, version); + + if (bootstrap === 'explicit') { + await cake.bootstrapScript(path, cakeToolSettings); + } + + await cake.runScript(path, cakeToolSettings, ...params); +} diff --git a/src/main.ts b/src/main.ts index 49ec56c..3823756 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,11 +1,8 @@ import * as core from '@actions/core'; -import { ToolsDirectory } from './toolsDirectory'; -import { CakeToolSettings } from './cakeToolSettings'; -import { isError, isString } from './guards'; -import * as dotnet from './dotnet'; -import * as cakeTool from './cakeTool'; -import * as cake from './cake'; import * as action from './action'; +import * as dotnet from './dotnet'; +import * as exec from './exec'; +import { isError, isString } from './guards'; export async function run() { try { @@ -14,24 +11,17 @@ export async function run() { const version = inputs.cakeVersion; const bootstrap = inputs.cakeBootstrap; - const toolsDir = new ToolsDirectory(); - toolsDir.create(); - - const cakeToolSettings = new CakeToolSettings(toolsDir, version?.version === 'tool-manifest'); - dotnet.disableTelemetry(); dotnet.disableWelcomeMessage(); - if (file.type === 'project') { - await cake.runProject(file.path, toolsDir, ...inputs.scriptArguments); - } else { - await cakeTool.install(toolsDir, version); - - if (bootstrap === 'explicit') { - await cake.bootstrapScript(file.path, cakeToolSettings); + switch (file.type) { + case 'project': + await exec.project(file.path, ...inputs.scriptArguments); + break; + case 'script': { + await exec.script(file.path, version, bootstrap, ...inputs.scriptArguments); + break; } - - await cake.runScript(file.path, cakeToolSettings, ...inputs.scriptArguments); } } catch (error) { if (isError(error)) { From 252741d37c8583e72eba470feb991b0416f3a49f Mon Sep 17 00:00:00 2001 From: Enrico Campidoglio Date: Wed, 2 Oct 2024 09:13:04 +0200 Subject: [PATCH 5/8] Extracts the concept of a build file Previous mentions of 'script file' and 'script arguments' are now called 'build file' and 'build arguments' to include both scripts as well as projects. --- __tests__/action.test.ts | 68 ++++++++++++++++++++-------------------- __tests__/main.test.ts | 48 ++++++++++++++-------------- dist/index.js | 24 +++++++------- src/action.ts | 40 +++++++++++------------ src/main.ts | 8 ++--- 5 files changed, 94 insertions(+), 94 deletions(-) diff --git a/__tests__/action.test.ts b/__tests__/action.test.ts index 4c215eb..d25ab69 100644 --- a/__tests__/action.test.ts +++ b/__tests__/action.test.ts @@ -17,12 +17,12 @@ describe('When getting the script-path input argument from the action', () => { when(fakeGetInput).calledWith('arguments').mockReturnValue(''); }); - test('it should return \'script\' as the file type parameter', () => { - expect(action.getInputs().file.type).toBe('script'); + test('it should return \'script\' as the build file type parameter', () => { + expect(action.getInputs().buildFile.type).toBe('script'); }); - test('it should return the argument for the script-path parameter as the file path parameter', () => { - expect(action.getInputs().file.path).toBe('path/to/script.cake'); + test('it should return the argument for the script-path parameter as the build file path parameter', () => { + expect(action.getInputs().buildFile.path).toBe('path/to/script.cake'); }); }); @@ -38,12 +38,12 @@ describe('When getting the csproj-path input argument from the action', () => { when(fakeGetInput).calledWith('arguments').mockReturnValue(''); }); - test('it should return \'project\' as the file type parameter', () => { - expect(action.getInputs().file.type).toBe('project'); + test('it should return \'project\' as the build file type parameter', () => { + expect(action.getInputs().buildFile.type).toBe('project'); }); - test('it should return the argument for the csproj-path parameter as the file path parameter', () => { - expect(action.getInputs().file.path).toBe('path/to/build.csproj'); + test('it should return the argument for the csproj-path parameter as the build file path parameter', () => { + expect(action.getInputs().buildFile.path).toBe('path/to/build.csproj'); }); }); @@ -59,12 +59,12 @@ describe('When getting both the script-path and csproj-path input arguments from when(fakeGetInput).calledWith('arguments').mockReturnValue(''); }); - test('it should return \'project\' as the file type parameter', () => { - expect(action.getInputs().file.type).toBe('project'); + test('it should return \'project\' as the build file type parameter', () => { + expect(action.getInputs().buildFile.type).toBe('project'); }); - test('it should return the argument for the csproj-path parameter as the file path parameter', () => { - expect(action.getInputs().file.path).toBe('path/to/build.csproj'); + test('it should return the argument for the csproj-path parameter as the build file path parameter', () => { + expect(action.getInputs().buildFile.path).toBe('path/to/build.csproj'); }); }); @@ -101,15 +101,15 @@ describe('When getting the Cake input arguments from the action', () => { }); test('it should return the argument for the target script parameter', () => { - expect(action.getInputs().scriptArguments).toContainEqual(new CakeArgument('target', 'Task-To-Run')); + expect(action.getInputs().buildArguments).toContainEqual(new CakeArgument('target', 'Task-To-Run')); }); test('it should return the argument for the verbosity script parameter', () => { - expect(action.getInputs().scriptArguments).toContainEqual(new CakeArgument('verbosity', 'Verbosity-Level')); + expect(action.getInputs().buildArguments).toContainEqual(new CakeArgument('verbosity', 'Verbosity-Level')); }); test('it should return the argument for the dry-run script parameter', () => { - expect(action.getInputs().scriptArguments).toContainEqual(new CakeSwitch('dryrun')); + expect(action.getInputs().buildArguments).toContainEqual(new CakeSwitch('dryrun')); }); }); @@ -122,7 +122,7 @@ describe('When getting the dry-run script input argument set to false from the a }); test('it should not pass the dry run switch to the script', () => { - expect(action.getInputs().scriptArguments).not.toContainEqual(new CakeSwitch('dryrun')); + expect(action.getInputs().buildArguments).not.toContainEqual(new CakeSwitch('dryrun')); }); }); @@ -138,15 +138,15 @@ describe('When getting multiple custom script input arguments from the action', }); test('it should return the argument for a custom string parameter', () => { - expect(action.getInputs().scriptArguments).toContainEqual(new CakeArgument('string-parameter', '\'value\'')); + expect(action.getInputs().buildArguments).toContainEqual(new CakeArgument('string-parameter', '\'value\'')); }); test('it should return the argument for a custom numeric parameter', () => { - expect(action.getInputs().scriptArguments).toContainEqual(new CakeArgument('numeric-parameter', '3')); + expect(action.getInputs().buildArguments).toContainEqual(new CakeArgument('numeric-parameter', '3')); }); test('it should return the argument for a custom boolean parameter', () => { - expect(action.getInputs().scriptArguments).toContainEqual(new CakeArgument('boolean-parameter', 'true')); + expect(action.getInputs().buildArguments).toContainEqual(new CakeArgument('boolean-parameter', 'true')); }); }); @@ -160,7 +160,7 @@ describe('When getting a single custom script input argument from the action', ( }); test('it should return the argument for the custom parameter', () => { - expect(action.getInputs().scriptArguments).toContainEqual(new CakeArgument('name', 'value')); + expect(action.getInputs().buildArguments).toContainEqual(new CakeArgument('name', 'value')); }); }); @@ -172,7 +172,7 @@ describe('When getting a single custom script input argument on one line from th }); test('it should return the argument for the custom parameter', () => { - expect(action.getInputs().scriptArguments).toContainEqual(new CakeArgument('name', 'value')); + expect(action.getInputs().buildArguments).toContainEqual(new CakeArgument('name', 'value')); }); }); @@ -186,7 +186,7 @@ describe('When getting a custom script input argument that contains colons (e.g. }); test('it should return the argument for the custom parameter', () => { - expect(action.getInputs().scriptArguments).toContainEqual(new CakeArgument('url', 'https://example.com')); + expect(action.getInputs().buildArguments).toContainEqual(new CakeArgument('url', 'https://example.com')); }); }); @@ -208,11 +208,11 @@ describe('When getting improperly formatted custom script input arguments from t }); test('it should not parse the invalid parameter names and values', () => { - expect(action.getInputs().scriptArguments).not.toContainEqual(new CakeArgument('name', 'value')); + expect(action.getInputs().buildArguments).not.toContainEqual(new CakeArgument('name', 'value')); }); test('it should not parse the invalid parameter names', () => { - expect(action.getInputs().scriptArguments).not.toContainEqual(new CakeArgument('name', '')); + expect(action.getInputs().buildArguments).not.toContainEqual(new CakeArgument('name', '')); }); }); @@ -230,12 +230,12 @@ describe('When getting no input arguments from the action', () => { when(fakeGetInput).calledWith('arguments').mockReturnValue(''); }); - test('it should return \'script\' as the file type parameter', () => { - expect(action.getInputs().file.type).toBe('script'); + test('it should return \'script\' as the build file type parameter', () => { + expect(action.getInputs().buildFile.type).toBe('script'); }); - test('it should return \'build.cake\' as the file path parameter', () => { - expect(action.getInputs().file.path).toBe('build.cake'); + test('it should return \'build.cake\' as the build file path parameter', () => { + expect(action.getInputs().buildFile.path).toBe('build.cake'); }); test('it should return latest for the cake-version parameter', () => { @@ -247,15 +247,15 @@ describe('When getting no input arguments from the action', () => { }); test('it should return an empty string for the target script parameter', () => { - expect(action.getInputs().scriptArguments).toContainEqual(new CakeArgument('target', '')); + expect(action.getInputs().buildArguments).toContainEqual(new CakeArgument('target', '')); }); test('it should return an empty string for the verbosity script parameter', () => { - expect(action.getInputs().scriptArguments).toContainEqual(new CakeArgument('verbosity', '')); + expect(action.getInputs().buildArguments).toContainEqual(new CakeArgument('verbosity', '')); }); test('it should not pass the dry run switch to the script', () => { - expect(action.getInputs().scriptArguments).not.toContainEqual(new CakeSwitch('dryrun')); + expect(action.getInputs().buildArguments).not.toContainEqual(new CakeSwitch('dryrun')); }); }); @@ -295,7 +295,7 @@ describe('When getting the cake-bootstrap script input argument set to auto from }); test('it should not pass the skip-bootstrap switch to the script', () => { - expect(action.getInputs().scriptArguments).not.toContainEqual(new CakeSwitch('skip-bootstrap')); + expect(action.getInputs().buildArguments).not.toContainEqual(new CakeSwitch('skip-bootstrap')); }); }); @@ -311,7 +311,7 @@ describe('When getting the cake-bootstrap script input argument set to explicit }); test('it should not pass the skip-bootstrap switch to the script', () => { - expect(action.getInputs().scriptArguments).not.toContainEqual(new CakeSwitch('skip-bootstrap')); + expect(action.getInputs().buildArguments).not.toContainEqual(new CakeSwitch('skip-bootstrap')); }); }); @@ -327,6 +327,6 @@ describe('When getting the cake-bootstrap script input argument set to skip from }); test('it should pass the skip-bootstrap switch to the script', () => { - expect(action.getInputs().scriptArguments).toContainEqual(new CakeSwitch('skip-bootstrap')); + expect(action.getInputs().buildArguments).toContainEqual(new CakeSwitch('skip-bootstrap')); }); }); diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts index 0ece8c6..898c431 100644 --- a/__tests__/main.test.ts +++ b/__tests__/main.test.ts @@ -20,8 +20,8 @@ describe('When running the action without any input arguments', () => { beforeAll(() => { fakeGetInputs.mockReturnValue({ - file: { type: 'script', path: 'build.cake' }, - scriptArguments: [] + buildFile: { type: 'script', path: 'build.cake' }, + buildArguments: [] }); }); @@ -57,8 +57,8 @@ describe('When running the action with the script path input argument', () => { beforeAll(() => { fakeGetInputs.mockReturnValue({ - file: { type: 'script', path: 'path/to/script.cake' }, - scriptArguments: [] + buildFile: { type: 'script', path: 'path/to/script.cake' }, + buildArguments: [] }); }); @@ -74,8 +74,8 @@ describe('When running the action with the csproj path input argument', () => { beforeAll(() => { fakeGetInputs.mockReturnValue({ - file: { type: 'project', path: 'path/to/build.csproj' }, - scriptArguments: [] + buildFile: { type: 'project', path: 'path/to/build.csproj' }, + buildArguments: [] }); }); @@ -96,9 +96,9 @@ describe('When running the action with tool-manifest as the Cake version input a beforeAll(() => { fakeGetInputs.mockReturnValue({ - file: { type: 'script', path: 'build.cake' }, + buildFile: { type: 'script', path: 'build.cake' }, cakeVersion: { version: 'tool-manifest' }, - scriptArguments: [] + buildArguments: [] }); }); @@ -114,9 +114,9 @@ describe('When running the action with latest as the Cake version input argument beforeAll(() => { fakeGetInputs.mockReturnValue({ - file: { type: 'script', path: 'build.cake' }, + buildFile: { type: 'script', path: 'build.cake' }, cakeVersion: { version: 'latest' }, - scriptArguments: [] + buildArguments: [] }); }); @@ -132,9 +132,9 @@ describe('When running the action with a specific version as the Cake version in beforeAll(() => { fakeGetInputs.mockReturnValue({ - file: { type: 'script', path: 'build.cake' }, + buildFile: { type: 'script', path: 'build.cake' }, cakeVersion: { version: 'specific', number: 'the.version.number' }, - scriptArguments: [] + buildArguments: [] }); }); @@ -153,9 +153,9 @@ describe('When running the action with auto as the Cake bootstrap input argument beforeAll(() => { fakeGetInputs.mockReturnValue({ - file: { type: 'script', path: 'build.cake' }, + buildFile: { type: 'script', path: 'build.cake' }, cakeBootstrap: 'auto', - scriptArguments: [] + buildArguments: [] }); }); @@ -171,9 +171,9 @@ describe('When running the action with explicit as the Cake bootstrap input argu beforeAll(() => { fakeGetInputs.mockReturnValue({ - file: { type: 'script', path: 'custom.cake' }, + buildFile: { type: 'script', path: 'custom.cake' }, cakeBootstrap: 'explicit', - scriptArguments: [] + buildArguments: [] }); }); @@ -189,9 +189,9 @@ describe('When running the action with skip as the Cake bootstrap input argument beforeAll(() => { fakeGetInputs.mockReturnValue({ - file: { type: 'script', path: 'build.cake' }, + buildFile: { type: 'script', path: 'build.cake' }, cakeBootstrap: 'skip', - scriptArguments: [] + buildArguments: [] }); }); @@ -207,8 +207,8 @@ describe('When running the action with the target input argument', () => { beforeAll(() => { fakeGetInputs.mockReturnValue({ - file: { type: 'script', path: 'build.cake' }, - scriptArguments: [new CakeArgument('target', 'Task-To-Run')] + buildFile: { type: 'script', path: 'build.cake' }, + buildArguments: [new CakeArgument('target', 'Task-To-Run')] }); }); @@ -225,8 +225,8 @@ describe('When running the action with the verbosity input argument', () => { beforeAll(() => { fakeGetInputs.mockReturnValue({ - file: { type: 'script', path: 'build.cake' }, - scriptArguments: [new CakeArgument('verbosity', 'Verbosity-Level')] + buildFile: { type: 'script', path: 'build.cake' }, + buildArguments: [new CakeArgument('verbosity', 'Verbosity-Level')] }); }); @@ -243,8 +243,8 @@ describe('When running the action with custom script input arguments', () => { beforeAll(() => { fakeGetInputs.mockReturnValue({ - file: { type: 'script', path: 'build.cake' }, - scriptArguments: [ + buildFile: { type: 'script', path: 'build.cake' }, + buildArguments: [ new CakeArgument('string-parameter', '\'value\''), new CakeArgument('numeric-parameter', '3'), new CakeArgument('boolean-parameter', 'true'), diff --git a/dist/index.js b/dist/index.js index 9c787e3..d8fe56d 100644 --- a/dist/index.js +++ b/dist/index.js @@ -3986,14 +3986,14 @@ var __importStar = (this && this.__importStar) || function (mod) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getInputs = void 0; const core = __importStar(__nccwpck_require__(2186)); -const script = __importStar(__nccwpck_require__(8714)); +const build = __importStar(__nccwpck_require__(8714)); const input = __importStar(__nccwpck_require__(6747)); function getInputs() { return { - file: getFileInput(), + buildFile: getFileInput(), cakeVersion: getCakeVersionInput(), cakeBootstrap: getCakeBootstrapInput(), - scriptArguments: getScriptInputs() + buildArguments: getScriptInputs() }; } exports.getInputs = getInputs; @@ -4037,8 +4037,8 @@ function getCakeBootstrapInput() { } function getScriptInputs() { return [ - new script.CakeArgument('target', core.getInput('target')), - new script.CakeArgument('verbosity', core.getInput('verbosity')), + new build.CakeArgument('target', core.getInput('target')), + new build.CakeArgument('verbosity', core.getInput('verbosity')), ...parseSkipBootstrapSwitch(), ...parseDryRunSwitch(), ...parseCustomArguments() @@ -4046,19 +4046,19 @@ function getScriptInputs() { } function parseSkipBootstrapSwitch() { return getCakeBootstrapInput() === 'skip' - ? [new script.CakeSwitch('skip-bootstrap')] + ? [new build.CakeSwitch('skip-bootstrap')] : []; } function parseDryRunSwitch() { return input.getBooleanInput('dry-run') - ? [new script.CakeSwitch('dryrun')] + ? [new build.CakeSwitch('dryrun')] : []; } function parseCustomArguments() { return input.getMultilineInput('arguments') .filter(line => containsArgumentDefinition(line)) .map(line => parseNameAndValue(line)) - .map(([name, value]) => new script.CakeArgument(name, value)); + .map(([name, value]) => new build.CakeArgument(name, value)); } function containsArgumentDefinition(line) { return /.+:.+/.test(line); @@ -4615,17 +4615,17 @@ function run() { return __awaiter(this, void 0, void 0, function* () { try { const inputs = action.getInputs(); - const file = inputs.file; + const buildFile = inputs.buildFile; const version = inputs.cakeVersion; const bootstrap = inputs.cakeBootstrap; dotnet.disableTelemetry(); dotnet.disableWelcomeMessage(); - switch (file.type) { + switch (buildFile.type) { case 'project': - yield exec.project(file.path, ...inputs.scriptArguments); + yield exec.project(buildFile.path, ...inputs.buildArguments); break; case 'script': { - yield exec.script(file.path, version, bootstrap, ...inputs.scriptArguments); + yield exec.script(buildFile.path, version, bootstrap, ...inputs.buildArguments); break; } } diff --git a/src/action.ts b/src/action.ts index b823918..2152717 100644 --- a/src/action.ts +++ b/src/action.ts @@ -1,23 +1,23 @@ import * as core from '@actions/core'; -import * as script from './cakeParameter'; +import * as build from './cakeParameter'; import * as input from './input'; interface CakeInputs { - readonly file: File, + readonly buildFile: BuildFile, readonly cakeVersion?: CakeVersion, readonly cakeBootstrap?: CakeBootstrap; } -interface ScriptInputs { - readonly scriptArguments: script.CakeParameter[]; +interface BuildInputs { + readonly buildArguments: build.CakeParameter[]; } -export type File = ScriptFile | ProjectFile; -type ScriptFile = { +export type BuildFile = Script | Project; +type Script = { readonly type: 'script', path: string; }; -type ProjectFile = { +type Project = { readonly type: 'project', path: string; }; @@ -42,16 +42,16 @@ export type CakeBootstrap = | 'explicit' | 'skip'; -export function getInputs(): CakeInputs & ScriptInputs { +export function getInputs(): CakeInputs & BuildInputs { return { - file: getFileInput(), + buildFile: getFileInput(), cakeVersion: getCakeVersionInput(), cakeBootstrap: getCakeBootstrapInput(), - scriptArguments: getScriptInputs() + buildArguments: getScriptInputs() }; } -function getFileInput(): File { +function getFileInput(): BuildFile { const scriptPath = core.getInput('script-path'); const projectPath = core.getInput('csproj-path'); @@ -91,33 +91,33 @@ function getCakeBootstrapInput(): CakeBootstrap { } } -function getScriptInputs(): script.CakeParameter[] { +function getScriptInputs(): build.CakeParameter[] { return [ - new script.CakeArgument('target', core.getInput('target')), - new script.CakeArgument('verbosity', core.getInput('verbosity')), + new build.CakeArgument('target', core.getInput('target')), + new build.CakeArgument('verbosity', core.getInput('verbosity')), ...parseSkipBootstrapSwitch(), ...parseDryRunSwitch(), ...parseCustomArguments() ]; } -function parseSkipBootstrapSwitch(): script.CakeParameter[] { +function parseSkipBootstrapSwitch(): build.CakeParameter[] { return getCakeBootstrapInput() === 'skip' - ? [new script.CakeSwitch('skip-bootstrap')] + ? [new build.CakeSwitch('skip-bootstrap')] : []; } -function parseDryRunSwitch(): script.CakeParameter[] { +function parseDryRunSwitch(): build.CakeParameter[] { return input.getBooleanInput('dry-run') - ? [new script.CakeSwitch('dryrun')] + ? [new build.CakeSwitch('dryrun')] : []; } -function parseCustomArguments(): script.CakeParameter[] { +function parseCustomArguments(): build.CakeParameter[] { return input.getMultilineInput('arguments') .filter(line => containsArgumentDefinition(line)) .map(line => parseNameAndValue(line)) - .map(([name, value]) => new script.CakeArgument(name, value)); + .map(([name, value]) => new build.CakeArgument(name, value)); } function containsArgumentDefinition(line: string): boolean { diff --git a/src/main.ts b/src/main.ts index 3823756..631d390 100644 --- a/src/main.ts +++ b/src/main.ts @@ -7,19 +7,19 @@ import { isError, isString } from './guards'; export async function run() { try { const inputs = action.getInputs(); - const file = inputs.file; + const buildFile = inputs.buildFile; const version = inputs.cakeVersion; const bootstrap = inputs.cakeBootstrap; dotnet.disableTelemetry(); dotnet.disableWelcomeMessage(); - switch (file.type) { + switch (buildFile.type) { case 'project': - await exec.project(file.path, ...inputs.scriptArguments); + await exec.project(buildFile.path, ...inputs.buildArguments); break; case 'script': { - await exec.script(file.path, version, bootstrap, ...inputs.scriptArguments); + await exec.script(buildFile.path, version, bootstrap, ...inputs.buildArguments); break; } } From 86fd49e8eec8f8e5b75c339df128d9aa59c56f83 Mon Sep 17 00:00:00 2001 From: Enrico Campidoglio Date: Wed, 2 Oct 2024 09:24:51 +0200 Subject: [PATCH 6/8] Renames the `csproj-path` input parameter to `project-path` --- README.md | 8 ++++---- __tests__/action.test.ts | 16 ++++++++-------- action.yml | 2 +- dist/index.js | 2 +- src/action.ts | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index ad954f7..327d8a6 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ steps: uses: cake-build/cake-action@v2 ``` -The Cake action will look for a script named `build.cake` in your repository's root directory and run it for you using the [Cake Tool](https://www.nuget.org/packages/Cake.Tool/). If you are using a [Cake Frosting](https://cakebuild.net/docs/running-builds/runners/cake-frosting) project, the `csproj-path` parameter must be specified and the `script-path` is ignored. +The Cake action will look for a script named `build.cake` in your repository's root directory and run it for you using the [Cake Tool](https://www.nuget.org/packages/Cake.Tool/). If you are using a [Cake Frosting](https://cakebuild.net/docs/running-builds/runners/cake-frosting) project, the `project-path` parameter must be specified and the `script-path` is ignored. All output from the Cake script or Cake Frosting project will be automatically redirected to the build log for inspection. @@ -32,16 +32,16 @@ steps: script-path: path/to/script.cake ``` -### `csproj-path` +### `project-path` -If you are using [Cake Frosting](https://cakebuild.net/docs/running-builds/runners/cake-frosting), you can specify the path with the `csproj-path` [input parameter](https://help.github.com/en/github/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idstepswith): +If you are using [Cake Frosting](https://cakebuild.net/docs/running-builds/runners/cake-frosting), you can specify the path with the `project-path` [input parameter](https://help.github.com/en/github/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idstepswith): ```yml steps: - name: Run Cake Frosting uses: cake-build/cake-action@v2 with: - csproj-path: path/to/build.csproj + project-path: path/to/build.csproj ``` ### `target` diff --git a/__tests__/action.test.ts b/__tests__/action.test.ts index d25ab69..7bff41b 100644 --- a/__tests__/action.test.ts +++ b/__tests__/action.test.ts @@ -10,7 +10,7 @@ describe('When getting the script-path input argument from the action', () => { beforeAll(() => { when(fakeGetInput).calledWith('script-path').mockReturnValue('path/to/script.cake'); - when(fakeGetInput).calledWith('csproj-path').mockReturnValue(''); + when(fakeGetInput).calledWith('project-path').mockReturnValue(''); when(fakeGetInput).calledWith('cake-version').mockReturnValue(''); when(fakeGetInput).calledWith('cake-bootstrap').mockReturnValue(''); when(fakeGetInput).calledWith('dry-run').mockReturnValue(''); @@ -26,12 +26,12 @@ describe('When getting the script-path input argument from the action', () => { }); }); -describe('When getting the csproj-path input argument from the action', () => { +describe('When getting the project-path input argument from the action', () => { const fakeGetInput = core.getInput as jest.MockedFunction; beforeAll(() => { when(fakeGetInput).calledWith('script-path').mockReturnValue(''); - when(fakeGetInput).calledWith('csproj-path').mockReturnValue('path/to/build.csproj'); + when(fakeGetInput).calledWith('project-path').mockReturnValue('path/to/build.csproj'); when(fakeGetInput).calledWith('cake-version').mockReturnValue(''); when(fakeGetInput).calledWith('cake-bootstrap').mockReturnValue(''); when(fakeGetInput).calledWith('dry-run').mockReturnValue(''); @@ -42,17 +42,17 @@ describe('When getting the csproj-path input argument from the action', () => { expect(action.getInputs().buildFile.type).toBe('project'); }); - test('it should return the argument for the csproj-path parameter as the build file path parameter', () => { + test('it should return the argument for the project-path parameter as the build file path parameter', () => { expect(action.getInputs().buildFile.path).toBe('path/to/build.csproj'); }); }); -describe('When getting both the script-path and csproj-path input arguments from the action', () => { +describe('When getting both the script-path and project-path input arguments from the action', () => { const fakeGetInput = core.getInput as jest.MockedFunction; beforeAll(() => { when(fakeGetInput).calledWith('script-path').mockReturnValue('path/to/build.cake'); - when(fakeGetInput).calledWith('csproj-path').mockReturnValue('path/to/build.csproj'); + when(fakeGetInput).calledWith('project-path').mockReturnValue('path/to/build.csproj'); when(fakeGetInput).calledWith('cake-version').mockReturnValue(''); when(fakeGetInput).calledWith('cake-bootstrap').mockReturnValue(''); when(fakeGetInput).calledWith('dry-run').mockReturnValue(''); @@ -63,7 +63,7 @@ describe('When getting both the script-path and csproj-path input arguments from expect(action.getInputs().buildFile.type).toBe('project'); }); - test('it should return the argument for the csproj-path parameter as the build file path parameter', () => { + test('it should return the argument for the project-path parameter as the build file path parameter', () => { expect(action.getInputs().buildFile.path).toBe('path/to/build.csproj'); }); }); @@ -221,7 +221,7 @@ describe('When getting no input arguments from the action', () => { beforeAll(() => { when(fakeGetInput).calledWith('script-path').mockReturnValue(''); - when(fakeGetInput).calledWith('csproj-path').mockReturnValue(''); + when(fakeGetInput).calledWith('project-path').mockReturnValue(''); when(fakeGetInput).calledWith('cake-version').mockReturnValue(''); when(fakeGetInput).calledWith('cake-bootstrap').mockReturnValue(''); when(fakeGetInput).calledWith('target').mockReturnValue(''); diff --git a/action.yml b/action.yml index ab30ddc..c3c0539 100644 --- a/action.yml +++ b/action.yml @@ -9,7 +9,7 @@ inputs: description: 'The path of the Cake script to run.' required: false default: 'build.cake' - csproj-path: + project-path: description: 'The path of the Cake Frosting Project to run. Takes precedence over script-path' required: false target: diff --git a/dist/index.js b/dist/index.js index d8fe56d..575b8d1 100644 --- a/dist/index.js +++ b/dist/index.js @@ -3999,7 +3999,7 @@ function getInputs() { exports.getInputs = getInputs; function getFileInput() { const scriptPath = core.getInput('script-path'); - const projectPath = core.getInput('csproj-path'); + const projectPath = core.getInput('project-path'); // When both script and project paths are specified, // the project path takes precedence. // If neither is provided, the default 'build.cake' script diff --git a/src/action.ts b/src/action.ts index 2152717..0f726d6 100644 --- a/src/action.ts +++ b/src/action.ts @@ -53,7 +53,7 @@ export function getInputs(): CakeInputs & BuildInputs { function getFileInput(): BuildFile { const scriptPath = core.getInput('script-path'); - const projectPath = core.getInput('csproj-path'); + const projectPath = core.getInput('project-path'); // When both script and project paths are specified, // the project path takes precedence. From 787f4b7f1bd3ad37f80e758cf46c24340ffbf802 Mon Sep 17 00:00:00 2001 From: Enrico Campidoglio Date: Tue, 2 Apr 2024 09:51:39 +0200 Subject: [PATCH 7/8] Adds integration tests for Cake Frosting --- .github/workflows/test.yml | 55 ++++++++++ integrationtests/frosting/Build.csproj | 10 ++ integrationtests/frosting/Program.cs | 139 +++++++++++++++++++++++++ 3 files changed, 204 insertions(+) create mode 100644 integrationtests/frosting/Build.csproj create mode 100644 integrationtests/frosting/Program.cs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 61328cd..6ffea84 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -153,3 +153,58 @@ jobs: string-parameter: 'value' numeric-parameter: 3 boolean-parameter: true + test-with-frosting: + name: Test with Cake Frosting + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [windows-latest, ubuntu-latest] + env: + project-directory: integrationtests/frosting + steps: + - name: Get the sources + uses: actions/checkout@v1 + - name: Install Node 20 + uses: actions/setup-node@v4 + with: + node-version: '20' + - name: Install the .NET 8 SDK + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + - name: Run a specific Cake Frosting project + uses: ./ + with: + project-path: ${{ env.project-directory}}/Build.csproj + - name: Run a specific target + uses: ./ + with: + project-path: ${{ env.project-directory}}/Build.csproj + target: Successful-Task + - name: Run with a specific verbosity level + uses: ./ + env: + EXPECTED_VERBOSITY: Diagnostic + with: + verbosity: Diagnostic + project-path: ${{ env.project-directory}}/Build.csproj + target: Test-Verbosity + - name: Do a dry run + uses: ./ + with: + dry-run: true + project-path: ${{ env.project-directory }}/Build.csproj + target: Test-Dry-Run + - name: Run with custom script parameters + uses: ./ + env: + EXPECTED_STRING_ARGUMENT: '''value''' + EXPECTED_NUMERIC_ARGUMENT: '3' + EXPECTED_BOOLEAN_ARGUMENT: 'true' + with: + project-path: ${{ env.project-directory }}/Build.csproj + target: Test-Script-Parameters + arguments: | + string-parameter: 'value' + numeric-parameter: 3 + boolean-parameter: true diff --git a/integrationtests/frosting/Build.csproj b/integrationtests/frosting/Build.csproj new file mode 100644 index 0000000..8f8fbc7 --- /dev/null +++ b/integrationtests/frosting/Build.csproj @@ -0,0 +1,10 @@ + + + Exe + net8.0 + $(MSBuildProjectDirectory) + + + + + diff --git a/integrationtests/frosting/Program.cs b/integrationtests/frosting/Program.cs new file mode 100644 index 0000000..6438d7c --- /dev/null +++ b/integrationtests/frosting/Program.cs @@ -0,0 +1,139 @@ +using System; +using Cake.Common; +using Cake.Core; +using Cake.Core.Diagnostics; +using Cake.Frosting; + +public static class Program +{ + public static int Main(string[] args) + { + return new CakeHost() + .UseContext() + .Run(args); + } +} + +public class BuildContext : FrostingContext +{ + public BuildContext(ICakeContext context) + : base(context) + { + StringParameter = context.Argument("String-Parameter", null); + NumericParameter = context.Argument("Numeric-Parameter", null); + BooleanParameter = context.Argument("Boolean-Parameter", null); + } + + public string StringParameter { get; } + + public int? NumericParameter { get; } + + public bool? BooleanParameter { get; } + +} + +[TaskName("Successful-Task")] +public sealed class SuccessfulTask : FrostingTask +{ + public override void Run(BuildContext context) + { + context.Log.Information("✓ Passed"); + } +} + +[TaskName("Test-Verbosity")] +public sealed class TestVerbosity : FrostingTask +{ + public override void Run(BuildContext context) + { + var hasExpectedVerbosity = Enum.TryParse( + context.EnvironmentVariable("EXPECTED_VERBOSITY"), + ignoreCase: true, + out Verbosity expectedVerbosity); + + if (!hasExpectedVerbosity) + { + throw new Exception( + "✕ The EXPECTED_VERBOSITY environment variable is not set or it doesn't contain a verbosity level"); + } + + var actualVerbosity = context.Log.Verbosity; + + if (expectedVerbosity != actualVerbosity) + { + throw new Exception($"✕ Expected verbosity {expectedVerbosity} but got {actualVerbosity}"); + } + + context.Log.Information("✓ Passed"); + } +} + +[TaskName("Test-Dry-Run")] +public sealed class TestDryRun : FrostingTask +{ + public override void Run(BuildContext context) + { + if (!context.IsDryRun()) + { + throw new Exception("✕ Expected this to be a dry run, but it isn't"); + } + + context.Log.Information("✓ Passed"); + } +} + +[TaskName("Test-Script-Parameters")] +public sealed class TestScriptParameters : FrostingTask +{ + public override void Run(BuildContext context) + { + var expectedStringArgument = context.EnvironmentVariable("EXPECTED_STRING_ARGUMENT"); + + var hasExpectedNumericArgument = int.TryParse( + context.EnvironmentVariable("EXPECTED_NUMERIC_ARGUMENT"), + out int expectedNumericArgument); + + var hasExpectedBooleanArgument = bool.TryParse( + context.EnvironmentVariable("EXPECTED_BOOLEAN_ARGUMENT"), + out bool expectedBooleanArgument); + + if (string.IsNullOrEmpty(expectedStringArgument)) + { + throw new Exception( + "✕ The EXPECTED_STRING_ARGUMENT environment variable is not set"); + } + + if (!hasExpectedNumericArgument) + { + throw new Exception( + "✕ The EXPECTED_NUMERIC_ARGUMENT environment variable is not set"); + } + + if (!hasExpectedBooleanArgument) + { + throw new Exception( + "✕ The EXPECTED_BOOLEAN_ARGUMENT environment variable is not set"); + } + + if (expectedStringArgument != context.StringParameter) + { + throw new Exception($"✕ Expected string argument {expectedStringArgument} but got {context.StringParameter}"); + } + + if (expectedNumericArgument != context.NumericParameter) + { + throw new Exception($"✕ Expected numeric argument {expectedNumericArgument} but got {context.NumericParameter}"); + } + + if (expectedBooleanArgument != context.BooleanParameter) + { + throw new Exception($"✕ Expected boolean argument {expectedBooleanArgument} but got {context.BooleanParameter}"); + } + + context.Log.Information("✓ Passed"); + } +} + +[TaskName("Default")] +[IsDependentOn(typeof(SuccessfulTask))] +public class DefaultTask : FrostingTask; From abbd3d6c6c8919bba8e4ca52892af0b8b2ffddf3 Mon Sep 17 00:00:00 2001 From: Enrico Campidoglio Date: Wed, 4 Dec 2024 13:54:27 +0100 Subject: [PATCH 8/8] Improves the documentation in the `README` file --- README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 327d8a6..2a6cb73 100644 --- a/README.md +++ b/README.md @@ -14,15 +14,15 @@ steps: uses: cake-build/cake-action@v2 ``` -The Cake action will look for a script named `build.cake` in your repository's root directory and run it for you using the [Cake Tool](https://www.nuget.org/packages/Cake.Tool/). If you are using a [Cake Frosting](https://cakebuild.net/docs/running-builds/runners/cake-frosting) project, the `project-path` parameter must be specified and the `script-path` is ignored. +The Cake action will look for a script named `build.cake` in your repository's root directory and run it for you using the [Cake Tool](https://www.nuget.org/packages/Cake.Tool/). You can also specify the path to your Cake script using the `script-path` option. If you are using a [Cake Frosting](https://cakebuild.net/docs/running-builds/runners/cake-frosting) project instead, you must specify the path to your `.csproj` file with the `project-path` parameter. -All output from the Cake script or Cake Frosting project will be automatically redirected to the build log for inspection. +All output from the Cake script or Cake Frosting project is automatically redirected to the build log for inspection. ## Inputs ### `script-path` -If your script is in another location, you can specify the path with the `script-path` [input parameter](https://help.github.com/en/github/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idstepswith): +If your Cake script is located somewhere other than the root directory of your project, you can specify its path using the `script-path` [input parameter](https://help.github.com/en/github/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idstepswith): ```yml steps: @@ -34,7 +34,7 @@ steps: ### `project-path` -If you are using [Cake Frosting](https://cakebuild.net/docs/running-builds/runners/cake-frosting), you can specify the path with the `project-path` [input parameter](https://help.github.com/en/github/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idstepswith): +If you are using [Cake Frosting](https://cakebuild.net/docs/running-builds/runners/cake-frosting), you must specify the path to the project file with the `project-path` [input parameter](https://help.github.com/en/github/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idstepswith): ```yml steps: @@ -102,11 +102,9 @@ The arguments are defined in a [multi-line string literal](https://yaml.org/spec ### `cake-version` -By default, the Cake action will run your script using the latest _stable_ version of the [Cake .NET Core Global tool](https://www.nuget.org/packages/Cake.Tool/). +By default, the Cake action runs your script using the latest _stable_ version of the [Cake .NET Core Global tool](https://www.nuget.org/packages/Cake.Tool/). -_Ignored_ for Cake Frosting Project. - -If for some reason you want to [use a specific version of Cake](https://cakebuild.net/docs/tutorials/pinning-cake-version) (for compatibility with older third-party addins, for example), you can do so by specifying the version number in the `cake-version` parameter.: +If you need to [use a specific version of Cake](https://cakebuild.net/docs/tutorials/pinning-cake-version) (e.g. for compatibility with older addins), you can specify it with the `cake-version` parameter: ```yml steps: @@ -116,7 +114,7 @@ steps: cake-version: 0.30.0 ``` -If you're pinning your Cake version using a [tool manifest file](https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools#install-a-local-tool), then you can have the action restore any local tools, including Cake, by specifying `tool-manifest` as the argument for `cake-version`: +If you're pinning your Cake version using a [tool manifest file](https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools#install-a-local-tool), you can have the action restore any local tools, including Cake, by specifying `tool-manifest` as the argument for `cake-version`: ```yml steps: @@ -126,13 +124,13 @@ steps: cake-version: tool-manifest ``` +> :information_source: This option is ignored when [using Cake Frosting projects](#project-path). + ### `cake-bootstrap` As of [Cake 1.0.0](https://github.com/cake-build/cake/releases/tag/v1.0.0), any [custom modules](https://cakebuild.net/docs/fundamentals/modules) that you reference in your script are [bootstrapped automatically](https://github.com/cake-build/cake/issues/2833) upon running it. -_Ignored_ for Cake Frosting Project. - -If you're using an older version of Cake, however, you need to explicitly [bootstrap](https://cakebuild.net/docs/fundamentals/preprocessor-directives#module-directive) them before running the script. The Cake action can take care of this extra step for you by setting the `cake-bootstrap` parameter to `explicit`: +For older versions of Cake, you need to explicitly [bootstrap](https://cakebuild.net/docs/fundamentals/preprocessor-directives#module-directive) any referenced modules before running the script. The Cake action can handle this extra step for you by setting the `cake-bootstrap` parameter to `explicit`: ```yml steps: @@ -143,7 +141,7 @@ steps: cake-version: 0.38.5 ``` -If you're using Cake 1.0.0 or later and wish to opt out of the automatic bootstrapping of modules, you can do so by setting the `cake-bootstrap` parameter to `skip`: +If you're using Cake 1.0.0 or later and wish to opt out of the automatic bootstrapping of modules, you can set the `cake-bootstrap` parameter to `skip`: ```yml steps: @@ -153,7 +151,9 @@ steps: cake-bootstrap: skip ``` -The default value is `auto`, which means that the modules will be automatically bootstrapped on Cake 1.0.0 or later. +The default value is `auto`, which means modules will be automatically bootstrapped on Cake 1.0.0 or later. + +> :information_source: This option is ignored when [using Cake Frosting projects](#script-path). ## Cross-platform