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

Gets the latest version of Cake from GitHub #56

Merged
merged 3 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
18 changes: 18 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,24 @@ jobs:
cake-version: tool-manifest
script-path: ${{ env.script-directory }}/build.cake
target: Test-Cake-Version
- name: Get the latest Cake release from GitHub
id: get-latest-cake-release
uses: octokit/request-action@v2.x
with:
route: GET /repos/cake-build/cake/releases/latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Set the EXPECTED_CAKE_VERSION environment variable
shell: bash
run: |
version=$(echo ${{ fromJson(steps.get-latest-cake-release.outputs.data).tag_name }} | sed 's/v//')
echo "EXPECTED_CAKE_VERSION=$version" >> $GITHUB_ENV
- name: Run with the latest Cake version
uses: ./
with:
cake-version: latest
script-path: ${{ env.script-directory }}/build.cake
target: Test-Cake-Version
- name: Run automatic bootstrapping of Cake modules (Cake >= 1.0.0)
uses: ./
with:
Expand Down
112 changes: 112 additions & 0 deletions __tests__/cakeRelease.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import * as http from '@actions/http-client';
import * as cakeRelease from '../src/cakeRelease';

describe('When retrieving the latest Cake version', () => {
beforeAll(async () => {
jest
.spyOn(http.HttpClient.prototype, 'getJson')
.mockImplementation(async () => ({
statusCode: 200,
result: {
tag_name: 'v1.0.0'
},
headers: {}
}));
});

test('it should return the latest version number from GitHub', async () => {
expect(await cakeRelease.getLatestVersion()).toBe('1.0.0');
});
});

describe('When retrieving the latest Cake version without the \'v\' prefix', () => {
beforeAll(async () => {
jest
.spyOn(http.HttpClient.prototype, 'getJson')
.mockImplementation(async () => ({
statusCode: 200,
result: {
tag_name: '1.0.0'
},
headers: {}
}));
});

test('it should return the latest version number from GitHub', async () => {
expect(await cakeRelease.getLatestVersion()).toBe('1.0.0');
});
});

describe('When failing to retrieve the latest Cake version due to a GitHub error', () => {
beforeAll(async () => {
jest
.spyOn(http.HttpClient.prototype, 'getJson')
.mockImplementation(async () => ({
statusCode: 500,
result: {},
headers: {}
}));
});

test('it should return null', async () => {
expect(await cakeRelease.getLatestVersion()).toBeNull();
});

test('it should log the fact that the GitHub API returned an error', async () => {
const log = jest.spyOn(console, 'log');
await cakeRelease.getLatestVersion();
expect(log).toHaveBeenCalledWith('Could not determine the latest version of Cake. GitHub returned status code 500');
});
});

describe('When failing to retrieve the latest Cake version due to an empty response from GitHub', () => {
beforeAll(async () => {
jest
.spyOn(http.HttpClient.prototype, 'getJson')
.mockImplementation(async () => ({
statusCode: 200,
result: {},
headers: {}
}));
});

test('it should return null', async () => {
expect(await cakeRelease.getLatestVersion()).toBeNull();
});
});

describe('When failing to retrieve the latest Cake version due to a missing tag name in the GitHub response', () => {
beforeAll(async () => {
jest
.spyOn(http.HttpClient.prototype, 'getJson')
.mockImplementation(async () => ({
statusCode: 200,
result: {
tag_name: null
},
headers: {}
}));
});

test('it should return null', async () => {
expect(await cakeRelease.getLatestVersion()).toBeNull();
});
});

describe('When failing to retrieve the latest Cake version due to an empty tag name in the GitHub response', () => {
beforeAll(async () => {
jest
.spyOn(http.HttpClient.prototype, 'getJson')
.mockImplementation(async () => ({
statusCode: 200,
result: {
tag_name: ''
},
headers: {}
}));
});

test('it should return null', async () => {
expect(await cakeRelease.getLatestVersion()).toBeNull();
});
});
50 changes: 45 additions & 5 deletions __tests__/cakeTool.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import * as path from 'path';
import * as dotnet from '../src/dotnet';
import * as cakeTool from '../src/cakeTool';
import * as cakeRelease from '../src/cakeRelease';
import { ToolsDirectory } from '../src/toolsDirectory';

const targetDirectory = path.join('target', 'directory');

jest.mock('../src/dotnet');
jest.mock('../src/cakeRelease');

describe('When installing the Cake Tool based on the tool manifest', () => {
const fakeRestoreTool = dotnet.restoreLocalTools as jest.MockedFunction<typeof dotnet.restoreLocalTools>;
Expand All @@ -26,14 +28,19 @@ describe('When installing the Cake Tool based on the tool manifest', () => {

describe('When installing the Cake Tool without a version number', () => {
const fakeInstallLocalTool = dotnet.installLocalTool as jest.MockedFunction<typeof dotnet.installLocalTool>;
const fakeGetLatestVersion = cakeRelease.getLatestVersion as jest.MockedFunction<typeof cakeRelease.getLatestVersion>;

beforeAll(() => {
fakeGetLatestVersion.mockResolvedValue('theLatestVersion');
});

test('it should install the latest version of the Cake.Tool in the tools directory', async () => {
await cakeTool.install();
expect(fakeInstallLocalTool).toHaveBeenCalledWith(
'Cake.Tool',
'dotnet-cake',
new ToolsDirectory(),
undefined);
'theLatestVersion');
});

test('it should install the latest version of the Cake.Tool in the specified target directory', async () => {
Expand All @@ -43,21 +50,25 @@ describe('When installing the Cake Tool without a version number', () => {
'Cake.Tool',
'dotnet-cake',
targetDir,
undefined);
'theLatestVersion');
});

});

describe('When installing the latest version of the Cake Tool', () => {
const fakeInstallLocalTool = dotnet.installLocalTool as jest.MockedFunction<typeof dotnet.installLocalTool>;
const fakeGetLatestVersion = cakeRelease.getLatestVersion as jest.MockedFunction<typeof cakeRelease.getLatestVersion>;

beforeAll(() => {
fakeGetLatestVersion.mockResolvedValue('theLatestVersion');
});

test('it should install the latest version of the Cake.Tool in the tools directory', async () => {
await cakeTool.install(undefined, { version: 'latest' });
expect(fakeInstallLocalTool).toHaveBeenCalledWith(
'Cake.Tool',
'dotnet-cake',
new ToolsDirectory(),
undefined);
'theLatestVersion');
});

test('it should install the latest version of the Cake.Tool in the specified target directory', async () => {
Expand All @@ -67,7 +78,7 @@ describe('When installing the latest version of the Cake Tool', () => {
'Cake.Tool',
'dotnet-cake',
targetDir,
undefined);
'theLatestVersion');
});
});

Expand Down Expand Up @@ -147,3 +158,32 @@ describe('When failing to install a specific version of the Cake Tool', () => {
.toThrow(installError);
});
});

describe('When failing to retrieve the latest version of the Cake Tool', () => {
const fakeInstallLocalTool = dotnet.installLocalTool as jest.MockedFunction<typeof dotnet.installLocalTool>;
const fakeGetLatestVersion = cakeRelease.getLatestVersion as jest.MockedFunction<typeof cakeRelease.getLatestVersion>;

beforeAll(() => {
fakeInstallLocalTool.mockReset();
fakeGetLatestVersion.mockResolvedValue(null);
});

test('it should install the Cake.Tool without a version number in the tools directory', async () => {
await cakeTool.install();
expect(fakeInstallLocalTool).toHaveBeenCalledWith(
'Cake.Tool',
'dotnet-cake',
new ToolsDirectory(),
undefined);
});

test('it should install the Cake.Tool without a version number in the specified target directory', async () => {
const targetDir = new ToolsDirectory(targetDirectory);
await cakeTool.install(targetDir);
expect(fakeInstallLocalTool).toHaveBeenCalledWith(
'Cake.Tool',
'dotnet-cake',
targetDir,
undefined);
});
});
70 changes: 69 additions & 1 deletion dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4148,6 +4148,72 @@ class CakeSwitch {
exports.CakeSwitch = CakeSwitch;


/***/ }),

/***/ 3040:
/***/ (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.getLatestVersion = void 0;
const http = __importStar(__nccwpck_require__(6255));
function getLatestVersion() {
return __awaiter(this, void 0, void 0, function* () {
const release = yield getLatestCakeReleaseFromGitHub();
return extractVersionNumber(release);
});
}
exports.getLatestVersion = getLatestVersion;
function getLatestCakeReleaseFromGitHub() {
return __awaiter(this, void 0, void 0, function* () {
const client = new http.HttpClient('cake-build/cake-action');
const response = yield client.getJson('https://api.github.com/repos/cake-build/cake/releases/latest');
if (response.statusCode != 200) {
console.log(`Could not determine the latest version of Cake. GitHub returned status code ${response.statusCode}`);
return null;
}
return response.result;
});
}
function extractVersionNumber(release) {
var _a;
return ((_a = release === null || release === void 0 ? void 0 : release.tag_name) === null || _a === void 0 ? void 0 : _a.replace(/^v/, '')) || null;
}


/***/ }),

/***/ 4574:
Expand Down Expand Up @@ -4191,15 +4257,17 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.install = void 0;
const dotnet = __importStar(__nccwpck_require__(9870));
const toolsDirectory_1 = __nccwpck_require__(6745);
const cakeRelease_1 = __nccwpck_require__(3040);
function install(toolsDir, version) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
switch (version === null || version === void 0 ? void 0 : version.version) {
case 'tool-manifest':
yield dotnet.restoreLocalTools();
break;
case 'latest':
case undefined:
yield installCakeLocalTool(toolsDir);
yield installCakeLocalTool(toolsDir, (_a = yield (0, cakeRelease_1.getLatestVersion)()) !== null && _a !== void 0 ? _a : undefined);
break;
case 'specific':
yield installCakeLocalTool(toolsDir, version.number);
Expand Down
1 change: 1 addition & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"dependencies": {
"@actions/core": "^1.9.1",
"@actions/exec": "^1.0.1",
"@actions/http-client": "^2.0.1",
"@actions/io": "^1.0.1"
},
"devDependencies": {
Expand Down
26 changes: 26 additions & 0 deletions src/cakeRelease.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as http from '@actions/http-client';

export async function getLatestVersion(): Promise<string | null> {
const release = await getLatestCakeReleaseFromGitHub();
return extractVersionNumber(release);
}

async function getLatestCakeReleaseFromGitHub(): Promise<GitHubRelease | null> {
const client = new http.HttpClient('cake-build/cake-action');
const response = await client.getJson<GitHubRelease>('https://api.github.com/repos/cake-build/cake/releases/latest');

if (response.statusCode != 200) {
console.log(`Could not determine the latest version of Cake. GitHub returned status code ${response.statusCode}`);
return null;
}

return response.result;
}

function extractVersionNumber(release: GitHubRelease | null): string | null {
return release?.tag_name?.replace(/^v/, '') || null;
}

interface GitHubRelease {
tag_name: string;
}
3 changes: 2 additions & 1 deletion src/cakeTool.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as dotnet from './dotnet';
import { ToolsDirectory } from './toolsDirectory';
import { CakeVersion } from './action';
import { getLatestVersion } from './cakeRelease';

export async function install(toolsDir?: ToolsDirectory, version?: CakeVersion) {
switch (version?.version) {
Expand All @@ -9,7 +10,7 @@ export async function install(toolsDir?: ToolsDirectory, version?: CakeVersion)
break;
case 'latest':
case undefined:
await installCakeLocalTool(toolsDir);
await installCakeLocalTool(toolsDir, await getLatestVersion() ?? undefined);
break;
case 'specific':
await installCakeLocalTool(toolsDir, version.number);
Expand Down
Loading