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
11 changes: 8 additions & 3 deletions .github/workflows/hub-infrastructure.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,22 @@ defaults:

jobs:
terraform:
name: Terraform ${{ matrix.command }}
name: Terraform ${{ matrix.version }} ${{ matrix.module }} ${{ matrix.command }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
version: ["1.13.4", "1.17.1"]
module: [".", "foundation", "provision"]
command: ["fmt -check -recursive", "validate", "test"]
defaults:
run:
working-directory: infrastructure/azure/${{ matrix.module }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2
with:
terraform_version: 1.13.4
terraform_version: ${{ matrix.version }}
terraform_wrapper: false
- run: terraform init -backend=false -input=false -lockfile=readonly
- run: terraform ${{ matrix.command }}
Expand All @@ -37,4 +42,4 @@ jobs:
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 24
- run: node --test deploy-hub/index.test.ts
- run: node --test deploy-hub/index.test.ts stop-hub/index.test.ts ../installer/command/describe/index.test.ts
9 changes: 7 additions & 2 deletions hub/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends openssl ca-cert
COPY package.json package-lock.json .npmrc ./
COPY schema ./schema
COPY prisma7.config.ts ./
RUN npm ci --engine-strict
ARG NPM_REGISTRY
RUN if [ -n "$NPM_REGISTRY" ]; then \
node -e 'const fs=require("node:fs"); const lock=JSON.parse(fs.readFileSync("package-lock.json","utf8")); for(const entry of Object.values(lock.packages)){delete entry.resolved} fs.writeFileSync("package-lock.json",JSON.stringify(lock))'; \
export npm_config_registry="$NPM_REGISTRY"; \
fi; \
npm ci --engine-strict --no-audit --no-fund
COPY . .
RUN npm run build:runtime

Expand All @@ -20,7 +25,7 @@ COPY --from=build /app/package.json /app/package-lock.json /app/.npmrc ./
COPY --from=build /app/schema ./schema
COPY --from=build /app/prisma7.config.ts ./
COPY --from=build /app/node_modules ./node_modules
RUN npm prune --omit=dev --ignore-scripts && npm cache clean --force
RUN npm prune --omit=dev --ignore-scripts --offline && npm cache clean --force
COPY --from=build /app/dist ./dist
USER node
EXPOSE 3001
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,9 @@ it("saves an edited URL before connecting and retains invalid drafts and save fa
fireEvent.click(screen.getByRole("button", { name: "Save Hub" }))
await screen.findByRole("link", { name: "https://new.example.com" })
expect(showModal).toHaveBeenCalledOnce()
expect(screen.getByRole("button", { name: "Edit Hub URL" })).toHaveFocus()
await waitFor(() => {
expect(screen.getByRole("button", { name: "Edit Hub URL" })).toHaveFocus()
})
expect(onSelectHub).toHaveBeenLastCalledWith("https://new.example.com")
expect(screen.queryByRole("alert")).not.toBeInTheDocument()
fireEvent.click(screen.getByRole("button", { name: "Connect to the Hub" }))
Expand Down
6 changes: 4 additions & 2 deletions hub/docs/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ The Hub image runs the frontend, API, and both workers together. It can expose G

## Image and runtime

Build from the repository root with `docker build -t replica-hub:check hub`. The image runs as a non-root user and contains compiled application code and migration tooling. It writes no application state to its writable layer. Publish and deploy the same image digest for setup and Hub.
Build from the repository root with `docker build -t replica-hub:check hub`. When an npm proxy is required, add `--build-arg NPM_REGISTRY=https://YOUR_PROXY/`. This resolves locked dependency versions through that registry without changing the source lockfile; the registry URL must not contain credentials. The image runs as a non-root user and contains compiled application code and migration tooling. It writes no application state to its writable layer. Publish and deploy the same image digest for setup and Hub.

Host npm configuration is not inherited by the image build. Without `NPM_REGISTRY`, the container needs public npm access. Where that access is not allowed, supply your approved proxy using the build argument above. Explicit scoped registry mappings in the copied `.npmrc` still apply.

Publishing is an explicit operator step, not a side effect of CI. From the approved checkout, authenticate to an authorized registry, then run `docker buildx build --platform linux/amd64 --tag YOUR_REGISTRY/replica-hub:YOUR_COMMIT --push hub`. Resolve the published digest with `docker buildx imagetools inspect YOUR_REGISTRY/replica-hub:YOUR_COMMIT` and give deployment the `registry/repository@sha256:...` reference, never the mutable tag.

Expand Down Expand Up @@ -53,7 +55,7 @@ The deployment orchestrator owns serialization of the complete release, includin

## Scope and evidence

CI checks the image against disposable PostgreSQL, including setup, frontend/API serving, readiness, and termination. This is not a supported local-production installation. The existing devcontainer remains the development environment.
CI checks the image against disposable PostgreSQL, including setup, frontend/API serving, readiness, and termination. Set `NPM_REGISTRY` when running `node hub/script/check-image/index.ts` locally if the build requires a proxy. This is not a supported local-production installation. The existing devcontainer remains the development environment.

The [Azure reference installation](../../infrastructure/README.md) uses Terraform and a serialized maintenance-window release. It consumes the same image but does not yet configure the optional authentication settings. Add those protected settings and the exact Container Apps proxy-chain length before enabling authentication there.

Expand Down
24 changes: 21 additions & 3 deletions hub/script/check-image/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,27 @@ export async function checkImage() {
let applicationCreated = false
let imageCreated = false
try {
execFileSync("docker", ["build", "-t", image, resolve(import.meta.dirname, "../..")], {
stdio: "inherit",
})
const registry = process.env.NPM_REGISTRY
if (registry) {
const url = new URL(registry)
assert(
url.protocol === "https:" && !url.username && !url.password && !url.search && !url.hash,
"NPM_REGISTRY must be an HTTPS URL without credentials, query, or fragment",
)
}
execFileSync(
"docker",
[
"build",
"-t",
image,
...(registry ? ["--build-arg", `NPM_REGISTRY=${registry}`] : []),
resolve(import.meta.dirname, "../.."),
],
{
stdio: "inherit",
},
)
imageCreated = true
docker("network", "create", network)
networkCreated = true
Expand Down
2 changes: 2 additions & 0 deletions infrastructure/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
.tools/
node_modules/
.local/
**/.terraform/
**/*.tfstate*
**/*.tfplan
Expand Down
Loading
Loading