Basti Buck

Basti Buck

// Name: Search starred GitHub repos
// Description: Search and filter starred GitHub repositories
// Author: Basti Buck
// Twitter: @bastibuck
import "@johnlindquist/kit";
import { Choices } from "@johnlindquist/kit";
const CACHE_SCRIPT_NAME = "cache-starred-repos.ts";
const { isValidCron }: typeof import("cron-validator") = await npm(
"cron-validator"
);
// setup caching script if not installed
const installedScripts = await readdir(home(kenvPath("/scripts")));
if (!installedScripts.includes(CACHE_SCRIPT_NAME)) {
const cronSchedule = await arg({
placeholder:
"Choose schedule or define your own schedule using CRON syntax",
strict: false,
choices: getCronChoices,
validate: validateCRON,
});
await appendFile(
home(kenvPath("/scripts"), CACHE_SCRIPT_NAME),
buildCacheScript({ cronSchedule })
);
await div(
`<div class='p-12 text-lg text-center'>
<h2 class='mb-8'>
Looks like this is your first run.
</h2>
<h3>I've setup a caching script for you.</h3>
<p class='mb-20'>
This script will run on the defined schedule and cache your starred repos for faster searching. If you need to run the caching manually you can do so from Kit.
</p>
<p>
To initialize your cache now, run <code class='text-yellow-400'>Cache starred GitHub repos</code>.
</p>
</div>`
);
exit(0);
}
// show filterable starred repos
let { cachedStars } = await db("starred-repos", { cachedStars: [] });
const star = await arg(
"Search starred repos...",
cachedStars.map((star) => {
return {
name: star.full_name,
value: star.html_url,
description: `${star.stargazers_count} ${
star.description ? " | " + star.description : ""
}`,
icon: star.owner.avatar_url,
};
})
);
browse(star);
// helper functions
function getCronChoices(): Choices<string> {
return [
{
name: "0 12 * * *",
value: "0 12 * * *",
description: "Daily at noon",
},
{
name: "0 8 * * *",
value: "0 8 * * *",
description: "Daily at 8:00 am",
},
{
name: "0 8 * * 1",
value: "0 8 * * 1",
description: "Weekly on Monday morning",
},
];
}
function validateCRON(cron: string) {
return isValidCron(cron)
? true
: "<span class='text-red-500'>Invalid CRON syntax. Check <a href='https://crontab.guru/'>crontab.guru</a> for help</span>";
}
function buildCacheScript({ cronSchedule }: { cronSchedule: string }) {
return `// Name: Cache starred GitHub repos
// Description: Cache starred GitHub repositories for future operations
// Author: Basti Buck
// Twitter: @bastibuck
\/\/ Schedule: ${cronSchedule}
import "@johnlindquist/kit";
const { z }: typeof import("zod") = await npm("zod");
const { Octokit }: typeof import("octokit") = await npm("octokit");
import { Endpoints } from "@octokit/types";
const AUTH_TOKEN = await env("GITHUB_AUTH_TOKEN", {
hint: md(
\`Create a [personal access token](https://github.com/settings/tokens) with the \\\`read:user\\\` scope.\`
),
ignoreBlur: true,
secret: true,
});
const USERNAME = await env("GITHUB_USERNAME", {
hint: md(\`Your GitHub username\`),
});
type Star = Endpoints["GET /user/starred"]["response"]["data"][number];
const cachedStarSchema = z.array(
z.object({
full_name: z.string(),
html_url: z.string(),
stargazers_count: z.number(),
description: z.string().optional().nullish(),
owner: z.object({
avatar_url: z.string(),
}),
})
);
const octokit = new Octokit({
auth: AUTH_TOKEN,
});
let { cachedStars, write } = await db("starred-repos", {
cachedStars: [],
});
const stars = await octokit.paginate<Star>(\`GET /users/\${USERNAME}/starred\`);
_.remove(cachedStars, () => true);
cachedStars.push(...cachedStarSchema.parse(stars));
await write();
`;
}