Skip to content

Commit 60a2fd1

Browse files
committed
add initial code
1 parent 4b8993c commit 60a2fd1

File tree

2 files changed

+129
-22
lines changed

2 files changed

+129
-22
lines changed

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@
3737
},
3838
"license": "MIT",
3939
"dependencies": {
40-
"@actions/core": "^1.11.1"
40+
"@actions/core": "^1.11.1",
41+
"@actions/github": "^6.0.0",
42+
"@actions/glob": "^0.5.0"
4143
},
4244
"devDependencies": {
4345
"@eslint/compat": "^1.2.7",

src/main.ts

Lines changed: 126 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,132 @@
1-
import * as core from '@actions/core'
2-
import { wait } from './wait.js'
3-
4-
/**
5-
* The main function for the action.
6-
*
7-
* @returns Resolves when the action is complete.
8-
*/
9-
export async function run(): Promise<void> {
10-
try {
11-
const ms: string = core.getInput('milliseconds')
1+
import * as core from '@actions/core';
2+
import * as github from '@actions/github';
3+
import * as glob from "@actions/glob";
4+
import { readFileSync, existsSync } from 'fs';
5+
6+
interface Input {
7+
token: string;
8+
'include-gitignore': boolean;
9+
'ignore-default': boolean;
10+
files: string;
11+
}
12+
13+
export function getInputs(): Input {
14+
const result = {} as Input;
15+
result.token = core.getInput('github-token');
16+
result['include-gitignore'] = core.getBooleanInput('include-gitignore');
17+
result['ignore-default'] = core.getBooleanInput('ignore-default');
18+
result.files = core.getInput('files');
19+
return result;
20+
}
21+
22+
export const runAction = async (octokit: ReturnType<typeof github.getOctokit>, input: Input): Promise<void> => {
23+
let allFiles: string[] = [];
24+
if (input.files) {
25+
allFiles = input.files.split(' ');
26+
allFiles = await (await glob.create(allFiles.join('\n'))).glob();
27+
} else {
28+
allFiles = await (await glob.create('*')).glob();
29+
}
30+
core.startGroup(`All Files: ${allFiles.length}`);
31+
core.info(JSON.stringify(allFiles));
32+
core.endGroup();
33+
34+
const codeownerContent = getCodeownerContent();
35+
core.startGroup('Reading CODEOWNERS File');
36+
core.endGroup();
37+
let codeownerFileGlobs = codeownerContent.split('\n')
38+
.map(line => line.split(' ')[0])
39+
.filter(file => !file.startsWith('#'))
40+
.map(file => file.replace(/^\//, ''));
41+
if (input['ignore-default'] === true) {
42+
codeownerFileGlobs = codeownerFileGlobs.filter(file => file !== '*');
43+
}
44+
45+
const codeownersGlob = await glob.create(codeownerFileGlobs.join('\n'));
46+
let codeownersFiles = await codeownersGlob.glob();
47+
core.startGroup(`CODEOWNERS Files: ${codeownersFiles.length}`);
48+
core.info(JSON.stringify(codeownersFiles));
49+
core.endGroup();
50+
codeownersFiles = codeownersFiles.filter(file => allFiles.includes(file));
51+
core.info(`CODEOWNER Files in All Files: ${codeownersFiles.length}`);
52+
core.startGroup('CODEOWNERS');
53+
core.info(JSON.stringify(codeownersFiles));
54+
core.endGroup();
55+
1256

13-
// Debug logs are only output if the `ACTIONS_STEP_DEBUG` secret is true
14-
core.debug(`Waiting ${ms} milliseconds ...`)
1557

16-
// Log the current timestamp, wait, then log the new timestamp
17-
core.debug(new Date().toTimeString())
18-
await wait(parseInt(ms, 10))
19-
core.debug(new Date().toTimeString())
58+
let filesCovered = codeownersFiles;
59+
let allFilesClean = allFiles;
60+
if (input['include-gitignore'] === true) {
61+
let gitIgnoreFiles: string[] = [];
62+
if(!existsSync('.gitignore')){
63+
core.warning('No .gitignore file found');
64+
} else {
65+
const gitIgnoreBuffer = readFileSync('.gitignore', 'utf8');
66+
const gitIgnoreGlob = await glob.create(gitIgnoreBuffer);
67+
gitIgnoreFiles = await gitIgnoreGlob.glob();
68+
core.info(`.gitignore Files: ${gitIgnoreFiles.length}`);
69+
}
70+
allFilesClean = allFiles.filter(file => !gitIgnoreFiles.includes(file));
71+
filesCovered = filesCovered.filter(file => !gitIgnoreFiles.includes(file));
72+
}
73+
if (input.files) {
74+
filesCovered = filesCovered.filter(file => allFilesClean.includes(file));
75+
}
76+
const coveragePercent = (filesCovered.length / allFilesClean.length) * 100;
77+
const coverageMessage = `${filesCovered.length}/${allFilesClean.length}(${coveragePercent.toFixed(2)}%) files covered by CODEOWNERS`;
78+
core.notice(coverageMessage, {
79+
title: 'Coverage',
80+
file: 'CODEOWNERS'
81+
});
82+
83+
const filesNotCovered = allFilesClean.filter(f => !filesCovered.includes(f));
84+
core.info(`Files not covered: ${filesNotCovered.length}`);
85+
86+
if (github.context.eventName === 'pull_request') {
87+
const checkResponse = await octokit.rest.checks.create({
88+
owner: github.context.repo.owner,
89+
repo: github.context.repo.repo,
90+
name: 'Changed Files have CODEOWNERS',
91+
head_sha: github.context.payload.pull_request?.head.sha || github.context.payload.after || github.context.sha,
92+
status: 'completed',
93+
completed_at: (new Date()).toISOString(),
94+
output: {
95+
title: 'Codeowners check!',
96+
summary: `Summary`,
97+
annotations: filesNotCovered.map(file => ({
98+
path: file,
99+
annotation_level: 'failure' as 'failure',
100+
message: 'File not covered by CODEOWNERS',
101+
start_line: 0,
102+
end_line: 1,
103+
})).slice(0, 50),
104+
},
105+
conclusion: coveragePercent < 100 ? 'failure' : 'success',
106+
});
107+
console.log('Check Response OK: ', checkResponse.status);
108+
}
109+
}
20110

21-
// Set outputs for other workflow steps to use
22-
core.setOutput('time', new Date().toTimeString())
111+
export async function run(): Promise<void> {
112+
try {
113+
const input = getInputs();
114+
const octokit: ReturnType<typeof github.getOctokit> = github.getOctokit(input.token);
115+
return runAction(octokit, input);
23116
} catch (error) {
24-
// Fail the workflow run if an error occurs
25-
if (error instanceof Error) core.setFailed(error.message)
117+
core.startGroup(error instanceof Error ? error.message : JSON.stringify(error));
118+
core.info(JSON.stringify(error, null, 2));
119+
core.endGroup();
120+
}
121+
};
122+
123+
124+
function getCodeownerContent(): string {
125+
if(existsSync('CODEOWNERS')) {
126+
return readFileSync('CODEOWNERS', 'utf8')
127+
}
128+
if(existsSync('.github/CODEOWNERS')){
129+
return readFileSync('.github/CODEOWNERS', 'utf8');
26130
}
131+
throw new Error('No CODEOWNERS file found');
27132
}

0 commit comments

Comments
 (0)