Skip to content

Commit

Permalink
Add basic implementation
Browse files Browse the repository at this point in the history
The 'setup-postgres' action has been developed by me for
xsnippet/xsnippet-api [1] purpose where we want to run our tests on all
major operating systems. Existing actions are built around Docker
containers, and hence are available for Linux only.

I figured that the work I have done there might be useful for someone
else, and so I decided to create a standalone reusable action and
publish it on GitHub Marketplace.

[1] https://github.com/xsnippet/xsnippet-api/blob/40953d18b70e19d8535bf3aee88f28fe1407b36a/.github/actions/setup-postgres/action.yml
  • Loading branch information
ikalnytskyi committed Nov 22, 2021
0 parents commit 88ff92d
Show file tree
Hide file tree
Showing 5 changed files with 278 additions and 0 deletions.
62 changes: 62 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: CI

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
schedule:
- cron: "0 8 * * *"

jobs:
default:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- ubuntu-latest
- windows-latest
- macos-latest
steps:
- uses: actions/checkout@v2

- name: Run setup-postgres
uses: ./
id: postgres

- name: Run tests
run: |
python3 -m pip install --upgrade pip pytest psycopg
python3 -m pytest -vv test_action.py
env:
CONNECTION_URI: ${{ steps.postgres.outputs.connection-uri }}
EXPECTED_CONNECTION_URI: postgresql://postgres:postgres@localhost/postgres
shell: bash

parametrized:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- ubuntu-latest
- windows-latest
- macos-latest
steps:
- uses: actions/checkout@v2

- name: Run setup-postgres
uses: ./
with:
username: yoda
password: GrandMaster
database: jedi_order
id: postgres

- name: Run tests
run: |
python3 -m pip install --upgrade pip pytest psycopg
python3 -m pytest -vv test_action.py
env:
CONNECTION_URI: ${{ steps.postgres.outputs.connection-uri }}
EXPECTED_CONNECTION_URI: postgresql://yoda:GrandMaster@localhost/jedi_order
shell: bash
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2021 Ihor Kalnytskyi

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
52 changes: 52 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# setup-postgres

This action sets up a PostgreSQL server for the rest of the job. Here are some
key features:

* Runs on Linux, macOS and Windows runners.
* Adds PostgreSQL [binaries][1] (e.g. `psql`) to `PATH`.
* Uses PostgreSQL installed in [GitHub Actions Virtual Environments][2].
* [Easy to check][3] that IT DOES NOT contain malicious code.

[1]: https://www.postgresql.org/docs/current/reference-client.html
[2]: https://github.com/actions/virtual-environments
[3]: action.yml

## Usage

| Key | Value |
|----------|-----------------------------------------------------|
| URI | `postgresql://postgres:postgres@localhost/postgres` |
| Host | `localhost` |
| Port | `5432` |
| Username | `postgres` |
| Password | `postgres` |
| Database | `postgres` |

#### Basic

```yaml
steps:
- uses: ikalnytskyi/action-setup-postgres@v1
```
#### Advanced
```yaml
steps:
- uses: ikalnytskyi/action-setup-postgres@v1
with:
username: ci
password: sw0rdfish
database: test
id: postgres

- run: pytest -vv tests/
env:
DATABASE_URI: ${{ steps.postgres.outputs.connection-uri }}
```
## License
The scripts and documentation in this project are released under the
[MIT License](LICENSE).
78 changes: 78 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: Setup PostgreSQL for Linux/macOS/Windows
author: Ihor Kalnytskyi
description: Setup a preinstalled PostgreSQL server.
branding:
icon: database
color: purple
inputs:
username:
description: The username of the user to setup.
default: postgres
required: false
password:
description: The password of the user to setup.
default: postgres
required: false
database:
description: The database name to setup and grant permissions to created user.
default: postgres
required: false
outputs:
connection-uri:
description: The connection URI to connect to PostgreSQL.
value: ${{ steps.connection-uri.outputs.value }}
runs:
using: composite
steps:
- name: Prerequisites
run: |
if [ "$RUNNER_OS" == "Linux" ]; then
echo "$(pg_config --bindir)" >> $GITHUB_PATH
elif [ "$RUNNER_OS" == "Windows" ]; then
echo "$PGBIN" >> $GITHUB_PATH
echo "PQ_LIB_DIR=$PGROOT\lib" >> $GITHUB_ENV
fi
shell: bash


- name: Setup and start PostgreSQL
run: |
export PGDATA="$RUNNER_TEMP/pgdata"
pg_ctl init
# Forbid creating unix sockets since they are created by default in the
# directory we don't have permissions to.
echo "unix_socket_directories = ''" >> "$PGDATA/postgresql.conf"
pg_ctl start
# Both PGHOST and PGUSER are used by PostgreSQL tooling such as 'psql'
# or 'createuser'. Since PostgreSQL data has been resetup, we cannot
# rely on defaults anymore.
#
# PGHOST is required for Linux and macOS since they default to unix
# sockets, and we have turned them off.
#
# PGUSER is required for Windows since default the tooling's default
# user is 'postgres', while 'pg_ctl init' creates one with the name of
# the current user.
echo "PGHOST=localhost" >> $GITHUB_ENV
echo "PGUSER=${USER:-$USERNAME}" >> $GITHUB_ENV
shell: bash

- name: Setup PostgreSQL user and database
run: |
createuser --createdb ${{ inputs.username }}
if [ "${{ inputs.database}}" != "postgres" ]; then
createdb -O ${{ inputs.username }} ${{ inputs.database }}
fi
psql -c "ALTER USER ${{ inputs.username }} PASSWORD '${{ inputs.password }}';" ${{ inputs.database }}
shell: bash

- name: Expose connection URI
run: |
CONNECTION_URI="postgresql://${{ inputs.username }}:${{ inputs.password }}@localhost/${{ inputs.database }}"
echo ::set-output name=value::$CONNECTION_URI
shell: bash
id: connection-uri
65 changes: 65 additions & 0 deletions test_action.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import typing as t
import os

import psycopg
import pytest


@pytest.fixture(scope="function")
def connection_factory() -> t.Callable[[str], psycopg.Connection]:
def factory(connection_uri: str) -> psycopg.Connection:
return psycopg.connect(connection_uri)
return factory


@pytest.fixture(scope="function")
def connection(connection_factory) -> psycopg.Connection:
return connection_factory(os.getenv("CONNECTION_URI"))


def test_connection_uri():
"""Test that CONNECTION_URI matches EXPECTED_CONNECTION_URI."""

connection_uri = os.getenv("CONNECTION_URI")
expected_connection_uri = os.getenv("EXPECTED_CONNECTION_URI")
assert connection_uri == expected_connection_uri


def test_user_permissions(connection: psycopg.Connection):
"""Test that a user can create databases but is not a superuser."""

with connection:
record = connection \
.execute("SELECT usecreatedb, usesuper FROM pg_user WHERE usename = CURRENT_USER") \
.fetchone()
assert record

usecreatedb, usesuper = record
assert usecreatedb
assert not usesuper


def test_user_create_insert_select(connection: psycopg.Connection):
"""Test that a user has CRUD permissions in a database."""

table_name = "test_setup_postgres"

with connection, connection.transaction(force_rollback=True):
records = connection \
.execute(f"CREATE TABLE {table_name}(eggs INTEGER, rice VARCHAR)") \
.execute(f"INSERT INTO {table_name}(eggs, rice) VALUES (1, '42')") \
.execute(f"SELECT * FROM {table_name}") \
.fetchall()
assert records == [(1, "42")]


def test_user_create_drop_database(connection: psycopg.Connection):
"""Test that a user has no permissions to create databases."""

# CREATE/DROP DATABASE statements don't work within transactions, and with
# autocommit disabled transactions are created by psycopg automatically.
connection.autocommit = True

database_name = "foobar42"
connection.execute(f"CREATE DATABASE {database_name}")
connection.execute(f"DROP DATABASE {database_name}")

0 comments on commit 88ff92d

Please sign in to comment.