Show a rename warning for valid playlist URLs

This commit is contained in:
Maciej Pędzich 2023-01-27 14:22:17 +01:00
parent b79596acef
commit 5e82956fcd
3 changed files with 62 additions and 33 deletions

74
bot.ts
View File

@ -1,19 +1,21 @@
import { ApplicationFunction } from 'probot'; import { ApplicationFunction } from 'probot';
import getMetaData from 'metadata-scraper'; import getMetaData from 'metadata-scraper';
import { getPlaylistIdFromUrl } from './getPlaylistIdFromUrl';
type ReviewEvent = 'REQUEST_CHANGES' | 'COMMENT' | 'APPROVE'; type ReviewEvent = 'REQUEST_CHANGES' | 'COMMENT' | 'APPROVE';
const bot: ApplicationFunction = (app) => { export const bot: ApplicationFunction = (app) => {
app.on( app.on(
['pull_request.opened', 'pull_request.synchronize'], ['pull_request.opened', 'pull_request.synchronize'],
async (context) => { async ({ payload, octokit }) => {
const registryDirectoryPath = 'playlists/registry/'; const registryDirectoryPath = 'playlists/registry/';
const siQueryStart = '?si='; const siQueryStart = '?si=';
const pull_number = context.payload.number; const pull_number = payload.number;
const workingRepo = { const workingRepo = {
owner: context.payload.repository.owner.login, owner: payload.repository.owner.login,
repo: context.payload.repository.name repo: payload.repository.name
}; };
const repoAllowlist = [ const repoAllowlist = [
@ -30,14 +32,14 @@ const bot: ApplicationFunction = (app) => {
event: ReviewEvent event: ReviewEvent
) => { ) => {
if (review_id) { if (review_id) {
await context.octokit.pulls.updateReview({ await octokit.pulls.updateReview({
...workingRepo, ...workingRepo,
pull_number, pull_number,
review_id, review_id,
body body
}); });
} else { } else {
await context.octokit.pulls.createReview({ await octokit.pulls.createReview({
...workingRepo, ...workingRepo,
pull_number, pull_number,
event, event,
@ -54,7 +56,7 @@ const bot: ApplicationFunction = (app) => {
if (!isAllowlistedRepo) return; if (!isAllowlistedRepo) return;
const { data: prFiles } = await context.octokit.pulls.listFiles({ const { data: prFiles } = await octokit.pulls.listFiles({
...workingRepo, ...workingRepo,
pull_number pull_number
}); });
@ -69,18 +71,22 @@ const bot: ApplicationFunction = (app) => {
const playlistLookupResults = await Promise.all( const playlistLookupResults = await Promise.all(
filesToVerify.map(async ({ filename }) => { filesToVerify.map(async ({ filename }) => {
const filenameWithoutPath = removePathFromFilename(filename); const filenameWithoutPath = removePathFromFilename(filename);
const url = `https://open.spotify.com/playlist/${filenameWithoutPath}`;
const expectedStatusCodes = [200, 404]; const url = getPlaylistIdFromUrl(filename)
? filename
: `https://open.spotify.com/playlist/${filenameWithoutPath}`;
const spotifyResponse = await fetch(url); const spotifyResponse = await fetch(url);
const expectedStatusCodes = [200, 404];
if (!expectedStatusCodes.includes(spotifyResponse.status)) if (!expectedStatusCodes.includes(spotifyResponse.status)) {
throw new Error( throw new Error(
`${spotifyResponse.url} responded with code ${spotifyResponse.status}` `${spotifyResponse.url} responded with ${spotifyResponse.status}`
); );
}
const found = spotifyResponse.status === 200; const found = spotifyResponse.status === 200;
let info: string | null = null; let info = '';
if (found) { if (found) {
const html = await spotifyResponse.text(); const html = await spotifyResponse.text();
@ -105,15 +111,17 @@ const bot: ApplicationFunction = (app) => {
({ found, filename }) => found && !filename.includes(siQueryStart) ({ found, filename }) => found && !filename.includes(siQueryStart)
); );
const entriesWithSiQuery = playlistLookupResults.filter( const entriesToRename = playlistLookupResults.filter(
({ found, filename }) => found && filename.includes(siQueryStart) ({ found, filename }) =>
found &&
(filename.includes(siQueryStart) || getPlaylistIdFromUrl(filename))
); );
const notFoundPlaylists = playlistLookupResults.filter( const notFoundPlaylists = playlistLookupResults.filter(
({ found }) => !found ({ found }) => !found
); );
const { data: priorReviews } = await context.octokit.pulls.listReviews({ const { data: priorReviews } = await octokit.pulls.listReviews({
...workingRepo, ...workingRepo,
pull_number pull_number
}); });
@ -134,21 +142,15 @@ const bot: ApplicationFunction = (app) => {
identifiedPlaylistsText = `### ✅ These playlists have been indentified:\n${playlistLinks}`; identifiedPlaylistsText = `### ✅ These playlists have been indentified:\n${playlistLinks}`;
} }
if (notFoundPlaylists.length > 0) { if (entriesToRename.length > 0) {
const renameList = notFoundPlaylists const renameList = entriesToRename
.map(({ filename }) => `- ${filename}`)
.join('\n');
successText = '';
reviewEvent = 'REQUEST_CHANGES';
notFoundText = `### ❌ Playlists for these entries don't exist:\n${renameList}`;
}
if (entriesWithSiQuery.length > 0) {
const renameList = entriesWithSiQuery
.map(({ filename }) => { .map(({ filename }) => {
const playlistIdFromPossibleUrl = getPlaylistIdFromUrl(filename);
const filenameWithoutPath = removePathFromFilename(filename); const filenameWithoutPath = removePathFromFilename(filename);
const [targetFilename] = filenameWithoutPath.split(siQueryStart);
const targetFilename =
playlistIdFromPossibleUrl ||
filenameWithoutPath.replace(siQueryStart, '');
return `- From ${filenameWithoutPath} to **${targetFilename}**`; return `- From ${filenameWithoutPath} to **${targetFilename}**`;
}) })
@ -159,6 +161,16 @@ const bot: ApplicationFunction = (app) => {
renameRequiredText = `### ⚠️ These entries have to be renamed:\n${renameList}`; renameRequiredText = `### ⚠️ These entries have to be renamed:\n${renameList}`;
} }
if (notFoundPlaylists.length > 0) {
const renameList = notFoundPlaylists
.map(({ filename }) => `- ${filename}`)
.join('\n');
successText = '';
reviewEvent = 'REQUEST_CHANGES';
notFoundText = `### ❌ Playlists for these entries don't exist:\n${renameList}`;
}
const reviewBody = [ const reviewBody = [
identifiedPlaylistsText, identifiedPlaylistsText,
renameRequiredText, renameRequiredText,
@ -172,7 +184,7 @@ const bot: ApplicationFunction = (app) => {
} catch (error) { } catch (error) {
console.error(error); console.error(error);
await context.octokit.pulls.createReview({ await octokit.pulls.createReview({
...workingRepo, ...workingRepo,
pull_number, pull_number,
event: 'COMMENT', event: 'COMMENT',
@ -182,5 +194,3 @@ const bot: ApplicationFunction = (app) => {
} }
); );
}; };
export = bot;

19
getPlaylistIdFromUrl.ts Normal file
View File

@ -0,0 +1,19 @@
export const getPlaylistIdFromUrl = (url: string) => {
try {
const urlObject = new URL(url);
const [collectionName, playlistId] = urlObject.pathname
.split('/')
.filter(Boolean);
const isValidPlaylistUrl =
urlObject.hostname === 'open.spotify.com' &&
collectionName === 'playlist' &&
playlistId;
if (!isValidPlaylistUrl) return null;
return playlistId;
} catch {
return null;
}
};

2
run.ts
View File

@ -1,4 +1,4 @@
import { run } from 'probot'; import { run } from 'probot';
import bot from './bot'; import { bot } from './bot';
run(bot); run(bot);