build(refactor): better project organization

This commit is contained in:
Michael Shick 2020-07-07 10:51:15 -04:00
parent 5cd99bf9c1
commit e2229d7f55
No known key found for this signature in database
GPG key ID: ADF5BC9704BB4A61
14 changed files with 9869 additions and 74 deletions

45
.eslintrc.js Normal file
View file

@ -0,0 +1,45 @@
module.exports = {
root: true,
extends: ['eslint:recommended', 'plugin:prettier/recommended', 'plugin:import/errors', 'plugin:import/warnings'],
plugins: ['prettier'],
env: {
node: true,
es6: true,
},
parserOptions: {
ecmaVersion: 2018,
},
settings: {
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
'import/resolver': {
typescript: {
alwaysTryTypes: true,
},
},
},
overrides: [
{
files: ['**/*.ts'],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
extends: [
'plugin:import/typescript',
'plugin:@typescript-eslint/recommended',
'prettier/@typescript-eslint',
'plugin:prettier/recommended',
],
parserOptions: {
ecmaVersion: 2018,
project: ['tsconfig.json'],
sourceType: 'module',
tsconfigRootDir: __dirname,
},
env: {
node: true,
es6: true,
},
},
],
}

View file

@ -1,35 +0,0 @@
{
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint", "prettier"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier/@typescript-eslint",
"plugin:prettier/recommended"
],
"rules": {
"prettier/prettier": [
"error",
{
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": false,
"printWidth": 120,
"tabWidth": 2,
"semi": false
}
],
"camelcase": "off",
"@typescript-eslint/camelcase": "off",
"@typescript-eslint/no-non-null-assertion": "off"
},
"env": {
"node": true,
"jest": true,
"es6": true
},
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
}
}

9
.prettierrc.js Normal file
View file

@ -0,0 +1,9 @@
module.exports = {
arrowParens: 'avoid',
bracketSpacing: false,
printWidth: 100,
semi: false,
singleQuote: true,
tabWidth: 2,
trailingComma: 'es5',
}

View file

@ -1,8 +0,0 @@
{
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": false,
"printWidth": 120,
"tabWidth": 2,
"semi": false
}

View file

@ -4,18 +4,19 @@ import * as core from '@actions/core'
import * as github from '@actions/github'
import {WebhookPayload} from '@actions/github/lib/interfaces'
import nock from 'nock'
import run from '../add-pr-comment'
import apiResponse from '../docs/sample-pulls-api-response.json'
import run from '../src/main'
import apiResponse from './sample-pulls-api-response.json'
const repoFullName = 'foo/bar'
const repoToken = '12345'
const userLogin = 'github-actions[bot]'
const commitSha = 'abc123'
let issueNumber = 1
const simpleMessage = 'hello world'
const multilineMessage = fs.readFileSync(path.resolve(__dirname, './message-windows.txt')).toString()
const multilineMessageWindows = fs.readFileSync(path.resolve(__dirname, './message-windows.txt')).toString()
let issueNumber = 1
const inputs = {
message: '',
'repo-token': '',

9574
dist/index.js vendored

File diff suppressed because one or more lines are too long

View file

@ -1,16 +1,9 @@
const processStdoutWrite = process.stdout.write.bind(process.stdout)
process.stdout.write = (str, encoding, cb) => {
if (!str.match(/^##/)) {
return processStdoutWrite(str, encoding, cb)
}
return false
}
module.exports = {
clearMocks: true,
moduleFileExtensions: ['js', 'ts'],
testEnvironment: 'node',
testMatch: ['**/*.test.ts'],
testRunner: 'jest-circus/runner',
transform: {
'^.+\\.ts$': 'ts-jest',
},

139
lib/main.js Normal file
View file

@ -0,0 +1,139 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(require("@actions/core"));
const github = __importStar(require("@actions/github"));
const http_client_1 = require("@actions/http-client");
const listCommitPulls = async (params) => {
const { repoToken, owner, repo, commitSha } = params;
const http = new http_client_1.HttpClient('http-client-add-pr-comment');
const additionalHeaders = {
accept: 'application/vnd.github.groot-preview+json',
authorization: `token ${repoToken}`,
};
const body = await http.getJson(`https://api.github.com/repos/${owner}/${repo}/commits/${commitSha}/pulls`, additionalHeaders);
return body.result;
};
const getIssueNumberFromCommitPullsList = (commitPullsList) => (commitPullsList.length ? commitPullsList[0].number : null);
const createCommentProxy = async (params) => {
const { repoToken, owner, repo, issueNumber, body, proxyUrl } = params;
const http = new http_client_1.HttpClient('http-client-add-pr-comment');
const response = await http.postJson(`${proxyUrl}/repos/${owner}/${repo}/issues/${issueNumber}/comments`, { body }, {
['temporary-github-token']: repoToken,
});
return response.result;
};
const isMessagePresent = (message, comments, login) => {
const cleanRe = new RegExp('\\R|\\s', 'g');
const messageClean = message.replace(cleanRe, '');
return comments.some(({ user, body }) => {
// If a username is provided we can save on a bit of processing
if (login && user.login !== login) {
return false;
}
return body.replace(cleanRe, '') === messageClean;
});
};
const getInputs = () => {
return {
allowRepeats: Boolean(core.getInput('allow-repeats') === 'true'),
message: core.getInput('message'),
proxyUrl: core.getInput('proxy-url').replace(/\/$/, ''),
repoToken: core.getInput('repo-token') || process.env['GITHUB_TOKEN'],
repoTokenUserLogin: core.getInput('repo-token-user-login'),
};
};
const run = async () => {
try {
const { allowRepeats, message, repoToken, repoTokenUserLogin, proxyUrl } = getInputs();
if (!repoToken) {
throw new Error('no github token provided, set one with the repo-token input or GITHUB_TOKEN env variable');
}
const { payload: { pull_request: pullRequest, repository }, sha: commitSha, } = github.context;
if (!repository) {
core.info('unable to determine repository from request type');
core.setOutput('comment-created', 'false');
return;
}
const { full_name: repoFullName } = repository;
const [owner, repo] = repoFullName.split('/');
let issueNumber;
if (pullRequest && pullRequest.number) {
issueNumber = pullRequest.number;
}
else {
// If this is not a pull request, attempt to find a PR matching the sha
const commitPullsList = await listCommitPulls({ repoToken, owner, repo, commitSha });
issueNumber = commitPullsList && getIssueNumberFromCommitPullsList(commitPullsList);
}
if (!issueNumber) {
core.info('this action only works on pull_request events or other commits associated with a pull');
core.setOutput('comment-created', 'false');
return;
}
const octokit = github.getOctokit(repoToken);
let shouldCreateComment = true;
if (!allowRepeats) {
core.debug('repeat comments are disallowed, checking for existing');
const { data: comments } = await octokit.issues.listComments({
owner,
repo,
issue_number: issueNumber,
});
if (isMessagePresent(message, comments, repoTokenUserLogin)) {
core.info('the issue already contains an identical message');
shouldCreateComment = false;
}
}
if (shouldCreateComment) {
if (proxyUrl) {
await createCommentProxy({
owner,
repo,
issueNumber,
body: message,
repoToken,
proxyUrl,
});
}
else {
await octokit.issues.createComment({
owner,
repo,
issue_number: issueNumber,
body: message,
});
}
core.setOutput('comment-created', 'true');
}
else {
core.setOutput('comment-created', 'false');
}
}
catch (error) {
core.setFailed(error.message);
}
};
// Don't auto-execute in the test environment
if (process.env['NODE_ENV'] !== 'test') {
run();
}
exports.default = run;

68
package-lock.json generated
View file

@ -1896,6 +1896,12 @@
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
"dev": true
},
"dedent": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
"integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=",
"dev": true
},
"deep-is": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
@ -3342,6 +3348,68 @@
}
}
},
"jest-circus": {
"version": "26.1.0",
"resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-26.1.0.tgz",
"integrity": "sha512-V5h5XJLPf0XXwP92GIOx8n0Q6vdPDcFPBuEVQ9/OPzpsx3gquL8fdxaJGZ5TsvkU3zWM7mDWULAKYJMRkA2e+g==",
"dev": true,
"requires": {
"@babel/traverse": "^7.1.0",
"@jest/environment": "^26.1.0",
"@jest/test-result": "^26.1.0",
"@jest/types": "^26.1.0",
"chalk": "^4.0.0",
"co": "^4.6.0",
"dedent": "^0.7.0",
"expect": "^26.1.0",
"is-generator-fn": "^2.0.0",
"jest-each": "^26.1.0",
"jest-matcher-utils": "^26.1.0",
"jest-message-util": "^26.1.0",
"jest-runtime": "^26.1.0",
"jest-snapshot": "^26.1.0",
"jest-util": "^26.1.0",
"pretty-format": "^26.1.0",
"stack-utils": "^2.0.2",
"throat": "^5.0.0"
},
"dependencies": {
"@jest/types": {
"version": "26.1.0",
"resolved": "https://registry.npmjs.org/@jest/types/-/types-26.1.0.tgz",
"integrity": "sha512-GXigDDsp6ZlNMhXQDeuy/iYCDsRIHJabWtDzvnn36+aqFfG14JmFV0e/iXxY4SP9vbXSiPNOWdehU5MeqrYHBQ==",
"dev": true,
"requires": {
"@types/istanbul-lib-coverage": "^2.0.0",
"@types/istanbul-reports": "^1.1.1",
"@types/yargs": "^15.0.0",
"chalk": "^4.0.0"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"pretty-format": {
"version": "26.1.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.1.0.tgz",
"integrity": "sha512-GmeO1PEYdM+non4BKCj+XsPJjFOJIPnsLewqhDVoqY1xo0yNmDas7tC2XwpMrRAHR3MaE2hPo37deX5OisJ2Wg==",
"dev": true,
"requires": {
"@jest/types": "^26.1.0",
"ansi-regex": "^5.0.0",
"ansi-styles": "^4.0.0",
"react-is": "^16.12.0"
}
}
}
},
"jest-config": {
"version": "26.1.0",
"resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.1.0.tgz",

View file

@ -2,11 +2,11 @@
"name": "@mshick/add-pr-comment",
"version": "1.0.0",
"description": "A GitHub Action which adds a comment to a Pull Request Issue.",
"main": "dist/index.js",
"main": "lib/main.js",
"scripts": {
"build": "tsc --noEmit && ncc build add-pr-comment.ts -o dist -m",
"build": "tsc && ncc build lib/main.js",
"lint": "eslint . --ext .ts",
"test": "tsc --noEmit && jest",
"test": "jest",
"clean": "rm -rf node_modules dist package-lock.json __tests__/runner/**/*"
},
"repository": {
@ -43,6 +43,7 @@
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-prettier": "^3.1.4",
"jest": "^26.1.0",
"jest-circus": "^26.1.0",
"nock": "^13.0.2",
"prettier": "^2.0.5",
"ts-jest": "^26.1.1",

View file

@ -3,7 +3,7 @@ import * as github from '@actions/github'
import {HttpClient} from '@actions/http-client'
import {Endpoints, RequestHeaders, IssuesListCommentsResponseData} from '@octokit/types'
type ListCommitPullsResponse = Endpoints['GET /repos/:owner/:repo/commits/:commit_sha/pulls']['response']['data']
type ListCommitPullsResponseData = Endpoints['GET /repos/:owner/:repo/commits/:commit_sha/pulls']['response']['data']
type CreateIssueCommentResponseData = Endpoints['POST /repos/:owner/:repo/issues/:issue_number/comments']['response']['data']
interface ListCommitPullsParams {
@ -13,7 +13,9 @@ interface ListCommitPullsParams {
commitSha: string
}
const listCommitPulls = async (params: ListCommitPullsParams): Promise<ListCommitPullsResponse | null> => {
const listCommitPulls = async (
params: ListCommitPullsParams
): Promise<ListCommitPullsResponseData | null> => {
const {repoToken, owner, repo, commitSha} = params
const http = new HttpClient('http-client-add-pr-comment')
@ -23,16 +25,17 @@ const listCommitPulls = async (params: ListCommitPullsParams): Promise<ListCommi
authorization: `token ${repoToken}`,
}
const body = await http.getJson<ListCommitPullsResponse>(
const body = await http.getJson<ListCommitPullsResponseData>(
`https://api.github.com/repos/${owner}/${repo}/commits/${commitSha}/pulls`,
additionalHeaders,
additionalHeaders
)
return body.result
}
const getIssueNumberFromCommitPullsList = (commitPullsList: ListCommitPullsResponse): number | null =>
commitPullsList.length ? commitPullsList[0].number : null
const getIssueNumberFromCommitPullsList = (
commitPullsList: ListCommitPullsResponseData
): number | null => (commitPullsList.length ? commitPullsList[0].number : null)
interface CreateCommentProxyParams {
repoToken: string
@ -43,7 +46,9 @@ interface CreateCommentProxyParams {
proxyUrl: string
}
const createCommentProxy = async (params: CreateCommentProxyParams): Promise<CreateIssueCommentResponseData | null> => {
const createCommentProxy = async (
params: CreateCommentProxyParams
): Promise<CreateIssueCommentResponseData | null> => {
const {repoToken, owner, repo, issueNumber, body, proxyUrl} = params
const http = new HttpClient('http-client-add-pr-comment')
@ -53,7 +58,7 @@ const createCommentProxy = async (params: CreateCommentProxyParams): Promise<Cre
{body},
{
['temporary-github-token']: repoToken,
},
}
)
return response.result
@ -62,7 +67,7 @@ const createCommentProxy = async (params: CreateCommentProxyParams): Promise<Cre
const isMessagePresent = (
message: AddPrCommentInputs['message'],
comments: IssuesListCommentsResponseData,
login?: string,
login?: string
): boolean => {
const cleanRe = new RegExp('\\R|\\s', 'g')
const messageClean = message.replace(cleanRe, '')
@ -100,7 +105,9 @@ const run = async (): Promise<void> => {
const {allowRepeats, message, repoToken, repoTokenUserLogin, proxyUrl} = getInputs()
if (!repoToken) {
throw new Error('no github token provided, set one with the repo-token input or GITHUB_TOKEN env variable')
throw new Error(
'no github token provided, set one with the repo-token input or GITHUB_TOKEN env variable'
)
}
const {
@ -128,7 +135,9 @@ const run = async (): Promise<void> => {
}
if (!issueNumber) {
core.info('this action only works on pull_request events or other commits associated with a pull')
core.info(
'this action only works on pull_request events or other commits associated with a pull'
)
core.setOutput('comment-created', 'false')
return
}

View file

@ -1,8 +1,8 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "esnext",
"lib": ["es2015", "es2017"],
"target": "ES2018",
"lib": ["ES2020"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
@ -11,8 +11,9 @@
"noImplicitAny": true,
"removeComments": false,
"preserveConstEnums": true,
"resolveJsonModule": true
"resolveJsonModule": true,
"rootDir": "./src",
"outDir": "./lib"
},
"include": ["**/*.ts"],
"exclude": ["node_modules"]
"exclude": ["node_modules", "**/*.test.ts"]
}