├── .github └── workflows │ ├── build.yml │ └── service.yml ├── LICENSE.txt ├── README.md ├── action.yml └── index.js /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | runs-on: ${{ matrix.os }} 6 | strategy: 7 | fail-fast: false 8 | matrix: 9 | os: [ubuntu-24.04, ubuntu-24.04-arm, ubuntu-22.04, ubuntu-22.04-arm, macos-15, macos-14, macos-13] 10 | postgres-version: [17, 16, 15, 14, 13] 11 | include: 12 | - os: windows-2025 13 | postgres-version: 17 14 | - os: windows-2022 15 | postgres-version: 14 16 | - os: windows-2019 17 | postgres-version: 14 18 | steps: 19 | - uses: actions/checkout@v4 20 | - uses: ./. 21 | with: 22 | postgres-version: ${{ matrix.postgres-version }} 23 | config: | 24 | shared_preload_libraries = 'pg_stat_statements' 25 | database: testdb 26 | dev-files: ${{ matrix.postgres-version == 13 }} 27 | - run: createdb testdb2 28 | - run: psql -d testdb -c 'SHOW server_version' 29 | - run: psql -d testdb -h localhost -c 'SHOW server_version' 30 | - run: psql --version 31 | - run: pg_config 32 | - if: ${{ matrix.postgres-version == 13 }} 33 | run: test -f $(pg_config --includedir-server)/postgres.h 34 | -------------------------------------------------------------------------------- /.github/workflows/service.yml: -------------------------------------------------------------------------------- 1 | name: service 2 | on: workflow_dispatch 3 | jobs: 4 | service: 5 | runs-on: ubuntu-latest 6 | services: 7 | postgres: 8 | image: postgres 9 | env: 10 | POSTGRES_HOST_AUTH_METHOD: trust 11 | options: >- 12 | --health-cmd pg_isready 13 | --health-interval 10s 14 | --health-timeout 5s 15 | --health-retries 5 16 | ports: 17 | - 5432:5432 18 | steps: 19 | - run: psql -h localhost -U postgres -d postgres -c 'SHOW server_version' 20 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020-2025 Andrew Kane 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # setup-postgres 2 | 3 | The missing action for Postgres :tada: 4 | 5 | - Faster (with the default version) and simpler than containers 6 | - Works on Linux, Mac, and Windows 7 | - Supports different versions 8 | 9 | [![Build Status](https://github.com/ankane/setup-postgres/actions/workflows/build.yml/badge.svg)](https://github.com/ankane/setup-postgres/actions) 10 | 11 | ## Getting Started 12 | 13 | Add it as a step to your workflow 14 | 15 | ```yml 16 | - uses: ankane/setup-postgres@v1 17 | ``` 18 | 19 | ## Versions 20 | 21 | Specify a version 22 | 23 | ```yml 24 | - uses: ankane/setup-postgres@v1 25 | with: 26 | postgres-version: 17 27 | ``` 28 | 29 | Currently supports 30 | 31 | Version | `17` | `16` | `15` | `14` | `13` 32 | --- | --- | --- | --- | --- | --- 33 | `ubuntu-24.04` | ✓ | default | ✓ | ✓ | ✓ 34 | `ubuntu-24.04-arm` | ✓ | default | ✓ | ✓ | ✓ 35 | `ubuntu-22.04` | ✓ | ✓ | ✓ | default | ✓ 36 | `ubuntu-22.04-arm` | ✓ | ✓ | ✓ | default | ✓ 37 | `macos-15` | default | ✓ | ✓ | ✓ | ✓ 38 | `macos-14` | default | ✓ | ✓ | ✓ | ✓ 39 | `macos-13` | default | ✓ | ✓ | ✓ | ✓ 40 | `windows-2025` | default | | | | | 41 | `windows-2022` | | | | default | | 42 | `windows-2019` | | | | default | | 43 | 44 | Test against multiple versions 45 | 46 | ```yml 47 | strategy: 48 | matrix: 49 | postgres-version: [17, 16, 15, 14, 13] 50 | steps: 51 | - uses: ankane/setup-postgres@v1 52 | with: 53 | postgres-version: ${{ matrix.postgres-version }} 54 | ``` 55 | 56 | ## Options 57 | 58 | Create a database 59 | 60 | ```yml 61 | - uses: ankane/setup-postgres@v1 62 | with: 63 | database: testdb 64 | ``` 65 | 66 | Set `postgresql.conf` config 67 | 68 | ```yml 69 | - uses: ankane/setup-postgres@v1 70 | with: 71 | config: | 72 | shared_preload_libraries = 'pg_stat_statements' 73 | ``` 74 | 75 | Install development files (for building extensions) 76 | 77 | ```yml 78 | - uses: ankane/setup-postgres@v1 79 | with: 80 | dev-files: true 81 | ``` 82 | 83 | ## Extra Steps 84 | 85 | Run queries 86 | 87 | ```yml 88 | - run: psql -d testdb -c 'SHOW server_version' 89 | ``` 90 | 91 | ## Related Actions 92 | 93 | - [setup-mysql](https://github.com/ankane/setup-mysql) 94 | - [setup-mariadb](https://github.com/ankane/setup-mariadb) 95 | - [setup-mongodb](https://github.com/ankane/setup-mongodb) 96 | - [setup-elasticsearch](https://github.com/ankane/setup-elasticsearch) 97 | - [setup-opensearch](https://github.com/ankane/setup-opensearch) 98 | - [setup-sqlserver](https://github.com/ankane/setup-sqlserver) 99 | 100 | ## Contributing 101 | 102 | Everyone is encouraged to help improve this project. Here are a few ways you can help: 103 | 104 | - [Report bugs](https://github.com/ankane/setup-postgres/issues) 105 | - Fix bugs and [submit pull requests](https://github.com/ankane/setup-postgres/pulls) 106 | - Write, clarify, or fix documentation 107 | - Suggest or add new features 108 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: Setup Postgres 2 | inputs: 3 | postgres-version: 4 | description: The Postgres version to download (if necessary) and use 5 | config: 6 | description: Set config 7 | database: 8 | description: Database to create 9 | dev-files: 10 | description: Install development files 11 | runs: 12 | using: node20 13 | main: index.js 14 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const execSync = require('child_process').execSync; 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | const spawnSync = require('child_process').spawnSync; 5 | 6 | function run(command) { 7 | console.log(command); 8 | let env = Object.assign({}, process.env); 9 | env.HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK = '1'; 10 | execSync(command, {stdio: 'inherit', env: env}); 11 | } 12 | 13 | function runSafe() { 14 | const args = Array.from(arguments); 15 | console.log(args.join(' ')); 16 | const command = args.shift(); 17 | // spawn is safer and more lightweight than exec 18 | const ret = spawnSync(command, args, {stdio: 'inherit'}); 19 | if (ret.status !== 0) { 20 | throw ret.error; 21 | } 22 | } 23 | 24 | function addToPath(newPath) { 25 | fs.appendFileSync(process.env.GITHUB_PATH, `${newPath}\n`); 26 | } 27 | 28 | function isMac() { 29 | return process.platform == 'darwin'; 30 | } 31 | 32 | function isWindows() { 33 | return process.platform == 'win32'; 34 | } 35 | 36 | function isArm() { 37 | return process.arch == 'arm64'; 38 | } 39 | 40 | // TODO read each line and replace existing value if needed 41 | function setConfig(dir) { 42 | const config = process.env['INPUT_CONFIG']; 43 | if (config) { 44 | const file = path.join(dir, 'postgresql.conf'); 45 | 46 | if (isMac() || isWindows()) { 47 | fs.appendFileSync(file, config); 48 | } else { 49 | const tmpfile = '/tmp/postgresql.conf'; 50 | fs.writeFileSync(tmpfile, config); 51 | execSync(`cat ${tmpfile} | sudo tee -a ${file}`); 52 | } 53 | } 54 | } 55 | 56 | function updateHba(dir) { 57 | const contents = ` 58 | # TYPE DATABASE USER ADDRESS METHOD 59 | local all postgres peer 60 | local all all peer 61 | host all $USER 127.0.0.1/32 trust 62 | host all $USER ::1/128 trust 63 | host all all 127.0.0.1/32 md5 64 | host all all ::1/128 md5 65 | ` 66 | execSync(`echo "${contents}" | sudo tee ${dir}/pg_hba.conf`); 67 | } 68 | 69 | function formulaPresent(formula) { 70 | const tapPrefix = process.arch == 'arm64' ? '/opt/homebrew' : '/usr/local/Homebrew'; 71 | const tap = `${tapPrefix}/Library/Taps/homebrew/homebrew-core`; 72 | return fs.existsSync(`${tap}/Formula/${formula[0]}/${formula}.rb`) || fs.existsSync(`${tap}/Aliases/${formula}`); 73 | } 74 | 75 | function getDefaultVersion() { 76 | if (isMac() || process.env['ImageOS'] == 'win25') { 77 | return 17; 78 | } else if (process.env['ImageOS'] == 'ubuntu24') { 79 | return 16; 80 | } else { 81 | return 14; 82 | } 83 | } 84 | 85 | const defaultVersion = getDefaultVersion(); 86 | const postgresVersion = parseFloat(process.env['INPUT_POSTGRES-VERSION'] || defaultVersion); 87 | if (![18, 17, 16, 15, 14, 13, 12, 11, 10, 9.6].includes(postgresVersion)) { 88 | throw `Postgres version not supported: ${postgresVersion}`; 89 | } 90 | 91 | const database = process.env['INPUT_DATABASE']; 92 | 93 | let bin; 94 | 95 | if (isMac()) { 96 | const prefix = isArm() ? '/opt/homebrew' : '/usr/local'; 97 | 98 | bin = `${prefix}/opt/postgresql@${postgresVersion}/bin`; 99 | let dataDir = `${prefix}/var/postgresql@${postgresVersion}`; 100 | 101 | if (!fs.existsSync(bin)) { 102 | if (fs.existsSync(`${prefix}/opt/postgresql@14`)) { 103 | // remove previous version 104 | run(`brew unlink postgresql@14`); 105 | } 106 | 107 | if (!formulaPresent(`postgresql@${postgresVersion}`)) { 108 | run(`brew update`); 109 | } 110 | 111 | // install new version 112 | run(`brew install postgresql@${postgresVersion}`); 113 | } 114 | 115 | setConfig(dataDir); 116 | 117 | // start 118 | run(`${bin}/pg_ctl -w -D ${dataDir} start`); 119 | } else if (isWindows()) { 120 | const supportedVersion = process.env['ImageOS'] == 'win25' ? 17 : 14; 121 | if (postgresVersion != supportedVersion) { 122 | throw `Postgres version not supported on Windows: ${postgresVersion}`; 123 | } 124 | 125 | setConfig(process.env.PGDATA); 126 | 127 | // start 128 | run(`sc config postgresql-x64-${supportedVersion} start=auto`); 129 | run(`net start postgresql-x64-${supportedVersion}`); 130 | 131 | bin = process.env.PGBIN; 132 | } else { 133 | // removed in https://github.com/actions/virtual-environments/pull/3091 134 | if (!fs.existsSync('/etc/apt/sources.list.d/pgdg.list')) { 135 | // beta versions require extra component 136 | // development snapshots require this and -snapshot after pgdg 137 | // https://wiki.postgresql.org/wiki/Apt/FAQ 138 | const suffix = postgresVersion >= 18 ? ` ${postgresVersion}` : ""; 139 | const snapshot = postgresVersion >= 19 ? `-snapshot` : ""; 140 | run(`curl -s https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/apt.postgresql.org.gpg >/dev/null`) 141 | run(`echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg${snapshot} main${suffix}" | sudo tee /etc/apt/sources.list.d/pgdg.list`); 142 | } 143 | 144 | if (postgresVersion != defaultVersion || isArm()) { 145 | // remove previous cluster so port 5432 is used 146 | if (!isArm()) { 147 | run(`sudo pg_dropcluster ${defaultVersion} main`); 148 | 149 | if (postgresVersion < defaultVersion) { 150 | run(`sudo apt-get remove postgresql-${defaultVersion}`); 151 | } 152 | } 153 | 154 | // install new version 155 | run(`sudo apt-get update -o Dir::Etc::sourcelist="sources.list.d/pgdg.list" -o Dir::Etc::sourceparts="-" -o APT::Get::List-Cleanup="0"`); 156 | run(`sudo apt-get install postgresql-${postgresVersion}`); 157 | } 158 | 159 | const devFiles = process.env['INPUT_DEV-FILES']; 160 | // maybe support other truthy values in future 161 | if (devFiles == 'true') { 162 | run(`sudo apt-get update`); 163 | run(`sudo apt-get install postgresql-server-dev-${postgresVersion}`); 164 | } 165 | 166 | const dataDir = `/etc/postgresql/${postgresVersion}/main`; 167 | setConfig(dataDir); 168 | updateHba(dataDir); 169 | 170 | // start 171 | const startCmd = isArm() ? `restart` : `start`; 172 | run(`sudo systemctl ${startCmd} postgresql@${postgresVersion}-main`); 173 | 174 | // add user 175 | run(`sudo -iu postgres createuser -s $USER`); 176 | 177 | bin = `/usr/lib/postgresql/${postgresVersion}/bin`; 178 | } 179 | 180 | if (database) { 181 | runSafe(path.join(bin, "createdb"), database); 182 | } 183 | 184 | addToPath(bin); 185 | --------------------------------------------------------------------------------