Skip to content

Commit a2522df

Browse files
committed
Merge branch 'main' into docs/postgres-supabase-doc-updates
2 parents 4bd7a1a + 94ef456 commit a2522df

16 files changed

Lines changed: 432 additions & 43 deletions

File tree

.github/workflows/main.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,24 @@ jobs:
239239
path: dist/${{ matrix.name == 'apple-xcframework' && 'CloudSync.*' || 'cloudsync.*'}}
240240
if-no-files-found: error
241241

242+
postgres-migration-check:
243+
if: ${{ !contains(github.event.head_commit.message, '[auto-update]') }}
244+
runs-on: ubuntu-22.04
245+
name: postgresql migration script check
246+
timeout-minutes: 2
247+
steps:
248+
- uses: actions/checkout@v4.2.2
249+
with:
250+
# Need full history + tags so `git describe` can find the previous
251+
# release tag that the check script compares against.
252+
fetch-depth: 0
253+
254+
- name: verify migration script for current CLOUDSYNC_VERSION
255+
run: make postgres-check-migration
256+
242257
postgres-test:
243258
if: ${{ !contains(github.event.head_commit.message, '[auto-update]') }}
259+
needs: [postgres-migration-check]
244260
runs-on: ubuntu-22.04
245261
name: postgresql ${{ matrix.postgres_tag }} build + test
246262
timeout-minutes: 10
@@ -284,6 +300,7 @@ jobs:
284300
285301
postgres-build:
286302
if: ${{ !contains(github.event.head_commit.message, '[auto-update]') }}
303+
needs: [postgres-migration-check]
287304
runs-on: ${{ matrix.os }}
288305
name: postgresql${{ matrix.postgres_version }}-${{ matrix.name }}-${{ matrix.arch }} build
289306
timeout-minutes: 15

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ dist/
77
/curl/src
88
openssl/
99

10+
# Generated PostgreSQL extension files (produced from .in templates by
11+
# docker/Makefile.postgresql; version is derived from src/cloudsync.h)
12+
/docker/postgresql/cloudsync.control
13+
/src/postgresql/cloudsync--*.sql
14+
1015
# Test artifacts
1116
/coverage
1217
unittest

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ Built on **CRDT** (Conflict-free Replicated Data Types), it guarantees:
5353

5454
### 1. Install
5555

56-
Download a pre-built binary from the [Releases](https://github.com/sqliteai/sqlite-sync/releases) page, or install a platform package (see [full installation guide](./docs/installation.md) for platform-specific code examples):
56+
Download a pre-built binary from the [Releases](https://github.com/sqliteai/sqlite-sync/releases) page, or install a platform package (see [full installation guide](./docs/INSTALLATION.md) for platform-specific code examples):
5757

5858
| Platform | Install |
5959
|----------|---------|
@@ -212,6 +212,12 @@ Part of the **[SQLite AI](https://sqlite.ai)** ecosystem:
212212
| **[SQLite-JS](https://github.com/sqliteai/sqlite-js)** | Custom SQLite functions in JavaScript |
213213
| **[Liteparser](https://github.com/sqliteai/liteparser)** | Fully compliant SQLite SQL parser |
214214

215+
## Versioning
216+
217+
This project follows [semver](https://semver.org/). The single source of truth is `CLOUDSYNC_VERSION` in `src/cloudsync.h`; all packaged artifacts (NPM, Maven, pub.dev, Swift, Docker, native tarballs) inherit this version. PATCH releases never alter the exposed API — they ship bug fixes, performance improvements, and internal changes only.
218+
219+
The PostgreSQL extension differs only in how it surfaces the version: its catalog version (`default_version` / `installed_version`) exposes `MAJOR.MINOR` only, so PATCH releases are transparent binary upgrades and only MINOR/MAJOR releases need `ALTER EXTENSION cloudsync UPDATE`. The `cloudsync_version()` SQL function always reports the full semver of the loaded `.so`. See the [PostgreSQL upgrade docs](docs/postgresql/quickstarts/postgres.md#upgrading-a-later-release) for the user-facing procedure.
220+
215221
## License
216222

217223
This project is licensed under the [Elastic License 2.0](./LICENSE.md). For production or managed service use, [contact SQLite Cloud, Inc](mailto:info@sqlitecloud.io) for a commercial license.

docker/Makefile.postgresql

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,17 @@ PG_INCLUDEDIR := $(shell $(PG_CONFIG) --includedir-server 2>/dev/null)
1212

1313
# Extension metadata
1414
EXTENSION = cloudsync
15-
EXTVERSION = 1.0
15+
# Read the binary version (full semver) from src/cloudsync.h, and derive
16+
# EXTVERSION as just MAJOR.MINOR. Rationale: PATCH bumps are binary-only
17+
# (no SQL surface change, no ALTER EXTENSION UPDATE needed); MINOR/MAJOR bumps
18+
# are the SQL-surface-changing ones that require a per-release upgrade script.
19+
# cloudsync_version() in the .so still reports the full semver for debugging.
20+
#
21+
# Recursive (=) rather than immediate (:=) assignment so the shell calls only
22+
# fire when a PG-related target actually references these variables. Non-PG
23+
# targets parse this included makefile without invoking sed/cut at all.
24+
CLOUDSYNC_VERSION_FULL = $(shell sed -n 's/^\#define CLOUDSYNC_VERSION[[:space:]]*"\([^"]*\)".*/\1/p' src/cloudsync.h)
25+
EXTVERSION = $(shell echo '$(CLOUDSYNC_VERSION_FULL)' | cut -d. -f1-2)
1626

1727
# Detect OS for platform-specific settings
1828
ifneq ($(OS),Windows_NT)
@@ -82,29 +92,61 @@ endif
8292
PG_EXTENSION_SQL = src/postgresql/$(EXTENSION)--$(EXTVERSION).sql
8393
PG_EXTENSION_CONTROL = docker/postgresql/$(EXTENSION).control
8494

95+
# Input templates (tracked). @EXTVERSION@ is substituted at build time.
96+
PG_EXTENSION_SQL_IN = src/postgresql/$(EXTENSION).sql.in
97+
PG_EXTENSION_CONTROL_IN = docker/postgresql/$(EXTENSION).control.in
98+
99+
# Upgrade scripts (cloudsync--<from>--<to>.sql) are hand-written per release.
100+
PG_MIGRATIONS_DIR = src/postgresql/migrations
101+
PG_MIGRATION_SQLS = $(wildcard $(PG_MIGRATIONS_DIR)/$(EXTENSION)--*--*.sql)
102+
85103
# ============================================================================
86104
# PostgreSQL Build Targets
87105
# ============================================================================
88106

89-
.PHONY: postgres-check postgres-build postgres-install postgres-package postgres-clean postgres-test \
107+
.PHONY: postgres-check postgres-check-migration postgres-generate-files postgres-build postgres-install postgres-package postgres-clean postgres-test \
90108
postgres-docker-build postgres-docker-build-asan postgres-docker-run postgres-docker-run-asan postgres-docker-stop postgres-docker-rebuild \
91109
postgres-docker-debug-build postgres-docker-debug-run postgres-docker-debug-rebuild \
92110
postgres-docker-shell postgres-dev-rebuild postgres-help unittest-pg \
93111
postgres-supabase-build postgres-supabase-rebuild postgres-supabase-run-smoke-test \
94112
postgres-docker-run-smoke-test
95113

114+
# Verify that a cloudsync--<prev>--<curr>.sql upgrade script exists for the
115+
# current CLOUDSYNC_VERSION in src/cloudsync.h. Release-blocking: a missing
116+
# upgrade script silently breaks ALTER EXTENSION cloudsync UPDATE for every
117+
# existing deployment. Runs in <1s; safe to call on every PR.
118+
postgres-check-migration:
119+
@scripts/check-postgres-migration.sh
120+
96121
# Check if PostgreSQL is available
97122
postgres-check:
98123
@echo "Checking PostgreSQL installation..."
99124
@which $(PG_CONFIG) > /dev/null || (echo "Error: pg_config not found. Install postgresql-server-dev." && exit 1)
125+
@[ -n "$(CLOUDSYNC_VERSION_FULL)" ] || (echo "Error: could not read CLOUDSYNC_VERSION from src/cloudsync.h" && exit 1)
126+
@[ -n "$(EXTVERSION)" ] || (echo "Error: could not derive MAJOR.MINOR EXTVERSION from CLOUDSYNC_VERSION '$(CLOUDSYNC_VERSION_FULL)'" && exit 1)
100127
@echo "PostgreSQL version: $$($(PG_CONFIG) --version)"
128+
@echo "CloudSync version : $(CLOUDSYNC_VERSION_FULL) (extension version $(EXTVERSION))"
101129
@echo "Extension directory: $(PG_PKGLIBDIR)"
102130
@echo "Share directory: $(PG_SHAREDIR)"
103131
@echo "Include directory: $(PG_INCLUDEDIR)"
104132

133+
# Render the versioned .sql install script and .control file from their .in
134+
# templates. This is a phony target (rather than file rules keyed on
135+
# $(PG_EXTENSION_SQL) / $(PG_EXTENSION_CONTROL)) so that $(EXTVERSION) never
136+
# appears in a rule's target or prerequisite position — those expansions
137+
# happen at parse time and would force sed/cut to run on every `make`
138+
# invocation, including for non-PG goals. Here, the references live inside
139+
# the recipe body and are only evaluated when this target actually fires.
140+
# Writes via a .tmp + atomic mv so a failed sed can't leave a half-rendered
141+
# output file in the extension share dir.
142+
postgres-generate-files: postgres-check
143+
@echo "Rendering extension files at version $(EXTVERSION) (binary $(CLOUDSYNC_VERSION_FULL))"
144+
@sed 's/@EXTVERSION@/$(EXTVERSION)/g' $(PG_EXTENSION_SQL_IN) > $(PG_EXTENSION_SQL).tmp && mv $(PG_EXTENSION_SQL).tmp $(PG_EXTENSION_SQL)
145+
@sed 's/@EXTVERSION@/$(EXTVERSION)/g' $(PG_EXTENSION_CONTROL_IN) > $(PG_EXTENSION_CONTROL).tmp && mv $(PG_EXTENSION_CONTROL).tmp $(PG_EXTENSION_CONTROL)
146+
105147
# Build PostgreSQL extension
106-
postgres-build: postgres-check
107-
@echo "Building PostgreSQL extension..."
148+
postgres-build: postgres-generate-files
149+
@echo "Building PostgreSQL extension (version $(EXTVERSION))..."
108150
@echo "Compiling source files..."
109151
@for src in $(PG_ALL_SRC); do \
110152
echo " CC $$src"; \
@@ -125,26 +167,37 @@ postgres-install: postgres-build
125167
install -m 644 $(PG_EXTENSION_SQL) $(PG_SHAREDIR)/extension/
126168
@echo "Installing control file to $(PG_SHAREDIR)/extension/"
127169
install -m 644 $(PG_EXTENSION_CONTROL) $(PG_SHAREDIR)/extension/
170+
@if [ -n "$(PG_MIGRATION_SQLS)" ]; then \
171+
echo "Installing $(words $(PG_MIGRATION_SQLS)) migration script(s) to $(PG_SHAREDIR)/extension/"; \
172+
install -m 644 $(PG_MIGRATION_SQLS) $(PG_SHAREDIR)/extension/; \
173+
fi
128174
@echo ""
129175
@echo "Installation complete!"
130176
@echo "To use the extension, run in psql:"
131177
@echo " CREATE EXTENSION $(EXTENSION);"
178+
@echo "To upgrade an existing installation, run in psql:"
179+
@echo " ALTER EXTENSION $(EXTENSION) UPDATE;"
132180

133181
# Package extension files for distribution
134182
PG_DIST_DIR = dist/postgresql
135183

136184
postgres-package: postgres-build
137-
@echo "Packaging PostgreSQL extension..."
185+
@echo "Packaging PostgreSQL extension (version $(EXTVERSION))..."
138186
@mkdir -p $(PG_DIST_DIR)
139187
cp $(PG_EXTENSION_LIB) $(PG_DIST_DIR)/
140188
cp $(PG_EXTENSION_SQL) $(PG_DIST_DIR)/
141189
cp $(PG_EXTENSION_CONTROL) $(PG_DIST_DIR)/
190+
@if [ -n "$(PG_MIGRATION_SQLS)" ]; then \
191+
echo "Including $(words $(PG_MIGRATION_SQLS)) migration script(s)"; \
192+
cp $(PG_MIGRATION_SQLS) $(PG_DIST_DIR)/; \
193+
fi
142194
@echo "Package ready in $(PG_DIST_DIR)/"
143195

144196
# Clean PostgreSQL build artifacts
145197
postgres-clean:
146198
@echo "Cleaning PostgreSQL build artifacts..."
147199
rm -f $(PG_OBJS) $(PG_EXTENSION_LIB)
200+
rm -f $(PG_EXTENSION_SQL) $(PG_EXTENSION_CONTROL)
148201
@echo "Clean complete"
149202

150203
# Test extension (requires running PostgreSQL)
@@ -314,7 +367,7 @@ postgres-supabase-build:
314367
echo "Using base image: $$supabase_cli_image"; \
315368
echo "Pulling fresh base image to avoid layer accumulation..."; \
316369
docker pull "$$supabase_cli_image" 2>/dev/null || true; \
317-
docker build --build-arg SUPABASE_POSTGRES_TAG="$(SUPABASE_POSTGRES_TAG)" -f "$$tmp_dockerfile" -t "$$supabase_cli_image" .; \
370+
docker build --build-arg SUPABASE_POSTGRES_TAG="$(SUPABASE_POSTGRES_TAG)" --build-arg CLOUDSYNC_VERSION="$(CLOUDSYNC_VERSION_FULL)" -f "$$tmp_dockerfile" -t "$$supabase_cli_image" .; \
318371
rm -f "$$tmp_dockerfile"; \
319372
echo "Build complete: $$supabase_cli_image"
320373

@@ -361,6 +414,8 @@ postgres-help:
361414
@echo ""
362415
@echo "Build & Install:"
363416
@echo " postgres-check - Verify PostgreSQL installation"
417+
@echo " postgres-check-migration - Verify a migration script exists for the current version"
418+
@echo " postgres-generate-files - Render cloudsync.control and cloudsync--<ext>.sql from templates"
364419
@echo " postgres-build - Build extension (.so file)"
365420
@echo " postgres-install - Install extension to PostgreSQL"
366421
@echo " postgres-clean - Clean build artifacts"

docker/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ docker/
1010
│ ├── Dockerfile # Custom PostgreSQL image
1111
│ ├── docker-compose.yml
1212
│ ├── init.sql # CloudSync metadata tables
13-
│ └── cloudsync.control
13+
│ └── cloudsync.control.in # template; cloudsync.control is generated at build time
1414
```
1515

1616
## Option 1: Standalone PostgreSQL

docker/postgresql/Dockerfile.release

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ RUN case "${TARGETARCH}" in \
3131
mkdir -p /tmp/cloudsync && \
3232
tar -xzf /tmp/cloudsync.tar.gz -C /tmp/cloudsync && \
3333
install -m 755 /tmp/cloudsync/cloudsync.so "$(pg_config --pkglibdir)/" && \
34-
install -m 644 /tmp/cloudsync/cloudsync--1.0.sql "$(pg_config --sharedir)/extension/" && \
34+
install -m 644 /tmp/cloudsync/cloudsync--*.sql "$(pg_config --sharedir)/extension/" && \
3535
install -m 644 /tmp/cloudsync/cloudsync.control "$(pg_config --sharedir)/extension/" && \
3636
rm -rf /tmp/cloudsync /tmp/cloudsync.tar.gz && \
3737
apt-get purge -y curl && apt-get autoremove -y && rm -rf /var/lib/apt/lists/*

docker/postgresql/Dockerfile.supabase

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,21 @@ RUN if [ ! -x "$CLOUDSYNC_PG_CONFIG" ]; then \
3030
# Collect build artifacts (avoid installing into the Nix store)
3131
RUN mkdir -p /tmp/cloudsync-artifacts/lib /tmp/cloudsync-artifacts/extension && \
3232
cp /tmp/cloudsync/cloudsync.so /tmp/cloudsync-artifacts/lib/ && \
33-
cp /tmp/cloudsync/src/postgresql/cloudsync--1.0.sql /tmp/cloudsync-artifacts/extension/ && \
34-
cp /tmp/cloudsync/docker/postgresql/cloudsync.control /tmp/cloudsync-artifacts/extension/
33+
cp /tmp/cloudsync/src/postgresql/cloudsync--*.sql /tmp/cloudsync-artifacts/extension/ && \
34+
cp /tmp/cloudsync/docker/postgresql/cloudsync.control /tmp/cloudsync-artifacts/extension/ && \
35+
# Include per-release upgrade scripts so ALTER EXTENSION ... UPDATE works
36+
if ls /tmp/cloudsync/src/postgresql/migrations/cloudsync--*--*.sql 1>/dev/null 2>&1; then \
37+
cp /tmp/cloudsync/src/postgresql/migrations/cloudsync--*--*.sql /tmp/cloudsync-artifacts/extension/; \
38+
fi
3539

3640
# Runtime image based on Supabase Postgres
3741
ARG SUPABASE_POSTGRES_TAG=17.6.1.071
3842
FROM public.ecr.aws/supabase/postgres:${SUPABASE_POSTGRES_TAG}
3943

44+
# Extension version (derived from src/cloudsync.h by the Makefile and passed in
45+
# as a build arg); used only for the image label.
46+
ARG CLOUDSYNC_VERSION=unknown
47+
4048
# Match builder pg_config path
4149
ENV CLOUDSYNC_PG_CONFIG=/root/.nix-profile/bin/pg_config
4250

@@ -85,5 +93,5 @@ EXPOSE 5432
8593
WORKDIR /
8694

8795
# Add label with extension version
88-
LABEL org.sqliteai.cloudsync.version="1.0" \
96+
LABEL org.sqliteai.cloudsync.version="${CLOUDSYNC_VERSION}" \
8997
org.sqliteai.cloudsync.description="PostgreSQL with CloudSync CRDT extension"

docker/postgresql/Dockerfile.supabase.release

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ RUN case "${TARGETARCH}" in \
4242
SHAREDIR_STD="/usr/share/postgresql" && \
4343
install -d "$PKGLIBDIR" "$SHAREDIR_PGCONFIG/extension" && \
4444
install -m 755 /tmp/cloudsync/cloudsync.so "$PKGLIBDIR/" && \
45-
install -m 644 /tmp/cloudsync/cloudsync--1.0.sql /tmp/cloudsync/cloudsync.control "$SHAREDIR_PGCONFIG/extension/" && \
45+
install -m 644 /tmp/cloudsync/cloudsync--*.sql /tmp/cloudsync/cloudsync.control "$SHAREDIR_PGCONFIG/extension/" && \
4646
if [ "$SHAREDIR_STD" != "$SHAREDIR_PGCONFIG" ]; then \
4747
install -d "$SHAREDIR_STD/extension" && \
48-
install -m 644 /tmp/cloudsync/cloudsync--1.0.sql /tmp/cloudsync/cloudsync.control "$SHAREDIR_STD/extension/"; \
48+
install -m 644 /tmp/cloudsync/cloudsync--*.sql /tmp/cloudsync/cloudsync.control "$SHAREDIR_STD/extension/"; \
4949
fi && \
5050
rm -rf /tmp/cloudsync /tmp/cloudsync.tar.gz && \
5151
apt-get purge -y curl && apt-get autoremove -y && rm -rf /var/lib/apt/lists/*

docker/postgresql/cloudsync.control

Lines changed: 0 additions & 22 deletions
This file was deleted.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# CloudSync PostgreSQL Extension Control File
2+
#
3+
# Generated from cloudsync.control.in by docker/Makefile.postgresql.
4+
# Do not edit the generated file; edit the .in template instead.
5+
# The version below is read from CLOUDSYNC_VERSION in src/cloudsync.h.
6+
7+
comment = 'CloudSync - CRDT-based multi-master database synchronization'
8+
default_version = '@EXTVERSION@'
9+
relocatable = true
10+
requires = ''
11+
superuser = false
12+
module_pathname = '$libdir/cloudsync'
13+
trusted = true

0 commit comments

Comments
 (0)