Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
shell: bash
run: |
repo_name="${GITHUB_REPOSITORY#*/}"
if [[ "$repo_name" == *.github.io ]]; then
if [[ -f site/CNAME || "$repo_name" == *.github.io ]]; then
echo 'SITE_BASE_PATH=' >> "$GITHUB_ENV"
else
echo "SITE_BASE_PATH=/$repo_name" >> "$GITHUB_ENV"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ We are mission-driven: a project does not need to recover its build or generatio
cost. AI-assisted development is a practical option, never a participation requirement.
Humans initiate any model work locally, with the tools and models they choose.

- [Website](https://508-dev.github.io/genairosity/)
- [Website](https://genairosity.com/)
- [Suggest a target](https://github.com/508-dev/genairosity/issues/new?template=nomination.yml)
- [Investigation shortlist](https://github.com/508-dev/genairosity/issues?q=is%3Aissue+label%3Anomination)
- [Contribute](CONTRIBUTING.md) · [Establish a project](docs/project-setup.md)
Expand Down
18 changes: 16 additions & 2 deletions docs/deployment.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
# GitHub Pages publishing

Production: https://508-dev.github.io/genairosity/
Production: https://genairosity.com/

The `Checks and Pages` GitHub Actions workflow checks every pull request. Pushes to
`main` and manual dispatches from `main` run the same checks, build the static site,
and deploy the Pages artifact. Only the deployment job has Pages write/OIDC access.
No model calls, provider credentials, or application server are involved.

## Custom domain and link prefix

`site/CNAME` declares the custom domain and is the single source of truth for how
links are written. While it exists, the site is served at the domain root: the
workflow builds with an empty `SITE_BASE_PATH`, every generated link and asset URL is
root-relative, and the build copies `CNAME` into the artifact so an Actions deployment
does not drop the domain. The build refuses a non-empty `SITE_BASE_PATH` while a
`CNAME` is present, because that combination publishes asset URLs that 404.

Moving back to the `508-dev.github.io/genairosity/` project URL means deleting
`site/CNAME`; the workflow then restores the `/genairosity` repository prefix on its
own. Clearing the domain in Settings → Pages without deleting the file, or the
reverse, breaks one of the two.

## Initial setup (maintainer)

Review `scripts/github-setup.ts`, then run:
Expand Down Expand Up @@ -45,5 +59,5 @@ Changing a project status requires a registry PR, not an issue label alone.
## Publication verification

Confirm the workflow succeeds, the public homepage and contribution page return 200,
styles/assets load under the repository prefix, and the five candidate links point
styles/assets load at the domain root, and the five candidate links point
to real nomination issues. Check the site on desktop and a narrow viewport.
10 changes: 10 additions & 0 deletions scripts/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ function projectCard(project: Project, base: string): string {
}
export async function build(root: string, prefix = "", output = join(root, "dist")) {
const base = basePath(prefix);
const domain = (
await Bun.file(join(root, "site", "CNAME"))
.text()
.catch(() => "")
).trim();
if (domain !== "" && base !== "")
throw new Error(
`site/CNAME serves ${domain} at the domain root; SITE_BASE_PATH must be empty, not ${base}`,
);
const { projects, candidates } = loadData(root);
let home = await readFile(join(root, "site/home.html"), "utf8");
const replacements: Record<string, string> = {
Expand Down Expand Up @@ -124,6 +133,7 @@ export async function build(root: string, prefix = "", output = join(root, "dist
for (const name of ["style.css", "mark.svg", "freedom.svg"])
await cp(join(root, "site", name), join(output, "assets", name));
await Bun.write(join(output, ".nojekyll"), "");
if (domain !== "") await Bun.write(join(output, "CNAME"), `${domain}\n`);
return {
output,
pages: Object.keys(pages),
Expand Down
1 change: 1 addition & 0 deletions site/CNAME
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
genairosity.com
15 changes: 11 additions & 4 deletions tests/foundation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,19 +141,20 @@ describe("static publishing", () => {
for (const path of ["//evil.test", "/../other", 'x"bad', "/with space"])
expect(() => basePath(path)).toThrow();
});
test("builds the launch under a repository subpath with valid local targets", async () => {
test("builds the launch at the custom domain root with valid local targets", async () => {
const output = join(scratch, "site");
const result = await build(root, "/genairosity", output);
const result = await build(root, "", output);
expect(result.pages).toContain("contribute/index.html");
expect(readFileSync(join(output, "CNAME"), "utf8").trim()).toBe("genairosity.com");
const home = readFileSync(join(output, "index.html"), "utf8");
expect(home).toContain("No projects are registered yet");
expect(home).toContain("CapCut");
expect(home).toContain("FileMaker");
expect(home).not.toContain("{{");
for (const page of result.pages) {
const html = readFileSync(join(output, page), "utf8");
for (const match of html.matchAll(/(?:href|src)="(\/genairosity\/[^"#]*)(?:#[^"]*)?"/g)) {
const target = (match[1] ?? "").slice("/genairosity/".length);
for (const match of html.matchAll(/(?:href|src)="(\/[^/"#][^"#]*)?(?:#[^"]*)?"/g)) {
const target = (match[1] ?? "").slice(1);
expect(
existsSync(
join(output, target === "" || target.endsWith("/") ? `${target}index.html` : target),
Expand All @@ -162,10 +163,16 @@ describe("static publishing", () => {
}
}
});
test("refuses a subpath prefix while a custom domain is declared", async () => {
expect(build(root, "/genairosity", join(scratch, "conflict"))).rejects.toThrow(
"genairosity.com",
);
});
test("renders registered details and removes stale pages when membership changes", async () => {
const fixture = join(scratch, "registered");
mkdirSync(join(fixture, "content/projects"), { recursive: true });
cpSync(join(root, "site"), join(fixture, "site"), { recursive: true });
rmSync(join(fixture, "site/CNAME"), { force: true });
for (const name of ["contribute.md", "principles.md"])
cpSync(join(root, "content", name), join(fixture, "content", name));
writeFileSync(join(fixture, "projects.json"), JSON.stringify(registry()));
Expand Down
Loading