Skip to content

Commit 9fac94a

Browse files
feat(backend): add a packages discoverer to get packages from repos
1 parent eeea23e commit 9fac94a

File tree

4 files changed

+114
-5
lines changed

4 files changed

+114
-5
lines changed

src/lib/repositories.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,28 @@ type Entries<T> = {
128128
export const repositories = Object.entries(repos) as unknown as Entries<typeof repos>;
129129

130130
/**
131-
* Get all a record of all GitHub repositories
132-
* from the collection.
131+
* Get a record of all GitHub repositories
132+
* from the collection, as an "owner-to-repo-names"
133+
* map.
133134
*/
134135
export const githubRepos = {
135136
sveltejs: [...new Set(repositories.flatMap(([, { repos }]) => repos.map(r => r.repoName)))]
136137
};
138+
139+
/**
140+
* Get a list of objects containing
141+
* the repo owner, the repo name, and the
142+
* associated transformation function to
143+
* transform a release tag name into a package
144+
* name.
145+
*/
146+
export const transformationRepos = repositories.flatMap(([, { repos }]) =>
147+
repos.map(r => ({
148+
owner: "sveltejs",
149+
repoName: r.repoName,
150+
tagToName: (tag: string) => {
151+
const [name] = r.metadataFromTag(tag);
152+
return name;
153+
}
154+
}))
155+
);

src/lib/server/github-cache.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,13 @@ export class SvelteGitHubCache {
214214
async deleteEntry(repo: string) {
215215
await this.#cache.deleteEntry(this.#owner, repo);
216216
}
217+
218+
/**
219+
* A convenience getter for the underlying cache
220+
*/
221+
get cache() {
222+
return this.#cache;
223+
}
217224
}
218225

219226
export const svelteGitHubCache = new SvelteGitHubCache(
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { GitHubCache, svelteGitHubCache } from "./github-cache";
2+
import { transformationRepos } from "$lib/repositories";
3+
4+
export class PackageDiscoverer {
5+
readonly #cache: GitHubCache;
6+
readonly #repos: { owner: string; repoName: string; tagToName: (name: string) => string }[];
7+
#packages: { owner: string; repoName: string; packages: string[] }[] = [];
8+
9+
constructor(
10+
cache: GitHubCache,
11+
repos: { owner: string; repoName: string; tagToName: (name: string) => string }[]
12+
) {
13+
this.#cache = cache;
14+
this.#repos = repos;
15+
}
16+
17+
/**
18+
* Discover the packages from the passed releases
19+
* and replaces/adds it to the #packages array from
20+
* the owner and repo name.
21+
*
22+
* @param owner the owner of the repository to use to populate the array
23+
* @param repoName the name of the repository to use to populate the array
24+
* @param releases the releases to populate the array from
25+
*/
26+
async discoverReleases(
27+
owner: string,
28+
repoName: string,
29+
releases: Awaited<ReturnType<InstanceType<typeof GitHubCache>["getReleases"]>>
30+
) {
31+
// 1. Find the transformation function in the existing repos, exit otherwise
32+
const repo = this.#repos.find(({ owner: o, repoName: n }) => o == owner && repoName == n);
33+
if (!repo) return;
34+
// 2. Compute the unique packages
35+
const uniquePackages = [...new Set(releases.map(({ tag_name }) => repo.tagToName(tag_name)))];
36+
// 3. Replace or add the value in the array
37+
for (const [i, { owner: o, repoName: n }] of this.#packages.entries()) {
38+
if (o === owner && repoName == n) {
39+
this.#packages[i]!.packages = uniquePackages;
40+
return;
41+
}
42+
}
43+
this.#packages.push({ owner, repoName, packages: uniquePackages });
44+
}
45+
46+
/**
47+
* A processing-heavy function that discovers all the
48+
* packages for the given #repos.
49+
* Populates the result into #packages.
50+
*/
51+
async discoverAll() {
52+
this.#packages = await Promise.all(
53+
this.#repos.map(async ({ owner, repoName, tagToName }) => {
54+
const releases = await this.#cache.getReleases(owner, repoName);
55+
const packages = [...new Set(releases.map(({ tag_name }) => tagToName(tag_name)))];
56+
return { owner, repoName, packages };
57+
})
58+
);
59+
}
60+
61+
/**
62+
* Returns the saved #packages if they're not empty,
63+
* otherwise calls {@link discoverAll} then returns the
64+
* #packages.
65+
*
66+
* @returns all the discovered packages per repo name
67+
*/
68+
async getOrDiscover() {
69+
if (this.#packages.length) {
70+
return this.#packages;
71+
}
72+
await this.discoverAll();
73+
return this.#packages;
74+
}
75+
}
76+
77+
export const discoverer = new PackageDiscoverer(svelteGitHubCache.cache, transformationRepos);

src/routes/cron/+server.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
import { error, type RequestHandler } from "@sveltejs/kit";
22
import { CRON_SECRET } from "$env/static/private";
33
import { svelteGitHubCache } from "$lib/server/github-cache";
4+
import { discoverer } from "$lib/server/package-discoverer";
45
import { githubRepos } from "$lib/repositories";
56

67
export const GET: RequestHandler = async ({ request }) => {
78
if (request.headers.get("Authorization") !== `Bearer ${CRON_SECRET}`) {
89
error(401);
910
}
1011

11-
for (const repo of githubRepos.sveltejs) {
12-
await svelteGitHubCache.fetchAndCacheReleases(repo);
13-
}
12+
await Promise.all(
13+
Object.entries(githubRepos).flatMap(([owner, repos]) =>
14+
repos.map(async repo => {
15+
const releases = await svelteGitHubCache.fetchAndCacheReleases(repo);
16+
await discoverer.discoverReleases(owner, repo, releases);
17+
})
18+
)
19+
);
1420

1521
return new Response();
1622
};

0 commit comments

Comments
 (0)