g1t/apps/web/app/lib/packages.ts

209 lines8,757 bytesCodeBlame

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member1/**
2 * Words and commands for the Packages pages: sizes, digests, and how each
3 * registry's own tool logs in and installs.
4 */
5import type { Ecosystem, PackageSummary } from "@g1t/contracts";
6
7/** What each registry is called on the pages. */
8export const ECOSYSTEM_LABEL: Record<Ecosystem, string> = {
9 container: "Container",
10 npm: "npm",
11 composer: "Composer",
12 cargo: "Cargo",
13 go: "Go",
Merge branch 'worktree-agent-a6a121745e81f639f'14 maven: "Maven",
15 nuget: "NuGet",
16 rubygems: "RubyGems",
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member17};
18
Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start19/**
20 * Each registry as the Packages page introduces it: what it is for, the
21 * command that starts one, its guide, and whether it is open yet.
22 */
23export const REGISTRIES: { ecosystem: Ecosystem; blurb: string; start: string; guide: string; ready: boolean }[] = [
24 {
25 ecosystem: "container",
26 blurb: "Docker and OCI images, signatures and attestations, pulled by anyone the image's repository lets read it.",
27 start: "docker push g1t.sh/<workspace>/<name>:<tag>",
28 guide: "/guides/containers/",
29 ready: true,
30 },
31 {
32 ecosystem: "npm",
33 blurb: "JavaScript and TypeScript packages under the workspace's scope, installed with npm, pnpm or yarn.",
34 start: "npm publish",
35 guide: "/guides/npm/",
36 ready: true,
37 },
38 {
39 ecosystem: "composer",
40 blurb: "PHP packages straight from the workspace's repositories: each tag is a version, nothing to upload.",
41 start: "git tag v1.0.0 && git push --tags",
42 guide: "/guides/composer/",
43 ready: true,
44 },
45 {
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version46 ecosystem: "cargo",
47 blurb: "Rust crates in a sparse registry of the workspace's own, published with cargo publish and added with cargo add.",
48 start: "cargo publish --registry <workspace>",
49 guide: "/guides/cargo/",
50 ready: true,
51 },
52 {
Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start53 ecosystem: "go",
54 blurb: "Go modules fetched from the repositories themselves with go get, private ones with a token.",
55 start: "go get g1t.sh/<workspace>/<repo>",
56 guide: "/guides/go/",
57 ready: true,
58 },
Merge branch 'worktree-agent-a6a121745e81f639f'59 {
60 ecosystem: "maven",
61 blurb: "Java and Kotlin libraries in a Maven repository of the workspace's own, deployed with Maven or Gradle, SNAPSHOTs included.",
62 start: "mvn deploy",
63 guide: "/guides/maven/",
64 ready: true,
65 },
66 {
67 ecosystem: "nuget",
68 blurb: ".NET packages in a NuGet feed of the workspace's own, pushed with dotnet nuget push and restored with dotnet.",
69 start: "dotnet nuget push <package>.nupkg --source <workspace>",
70 guide: "/guides/nuget/",
71 ready: true,
72 },
73 {
74 ecosystem: "rubygems",
75 blurb: "Ruby gems in a registry of the workspace's own, pushed with gem push and installed with Bundler.",
76 start: "gem push <gem>.gem --host https://g1t.sh/-/rubygems/<workspace>",
77 guide: "/guides/rubygems/",
78 ready: true,
79 },
Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start80];
81
82export type PackageSort = "updated" | "downloads" | "name";
83export type VisibilityFilter = "all" | "public" | "private";
84
85export const SORT_LABEL: Record<PackageSort, string> = {
86 updated: "Recently updated",
87 downloads: "Most downloads",
88 name: "Name",
89};
90
91export const VISIBILITY_LABEL: Record<VisibilityFilter, string> = { all: "All", public: "Public", private: "Private" };
92
93/** The packages to list: those of the visibility asked for, in the order asked for. */
94export function arrange<T extends Pick<PackageSummary, "visibility" | "updated_at" | "downloads" | "name">>(
95 list: T[],
96 visibility: VisibilityFilter,
97 sort: PackageSort,
98): T[] {
99 const shown = visibility === "all" ? [...list] : list.filter((pkg) => pkg.visibility === visibility);
100 const byName = (a: T, b: T) => a.name.localeCompare(b.name);
101 if (sort === "name") return shown.sort(byName);
102 if (sort === "downloads") return shown.sort((a, b) => b.downloads - a.downloads || byName(a, b));
103 return shown.sort((a, b) => b.updated_at.localeCompare(a.updated_at) || byName(a, b));
104}
105
106/** "940", "16.1k", "2.3M": download counts, short. */
107export function shortCount(n: number): string {
108 if (n >= 1_000_000) return `${Number((n / 1_000_000).toFixed(1))}M`;
109 if (n >= 1_000) return `${Number((n / 1_000).toFixed(1))}k`;
110 return String(Math.max(0, Math.round(n)));
111}
112
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member113/** "0 B", "812 KB", "12.3 MB", "1.4 GB", in powers of 1,000 as registries show them. */
114export function formatBytes(bytes: number): string {
115 if (!Number.isFinite(bytes) || bytes <= 0) return "0 B";
116 const units = ["B", "KB", "MB", "GB", "TB"];
117 let value = bytes;
118 let unit = 0;
119 while (value >= 1000 && unit < units.length - 1) {
120 value /= 1000;
121 unit++;
122 }
123 const shown = unit === 0 || value >= 100 ? Math.round(value) : Number(value.toFixed(1));
124 return `${shown} ${units[unit]}`;
125}
126
127/** `sha256:3f2a…` shortened to `3f2a9c1b7d0e` for lists. */
128export function shortDigest(digest: string): string {
129 return digest.replace(/^sha256:/, "").slice(0, 12);
130}
131
132/** The host packages are published to and installed from. */
133export function registryHost(address: string): string {
134 return address.split("/")[0] || "g1t.sh";
135}
136
137/**
138 * How to log in and install `pkg` at `version` (a tag or version) with its
139 * tool. `you` stands in the username a login takes.
140 */
npm on g1t.sh: publish and install @<workspace>/<name> with the npm CLI and a g1t token141export function installCommands(
142 pkg: Pick<PackageSummary, "ecosystem" | "address" | "name" | "workspace">,
143 version: string | null,
144 you: string,
145): { login: string; install: string; registry?: string } {
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member146 const host = registryHost(pkg.address);
147 switch (pkg.ecosystem) {
148 case "container":
149 return {
150 login: `docker login ${host} -u ${you}`,
151 install: `docker pull ${pkg.address}${version ? `:${version}` : ""}`,
152 };
153 case "npm":
npm on g1t.sh: publish and install @<workspace>/<name> with the npm CLI and a g1t token154 // The scope's registry (in .npmrc, needed for any install), then the
155 // token a private package also needs.
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member156 return {
npm on g1t.sh: publish and install @<workspace>/<name> with the npm CLI and a g1t token157 registry: `npm config set @${pkg.workspace}:registry=https://${host}/-/npm/`,
158 login: `npm config set //${host}/-/npm/:_authToken=YOUR_TOKEN`,
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member159 install: `npm install @${pkg.workspace}/${pkg.name}${version ? `@${version}` : ""}`,
160 };
161 case "composer":
Composer from the workspace's own repositories, and go get from g1t.sh162 // The workspace's repository (in composer.json, needed for any
163 // install), then the credentials a private package also needs.
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member164 return {
Composer from the workspace's own repositories, and go get from g1t.sh165 registry: `composer config repositories.${pkg.workspace} composer https://${host}/-/composer/${pkg.workspace}/`,
166 login: `composer config --global --auth http-basic.${host} ${you} YOUR_TOKEN`,
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member167 install: `composer require ${pkg.name}${version ? `:${version}` : ""}`,
168 };
169 case "cargo":
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version170 // The workspace's registry (in .cargo/config.toml, needed for any
171 // install), then the token a private crate also needs, which cargo
172 // login asks for.
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member173 return {
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version174 registry: `mkdir -p .cargo && printf '[registries.${pkg.workspace}]\\nindex = "sparse+https://${host}/-/cargo/${pkg.workspace}/index/"\\ncredential-provider = "cargo:token"\\n' >> .cargo/config.toml`,
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member175 login: `cargo login --registry ${pkg.workspace}`,
Cargo on g1t.sh: a sparse registry per workspace, cargo publish/add/yank/search with a g1t token; npm rows show their version176 install: `cargo add ${pkg.name}${version ? `@${version}` : ""} --registry ${pkg.workspace}`,
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member177 };
178 case "go":
179 return {
180 login: `go env -w GOPRIVATE=${host}/${pkg.workspace}`,
181 install: `go get ${pkg.address}${version ? `@${version}` : ""}`,
182 };
Merge branch 'worktree-agent-a6a121745e81f639f'183 case "maven": {
184 // Maven takes the repository with the artifact; its credentials are a
185 // <server> of the same id in ~/.m2/settings.xml, written here only
186 // when there is no settings.xml yet.
187 const repository = `https://${host}/-/maven/${pkg.workspace}/`;
188 return {
189 login: `mkdir -p ~/.m2 && [ ! -e ~/.m2/settings.xml ] && printf '<settings><servers><server><id>${pkg.workspace}</id><username>${you}</username><password>YOUR_TOKEN</password></server></servers></settings>\\n' > ~/.m2/settings.xml`,
190 install: `mvn dependency:get -Dartifact=${pkg.name}${version ? `:${version}` : ":LATEST"} -DremoteRepositories=${pkg.workspace}::default::${repository}`,
191 };
192 }
193 case "nuget":
194 // The workspace's feed as a named source (needed for any install),
195 // then the credentials a private package also needs.
196 return {
197 registry: `dotnet nuget add source https://${host}/-/nuget/${pkg.workspace}/v3/index.json --name ${pkg.workspace}`,
198 login: `dotnet nuget update source ${pkg.workspace} --username ${you} --password YOUR_TOKEN --store-password-in-clear-text`,
199 install: `dotnet add package ${pkg.name}${version ? ` --version ${version}` : ""} --source https://${host}/-/nuget/${pkg.workspace}/v3/index.json`,
200 };
201 case "rubygems": {
202 const source = `https://${host}/-/rubygems/${pkg.workspace}/`;
203 return {
204 login: `bundle config set --global ${source} ${you}:YOUR_TOKEN`,
205 install: `bundle add ${pkg.name}${version ? ` --version ${version}` : ""} --source ${source}`,
206 };
207 }
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member208 }
209}

This file's history is long; its oldest lines are credited to the oldest commit read.