Skip to content
Closed
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
112 changes: 112 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Builds every PostgreSQL version under binaries/ for every platform we ship,
# smoke-tests each tarball, and on a version tag publishes them all as one
# release. A release is only created when every build and test passed, so a
# release never carries one platform without the other.
#
# Platforms:
# linux_x86_64 ubuntu-24.04 pinned: the binaries link the runner's libicu
# and glibc, which must match the Ubuntu 24.04
# build workers that run them
# darwin_arm64 macos-14 Apple Silicon
#
# To release: bump RELEASE_VERSION in build_defs/postgres.build_defs, merge,
# then push the matching tag (git tag v0.0.N && git push origin v0.0.N).
name: Release binaries

on:
pull_request:
push:
branches: [master]
tags: ["v*"]
workflow_dispatch:

permissions:
contents: read

jobs:
versions:
runs-on: ubuntu-24.04
outputs:
versions: ${{ steps.list.outputs.versions }}
steps:
- uses: actions/checkout@v4
- id: list
run: echo "versions=$(ls binaries | jq -R . | jq -sc .)" >> "$GITHUB_OUTPUT"

build:
needs: versions
strategy:
fail-fast: false
matrix:
version: ${{ fromJSON(needs.versions.outputs.versions) }}
runner: [ubuntu-24.04, macos-14]
runs-on: ${{ matrix.runner }}
name: ${{ matrix.version }} (${{ matrix.runner }})
steps:
- uses: actions/checkout@v4

- name: Install build dependencies (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y build-essential flex bison perl pkg-config \
libicu-dev libreadline-dev zlib1g-dev uuid-dev

- name: Install build dependencies (macOS)
if: runner.os == 'macOS'
run: |
brew install icu4c readline pkgconf
# postgres.build_defs finds them at these paths; fail here, not mid-configure
test -d /opt/homebrew/opt/icu4c/lib/pkgconfig
test -d /opt/homebrew/opt/readline/lib/pkgconfig

- name: Build
run: ./pleasew build //binaries/${{ matrix.version }}/...

- name: Smoke test
run: |
set -euo pipefail
for tarball in $(./pleasew query output //binaries/${{ matrix.version }}/... | grep 'psql-.*\.tar\.gz$'); do
case "$(basename "$tarball")" in
psql-ai-*) variant=ai ;;
*) variant=base ;;
esac
release/smoke_test.sh "$tarball" "$variant"
cp "$tarball" "$RUNNER_TEMP/"
done

- uses: actions/upload-artifact@v4
with:
name: psql-${{ matrix.version }}-${{ matrix.runner }}
path: ${{ runner.temp }}/psql-*.tar.gz
if-no-files-found: error

release:
if: startsWith(github.ref, 'refs/tags/v')
needs: build
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- uses: actions/checkout@v4

- name: Check the tag matches RELEASE_VERSION
run: |
RELEASE_VERSION=$(sed -n 's/^RELEASE_VERSION = "\(.*\)"$/\1/p' build_defs/postgres.build_defs)
if [[ "$RELEASE_VERSION" != "$GITHUB_REF_NAME" ]]; then
echo "Tag $GITHUB_REF_NAME does not match RELEASE_VERSION $RELEASE_VERSION in build_defs/postgres.build_defs"
exit 1
fi

- uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true

- name: Publish
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release create "$GITHUB_REF_NAME" dist/psql-*.tar.gz \
--title "PostgreSQL Release $GITHUB_REF_NAME" \
--notes "PostgreSQL binaries for linux_x86_64 and darwin_arm64, built and smoke-tested by the Release binaries workflow."
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,19 @@ You may need to pass the `-L /path/to/psql/share` flag to `initdb`.

You can find all available versions and corresponding binaries on the [releases page](https://github.com/becomeliminal/postgres/releases).

Releases are built by CI, never from a laptop. The
[Release binaries](.github/workflows/release.yaml) workflow builds every
version under `binaries/` on `ubuntu-24.04` (linux_x86_64) and `macos-14`
(darwin_arm64), and smoke-tests each tarball with `release/smoke_test.sh`
(starts postgres, creates the extensions it ships). It runs on every pull
request and push to master. To cut a release:

1. Bump `RELEASE_VERSION` in `build_defs/postgres.build_defs` and merge.
2. Push the matching tag: `git tag v0.0.N && git push origin v0.0.N`.

The workflow publishes the release only if every build and smoke test passed,
and refuses a tag that doesn't match `RELEASE_VERSION`.

---

## Need Additional Pre-Built Binaries?
Expand Down
10 changes: 5 additions & 5 deletions build_defs/postgres.build_defs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def postgres(name:str, version:str, os=None, arch=None, hashes:list=[], visibili
f'mkdir -p {target}/psql',
'cd $SRC',
env_prefix + f'./configure --prefix "$HOME/{target}/psql" --with-uuid=e2fs ' + ' '.join(configure_flags),
'make world-bin',
'make -j"$(getconf _NPROCESSORS_ONLN)" world-bin',
'make install-world-bin',
]
# On macOS, rewrite dylib paths from absolute to relative using @loader_path
Expand Down Expand Up @@ -98,7 +98,7 @@ def postgres(name:str, version:str, os=None, arch=None, hashes:list=[], visibili
"share": [f'{target}/psql/share'],
},
cmd = ' && '.join(install_cmds),
build_timeout = 1200,
build_timeout = 3600,
building_description = f'Installing PostgreSQL v{version} for {target}...',
)
if _compress:
Expand Down Expand Up @@ -209,7 +209,7 @@ def postgres_ai(name:str, version:str, pgvector_version:str="0.8.0", age_version
'tar -xzf $SRCS_PG',
f'cd postgresql-{version}',
env_prefix + f'./configure --prefix "{psql_dir}" --with-uuid=e2fs ' + ' '.join(configure_flags),
'make world-bin',
'make -j"$(getconf _NPROCESSORS_ONLN)" world-bin',
'make install-world-bin',
'cd $HOME',
# Build and install pgvector
Expand Down Expand Up @@ -256,7 +256,7 @@ def postgres_ai(name:str, version:str, pgvector_version:str="0.8.0", age_version
"share": [f'{out_dir}/psql/share'],
},
cmd = ' && '.join(install_cmds),
build_timeout = 1200,
build_timeout = 3600,
building_description = f'Installing PostgreSQL AI v{version} for {target}...',
)
if _compress:
Expand Down Expand Up @@ -291,7 +291,7 @@ def postgres_ai(name:str, version:str, pgvector_version:str="0.8.0", age_version
)


RELEASE_VERSION = "v0.0.5"
RELEASE_VERSION = "v0.0.6"


def _resolve_target():
Expand Down
52 changes: 52 additions & 0 deletions release/smoke_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/bin/bash
# Proves a release tarball works on the machine it was built for: unpack it,
# start postgres from it, and create the extensions it claims to ship.
#
# release/smoke_test.sh <tarball> <base|ai>
set -euo pipefail

TARBALL="$1"
VARIANT="$2"

WORK=$(mktemp -d)
PGDATA="$WORK/data"
cleanup() {
"$WORK/psql/bin/pg_ctl" -D "$PGDATA" stop -m immediate > /dev/null 2>&1 || true
rm -rf "$WORK"
}
trap cleanup EXIT

mkdir -p "$WORK/psql"
tar -xzf "$TARBALL" -C "$WORK/psql"
BIN="$WORK/psql/bin"
# Linux binaries carry no rpath: consumers point the loader at lib/, as this
# does. (macOS binaries find libpq via @executable_path instead.)
export LD_LIBRARY_PATH="$WORK/psql/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"

"$BIN/initdb" -D "$PGDATA" --no-locale --encoding=UTF8 > /dev/null
PRELOAD="pg_stat_statements"
if [[ "$VARIANT" == "ai" ]]; then
PRELOAD="pg_stat_statements,age"
fi
cat >> "$PGDATA/postgresql.conf" <<EOF
listen_addresses = ''
unix_socket_directories = '$WORK'
shared_preload_libraries = '$PRELOAD'
EOF
"$BIN/pg_ctl" -D "$PGDATA" -l "$WORK/postgres.log" start -w > /dev/null || {
cat "$WORK/postgres.log"
exit 1
}

psql() { "$BIN/psql" -h "$WORK" -d postgres --no-psqlrc -v ON_ERROR_STOP=1 -t -A "$@"; }

psql -c 'CREATE EXTENSION "uuid-ossp";' -c 'SELECT uuid_generate_v4();'
psql -c 'CREATE EXTENSION pg_stat_statements;' -c 'SELECT count(*) FROM pg_stat_statements;'
if [[ "$VARIANT" == "ai" ]]; then
psql -c 'CREATE EXTENSION vector;' -c "SELECT '[1,2,3]'::vector <-> '[1,2,4]'::vector;"
psql -c 'CREATE EXTENSION age;' -c "LOAD 'age';" \
-c 'SET search_path = ag_catalog, "$user", public;' \
-c "SELECT create_graph('smoke');"
fi

echo "PASS: $(basename "$TARBALL")"
Loading