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
135 changes: 108 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,138 @@
# GitHub Activity Report

This is a simple End of Day (EOD) update generator that helps you create a summary of your GitHub activity. Built using Next.js, this app provides a clean and organized way to track your daily progress on GitHub.

![image](https://github.com/Ashesh3/github-activity/assets/3626859/c8aeff4d-e69e-44ed-91f1-f18e5b0bc36e)
A comprehensive tool for generating GitHub activity reports and downloading repository activity data with customizable filters.

## Features

- Generate EOD updates for your GitHub activity
- Track pull requests created, issues created, issues assigned, and commits made
- Quickly review your activity for the day or a specific time period
### 1. Activity Report Generator
- Generate EOD (End of Day) reports from your GitHub activity
- Track pull requests, issues, commits, and more
- Customizable date ranges and organization filters
- Export reports in markdown format

### 2. Activity Downloader (New!)
- Download repository activity data with customizable filters
- Support for multiple activity types: Pull Requests, Issues, Commits, Releases, Discussions
- Filter by date range, authors, labels, and state
- Export in CSV, JSON, or Excel formats
- Real-time progress tracking during downloads

## Getting Started

To run the project locally, follow these steps:
### Prerequisites
- Node.js 18+
- npm or yarn

1. Clone the repository:
### Installation

1. Clone the repository:
```bash
git clone https://github.com/Ashesh3/github-activity-report.git
git clone <repository-url>
cd github-activity
```

2. Change the directory to the project folder:

```bash
cd github-activity-report
```

3. Install the dependencies:

2. Install dependencies:
```bash
npm install
```

4. Run the development server:

3. Run the development server:
```bash
npm run dev
```

5. Open your browser and navigate to [http://localhost:3000](http://localhost:3000) to view the app.
4. Open [http://localhost:3000](http://localhost:3000) in your browser.

## Usage

1. Enter your GitHub username and the time range for which you want to generate the report.
2. Click the "Generate Report" button.
3. Review your GitHub activity, including pull requests, issues, and commits.
4. Edit the generated EOD update text as needed.
5. Share your EOD update with your team or save it for your records.
### Activity Report Generator

1. **Enter GitHub Username**: Enter your GitHub username to fetch your activity
2. **Select Organization**: Choose an organization to filter activity (optional)
3. **Set Date Range**: Select the time period for your activity report
4. **Configure Settings**: Add your GitHub token for private repository access
5. **Generate Report**: Click "Fetch" to generate your activity timeline
6. **Customize EOD**: Select which activities to include in your EOD report
7. **Export**: Copy the generated markdown report

### Activity Downloader

1. **Enter Repository**: Enter a repository URL or owner/repo format (e.g., `ohcnetwork/care_fe`)
2. **Set Date Range**: Choose the time period for activity data
3. **Select Activity Types**: Choose which types of activity to download:
- Pull Requests
- Issues
- Commits
- Releases
- Discussions
4. **Apply Filters**: Filter by state, authors, and labels
5. **Choose Format**: Select CSV, JSON, or Excel format
6. **Download**: Click "Download Activity" to get your data

## Supported Repository Formats

The downloader supports various repository input formats:
- Full URL: `https://github.com/ohcnetwork/care_fe`
- Owner/Repo: `ohcnetwork/care_fe`
- Any public or private repository (with GitHub token)

## GitHub Token Setup

For enhanced functionality and access to private repositories:

1. Go to [GitHub Settings > Developer settings > Personal access tokens](https://github.com/settings/tokens)
2. Generate a new token with appropriate permissions:
- `repo` (for private repositories)
- `read:org` (for organization access)
3. Copy the token and paste it in the settings modal
4. The token will be saved locally for future use

## API Endpoints

### `/api/github-activity`
POST endpoint for fetching repository activity data.

**Request Body:**
```json
{
"owner": "string",
"repo": "string",
"activityTypes": ["pull_requests", "issues", "commits"],
"startDate": "YYYY-MM-DD",
"endDate": "YYYY-MM-DD",
"state": "all|open|closed",
"authors": ["username1", "username2"],
"labels": ["label1", "label2"],
"githubToken": "optional_token"
}
```

**Response:**
```json
{
"success": true,
"data": [...],
"total": 42,
"repository": "owner/repo"
}
```

## Technologies Used

- **Next.js 14** - React framework
- **Ant Design** - UI components
- **Day.js** - Date manipulation
- **TypeScript** - Type safety
- **Tailwind CSS** - Styling

## Contributing

Contributions are welcome! Feel free to submit a pull request or open an issue for any bugs or feature requests.
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests if applicable
5. Submit a pull request

## License

This project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for more information.
This project is licensed under the MIT License.
152 changes: 152 additions & 0 deletions src/app/api/github-activity/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { NextRequest, NextResponse } from "next/server";
import { exec } from "child_process";
import { promisify } from "util";

const execAsync = promisify(exec);

export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { owner, repo, startDate, endDate, state = "merged", githubToken } = body;

if (!owner || !repo) {
return NextResponse.json({ error: "Owner and repository are required" }, { status: 400 });
}

if (!startDate || !endDate) {
return NextResponse.json({ error: "Start date and end date are required" }, { status: 400 });
}

// Set GitHub token if provided
if (githubToken) {
process.env.GITHUB_TOKEN = githubToken;
}

// Try GitHub CLI first, then fallback to GitHub API
let data = [];
let useFallback = false;

try {
// First, let's test if we can access the repository
console.log("Testing repository access...");
const testCommand = `gh repo view ${owner}/${repo} --json name,description`;

try {
await execAsync(testCommand);
console.log("Repository access successful");
} catch (testError: any) {
console.error("Repository access failed:", testError.message);
useFallback = true;
}

if (!useFallback) {
// Construct the GitHub CLI command - simplified version
const command = `gh pr list --repo ${owner}/${repo} --state ${state} --json number,title,state,author,createdAt,mergedAt,labels,assignees,url --limit 100`;

console.log("Executing command:", command);

// Add timeout to prevent hanging
const { stdout, stderr } = (await Promise.race([
execAsync(command),
new Promise((_, reject) => setTimeout(() => reject(new Error("Command timeout after 30 seconds")), 30000)),
])) as { stdout: string; stderr: string };

console.log("Command stdout length:", stdout.length);
console.log("Command stderr:", stderr);

if (stderr && !stderr.includes("warning")) {
console.warn("GitHub CLI stderr:", stderr);
}

if (stdout.trim()) {
try {
data = JSON.parse(stdout);
console.log("Successfully parsed JSON, found", data.length, "pull requests");
} catch (parseError) {
console.error("Failed to parse JSON output:", parseError);
console.log("Raw stdout (first 500 chars):", stdout.substring(0, 500));
useFallback = true;
}
} else {
console.log("No stdout received from command");
useFallback = true;
}
}
} catch (execError: any) {
console.error("GitHub CLI execution error:", execError);
useFallback = true;
}

// Fallback to GitHub API if CLI failed
if (useFallback || data.length === 0) {
console.log("Using GitHub API fallback...");

const headers: { [key: string]: string } = {
Accept: "application/vnd.github.v3+json",
};

if (githubToken) {
headers["Authorization"] = `token ${githubToken}`;
}

const url = `https://api.github.com/repos/${owner}/${repo}/pulls`;
const params = new URLSearchParams();
params.set("state", state === "merged" ? "closed" : state);
params.set("per_page", "100");

const response = await fetch(`${url}?${params.toString()}`, { headers });

if (!response.ok) {
throw new Error(`GitHub API Error: ${response.status} ${response.statusText}`);
}

const apiData = await response.json();

// Filter for merged PRs if state is "merged"
if (state === "merged") {
data = apiData.filter((item: any) => item.merged_at != null);
} else {
data = apiData;
}

console.log("GitHub API fallback found", data.length, "pull requests");
}

// Filter by date range if needed
let filteredData = data;
if (startDate && endDate) {
filteredData = data.filter((item: any) => {
if (!item.mergedAt && !item.merged_at) return false;
const mergedDate = new Date(item.mergedAt || item.merged_at);
const start = new Date(startDate);
const end = new Date(endDate);
return mergedDate >= start && mergedDate <= end;
});
console.log("Filtered to", filteredData.length, "PRs in date range");
}

// Transform the data to match the expected format
const transformedData = filteredData.map((item: any) => ({
number: item.number,
title: item.title,
type: "pull_requests",
state: item.state,
author: item.author?.login || item.user?.login || "",
created_at: item.createdAt || item.created_at,
merged_at: item.mergedAt || item.merged_at,
labels: item.labels?.map((l: any) => l.name).join(", ") || "",
assignees: item.assignees?.map((a: any) => a.login).join(", ") || "",
html_url: item.url || item.html_url,
}));

return NextResponse.json({
success: true,
data: transformedData,
total: transformedData.length,
repository: `${owner}/${repo}`,
});
} catch (error: any) {
console.error("GitHub Activity API Error:", error);
return NextResponse.json({ error: error.message || "Internal server error" }, { status: 500 });
}
}
Loading