Skip to content

Commit

Merge remote-tracking branch 'origin/main' into claude/cool-newton-7nm8r9

Claudeagentcommitted Parentsff7e62b5d6cbc4Browse files
12 files+110−310/12 viewed
+25−5
6767 needs: version
6868 # The larger machine: linking Tauri on the small one dies with a bus error.
6969 runs-on: g1t-4core
70− environment: production
70+ # Production's secrets, without a deployment: a release is not a deploy of
71+ # g1t.sh, and would otherwise show on its card as one in progress.
72+ environment:
73+ name: production
74+ deployment: false
7175 timeout-minutes: 60 # the most g1t's own machines run a job
7276 steps:
7377 - uses: actions/checkout@v5
114118 needs: version
115119 if: vars.DESKTOP_WINDOWS_RUNNER == 'true'
116120 runs-on: [self-hosted, windows]
117− environment: production
121+ # Production's secrets, without a deployment: a release is not a deploy of
122+ # g1t.sh, and would otherwise show on its card as one in progress.
123+ environment:
124+ name: production
125+ deployment: false
118126 timeout-minutes: 90
119127 steps:
120128 - uses: actions/checkout@v5
147155 needs: version
148156 if: vars.DESKTOP_WINDOWS_FROM_LINUX == 'true' && vars.DESKTOP_WINDOWS_RUNNER != 'true'
149157 runs-on: g1t-4core
150− environment: production
158+ # Production's secrets, without a deployment: a release is not a deploy of
159+ # g1t.sh, and would otherwise show on its card as one in progress.
160+ environment:
161+ name: production
162+ deployment: false
151163 timeout-minutes: 60 # the most g1t's own machines run a job
152164 steps:
153165 - uses: actions/checkout@v5
194206 needs: version
195207 if: vars.DESKTOP_MACOS_RUNNER == 'true'
196208 runs-on: [self-hosted, macos]
197− environment: production
209+ # Production's secrets, without a deployment: a release is not a deploy of
210+ # g1t.sh, and would otherwise show on its card as one in progress.
211+ environment:
212+ name: production
213+ deployment: false
198214 timeout-minutes: 90
199215 steps:
200216 - uses: actions/checkout@v5
226242 && contains(fromJSON('["success","skipped"]'), needs.windows_cross.result)
227243 && contains(fromJSON('["success","skipped"]'), needs.macos.result)
228244 runs-on: ubuntu-latest
229− environment: production
245+ # Production's secrets, without a deployment: a release is not a deploy of
246+ # g1t.sh, and would otherwise show on its card as one in progress.
247+ environment:
248+ name: production
249+ deployment: false
230250 timeout-minutes: 20
231251 steps:
232252 - uses: actions/checkout@v5
+5−1
4545 release:
4646 name: Build and publish to g1t.sh/downloads/mobile
4747 runs-on: ubuntu-latest
48− environment: production
48+ # Production's secrets, without a deployment: a release is not a deploy of
49+ # g1t.sh, and would otherwise show on its card as one in progress.
50+ environment:
51+ name: production
52+ deployment: false
4953 timeout-minutes: 60
5054 steps:
5155 - uses: actions/checkout@v5
+5−1
5858 # repository variable RUNNER_BASE_SELF_HOSTED is "true": without one,
5959 # each run waited in the queue for a day and a half and then failed.
6060 if: ${{ vars.RUNNER_BASE_SELF_HOSTED == 'true' }}
61− environment: production
61+ # Production's secrets, without a deployment: a release is not a deploy of
62+ # g1t.sh, and would otherwise show on its card as one in progress.
63+ environment:
64+ name: production
65+ deployment: false
6266 timeout-minutes: 60
6367 steps:
6468 - uses: actions/checkout@v5
+10−2
2929 binaries:
3030 name: Build, sign and publish
3131 runs-on: ubuntu-latest
32− environment: production
32+ # Production's secrets, without a deployment: a release is not a deploy of
33+ # g1t.sh, and would otherwise show on its card as one in progress.
34+ environment:
35+ name: production
36+ deployment: false
3337 timeout-minutes: 60
3438 steps:
3539 - uses: actions/checkout@v5
9599 # Builds for arm64 as well as amd64, which needs QEMU's emulators
96100 # registered on the machine: a self-hosted runner's, for now.
97101 runs-on: [self-hosted, docker]
98− environment: production
102+ # Production's secrets, without a deployment: a release is not a deploy of
103+ # g1t.sh, and would otherwise show on its card as one in progress.
104+ environment:
105+ name: production
106+ deployment: false
99107 timeout-minutes: 30
100108 # Its token pushes the image to g1t's registry.
101109 permissions:
+5−3
2222 | **Security policy** | A link to `SECURITY.md` at the root, or in `.g1t`, `.github` or `docs`. |
2323 | **Activity** | The repository's [activity](#activity): pushes, merges, new branches and tags. |
2424 | **Stars**, **watching** | How many people [starred](#stars) it, and how many watch all or some of its activity from the [Watch menu](/guides/notifications/). |
25−| **Releases** | How many [releases](/guides/releases/) it has and the latest, or **Create a new release** for people who can push. |
25+| **Releases** | How many [releases](/guides/releases/) it has and the latest. With none published, how many tags it has, linked to its tags, and **Create a new release** for people who can push. |
2626 | **Packages** | [Packages](/guides/packages/) linked to it, or how to publish the first. |
27−| **Contributors** | How many people and agents made it, and the most active. See [contributors](#contributors). |
27+| **Contributors** | How many people and agents made it. Up to five are listed by name with how many commits each made; more show as the most active's avatars. Each opens their card. See [contributors](#contributors). |
2828 | **Languages** | The languages it is written in, by bytes. See [languages](#languages). |
2929
3030 The license, security policy, languages and contributors are read from the
3636
3737 The bar counts the bytes of each language's files on the default branch.
3838 Programming and markup languages count; data such as JSON and YAML, and
39−prose such as Markdown, do not. Neither do:
39+prose such as Markdown, do not. TSX counts as TypeScript and JSX as
40+JavaScript. The sidebar lists every language down to a tenth of a percent,
41+the largest eight, and gathers the rest as **Other**. Neither do these count:
4042
4143 | Files | Such as |
4244 | --- | --- |
+37−5
2828
2929 import { compact, count, languageBar, licenseLabel } from "../lib/about";
3030 import { contributorPerson, profileHref, shownName } from "../lib/commit-people";
31−import { CommitAvatar } from "./commit-person";
31+import { CommitAvatar, CommitName } from "./commit-person";
3232 import { UserCard } from "./user-card";
3333 import { PackageIcon as EcosystemIcon } from "./package-icon";
3434 import { Topics } from "./topics";
3939
4040 /** How many contributors' avatars the About shows. */
4141 const AVATARS = 14;
42+/** Up to this many contributors are listed by name, one a row; more are shown as avatars. */
43+const LISTED = 5;
4244 /** How often, and how many times, it asks again while the default branch is first read. */
4345 const RETRY_MS = 3_000;
4446 const RETRIES = 6;
144146 return <p className="text-xs text-faint">Reading the default branch…</p>;
145147 }
146148
147−function Releases({ base, about, canPush }: { base: string; about: RepoAbout; canPush: boolean }) {
149+function Releases({ base, about, canPush, tags }: { base: string; about: RepoAbout; canPush: boolean; tags: number | null }) {
148150 const latest = about.latestRelease;
151+ // Tags, when no release names them yet: what is there to release from.
152+ const tagLine =
153+ tags && about.releases === 0 ? (
154+ <Link to={`${base}/tags`} className="flex items-center gap-1.5 text-sm font-medium text-fg hover:text-accent">
155+ <TagIcon size={14} className="text-muted" />
156+ {count(tags, "tag")}
157+ </Link>
158+ ) : null;
149159 return (
150160 <Section title="Releases" count={about.releases} to={`${base}/releases`}>
151161 {latest ? (
172182 </Link>
173183 ) : (
174184 <div className="text-sm text-muted">
175− <p>No releases published</p>
185+ {tagLine ?? <p>No releases published</p>}
176186 {canPush && (
177187 <Link to={`${base}/releases/new`} className="mt-1 inline-flex items-center gap-1 text-accent hover:underline">
178188 <Plus size={13} />
221231 <Reading />
222232 ) : shown.length === 0 ? (
223233 <p className="text-sm text-muted">No commits yet</p>
234+ ) : about.contributors <= LISTED ? (
235+ // A few people: each by name, as a row, with how much they did.
236+ <ul className="space-y-2">
237+ {shown.map((contributor) => {
238+ const person = contributorPerson(contributor);
239+ return (
240+ <li key={`${contributor.kind}:${contributor.username ?? contributor.name}`} className="flex min-w-0 items-center gap-2.5 text-sm">
241+ <CommitAvatar person={person} size={28} />
242+ <CommitName person={person} className="truncate font-medium text-fg" />
243+ <span className="ml-auto shrink-0 text-xs text-faint tabular-nums">{count(contributor.commits, "commit")}</span>
244+ </li>
245+ );
246+ })}
247+ </ul>
224248 ) : (
225249 <>
226250 <ul className="flex flex-wrap gap-1.5">
327351 first,
328352 canPush,
329353 packages,
354+ tags,
330355 deployments,
331356 }: {
332357 base: string;
333358 first: RepoAbout;
334359 canPush: boolean;
335360 packages: Promise<PackageSummary[] | null> | PackageSummary[] | null;
361+ tags: Promise<number | null> | number | null;
336362 deployments: ReactNode;
337363 }) {
338364 const about = useLive(base, first);
339365 return (
340366 <>
341− <Releases base={base} about={about} canPush={canPush} />
367+ <Suspense fallback={<Releases base={base} about={about} canPush={canPush} tags={null} />}>
368+ <Await resolve={tags} errorElement={<Releases base={base} about={about} canPush={canPush} tags={null} />}>
369+ {(found) => <Releases base={base} about={about} canPush={canPush} tags={found} />}
370+ </Await>
371+ </Suspense>
342372 <Suspense fallback={<SectionSkeleton title="Packages" />}>
343373 <Await resolve={packages} errorElement={<Packages packages={null} />}>
344374 {(found) => <Packages packages={found} />}
369399 watchers: Promise<number | null> | number | null;
370400 /** Packages published from it; null when they could not be read. */
371401 packages: Promise<PackageSummary[] | null> | PackageSummary[] | null;
402+ /** How many tags it has; null when they could not be counted. */
403+ tags?: Promise<number | null> | number | null;
372404 };
373405
374406 export function RepoAboutPanel({
443475 <Await resolve={data.about} errorElement={<>{deployments}</>}>
444476 {(about) =>
445477 about ? (
446− <LoadedSections base={base} first={about} canPush={canPush} packages={data.packages} deployments={deployments} />
478+ <LoadedSections base={base} first={about} canPush={canPush} packages={data.packages} tags={data.tags ?? null} deployments={deployments} />
447479 ) : (
448480 <>{deployments}</>
449481 )
+1−1
414414 repo={repo}
415415 gitRef={ref}
416416 readme={Boolean(readme)}
417− data={about ?? { about: null, watchers: null, packages: null }}
417+ data={about ?? { about: null, watchers: null, packages: null, tags: null }}
418418 canPush={canPush}
419419 homepage={homepage}
420420 deployments={<DeploymentsPanel base={base} summary={deployments} className="border-t border-line pt-4" />}
+2−1
11 /**
22 * The Files page's About, streamed in after the files: what the default
33 * branch says, stars and releases from repos; how many watch it from the
4− * inbox; packages published from it. Each is its own promise, so a slow
4+ * inbox; packages published from it; how many tags it has. Each is its own promise, so a slow
55 * one holds up only its own section, and a failure shows the section
66 * without it rather than failing the page.
77 */
2828 packages: within(
2929 packages.list(path.namespace, viewer, { repoId }).then((found): PackageSummary[] | null => (found.ok ? found.value : null)),
3030 ),
31+ tags: within(repos.tags(path, viewer).then((found) => (found.ok ? found.value.length : null))),
3132 };
3233 }
+10−3
1919 assert.equal(count(3, "watching", "watching"), "3 watching");
2020 });
2121
22−test("small languages are gathered as Other", () => {
22+test("every language down to a tenth of a percent is listed, at most eight; the rest are Other", () => {
2323 const rust = { name: "Rust", color: "#dea584", bytes: 900, percent: 90 };
2424 const shell = { name: "Shell", color: "#89e051", bytes: 95, percent: 9.5 };
25− const nix = { name: "Nix", color: "#7e7eff", bytes: 5, percent: 0.5 };
25+ const nix = { name: "Nix", color: "#7e7eff", bytes: 5, percent: 0.05 };
26+ const css = { name: "CSS", color: "#663399", bytes: 3, percent: 0.3 };
27+ assert.deepEqual(languageBar([rust, css, shell]).map((l) => l.name), ["Rust", "Shell", "CSS"], "sorted, and a 0.3% language is shown");
28+ const many = Array.from({ length: 10 }, (_, i) => ({ name: `L${i}`, color: null, bytes: 10 - i, percent: 10 - i }));
29+ const capped = languageBar(many);
30+ assert.equal(capped.length, 9);
31+ assert.equal(capped[8]!.name, "Other");
32+ assert.equal(capped[8]!.percent, 3);
2633 assert.deepEqual(languageBar([rust, shell]), [rust, shell]);
2734 const bar = languageBar([rust, shell, nix]);
2835 assert.equal(bar.length, 3);
2936 assert.equal(bar[2].name, "Other");
30− assert.equal(bar[2].percent, 0.5);
37+ assert.equal(bar[2].percent, 0.1);
3138 assert.deepEqual(languageBar([rust, { ...nix, percent: 0 }]), [rust], "nothing to show for nothing");
3239 assert.deepEqual(languageBar([]), []);
3340 });
+5−3
3131 }
3232
3333 /** The language bar's pieces: the largest, then the rest under 1% as Other. */
34−export function languageBar(languages: LanguageShare[], least = 1): LanguageShare[] {
35− const shown = languages.filter((language) => language.percent >= least);
36− const rest = languages.filter((language) => language.percent < least);
34+export function languageBar(languages: LanguageShare[], least = 0.1, most = 8): LanguageShare[] {
35+ // Every language down to a tenth of a percent, the largest `most` of them; the rest as Other.
36+ const sorted = [...languages].sort((a, b) => b.percent - a.percent);
37+ const shown = sorted.filter((language, index) => language.percent >= least && index < most);
38+ const rest = sorted.filter((language) => !shown.includes(language));
3739 if (rest.length === 0) return shown;
3840 const bytes = rest.reduce((sum, language) => sum + language.bytes, 0);
3941 const percent = Math.round(rest.reduce((sum, language) => sum + language.percent, 0) * 10) / 10;
+3−4
110110 lang("Svelte", "#ff3e00", Markup),
111111 lang("Swift", "#F05138", Programming),
112112 lang("TOML", "#9c4221", Data),
113− lang("TSX", "#3178c6", Programming),
114113 lang("Twig", "#c1d026", Markup),
115114 lang("TypeScript", "#3178c6", Programming),
116115 lang("Vim Script", "#199f4b", Programming),
214213 "svelte" => "Svelte",
215214 "swift" => "Swift",
216215 "toml" => "TOML",
217− "tsx" => "TSX",
218216 "twig" => "Twig",
219− "ts" | "mts" | "cts" => "TypeScript",
217+ // TSX is TypeScript, as JSX is JavaScript: one language on the bar.
218+ "ts" | "tsx" | "mts" | "cts" => "TypeScript",
220219 "vim" => "Vim Script",
221220 "vb" => "Visual Basic .NET",
222221 "vue" => "Vue",
431430 #[test]
432431 fn files_are_known_by_extension_and_name() {
433432 assert_eq!(name("src/main.rs"), Some("Rust"));
434− assert_eq!(name("app/routes/home.tsx"), Some("TSX"));
433+ assert_eq!(name("app/routes/home.tsx"), Some("TypeScript"));
435434 assert_eq!(name("lib/x.mts"), Some("TypeScript"));
436435 assert_eq!(name("Dockerfile"), Some("Dockerfile"));
437436 assert_eq!(name("deploy/Dockerfile.prod"), Some("Dockerfile"));
+2−2
4646 /// Where a security policy may be.
4747 const POLICY_DIRS: [&str; 4] = ["", ".g1t/", ".github/", "docs/"];
4848
49−/// How the About is worked out (2: coding agents are contributors of their own); a row kept by an older version is worked
49+/// How the About is worked out (2: coding agents are contributors of their own; 3: TSX counts as TypeScript); a row kept by an older version is worked
5050 /// out again. 1: g1t's older commit addresses count as g1t.
51−pub const STATS_VERSION: f64 = 2.0;
51+pub const STATS_VERSION: f64 = 3.0;
5252
5353 /// What `repo_stats` keeps for the About, without the full contributors.
5454 #[derive(Debug, Default, Deserialize)]