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
4 changes: 2 additions & 2 deletions .github/scripts/image-smoke.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@
#
# Usage: image-smoke.sh <image> [<commit>]
# Needs on PATH: docker, curl, jq, cmp, tar. Images (defaults match ci.yml):
# FORGE_SMOKE_POSTGRES_IMAGE (mirror.gcr.io/library/postgres:17)
# FORGE_SMOKE_POSTGRES_IMAGE (mirror.gcr.io/library/postgres:18)
# FORGE_SMOKE_S3_IMAGE (mirror.gcr.io/versity/versitygw:v1.4.1)
set -euo pipefail

image="$1"
commit="${2:-}"
postgres_image="${FORGE_SMOKE_POSTGRES_IMAGE:-mirror.gcr.io/library/postgres:17}"
postgres_image="${FORGE_SMOKE_POSTGRES_IMAGE:-mirror.gcr.io/library/postgres:18}"
s3_image="${FORGE_SMOKE_S3_IMAGE:-mirror.gcr.io/versity/versitygw:v1.4.1}"
package="$(realpath "$(dirname "$0")/../../fixtures/nuget/sample.lib.1.0.0.nupkg")"
port="${FORGE_SMOKE_PORT:-18421}"
Expand Down
14 changes: 7 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ jobs:
# queries and schema. Pinned by digest; Dependabot does not see
# workflow images, so bump it with deploy/kubernetes/components/postgres.
postgres:
image: mirror.gcr.io/library/postgres:17@sha256:a63c1ea3f6a3a5a1621f97d2aba3b30faecfab4224c1b0000cc85ced57bb1939
image: mirror.gcr.io/library/postgres:18@sha256:edb0c08dc33befd0262ab3b5cf1c4f3f8cfd8b09e88c05b45adee29d6761ddb6
env:
POSTGRES_DB: forge
POSTGRES_USER: forge
Expand Down Expand Up @@ -341,7 +341,7 @@ jobs:
cache-to: ${{ github.event_name == 'push' && format('type=gha,mode=max,scope=docker-{0}', matrix.arch) || '' }}
- name: Smoke test the image (as the manifests run it)
env:
FORGE_SMOKE_POSTGRES_IMAGE: mirror.gcr.io/library/postgres:17@sha256:a63c1ea3f6a3a5a1621f97d2aba3b30faecfab4224c1b0000cc85ced57bb1939
FORGE_SMOKE_POSTGRES_IMAGE: mirror.gcr.io/library/postgres:18@sha256:edb0c08dc33befd0262ab3b5cf1c4f3f8cfd8b09e88c05b45adee29d6761ddb6
FORGE_SMOKE_S3_IMAGE: mirror.gcr.io/versity/versitygw:v1.4.1@sha256:0400cb59f59da0f1cf9f7fd49505191abc348dfadf54509bf1988caaff4eb96f
run: .github/scripts/image-smoke.sh forge:ci "${{ github.sha }}"

Expand Down Expand Up @@ -472,7 +472,7 @@ jobs:
if: ${{ !cancelled() && needs.changes.outputs.rust != 'false' }}
services:
postgres:
image: mirror.gcr.io/library/postgres:17@sha256:a63c1ea3f6a3a5a1621f97d2aba3b30faecfab4224c1b0000cc85ced57bb1939
image: mirror.gcr.io/library/postgres:18@sha256:edb0c08dc33befd0262ab3b5cf1c4f3f8cfd8b09e88c05b45adee29d6761ddb6
env:
POSTGRES_DB: forge
POSTGRES_USER: forge
Expand Down Expand Up @@ -562,7 +562,7 @@ jobs:
if: ${{ !cancelled() && needs.changes.outputs.rust != 'false' }}
services:
postgres:
image: mirror.gcr.io/library/postgres:17@sha256:a63c1ea3f6a3a5a1621f97d2aba3b30faecfab4224c1b0000cc85ced57bb1939
image: mirror.gcr.io/library/postgres:18@sha256:edb0c08dc33befd0262ab3b5cf1c4f3f8cfd8b09e88c05b45adee29d6761ddb6
env:
POSTGRES_DB: forge
POSTGRES_USER: forge
Expand Down Expand Up @@ -618,7 +618,7 @@ jobs:
if: ${{ !cancelled() && needs.changes.outputs.rust != 'false' }}
services:
postgres:
image: mirror.gcr.io/library/postgres:17@sha256:a63c1ea3f6a3a5a1621f97d2aba3b30faecfab4224c1b0000cc85ced57bb1939
image: mirror.gcr.io/library/postgres:18@sha256:edb0c08dc33befd0262ab3b5cf1c4f3f8cfd8b09e88c05b45adee29d6761ddb6
env:
POSTGRES_DB: forge
POSTGRES_USER: forge
Expand Down Expand Up @@ -716,7 +716,7 @@ jobs:
# (.github/scripts/image-pin-check.sh leaves `docker pull` alone).
- name: Pre-pull container images
run: |
docker pull mirror.gcr.io/library/postgres:17
docker pull mirror.gcr.io/library/postgres:18
docker pull mirror.gcr.io/versity/versitygw:v1.4.1
docker pull mirror.gcr.io/versity/versitygw:v1.9.0
docker pull mirror.gcr.io/library/redis:5.0
Expand Down Expand Up @@ -780,7 +780,7 @@ jobs:
# By tag, like the e2e job: web/e2e names them by tag.
- name: Pre-pull container images
run: |
docker pull mirror.gcr.io/library/postgres:17
docker pull mirror.gcr.io/library/postgres:18
docker pull mirror.gcr.io/versity/versitygw:v1.4.1
- name: Install deps (frozen lockfile)
run: pnpm install --frozen-lockfile
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ are rate-limited and fail with `429 Too Many Requests`. Pull it through
Google's mirror, `mirror.gcr.io`, which serves the same images under the same
digests:

- an official image `postgres:17` is `mirror.gcr.io/library/postgres:17`;
- an official image `postgres:18` is `mirror.gcr.io/library/postgres:18`;
- any other `versity/versitygw:v1.4.1` is `mirror.gcr.io/versity/versitygw:v1.4.1`.

This holds for every pull: `docker pull`, `docker run`, testcontainers in new
Expand Down
6 changes: 5 additions & 1 deletion DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ By default (`--db local`) the server points at a host Postgres on **:5432**
`FORGE_DATABASE__URL`; versitygw (S3) still comes up via docker. Use `--db
docker` for a fully self-contained stack (Postgres on :5433) if you don't run a
local PostgreSQL. `--db none` starts no infra at all. Run `cargo xtask help` for
the full command list.
the full command list. Forge needs PostgreSQL 18 or newer, the host's too.

> First run compiles the workspace (a minute or two) and runs DB migrations
> automatically. A bootstrap admin **`admin@example.com` / `changeme`** is created (the server
Expand Down Expand Up @@ -752,6 +752,10 @@ server hashes its bootstrap admin password.

- `cargo xtask down` stops the containers but **keeps** the Postgres volume. To
wipe all state (start fresh): `docker compose down -v`.
- **An empty `--db docker` database after the move to PostgreSQL 18** — the
compose PostgreSQL 18 keeps its data in the volume `forge-dev_postgresql`.
The PostgreSQL 17 cluster in the earlier volume, `forge-dev_db_data`, is not
used (18 cannot open it): `docker volume rm forge-dev_db_data` removes it.
- **`the database was created by Forge 1.x or by a build before 2.0.0`** — the
schema started over at one migration in 2.0.0, and a database an earlier
build migrated is refused (`cargo xtask migrate` and `prepare`, which run
Expand Down
2 changes: 1 addition & 1 deletion crates/core/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ async fn endpoint() -> &'static str {
.with_user("forge")
.with_password("forge")
.with_name("mirror.gcr.io/library/postgres")
.with_tag("17")
.with_tag("18")
.start()
.await
.expect("failed to start postgres testcontainer (is Docker running?)");
Expand Down
2 changes: 1 addition & 1 deletion crates/db/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ async fn endpoint() -> &'static str {
.with_user("forge")
.with_password("forge")
.with_name("mirror.gcr.io/library/postgres")
.with_tag("17")
.with_tag("18")
.start()
.await
.expect("failed to start postgres testcontainer (is Docker running?)");
Expand Down
2 changes: 1 addition & 1 deletion crates/db/tests/encryption.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ async fn database_url() -> &'static str {
.with_user("forge")
.with_password("forge")
.with_name("mirror.gcr.io/library/postgres")
.with_tag("17")
.with_tag("18")
.start()
.await
.expect("start postgres testcontainer (is Docker running? set FORGE_TEST_DATABASE_URL to use an external Postgres)");
Expand Down
4 changes: 2 additions & 2 deletions crates/db/tests/migrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,7 @@ async fn a_database_of_an_earlier_build_is_refused() {
}
}

/// A Postgres server for one test: a throwaway Postgres 17 container matching
/// A Postgres server for one test: a throwaway Postgres 18 container matching
/// docker-compose (db/user/pw = forge), or, with FORGE_TEST_DATABASE_URL (no
/// Docker), that server. Returns the URL of a database on it, and the
/// container to keep alive.
Expand All @@ -794,7 +794,7 @@ async fn server() -> (String, Option<ContainerAsync<Postgres>>) {
.with_user("forge")
.with_password("forge")
.with_name("mirror.gcr.io/library/postgres")
.with_tag("17")
.with_tag("18")
.start()
.await
.expect("failed to start postgres testcontainer (is Docker running?)");
Expand Down
2 changes: 1 addition & 1 deletion crates/e2e/src/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ async fn pg_endpoint() -> &'static str {
.with_user("forge")
.with_password("forge")
.with_name("mirror.gcr.io/library/postgres")
.with_tag("17")
.with_tag("18")
.start()
.await
.expect("start postgres testcontainer (is Docker running?)");
Expand Down
2 changes: 1 addition & 1 deletion crates/server/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ async fn pg_endpoint() -> &'static str {
.with_user("forge")
.with_password("forge")
.with_name("mirror.gcr.io/library/postgres")
.with_tag("17")
.with_tag("18")
.start()
.await
.expect("start postgres testcontainer (is Docker running?)");
Expand Down
2 changes: 1 addition & 1 deletion crates/storage/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ async fn pg_endpoint() -> &'static str {
.with_user("forge")
.with_password("forge")
.with_name("mirror.gcr.io/library/postgres")
.with_tag("17")
.with_tag("18")
.start()
.await
.expect("failed to start postgres testcontainer (is Docker running?)");
Expand Down
2 changes: 1 addition & 1 deletion deploy/helm/forge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ and wired into Forge when enabled:

| Dependency | Enable with | Sets |
| --- | --- | --- |
| PostgreSQL 17 (StatefulSet) | `postgresql.enabled` | `FORGE_DATABASE__URL` |
| PostgreSQL 18 (StatefulSet) | `postgresql.enabled` | `FORGE_DATABASE__URL` |
| Valkey, cache only (Deployment) | `valkey.enabled` | `FORGE_REDIS__URL` |
| versitygw S3 gateway (StatefulSet) | `versitygw.enabled` | `s3.endpoint`, `s3.bucket`, `FORGE_S3__ACCESS_KEY` / `_SECRET_KEY` (static root credentials only: not with short-lived `s3.credentials`) |

Expand Down
2 changes: 1 addition & 1 deletion deploy/helm/forge/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ extraObjects: null
# auth.password or auth.existingSecret there (and, for Valkey with
# auth.password, auth.checksumSalt).

# PostgreSQL 17 StatefulSet; sets FORGE_DATABASE__URL.
# PostgreSQL 18 StatefulSet; sets FORGE_DATABASE__URL.
postgresql:
enabled: false

Expand Down
2 changes: 1 addition & 1 deletion deploy/kubernetes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ deploy/kubernetes/
│ ├── high-availability/ two replicas, spread over nodes, PodDisruptionBudget
│ ├── local-cache/ local blob cache on an emptyDir (faster downloads, serves through an S3 outage)
│ ├── local-cache-persistent/ the same on a PersistentVolume (one replica, survives restarts)
│ ├── postgres/ in-cluster PostgreSQL 17 (evaluation / small installs)
│ ├── postgres/ in-cluster PostgreSQL 18 (evaluation / small installs)
│ ├── s3-versitygw/ in-cluster S3 gateway on a PersistentVolume (evaluation / small installs)
│ ├── s3-workload-identity/ short-lived S3 credentials from a projected service-account token (no S3 keys)
│ ├── opentelemetry/ metrics, traces and logs over OTLP to an OpenTelemetry Collector
Expand Down
2 changes: 1 addition & 1 deletion deploy/kubernetes/components/postgres/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# A single-instance PostgreSQL 17 StatefulSet for evaluation and small
# A single-instance PostgreSQL 18 StatefulSet for evaluation and small
# installations, wired into Forge. It has no backups, replication or
# failover: for production use a managed PostgreSQL or an operator
# (CloudNativePG, …) and set FORGE_DATABASE__URL in the forge-env Secret
Expand Down
11 changes: 7 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
name: forge-dev

services:
# PostgreSQL 17 — metadata store
# PostgreSQL 18 — metadata store
db:
image: mirror.gcr.io/library/postgres:17
image: mirror.gcr.io/library/postgres:18
environment:
POSTGRES_DB: forge
POSTGRES_USER: forge
Expand All @@ -13,7 +13,10 @@ services:
# already be listening on the host's default 5432. forge.toml points here.
- "5433:5432"
volumes:
- db_data:/var/lib/postgresql/data
# The 18 image keeps its cluster in /var/lib/postgresql/18/docker and
# refuses to start with a volume on /var/lib/postgresql/data. The volume
# of the earlier PostgreSQL 17 (db_data) is not used.
- postgresql:/var/lib/postgresql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U forge -d forge"]
interval: 5s
Expand Down Expand Up @@ -54,5 +57,5 @@ services:
retries: 10

volumes:
db_data:
postgresql:
s3_data:
5 changes: 5 additions & 0 deletions docs/changes/2.2.0/637.upgrade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Forge requires PostgreSQL 18 or newer; it asked for 15 or newer before. On
PostgreSQL 15 to 17, upgrade PostgreSQL first (`pg_upgrade`, or a dump and
restore), then Forge: Forge does not check the server's version, and on an
older server a query that uses what 18 added fails. The in-cluster PostgreSQL
of the Helm chart and of the kustomize component runs 18 already.
2 changes: 1 addition & 1 deletion docs/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ CI publishing without stored API keys see

| Dependency | Notes |
| --- | --- |
| PostgreSQL 15+ (17 tested) | The authority for all metadata, sessions, RBAC and coordination. Migrations run automatically at startup. From PostgreSQL 15 on, `public` no longer grants CREATE to every role, so the role in `FORGE_DATABASE__URL` must own the database (`CREATE DATABASE forge OWNER forge`) or hold CREATE on its schema. |
| PostgreSQL 18+ | The authority for all metadata, sessions, RBAC and coordination. Migrations run automatically at startup. `public` grants CREATE to the database's owner only, so the role in `FORGE_DATABASE__URL` must own the database (`CREATE DATABASE forge OWNER forge`) or hold CREATE on its schema. |
| S3-compatible object store | Package blobs (content-addressed). AWS S3, MinIO, Ceph RGW, versitygw, … A missing bucket is created in the configured region; see [S3 permissions](#s3-permissions). Static keys or [short-lived credentials](#short-lived-s3-credentials) (IRSA, EKS Pod Identity, STS web identity). |
| TLS termination | A reverse proxy or load balancer in front of Forge (nginx, Traefik, an ingress, an ALB). Forge itself speaks plain HTTP. |
| Valkey or Redis 6+ (optional) | Shared L2 cache and cross-instance invalidation. Recommended as soon as you run more than one instance. TLS via `rediss://`. The Helm chart (`valkey.enabled`) and the kustomize component `valkey` can run a Valkey in the cluster. |
Expand Down
4 changes: 2 additions & 2 deletions web/e2e/docker-compose.e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
# harness.ts) so running e2e never disturbs a dev stack.
#
# Mirrors the Rust testcontainers harness (crates/e2e/src/stack.rs): the same
# Postgres 17 and versity/versitygw:v1.4.1 images (through mirror.gcr.io), the
# Postgres 18 and versity/versitygw:v1.4.1 images (through mirror.gcr.io), the
# same credentials shape.
services:
db:
image: mirror.gcr.io/library/postgres:17
image: mirror.gcr.io/library/postgres:18
environment:
POSTGRES_DB: forge
POSTGRES_USER: forge
Expand Down
2 changes: 1 addition & 1 deletion web/e2e/harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* bootstrap-admin credentials to the Playwright specs.
*
* This deliberately drives the **actual Rust server binary** against the same
* container images the Rust testcontainers harness uses (Postgres 17 +
* container images the Rust testcontainers harness uses (Postgres 18 +
* versity/versitygw:v1.4.1), proving the single-origin serving model end to end:
* the browser hits `/`, `/admin/*`, `/browse/*`, `/repository/*` on one origin with no
* Node proxy.
Expand Down
Loading