From 3a55fb8d46847eb88614c1153770fe3c904c2e81 Mon Sep 17 00:00:00 2001 From: Ilshat Sultanov Date: Thu, 16 Jun 2022 16:57:17 +0500 Subject: [PATCH] Add cljstyle support --- .github/workflows/smoke-tests.yml | 32 +++++++ README.md | 13 +++ __tests__/cljstyle.test.ts | 151 ++++++++++++++++++++++++++++++ __tests__/entrypoint.test.ts | 25 +++++ action.yml | 2 + src/cljstyle.ts | 66 +++++++++++++ src/entrypoint.ts | 9 ++ 7 files changed, 298 insertions(+) create mode 100644 __tests__/cljstyle.test.ts create mode 100644 src/cljstyle.ts diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index a18237b..c3f3f3b 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -179,6 +179,34 @@ jobs: - name: Check clj-kondo version run: clj-kondo --version + test-cljstyle: + + strategy: + matrix: + operating-system: [ubuntu-latest, macOS-latest] + + runs-on: ${{ matrix.operating-system }} + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Prepare java + uses: actions/setup-java@v2 + with: + distribution: 'zulu' + java-version: '8' + + - name: Install cljstyle + # uses: DeLaGuardo/setup-clojure@master + uses: ./ + with: + cljstyle: latest + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Check cljstyle version + run: cljstyle version + all-together: runs-on: ubuntu-latest @@ -202,6 +230,7 @@ jobs: boot: latest bb: latest clj-kondo: latest + cljstyle: latest github-token: ${{ secrets.GITHUB_TOKEN }} - name: Check Clojure CLI @@ -218,3 +247,6 @@ jobs: - name: Check clj-kondo version run: clj-kondo --version + + - name: Check cljstyle version + run: cljstyle version diff --git a/README.md b/README.md index 00881da..2c39ad8 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ This action sets up Clojure tools environment for using in GitHub Actions. * boot-clj * Babashka * Clj-kondo +* cljstyle All three major tools available for MacOS and ubuntu based runners, Leiningen and Clojure CLI also available on Windows @@ -50,6 +51,7 @@ jobs: boot: 2.8.3 # Boot.clj bb: 0.7.8 # Babashka clj-kondo: 2022.05.31 # Clj-kondo + cljstyle: 0.15.0 # cljstyle # (optional) To avoid rate limit errors please provide github token github-token: ${{ secrets.GITHUB_TOKEN }} @@ -71,6 +73,17 @@ jobs: # Boot is not yet available for windows if: ${{ matrix.os != 'windows-latest' }} run: boot -V + + - name: Get babashka version + run: bb --version + + - name: Get clj-kondo version + run: clj-kondo --version + + - name: Get cljstyle version + # cljstyle is not yet available for windows + if: ${{ matrix.os != 'windows-latest' }} + run: cljstyle version ``` For more application cases please check [Smoke Test Workflow file](https://github.com/DeLaGuardo/setup-clojure/blob/master/.github/workflows/smoke-tests.yml) diff --git a/__tests__/cljstyle.test.ts b/__tests__/cljstyle.test.ts new file mode 100644 index 0000000..daff544 --- /dev/null +++ b/__tests__/cljstyle.test.ts @@ -0,0 +1,151 @@ +import _os from 'os' +import * as _core from '@actions/core' +import * as _tc from '@actions/tool-cache' +import * as cljstyle from '../src/cljstyle' + +const getJson = jest.fn() +jest.mock('@actions/http-client', () => ({ + HttpClient: function () { + return {getJson} + } +})) + +jest.mock('os') +const os: jest.Mocked = _os as never + +jest.mock('@actions/tool-cache') +const tc: jest.Mocked = _tc as never + +jest.mock('@actions/core') +const core: jest.Mocked = _core as never + +describe('cljstyle tests', () => { + beforeEach(() => { + jest.resetAllMocks() + }) + + describe('getLatestCljstyle', () => { + it('uses tag_name as latest version', async () => { + getJson.mockResolvedValueOnce({ + result: {tag_name: '1.2.3'} + }) + const res = await cljstyle.getLatestCljstyle() + expect(res).toBe('1.2.3') + expect(getJson).toHaveBeenCalledWith( + 'https://api.github.com/repos/greglook/cljstyle/releases/latest', + undefined + ) + }) + + it('supports authorization', async () => { + getJson.mockResolvedValueOnce({ + result: {tag_name: '1.2.3'} + }) + const res = await cljstyle.getLatestCljstyle('token 123') + expect(res).toBe('1.2.3') + expect(getJson).toHaveBeenCalledWith( + 'https://api.github.com/repos/greglook/cljstyle/releases/latest', + {Authorization: 'token 123'} + ) + }) + + it('throws on http client error', async () => { + getJson.mockRejectedValueOnce(new Error('some error')) + await expect(cljstyle.getLatestCljstyle()).rejects.toThrow('some error') + }) + + it('throws on wrong client answer', async () => { + getJson.mockResolvedValueOnce({result: {foo: 'bar'}}) + await expect(cljstyle.getLatestCljstyle()).rejects.toThrow( + `Can't obtain latest cljstyle version` + ) + }) + }) + + describe('getArtifactName', () => { + test.each` + platform | artifact + ${'darwin'} | ${`cljstyle_1.2.3_macos.zip`} + ${'linux'} | ${`cljstyle_1.2.3_linux.zip`} + ${'foobar'} | ${`cljstyle_1.2.3_linux.zip`} + `('$platform -> $artifact', ({platform, artifact}) => { + os.platform.mockReturnValueOnce(platform as never) + expect(cljstyle.getArtifactName('1.2.3')).toBe(artifact) + }) + }) + + describe('getArtifactUrl', () => { + test.each` + platform | artifact + ${'darwin'} | ${`cljstyle_1.2.3_macos.zip`} + ${'linux'} | ${`cljstyle_1.2.3_linux.zip`} + ${'foobar'} | ${`cljstyle_1.2.3_linux.zip`} + `('$platform -> $artifact', ({platform, artifact}) => { + os.platform.mockReturnValueOnce(platform as never) + expect(cljstyle.getArtifactUrl('1.2.3')).toBe( + `https://github.com/greglook/cljstyle/releases/download/1.2.3/${artifact}` + ) + }) + }) + + describe('setup', () => { + it('uses cache', async () => { + tc.find.mockReturnValueOnce('/foo/bar') + + await cljstyle.setup('1.2.3') + + expect(tc.find).toHaveBeenCalledWith('cljstyle', '1.2.3') + expect(core.addPath).toHaveBeenCalledWith('/foo/bar') + }) + + it('uses cache', async () => { + tc.find.mockReturnValueOnce('/foo/bar') + + await cljstyle.setup('1.2.3') + + expect(tc.find).toHaveBeenCalledWith('cljstyle', '1.2.3') + expect(core.addPath).toHaveBeenCalledWith('/foo/bar') + }) + + it('fetches exact version', async () => { + tc.downloadTool.mockResolvedValueOnce('/foo/cljstyle.tar.gz') + tc.extractZip.mockResolvedValueOnce('/bar/baz') + tc.cacheDir.mockResolvedValueOnce('/qux') + + await cljstyle.setup('1.2.3', 'token 123') + + expect(tc.find).toHaveBeenCalledWith('cljstyle', '1.2.3') + expect(tc.downloadTool).toHaveBeenCalledWith( + 'https://github.com/greglook/cljstyle/releases/download/1.2.3/cljstyle_1.2.3_linux.zip', + undefined, + 'token 123' + ) + expect(tc.cacheDir).toHaveBeenCalledWith('/bar/baz', 'cljstyle', '1.2.3') + expect(core.addPath).toHaveBeenCalledWith('/qux') + }) + + it('fetches latest version', async () => { + getJson.mockResolvedValueOnce({ + result: {tag_name: 'v9.9.9'} + }) + tc.downloadTool.mockResolvedValueOnce('/foo/cljstyle.tar.gz') + tc.extractZip.mockResolvedValueOnce('/bar/baz') + tc.cacheDir.mockResolvedValueOnce('/qux') + + await cljstyle.setup('latest', 'token 123') + + expect(getJson).toHaveBeenCalledWith( + 'https://api.github.com/repos/greglook/cljstyle/releases/latest', + {Authorization: 'token 123'} + ) + expect(tc.find).toHaveBeenCalledWith('cljstyle', '9.9.9') + expect(tc.downloadTool).toHaveBeenCalledWith( + 'https://github.com/greglook/cljstyle/releases/download/9.9.9/cljstyle_9.9.9_linux.zip', + undefined, + 'token 123' + ) + expect(tc.cacheDir).toHaveBeenCalledWith('/bar/baz', 'cljstyle', '9.9.9') + expect(core.addPath).toHaveBeenCalledWith('/qux') + }) + }) +}) diff --git a/__tests__/entrypoint.test.ts b/__tests__/entrypoint.test.ts index 6462c1c..1826408 100644 --- a/__tests__/entrypoint.test.ts +++ b/__tests__/entrypoint.test.ts @@ -4,6 +4,7 @@ import * as _boot from '../src/boot' import * as _cli from '../src/cli' import * as _bb from '../src/babashka' import * as _cljKondo from '../src/clj-kondo' +import * as _cljstyle from '../src/cljstyle' import * as _utils from '../src/utils' import {run} from '../src/entrypoint' @@ -25,6 +26,9 @@ const bb: jest.Mocked = _bb as never jest.mock('../src/clj-kondo') const cljKondo: jest.Mocked = _cljKondo as never +jest.mock('../src/cljstyle') +const cljstyle: jest.Mocked = _cljstyle as never + jest.mock('../src/utils') const utils: jest.Mocked = _utils as never @@ -119,6 +123,27 @@ describe('setup-clojure', () => { expect(cljKondo.setup).toHaveBeenCalledWith('1.2.3', 'token abc') }) + it('sets up cljstyle', async () => { + inputs['cljstyle'] = '1.2.3' + inputs['github-token'] = 'abc' + + await run() + + expect(cljstyle.setup).toHaveBeenCalledWith('1.2.3', 'token abc') + }) + + it('throws on cljstyle setup in Windows', async () => { + inputs['cljstyle'] = '1.2.3' + inputs['github-token'] = 'abc' + utils.isWindows.mockReturnValue(true) + + await run() + + expect(core.setFailed).toHaveBeenCalledWith( + 'cljstyle on windows is not supported yet.' + ) + }) + it('throws if none of Clojure tools is specified', async () => { await run() expect(core.setFailed).toHaveBeenCalledWith( diff --git a/action.yml b/action.yml index 132a0e0..748369c 100644 --- a/action.yml +++ b/action.yml @@ -18,6 +18,8 @@ inputs: description: 'Babashka version to install, `latest` can be used.' clj-kondo: description: 'Clj-kondo version to install, `latest` can be used.' + cljstyle: + description: 'cljstyle version to install, `latest` can be used.' github-token: description: >+ To fix rate limit errors, provide `secrets.GITHUB_TOKEN` value to this field. diff --git a/src/cljstyle.ts b/src/cljstyle.ts new file mode 100644 index 0000000..233a8f0 --- /dev/null +++ b/src/cljstyle.ts @@ -0,0 +1,66 @@ +import * as core from '@actions/core' +import * as http from '@actions/http-client' +import * as os from 'os' +import * as tc from '@actions/tool-cache' + +export async function getLatestCljstyle(githubAuth?: string): Promise { + const client = new http.HttpClient('actions/setup-clojure', undefined, { + allowRetries: true, + maxRetries: 3 + }) + + const res = await client.getJson<{tag_name: string}>( + `https://api.github.com/repos/greglook/cljstyle/releases/latest`, + githubAuth ? {Authorization: githubAuth} : undefined + ) + + const result = res.result?.tag_name?.replace(/^v/, '') + if (result) { + return result + } + + throw new Error(`Can't obtain latest cljstyle version`) +} + +export function getArtifactName(version: string): string { + const platform = os.platform() + switch (platform) { + case 'darwin': + return `cljstyle_${version}_macos.zip` + default: + return `cljstyle_${version}_linux.zip` + } +} + +export function getArtifactUrl(version: string): string { + const archiveName = getArtifactName(version) + return `https://github.com/greglook/cljstyle/releases/download/${version}/${archiveName}` +} + +export async function setup( + version: string, + githubAuth?: string +): Promise { + const ver = + version === 'latest' ? await getLatestCljstyle(githubAuth) : version + + let toolDir = tc.find('cljstyle', ver) + if (!toolDir) { + const archiveUrl = getArtifactUrl(ver) + core.info(`Downloading: ${archiveUrl}`) + + const artifactFile = await tc.downloadTool( + archiveUrl, + undefined, + githubAuth + ) + + const extractedDir = await tc.extractZip(artifactFile) + toolDir = await tc.cacheDir(extractedDir, 'cljstyle', ver) + core.info(`Caching directory: ${toolDir}`) + } else { + core.info(`Using cached directory: ${toolDir}`) + } + + core.addPath(toolDir) +} diff --git a/src/entrypoint.ts b/src/entrypoint.ts index 50b6ad5..3784b16 100644 --- a/src/entrypoint.ts +++ b/src/entrypoint.ts @@ -4,6 +4,7 @@ import * as boot from './boot' import * as cli from './cli' import * as bb from './babashka' import * as cljKondo from './clj-kondo' +import * as cljstyle from './cljstyle' import * as utils from './utils' export async function run(): Promise { @@ -14,6 +15,7 @@ export async function run(): Promise { const CLI_VERSION = core.getInput('cli') const BB_VERSION = core.getInput('bb') const CLJ_KONDO_VERSION = core.getInput('clj-kondo') + const CLJSTYLE_VERSION = core.getInput('cljstyle') const githubToken = core.getInput('github-token') const githubAuth = githubToken ? `token ${githubToken}` : undefined @@ -56,6 +58,13 @@ export async function run(): Promise { tools.push(cljKondo.setup(CLJ_KONDO_VERSION, githubAuth)) } + if (CLJSTYLE_VERSION) { + if (IS_WINDOWS) { + throw new Error('cljstyle on windows is not supported yet.') + } + tools.push(cljstyle.setup(CLJSTYLE_VERSION, githubAuth)) + } + if (tools.length === 0) { throw new Error('You must specify at least one clojure tool.') }