Compare commits

..

14 Commits

6 changed files with 830 additions and 5461 deletions

View File

@@ -1,14 +1,14 @@
FROM node:lts-alpine AS build FROM node:lts-alpine AS build
WORKDIR /app WORKDIR /app
COPY package*.json ./ COPY package*.json ./
RUN npm ci RUN npm i
COPY . . COPY . .
RUN npm run build RUN npm run build
FROM node:lts-alpine AS runtime FROM node:lts-alpine AS runtime
USER node
WORKDIR /app WORKDIR /app
ENV NODE_ENV production ENV NODE_ENV production
USER node
COPY --chown=node:node package*.json ./ COPY --chown=node:node package*.json ./
RUN npm ci --omit=dev RUN npm ci --omit=dev
COPY --from=build --chown=node:node /app/dist ./dist COPY --from=build --chown=node:node /app/dist ./dist

View File

@@ -2,6 +2,8 @@
GitHub bot that automatically verifies pull requests with new playlist entries for [spotify-playlist-archive](https://github.com/mackorone/spotify-playlist-archive) repo. GitHub bot that automatically verifies pull requests with new playlist entries for [spotify-playlist-archive](https://github.com/mackorone/spotify-playlist-archive) repo.
[Example of a validated pull request](https://github.com/mackorone/spotify-playlist-archive/pull/408)
## Development ## Development
### Prerequisites ### Prerequisites

View File

@@ -7,9 +7,7 @@ import { getPlaylistIdFromUrl } from './getPlaylistIdFromUrl';
type ReviewEvent = 'REQUEST_CHANGES' | 'COMMENT' | 'APPROVE'; type ReviewEvent = 'REQUEST_CHANGES' | 'COMMENT' | 'APPROVE';
const appFn: ApplicationFunction = (app: Probot, { getRouter }) => { const appFn: ApplicationFunction = (app: Probot) => {
getRouter!('/ping').get('/pong', (_, res) => res.sendStatus(200));
app.on( app.on(
['pull_request.opened', 'pull_request.synchronize'], ['pull_request.opened', 'pull_request.synchronize'],
async ({ payload, octokit, log }) => { async ({ payload, octokit, log }) => {
@@ -31,14 +29,14 @@ const appFn: ApplicationFunction = (app: Probot, { getRouter }) => {
body: string body: string
) => { ) => {
if (review_id) { if (review_id) {
await octokit.pulls.updateReview({ await octokit.rest.pulls.updateReview({
...workingRepo, ...workingRepo,
pull_number, pull_number,
review_id, review_id,
body body
}); });
} else { } else {
await octokit.pulls.createReview({ await octokit.rest.pulls.createReview({
...workingRepo, ...workingRepo,
pull_number, pull_number,
event, event,
@@ -49,6 +47,7 @@ const appFn: ApplicationFunction = (app: Probot, { getRouter }) => {
const repoAllowlist = [ const repoAllowlist = [
{ owner: 'mackorone', repo: 'spotify-playlist-archive' }, { owner: 'mackorone', repo: 'spotify-playlist-archive' },
{ owner: 'mackorone', repo: 'spotify-playlist-archive-2' },
{ owner: 'maciejpedzich', repo: 'bot-testing-ground' } { owner: 'maciejpedzich', repo: 'bot-testing-ground' }
]; ];
@@ -61,7 +60,7 @@ const appFn: ApplicationFunction = (app: Probot, { getRouter }) => {
if (!isAllowlistedRepo) return; if (!isAllowlistedRepo) return;
type PRFileArray = Awaited< type PRFileArray = Awaited<
ReturnType<typeof octokit.pulls.listFiles> ReturnType<typeof octokit.rest.pulls.listFiles>
>['data']; >['data'];
const prFiles: PRFileArray = []; const prFiles: PRFileArray = [];
@@ -72,7 +71,7 @@ const appFn: ApplicationFunction = (app: Probot, { getRouter }) => {
while (isLoadingPages) { while (isLoadingPages) {
await setTimeout(timeToRateLimitReset); await setTimeout(timeToRateLimitReset);
const { data, headers } = await octokit.pulls.listFiles({ const { data, headers } = await octokit.rest.pulls.listFiles({
...workingRepo, ...workingRepo,
pull_number, pull_number,
page page
@@ -132,7 +131,11 @@ const appFn: ApplicationFunction = (app: Probot, { getRouter }) => {
if (found) { if (found) {
const html = await spotifyResponse.text(); const html = await spotifyResponse.text();
const { author: authorUrl, description } = await getMetaData({ const {
// author: authorUrl,
description,
title
} = await getMetaData({
html, html,
customRules: { customRules: {
author: { author: {
@@ -146,32 +149,39 @@ const appFn: ApplicationFunction = (app: Probot, { getRouter }) => {
} }
}); });
let authorName = (authorUrl as string).endsWith('/user/spotify') // let authorName = (authorUrl as string).endsWith('/user/spotify')
? 'Spotify' // ? 'Spotify'
: ''; // : '';
if (authorName === '') { // if (authorName === '') {
const playlistAuthorResponse = await fetch(authorUrl as string); // const playlistAuthorResponse = await fetch(authorUrl as string);
if (!playlistAuthorResponse.ok) // if (!playlistAuthorResponse.ok)
throw new Error( // throw new Error(
`Received ${playlistAuthorResponse.status} status code from ${authorUrl}` // `Received ${playlistAuthorResponse.status} status code from ${authorUrl}`
); // );
const authorPageHtml = await playlistAuthorResponse.text(); // const authorPageHtml = await playlistAuthorResponse.text();
const { title: authorPageTitle } = await getMetaData({ // const { title: authorPageTitle } = await getMetaData({
html: authorPageHtml // html: authorPageHtml
}); // });
authorName = authorPageTitle as string; // authorName = authorPageTitle as string;
} // }
const playlistMeta = (description || '') // const playlistMeta = (description || '')
.split(' · ') // .split(' · ')
.filter((text) => text !== 'Playlist') // .filter((text) => text !== 'Playlist')
.concat(authorName as string); // .concat(authorName as string);
details = playlistMeta.join(' · '); // details = playlistMeta.join(' · ');
details = (
title +
' · ' +
description!.replace('Playlist · ', '')
)
.replace(/\s+/g, ' ')
.trim();
} }
numProcessedEntries++; numProcessedEntries++;
@@ -236,7 +246,7 @@ const appFn: ApplicationFunction = (app: Probot, { getRouter }) => {
if (urlFilenameEntries.length > 0) { if (urlFilenameEntries.length > 0) {
successText = ''; successText = '';
const forkPageUrl = payload.pull_request.head.repo.html_url; const forkPageUrl = payload.pull_request.head.repo?.html_url;
const httpsDirUrl = `${forkPageUrl}/tree/main/playlists/registry/https:`; const httpsDirUrl = `${forkPageUrl}/tree/main/playlists/registry/https:`;
const baseCreateUrl = `${forkPageUrl}/new/main/playlists/registry/FOO`; const baseCreateUrl = `${forkPageUrl}/new/main/playlists/registry/FOO`;
@@ -276,7 +286,7 @@ const appFn: ApplicationFunction = (app: Probot, { getRouter }) => {
.filter(Boolean) .filter(Boolean)
.join('\n\n'); .join('\n\n');
const { data: reviews } = await octokit.pulls.listReviews({ const { data: reviews } = await octokit.rest.pulls.listReviews({
...workingRepo, ...workingRepo,
pull_number pull_number
}); });

View File

@@ -1,4 +1,8 @@
import { run } from 'probot'; import { run } from 'probot';
import appFn from './appFn'; import appFn from './appFn';
run(appFn); run(appFn).then((server) => {
process.on('SIGTERM', async () => {
await server.stop();
});
});

6201
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -17,16 +17,16 @@
}, },
"homepage": "https://github.com/maciejpedzich/mackorone-playlist-pr-bot#readme", "homepage": "https://github.com/maciejpedzich/mackorone-playlist-pr-bot#readme",
"dependencies": { "dependencies": {
"metadata-scraper": "^0.2.60", "metadata-scraper": "^0.2.61",
"probot": "^12.3.3", "probot": "^14.2.4",
"promise-throttle-all": "^1.1.1" "promise-throttle-all": "^1.1.1"
}, },
"engines": { "engines": {
"node": ">=18" "node": ">=18"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.14.11", "@types/node": "^22.15.29",
"ts-node-dev": "^2.0.0", "ts-node-dev": "^2.0.0",
"typescript": "^4.8.3" "typescript": "^5.9.3"
} }
} }