Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

easy way to list, approve and merge all dependabot PRs #1

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
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
6 changes: 4 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
NEXTAUTH_URL=http://localhost:4002
NEXTAUTH_URL=http://localhost:5500

# You can use openssl to generate a random 32 character key: openssl rand -base64 32
NEXTAUTH_SECRET=rZTFtfNuSMajLnfFrWT2PZ3lX8WZv7W/Xs2H8hkEY6g=
Expand All @@ -13,7 +13,7 @@ SMTP_FROM=
# If you are using Docker, you can retrieve the values from: docker-compose.yml
DATABASE_URL=postgresql://<USER-HERE>:<PASSWORD-HERE>@localhost:5432/<DATABASE NAME HERE>

APP_URL=http://localhost:4002
APP_URL=http://localhost:5500

SVIX_URL=https://api.eu.svix.com
SVIX_API_KEY=
Expand Down Expand Up @@ -44,6 +44,8 @@ DISABLE_NON_BUSINESS_EMAIL_SIGNUP=false
# Mixpanel
NEXT_PUBLIC_MIXPANEL_TOKEN=

GITHUB_TOKEN=

# If you want to use Jackson that is self-hosted or our SaaS instead of the embedded Jackson that comes with SaaS Starter Kit
# JACKSON_URL=http://localhost:5225
# JACKSON_EXTERNAL_URL=https://sso.eu.boxyhq.com
Expand Down
4 changes: 4 additions & 0 deletions middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ const unAuthenticatedRoutes = [
'/api/webhooks/dsync',
'/auth/**',
'/invitations/*',
'/api/github',
'/api/github/**',
'/github',
'/github/**',
'/terms-condition',
'/unlock-account',
'/login/saml',
Expand Down
4,519 changes: 1,440 additions & 3,079 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
},
"scripts": {
"release": "git checkout release && git merge origin/main && release-it && git checkout main && git merge origin/release && git push origin main",
"dev": "next dev --port 4002",
"dev": "next dev --port 5500",
"build": "prisma generate && prisma db push && next build",
"start": "next start --port 4002",
"start": "next start --port 5500",
"check-types": "tsc --pretty --noEmit",
"check-format": "prettier --check .",
"check-lint": "eslint -c eslint.config.cjs ./",
Expand All @@ -32,6 +32,7 @@
"@boxyhq/saml-jackson": "1.35.1",
"@heroicons/react": "2.2.0",
"@next-auth/prisma-adapter": "1.0.7",
"@octokit/core": "5.0.0",
"@prisma/client": "6.2.1",
"@react-email/components": "0.0.32",
"@react-email/render": "1.0.4",
Expand Down
99 changes: 99 additions & 0 deletions pages/api/github/[owner]/[repo]/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { Octokit } from '@octokit/core';
import { slugify } from '@/lib/common';
import { ApiError } from '@/lib/errors';
import { getSession } from '@/lib/session';
import { createTeam, isTeamExists } from 'models/team';
import type { NextApiRequest, NextApiResponse } from 'next';

const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });

export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { method } = req;

try {
switch (method) {
case 'GET':
await handleGET(req, res);
break;
case 'POST':
await handlePOST(req, res);
break;
default:
res.setHeader('Allow', 'GET, POST');
res.status(405).json({
error: { message: `Method ${method} Not Allowed` },
});
}
} catch (error: any) {
const message = error.message || 'Something went wrong';
const status = error.status || 500;

res.status(status).json({ error: { message } });
}
}

// Get teams
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
const { owner, repo, excludes } = req.query;

let excludesList = {};
if (excludes) {
excludesList = JSON.parse(excludes as string);
}

try {
const response = await octokit.request('GET /repos/{owner}/{repo}/pulls', {
owner: owner as string,
repo: repo as string,
state: 'open',
});

const prs = response.data.filter((pr) => {
if (pr.user!.login === 'dependabot[bot]') {
const excluded = excludesList[`${owner}/${repo}`] || [];
for (const exc of excluded) {
if (pr.title.startsWith(`Bump ${exc} from`)) {
return false;
}
}
return true;
}

return false;
});

// console.log('prs:', prs);

res.status(200).json({
data: prs,
});
} catch (error: any) {
console.error('error:', error);
res
.status(error.status || 500)
.json({ error: { message: 'Something went wrong' } });
}
};

// Create a team
const handlePOST = async (req: NextApiRequest, res: NextApiResponse) => {
const { name } = req.body;

const session = await getSession(req, res);
const slug = slugify(name);

if (await isTeamExists([{ slug }])) {
throw new ApiError(400, 'A team with the name already exists.');
}

const team = await createTeam({
userId: session?.user?.id as string,
name,
slug,
});

res.status(200).json({ data: team });
};
95 changes: 95 additions & 0 deletions pages/api/github/[owner]/[repo]/review.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { Octokit } from '@octokit/core';
import { slugify } from '@/lib/common';
import { ApiError } from '@/lib/errors';
import { getSession } from '@/lib/session';
import { createTeam, isTeamExists } from 'models/team';
import type { NextApiRequest, NextApiResponse } from 'next';

const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });

export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { method } = req;

try {
switch (method) {
case 'GET':
await handleGET(req, res);
break;
case 'POST':
await handlePOST(req, res);
break;
default:
res.setHeader('Allow', 'GET, POST');
res.status(405).json({
error: { message: `Method ${method} Not Allowed` },
});
}
} catch (error: any) {
const message = error.message || 'Something went wrong';
const status = error.status || 500;

res.status(status).json({ error: { message } });
}
}

// Get teams
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
try {
const { pull_number, owner, repo } = req.query;
const pullNumber = parseInt('' + pull_number, 10);

await octokit.request(
'POST /repos/{owner}/{repo}/pulls/{pull_number}/reviews',
{
owner: owner as string,
repo: repo as string,
pull_number: pullNumber,
event: 'APPROVE',
}
);

// console.log('response:', response);

await octokit.request(
'PUT /repos/{owner}/{repo}/pulls/{pull_number}/merge',
{
owner: owner as string,
repo: repo as string,
pull_number: pullNumber,
merge_method: 'squash',
}
);

// console.log('response1:', response1);

res.status(200).json({});
} catch (error: any) {
console.error('error:', error);
res
.status(error.status || 500)
.json({ error: { message: 'Something went wrong' } });
}
};

// Create a team
const handlePOST = async (req: NextApiRequest, res: NextApiResponse) => {
const { name } = req.body;

const session = await getSession(req, res);
const slug = slugify(name);

if (await isTeamExists([{ slug }])) {
throw new ApiError(400, 'A team with the name already exists.');
}

const team = await createTeam({
userId: session?.user?.id as string,
name,
slug,
});

res.status(200).json({ data: team });
};
Loading