Skip to content

Commit

Permalink
Add pgrx test and reorganize PGXS tests
Browse files Browse the repository at this point in the history
Move the PGXS tests to a subdirectory separate testing PGXS from testing
zip vs. Git bundling in `cicd.yml`. Then add a pgrx "hello world"
project in `test/pgrx` and test it, too, including `pg_regress` tests.

Update `/etc/sudoers` and the `PATH` environment variable to allow
otherwise unprivileged users to do what they need to do.

Copy-edit the README a bit and explicitly set the PATH environment
variable in the `Dockerfile`, because `/etc/profile.d` scripts
apparently don't run in GitHub workflows.
  • Loading branch information
theory committed Apr 23, 2024
1 parent bf83f61 commit 843262f
Show file tree
Hide file tree
Showing 23 changed files with 200 additions and 45 deletions.
68 changes: 47 additions & 21 deletions .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ on:
schedule:
- cron: '0 14 3 * *' # Monthly at 2pm on the 3rd
jobs:
test:
name: 🐘 PostgreSQL ${{ matrix.pg }}
pgxs:
name: 🐘 PGXS on PostgreSQL ${{ matrix.pg }}
runs-on: ubuntu-latest
strategy:
matrix:
Expand All @@ -15,31 +15,57 @@ jobs:
- uses: actions/checkout@v4
- name: Build Image
run: docker build -t pgxn-tools-test .
# Test with Git repo
- name: Test Git as root
run: "docker run -w /repo --rm --volume \"$(pwd):/repo\" pgxn-tools-test ./test/runtest.sh ${{ matrix.pg }} git"
- name: Test Git as non-root
run: "docker run -w /repo --rm --volume \"$(pwd):/repo\" -e AS_USER=worker pgxn-tools-test ./test/runtest.sh ${{ matrix.pg }} git"
- name: Test Git with extra file
run: "docker run -w /repo --rm --volume \"$(pwd):/repo\" pgxn-tools-test ./test/runtest.sh ${{ matrix.pg }} git yes"
- name: Test Git with archive-all
- name: Test PGXS as root
run: "docker run -w /repo --rm --volume \"$(pwd):/repo\" pgxn-tools-test ./test/pgxs/runtest.sh ${{ matrix.pg }}"
# - name: Test PGXS as nobody
# run: "docker run -w /repo --rm --volume \"$(pwd):/repo\" -u nobody pgxn-tools-test ./test/pgxs/runtest.sh ${{ matrix.pg }}"
- name: Test PGXS as custom user
run: "docker run -w /repo --rm --volume \"$(pwd):/repo\" -e AS_USER=pgxn_worker pgxn-tools-test ./test/pgxs/runtest.sh ${{ matrix.pg }}"

pgrx:
name: 🦀 pgrx on PostgreSQL ${{ matrix.pg }}
runs-on: ubuntu-latest
strategy:
matrix:
pg: [16, 15, 14, 13, 12, 11]
steps:
- uses: actions/checkout@v4
- name: Build Image
run: docker build -t pgxn-tools-test .
- name: Test pgrx as root
run: "docker run -w /repo --rm --volume \"$(pwd):/repo\" pgxn-tools-test ./test/pgrx/runtest.sh ${{ matrix.pg }}"
# - name: Test pgrx as nobody
# run: "docker run -w /repo --rm --volume \"$(pwd):/repo\" -u nobody pgxn-tools-test ./test/pgrx/runtest.sh ${{ matrix.pg }}"
- name: Test pgrx as custom user
run: "docker run -w /repo --rm --volume \"$(pwd):/repo\" -e AS_USER=pgxn_worker pgxn-tools-test ./test/pgrx/runtest.sh ${{ matrix.pg }}"

bundle:
name: ${{ matrix.util.icon }} Bundle with ${{ matrix.util.name }}
runs-on: ubuntu-latest
strategy:
matrix:
util:
- { icon: 🏷️, name: git, dir: "", run: ./test/pgxs }
- { icon: 🤐, name: zip, dir: /test, run: ./pgxs }
env: { PG_VERSION: 16 }
steps:
- uses: actions/checkout@v4
- name: Build Image
run: docker build -t pgxn-tools-test .
- name: Test ${{ matrix.util.name }}
run: "docker run -w /repo --rm --volume \"$(pwd)${{ matrix.util.dir }}:/repo\" pgxn-tools-test ${{ matrix.util.run }}/runtest.sh ${{ env.PG_VERSION }} ${{ matrix.util[1] }}"
- name: Test ${{ matrix.util.name }} with extra file
run: "docker run -w /repo --rm --volume \"$(pwd)${{ matrix.util.dir }}:/repo\" pgxn-tools-test ${{ matrix.util.run }}/runtest.sh ${{ env.PG_VERSION }} ${{ matrix.util[1] }} yes"
- name: Test ${{ matrix.util.name }} with archive-all
env: { GIT_ARCHIVE_CMD: archive-all }
run: "docker run -w /repo --rm --volume \"$(pwd):/repo\" pgxn-tools-test ./test/runtest.sh ${{ matrix.pg }} git"
# Test without Git repo
- name: Test Zip as root
run: "docker run -w /repo --rm --volume \"$(pwd)/test:/repo\" pgxn-tools-test ./runtest.sh ${{ matrix.pg }} zip"
- name: Test Zip as non-root
run: "docker run -w /repo --rm --volume \"$(pwd)/test:/repo\" -e AS_USER=worker pgxn-tools-test ./runtest.sh ${{ matrix.pg }} zip"
- name: Test Zip with zip excluded file
run: "docker run -w /repo --rm --volume \"$(pwd)/test:/repo\" pgxn-tools-test ./runtest.sh ${{ matrix.pg }} zip yes"
# Test NO_CLUSTER
run: "docker run -w /repo --rm --volume \"$(pwd)${{ matrix.util.dir }}:/repo\" pgxn-tools-test ${{ matrix.util.run }}/runtest.sh ${{ env.PG_VERSION }} ${{ matrix.util[1] }}"
- name: Test NO_CLUSTER
run: "docker run -w /repo -e NO_CLUSTER=1 --rm --volume \"$(pwd):/repo\" pgxn-tools-test ./test/no_cluster_test.sh ${{ matrix.pg }}"
run: "docker run -w /repo -e NO_CLUSTER=1 --rm --volume \"$(pwd)${{ matrix.util.dir }}:/repo\" pgxn-tools-test ${{ matrix.util.run }}/no_cluster_test.sh ${{ env.PG_VERSION }}"

publish:
# Publish for a tag starting with v.
name: Push to Docker Hub
needs: test
needs: [pgxs, pgrx, bundle]
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
steps:
Expand Down
10 changes: 8 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,27 @@ FROM debian:bookworm-slim AS pgxn-config
ADD https://salsa.debian.org/postgresql/postgresql-common/-/raw/master/pgdg/apt.postgresql.org.sh /usr/local/bin/

RUN chmod +x /usr/local/bin/apt.postgresql.org.sh \
# Install apt dependencies
&& apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential clang llvm llvm-dev llvm-runtime cmake libtoml-parser-perl \
pgxnclient libtap-parser-sourcehandler-pgtap-perl libipc-run-perl libtest-simple-perl sudo gosu \
ca-certificates gnupg2 zip unzip libarchive-tools curl git libicu-dev libxml2 locales ssl-cert \
# Clean out unwanted stuff
&& apt-get -y purge postgresql-client-common \
&& apt-get clean \
&& rm -rf /var/cache/apt/* /var/lib/apt/lists/* \
# Install CPAN dependencies
&& curl -L https://cpanmin.us/ -o cpanm && chmod +x cpanm \
&& ./cpanm --notest PGXN::Meta::Validator \
&& rm -r cpanm ~/.cpanm \
# Configure sudoers to allow otherwise unprivileged users to do everything.
&& echo Defaults lecture = never >> /etc/sudoers \
&& echo 'Defaults env_keep += "CARGO_* RUSTUP_* PGRX_*"' >> /etc/sudoers \
&& perl -i -pe 's/\bALL$/NOPASSWD:ALL/g' /etc/sudoers \
&& echo 'postgres ALL=(ALL:ALL) NOPASSWD:ALL' >> /etc/sudoers \
&& echo 'nobody ALL=(ALL:ALL) NOPASSWD:ALL' >> /etc/sudoers \
&& perl -i -pe 's/^(Defaults\s+secure_path)/# $1/' /etc/sudoers \
# Ensure Git can do stuff in the working directory (issue #5).
&& git config --system --add safe.directory '*' \
# Install git-archive-all
Expand All @@ -27,10 +33,10 @@ RUN chmod +x /usr/local/bin/apt.postgresql.org.sh \
&& rm git_archive_all.py \
# Install the Rust toolchain
&& curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | env CARGO_HOME=/usr/share/cargo RUSTUP_HOME=/usr/share/rustup bash -s -- -y \
&& echo "PATH=\"${PATH}:/usr/share/cargo/bin\"" > /etc/profile.d/cargo.sh
&& chmod 0777 /usr/share/cargo /usr/share/cargo/bin

COPY bin/* /usr/local/bin/

ENV LC_ALL=C.UTF-8 LANG=C.UTF-8 CARGO_HOME=/usr/share/cargo RUSTUP_HOME=/usr/share/rustup
ENV LC_ALL=C.UTF-8 LANG=C.UTF-8 CARGO_HOME=/usr/share/cargo PGRX_HOME=/tmp/.pgrx RUSTUP_HOME=/usr/share/rustup PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/share/cargo/bin
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["/bin/bash"]
47 changes: 29 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ releases to PGXN. The image contains these utilities:

* [`pgxn`][cli]: The PGXN command-line client
* [`pg_prove`]: Runs and harnessing pgTAP tests
* [`pg-start`] Pass a PostgreSQL major version to install and starts a PostgreSQL cluster
* [`pg-build-test`]: Builds and tests an extension in the current directory
* [`pg-start`] Installs a major version of PostgreSQL and starts a cluster
* [`pg-build-test`]: Builds and tests a [PGXS] extension in the current directory
* [`pgrx-build-test`]: Builds and tests a [pgrx] extension in the current directory
* [`pgxn-bundle`]: Validates the PGXN META.json file and bundles up a release
* [`pgxn-release`]: Release to PGXN
Expand All @@ -35,10 +35,20 @@ directory.

### Unprivileged User

By default the container runs as `root`. To run as an unprivileged user, pass
the `AS_USER` environment variable and a user with that name will be created
with `sudo` privileges (already used by `pg-start`, `pg-build-test`, and
`pgrx-build-test`):
By default the container runs as `root`. To run as an unprivileged user, use
the `nobody` user, which has full permission to use `sudo` without a password
prompt (already used by `pg-start`, `pg-build-test`, and `pgrx-build-test`):

``` sh
docker run -it --rm -w /repo -u nobody \
--volume "$PWD:/repo" pgxn/pgxn-tools \
sh -c 'sudo pg-start 14 && pg-build-test'
```

#### Custom User

To run as some other custom unprivileged user, pass the `AS_USER` environment
variable and a user with that name will be created with `sudo` privileges:

``` sh
docker run -it --rm -w /repo -e AS_USER=worker \
Expand All @@ -56,10 +66,10 @@ docker run -it --rm -w /repo -e AS_USER=worker -e LOCAL_UID=$(id -u) \
sh -c 'sudo pg-start 14 && pg-build-test'
```

### Sudo-Enabled Users
#### Postgres User

The `nobody` user, included in the image, and the `postgres` user, created by
`pg-start`, also have full permission to use `sudo` without a password prompt.
The `postgres` user, created by `pg-start`, also has full permission to use
`sudo` without a password prompt.

GitHub Workflow
---------------
Expand All @@ -84,7 +94,7 @@ jobs:
- name: Check out the repo
uses: actions/checkout@v4
- name: Test on PostgreSQL ${{ matrix.pg }}
run: pg-build-test # or pgrx-build-test for a pgrx extension
run: pg-build-test # or pgrx-build-test
```

This example demonstrates automatic publishing of a release whenever a tag is
Expand Down Expand Up @@ -229,8 +239,8 @@ pg_createcluster --start 16 my16 -p 5416 -- -A trust
pg-build-test
```

Simply builds, installs, and tests a PostgreSQL extension or other code in the
current directory. Effectively the equivalent of:
Simply builds, installs, and tests a [PGXS] PostgreSQL extension or other code
in the current directory. Effectively the equivalent of:

``` sh
make
Expand All @@ -256,9 +266,9 @@ pg-build-test
pgrx-build-test
```

Build, install, and test a PostgreSQL [pgrx] extension. It reads the required
version of [pgrx] from the `Cargo.toml` file, which must be v0.11.4 or higher.
Effectively the equivalent of:
Builds, installs, and tests a PostgreSQL [pgrx] extension. It reads the
required version of [pgrx] from the `Cargo.toml` file, which must be v0.11.4
or higher. Effectively the equivalent of:

``` sh
cargo install --locked cargo-pgrx --version ${PGRX_VERSION}
Expand All @@ -274,9 +284,9 @@ appears to define the `installcheck` target, and emit the contents of the
`regression.diffs` file if it fails.

**Note:** Since `pgrx` uses `sudo` to start the cluster as the `postgres`
user, so some environment variables may not be present while tests run. If
your Rust code reads environment variables it should guard against
`NotPresent` errors to handle unexpectedly missing environment variables.
user, some environment variables may not be present while tests run. If your
Rust code reads environment variables it should guard against `NotPresent`
errors to handle unexpectedly missing environment variables.

### [`pgxn-bundle`]

Expand Down Expand Up @@ -474,3 +484,4 @@ Copyright (c) 2020-2024 The PGXN Maintainers. Distributed under the
[PostgreSQL TAP]: https://www.postgresql.org/docs/current/regress-tap.html
[TAP]: https://testanything.org "Test Anything Protocol"
[pgrx]: https://github.com/pgcentralfoundation/pgrx
[PGXS]: https://www.postgresql.org/docs/current/extend-pgxs.html
2 changes: 1 addition & 1 deletion bin/pgrx-build-test
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ run [qw(cargo pgrx package --test --pg-config), $pg_config] or exit $? >> 8;
# Install the extension.
# (Must come before test: https://github.com/pgcentralfoundation/pgrx/issues/1670)
say "### Installing $cfg->{package}{name}";
run [qw(cargo pgrx install --test --pg-config), $pg_config] or exit $? >> 8;
run [qw(sudo cargo pgrx install --test --pg-config), $pg_config] or exit $? >> 8;

# Run the tests as the postgres user.
say "### Testing $cfg->{package}{name}";
Expand Down
2 changes: 1 addition & 1 deletion test/.gitattributes
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.gitignore export-ignore
.gitattributes export-ignore
.github export-ignore
/runtest.sh export-ignore
runtest.sh export-ignore
3 changes: 3 additions & 0 deletions test/pgrx/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[target.'cfg(target_os="macos")']
# Postgres symbols won't be available until runtime
rustflags = ["-Clink-arg=-Wl,-undefined,dynamic_lookup"]
8 changes: 8 additions & 0 deletions test/pgrx/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.DS_Store
.idea/
/target
*.iml
**/*.rs.bk
Cargo.lock
regression.*
results/
32 changes: 32 additions & 0 deletions test/pgrx/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[package]
name = "hello"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[features]
default = ["pg13"]
pg11 = ["pgrx/pg11", "pgrx-tests/pg11" ]
pg12 = ["pgrx/pg12", "pgrx-tests/pg12" ]
pg13 = ["pgrx/pg13", "pgrx-tests/pg13" ]
pg14 = ["pgrx/pg14", "pgrx-tests/pg14" ]
pg15 = ["pgrx/pg15", "pgrx-tests/pg15" ]
pg16 = ["pgrx/pg16", "pgrx-tests/pg16" ]
pg_test = []

[dependencies]
pgrx = "=0.11.4"

[dev-dependencies]
pgrx-tests = "=0.11.4"

[profile.dev]
panic = "unwind"

[profile.release]
panic = "unwind"
opt-level = 3
lto = "fat"
codegen-units = 1
9 changes: 9 additions & 0 deletions test/pgrx/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
EXTENSION = hello
EXTVERSION = 0.1.0
TESTS = test/sql/base.sql
REGRESS = base
REGRESS_OPTS = --inputdir=test --load-extension=$(EXTENSION)
PG_CONFIG = pg_config

PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
5 changes: 5 additions & 0 deletions test/pgrx/hello.control
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
comment = 'hello: Created by pgrx'
default_version = '@CARGO_VERSION@'
module_pathname = '$libdir/hello'
relocatable = false
superuser = true
6 changes: 6 additions & 0 deletions test/pgrx/results/base.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
SELECT hello_hello();
hello_hello
--------------
Hello, hello
(1 row)

8 changes: 8 additions & 0 deletions test/pgrx/runtest.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/sh

set -eu

pgversion=$1
cd "$(dirname "$0")"
pg-start "$pgversion"
pgrx-build-test
34 changes: 34 additions & 0 deletions test/pgrx/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use pgrx::prelude::*;

pgrx::pg_module_magic!();

#[pg_extern]
fn hello_hello() -> &'static str {
"Hello, hello"
}

#[cfg(any(test, feature = "pg_test"))]
#[pg_schema]
mod tests {
use pgrx::prelude::*;

#[pg_test]
fn test_hello_hello() {
assert_eq!("Hello, hello", crate::hello_hello());
}

}

/// This module is required by `cargo pgrx test` invocations.
/// It must be visible at the root of your extension crate.
#[cfg(test)]
pub mod pg_test {
pub fn setup(_options: Vec<&str>) {
// perform one-off initialization when the pg_test framework starts
}

pub fn postgresql_conf_options() -> Vec<&'static str> {
// return any postgresql.conf settings that are required for your tests
vec![]
}
}
6 changes: 6 additions & 0 deletions test/pgrx/test/expected/base.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
SELECT hello_hello();
hello_hello
--------------
Hello, hello
(1 row)

1 change: 1 addition & 0 deletions test/pgrx/test/sql/base.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SELECT hello_hello();
File renamed without changes.
File renamed without changes.
File renamed without changes.
4 changes: 2 additions & 2 deletions test/runtest.sh → test/pgxs/runtest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ unzip "$zipfile"
if [ "$expectutil" = "git" ]; then
# Make sure runtest.sh was omitted thanks to .gitattributes.
if [ -f "$prefix/runtests.sh" ]; then
echo 'ERROR: Zip file contains runtests.sh and should not'
echo 'ERROR: Zip file contains runtests.sh but should not'
# shellcheck disable=SC2016
echo ' Did pgxn-bundle use `zip` instead of `git archive`?'
exit 2
Expand All @@ -51,7 +51,7 @@ if [ "$expectutil" = "git" ]; then
else
# Make sure runtest.sh is included in the zip file.
if [ ! -f "$prefix/runtest.sh" ]; then
echo 'ERROR: Zip file contains runtests.sh and should not'
echo 'ERROR: Zip file does not contain runtests.sh but should'
# shellcheck disable=SC2016
echo ' Did pgxn-bundle use `git archive` instead of `zip`?'
exit 2
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 comments on commit 843262f

Please sign in to comment.