Skip to content

Commit 376c069

Browse files
committed
feat: harden pipelines and repository lifecycle
- require Docker isolation for production pipeline commands - add restricted CI networking and runtime health checks - secure pipeline secrets, paths, artifacts, and cancellation - add explicit per-repository Pages publishing controls - preserve web, Pages, HTTP, and SSH access across repo renames - fully clean repository data and unshared LFS objects on deletion - restore queued runs and scope cancellation to individual runs - fix pnpm 11.1.3 CI setup and documentation deep links - add migrations, documentation, and regression coverage
1 parent 115ed10 commit 376c069

40 files changed

Lines changed: 4330 additions & 240 deletions

.github/workflows/ci.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ jobs:
1212
- uses: actions/checkout@v4
1313

1414
- uses: pnpm/action-setup@v4
15-
with:
16-
version: 10
1715

1816
- uses: actions/setup-node@v4
1917
with:
@@ -40,6 +38,9 @@ jobs:
4038
- name: Build
4139
run: pnpm build
4240

41+
- name: Run pipeline unit tests
42+
run: pnpm --filter @groffee/web test:unit
43+
4344
- name: Start production server
4445
run: pnpm --filter @groffee/web start &
4546
env:

.github/workflows/docs.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ jobs:
2424
- uses: actions/checkout@v4
2525

2626
- uses: pnpm/action-setup@v4
27-
with:
28-
version: 10
2927

3028
- uses: actions/setup-node@v4
3129
with:

.groffee/pipelines.yml

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pipelines:
1818
timeout: 120
1919
steps:
2020
- name: Install pnpm
21-
run: corepack enable && corepack prepare pnpm@latest --activate
21+
run: mkdir -p "$HOME/.local/bin" && corepack enable --install-directory "$HOME/.local/bin"
2222
- name: Install dependencies
2323
run: pnpm install --frozen-lockfile
2424
- name: Lint
@@ -30,7 +30,7 @@ pipelines:
3030
timeout: 180
3131
steps:
3232
- name: Install pnpm
33-
run: corepack enable && corepack prepare pnpm@latest --activate
33+
run: mkdir -p "$HOME/.local/bin" && corepack enable --install-directory "$HOME/.local/bin"
3434
- name: Install dependencies
3535
run: pnpm install --frozen-lockfile
3636
- name: Typecheck
@@ -43,7 +43,7 @@ pipelines:
4343
timeout: 300
4444
steps:
4545
- name: Install pnpm
46-
run: corepack enable && corepack prepare pnpm@latest --activate
46+
run: mkdir -p "$HOME/.local/bin" && corepack enable --install-directory "$HOME/.local/bin"
4747
- name: Install dependencies
4848
run: pnpm install --frozen-lockfile
4949
- name: Build
@@ -55,16 +55,24 @@ pipelines:
5555

5656
test:
5757
name: E2E Tests
58-
image: mcr.microsoft.com/playwright:v1.52.0-noble
58+
image: mcr.microsoft.com/playwright:v1.60.0-noble
5959
needs: [build]
6060
timeout: 600
6161
steps:
6262
- name: Install pnpm
63-
run: corepack enable && corepack prepare pnpm@latest --activate
63+
run: mkdir -p "$HOME/.local/bin" && corepack enable --install-directory "$HOME/.local/bin"
6464
- name: Install dependencies
6565
run: pnpm install --frozen-lockfile
6666
- name: Build
6767
run: pnpm build
68+
- name: Unit tests
69+
run: pnpm --filter @groffee/web test:unit
70+
- name: Seed test data
71+
run: pnpm seed && pnpm make-admin alice
72+
- name: Start production server
73+
run: pnpm --filter @groffee/web start > /tmp/groffee.log 2>&1 &
74+
- name: Wait for server
75+
run: npx wait-on http://localhost:3000 --timeout 30000
6876
- name: Run E2E tests
6977
run: pnpm test
7078
artifacts:

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ RUN pnpm install --frozen-lockfile
4040

4141
COPY . .
4242
ENV EXTERNAL_URL=""
43+
ENV NODE_ENV="production"
4344

4445
EXPOSE 3000
4546
EXPOSE 2223

README.md

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ application ships as one Node.js service plus an SSH listener.
2828
## Requirements
2929

3030
- Node.js 22+
31-
- pnpm 10+
31+
- pnpm 11.1.3 (declared by `packageManager` and activated through Corepack)
3232
- git
3333
- git-lfs
3434

@@ -108,22 +108,26 @@ EXTERNAL_URL=https://groffee.example.com pnpm start
108108

109109
## Environment Variables
110110

111-
| Variable | Default | Description |
112-
| -------------- | ------------------------ | ----------------------------------------------------- |
113-
| `PORT` | `3000` | HTTP server port |
114-
| `SSH_PORT` | `2223` | SSH server port |
115-
| `DATA_DIR` | `./data` | Directory for database, repositories, and LFS objects |
116-
| `EXTERNAL_URL` | `http://localhost:$PORT` | Public-facing URL (required for Git LFS over SSH) |
111+
See the [configuration guide](packages/docs/src/pages/configuration.mdx) for the complete environment reference, including Docker runner limits and Pages settings.
117112

118113
## Docker
119114

120115
```bash
121116
docker build -t groffee .
122117
docker run -p 3000:3000 -p 2223:2223 \
123118
-e EXTERNAL_URL=https://groffee.example.com \
124-
-v groffee-data:/app/data groffee
119+
-e NODE_ENV=production \
120+
-e DOCKER_HOST_DATA_DIR=/var/lib/docker/volumes/groffee-data/_data \
121+
-v groffee-data:/app/data \
122+
-v /var/run/docker.sock:/var/run/docker.sock groffee
125123
```
126124

125+
Production CI requires a dedicated `groffee-ci` Docker network with restricted egress. See the configuration guide before enabling repository pipelines.
126+
127+
Pages publishing is disabled per repository by default, including for public repositories. An
128+
owner must explicitly enable it in repository settings. Published Pages sites are public even when
129+
their source repository is private.
130+
127131
## Project Structure
128132

129133
```

e2e/repo/pages-settings.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { expect, test } from "../fixtures";
2+
3+
test.describe("repository Pages policy", () => {
4+
test("requires explicit public publishing opt-in", async ({ page, snap }) => {
5+
await page.goto("/alice/mega-app/settings");
6+
const publishing = page.getByRole("checkbox", { name: "Publish pipeline deployments" });
7+
await expect(publishing).toBeVisible();
8+
await expect(publishing).not.toBeChecked();
9+
await expect(
10+
page.getByText("Pages sites are publicly reachable, even when this repository is private."),
11+
).toBeVisible();
12+
await snap("settings-pages-opt-in");
13+
14+
await page.goto("/alice/mega-app/pages");
15+
await expect(page.getByRole("heading", { name: "Pages publishing is disabled" })).toBeVisible();
16+
await expect(
17+
page.getByText(
18+
"The repository owner must explicitly enable public Pages publishing in Settings.",
19+
),
20+
).toBeVisible();
21+
await snap("pages-disabled");
22+
});
23+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CREATE TABLE `repository_redirects` (
2+
`id` text PRIMARY KEY NOT NULL,
3+
`owner_id` text NOT NULL,
4+
`old_name` text NOT NULL,
5+
`repo_id` text NOT NULL,
6+
`created_at` integer NOT NULL,
7+
FOREIGN KEY (`owner_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
8+
FOREIGN KEY (`repo_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade
9+
);
10+
--> statement-breakpoint
11+
CREATE UNIQUE INDEX `repo_redirect_owner_name_idx` ON `repository_redirects` (`owner_id`,`old_name`);--> statement-breakpoint
12+
CREATE INDEX `repo_redirect_repo_idx` ON `repository_redirects` (`repo_id`);--> statement-breakpoint
13+
ALTER TABLE `repositories` ADD `pages_enabled` integer DEFAULT false NOT NULL;

0 commit comments

Comments
 (0)