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
60 changes: 60 additions & 0 deletions .github/workflows/sync-upstream-images.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Sync Upstream Images

# Weekly job that pulls component image versions from the upstream Supabase
# compose file and opens a PR when anything changed. The existing e2e
# workflow on the PR acts as the compatibility gate for version bumps.
#
# Note: PRs created with the default GITHUB_TOKEN do not trigger other
# workflows (lint, test, e2e). Configure a SYNC_UPSTREAM_TOKEN secret with
# a PAT (repo scope) so CI runs on the sync PRs automatically. Without it
# the job falls back to GITHUB_TOKEN and CI must be triggered by closing
# and reopening the PR.

on:
schedule:
- cron: "0 3 * * 1"
workflow_dispatch: {}

permissions:
contents: write
pull-requests: write

jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Clone the code
uses: actions/checkout@v4

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Sync image versions from upstream
run: ./hack/sync-upstream-images.sh

- name: Regenerate CRD schema and run tests
run: make test

- name: Create pull request
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.SYNC_UPSTREAM_TOKEN || secrets.GITHUB_TOKEN }}
branch: chore/sync-upstream-images
commit-message: "chore: sync component image versions from upstream Supabase"
title: "chore: sync component image versions from upstream Supabase"
body: |
Automated sync of default component image versions from the
upstream Supabase compose file:
https://github.com/supabase/supabase/blob/master/docker/docker-compose.yml

Updated files:
- `api/v1alpha1/wellknown_images.go`
- `api/v1alpha1/supabaseproject_types.go`
- `helm/supabase-operator/crds/` (regenerated)

Please review the version changes and make sure the e2e workflow
passes before merging. Major version jumps (for example Kong)
may need manual config changes in the component builders.
delete-branch: true
10 changes: 5 additions & 5 deletions api/v1alpha1/supabaseproject_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ type KongConfig struct {
}

type AuthConfig struct {
// +kubebuilder:default="supabase/gotrue:v2.177.0"
// +kubebuilder:default="supabase/gotrue:v2.180.0"
// +optional
Image string `json:"image,omitempty"`

Expand All @@ -141,7 +141,7 @@ type AuthConfig struct {
}

type RealtimeConfig struct {
// +kubebuilder:default="supabase/realtime:v2.34.47"
// +kubebuilder:default="supabase/realtime:v2.51.11"
// +optional
Image string `json:"image,omitempty"`

Expand All @@ -159,7 +159,7 @@ type RealtimeConfig struct {
}

type PostgRESTConfig struct {
// +kubebuilder:default="postgrest/postgrest:v12.2.12"
// +kubebuilder:default="postgrest/postgrest:v13.0.7"
// +optional
Image string `json:"image,omitempty"`

Expand All @@ -177,7 +177,7 @@ type PostgRESTConfig struct {
}

type StorageAPIConfig struct {
// +kubebuilder:default="supabase/storage-api:v1.25.7"
// +kubebuilder:default="supabase/storage-api:v1.32.0"
// +optional
Image string `json:"image,omitempty"`

Expand All @@ -195,7 +195,7 @@ type StorageAPIConfig struct {
}

type MetaConfig struct {
// +kubebuilder:default="supabase/postgres-meta:v0.91.0"
// +kubebuilder:default="supabase/postgres-meta:v0.93.1"
// +optional
Image string `json:"image,omitempty"`

Expand Down
14 changes: 7 additions & 7 deletions api/v1alpha1/supabaseproject_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ func TestStorageConfig_SecretReference(t *testing.T) {
func TestKongConfig_Defaults(t *testing.T) {
config := &KongConfig{}

expectedImage := "kong:2.8.1"
expectedImage := DefaultKongImage
if config.Image == "" {
config.Image = expectedImage
}
Expand Down Expand Up @@ -203,7 +203,7 @@ func TestKongConfig_Defaults(t *testing.T) {
func TestAuthConfig_Defaults(t *testing.T) {
config := &AuthConfig{}

expectedImage := "supabase/gotrue:v2.177.0"
expectedImage := DefaultAuthImage
if config.Image == "" {
config.Image = expectedImage
}
Expand All @@ -225,7 +225,7 @@ func TestAuthConfig_Defaults(t *testing.T) {
func TestRealtimeConfig_Defaults(t *testing.T) {
config := &RealtimeConfig{}

expectedImage := "supabase/realtime:v2.34.47"
expectedImage := DefaultRealtimeImage
if config.Image == "" {
config.Image = expectedImage
}
Expand All @@ -247,7 +247,7 @@ func TestRealtimeConfig_Defaults(t *testing.T) {
func TestPostgRESTConfig_Defaults(t *testing.T) {
config := &PostgRESTConfig{}

expectedImage := "postgrest/postgrest:v12.2.12"
expectedImage := DefaultPostgRESTImage
if config.Image == "" {
config.Image = expectedImage
}
Expand All @@ -269,7 +269,7 @@ func TestPostgRESTConfig_Defaults(t *testing.T) {
func TestStorageAPIConfig_Defaults(t *testing.T) {
config := &StorageAPIConfig{}

expectedImage := "supabase/storage-api:v1.25.7"
expectedImage := DefaultStorageAPIImage
if config.Image == "" {
config.Image = expectedImage
}
Expand All @@ -291,7 +291,7 @@ func TestStorageAPIConfig_Defaults(t *testing.T) {
func TestMetaConfig_Defaults(t *testing.T) {
config := &MetaConfig{}

expectedImage := "supabase/postgres-meta:v0.91.0"
expectedImage := DefaultMetaImage
if config.Image == "" {
config.Image = expectedImage
}
Expand All @@ -313,7 +313,7 @@ func TestMetaConfig_Defaults(t *testing.T) {
func TestStudioConfig_Defaults(t *testing.T) {
config := &StudioConfig{}

expectedImage := "supabase/studio:2025.10.01-sha-8460121"
expectedImage := DefaultStudioImage
if config.Image == "" {
config.Image = expectedImage
}
Expand Down
24 changes: 24 additions & 0 deletions api/v1alpha1/wellknown_images.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package v1alpha1

// Default image versions for Supabase components.
//
// This file is the single source of truth for component image defaults.
// The kubebuilder default markers in supabaseproject_types.go must carry
// the same values so that the generated CRD schema matches the webhook
// defaults. Both files are updated together by hack/sync-upstream-images.sh,
// which reads the upstream compose file:
// https://github.com/supabase/supabase/blob/master/docker/docker-compose.yml
const (
DefaultKongImage = "kong:2.8.1"
DefaultAuthImage = "supabase/gotrue:v2.180.0"
DefaultPostgRESTImage = "postgrest/postgrest:v13.0.7"
DefaultRealtimeImage = "supabase/realtime:v2.51.11"
DefaultStorageAPIImage = "supabase/storage-api:v1.32.0"
DefaultMetaImage = "supabase/postgres-meta:v0.93.1"
DefaultStudioImage = "supabase/studio:2025.10.01-sha-8460121"
)

// DefaultPostgresImage is used by the database init job only. The operator
// requires a user provided PostgreSQL, so this image is intentionally not
// synced from upstream.
const DefaultPostgresImage = "postgres:15-alpine"
100 changes: 100 additions & 0 deletions hack/sync-upstream-images.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#!/bin/bash
set -euo pipefail

# Sync default component image versions from the upstream Supabase compose file
# https://github.com/supabase/supabase/blob/master/docker/docker-compose.yml
#
# Updates two files that must stay consistent:
# api/v1alpha1/wellknown_images.go Go constants used by the webhook
# api/v1alpha1/supabaseproject_types.go kubebuilder default markers
# Run "make manifests" afterwards to regenerate the CRD schema.
#
# Usage: ./hack/sync-upstream-images.sh [REF]
# REF defaults to master, a tag or commit of supabase/supabase also works.

UPSTREAM_REPO="https://raw.githubusercontent.com/supabase/supabase"
REF="${1:-master}"
COMPOSE_URL="${UPSTREAM_REPO}/${REF}/docker/docker-compose.yml"

CONSTANTS_FILE="api/v1alpha1/wellknown_images.go"
TYPES_FILE="api/v1alpha1/supabaseproject_types.go"

# Format: "compose_service constant_name struct_name"
# The db service is intentionally not listed, the operator requires a user
# provided PostgreSQL so DefaultPostgresImage is never synced from upstream.
MAPPINGS=(
"kong DefaultKongImage KongConfig"
"auth DefaultAuthImage AuthConfig"
"rest DefaultPostgRESTImage PostgRESTConfig"
"realtime DefaultRealtimeImage RealtimeConfig"
"storage DefaultStorageAPIImage StorageAPIConfig"
"meta DefaultMetaImage MetaConfig"
"studio DefaultStudioImage StudioConfig"
)

compose_file="$(mktemp)"
trap 'rm -f "$compose_file"' EXIT

echo "Fetching ${COMPOSE_URL}..."
curl -fsSL "$COMPOSE_URL" -o "$compose_file"

lookup_image() {
awk -v svc="$1" '
/^ [a-zA-Z0-9_-]+:$/ { current = substr($1, 1, length($1) - 1) }
current == svc && $1 == "image:" { print $2; exit }
' "$compose_file"
}

current_constant() {
awk -v name="$1" '$1 == name { gsub(/"/, "", $3); print $3; exit }' "$CONSTANTS_FILE"
}

update_constant() {
local name="$1" image="$2"
sed -E -i.bak "s|(${name}[[:space:]]*=[[:space:]]*)\"[^\"]*\"|\1\"${image}\"|" "$CONSTANTS_FILE"
rm -f "${CONSTANTS_FILE}.bak"
}

update_marker() {
local struct="$1" image="$2"
awk -v st="$struct" -v img="$image" '
$0 ~ "^type " st " struct" { in_struct = 1 }
in_struct && /^}/ { in_struct = 0 }
in_struct && /\+kubebuilder:default="/ && !done {
sub(/\+kubebuilder:default="[^"]*"/, "+kubebuilder:default=\"" img "\"")
done = 1
}
{ print }
' "$TYPES_FILE" > "${TYPES_FILE}.tmp"
mv "${TYPES_FILE}.tmp" "$TYPES_FILE"
}

changed=0
for entry in "${MAPPINGS[@]}"; do
read -r service constant struct <<< "$entry"

new_image="$(lookup_image "$service")"
if [ -z "$new_image" ]; then
echo "ERROR: service '${service}' not found in upstream compose file" >&2
exit 1
fi

old_image="$(current_constant "$constant")"
if [ "$old_image" = "$new_image" ]; then
echo " = ${service}: ${old_image} (up to date)"
continue
Comment on lines +83 to +85

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reconcile markers even when constants match upstream

When a constant already matches the upstream compose image, this branch skips update_marker, so a stale kubebuilder default marker/CRD can remain stale and the script will still report the service as up to date. That recreates the drift this change is meant to prevent: for resources where Kubernetes applies the CRD default before the webhook, users continue to get the marker value rather than the constant. Consider checking and updating the marker independently of whether the Go constant changed.

Useful? React with 👍 / 👎.

fi

echo " * ${service}: ${old_image} to ${new_image}"
update_constant "$constant" "$new_image"
update_marker "$struct" "$new_image"
changed=1
done

if [ "$changed" -eq 0 ]; then
echo "All component images are up to date."
exit 0
fi

gofmt -w "$CONSTANTS_FILE" "$TYPES_FILE"
echo "Done. Run 'make manifests' to regenerate the CRD schema."
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ spec:
type: object
type: array
image:
default: supabase/gotrue:v2.177.0
default: supabase/gotrue:v2.180.0
type: string
oauthSecretRef:
description: |-
Expand Down Expand Up @@ -748,7 +748,7 @@ spec:
type: object
type: array
image:
default: supabase/postgres-meta:v0.91.0
default: supabase/postgres-meta:v0.93.1
type: string
replicas:
default: 1
Expand Down Expand Up @@ -977,7 +977,7 @@ spec:
type: object
type: array
image:
default: postgrest/postgrest:v12.2.12
default: postgrest/postgrest:v13.0.7
type: string
replicas:
default: 1
Expand Down Expand Up @@ -1209,7 +1209,7 @@ spec:
type: object
type: array
image:
default: supabase/realtime:v2.34.47
default: supabase/realtime:v2.51.11
type: string
replicas:
default: 1
Expand Down Expand Up @@ -1470,7 +1470,7 @@ spec:
type: object
type: array
image:
default: supabase/storage-api:v1.25.7
default: supabase/storage-api:v1.32.0
type: string
replicas:
default: 1
Expand Down
3 changes: 1 addition & 2 deletions internal/component/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package component

import (
"github.com/strrl/supabase-operator/api/v1alpha1"
"github.com/strrl/supabase-operator/internal/webhook"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
Expand All @@ -24,7 +23,7 @@ func (b *AuthBuilder) BuildDeployment(project *v1alpha1.SupabaseProject) (*appsv
replicas = project.Spec.Auth.Replicas
}

image := webhook.DefaultAuthImage
image := v1alpha1.DefaultAuthImage
if project.Spec.Auth != nil && project.Spec.Auth.Image != "" {
image = project.Spec.Auth.Image
}
Expand Down
3 changes: 1 addition & 2 deletions internal/component/database_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package component
import (
"github.com/strrl/supabase-operator/api/v1alpha1"
"github.com/strrl/supabase-operator/internal/database/migrations"
"github.com/strrl/supabase-operator/internal/webhook"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -138,7 +137,7 @@ func BuildDatabaseInitJob(project *v1alpha1.SupabaseProject) *batchv1.Job {
Containers: []corev1.Container{
{
Name: "init",
Image: webhook.DefaultPostgresImage,
Image: v1alpha1.DefaultPostgresImage,
Command: []string{
"bash",
"/scripts/run-migrations.sh",
Expand Down
3 changes: 1 addition & 2 deletions internal/component/kong.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"strings"

"github.com/strrl/supabase-operator/api/v1alpha1"
"github.com/strrl/supabase-operator/internal/webhook"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
Expand All @@ -27,7 +26,7 @@ func (b *KongBuilder) BuildDeployment(project *v1alpha1.SupabaseProject) (*appsv
replicas = project.Spec.Kong.Replicas
}

image := webhook.DefaultKongImage
image := v1alpha1.DefaultKongImage
if project.Spec.Kong != nil && project.Spec.Kong.Image != "" {
image = project.Spec.Kong.Image
}
Expand Down
Loading
Loading