Skip to content
Open
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
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
GITHUB_TOKEN=your_github_token_here
# Public GitHub repository URL shown by the app.
# If omitted, the app falls back to https://github.com/O2sa/DevImpact
NEXT_PUBLIC_GITHUB_REPO_URL=your_github_repo_url_here


# GitHub query limits
GITHUB_REPO_COUNT=30
GITHUB_PR_COUNT=80
GITHUB_ISSUE_COUNT=20
GITHUB_DISCUSSION_COUNT=10

# Redis caching (optional)
# Use either redis://localhost:6379 or include password if enabled: redis://:password@localhost:6379
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,12 @@ Create a `.env` file:

```
GITHUB_TOKEN=your_github_token
NEXT_PUBLIC_GITHUB_REPO_URL=your_github_repo_url
GITHUB_REPO_COUNT=30
GITHUB_PR_COUNT=80
GITHUB_ISSUE_COUNT=20
GITHUB_DISCUSSION_COUNT=10

```

---
Expand Down
3 changes: 2 additions & 1 deletion components/github-link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ type GithubLinkProps = {
export function GithubLink({ variant = "compact" }: GithubLinkProps) {
const isProminent = variant === "prominent";

const githubRepoUrl= process.env.NEXT_PUBLIC_GITHUB_REPO_URL || "https://github.com/O2sa/DevImpact";
return (
<a
href="https://github.com/O2sa/DevImpact"
href={githubRepoUrl}
target="_blank"
rel="noopener noreferrer"
aria-label="GitHub repository"
Expand Down
29 changes: 29 additions & 0 deletions lib/github.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {describe, expect, it} from "vitest";
import {parseCountEnv} from "./github";


describe("parseCountEnv", () => {
it("uses fallback for undefined", () => {
expect(parseCountEnv(undefined, 30, 100)).toBe(30);
});

it("parses a valid value", () => {
expect(parseCountEnv("50", 30, 100)).toBe(50);
});

it("uses fallback for non-numeric input", () => {
expect(parseCountEnv("abc", 30, 100)).toBe(30);
});

it("uses fallback for zero", () => {
expect(parseCountEnv("0", 30, 100)).toBe(30);
});

it("uses fallback for negative values", () => {
expect(parseCountEnv("-5", 30, 100)).toBe(30);
});

it("clamps values above the maximum", () => {
expect(parseCountEnv("500", 30, 100)).toBe(100);
});
});
62 changes: 57 additions & 5 deletions lib/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import type {

type Logger = Pick<Console, "info" | "warn">;

type GitHubRawUser = {
type GitHubRawUser = {
name: string | null;
avatarUrl: string;
repositories: { nodes: Array<RepoNode | null> };
Expand Down Expand Up @@ -71,6 +71,31 @@ export type GitHubFetcherDependencies = {
logger?: Logger;
};

const DEFAULT_GITHUB_REPO_COUNT = 30;
const DEFAULT_GITHUB_PR_COUNT = 80;
const DEFAULT_GITHUB_ISSUE_COUNT = 20;
const DEFAULT_GITHUB_DISCUSSION_COUNT = 10;


const MAX_GITHUB_REPO_COUNT = 100;
const MAX_GITHUB_PR_COUNT = 100;
const MAX_GITHUB_ISSUE_COUNT = 100;
const MAX_GITHUB_DISCUSSION_COUNT = 100;

export function parseCountEnv(
value: string | undefined,
fallback: number,
maxValue: number,
): number{

const parsed = Number.parseInt(value ?? "", 10);
if(!Number.isInteger(parsed)|| parsed<=0){
return fallback;
}
return Math.min(parsed, maxValue);

}

const USER_AND_PULL_REQUESTS_QUERY = /* GraphQL */ `
query FetchUserAndPullRequests(
$login: String!
Expand Down Expand Up @@ -275,6 +300,8 @@ export function buildGitHubUserCacheKey(
return `${namespace}:github-user:${normalizeGitHubUsername(username)}`;
}



async function fetchUserDataFromGitHub(
executor: GitHubQueryExecutor,
username: string,
Expand All @@ -283,6 +310,31 @@ async function fetchUserDataFromGitHub(
const externalIssueQuery = `type:issue author:${username} -user:${username}`;
const externalDiscussionQuery = `author:${username} -user:${username}`;


const repoCount = parseCountEnv(
process.env.GITHUB_REPO_COUNT,
DEFAULT_GITHUB_REPO_COUNT,
MAX_GITHUB_REPO_COUNT,
);

const prCount = parseCountEnv(
process.env.GITHUB_PR_COUNT,
DEFAULT_GITHUB_PR_COUNT,
MAX_GITHUB_PR_COUNT,
);

const issueCount = parseCountEnv(
process.env.GITHUB_ISSUE_COUNT,
DEFAULT_GITHUB_ISSUE_COUNT,
MAX_GITHUB_ISSUE_COUNT,
);

const discussionCount = parseCountEnv(
process.env.GITHUB_DISCUSSION_COUNT,
DEFAULT_GITHUB_DISCUSSION_COUNT,
MAX_GITHUB_DISCUSSION_COUNT,
);

const userAndPrResponse =
await executor.execute<
FetchUserAndPullRequestsResponse,
Expand All @@ -297,8 +349,8 @@ async function fetchUserDataFromGitHub(
query: USER_AND_PULL_REQUESTS_QUERY,
variables: {
login: username,
repoCount: 30,
prCount: 80,
repoCount,
prCount,
externalPrQuery,
},
});
Expand All @@ -319,7 +371,7 @@ async function fetchUserDataFromGitHub(
operationName: "FetchUserIssues",
query: ISSUES_QUERY,
variables: {
issueCount: 20,
issueCount,
externalIssueQuery,
},
}),
Expand All @@ -333,7 +385,7 @@ async function fetchUserDataFromGitHub(
operationName: "FetchUserDiscussions",
query: DISCUSSIONS_QUERY,
variables: {
discussionCount: 10,
discussionCount,
externalDiscussionQuery,
},
}),
Expand Down
Loading