|
| 1 | +# Contributing: Adding a New PostgreSQL Extension |
| 2 | + |
| 3 | +This guide walks you through the lifecycle of adding a new extension, from |
| 4 | +setting up your environment to submitting a Pull Request. |
| 5 | + |
| 6 | +> [!IMPORTANT] |
| 7 | +> Please ensure you have also read the general |
| 8 | +> [CONTRIBUTING.md](https://github.com/cloudnative-pg/governance/blob/main/CONTRIBUTING.md) |
| 9 | +> for CloudNativePG before proceeding. |
| 10 | +
|
| 11 | +## 1. Phase One: Fork, Clone, and Validate |
| 12 | + |
| 13 | +Before proposing a change, ensure your local machine is compatible with the |
| 14 | +[build stack](BUILD.md). |
| 15 | + |
| 16 | +1. **Fork** the [cloudnative-pg/postgres-extensions-containers](https://github.com/cloudnative-pg/postgres-extensions-containers) repository. |
| 17 | +2. **Clone** your fork and enter the directory: |
| 18 | +```sh |
| 19 | +git clone https://github.com/<your-username>/postgres-extensions-containers.git |
| 20 | +cd postgres-extensions-containers |
| 21 | +``` |
| 22 | +3. **Verify the Environment:** Run the following to ensure you can build the |
| 23 | + existing project ecosystem. |
| 24 | +```sh |
| 25 | +task prereqs # Check if Go, Task, and Docker are ready |
| 26 | +task checks:all # Validate current configurations |
| 27 | +task bake:all # Optional: build all existing extensions to confirm the Dagger engine |
| 28 | +``` |
| 29 | + |
| 30 | +--- |
| 31 | + |
| 32 | +## 2. Phase Two: The Proposal & Package Discovery |
| 33 | + |
| 34 | +To maintain high standards and avoid duplicated effort or architectural |
| 35 | +conflicts, every new extension begins with a formal proposal. |
| 36 | +During this phase, you must verify that the extension is available in the PGDG |
| 37 | +(PostgreSQL Global Development Group) repositories and identify its versioning |
| 38 | +logic. |
| 39 | + |
| 40 | +### Identifying the Package & Version |
| 41 | + |
| 42 | +You must verify the package across both the current Debian `stable` and |
| 43 | +`oldstable` distributions to ensure compatibility. Use a temporary container to |
| 44 | +search the repositories: |
| 45 | + |
| 46 | +For Debian `stable` (13, `trixie`): |
| 47 | + |
| 48 | +```sh |
| 49 | +docker run -u root -ti --rm ghcr.io/cloudnative-pg/postgresql:18-minimal-trixie |
| 50 | +``` |
| 51 | + |
| 52 | +For Debian `oldstable` (12, `bookworm`): |
| 53 | + |
| 54 | +```sh |
| 55 | +docker run -u root -ti --rm ghcr.io/cloudnative-pg/postgresql:18-minimal-bookworm |
| 56 | +``` |
| 57 | + |
| 58 | +Then, inside the container: |
| 59 | + |
| 60 | +```sh |
| 61 | +apt update && apt search <EXTENSION_NAME> |
| 62 | +``` |
| 63 | + |
| 64 | +#### Understanding the Version String |
| 65 | + |
| 66 | +Take note of both the **package name** and the **version string**. |
| 67 | +Using `pgvector` as an example, you will notice that while the package name |
| 68 | +remains constant, the versioning reflects the underlying Debian release: |
| 69 | + |
| 70 | +- `trixie`: `0.8.2-1.pgdg13+1` |
| 71 | +- `bookworm`: `0.8.2-1.pgdg12+1` |
| 72 | + |
| 73 | +> [!IMPORTANT] |
| 74 | +> The `pgdg13` or `pgdg12` suffix is critical. Correctly identifying this |
| 75 | +> versioning pattern ensures that `renovate` can automatically monitor the |
| 76 | +> upstream repositories and trigger update Pull Requests once your extension is |
| 77 | +> merged. |
| 78 | +
|
| 79 | +#### Inspecting the Package Content |
| 80 | + |
| 81 | +If you want to get a list of the files contained in the package, you need to |
| 82 | +first install the extension in the disposable container: |
| 83 | + |
| 84 | +```sh |
| 85 | +apt install <package-name> |
| 86 | +``` |
| 87 | + |
| 88 | +Then, list the content of the package with: |
| 89 | + |
| 90 | +```sh |
| 91 | +dpkg -L <full-package-name> |
| 92 | +``` |
| 93 | + |
| 94 | +Confirm that `.control` and `.sql` files are present in the expected PostgreSQL |
| 95 | +paths. |
| 96 | + |
| 97 | +> [!IMPORTANT] |
| 98 | +> If the package doesn't contain any `.control` file, it is likely to be a |
| 99 | +> **PostgreSQL module** rather than an extension. In this case, remember to set |
| 100 | +> the `create_extension` option to `false` in your `metadata.hcl` file. |
| 101 | +
|
| 102 | +### Opening an Issue |
| 103 | + |
| 104 | +> [!IMPORTANT] |
| 105 | +> **Community Commitment:** By opening the issue, you are confirming your |
| 106 | +> intent to help maintain this extension on behalf of the CloudNativePG |
| 107 | +> community. |
| 108 | +
|
| 109 | +After gathering the package details and verifying the extension's license, |
| 110 | +submit your proposal: |
| 111 | + |
| 112 | +1. Point your browser to ["New Extension Proposal"](https://github.com/cloudnative-pg/postgres-extensions-containers/issues/new/choose). |
| 113 | +2. Provide the package name, versioning info, and a link to the upstream source. |
| 114 | +3. State the license clearly: |
| 115 | + - CNCF-Allowed: licenses on the [CNCF Allowlist](https://github.com/cncf/foundation/blob/main/policies-guidance/allowed-third-party-license-policy.md) (e.g., Apache-2.0, MIT, or PostgreSQL) are generally pre-approved. |
| 116 | + - Other Open Source: licenses like FSF-approved (GNU GPL) will be evaluated on a case-by-case basis. |
| 117 | + - Redistribution: since we redistribute unmodified software, ensure you |
| 118 | + identify where the upstream source code can be found (required for GNU |
| 119 | + GPL compliance). |
| 120 | + |
| 121 | +> [!NOTE] |
| 122 | +> You do not need to wait for maintainer approval to begin development or |
| 123 | +> submit your PR. You are encouraged to proceed immediately; however, please be |
| 124 | +> aware that if a fundamental issue (e.g., licensing) is discovered during the |
| 125 | +> proposal review, you may need to modify or discard your work. |
| 126 | +
|
| 127 | +--- |
| 128 | + |
| 129 | +## 3. Phase Three: Implementation & Scaffolding |
| 130 | + |
| 131 | +### Creating a Branch |
| 132 | + |
| 133 | +```sh |
| 134 | +git checkout -b dev/<extension-name> |
| 135 | +``` |
| 136 | + |
| 137 | +### Scaffolding |
| 138 | + |
| 139 | +Generate the directory structure automatically: |
| 140 | + |
| 141 | +```sh |
| 142 | +task create-extension NAME=<extension-name> |
| 143 | +``` |
| 144 | + |
| 145 | +> [!NOTE] |
| 146 | +> For advanced scaffolding (custom distros or versions), see |
| 147 | +> [`BUILD.md`](./BUILD.md#advanced-scaffolding). |
| 148 | +
|
| 149 | +### Customizing the Files |
| 150 | + |
| 151 | +The scaffolding generates `metadata.hcl`, `Dockerfile`, and `README.md`. |
| 152 | +Follow the specific instructions and "TODO" comments found within each |
| 153 | +generated file to finalize your extension. |
| 154 | + |
| 155 | +> [!TIP] |
| 156 | +> Pay close attention to the `// renovate:` comments in the metadata; these are |
| 157 | +> required for automated version tracking. |
| 158 | +
|
| 159 | +--- |
| 160 | + |
| 161 | +## 4. Phase Four: Local Testing & Validation |
| 162 | + |
| 163 | +Testing is the most critical part of the lifecycle. |
| 164 | + |
| 165 | +### Automated E2E Testing |
| 166 | + |
| 167 | +> [!NOTE] |
| 168 | +> For a detailed breakdown of the testing infrastructure, refer to |
| 169 | +> [`BUILD.md`](./BUILD.md#local-testing-guide). |
| 170 | +
|
| 171 | +The repository provides a framework for full End-to-End validation. Ensure that |
| 172 | +the entire pipeline is working: |
| 173 | + |
| 174 | +```sh |
| 175 | +task checks:all |
| 176 | +``` |
| 177 | + |
| 178 | +Then run the full E2E tests for the extension. This task will build your image, |
| 179 | +push it to a local registry, spin up a Kind cluster, and run the functional |
| 180 | +tests: |
| 181 | + |
| 182 | +```sh |
| 183 | +task e2e:test:full TARGET="<extension-name>" |
| 184 | +``` |
| 185 | + |
| 186 | +### Local Manual Verification |
| 187 | + |
| 188 | +Once the automated tests have run, the Kind cluster remains active. You can |
| 189 | +"drop in" to this environment to verify the instructions you wrote in your |
| 190 | +`README.md`. |
| 191 | + |
| 192 | +#### Exporting the Kubeconfig |
| 193 | + |
| 194 | +```sh |
| 195 | +task e2e:export-kubeconfig KUBECONFIG_PATH=./kubeconfig |
| 196 | +export KUBECONFIG=$PWD/kubeconfig |
| 197 | +``` |
| 198 | + |
| 199 | +#### Identifying the Image Tag |
| 200 | + |
| 201 | +Once the image is built and pushed to the local registry (`localhost:5000`), |
| 202 | +you should verify the generated tags. You can use tools like `skopeo` to |
| 203 | +inspect the local registry: |
| 204 | + |
| 205 | +```bash |
| 206 | +skopeo list-tags --tls-verify=false docker://localhost:5000/<extension-name>-testing |
| 207 | +``` |
| 208 | + |
| 209 | +> [!IMPORTANT] |
| 210 | +> Remember to add the `-testing` suffix to the container registry. |
| 211 | +
|
| 212 | +Verify that the output lists tags for all expected PostgreSQL and Debian |
| 213 | +version combinations. |
| 214 | + |
| 215 | +#### Testing the Extension |
| 216 | + |
| 217 | +Create a `Cluster` resource using the instructions from your `README.md`. |
| 218 | +Pay close attention to the image location. Inside the Kubernetes cluster, the |
| 219 | +local registry is reachable at `registry.pg-extensions:5000`: |
| 220 | + |
| 221 | +```yaml |
| 222 | +image: registry.pg-extensions:5000/<extension-name>-testing:<tag> |
| 223 | +``` |
| 224 | +
|
| 225 | +### Extending Tests |
| 226 | +
|
| 227 | +While the framework provides a generic smoke test, we highly encourage you to |
| 228 | +add **extension-specific tests**. Review the [`postgis`](./postgis) directory |
| 229 | +for an example of additional testing using the Chainsaw framework. |
| 230 | + |
| 231 | +### Cleaning up |
| 232 | + |
| 233 | +Once you have finished your manual verification, tear down the test |
| 234 | +environment: |
| 235 | + |
| 236 | +```bash |
| 237 | +task e2e:cleanup |
| 238 | +``` |
| 239 | + |
| 240 | +--- |
| 241 | + |
| 242 | +## 5. Phase Five: Documentation & The Pull Request |
| 243 | + |
| 244 | +### The `README.md` file |
| 245 | + |
| 246 | +The `README.md` is typically the last file you complete. A clear, professional |
| 247 | +`README.md` makes an extension successful. Ensure it includes YAML examples for |
| 248 | +`Cluster` and `Database` resources so users can immediately adopt your work. |
| 249 | + |
| 250 | +### Commit and Submit |
| 251 | + |
| 252 | +Once you have verified your extension locally and are satisfied with the |
| 253 | +results, it is time to submit your contribution. |
| 254 | + |
| 255 | +To maintain a clean and searchable history, we require a specific commit |
| 256 | +format. If you have multiple experimental commits on your branch, please squash |
| 257 | +them into a single commit before submitting. |
| 258 | + |
| 259 | +Format: |
| 260 | + |
| 261 | +```text |
| 262 | +feat: add `<extension-name>` container image |
| 263 | + |
| 264 | +<DESCRIPTION: Explain what the extension does and why it's being added.> |
| 265 | + |
| 266 | +Closes #<issue-id> |
| 267 | +``` |
| 268 | + |
| 269 | +Submission Requirements: |
| 270 | + |
| 271 | +- **DCO Compliance**: All commits must be signed (`git commit -s`) to certify |
| 272 | + that you have the right to submit the code under the project's license. |
| 273 | +- **Upstream Target**: Ensure your Pull Request is targeting the `main` branch of |
| 274 | + the upstream repository. |
| 275 | + |
| 276 | +By submitting, you confirm your commitment to maintain this extension on behalf |
| 277 | +of the CloudNativePG Community. |
0 commit comments