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
26 changes: 26 additions & 0 deletions www/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ environment variable — and the command line wins when both are supplied.
| `--github-token` | `GITHUB_TOKEN` | _unauthenticated_ | GitHub access token for the API |
| `--jsr-api` | `JSR_API` | _none_ | JSR API token; the score card is skipped without it |
| `--deno-deployment-id` | `DENO_DEPLOYMENT_ID` | fresh id per boot | Deployment identity behind the `ETag` |
| `--effectionx-dir` | `EFFECTIONX_DIR` | _none_ | Local effectionx checkout to read instead of cloning |
| `--clones-dir` | `CLONES_DIR` | `build/clones` | Git clones of the documented repositories |
| `--worktrees-dir` | `WORKTREES_DIR` | `build/worktrees` | A git worktree per documented series |
| `--pagefind-dir` | `PAGEFIND_DIR` | `pagefind` | Generated Pagefind search bundle |
Expand All @@ -31,6 +32,31 @@ environment variable — and the command line wins when both are supplied.
The defaults reproduce what used to be hardcoded, so `deno task dev` and the
deployment workflow need no flags.

## Development

```
deno task dev
```

### Using a local checkout of effectionx

The website clones
[thefrontside/effectionx](https://github.com/thefrontside/effectionx) into
`build/clones` and reads its packages from `main`. To see a checkout you are
working in instead — its branch, its uncommitted changes and all — point
`EFFECTIONX_DIR` at it:

```
EFFECTIONX_DIR=../effectionx deno task dev
```

`--effectionx-dir ../effectionx` does the same thing, as with every other
parameter in the table above.

The directory is used exactly as it is on disk. It is never fetched or reset, so
the site cannot disturb work in progress, and a path that does not exist fails
at startup rather than on the first request that needs it.

## About Git Integration

The Effection website uses sophisticated GitHub integration to dynamically load
Expand Down
7 changes: 7 additions & 0 deletions www/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ export const www = command(
),
schema(fallback("")),
),
option(
name("effectionxDir"),
description(
"Local checkout of thefrontside/effectionx to read instead of cloning it.",
),
schema(fallback("")),
),
option(
name("clonesDir"),
description("Directory holding git clones of the documented repositories."),
Expand Down
2 changes: 1 addition & 1 deletion www/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"tasks": {
"dev": "deno run -A @effectionx/watch deno run -A main.tsx",
"staticalize": "deno run -A jsr:@frontside/staticalize@0.2.2/cli --site http://localhost:8000 --output=built --base=http://localhost:8000",
"test": "deno test --allow-run --allow-write --allow-read"
"test": "deno test --allow-run --allow-write --allow-read --allow-env"
},
"lint": {
"exclude": [
Expand Down
22 changes: 22 additions & 0 deletions www/lib/clones.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { assertEquals, assertThrows } from "@std/assert";
import { resolve } from "node:path";

import { resolveCheckouts } from "./clones.ts";

Deno.test("resolveCheckouts resolves a checkout to an absolute path", () => {
let lib = import.meta.dirname!;

assertEquals(resolveCheckouts({ "acme/widgets": `${lib}/../lib` }), {
"acme/widgets": lib,
});
});

Deno.test("resolveCheckouts rejects a directory that is not there", () => {
let missing = resolve(import.meta.dirname!, "nowhere");

assertThrows(
() => resolveCheckouts({ "acme/widgets": missing }),
Error,
`cannot use ${missing} as a local checkout of acme/widgets: no such directory`,
);
});
44 changes: 43 additions & 1 deletion www/lib/clones.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,28 @@ type Checkout = (nameWithOwner: string) => Operation<string>;

const Clones = createContext<Checkout>("clones");

export function* initClones(path: string): Operation<void> {
export interface ClonesOptions {
/**
* Directories to use in place of a clone, keyed by `owner/repo`.
*
* A local checkout is used exactly as it is on disk. It is never fetched or
* reset, both so that uncommitted work shows up on the site, and so that the
* site never touches a checkout you are working in.
*/
checkouts?: Record<string, string>;
}

export function* initClones(
path: string,
options: ClonesOptions = {},
): Operation<void> {
// resolved before anything is removed, so that a bad path fails at startup
// rather than on the first request that needs the repo
let checkouts = resolveCheckouts(options.checkouts ?? {});
for (let [nameWithOwner, dirpath] of Object.entries(checkouts)) {
console.log(`${nameWithOwner} -> ${dirpath}`);
}

yield* $(`rm -rf ${path}`);
yield* $(`mkdir -p ${path}`);

Expand All @@ -28,6 +49,11 @@ export function* initClones(path: string): Operation<void> {
// scope and every other checkout with it, so failures come back as a Result
// and the entry is evicted to allow a retry.
yield* Clones.set(function* (nameWithOwner) {
let checkout = checkouts[nameWithOwner];
if (checkout) {
return checkout;
}

let attempt = attempts.get(nameWithOwner);
if (!attempt) {
attempt = scope.run(() => cloneOrRefresh(path, nameWithOwner));
Expand All @@ -47,6 +73,22 @@ export function* useClone(nameWithOwner: string): Operation<string> {
return yield* checkout(nameWithOwner);
}

export function resolveCheckouts(
checkouts: Record<string, string>,
): Record<string, string> {
return Object.fromEntries(
Object.entries(checkouts).map(([nameWithOwner, path]) => {
let dirpath = resolve(path);
if (!existsSync(dirpath)) {
throw new Error(
`cannot use ${dirpath} as a local checkout of ${nameWithOwner}: no such directory`,
);
}
return [nameWithOwner, dirpath];
}),
);
}

function* cloneOrRefresh(
basepath: string,
nameWithOwner: string,
Expand Down
16 changes: 15 additions & 1 deletion www/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ function* serve(options: Options) {
// Get stable series (no prereleases) for guides
let stableSeries = series.filter((s) => !s.includePrerelease);

yield* initClones(options.clonesDir);
yield* initClones(options.clonesDir, {
checkouts: localCheckouts(options.effectionxDir),
});
yield* initWorktrees(options.worktreesDir);
yield* initGuides({
current,
Expand Down Expand Up @@ -152,6 +154,18 @@ function* serve(options: Options) {
yield* suspend();
}

/**
* Checkouts to use instead of cloning from GitHub, so that a repository you
* are working in shows up on the site:
*
* ```
* EFFECTIONX_DIR=../effectionx deno task dev
* ```
*/
function localCheckouts(effectionx: string): Record<string, string> {
return effectionx ? { "thefrontside/effectionx": effectionx } : {};
}

function urlFromServer(server: ServerInfo) {
return new URL(
"/",
Expand Down
Loading