diff --git a/__tests__/tools.test.ts b/__tests__/tools.test.ts index 36468ccc..8378d3db 100644 --- a/__tests__/tools.test.ts +++ b/__tests__/tools.test.ts @@ -32,15 +32,29 @@ function getData(data: IData): Record { jest .spyOn(utils, 'fetch') - .mockImplementation(async (url: string): Promise => { - return `[{"ref": "refs/tags/1.2.3", "url": "${url}"}]`; + .mockImplementation(async (url: string, token?: string): Promise< + Record + > => { + if (!token || token === 'valid_token') { + return {data: `[{"ref": "refs/tags/1.2.3", "url": "${url}"}]`}; + } else if (token === 'no_data') { + return {data: '[]'}; + } else { + return {error: 'Invalid token'}; + } }); describe('Tools tests', () => { - it('checking getToolSemver', async () => { + it.each` + token | version + ${'invalid_token'} | ${'1.2'} + ${'valid_token'} | ${'1.2.3'} + ${''} | ${'1.2.3'} + `('checking getToolSemver: $token', async ({token, version}) => { + process.env['COMPOSER_TOKEN'] = token; expect( - await tools.getToolSemver(getData({tool: 'tool', version: 'latest'})) - ).toBe('1.2.3'); + await tools.getToolSemver(getData({tool: 'tool', version: '1.2'})) + ).toBe(version); }); it.each` @@ -90,49 +104,22 @@ describe('Tools tests', () => { }); }); - it.each([ - [ - ['a', 'b'], - ['composer', 'a', 'b'] - ], - [ - ['a', 'b', 'composer'], - ['composer', 'a', 'b'] - ], - [ - ['a', 'b', 'composer:1.2.3'], - ['composer:1.2.3', 'a', 'b'] - ], - [ - ['a', 'b', 'composer:v1.2.3'], - ['composer:1.2.3', 'a', 'b'] - ], - [ - ['a', 'b', 'composer:snapshot'], - ['composer:snapshot', 'a', 'b'] - ], - [ - ['a', 'b', 'composer:preview'], - ['composer:preview', 'a', 'b'] - ], - [ - ['a', 'b', 'composer:1'], - ['composer:1', 'a', 'b'] - ], - [ - ['a', 'b', 'composer:2'], - ['composer:2', 'a', 'b'] - ], - [ - ['a', 'b', 'composer:v1'], - ['composer:1', 'a', 'b'] - ], - [ - ['a', 'b', 'composer:v2'], - ['composer:2', 'a', 'b'] - ] - ])('checking filterList', async (input_list, filtered_list) => { - expect(await tools.filterList(input_list)).toStrictEqual(filtered_list); + it.each` + input_list | filtered_list + ${'a, b'} | ${'composer, a, b'} + ${'a, b, composer'} | ${'composer, a, b'} + ${'a, b, composer:1.2.3'} | ${'composer:1.2.3, a, b'} + ${'a, b, composer:v1.2.3'} | ${'composer:1.2.3, a, b'} + ${'a, b, composer:snapshot'} | ${'composer:snapshot, a, b'} + ${'a, b, composer:preview'} | ${'composer:preview, a, b'} + ${'a, b, composer:1'} | ${'composer:1, a, b'} + ${'a, b, composer:2'} | ${'composer:2, a, b'} + ${'a, b, composer:v1'} | ${'composer:1, a, b'} + ${'a, b, composer:v2'} | ${'composer:2, a, b'} + `('checking filterList $input_list', async ({input_list, filtered_list}) => { + expect(await tools.filterList(input_list.split(', '))).toStrictEqual( + filtered_list.split(', ') + ); }); it.each` @@ -174,7 +161,7 @@ describe('Tools tests', () => { ${'darwin'} | ${'add_tool https://example.com/tool.phar tool "-v"'} ${'win32'} | ${'Add-Tool https://example.com/tool.phar tool "-v"'} ${'openbsd'} | ${'Platform openbsd is not supported'} - `('checking addPackage: $tool, $os_version', async ({os_version, script}) => { + `('checking addPackage: $os_version', async ({os_version, script}) => { const data = getData({ tool: 'tool', version: 'latest', @@ -474,4 +461,14 @@ describe('Tools tests', () => { `('checking composer setup: $tools_csv', async ({tools_csv, script}) => { expect(await tools.addTools(tools_csv, '7.4', 'linux')).toContain(script); }); + + it.each` + tools_csv | token | script + ${'cs2pr:1.2'} | ${'invalid_token'} | ${'add_log "$cross" "cs2pr" "Invalid token"'} + ${'phpunit:1.2'} | ${'invalid_token'} | ${'add_log "$cross" "phpunit" "Invalid token"'} + ${'phpunit:0.1'} | ${'no_data'} | ${'add_log "$cross" "phpunit" "No version found with prefix 0.1."'} + `('checking error: $tools_csv', async ({tools_csv, token, script}) => { + process.env['COMPOSER_TOKEN'] = token; + expect(await tools.addTools(tools_csv, '7.4', 'linux')).toContain(script); + }); }); diff --git a/__tests__/utils.test.ts b/__tests__/utils.test.ts index d218a79b..7d572364 100644 --- a/__tests__/utils.test.ts +++ b/__tests__/utils.test.ts @@ -8,10 +8,6 @@ jest.mock('@actions/core', () => ({ }) })); -jest.spyOn(utils, 'fetch').mockImplementation(async (url): Promise => { - return `{ "latest": "8.0", "5.x": "5.6", "url": "${url}" }`; -}); - async function cleanup(path: string): Promise { fs.unlink(path, error => { if (error) { @@ -38,16 +34,29 @@ describe('Utils tests', () => { }); it('checking fetch', async () => { - expect(await utils.fetch('test_url')).toBe( - '{ "latest": "8.0", "5.x": "5.6", "url": "test_url" }' - ); - process.env['COMPOSER_TOKEN'] = 'GITHUB_TOKEN'; - expect(await utils.fetch('test_url')).toBe( - '{ "latest": "8.0", "5.x": "5.6", "url": "test_url" }' - ); + const manifest = await utils.getManifestURL(); + let response: Record = await utils.fetch(manifest); + expect(response.error).toBe(undefined); + expect(response.data).toContain('latest'); + + response = await utils.fetch(manifest, 'invalid_token'); + expect(response.error).toBe('404: Not Found'); + }); + + it('checking getManifestURL', async () => { + expect(await utils.getManifestURL()).toContain('php-versions.json'); }); it('checking parseVersion', async () => { + jest.spyOn(utils, 'fetch').mockImplementation(async (url, token?): Promise< + Record + > => { + if (!token || token === 'valid_token') { + return {data: `{ "latest": "8.0", "5.x": "5.6", "url": "${url}" }`}; + } else { + return {error: 'Invalid token'}; + } + }); expect(await utils.parseVersion('latest')).toBe('8.0'); expect(await utils.parseVersion('7')).toBe('7.0'); expect(await utils.parseVersion('7.4')).toBe('7.4'); diff --git a/dist/index.js b/dist/index.js index 08923045..f337754b 100644 --- a/dist/index.js +++ b/dist/index.js @@ -495,10 +495,18 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.addTools = exports.initToolData = exports.functionRecord = exports.addWPCLI = exports.addSymfony = exports.addPHPUnitTools = exports.addPhive = exports.addPhing = exports.addPECL = exports.addDevTools = exports.addDeployer = exports.addComposer = exports.addBlackfirePlayer = exports.addPackage = exports.addArchive = exports.getPharUrl = exports.getUrl = exports.filterList = exports.parseRelease = exports.getToolVersion = exports.getToolSemver = void 0; const utils = __importStar(__nccwpck_require__(918)); async function getToolSemver(data) { - const api_url = `https://api.github.com/repos/${data['repository']}/git/matching-refs/tags%2F${data['version_prefix']}${data['version']}`; - return JSON.parse(await utils.fetch(api_url)) - .pop()['ref'].split('/') - .pop(); + var _a; + const ref = data['version_prefix'] + data['version']; + const url = `https://api.github.com/repos/${data['repository']}/git/matching-refs/tags%2F${ref}.`; + const token = await utils.readEnv('COMPOSER_TOKEN'); + const response = await utils.fetch(url, token); + if (response.error || response.data === '[]') { + data['error'] = (_a = response.error) !== null && _a !== void 0 ? _a : `No version found with prefix ${ref}.`; + return data['version']; + } + else { + return JSON.parse(response['data']).pop()['ref'].split('/').pop(); + } } exports.getToolSemver = getToolSemver; async function getToolVersion(data) { @@ -788,6 +796,9 @@ async function addTools(tools_csv, php_version, os_version) { const data = await initToolData(await utils.getToolData(release.split(':')[0]), release, php_version, os_version); script += '\n'; switch (true) { + case data['error'] !== undefined: + script += await utils.addLog('$cross', data['tool'], data['error'], data['os_version']); + break; case 'phar' === data['type']: data['url'] = await getUrl(data); script += await addArchive(data); @@ -805,7 +816,7 @@ async function addTools(tools_csv, php_version, os_version) { case /^none$/.test(data['tool']): break; default: - script += await utils.addLog('$cross', data['tool'], 'Tool ' + data['tool'] + ' is not supported', os_version); + script += await utils.addLog('$cross', data['tool'], 'Tool ' + data['tool'] + ' is not supported', data['os_version']); break; } }); @@ -840,7 +851,7 @@ var __importStar = (this && this.__importStar) || function (mod) { return result; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.parseExtensionSource = exports.customPackage = exports.scriptTool = exports.scriptExtension = exports.joins = exports.getCommand = exports.getUnsupportedLog = exports.suppressOutput = exports.getExtensionPrefix = exports.CSVArray = exports.extensionArray = exports.getToolData = exports.writeScript = exports.readFile = exports.addLog = exports.stepLog = exports.log = exports.color = exports.asyncForEach = exports.parseVersion = exports.fetch = exports.getInput = exports.readEnv = void 0; +exports.parseExtensionSource = exports.customPackage = exports.scriptTool = exports.scriptExtension = exports.joins = exports.getCommand = exports.getUnsupportedLog = exports.suppressOutput = exports.getExtensionPrefix = exports.CSVArray = exports.extensionArray = exports.getToolData = exports.writeScript = exports.readFile = exports.addLog = exports.stepLog = exports.log = exports.color = exports.asyncForEach = exports.parseVersion = exports.getManifestURL = exports.fetch = exports.getInput = exports.readEnv = void 0; const fs = __importStar(__nccwpck_require__(747)); const https = __importStar(__nccwpck_require__(211)); const path = __importStar(__nccwpck_require__(622)); @@ -871,35 +882,45 @@ async function getInput(name, mandatory) { } } exports.getInput = getInput; -async function fetch(input_url) { +async function fetch(input_url, auth_token) { const fetch_promise = new Promise(resolve => { const url_object = new url.URL(input_url); - const auth_token = process.env['COMPOSER_TOKEN'] || ''; - const auth_header = auth_token ? 'Bearer' + auth_token : ''; + const headers = { + 'User-Agent': `Mozilla/5.0 (${process.platform} ${process.arch}) setup-php` + }; + if (auth_token) { + headers.authorization = 'Bearer ' + auth_token; + } const options = { hostname: url_object.hostname, path: url_object.pathname, - headers: { - authorization: auth_header, - 'User-Agent': 'setup-php' - } + headers: headers }; const req = https.get(options, (res) => { - res.setEncoding('utf8'); - let body = ''; - res.on('data', chunk => (body += chunk)); - res.on('end', () => resolve(body)); + if (res.statusCode != 200) { + resolve({ error: `${res.statusCode}: ${res.statusMessage}` }); + } + else { + let body = ''; + res.setEncoding('utf8'); + res.on('data', chunk => (body += chunk)); + res.on('end', () => resolve({ data: `${body}` })); + } }); req.end(); }); return await fetch_promise; } exports.fetch = fetch; +async function getManifestURL() { + return 'https://raw.githubusercontent.com/shivammathur/setup-php/develop/src/configs/php-versions.json'; +} +exports.getManifestURL = getManifestURL; async function parseVersion(version) { - const manifest = 'https://raw.githubusercontent.com/shivammathur/setup-php/develop/src/configs/php-versions.json'; + const manifest = await getManifestURL(); switch (true) { case /^(latest|\d+\.x)$/.test(version): - return JSON.parse(await fetch(manifest))[version]; + return JSON.parse((await fetch(manifest))['data'])[version]; default: switch (true) { case version.length > 1: diff --git a/src/tools.ts b/src/tools.ts index 1043c66b..322876f8 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -3,11 +3,16 @@ import * as utils from './utils'; export async function getToolSemver( data: Record ): Promise { - const api_url = `https://api.github.com/repos/${data['repository']}/git/matching-refs/tags%2F${data['version_prefix']}${data['version']}`; - return JSON.parse(await utils.fetch(api_url)) - .pop() - ['ref'].split('/') - .pop(); + const ref: string = data['version_prefix'] + data['version']; + const url = `https://api.github.com/repos/${data['repository']}/git/matching-refs/tags%2F${ref}.`; + const token: string = await utils.readEnv('COMPOSER_TOKEN'); + const response: Record = await utils.fetch(url, token); + if (response.error || response.data === '[]') { + data['error'] = response.error ?? `No version found with prefix ${ref}.`; + return data['version']; + } else { + return JSON.parse(response['data']).pop()['ref'].split('/').pop(); + } } /** @@ -463,6 +468,14 @@ export async function addTools( ); script += '\n'; switch (true) { + case data['error'] !== undefined: + script += await utils.addLog( + '$cross', + data['tool'], + data['error'], + data['os_version'] + ); + break; case 'phar' === data['type']: data['url'] = await getUrl(data); script += await addArchive(data); @@ -489,7 +502,7 @@ export async function addTools( '$cross', data['tool'], 'Tool ' + data['tool'] + ' is not supported', - os_version + data['os_version'] ); break; } diff --git a/src/utils.ts b/src/utils.ts index 56739fa1..771d97fd 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,4 @@ -import {IncomingMessage} from 'http'; +import {IncomingMessage, OutgoingHttpHeaders} from 'http'; import * as fs from 'fs'; import * as https from 'https'; import * as path from 'path'; @@ -48,42 +48,59 @@ export async function getInput( * Function to fetch an URL * * @param input_url + * @param auth_token */ -export async function fetch(input_url: string): Promise { - const fetch_promise: Promise = new Promise(resolve => { - const url_object: url.UrlObject = new url.URL(input_url); - const auth_token: string = process.env['COMPOSER_TOKEN'] || ''; - const auth_header: string = auth_token ? 'Bearer' + auth_token : ''; - const options: https.RequestOptions = { - hostname: url_object.hostname, - path: url_object.pathname, - headers: { - authorization: auth_header, - 'User-Agent': 'setup-php' +export async function fetch( + input_url: string, + auth_token?: string +): Promise> { + const fetch_promise: Promise> = new Promise( + resolve => { + const url_object: url.UrlObject = new url.URL(input_url); + const headers: OutgoingHttpHeaders = { + 'User-Agent': `Mozilla/5.0 (${process.platform} ${process.arch}) setup-php` + }; + if (auth_token) { + headers.authorization = 'Bearer ' + auth_token; } - }; - const req = https.get(options, (res: IncomingMessage) => { - res.setEncoding('utf8'); - let body = ''; - res.on('data', chunk => (body += chunk)); - res.on('end', () => resolve(body)); - }); - req.end(); - }); + const options: https.RequestOptions = { + hostname: url_object.hostname, + path: url_object.pathname, + headers: headers + }; + const req = https.get(options, (res: IncomingMessage) => { + if (res.statusCode != 200) { + resolve({error: `${res.statusCode}: ${res.statusMessage}`}); + } else { + let body = ''; + res.setEncoding('utf8'); + res.on('data', chunk => (body += chunk)); + res.on('end', () => resolve({data: `${body}`})); + } + }); + req.end(); + } + ); return await fetch_promise; } +/** Function to get manifest URL + * + */ +export async function getManifestURL(): Promise { + return 'https://raw.githubusercontent.com/shivammathur/setup-php/develop/src/configs/php-versions.json'; +} + /** * Function to parse PHP version. * * @param version */ export async function parseVersion(version: string): Promise { - const manifest = - 'https://raw.githubusercontent.com/shivammathur/setup-php/develop/src/configs/php-versions.json'; + const manifest = await getManifestURL(); switch (true) { case /^(latest|\d+\.x)$/.test(version): - return JSON.parse(await fetch(manifest))[version]; + return JSON.parse((await fetch(manifest))['data'])[version]; default: switch (true) { case version.length > 1: diff --git a/tsconfig.json b/tsconfig.json index 995f1479..4e6bddea 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "esModuleInterop": true, "lib": [ - "ESNext" + "ES2019" ], "module": "commonjs", "moduleResolution": "node", @@ -12,7 +12,7 @@ "rootDir": "./src", "sourceMap": true, "strict": true, - "target": "ESNext" + "target": "ES2019" }, "exclude": ["__tests__", "lib", "node_modules"] } \ No newline at end of file