diff --git a/src/action.ts b/src/action.ts index 328a9cf..df80b0a 100644 --- a/src/action.ts +++ b/src/action.ts @@ -1,154 +1,37 @@ -import nodeFetch from "node-fetch"; -import path from "path"; import fs from "fs"; import {getInput} from "@actions/core"; -import {promisify} from "util"; import {context} from "@actions/github"; import {Octokit} from "@octokit/action"; import { - branchExists, - createBranch, - createInitialCommit, + createBranchIfNotExists, createOrUpdateFile, createPRComment, getRepoName, - getRepoOwner + getRepoOwner, + getSourceBranch, + isPullRequest } from "./github"; import {exec, getExecOutput} from "@actions/exec"; -import {makeBadge} from "badge-maker"; +import {downloadCodeLimitBinary, getBadgeContent, getReportContent} from "./codelimit"; +import {getChangedFiles} from "./utils"; -const streamPipeline = promisify(require('stream').pipeline); - - -function getBinaryName() { - const binaries: { [platform: string]: string } = { - 'darwin': 'codelimit-macos', - 'win32': 'codelimit.exe', - 'linux': 'codelimit-linux' - }; - if (process.env.RUNNER_OS) { - const platform = process.env.RUNNER_OS.toLowerCase(); - if (platform in binaries) { - return binaries[platform]; - } - } - if (process.platform in binaries) { - return binaries[process.platform]; - } - return binaries['linux']; -} - -async function getLatestBinaryUrl() { - const latestUrl = 'https://github.com/getcodelimit/codelimit/releases/latest'; - const res = await nodeFetch(latestUrl); - const downloadUrl = res.url.replace('/tag/', '/download/'); - return `${downloadUrl}/${getBinaryName()}`; -} - -async function downloadBinary() { - const binaryUrl = await getLatestBinaryUrl(); - console.log(`Downloading Code Limit binary from URL: ${binaryUrl}`); - const response = await nodeFetch(binaryUrl); - const filename = path.join(__dirname, getBinaryName()); - await streamPipeline(response.body, fs.createWriteStream(filename)); - fs.chmodSync(filename, '777'); - console.log(`Code Limit binary downloaded: ${filename}`); - return filename; -} - -async function getChangedFiles(token: string) { - const eventName = context.eventName - if (eventName === undefined) { - return ['.']; - } - let base; - let head; - if (eventName === 'pull_request') { - base = context.payload.pull_request?.base?.sha - head = context.payload.pull_request?.head?.sha - } else { - base = context.payload.before - head = context.payload.after - } - console.log(`Base commit: ${base}`); - console.log(`Head commit: ${head}`); - const octokit = new Octokit({auth: token}); - const response = await octokit.repos.compareCommits({ - base, head, owner: context.repo.owner, repo: context.repo.repo - }); - if (response.status !== 200) { - return ['.']; - } - const files = response.data.files - const result = []; - if (files) { - for (const file of files) { - const filename = file.filename - if (file.status === 'modified' || file.status === 'added') { - result.push(filename); - } - } - } +async function generateMarkdownReport(clBinary: string) { + const totalsMarkdown = await getExecOutput(clBinary, ['report', '--totals', '--format', 'markdown']); + const unitsMarkdown = await getExecOutput(clBinary, ['report', '--full', '--format', 'markdown']); + let result = ''; + result += '## Codebase totals\n'; + result += totalsMarkdown.stdout; + result += '\n'; + result += '## Refactoring report\n'; + result += unitsMarkdown.stdout; return result; } -function isPullRequest() { - return context.eventName === 'pull_request'; -} - -function getSourceBranch() { - if (isPullRequest()) { - return process.env.GITHUB_HEAD_REF; - } else { - return process.env.GITHUB_REF_NAME; - } -} - -async function createReportsBranchIfNotExists(octokit: Octokit, owner: string, repo: string) { - if (!await branchExists(octokit, owner, repo, '_codelimit_reports')) { - const initialCommitSha = await createInitialCommit(octokit, owner, repo); - await createBranch(octokit, owner, repo, '_codelimit_reports', initialCommitSha); - } else { - console.log('Branch _codelimit_reports already exists'); - } -} - -function makeBadgeSvg(message: string, color: 'red' | 'orange' | 'green' | 'grey'): string { - const badge = { - label: 'Code Limit', - message: message, - color: color - }; - return makeBadge(badge); -} - -function getReportContent(): string | undefined { - return fs.readFileSync('.codelimit_cache/codelimit.json', 'utf8'); -} - -function getBadgeContent(reportContent: string | undefined): string { - if (!reportContent) { - return makeBadgeSvg('Not found', 'grey'); - } else { - const reportJson = JSON.parse(reportContent); - const profile = reportJson.codebase.tree['./'].profile - if (profile[3] > 0) { - return makeBadgeSvg('Needs refactoring', 'red'); - } else if (profile[2] > 0) { - return makeBadgeSvg('Needs refactoring', 'orange'); - } else { - return makeBadgeSvg('Passed', 'green'); - } - } -} - async function main() { - const filename = await downloadBinary(); + const clBinary = await downloadCodeLimitBinary(); console.log('Scanning codebase...'); - await exec(filename, ['scan', '.']); - const totalsMarkdown = await getExecOutput(filename, ['report', '--totals', '--format', 'markdown']); - const unitsMarkdown = await getExecOutput(filename, ['report', '--full', '--format', 'markdown']); - const markdownReport = `${totalsMarkdown.stdout}\n${unitsMarkdown.stdout}`; + await exec(clBinary, ['scan', '.']); + const markdownReport = await generateMarkdownReport(clBinary); const doUpload = getInput('upload') || false; const token = getInput('token'); const octokit = new Octokit({auth: token}); @@ -159,7 +42,7 @@ async function main() { process.exit(1); } const branch = getSourceBranch(); - await createReportsBranchIfNotExists(octokit, owner, repo); + await createBranchIfNotExists(octokit, owner, repo, '_codelimit_reports'); const reportContent = getReportContent(); await createOrUpdateFile(octokit, owner, repo, '_codelimit_reports', `${branch}/badge.svg`, getBadgeContent(reportContent)); if (reportContent) { @@ -181,7 +64,7 @@ async function main() { } const slug = context.payload.repository?.full_name; if (slug && branch) { - exitCode = await exec(filename, ['app', 'upload', '--token', token, slug, branch]); + exitCode = await exec(clBinary, ['app', 'upload', '--token', token, slug, branch]); } } const doCheck = getInput('check') || true; @@ -192,10 +75,10 @@ async function main() { console.log('No files changed, skipping Code Limit'); } else { console.log('Running Code Limit...'); - exitCode = await exec(filename, ['check'].concat(changedFiles), {ignoreReturnCode: true}); + exitCode = await exec(clBinary, ['check'].concat(changedFiles), {ignoreReturnCode: true}); } } - fs.unlinkSync(filename); + fs.unlinkSync(clBinary); console.log('Done!'); process.exit(exitCode); } diff --git a/src/codelimit.ts b/src/codelimit.ts new file mode 100644 index 0000000..776067c --- /dev/null +++ b/src/codelimit.ts @@ -0,0 +1,72 @@ +import nodeFetch from "node-fetch"; +import path from "path"; +import fs from "fs"; +import {promisify} from "util"; +import {makeBadge} from "badge-maker"; + +const streamPipeline = promisify(require('stream').pipeline); + +function getBinaryName() { + const binaries: { [platform: string]: string } = { + 'darwin': 'codelimit-macos', + 'win32': 'codelimit.exe', + 'linux': 'codelimit-linux' + }; + if (process.env.RUNNER_OS) { + const platform = process.env.RUNNER_OS.toLowerCase(); + if (platform in binaries) { + return binaries[platform]; + } + } + if (process.platform in binaries) { + return binaries[process.platform]; + } + return binaries['linux']; +} + +async function getLatestBinaryUrl() { + const latestUrl = 'https://github.com/getcodelimit/codelimit/releases/latest'; + const res = await nodeFetch(latestUrl); + const downloadUrl = res.url.replace('/tag/', '/download/'); + return `${downloadUrl}/${getBinaryName()}`; +} + +export async function downloadCodeLimitBinary() { + const binaryUrl = await getLatestBinaryUrl(); + console.log(`Downloading Code Limit binary from URL: ${binaryUrl}`); + const response = await nodeFetch(binaryUrl); + const filename = path.join(__dirname, getBinaryName()); + await streamPipeline(response.body, fs.createWriteStream(filename)); + fs.chmodSync(filename, '777'); + console.log(`Code Limit binary downloaded: ${filename}`); + return filename; +} + +export function getReportContent(): string | undefined { + return fs.readFileSync('.codelimit_cache/codelimit.json', 'utf8'); +} + +function makeBadgeSvg(message: string, color: 'red' | 'orange' | 'green' | 'grey'): string { + const badge = { + label: 'Code Limit', + message: message, + color: color + }; + return makeBadge(badge); +} + +export function getBadgeContent(reportContent: string | undefined): string { + if (!reportContent) { + return makeBadgeSvg('Not found', 'grey'); + } else { + const reportJson = JSON.parse(reportContent); + const profile = reportJson.codebase.tree['./'].profile + if (profile[3] > 0) { + return makeBadgeSvg('Needs refactoring', 'red'); + } else if (profile[2] > 0) { + return makeBadgeSvg('Needs refactoring', 'orange'); + } else { + return makeBadgeSvg('Passed', 'green'); + } + } +} \ No newline at end of file diff --git a/src/github.ts b/src/github.ts index 9663dc9..d29062d 100644 --- a/src/github.ts +++ b/src/github.ts @@ -1,5 +1,6 @@ import {Octokit} from "@octokit/action"; import {Context} from "@actions/github/lib/context"; +import {context} from "@actions/github"; export async function branchExists(octokit: Octokit, owner: string, repo: string, branchName: string) { try { @@ -98,4 +99,25 @@ export async function createPRComment(octokit: Octokit, owner: string, repo: str issue_number: prNumber, body: comment }); +} + +export function isPullRequest() { + return context.eventName === 'pull_request'; +} + +export function getSourceBranch() { + if (isPullRequest()) { + return process.env.GITHUB_HEAD_REF; + } else { + return process.env.GITHUB_REF_NAME; + } +} + +export async function createBranchIfNotExists(octokit: Octokit, owner: string, repo: string, branchName: string) { + if (!await branchExists(octokit, owner, repo, branchName)) { + const initialCommitSha = await createInitialCommit(octokit, owner, repo); + await createBranch(octokit, owner, repo, branchName, initialCommitSha); + } else { + console.log(`Branch ${branchName} already exists`); + } } \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..0c794c0 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,36 @@ +import {context} from "@actions/github"; +import {Octokit} from "@octokit/action"; + +export async function getChangedFiles(token: string) { + const eventName = context.eventName + if (eventName === undefined) { + return ['.']; + } + let base; + let head; + if (eventName === 'pull_request') { + base = context.payload.pull_request?.base?.sha + head = context.payload.pull_request?.head?.sha + } else { + base = context.payload.before + head = context.payload.after + } + const octokit = new Octokit({auth: token}); + const response = await octokit.repos.compareCommits({ + base, head, owner: context.repo.owner, repo: context.repo.repo + }); + if (response.status !== 200) { + return ['.']; + } + const files = response.data.files + const result = []; + if (files) { + for (const file of files) { + const filename = file.filename + if (file.status === 'modified' || file.status === 'added') { + result.push(filename); + } + } + } + return result; +} \ No newline at end of file