Skip to content

Commit

Permalink
Merge pull request #66 from ubiquity/chore/test-empty-string-2
Browse files Browse the repository at this point in the history
Chore/test empty string 2
  • Loading branch information
0x4007 authored Oct 9, 2024
2 parents 3addf6b + e956bec commit 0deed0e
Show file tree
Hide file tree
Showing 6 changed files with 330 additions and 88 deletions.
129 changes: 129 additions & 0 deletions .github/empty-string-checker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import * as core from "@actions/core";
import { Octokit } from "@octokit/rest";
import simpleGit from "simple-git";

const token = process.env.GITHUB_TOKEN;
const [owner, repo] = process.env.GITHUB_REPOSITORY?.split("/") || [];
const pullNumber = process.env.GITHUB_PR_NUMBER || process.env.PULL_REQUEST_NUMBER || "0";
const baseRef = process.env.GITHUB_BASE_REF;

if (!token || !owner || !repo || pullNumber === "0" || !baseRef) {
core.setFailed("Missing required environment variables.");
process.exit(1);
}

const octokit = new Octokit({ auth: token });
const git = simpleGit();

async function main() {
try {
const { data: pullRequest } = await octokit.pulls.get({
owner,
repo,
pull_number: parseInt(pullNumber, 10),
});

const baseSha = pullRequest.base.sha;
const headSha = pullRequest.head.sha;

await git.fetch(["origin", baseSha, headSha]);

const diff = await git.diff([`${baseSha}...${headSha}`]);

core.info("Checking for empty strings...");
const violations = parseDiffForEmptyStrings(diff);

if (violations.length > 0) {
violations.forEach(({ file, line, content }) => {
core.warning(
"Detected an empty string.\n\nIf this is during variable initialization, consider using a different approach.\nFor more information, visit: https://www.github.com/ubiquity/ts-template/issues/31",
{
file,
startLine: line,
}
);
});

// core.setFailed(`${violations.length} empty string${violations.length > 1 ? "s" : ""} detected in the code.`);

await octokit.rest.checks.create({
owner,
repo,
name: "Empty String Check",
head_sha: headSha,
status: "completed",
conclusion: violations.length > 0 ? "failure" : "success",
output: {
title: "Empty String Check Results",
summary: `Found ${violations.length} violation${violations.length !== 1 ? "s" : ""}`,
annotations: violations.map((v) => ({
path: v.file,
start_line: v.line,
end_line: v.line,
annotation_level: "warning",
message: "Empty string found",
raw_details: v.content,
})),
},
});
} else {
core.info("No empty strings found.");
}
} catch (error) {
core.setFailed(`An error occurred: ${error instanceof Error ? error.message : String(error)}`);
}
}

function parseDiffForEmptyStrings(diff: string) {
const violations: Array<{ file: string; line: number; content: string }> = [];
const diffLines = diff.split("\n");

let currentFile: string;
let headLine = 0;
let inHunk = false;

diffLines.forEach((line) => {
const hunkHeaderMatch = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/.exec(line);
if (hunkHeaderMatch) {
headLine = parseInt(hunkHeaderMatch[1], 10);
inHunk = true;
return;
}

if (line.startsWith("--- a/") || line.startsWith("+++ b/")) {
currentFile = line.slice(6);
inHunk = false;
return;
}

// Only process TypeScript files
if (!currentFile?.endsWith(".ts")) {
return;
}

if (inHunk && line.startsWith("+")) {
// Check for empty strings in TypeScript syntax
if (/^\+.*""/.test(line)) {
// Ignore empty strings in comments
if (!line.trim().startsWith("//") && !line.trim().startsWith("*")) {
// Ignore empty strings in template literals
if (!/`[^`]*\$\{[^}]*\}[^`]*`/.test(line)) {
violations.push({
file: currentFile,
line: headLine,
content: line.substring(1).trim(),
});
}
}
}
headLine++;
} else if (!line.startsWith("-")) {
headLine++;
}
});

return violations;
}
main().catch((error) => {
core.setFailed(`Error running empty string check: ${error instanceof Error ? error.message : String(error)}`);
});
2 changes: 1 addition & 1 deletion .github/knip.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { KnipConfig } from "knip";

const config: KnipConfig = {
entry: ["build/index.ts"],
entry: ["build/index.ts", ".github/empty-string-checker.ts"],
project: ["src/**/*.ts"],
ignore: ["src/types/config.ts", "**/__mocks__/**", "**/__fixtures__/**"],
ignoreExportsUsedInFile: true,
Expand Down
71 changes: 16 additions & 55 deletions .github/workflows/no-empty-strings.yml
Original file line number Diff line number Diff line change
@@ -1,71 +1,32 @@
name: Check for Empty Strings
name: Empty String Check

on:
pull_request:
types: [opened, synchronize, reopened]

jobs:
check-empty-strings:
check-for-empty-strings:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20.10.0"
- name: Get GitHub App token
uses: tibdex/github-app-token@v1.7.0
id: get_installation_token
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.APP_PRIVATE_KEY }}

- name: Find empty strings
id: find-empty-strings
- name: Install Dependencies
run: |
# Get the base branch (usually main or master)
BASE_BRANCH=$(gh pr view ${{ github.event.pull_request.number }} --json baseRefName --jq .baseRefName)
# Get the list of changed files in the PR
CHANGED_FILES=$(gh pr diff ${{ github.event.pull_request.number }} --name-only)
# Initialize empty string for results
EMPTY_STRINGS=""
# Loop through changed files
for file in $CHANGED_FILES; do
if [[ $file =~ \.(ts|tsx)$ ]]; then
# Get the diff for this file
DIFF=$(gh pr diff ${{ github.event.pull_request.number }} --color never -- $file)
# Find added lines with empty strings
ADDED_EMPTY=$(echo "$DIFF" | grep -n "^+" | grep -E "(''{2}|\"\"{2})" | sed "s/^+//g" | sed "s/^/${file}:/")
# Append results
if [ ! -z "$ADDED_EMPTY" ]; then
EMPTY_STRINGS+="$ADDED_EMPTY"$'\n'
fi
fi
done
# Output results
echo "EMPTY_STRINGS<<EOF" >> $GITHUB_OUTPUT
echo "$EMPTY_STRINGS" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
yarn add tsx simple-git
- name: Check for Empty Strings
run: |
yarn tsx .github/empty-string-checker.ts
env:
GH_TOKEN: ${{ steps.get_installation_token.outputs.token }}

- name: Comment on PR
uses: actions/github-script@v6
if: steps.find-empty-strings.outputs.EMPTY_STRINGS
with:
github-token: ${{ steps.get_installation_token.outputs.token }}
script: |
const emptyStrings = process.env.EMPTY_STRINGS.trim().split('\n');
if (emptyStrings.length > 0) {
const body = `### Empty Strings Found\n\nPlease review the following lines containing empty strings:\n\n${emptyStrings.map(line => `- \`${line}\``).join('\n')}`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.name,
body: body
});
}
GITHUB_TOKEN: ${{ steps.get_installation_token.outputs.token }}
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }}
GITHUB_BASE_REF: ${{ github.base_ref }}
4 changes: 1 addition & 3 deletions .github/workflows/sync-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.APP_PRIVATE_KEY }}

- name: Sync branch to template
env:
GH_TOKEN: ${{ steps.get_installation_token.outputs.token }}
Expand All @@ -45,5 +45,3 @@ jobs:
git commit -m "chore: sync template"
git push "$original_remote" "$pr_branch"
gh pr create --title "Sync branch to template" --body "This pull request merges changes from the template repository." --head "$pr_branch" --base "$branch_name"
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,15 @@
"dotenv": "^16.4.4"
},
"devDependencies": {
"@actions/core": "^1.11.1",
"@commitlint/cli": "^18.6.1",
"@commitlint/config-conventional": "^18.6.2",
"@cspell/dict-node": "^4.0.3",
"@cspell/dict-software-terms": "^3.3.18",
"@cspell/dict-typescript": "^3.1.2",
"@jest/globals": "29.7.0",
"@mswjs/data": "0.16.1",
"@octokit/rest": "^21.0.2",
"@types/jest": "29.5.12",
"@types/node": "^20.11.19",
"@typescript-eslint/eslint-plugin": "^7.0.1",
Expand All @@ -60,6 +62,7 @@
"lint-staged": "^15.2.2",
"npm-run-all": "^4.1.5",
"prettier": "^3.2.5",
"simple-git": "^3.27.0",
"ts-jest": "29.1.2",
"tsx": "^4.7.1",
"typescript": "^5.3.3"
Expand Down
Loading

0 comments on commit 0deed0e

Please sign in to comment.