Public: anyone can read this; the crew can change it.
A repository's danger-zone labels live in one module, read by Settings and search #5721
Check · Review · Land · Live done — Landed and live in production.
mason/forge:mason/5657-a-repository-s-danger-zone-labels-live-i → main 1 commit fork
4 files
changed +47
−6
src/web/danger_words.ts added +11
| @@ -0,0 +1,11 @@ | ||
| 1 | /** | |
| 2 | * The words on a repository's danger zone: the two acts that cannot be | |
| 3 | * undone. Settings shows them as headings and buttons, and search's | |
| 4 | * confirmation names the same act, so each sentence is said here once. | |
| 5 | */ | |
| 6 | ||
| 7 | /** Stops production and every preview and removes their data. */ | |
| 8 | export const STOP_APP = "Stop running as an app"; | |
| 9 | ||
| 10 | /** Deletes the repository, its issues, pulls and git objects. */ | |
| 11 | export const DELETE_REPO = "Delete this repository"; |
src/web/pages/search.ts +3 −2
| @@ -30,6 +30,7 @@ import type { Ctx } from "../../http/context.ts"; | ||
| 30 | 30 | import { notePhase, takeSessionTime, timePhase, timePhaseAsync } from "../../core/route_timing.ts"; |
| 31 | 31 | import { notFoundPage, pageHead } from "../chrome.ts"; |
| 32 | 32 | import { html, when, type Html } from "../html.ts"; |
| 33 | import { DELETE_REPO, STOP_APP } from "../danger_words.ts"; | |
| 33 | 34 | import { icon } from "../icons.ts"; |
| 34 | 35 | import { page } from "../layout.ts"; |
| 35 | 36 | import { browseBase } from "../scope.ts"; |
| @@ -518,7 +519,7 @@ const DESTRUCTIVE: { | ||
| 518 | 519 | { |
| 519 | 520 | pattern: /^\/([^/]+)\/([^/]+)\/settings\/delete$/, |
| 520 | 521 | describe: (m) => ({ |
| 521 | verb: "Delete this repository", | |
| 522 | verb: DELETE_REPO, | |
| 522 | 523 | title: `Delete ${m[1]}/${m[2]}?`, |
| 523 | 524 | detail: |
| 524 | 525 | "Every issue, pull request, comment and webhook goes with it, and the git objects are removed from disk. Forks are detached first and keep working. This cannot be undone.", |
| @@ -638,7 +639,7 @@ const DESTRUCTIVE: { | ||
| 638 | 639 | { |
| 639 | 640 | pattern: /^\/([^/]+)\/([^/]+)\/settings\/app\/disable$/, |
| 640 | 641 | describe: (m) => ({ |
| 641 | verb: "Stop running as an app", | |
| 642 | verb: STOP_APP, | |
| 642 | 643 | title: `Stop running ${m[1]}/${m[2]} as an app?`, |
| 643 | 644 | detail: |
| 644 | 645 | "Production and every preview stop, and their data directories and checkouts are removed from disk. The repository and its code are untouched. Run it as an app again later and it starts from empty data.", |
src/web/pages/settings.ts +5 −4
| @@ -96,6 +96,7 @@ import { pageTitle } from "../studio/nouns.ts"; | ||
| 96 | 96 | import { appHref } from "./apps.ts"; |
| 97 | 97 | import { field, readForm, type Form } from "../form.ts"; |
| 98 | 98 | import { html, when, type Html } from "../html.ts"; |
| 99 | import { DELETE_REPO, STOP_APP } from "../danger_words.ts"; | |
| 99 | 100 | import { icon } from "../icons.ts"; |
| 100 | 101 | import { flashTo, page, redirect } from "../layout.ts"; |
| 101 | 102 | import type { Flash } from "../flash.ts"; |
| @@ -733,13 +734,13 @@ function appSettingsView( | ||
| 733 | 734 | <div class="form-actions"> |
| 734 | 735 | ${confirmAction({ |
| 735 | 736 | action: `${base}/settings/app/disable`, |
| 736 | verb: "Stop running as an app", | |
| 737 | verb: STOP_APP, | |
| 737 | 738 | })} |
| 738 | 739 | </div>`, |
| 739 | 740 | { |
| 740 | 741 | tone: "danger", |
| 741 | 742 | head: html`${icon("alert")} |
| 742 | <h2>Stop running as an app</h2>`, | |
| 743 | <h2>${STOP_APP}</h2>`, | |
| 743 | 744 | }, |
| 744 | 745 | )} |
| 745 | 746 | </div>`; |
| @@ -2081,13 +2082,13 @@ export function dangerPage(ctx: Ctx): Response { | ||
| 2081 | 2082 | <div class="form-actions"> |
| 2082 | 2083 | ${confirmAction({ |
| 2083 | 2084 | action: `${base}/settings/delete`, |
| 2084 | verb: "Delete this repository", | |
| 2085 | verb: DELETE_REPO, | |
| 2085 | 2086 | })} |
| 2086 | 2087 | </div>`, |
| 2087 | 2088 | { |
| 2088 | 2089 | tone: "danger", |
| 2089 | 2090 | head: html`${icon("alert")} |
| 2090 | <h2>Delete this repository</h2>`, | |
| 2091 | <h2>${DELETE_REPO}</h2>`, | |
| 2091 | 2092 | }, |
| 2092 | 2093 | ); |
| 2093 | 2094 | const response = settingsPageResponse(ctx, repo, "danger", "Danger zone", body); |
test/danger-words.test.ts added +28
| @@ -0,0 +1,28 @@ | ||
| 1 | /** | |
| 2 | * A repository's danger-zone labels are said once, in src/web/danger_words.ts | |
| 3 | * (#5657). The pages that show them import them; a copy typed back into one | |
| 4 | * of them fails here. | |
| 5 | */ | |
| 6 | ||
| 7 | import { expect, test } from "bun:test"; | |
| 8 | import { readFileSync } from "node:fs"; | |
| 9 | import { join } from "node:path"; | |
| 10 | import { DELETE_REPO, STOP_APP } from "../src/web/danger_words.ts"; | |
| 11 | ||
| 12 | const root = join(import.meta.dir, ".."); | |
| 13 | const readers = ["src/web/pages/settings.ts", "src/web/pages/search.ts"]; | |
| 14 | ||
| 15 | test("the module says each sentence as it always read", () => { | |
| 16 | expect(STOP_APP).toBe("Stop running as an app"); | |
| 17 | expect(DELETE_REPO).toBe("Delete this repository"); | |
| 18 | }); | |
| 19 | ||
| 20 | test("no file that shows them types a sentence out again", () => { | |
| 21 | for (const file of readers) { | |
| 22 | const text = readFileSync(join(root, file), "utf8"); | |
| 23 | expect(text).toContain('from "../danger_words.ts"'); | |
| 24 | for (const sentence of [STOP_APP, DELETE_REPO]) { | |
| 25 | expect({ file, has: text.includes(sentence) }).toEqual({ file, has: false }); | |
| 26 | } | |
| 27 | } | |
| 28 | }); |
new above
offline