Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/actions/code-pushup/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Code PushUp
description: Minimalist GitHub Action that executes Code PushUp using local @code-pushup/ci source code

inputs:
token:
description: GitHub token for API access
required: true
default: ${{ github.token }}

runs:
using: composite
steps:
- name: Run Node script
run: npx tsx .github/actions/code-pushup/src/runner.ts
shell: bash
env:
TSX_TSCONFIG_PATH: .github/actions/code-pushup/tsconfig.json
GH_TOKEN: ${{ inputs.token }}
7 changes: 7 additions & 0 deletions .github/actions/code-pushup/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "@code-pushup/local-action",
"version": "0.0.0",
"private": true,
"type": "module",
"description": "Minimalist GitHub Action that executes Code PushUp using local @code-pushup/ci source code for testing CI changes"
}
8 changes: 8 additions & 0 deletions .github/actions/code-pushup/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "local-action",
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": ".github/actions/code-pushup/src",
"projectType": "application",
"targets": {},
"tags": ["type:app"]
}
159 changes: 159 additions & 0 deletions .github/actions/code-pushup/src/runner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import * as core from '@actions/core';
import * as github from '@actions/github';
import type { WebhookPayload } from '@actions/github/lib/interfaces';
import type { components } from '@octokit/openapi-types';
import {
type Comment,
type GitBranch,
type Options,
type ProviderAPIClient,
type SourceFileIssue,
runInCI,
} from '@code-pushup/ci';
import { CODE_PUSHUP_UNICODE_LOGO } from '@code-pushup/utils';

type GitHubRefs = {
head: GitBranch;
base?: GitBranch;
};

type PullRequestPayload = NonNullable<WebhookPayload['pull_request']> &
components['schemas']['pull-request-minimal'];

const LOG_PREFIX = '[Code PushUp GitHub action]';

const MAX_COMMENT_CHARS = 65_536;

function convertComment(
comment: Pick<components['schemas']['issue-comment'], 'id' | 'body' | 'url'>,
): Comment {
const { id, body = '', url } = comment;
return { id, body, url };
}

function isPullRequest(
payload: WebhookPayload['pull_request'],
): payload is PullRequestPayload {
return payload != null;
}

function parseBranchRef({ ref, sha }: GitBranch): GitBranch {
return {
ref: ref.split('/').at(-1) ?? ref,
sha,
};
}

function parseGitRefs(): GitHubRefs {
if (isPullRequest(github.context.payload.pull_request)) {
const { head, base } = github.context.payload.pull_request;
return { head: parseBranchRef(head), base: parseBranchRef(base) };
}
return { head: parseBranchRef(github.context) };
}

function createAnnotationsFromIssues(issues: SourceFileIssue[]): void {
if (issues.length > 0) {
core.info(`Creating annotations for ${issues.length} issues:`);
}
// eslint-disable-next-line functional/no-loop-statements
for (const issue of issues) {
const message = issue.message;
const properties: core.AnnotationProperties = {
title: `${CODE_PUSHUP_UNICODE_LOGO} ${issue.plugin.title} | ${issue.audit.title}`,
file: issue.source.file,
startLine: issue.source.position?.startLine,
startColumn: issue.source.position?.startColumn,
endLine: issue.source.position?.endLine,
endColumn: issue.source.position?.endColumn,
};
switch (issue.severity) {
case 'error':
core.error(message, properties);
break;
case 'warning':
core.warning(message, properties);
break;
case 'info':
core.notice(message, properties);
break;
}
}
}

function createGitHubApiClient(): ProviderAPIClient {
const token = process.env.GH_TOKEN;

if (!token) {
throw new Error('No GitHub token found');
}

const octokit = github.getOctokit(token);

return {
maxCommentChars: MAX_COMMENT_CHARS,

listComments: async (): Promise<Comment[]> => {
const comments = await octokit.paginate(
octokit.rest.issues.listComments,
{
...github.context.repo,
issue_number: github.context.issue.number,
},
);
return comments.map(convertComment);
},

createComment: async (body: string): Promise<Comment> => {
const { data } = await octokit.rest.issues.createComment({
...github.context.repo,
issue_number: github.context.issue.number,
body,
});
return convertComment(data);
},

updateComment: async (id: number, body: string): Promise<Comment> => {
const { data } = await octokit.rest.issues.updateComment({
...github.context.repo,
comment_id: id,
body,
});
return convertComment(data);
},
};
}

async function run(): Promise<void> {
try {
const options: Options = {
bin: 'npx nx code-pushup --nx-bail --',
};

const gitRefs = parseGitRefs();

const apiClient = createGitHubApiClient();

const result = await runInCI(gitRefs, apiClient, options);

const issues =
result.mode === 'standalone'
? (result.newIssues ?? [])
: result.projects.flatMap(project => project.newIssues ?? []);

if (issues.length > 0) {
core.info(
`Found ${issues.length} new issues, creating GitHub annotations`,
);
createAnnotationsFromIssues(issues);
}

core.info(`${LOG_PREFIX} Finished running successfully`);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
core.error(`${LOG_PREFIX} Failed: ${message}`);
core.setFailed(message);
}
}

await run();
4 changes: 4 additions & 0 deletions .github/actions/code-pushup/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "../../../tsconfig.base.json",
"include": ["src/**/*"]
}
4 changes: 2 additions & 2 deletions .github/workflows/code-pushup-fork.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,6 @@ jobs:
- name: Install dependencies
run: npm ci
- name: Run Code PushUp action
uses: code-pushup/github-action@v0
uses: ./.github/actions/code-pushup
with:
bin: npx nx code-pushup --nx-bail --
token: ${{ secrets.GITHUB_TOKEN }}
4 changes: 2 additions & 2 deletions .github/workflows/code-pushup.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,6 @@ jobs:
- name: Install dependencies
run: npm ci
- name: Run Code PushUp action
uses: code-pushup/github-action@v0
uses: ./.github/actions/code-pushup
with:
bin: npx nx code-pushup --nx-bail --
token: ${{ secrets.GITHUB_TOKEN }}
93 changes: 93 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
"zod": "^4.0.5"
},
"devDependencies": {
"@actions/core": "^1.11.1",
"@actions/github": "^6.0.1",
"@beaussan/nx-knip": "^0.0.5-15",
"@code-pushup/eslint-config": "^0.14.2",
"@commitlint/cli": "^19.5.0",
Expand Down
Loading