Add production deployment tooling: Nginx, Gitea, prod Dockerfiles

Sets up everything needed to deploy behind Cloudflare with a self-hosted
git server: multi-stage prod Dockerfiles (non-root), docker-compose.prod.yml
(Postgres/Redis with no host ports, Nginx reverse proxy, Gitea with
public-read/admin-write access control), scripts/bootstrap-env.sh to
auto-generate required secrets on first clone, and DEPLOYMENT.md covering
the full runbook. Provider API keys (Anthropic/Brave/NinjaPear/USPTO/
Turnstile) are deliberately kept out of .env in favor of the existing
DB-backed Settings UI, so the public repo stays safe to expose.

Also fixes two bugs only surfaced by live-testing the prod stack: Celery
beat couldn't write its schedule file as a non-root user, and Gitea's
embedded SSH server conflicted with the base image's own sshd on port 22.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
2026-08-05 12:59:41 -04:00
co-authored by Claude Sonnet 5
parent 1a4c80958f
commit 086ca1f13f
14 changed files with 644 additions and 1 deletions
+15
View File
@@ -67,4 +67,19 @@ describe("LandingPage", () => {
);
expect(screen.queryByText(/local mode/i)).not.toBeInTheDocument();
});
it("hides the Git Repository link when no NEXT_PUBLIC_GIT_REPO_URL is configured", () => {
renderWithQueryClient(<LandingPage />);
expect(screen.queryByRole("link", { name: /git repository/i })).not.toBeInTheDocument();
});
it("shows a Git Repository link opening in a new tab when configured", () => {
vi.stubEnv("NEXT_PUBLIC_GIT_REPO_URL", "https://git.ciagent.org/admin/ci-agent");
renderWithQueryClient(<LandingPage />);
const link = screen.getByRole("link", { name: /git repository/i });
expect(link).toHaveAttribute("href", "https://git.ciagent.org/admin/ci-agent");
expect(link).toHaveAttribute("target", "_blank");
expect(link).toHaveAttribute("rel", "noopener noreferrer");
});
});