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
136 changes: 136 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
name: CI
on:
push:
branches:
- master
pull_request:
env:
PGUSER: postgres
jobs:
# Cheap gate that lets the test matrix below skip itself on commits that
# touch only docs. Runs on every push/pull_request unconditionally (no
# paths-ignore on the workflow itself) -- a workflow-level paths-ignore
# would skip this job too on a docs-only push, and the required
# all-checks-passed check would then never report and get stuck Pending in
# branch protection.
#
# Also derives the supported-PostgreSQL-major list for the test job's matrix
# (see the step below).
changes:
name: 🔍 Detect changes & derive PG matrix
runs-on: ubuntu-latest
outputs:
docs_only: ${{ steps.diff.outputs.docs_only }}
supported_pg: ${{ steps.pg.outputs.supported_pg }}
steps:
- name: Check out the repo
uses: actions/checkout@v4
with:
# Full history needed so BASE and HEAD below are both reachable
# for `git diff`.
fetch-depth: 0
- name: Compute per-push changed files
id: diff
run: |
# Fail-safe FIRST, before anything else runs: any early exit below
# (an unusable BASE/HEAD, a failed git diff) leaves this in place,
# so the test matrix only ever gets skipped after actually proving
# the push is docs-only.
echo "docs_only=false" >> "$GITHUB_OUTPUT"

if [ "${{ github.event_name }}" = "pull_request" ]; then
BASE="${{ github.event.pull_request.base.sha }}"
HEAD="${{ github.event.pull_request.head.sha }}"
else
BASE="${{ github.event.before }}"
HEAD="${{ github.event.after }}"
fi

echo "base=$BASE"
echo "head=$HEAD"

# A missing HEAD, or an all-zeros BASE (a new branch's first push,
# where GitHub reports no prior commit), means no real diff can be
# computed -- leave the fail-safe in place.
if [ -z "$HEAD" ] || [ -z "$BASE" ] || [[ "$BASE" =~ ^0+$ ]]; then
exit 0
fi

CHANGED=$(git diff --name-only "$BASE" "$HEAD") || exit 0
[ -z "$CHANGED" ] && exit 0

DOCS_ONLY=true
while IFS= read -r f; do
if ! [[ "$f" =~ \.(md|asc)$ ]]; then
DOCS_ONLY=false
break
fi
done <<< "$CHANGED"

echo "changed files:"
echo "$CHANGED"
echo "docs_only=$DOCS_ONLY" >> "$GITHUB_OUTPUT"
- name: Derive the supported-PostgreSQL-major list
id: pg
run: |
# SINGLE SOURCE OF TRUTH for the supported PostgreSQL majors. To add
# or drop a major, edit ONLY the constants below; the test job's
# matrix derives from them (adding the newest major is a one-line
# NEWEST bump). Do NOT hardcode a supported major in a job matrix.
#
# NEWEST -- highest PostgreSQL major tested.
# FLOOR -- oldest PostgreSQL major supported; keep it in sync
# with the PostgreSQL build prereq in META.in.json.
NEWEST=18
FLOOR=12

supported="$(seq "$NEWEST" -1 "$FLOOR")"

# Emit a JSON array for the test job's matrix to consume via
# fromJSON.
json=$(printf '%s\n' $supported | paste -sd, - | sed 's/^/[/; s/$/]/')
echo "supported_pg=$json" >> "$GITHUB_OUTPUT"

test:
needs: [changes]
if: needs.changes.outputs.docs_only != 'true'
strategy:
matrix:
# Supported majors, from the single source in the changes job.
pg: ${{ fromJSON(needs.changes.outputs.supported_pg) }}
name: 🐘 PostgreSQL ${{ matrix.pg }}
runs-on: ubuntu-latest
container: pgxn/pgxn-tools
steps:
- name: Start PostgreSQL ${{ matrix.pg }}
run: pg-start ${{ matrix.pg }}
- name: Check out the repo
uses: actions/checkout@v4
- name: Test on PostgreSQL ${{ matrix.pg }}
# verify-results runs the same TEST_DEPS chain as `make test`, then
# inspects the pgTAP results (PGXNTOOL_ENABLE_VERIFY_RESULTS defaults
# to yes, PGXNTOOL_VERIFY_RESULTS_MODE to pgtap) and exits non-zero on
# failure.
run: make verify-results
- name: Test the update path on PostgreSQL ${{ matrix.pg }}
# Same suite and expected output, but each test installs the Makefile's
# TEST_UPDATE_FROM version and runs ALTER EXTENSION UPDATE.
run: make verify-results TEST_LOAD_SOURCE=update

# A single stable check name for use as a required status check in branch
# protection rules. Matrix jobs produce check names like "🐘 PostgreSQL 14"
# which would all need to be listed individually and updated whenever the
# matrix changes. This job passes if all others passed or were skipped
# (e.g. test, on a docs-only push), and fails if any failed or were
# cancelled.
all-checks-passed:
needs: [changes, test]
if: always()
runs-on: ubuntu-latest
steps:
- name: Check all jobs passed or were skipped
run: |
if [[ "${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}" == "true" ]]; then
echo "One or more jobs failed or were cancelled"
exit 1
fi
9 changes: 0 additions & 9 deletions .travis.yml

This file was deleted.

3 changes: 3 additions & 0 deletions HISTORY.asc
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
STABLE
------
=== Require PostgreSQL 12 or newer
extension_drop pulls in cat_tools, and cat_tools 0.3.0 and newer require PostgreSQL 12.

=== Fix bug where extension could not be dropped
The extension now requires extension_drop and trunklet 0.3.3 or later, neither of which 0.2.0 required. Updating from trunklet-format 0.2.0 (PostgreSQL 12 or later) requires trunklet >= 0.3.3:

Expand Down
4 changes: 2 additions & 2 deletions META.in.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,14 @@
"X_comment": "Valid keys for 'prereqs' are configure, build, runtime, or test.",
"X_comment": "Valid keys for 'requires' are requires, recommends, suggests, or conflicts.",
"X_comment": "Requires JSONB, which was released in 9.4",
"X_comment": "trunklet currently requires 9.5",
"X_comment": "PostgreSQL 12: extension_drop pulls in cat_tools, and cat_tools >= 0.3.0 requires PostgreSQL 12",
"X_comment": "extension_drop: 0.1.1 fixes bug with dependencies",
"X_comment": "trunklet 0.3.3: the drop command uses template_language__remove(ignore_missing_functions), added in 0.3.3",
"prereqs": {

"build": {
"requires": {
"PostgreSQL": "9.5.0",
"PostgreSQL": "12.0.0",
"extension_drop": ">= 0.1.1",
"trunklet": ">= 0.3.3"
}
Expand Down
4 changes: 2 additions & 2 deletions META.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,14 @@
"X_comment": "Valid keys for 'prereqs' are configure, build, runtime, or test.",
"X_comment": "Valid keys for 'requires' are requires, recommends, suggests, or conflicts.",
"X_comment": "Requires JSONB, which was released in 9.4",
"X_comment": "trunklet currently requires 9.5",
"X_comment": "PostgreSQL 12: extension_drop pulls in cat_tools, and cat_tools >= 0.3.0 requires PostgreSQL 12",
"X_comment": "extension_drop: 0.1.1 fixes bug with dependencies",
"X_comment": "trunklet 0.3.3: the drop command uses template_language__remove(ignore_missing_functions), added in 0.3.3",
"prereqs": {

"build": {
"requires": {
"PostgreSQL": "9.5.0",
"PostgreSQL": "12.0.0",
"extension_drop": ">= 0.1.1",
"trunklet": ">= 0.3.3"
}
Expand Down
22 changes: 22 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
include pgxntool/base.mk

# TEST_LOAD_SOURCE selects how pg_temp._create() (test/deps.sql) installs the
# extension:
# - fresh (default): CREATE EXTENSION at the current version.
# - update: CREATE EXTENSION at TEST_UPDATE_FROM, then ALTER EXTENSION UPDATE
# to the current version. The suite and its expected output are the same,
# so a pass means the updated install behaves like a fresh one.
# The settings reach test/deps.sql as placeholder GUCs via PGOPTIONS, which the
# psql processes pg_regress spawns inherit. They are exported unconditionally so
# deps.sql can read them without missing_ok and fail if they didn't propagate.
TEST_LOAD_SOURCE ?= fresh
ifeq ($(filter $(TEST_LOAD_SOURCE),fresh update),)
$(error TEST_LOAD_SOURCE must be 'fresh' or 'update', got '$(TEST_LOAD_SOURCE)')
endif
TEST_UPDATE_FROM ?= 0.2.0
export PGOPTIONS := $(PGOPTIONS) -c trunklet_format.test_load_mode=$(TEST_LOAD_SOURCE) -c trunklet_format.test_update_from=$(TEST_UPDATE_FROM)

# Recurses rather than depending on `test` so the TEST_LOAD_SOURCE check above
# re-evaluates with update set.
.PHONY: test-update
test-update:
$(MAKE) test TEST_LOAD_SOURCE=update

# TODO: Remove this after merging pgxntool 0.2.1+
testdeps: $(TEST_SQL_FILES) $(TEST_SOURCE_FILES)

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ You need to run the test suite using a super user, such as the default
make installcheck PGUSER=postgres

Once trunklet-format is installed, you can add it to a database. If you're running
PostgreSQL 9.1.0 or greater, it's a simple as connecting to a database as a
PostgreSQL 12 or greater, it's a simple as connecting to a database as a
super user and running:

CREATE EXTENSION "trunklet-format";
Expand Down
29 changes: 0 additions & 29 deletions pg-travis-test.sh

This file was deleted.

21 changes: 20 additions & 1 deletion test/deps.sql
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,28 @@
CREATE OR REPLACE FUNCTION pg_temp._create() RETURNS void LANGUAGE plpgsql
SET client_min_messages = WARNING -- Necessary due to CASCADE
AS $body$
DECLARE
c_mode CONSTANT text := current_setting('trunklet_format.test_load_mode'); -- Set by the Makefile
BEGIN
-- No IF NOT EXISTS because we'll be confused if we're not loading the new stuff
CREATE EXTENSION "trunklet-format" CASCADE;
IF c_mode = 'fresh' THEN
CREATE EXTENSION "trunklet-format" CASCADE;
ELSIF c_mode = 'update' THEN
-- 0.2.0 did not require extension_drop, so updating from it needs it created first
CREATE EXTENSION IF NOT EXISTS extension_drop CASCADE;
EXECUTE format(
'CREATE EXTENSION "trunklet-format" VERSION %L CASCADE'
, current_setting('trunklet_format.test_update_from')
);
ALTER EXTENSION "trunklet-format" UPDATE;
IF (SELECT extversion FROM pg_extension WHERE extname = 'trunklet-format')
IS DISTINCT FROM (SELECT default_version FROM pg_available_extensions WHERE name = 'trunklet-format')
THEN
RAISE EXCEPTION 'ALTER EXTENSION UPDATE did not reach the default version';
END IF;
ELSE
RAISE EXCEPTION 'unknown trunklet_format.test_load_mode "%"', c_mode;
END IF;
END
$body$;

57 changes: 30 additions & 27 deletions test/expected/base.out
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ ok 24 - threw Unexpected character "i" in format specifier "i"
ok 25 - threw Unexpected character "i" in format specifier "Oi"
ok 26 - threw Unexpected character "l" in format specifier "l"
ok 27 - threw Unexpected character "l" in format specifier "Ol"
ok 28 - Should throw exception like 'parameter "mia" not found'
ok 29 - Should throw exception like 'parameter "mia" not found'
ok 30 - Should throw exception like 'parameter "mia" not found'
ok 28 - DETAIL for 'some stuff%test parameter%OIsome more stuff'
ok 29 - DETAIL for 'more%Moo%OSmore'
ok 30 - DETAIL for 'more%Moo%lmore'
ok 31 - Should throw exception like 'parameter "mia" not found'
ok 32 - Should throw exception like 'parameter "mia" not found'
ok 33 - Should throw exception like 'parameter "mia" not found'
Expand All @@ -59,28 +59,31 @@ ok 48 - Should throw exception like 'parameter "mia" not found'
ok 49 - Should throw exception like 'parameter "mia" not found'
ok 50 - Should throw exception like 'parameter "mia" not found'
ok 51 - Should throw exception like 'parameter "mia" not found'
ok 52 - Test extract_paramaters() for single string
ok 53 - Test extract_paramaters() for number
ok 54 - Test extract_paramaters() for multiple
ok 55 - Test extract_paramaters() for num & bogus
ok 56 - Test extract_paramaters() for bogus
ok 57 - SELECT trunklet.process_language('format', 'template', ('{"value": 1.1}'::jsonb)->'value')
ok 58 - SELECT trunklet.process_language('format', 'template', ('{"value": "text"}'::jsonb)->'value')
ok 59 - SELECT trunklet.process_language('format', 'template', ('{"value": true}'::jsonb)->'value')
ok 60 - SELECT trunklet.process_language('format', 'template', ('{"value": [1, 2, 3]}'::jsonb)->'value')
ok 61 - SELECT trunklet.extract_parameters('format', ('{"value": 1.1}'::jsonb)->'value', '{a}'::text[])
ok 62 - SELECT trunklet.extract_parameters('format', ('{"value": "text"}'::jsonb)->'value', '{a}'::text[])
ok 63 - SELECT trunklet.extract_parameters('format', ('{"value": true}'::jsonb)->'value', '{a}'::text[])
ok 64 - SELECT trunklet.extract_parameters('format', ('{"value": [1, 2, 3]}'::jsonb)->'value', '{a}'::text[])
ok 65 - process_language('format', 'template', %L::jsonb): parameter names may not contain "%"
ok 66 - process_language('format', 'template', %L::jsonb): parameter names may not contain "%"
ok 67 - process_language('format', 'template', %L::jsonb): parameter names may not contain "%"
ok 68 - extract_parameters('format', %L::jsonb, '{a}'::text[]): parameter names may not contain "%"
ok 69 - extract_parameters('format', %L::jsonb, '{a}'::text[]): parameter names may not contain "%"
ok 70 - extract_parameters('format', %L::jsonb, '{a}'::text[]): parameter names may not contain "%"
ok 71 - threw object is not supported as a parameter type
ok 72 - threw array is not supported as a parameter type
ok 73 - threw object is not supported as a parameter type
ok 74 - threw array is not supported as a parameter type
1..74
ok 52 - Should throw exception like 'parameter "mia" not found'
ok 53 - Should throw exception like 'parameter "mia" not found'
ok 54 - Should throw exception like 'parameter "mia" not found'
ok 55 - Test extract_paramaters() for single string
ok 56 - Test extract_paramaters() for number
ok 57 - Test extract_paramaters() for multiple
ok 58 - Test extract_paramaters() for num & bogus
ok 59 - Test extract_paramaters() for bogus
ok 60 - SELECT trunklet.process_language('format', 'template', ('{"value": 1.1}'::jsonb)->'value')
ok 61 - SELECT trunklet.process_language('format', 'template', ('{"value": "text"}'::jsonb)->'value')
ok 62 - SELECT trunklet.process_language('format', 'template', ('{"value": true}'::jsonb)->'value')
ok 63 - SELECT trunklet.process_language('format', 'template', ('{"value": [1, 2, 3]}'::jsonb)->'value')
ok 64 - SELECT trunklet.extract_parameters('format', ('{"value": 1.1}'::jsonb)->'value', '{a}'::text[])
ok 65 - SELECT trunklet.extract_parameters('format', ('{"value": "text"}'::jsonb)->'value', '{a}'::text[])
ok 66 - SELECT trunklet.extract_parameters('format', ('{"value": true}'::jsonb)->'value', '{a}'::text[])
ok 67 - SELECT trunklet.extract_parameters('format', ('{"value": [1, 2, 3]}'::jsonb)->'value', '{a}'::text[])
ok 68 - process_language('format', 'template', %L::jsonb): parameter names may not contain "%"
ok 69 - process_language('format', 'template', %L::jsonb): parameter names may not contain "%"
ok 70 - process_language('format', 'template', %L::jsonb): parameter names may not contain "%"
ok 71 - extract_parameters('format', %L::jsonb, '{a}'::text[]): parameter names may not contain "%"
ok 72 - extract_parameters('format', %L::jsonb, '{a}'::text[]): parameter names may not contain "%"
ok 73 - extract_parameters('format', %L::jsonb, '{a}'::text[]): parameter names may not contain "%"
ok 74 - threw object is not supported as a parameter type
ok 75 - threw array is not supported as a parameter type
ok 76 - threw object is not supported as a parameter type
ok 77 - threw array is not supported as a parameter type
1..77
# TRANSACTION INTENTIONALLY LEFT OPEN!
Loading
Loading