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
99 changes: 99 additions & 0 deletions CONTRIBUTING.es.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Contribuir al proyecto

¡Gracias por tu interés en contribuir! Este documento explica cómo reportar problemas, proponer características, contribuir con código y cómo preparar un Pull Request (PR) claro para que los mantenedores puedan revisar y fusionar tus cambios con rapidez.

Contenido
- Reportar problemas o proponer características
- Contribuir con código (flujo de trabajo)
- Lista de verificación para PR
- Notas de codificación y estilo
- Integración continua / pruebas
- Documentación y actualizaciones
- Licencia y soporte

---

## 1) Reportar un problema o proponer una característica

- Para bugs: abre un Issue usando la plantilla **Bug report** en `.github/ISSUE_TEMPLATE/bug_report.md`.
- Para propuestas: abre un Issue usando la plantilla **Feature request** en `.github/ISSUE_TEMPLATE/feature_request.md`.

Incluye en el Issue:
- Título claro y resumen conciso.
- Pasos para reproducir (si aplica).
- Comportamiento esperado vs comportamiento observado.
- Entorno: versión de Postgres solicitada, imagen base, sistema operativo, CI, etc.
- Logs o salidas relevantes (copiar/pegar fragmentos útiles).
- Comandos ejecutados (por ejemplo, comandos `docker build` o `cargo`).

Antes de abrir un Issue:
1. Busca Issues y PRs abiertos para evitar duplicados.
2. Si piensas implementar la solución, indícalo en el Issue para coordinar trabajo.

---

## 2) Contribuir con código — flujo general

1. Haz fork del repositorio.
2. Crea una rama con un nombre descriptivo:
- `fix/<breve-descripción>`
- `feat/<breve-descripción>`
- `chore/<breve-descripción>`
3. Si el cambio es significativo, abre un Issue antes de implementar o vincula uno existente.
4. Implementa los cambios con commits atómicos y descriptivos.
5. Construye localmente: `cargo build --release`.
6. Prueba en un entorno representativo (contenedor Linux si procede).
7. Abre un PR hacia `main` indicando:
- Motivación y resumen de cambios.
- Cómo probar localmente (comandos y ejemplos).
- Número del Issue relacionado.
- Notas de compatibilidad y dependencias del sistema.

---

## 3) Lista de verificación para PR

Antes de solicitar revisión:
- [ ] Existe un Issue que describa el cambio o el PR lo referencia.
- [ ] La rama está basada en `main` actualizada.
- [ ] El código compila: `cargo build --release`.
- [ ] La descripción del PR contiene pasos de prueba reproducibles.
- [ ] Documentaste cualquier cambio en comportamiento público o flags.
- [ ] Actualizaste documentación relevante (`README.md`, `README.es.md`, u otros).

---

## 4) Notas de codificación y estilo

- Usamos Rust estable; sigue las prácticas idiomáticas de Rust.
- Mantén el código claro y robusto; detecta y maneja errores en vez de suprimirlos.
- Para ejecutar comandos shell desde Rust utiliza los helpers existentes (si los hay) en lugar de duplicar lógica.
- Comenta y documenta funciones públicas y comportamientos no triviales.
- Evita introducir dependencias innecesarias; prioriza soluciones simples.

---

## 5) Integración continua y pruebas

- Los PRs disparan workflows de GitHub Actions definidos en `.github/workflows/` (p. ej. `pr-test.yml`) que construyen la imagen en una matriz de versiones.
- Asegúrate de que tus cambios sean compatibles con las versiones y plataformas probadas en CI.
- Si tu cambio añade dependencias del sistema o modifica el proceso de build, indica claramente el motivo y los pasos de validación en el PR.

---

## 6) Documentación y actualizaciones

- Mantén `README.md` actualizado con cambios relevantes para usuarios.
- Para contenido en español, actualiza `README.es.md` o abre un Issue solicitando ayuda con la traducción si corresponde.
- Añade notas de cambios en la descripción del PR para facilitar la revisión.

---

## 7) Licencia y soporte

- Contribuciones están sujetas a la licencia del proyecto (archivo `LICENSE`).
- Si necesitas orientación antes de empezar, abre un Issue describiendo tu plan y marca la conversación como `discussion` o `help wanted`.

---

Gracias por querer mejorar este proyecto. Tu colaboración ayuda a que el software sea más útil y robusto para todos.
105 changes: 105 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Contributing to PostgreSQL Extensions Installer

Thank you for your interest in contributing! This document explains how to report problems, propose features, contribute code (including new extensions), and how to prepare a clear PR so maintainers can review and merge your changes quickly.

Contents
- Reporting issues or proposing features
- Contributing code (high-level workflow)
- Pull request checklist
- How the extension system works
- How to add a new extension (step-by-step)
- Coding and style notes
- Continuous Integration / tests
- License and code of conduct

---

## Reporting issues or proposing features

- If you found a bug, please open an Issue using the **Bug Report** template.
- If you want to propose a new feature (including adding a new extension), open an Issue using the **Feature Request** template.

Every issue should include:
- A clear title and a short description.
- Steps to reproduce (if it's a bug).
- Expected behavior vs actual behavior.
- The environment you used (docker image / base postgres version used, OS if relevant).
- Logs / command output (when applicable).

Before opening an issue:
1. Search existing issues to avoid duplicates.
2. If you plan to implement the fix or feature, mention that in the issue to avoid duplicated work.

---

## Contributing code — high-level workflow

1. Fork the repository.
2. Create a feature branch named using the pattern:
- `fix/<short-description>`, or
- `feat/<short-description>`, or
- `ext/<extension-name>` (for new extension work)
3. If you are implementing a non-trivial change, create an Issue first (see above). If none exists, create one and reference it from your PR.
4. Implement your changes. Build and test locally (see "CI / tests" below).
5. Create a pull request (PR) from your branch to `main` with a descriptive title.
6. In the PR description include:
- Motivation and summary of changes.
- How to test locally (commands and examples).
- Linked issue number (if any).
7. Wait for CI and maintainers' review. Respond to review comments and iterate as needed.

---

## Pull request checklist

Before requesting review, make sure:

- [ ] You signed and/or accepted the project license terms by contributing (the repository uses an open source license in `LICENSE`).
- [ ] You created / linked an Issue describing the problem or feature (unless the change is trivial).
- [ ] Your branch is based on the latest `main`.
- [ ] The code compiles: `cargo build --release`.
- [ ] You included testing steps in the PR description.
- [ ] You documented any new public API, command-line behavior, or Docker build args.
- [ ] You updated relevant docs (README or this CONTRIBUTING file if needed).

GitHub Actions are configured to perform build tests for multiple Postgres versions. The CI matrix is defined in `.github/workflows/pr-test.yml`.


---

## Coding and style notes

- This project uses stable Rust with `tokio` for concurrency.
- Keep code simple and robust: handle command failures and avoid silent errors.
- Use `crate::common::run(...)` and `run_output(...)` rather than reimplementing command execution.
- When adding new public API surfaces, document them in the README and update the top-level README with notes for the new extension.

---

## CI and testing

- PRs trigger a matrix build defined in `.github/workflows/pr-test.yml`. The CI builds images for multiple Postgres versions and architectures.
- Ensure your changes are compatible across the supported Postgres versions listed in the matrix (see the workflow file).
- If your change only affects docs, you can still create a PR, and maintainers may merge it if checks are satisfied.

---

## Documentation updates

- Update `README.md` when adding new extensions or changing default behavior.
- For Spanish users, there's a `README.es.md` — when adding content that should appear in the Spanish readme, propose a matching translation or open an issue asking for assistance.

Suggested README link updates:
- Add a link to this file in `README.md`:
- `See CONTRIBUTING.md for contribution guidelines.`
- Add a link to the Spanish copy (when created) in `README.es.md`.

---

## Questions and support

If you need help deciding how to implement an extension or want feedback before coding, open an Issue with the "feature request" template describing your plan and the extension you want to add.

---

Thank you for helping improve this project — contributions are very welcome!
60 changes: 60 additions & 0 deletions README.es.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Instalador de extensiones para PostgreSQL (imagen Docker derivada)
![Docker Pulls](https://img.shields.io/docker/pulls/ruxwez/postgres?style=flat-square)
![GitHub branch status](https://img.shields.io/github/checks-status/ruxwez/postgres/main?style=flat-square)
![GitHub contributors](https://img.shields.io/github/contributors/ruxwez/postgres?style=flat-square)
![GitHub License](https://img.shields.io/github/license/ruxwez/postgres?style=flat-square)
![GitHub Issues or Pull Requests](https://img.shields.io/github/issues/ruxwez/postgres?style=flat-square)

## Propósito

Este proyecto genera una imagen Docker derivada de `postgres` que ya incluye varias extensiones populares de PostgreSQL instaladas y listas para usar. La instalación de las extensiones se realiza durante el proceso de build mediante un binario escrito en Rust (`install-extensions`) que se compila en una etapa `builder` y se ejecuta dentro de la imagen base de PostgreSQL.

El objetivo principal es ahorrar tiempo y complejidad al desplegar contenedores PostgreSQL que necesiten extensiones comunes ya instaladas y correctamente compiladas para la versión de PostgreSQL seleccionada.

## Extensiones incluidas

- PostGIS — PostGIS 3 (PostgreSQL 16 / 17 / 18) — ver en `src/extensions/postgis.rs`
- pgvector — v0.8.1 (PostgreSQL 16 / 17 / 18) — ver en `src/extensions/pgvector.rs`
- pgmq — v1.7.0 (PostgreSQL 16 / 17 / 18) — ver en `src/extensions/pgmq.rs`

También se instala:

- `postgresql-contrib`

Durante el build se instalan temporalmente paquetes necesarios para compilar las extensiones (por ejemplo `build-essential`, `git`, `postgresql-server-dev-<major>`, `ca-certificates`). Estos paquetes se purgan al finalizar el proceso para reducir el tamaño final de la imagen.

## ¿Por qué he usado `Rust`?

He elegido `Rust` para el instalador porque necesitaba:

- Control total sobre qué versiones de cada extensión se instalan y cómo se instalan.
- Un binario autocontenible que pueda compilarse en la etapa `builder` y copiarse a la imagen final (`/usr/local/bin/installer`), para ejecutarlo y eliminarlo después (ver `Dockerfile`).
Esto permite que la imagen final sea ligera y no incluya dependencias de compilación ni runtimes interpretados.

Ventajas concretas de `Rust` en este caso:

- Binario nativo y sin runtime adicional: reduce el tamaño de la imagen y la superficie de ataque frente a soluciones basadas en `Python` u otros lenguajes interpretados.
- Tipado estático y manejo de errores: facilita detectar y controlar condiciones de fallo durante la instalación en tiempo de compilación y en ejecución.
- Buenas herramientas para concurrencia: puedo usar crates como `tokio` para ejecutar instalaciones en paralelo de forma segura y eficiente.
- Reproducibilidad y empaquetado: con `cargo` y la compilación en `builder` stage la lógica queda encapsulada en un único ejecutable.

## ¿Como funciona el gestor de versiones?

```rust
static VERSIONS: LazyLock<ExtensionVersionCompatibility> =
LazyLock::new(|| ExtensionVersionCompatibility {
v16: "1.7.0",
v17: "1.7.0",
v18: "1.7.0",
});
```

Esta estructura define la ultima version compatible con la version major de PostgreSQL. Esto permite que el instalador seleccione automáticamente la versión correcta de cada extensión según la versión de PostgreSQL en uso.

Por ejemplo, para `pgmq`, la versión `1.7.0` es compatible con PostgreSQL 16, 17 y 18. Si en el futuro se elimina el soporte en la version `1.8.0` para PostgreSQL 16, se mantendria la version `1.7.0` para PostgreSQL 16 y se podria definir una nueva version `1.8.0` para PostgreSQL 17 y 18.

Ademas, si se desea agregar soporte para una nueva version mayor de PostgreSQL (por ejemplo, PostgreSQL 19), solo se necesita actualizar esta estructura para incluir la nueva version compatible de cada extensión.

## ¿Como contribuir?
- Consulta `CONTRIBUTING.es.md` para las pautas de contribución en español.
- Para la versión en inglés, consulta `CONTRIBUTING.md`.
59 changes: 59 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# PostgreSQL Extensions Installer (derived Docker image)
![Docker Pulls](https://img.shields.io/docker/pulls/ruxwez/postgres?style=flat-square)
![GitHub branch status](https://img.shields.io/github/checks-status/ruxwez/postgres/main?style=flat-square)
![GitHub contributors](https://img.shields.io/github/contributors/ruxwez/postgres?style=flat-square)
![GitHub License](https://img.shields.io/github/license/ruxwez/postgres?style=flat-square)
![GitHub Issues or Pull Requests](https://img.shields.io/github/issues/ruxwez/postgres?style=flat-square)

## Purpose

This project builds a Docker image derived from the official `postgres` image that already includes several popular PostgreSQL extensions installed and ready to use. Extensions are installed during the image build using a Rust binary (`install-extensions`) that is compiled in a `builder` stage and executed inside the base PostgreSQL image.

The main goal is to save time and reduce complexity when deploying PostgreSQL containers that require common extensions preinstalled and compiled for the selected PostgreSQL version.

## Included extensions

- PostGIS — PostGIS 3 (PostgreSQL 16 / 17 / 18) — see `src/extensions/postgis.rs`
- pgvector — v0.8.1 (PostgreSQL 16 / 17 / 18) — see `src/extensions/pgvector.rs`
- pgmq — v1.7.0 (PostgreSQL 16 / 17 / 18) — see `src/extensions/pgmq.rs`

Also installed:

- `postgresql-contrib`

During the build, temporary packages required to compile the extensions are installed (for example `build-essential`, `git`, `postgresql-server-dev-<major>`, `ca-certificates`). These packages are removed at the end of the build process to reduce the final image size.

## Why Rust?

I chose `Rust` for the installer because I needed:

- Full control over which versions of each extension are installed and how they are installed.
- A self-contained binary that can be built in the `builder` stage and copied into the final image (`/usr/local/bin/installer`), executed, and then removed (see the `Dockerfile`). This keeps the final image slim and free of build dependencies or interpreted runtimes.

Concrete advantages of using `Rust` here:

- Native binary without an additional runtime: reduces image size and the attack surface compared to solutions based on `Python` or other interpreted languages.
- Static typing and robust error handling: helps catch and manage failure conditions both at compile time and runtime.
- Good concurrency tooling: crates like `tokio` can be used to run installations in parallel safely and efficiently.
- Reproducibility and packaging: with `cargo` and the `builder` stage, the installation logic is encapsulated in a single executable.

## How does the version manager work?

```/dev/null/versions.rs#L1-9
static VERSIONS: LazyLock<ExtensionVersionCompatibility> =
LazyLock::new(|| ExtensionVersionCompatibility {
v16: "1.7.0",
v17: "1.7.0",
v18: "1.7.0",
});
```

This structure defines the latest compatible version for each PostgreSQL major version. It allows the installer to automatically select the correct version of each extension according to the PostgreSQL version in use.

For example, for `pgmq`, version `1.7.0` is compatible with PostgreSQL 16, 17, and 18. If in the future version `1.8.0` drops support for PostgreSQL 16, you would keep `1.7.0` mapped to PostgreSQL 16 and define `1.8.0` for PostgreSQL 17 and 18.

Also, if you want to add support for a new major PostgreSQL release (for example, PostgreSQL 19), you only need to update this structure to include the new compatible version for each extension.

## How to contribute

See `CONTRIBUTING.md` for detailed contribution guidelines: how to report bugs or feature requests (use the provided issue templates), the PR workflow, and step-by-step instructions to add a new PostgreSQL extension. A Spanish translation of the contributor guide is available at `CONTRIBUTING.es.md` and is referenced from `README.es.md`.
7 changes: 7 additions & 0 deletions makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

test:
docker build -t ruxwez/postgres . --no-cache
docker rmi ruxwez/postgres

build:
docker build -t ruxwez/postgres .
10 changes: 6 additions & 4 deletions src/extensions/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use std::sync::Arc;

mod pgmq;
mod pgvector;
mod postgis;

pub async fn install(pg_version: String) {
postgis::install(pg_version);
pub async fn install(pg_version: Arc<String>) {
postgis::install(pg_version.clone());

let pgmq_handle = pgmq::install();
let pgvector_handle = pgvector::install();
let pgmq_handle = pgmq::install(pg_version.clone());
let pgvector_handle = pgvector::install(pg_version.clone());

// Wait for all installations to complete
let _ = tokio::join!(pgmq_handle, pgvector_handle);
Expand Down
Loading