From 85ef93202eaf6d3350f446cb6d1823f5f8a10c86 Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Mon, 30 Jan 2023 02:31:09 +0100 Subject: [PATCH] move GitHub specific to its own class Signed-off-by: CrazyMax --- __tests__/context.test.ts | 52 +++----------------------------------- __tests__/github.test.ts | 53 +++++++++++++++++++++++++++++++++++++++ src/context.ts | 44 +++----------------------------- src/github.ts | 40 +++++++++++++++++++++++++++++ src/toolkit.ts | 10 +++++--- 5 files changed, 107 insertions(+), 92 deletions(-) create mode 100644 __tests__/github.test.ts create mode 100644 src/github.ts diff --git a/__tests__/context.test.ts b/__tests__/context.test.ts index 2adab37..74d7c19 100644 --- a/__tests__/context.test.ts +++ b/__tests__/context.test.ts @@ -3,22 +3,18 @@ import path from 'path'; import rimraf from 'rimraf'; import {describe, expect, jest, it, beforeEach, afterEach} from '@jest/globals'; -import {Context, ReposGetResponseData} from '../src/context'; +import {Context} from '../src/context'; const tmpDir = path.join('/tmp/.docker-actions-toolkit-jest').split(path.sep).join(path.posix.sep); const tmpName = path.join(tmpDir, '.tmpname-jest').split(path.sep).join(path.posix.sep); -beforeEach(() => { - jest.clearAllMocks(); -}); - -jest.spyOn(Context.prototype as any, 'tmpDir').mockImplementation((): string => { +jest.spyOn(Context.prototype, 'tmpDir').mockImplementation((): string => { if (!fs.existsSync(tmpDir)) { fs.mkdirSync(tmpDir, {recursive: true}); } return tmpDir; }); -jest.spyOn(Context.prototype as any, 'tmpName').mockImplementation((): string => { +jest.spyOn(Context.prototype, 'tmpName').mockImplementation((): string => { return tmpName; }); @@ -30,34 +26,6 @@ afterEach(() => { rimraf.sync(tmpDir); }); -import * as repoFixture from './fixtures/repo.json'; -jest.spyOn(Context.prototype as any, 'repoData').mockImplementation((): Promise => { - return >(repoFixture as unknown); -}); - -describe('serverURL', () => { - const originalEnv = process.env; - beforeEach(() => { - jest.resetModules(); - process.env = { - ...originalEnv, - GITHUB_SERVER_URL: 'https://foo.github.com' - }; - }); - afterEach(() => { - process.env = originalEnv; - }); - it('returns default', async () => { - process.env.GITHUB_SERVER_URL = ''; - const context = new Context(); - expect(context.serverURL).toEqual('https://github.com'); - }); - it('returns from env', async () => { - const context = new Context(); - expect(context.serverURL).toEqual('https://foo.github.com'); - }); -}); - describe('gitContext', () => { it('returns refs/heads/master', async () => { const context = new Context(); @@ -71,17 +39,3 @@ describe('provenanceBuilderID', () => { expect(context.provenanceBuilderID).toEqual('https://github.com/docker/actions-toolkit/actions/runs/123'); }); }); - -describe('repo', () => { - it('returns GitHub repository', async () => { - const context = new Context(); - expect((await context.repoData()).name).toEqual('Hello-World'); - }); -}); - -describe('fromPayload', () => { - it('returns repository name from payload', async () => { - const context = new Context(); - expect(await context.fromPayload('repository.name')).toEqual('test-docker-action'); - }); -}); diff --git a/__tests__/github.test.ts b/__tests__/github.test.ts new file mode 100644 index 0000000..b8b32a0 --- /dev/null +++ b/__tests__/github.test.ts @@ -0,0 +1,53 @@ +import {describe, expect, jest, it, beforeEach, afterEach} from '@jest/globals'; + +import {GitHub, GitHubRepo} from '../src/github'; + +beforeEach(() => { + jest.clearAllMocks(); +}); + +import * as repoFixture from './fixtures/repo.json'; +jest.spyOn(GitHub.prototype, 'repoData').mockImplementation((): Promise => { + return >(repoFixture as unknown); +}); + +describe('context', () => { + it('returns repository name from payload', async () => { + const github = new GitHub(); + expect(github.context.payload.repository?.name).toEqual('test-docker-action'); + }); + it('is repository private', async () => { + const github = new GitHub(); + expect(github.context.payload.repository?.private).toEqual(true); + }); +}); + +describe('serverURL', () => { + const originalEnv = process.env; + beforeEach(() => { + jest.resetModules(); + process.env = { + ...originalEnv, + GITHUB_SERVER_URL: 'https://foo.github.com' + }; + }); + afterEach(() => { + process.env = originalEnv; + }); + it('returns default', async () => { + process.env.GITHUB_SERVER_URL = ''; + const github = new GitHub(); + expect(github.serverURL).toEqual('https://github.com'); + }); + it('returns from env', async () => { + const github = new GitHub(); + expect(github.serverURL).toEqual('https://foo.github.com'); + }); +}); + +describe('repoData', () => { + it('returns GitHub repository', async () => { + const github = new GitHub(); + expect((await github.repoData()).name).toEqual('Hello-World'); + }); +}); diff --git a/src/context.ts b/src/context.ts index af7520a..8854ef1 100644 --- a/src/context.ts +++ b/src/context.ts @@ -2,26 +2,18 @@ import fs from 'fs'; import os from 'os'; import path from 'path'; import * as tmp from 'tmp'; -import jwt_decode, {JwtPayload} from 'jwt-decode'; -import {GitHub} from '@actions/github/lib/utils'; import * as github from '@actions/github'; -import {components as OctoOpenApiTypes} from '@octokit/openapi-types'; -export type ReposGetResponseData = OctoOpenApiTypes['schemas']['repository']; -export interface Jwt extends JwtPayload { - ac?: string; -} +import {GitHub} from './github'; export class Context { - public serverURL: string; public gitRef: string; public buildGitContext: string; public provenanceBuilderID: string; - public octokit: InstanceType; private readonly _tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'docker-actions-toolkit-')).split(path.sep).join(path.posix.sep); - constructor(githubToken?: string) { + constructor() { this.gitRef = github.context.ref; if (github.context.sha && this.gitRef && !this.gitRef.startsWith('refs/')) { this.gitRef = `refs/heads/${github.context.ref}`; @@ -29,10 +21,8 @@ export class Context { if (github.context.sha && !this.gitRef.startsWith(`refs/pull/`)) { this.gitRef = github.context.sha; } - this.serverURL = process.env.GITHUB_SERVER_URL || 'https://github.com'; - this.buildGitContext = `${this.serverURL}/${github.context.repo.owner}/${github.context.repo.repo}.git#${this.gitRef}`; - this.provenanceBuilderID = `${this.serverURL}/${github.context.repo.owner}/${github.context.repo.repo}/actions/runs/${github.context.runId}`; - this.octokit = github.getOctokit(`${githubToken}`); + this.buildGitContext = `${GitHub.serverURL}/${github.context.repo.owner}/${github.context.repo.repo}.git#${this.gitRef}`; + this.provenanceBuilderID = `${GitHub.serverURL}/${github.context.repo.owner}/${github.context.repo.repo}/actions/runs/${github.context.runId}`; } public tmpDir(): string { @@ -42,30 +32,4 @@ export class Context { public tmpName(options?: tmp.TmpNameOptions): string { return tmp.tmpNameSync(options); } - - public repoData(): Promise { - return this.octokit.rest.repos.get({...github.context.repo}).then(response => response.data as ReposGetResponseData); - } - - public parseRuntimeToken(): Jwt { - return jwt_decode(process.env['ACTIONS_RUNTIME_TOKEN'] || ''); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public fromPayload(path: string): any { - return this.select(github.context.payload, path); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private select(obj: any, path: string): any { - if (!obj) { - return undefined; - } - const i = path.indexOf('.'); - if (i < 0) { - return obj[path]; - } - const key = path.slice(0, i); - return this.select(obj[key], path.slice(i + 1)); - } } diff --git a/src/github.ts b/src/github.ts new file mode 100644 index 0000000..569f683 --- /dev/null +++ b/src/github.ts @@ -0,0 +1,40 @@ +import {GitHub as Octokit} from '@actions/github/lib/utils'; +import * as github from '@actions/github'; +import {components as OctoOpenApiTypes} from '@octokit/openapi-types'; +import jwt_decode, {JwtPayload} from 'jwt-decode'; +import {Context} from '@actions/github/lib/context'; + +export type GitHubRepo = OctoOpenApiTypes['schemas']['repository']; + +export interface GitHubRuntimeToken extends JwtPayload { + ac?: string; +} + +export interface GitHubOpts { + token?: string; +} + +export class GitHub { + public static readonly serverURL: string = process.env.GITHUB_SERVER_URL || 'https://github.com'; + public readonly octokit: InstanceType; + + constructor(opts?: GitHubOpts) { + this.octokit = github.getOctokit(`${opts?.token}`); + } + + get context(): Context { + return github.context; + } + + get serverURL(): string { + return process.env.GITHUB_SERVER_URL || 'https://github.com'; + } + + get runtimeToken(): GitHubRuntimeToken { + return jwt_decode(`${process.env['ACTIONS_RUNTIME_TOKEN']}`); + } + + public repoData(): Promise { + return this.octokit.rest.repos.get({...github.context.repo}).then(response => response.data as GitHubRepo); + } +} diff --git a/src/toolkit.ts b/src/toolkit.ts index 54c2ce4..edd6565 100644 --- a/src/toolkit.ts +++ b/src/toolkit.ts @@ -1,13 +1,15 @@ import {Context} from './context'; import {Buildx} from './buildx'; import {BuildKit} from './buildkit'; +import {GitHub} from './github'; -export {BuildKit, BuildKitOpts} from './buildkit'; export {Builder, BuilderOpts, BuilderInfo, NodeInfo} from './builder'; +export {BuildKit, BuildKitOpts} from './buildkit'; export {Buildx, BuildxOpts} from './buildx'; -export {Context, ReposGetResponseData, Jwt} from './context'; +export {Context} from './context'; export {Docker} from './docker'; export {Git} from './git'; +export {GitHub, GitHubRepo, GitHubRuntimeToken} from './github'; export {Util} from './util'; export interface ToolkitOpts { @@ -20,11 +22,13 @@ export interface ToolkitOpts { export class Toolkit { public context: Context; + public github: GitHub; public buildx: Buildx; public buildkit: BuildKit; constructor(opts: ToolkitOpts = {}) { - this.context = new Context(opts.githubToken); + this.context = new Context(); + this.github = new GitHub({token: opts.githubToken}); this.buildx = new Buildx({context: this.context}); this.buildkit = new BuildKit({context: this.context, buildx: this.buildx}); }