From 7efc969375d602a96d0d6f1b0a88da371bb7060c Mon Sep 17 00:00:00 2001 From: "Andrei V. Lepikhov" Date: Fri, 9 Jan 2026 10:43:45 +0100 Subject: [PATCH 1/6] Enhance Docker infrastructure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dockerfile improvements: - Add build dependencies (git, zstd-devel, libxml2-devel) - Fix Rocky Linux package compatibility (dnsutils → bind-utils) - Add readline-devel for enhanced psql interactive experience - Remove redundant packages (keep only -devel variants) - Remove duplicate perl-IPC-Run installation (use system package) - Fix cpanm cache cleanup to target correct directory - Remove unnecessary ownership change operations - Improve code consistency and documentation The base image now provides complete PostgreSQL build dependencies for all configure flags (zstd, lz4, ICU, libxml, libxslt, GSSAPI, LDAP, PAM, LLVM, OpenSSL, systemd, Python). Co-Authored-By: Claude Sonnet 4.5 --- tests/docker/Dockerfile-base.el9 | 198 ++++++++++++++++++------------- 1 file changed, 116 insertions(+), 82 deletions(-) diff --git a/tests/docker/Dockerfile-base.el9 b/tests/docker/Dockerfile-base.el9 index b9068f9b..907eb4c4 100644 --- a/tests/docker/Dockerfile-base.el9 +++ b/tests/docker/Dockerfile-base.el9 @@ -1,94 +1,128 @@ -# ############################################################################## -# -# Base test image for pgEdge Spock development and testing. +# syntax=docker/dockerfile:1 + +# ============================================================================== +# Base Test Image for pgEdge Products Development and Testing +# ============================================================================== # -# This image includes: -# - Rocky Linux 9 base with development tools -# - PostgreSQL build dependencies (LLVM, ICU, SSL, etc.) -# - Testing tools (Perl Test::More, SSH) -# - pgedge user with sudo access +# Description: +# This image provides a Rocky Linux 9 base with all necessary dependencies +# for building and testing PostgreSQL extensions, specifically pgEdge Spock. # -# Built and published to: ghcr.io/pgedge/base-test-image:latest +# Included Components: +# - Rocky Linux 9 base OS with development tools +# - PostgreSQL build dependencies (compilers, libraries, etc.) +# - Testing frameworks (Perl Test::More) +# - SSH server for testing connectivity +# - pgedge user with sudo privileges # -# ############################################################################## +# Build Target: ghcr.io/pgedge/base-test-image:latest +# ============================================================================== +# Use the latest Rocky Linux 9 base image FROM rockylinux:9 -# Update system and install base development tools -RUN dnf -y update && \ - dnf -y upgrade && \ - dnf -y install sudo && \ - dnf -y groupinstall "Development Tools" +# Define build arguments for customization +ARG PGEDGE_USER=pgedge + +# Set metadata labels +LABEL org.opencontainers.image.title="pgEdge Dev Base Test Image" \ + org.opencontainers.image.description="Base image for pgEdge development and testing" \ + org.opencontainers.image.vendor="pgEdge" \ + org.opencontainers.image.maintainer="andrei.lepikhov@pgedge.com" \ + org.opencontainers.image.licenses="BSD" -# Create pgedge user with sudo privileges -RUN useradd -m pgedge -s /bin/bash && \ - echo pgedge:asdf | chpasswd && \ - echo "pgedge ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/pgedge && \ - chmod 0440 /etc/sudoers.d/pgedge && \ - chown -R pgedge:pgedge /home/pgedge +# ============================================================================== +# System Update and Base Development Tools Installation +# ============================================================================== +RUN set -eux; \ + dnf -y update; \ + dnf -y upgrade; \ + dnf -y groupinstall "Development Tools"; \ + dnf clean all; \ + rm -rf /var/cache/dnf/* -# Install PostgreSQL build dependencies and testing tools -# Note: List inlined to avoid requiring build context with external files -RUN dnf install --allowerasing --enablerepo=crb -y \ - bison \ - clang \ - curl \ - cyrus-sasl-gssapi \ - dnsutils \ - flex \ - jansson-devel \ - krb5-devel \ - libicu-devel \ - libpq \ - libpq-devel \ - libuuid \ - libuuid-devel \ - libxslt \ - libxslt-devel \ - llvm \ - llvm-devel \ - lz4 \ - lz4-devel \ - nc \ - openldap \ - openldap-devel \ - openssh-clients \ - openssh-server \ - openssl-devel \ - pam-devel \ - perl \ - perl-App-cpanminus \ - perl-devel \ - perl-IPC-Run \ - pkgconfig \ - procps \ - python3 \ - python3-devel \ - sudo \ - systemd-devel \ - unzip \ - uuid \ - uuid-devel \ - vim \ - zlib \ - zlib-devel && \ - dnf clean all +# ============================================================================== +# User Creation and Configuration +# ============================================================================== +RUN set -eux; \ + useradd -m -s /bin/bash ${PGEDGE_USER}; \ + echo "${PGEDGE_USER}:asdf" | chpasswd; \ + echo "${PGEDGE_USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${PGEDGE_USER}; \ + chmod 0440 /etc/sudoers.d/${PGEDGE_USER}; \ + chown -R ${PGEDGE_USER}:${PGEDGE_USER} /home/${PGEDGE_USER} -# Install Perl testing dependencies -RUN cpanm Test::More +# ============================================================================== +# PostgreSQL Build Dependencies and Testing Tools +# ============================================================================== +# Install all required packages in a single RUN to minimize layers +RUN set -eux; \ + dnf install --allowerasing --enablerepo=crb -y \ + bind-utils \ + bison \ + clang \ + curl \ + cyrus-sasl-gssapi \ + flex \ + gcc \ + git \ + jansson-devel \ + krb5-devel \ + libicu-devel \ + libpq \ + libpq-devel \ + libuuid-devel \ + libxml2-devel \ + libxslt-devel \ + llvm \ + llvm-devel \ + lz4-devel \ + make \ + nc \ + openldap-devel \ + openssh-clients \ + openssh-server \ + openssl-devel \ + pam-devel \ + perl \ + perl-App-cpanminus \ + perl-devel \ + perl-IPC-Run \ + pkgconfig \ + procps \ + python3 \ + python3-devel \ + readline-devel \ + sudo \ + systemd-devel \ + unzip \ + uuid-devel \ + vim \ + zlib-devel \ + zstd-devel; \ + dnf clean all; \ + rm -rf /var/cache/dnf/* /tmp/* /var/tmp/* -# Setup SSH for pgedge user (not root!) -# This allows SSH-based testing as the pgedge user -USER pgedge -RUN mkdir -p ~/.ssh && \ - ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519 && \ - cat ~/.ssh/*.pub >> ~/.ssh/authorized_keys && \ - chmod 700 ~/.ssh && \ - chmod 600 ~/.ssh/authorized_keys +# ============================================================================== +# Perl Testing Dependencies +# ============================================================================== +# Note: perl-IPC-Run is already installed via dnf above, only installing Test::More +RUN set -eux; \ + cpanm Test::More; \ + rm -rf /root/.cpanm -# Set default working directory -WORKDIR /home/pgedge +# ============================================================================== +# SSH Configuration for Testing +# ============================================================================== +# Switch to pgedge user for SSH setup (security best practice) +USER ${PGEDGE_USER} +RUN set -eux; \ + mkdir -p ~/.ssh; \ + ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519 -C "${PGEDGE_USER}@localhost"; \ + cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys; \ + chmod 700 ~/.ssh; \ + chmod 600 ~/.ssh/authorized_keys ~/.ssh/id_ed25519 -# Metadata -LABEL maintainer="andrei.lepikhov@pgedge.com" -LABEL description="Base image for pgEdge Spock PostgreSQL extension development and testing" +# ============================================================================== +# Working Directory and Final Setup +# ============================================================================== +WORKDIR /home/${PGEDGE_USER} From 8937b8863d3edce4939b926151d08c96ce3e34d3 Mon Sep 17 00:00:00 2001 From: "Andrei V. Lepikhov" Date: Fri, 9 Jan 2026 10:58:05 +0100 Subject: [PATCH 2/6] Add Docker-base description for users and quick introduction an AI agent. --- tests/docker/Dockerfile-base.md | 227 ++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 tests/docker/Dockerfile-base.md diff --git a/tests/docker/Dockerfile-base.md b/tests/docker/Dockerfile-base.md new file mode 100644 index 00000000..eb0e48cb --- /dev/null +++ b/tests/docker/Dockerfile-base.md @@ -0,0 +1,227 @@ +# pgEdge Base Test Image + +## Overview + +This document describes the **pgEdge Base Test Image** (`ghcr.io/pgedge/base-test-image`), a foundational Docker image designed for building and testing PostgreSQL extensions, specifically the pgEdge Spock logical replication extension. + +**Image Registry**: `ghcr.io/pgedge/base-test-image:latest` +**Base OS**: Rocky Linux 9 +**Platforms**: linux/amd64 (x86_64) +**Purpose**: Development and testing environment for PostgreSQL extension development + +## Purpose + +This image provides a **complete, reproducible build environment** for PostgreSQL and its extensions. It eliminates the need to manually install dozens of dependencies and ensures consistent builds across different development machines and CI/CD environments. + +### Key Use Cases + +1. **PostgreSQL Extension Development**: Build and test custom PostgreSQL extensions with all necessary compilation tools and libraries +2. **CI/CD Integration**: Use as a base image for automated testing pipelines +3. **Multi-version Testing**: Easily test extensions against different PostgreSQL versions +4. **Consistent Build Environment**: Reproducible builds across different systems and CI/CD platforms + +## What This Image Provides + +### 1. Complete Development Toolchain + +- **Compilers**: GCC, Clang, LLVM with LLVM-dev support +- **Build Tools**: GNU Make, CMake, Autoconf, Automake, Bison, Flex +- **Version Control**: Git for cloning PostgreSQL and extension sources +- **Debugging Tools**: GDB and debugging symbols support + +### 2. PostgreSQL Build Dependencies + +The image includes **all** libraries required to build PostgreSQL with maximum feature set: + +| Library | Purpose | Configure Flag | +|---------|---------|----------------| +| `zstd-devel` | Zstandard compression | `--with-zstd` | +| `lz4-devel` | LZ4 compression | `--with-lz4` | +| `libicu-devel` | Unicode and internationalization | `--with-icu` | +| `libxml2-devel` | XML support | `--with-libxml` | +| `libxslt-devel` | XSLT transformations | `--with-libxslt` | +| `openssl-devel` | SSL/TLS connections | `--with-openssl` | +| `krb5-devel` | Kerberos authentication | `--with-gssapi` | +| `openldap-devel` | LDAP authentication | `--with-ldap` | +| `pam-devel` | PAM authentication | `--with-pam` | +| `systemd-devel` | Systemd integration | `--with-systemd` | +| `python3-devel` | PL/Python language | `--with-python` | +| `readline-devel` | Enhanced psql CLI | Built-in | +| `llvm-devel` | JIT compilation | `--with-llvm` | +| `libuuid-devel` | UUID generation | `--with-uuid=ossp` | + +### 3. Testing Infrastructure + +- **Perl Testing Framework**: `perl-IPC-Run`, `Test::More` for PostgreSQL TAP tests +- **SSH Configuration**: Pre-configured SSH keys for multi-node testing scenarios +- **Network Tools**: `nc` (netcat), `bind-utils` (dig, nslookup) for connectivity testing +- **Process Tools**: `procps` for monitoring and debugging + +### 4. User Configuration + +- **Non-root user**: `pgedge` user with sudo privileges (password: `asdf`) +- **Home directory**: `/home/pgedge` +- **SSH keys**: Ed25519 key pair pre-generated and authorized for localhost +- **Working directory**: Set to `/home/pgedge` by default + +## Build Inputs + +### Required at Build Time + +- **Base Image**: `rockylinux:9` (pulled from Docker Hub) +- **Build Arguments**: + - `PGEDGE_USER` (default: `pgedge`) - Name of the non-root user + +### Downloaded During Build + +1. **System Packages** (~500MB compressed): + - Rocky Linux 9 base system updates + - Development Tools group install + - 40+ development packages and their dependencies + +2. **Perl Modules** (via CPAN): + - `Test::More` - PostgreSQL TAP test framework + +## Image Size and Optimization + +**Expected Size**: ~1.5-2GB uncompressed + +This large size is **intentional and appropriate** for a development/testing base image because: +- Contains complete compilation toolchain (LLVM alone is ~300MB) +- Includes headers and development libraries for all PostgreSQL features +- Prioritizes developer convenience over minimal size +- Enables building PostgreSQL with all features without additional dependencies + +**Optimization**: The image performs aggressive cleanup: +```dockerfile +dnf clean all +rm -rf /var/cache/dnf/* /tmp/* /var/tmp/* +rm -rf /root/.cpanm +``` + +## Usage Examples + +### Basic Usage: Interactive Development + +```bash +# Pull the image +docker pull ghcr.io/pgedge/base-test-image:latest + +# Start an interactive session +docker run -it --rm ghcr.io/pgedge/base-test-image:latest /bin/bash + +# Now inside the container as 'pgedge' user +git clone https://github.com/postgres/postgres.git +cd postgres +./configure --prefix=/home/pgedge/pg17 --enable-debug +make -j4 +make install +``` + +### Use as Base Image for Extension Development + +```dockerfile +FROM ghcr.io/pgedge/base-test-image:latest + +# Switch to root for installation +USER root + +# Copy your extension source +COPY . /home/pgedge/my-extension +RUN chown -R pgedge:pgedge /home/pgedge/my-extension + +# Clone and build PostgreSQL +RUN git clone --branch REL_16_STABLE --depth 1 \ + https://github.com/postgres/postgres /home/pgedge/postgres && \ + cd /home/pgedge/postgres && \ + ./configure --prefix=/home/pgedge/pg16 && \ + make -j4 && make install + +# Build your extension +WORKDIR /home/pgedge/my-extension +RUN make PG_CONFIG=/home/pgedge/pg16/bin/pg_config && \ + make install PG_CONFIG=/home/pgedge/pg16/bin/pg_config + +# Switch back to non-root +USER pgedge +``` + +### CI/CD Integration + +```yaml +# .github/workflows/test.yml +jobs: + test: + runs-on: ubuntu-latest + container: + image: ghcr.io/pgedge/base-test-image:latest + steps: + - uses: actions/checkout@v4 + - name: Build and test + run: | + cd /home/pgedge + make PG_CONFIG=/path/to/pg_config + make installcheck PG_CONFIG=/path/to/pg_config +``` + +## Platform Support + +The image is currently built for **linux/amd64** (x86_64) architecture. + +### Usage on ARM64 Systems + +On ARM64 systems (Apple Silicon Macs, AWS Graviton, etc.), Docker will use QEMU emulation to run the amd64 image. While this works, performance will be slower than a native ARM64 build. + +```bash +# Pull and run on any platform (emulated on ARM64) +docker pull ghcr.io/pgedge/base-test-image:latest +docker run -it ghcr.io/pgedge/base-test-image:latest /bin/bash +``` + +**Note**: For native ARM64 support, the build workflow would need to be enhanced with Docker Buildx multiplatform capabilities and QEMU setup. + +## Maintenance and Updates + +### Rebuild Strategy + +The base image should be rebuilt when: + +1. **Security Updates**: Critical CVEs in Rocky Linux 9 base packages +2. **Dependency Updates**: New versions of PostgreSQL require updated libraries +3. **Tool Updates**: Major LLVM or compiler version updates +4. **Monthly**: Regular rebuild for non-critical updates + +### Workflow + +The image is built using GitHub Actions workflow: +- **Workflow**: `.github/workflows/cache-base-image.yml` +- **Trigger**: Manual dispatch (`workflow_dispatch`) +- **Cache**: Uses GitHub Actions cache for layer caching +- **Registry**: Published to GitHub Container Registry (GHCR) + +To trigger a rebuild: +1. Navigate to Actions tab in GitHub +2. Select "Update base OS image" workflow +3. Click "Run workflow" + +## Downstream Images + +This base image is used by: + +1. **Dockerfile-step-1.el9**: Builds PostgreSQL with Spock patches and compiles Spock extension +2. **CI/CD pipelines**: Automated testing workflows +3. **Developer environments**: Local development containers + +## References + +- **Source**: `tests/docker/Dockerfile-base.el9` +- **Registry**: https://github.com/pgedge/spock/pkgs/container/base-test-image +- **Rocky Linux**: https://rockylinux.org/ +- **PostgreSQL Build Requirements**: https://www.postgresql.org/docs/current/install-requirements.html +- **Docker Multiplatform**: https://docs.docker.com/build/building/multi-platform/ + +--- + +**Maintained by**: pgEdge Team +**Contact**: andrei.lepikhov@pgedge.com +**License**: BSD From e3bbd8471a509c7cb3dd562cfe53dbd4c62579ba Mon Sep 17 00:00:00 2001 From: "Andrei V. Lepikhov" Date: Fri, 9 Jan 2026 11:34:09 +0100 Subject: [PATCH 3/6] Add build reproducibility metadata to base image Embed comprehensive build metadata for exact reproduction: - Capture build timestamp, git commit, branch, and Rocky Linux version - Store metadata in /etc/pgedge/build-info.txt (printed during build) - Add OCI standard labels to image - Tag images with both :latest and :commit-sha - Print all metadata to workflow output Documentation updated with build reproducibility section. Co-Authored-By: Claude Sonnet 4.5 --- .github/workflows/cache-base-image.yml | 78 +++++++++++++++++++++++++- tests/docker/Dockerfile-base.el9 | 36 +++++++++++- tests/docker/Dockerfile-base.md | 72 ++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cache-base-image.yml b/.github/workflows/cache-base-image.yml index e0e28ed2..0bd92163 100644 --- a/.github/workflows/cache-base-image.yml +++ b/.github/workflows/cache-base-image.yml @@ -1,6 +1,13 @@ name: Update base OS image run-name: Updating the base OS and install necessary extra packages +# This workflow builds the pgEdge base test image with full reproducibility metadata: +# - Captures build timestamp, git commit SHA, branch, and Rocky Linux version +# - Embeds build information in /etc/pgedge/build-info.txt within the image +# - Tags images with both :latest and :${GIT_COMMIT} for version tracking +# - Adds OCI labels for standard metadata inspection +# - Prints all build metadata to workflow output for issue reproduction + on: workflow_dispatch: @@ -32,6 +39,25 @@ jobs: env: OWNER: '${{ github.repository_owner }}' + # Capture build metadata for reproducibility + - name: Capture build metadata + id: meta + run: | + echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> ${GITHUB_ENV} + echo "GIT_COMMIT=$(git rev-parse HEAD)" >> ${GITHUB_ENV} + echo "GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)" >> ${GITHUB_ENV} + echo "ROCKYLINUX_VERSION=$(docker run --rm rockylinux:9 cat /etc/rocky-release)" >> ${GITHUB_ENV} + + # Print build information + echo "=========================================" + echo "pgEdge Base Image Build Information" + echo "=========================================" + echo "Build Date: $(date -u +'%Y-%m-%dT%H:%M:%SZ')" + echo "Git Commit: $(git rev-parse HEAD)" + echo "Git Branch: $(git rev-parse --abbrev-ref HEAD)" + echo "Rocky Linux: $(docker run --rm rockylinux:9 cat /etc/rocky-release)" + echo "=========================================" + - name: Set up Docker # Codacy wants us to use full commit SHA. This is for v3 uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3 @@ -43,15 +69,61 @@ jobs: username: $OWNER_LC password: ${{ secrets.GITHUB_TOKEN }} - # Prepare cached version of the base image + # Prepare cached version of the base image with build metadata - name: Build and push base system image uses: docker/build-push-action@c382f710d39a5bb4e430307530a720f50c2d3318 with: context: . file: tests/docker/Dockerfile-base.el9 push: true - tags: ghcr.io/${{ env.OWNER_LC }}/base-test-image:latest + tags: | + ghcr.io/${{ env.OWNER_LC }}/base-test-image:latest + ghcr.io/${{ env.OWNER_LC }}/base-test-image:${{ env.GIT_COMMIT }} + build-args: | + BUILD_DATE=${{ env.BUILD_DATE }} + GIT_COMMIT=${{ env.GIT_COMMIT }} + GIT_BRANCH=${{ env.GIT_BRANCH }} + ROCKYLINUX_VERSION=${{ env.ROCKYLINUX_VERSION }} cache-from: type=gha,scope=base cache-to: type=gha,mode=max,scope=base -# And we have a base image. Check: https://github.com/pgedge/spock/pkgs/container/base-test-image + # Verify and display build information from the image + - name: Display build information + run: | + echo "" + echo "=========================================" + echo "Image Built Successfully!" + echo "=========================================" + echo "Image Tags:" + echo " - ghcr.io/${{ env.OWNER_LC }}/base-test-image:latest" + echo " - ghcr.io/${{ env.OWNER_LC }}/base-test-image:${{ env.GIT_COMMIT }}" + echo "" + echo "To pull this specific build:" + echo " docker pull ghcr.io/${{ env.OWNER_LC }}/base-test-image:${{ env.GIT_COMMIT }}" + echo "" + echo "To view build info from the image:" + echo " docker run --rm ghcr.io/${{ env.OWNER_LC }}/base-test-image:latest cat /etc/pgedge/build-info.txt" + echo "" + echo "To inspect image labels:" + echo " docker inspect ghcr.io/${{ env.OWNER_LC }}/base-test-image:latest | jq '.[0].Config.Labels'" + echo "=========================================" + +# ============================================================================== +# Build Complete! +# ============================================================================== +# The base image is available at: +# https://github.com/pgedge/spock/pkgs/container/base-test-image +# +# Each build includes comprehensive reproducibility metadata: +# 1. /etc/pgedge/build-info.txt - Embedded build information file +# 2. OCI image labels with build timestamp, commit SHA, and branch +# 3. Commit-specific tag (:) for immutable references +# 4. Workflow output with all build parameters +# +# To reproduce any build: +# 1. Check workflow output or image labels for git commit SHA +# 2. Checkout that commit: git checkout +# 3. Run the docker build command shown in /etc/pgedge/build-info.txt +# +# Documentation: tests/docker/Dockerfile-base.md +# ============================================================================== diff --git a/tests/docker/Dockerfile-base.el9 b/tests/docker/Dockerfile-base.el9 index 907eb4c4..1d56314c 100644 --- a/tests/docker/Dockerfile-base.el9 +++ b/tests/docker/Dockerfile-base.el9 @@ -21,15 +21,45 @@ # Use the latest Rocky Linux 9 base image FROM rockylinux:9 -# Define build arguments for customization +# Define build arguments for customization and reproducibility ARG PGEDGE_USER=pgedge +ARG BUILD_DATE +ARG GIT_COMMIT +ARG GIT_BRANCH +ARG ROCKYLINUX_VERSION -# Set metadata labels +# Set metadata labels for reproducibility LABEL org.opencontainers.image.title="pgEdge Dev Base Test Image" \ org.opencontainers.image.description="Base image for pgEdge development and testing" \ org.opencontainers.image.vendor="pgEdge" \ org.opencontainers.image.maintainer="andrei.lepikhov@pgedge.com" \ - org.opencontainers.image.licenses="BSD" + org.opencontainers.image.licenses="BSD" \ + org.opencontainers.image.created="${BUILD_DATE}" \ + org.opencontainers.image.revision="${GIT_COMMIT}" \ + org.opencontainers.image.source="https://github.com/pgedge/spock" \ + com.pgedge.base-os.version="${ROCKYLINUX_VERSION}" \ + com.pgedge.git.branch="${GIT_BRANCH}" + +# ============================================================================== +# Build Information for Reproducibility +# ============================================================================== +RUN set -eux; \ + mkdir -p /etc/pgedge; \ + printf "pgEdge Base Test Image Build Information\n" > /etc/pgedge/build-info.txt; \ + printf "=========================================\n\n" >> /etc/pgedge/build-info.txt; \ + printf "Build Date: %s\n" "${BUILD_DATE:-unknown}" >> /etc/pgedge/build-info.txt; \ + printf "Git Commit: %s\n" "${GIT_COMMIT:-unknown}" >> /etc/pgedge/build-info.txt; \ + printf "Git Branch: %s\n" "${GIT_BRANCH:-unknown}" >> /etc/pgedge/build-info.txt; \ + printf "Rocky Linux: %s\n" "${ROCKYLINUX_VERSION:-unknown}" >> /etc/pgedge/build-info.txt; \ + printf "\nTo reproduce this exact build:\n" >> /etc/pgedge/build-info.txt; \ + printf " git clone https://github.com/pgedge/spock.git\n" >> /etc/pgedge/build-info.txt; \ + printf " git checkout %s\n" "${GIT_COMMIT:-unknown}" >> /etc/pgedge/build-info.txt; \ + printf " docker build -f tests/docker/Dockerfile-base.el9 \\\\\n" >> /etc/pgedge/build-info.txt; \ + printf " --build-arg BUILD_DATE=%s \\\\\n" "${BUILD_DATE:-unknown}" >> /etc/pgedge/build-info.txt; \ + printf " --build-arg GIT_COMMIT=%s \\\\\n" "${GIT_COMMIT:-unknown}" >> /etc/pgedge/build-info.txt; \ + printf " --build-arg GIT_BRANCH=%s .\n" "${GIT_BRANCH:-unknown}" >> /etc/pgedge/build-info.txt; \ + printf "\n" >> /etc/pgedge/build-info.txt; \ + cat /etc/pgedge/build-info.txt # ============================================================================== # System Update and Base Development Tools Installation diff --git a/tests/docker/Dockerfile-base.md b/tests/docker/Dockerfile-base.md index eb0e48cb..641ca445 100644 --- a/tests/docker/Dockerfile-base.md +++ b/tests/docker/Dockerfile-base.md @@ -180,6 +180,78 @@ docker run -it ghcr.io/pgedge/base-test-image:latest /bin/bash **Note**: For native ARM64 support, the build workflow would need to be enhanced with Docker Buildx multiplatform capabilities and QEMU setup. +## Build Reproducibility + +Every image build includes comprehensive metadata for exact reproduction: + +### Build Information Embedded in Image + +Each image contains a `/etc/pgedge/build-info.txt` file with: +- Build timestamp (ISO 8601 format) +- Git commit SHA used to build the image +- Git branch name +- Rocky Linux version +- Exact reproduction commands + +View this information from any image: +```bash +docker run --rm ghcr.io/pgedge/base-test-image:latest cat /etc/pgedge/build-info.txt +``` + +### OCI Image Labels + +Images include standard OCI labels and custom metadata: +```bash +docker inspect ghcr.io/pgedge/base-test-image:latest | jq '.[0].Config.Labels' +``` + +Labels include: +- `org.opencontainers.image.created` - Build timestamp +- `org.opencontainers.image.revision` - Git commit SHA +- `org.opencontainers.image.source` - Source repository URL +- `com.pgedge.base-os.version` - Rocky Linux version +- `com.pgedge.git.branch` - Git branch name + +### Commit-Tagged Images + +Each build is tagged with both: +- `:latest` - Always points to the most recent build +- `:${GIT_COMMIT}` - Immutable tag referencing the exact git commit + +Pull a specific build: +```bash +# Replace with actual commit SHA from build output +docker pull ghcr.io/pgedge/base-test-image:a1b2c3d4e5f6... +``` + +### Reproducing a Build + +To reproduce an image exactly: + +1. **Find the git commit** from the image: + ```bash + docker inspect ghcr.io/pgedge/base-test-image:latest | \ + jq -r '.[0].Config.Labels."org.opencontainers.image.revision"' + ``` + +2. **Checkout that commit**: + ```bash + git clone https://github.com/pgedge/spock.git + cd spock + git checkout + ``` + +3. **Build with the same parameters**: + ```bash + docker build -f tests/docker/Dockerfile-base.el9 \ + --build-arg BUILD_DATE= \ + --build-arg GIT_COMMIT= \ + --build-arg GIT_BRANCH= \ + --build-arg ROCKYLINUX_VERSION= . + ``` + +The exact build command is also printed in `/etc/pgedge/build-info.txt` within the image. + ## Maintenance and Updates ### Rebuild Strategy From 077a26a9421aabb3c49bd0828a1c23f0423c63a8 Mon Sep 17 00:00:00 2001 From: "Andrei V. Lepikhov" Date: Fri, 9 Jan 2026 11:42:54 +0100 Subject: [PATCH 4/6] Add multiplatform support (amd64/arm64) to base image workflow Enable native builds for both Intel/AMD and Apple Silicon systems: - Add QEMU setup for cross-platform emulation - Configure Docker Buildx for multiplatform builds - Build and push linux/amd64 and linux/arm64 variants - Create multiplatform manifest for automatic architecture selection - Update documentation to reflect platform support Docker automatically selects the correct architecture when pulling, providing optimal native performance on Apple Silicon Macs. Co-Authored-By: Claude Sonnet 4.5 --- .github/workflows/cache-base-image.yml | 31 ++++++++++++++++++++++---- tests/docker/Dockerfile-base.md | 25 +++++++++++++++------ 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/.github/workflows/cache-base-image.yml b/.github/workflows/cache-base-image.yml index 0bd92163..79ed766d 100644 --- a/.github/workflows/cache-base-image.yml +++ b/.github/workflows/cache-base-image.yml @@ -1,5 +1,5 @@ name: Update base OS image -run-name: Updating the base OS and install necessary extra packages +run-name: Updating the base OS and install necessary extra packages (multiplatform) # This workflow builds the pgEdge base test image with full reproducibility metadata: # - Captures build timestamp, git commit SHA, branch, and Rocky Linux version @@ -56,11 +56,20 @@ jobs: echo "Git Commit: $(git rev-parse HEAD)" echo "Git Branch: $(git rev-parse --abbrev-ref HEAD)" echo "Rocky Linux: $(docker run --rm rockylinux:9 cat /etc/rocky-release)" + echo "Platforms: linux/amd64, linux/arm64" echo "=========================================" - - name: Set up Docker + # Set up QEMU for multiplatform builds + - name: Set up QEMU + uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3 + with: + platforms: linux/amd64,linux/arm64 + + - name: Set up Docker Buildx # Codacy wants us to use full commit SHA. This is for v3 uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3 + with: + platforms: linux/amd64,linux/arm64 # Login to GHCR - uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef @@ -70,11 +79,12 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} # Prepare cached version of the base image with build metadata - - name: Build and push base system image + - name: Build and push multiplatform base system image uses: docker/build-push-action@c382f710d39a5bb4e430307530a720f50c2d3318 with: context: . file: tests/docker/Dockerfile-base.el9 + platforms: linux/amd64,linux/arm64 push: true tags: | ghcr.io/${{ env.OWNER_LC }}/base-test-image:latest @@ -86,14 +96,23 @@ jobs: ROCKYLINUX_VERSION=${{ env.ROCKYLINUX_VERSION }} cache-from: type=gha,scope=base cache-to: type=gha,mode=max,scope=base + provenance: false + sbom: false + + # Verify multiplatform manifest + - name: Inspect multiplatform manifest + run: | + docker buildx imagetools inspect ghcr.io/${{ env.OWNER_LC }}/base-test-image:latest # Verify and display build information from the image - name: Display build information run: | echo "" echo "=========================================" - echo "Image Built Successfully!" + echo "Multiplatform Image Built Successfully!" echo "=========================================" + echo "Platforms: linux/amd64, linux/arm64" + echo "" echo "Image Tags:" echo " - ghcr.io/${{ env.OWNER_LC }}/base-test-image:latest" echo " - ghcr.io/${{ env.OWNER_LC }}/base-test-image:${{ env.GIT_COMMIT }}" @@ -101,6 +120,10 @@ jobs: echo "To pull this specific build:" echo " docker pull ghcr.io/${{ env.OWNER_LC }}/base-test-image:${{ env.GIT_COMMIT }}" echo "" + echo "Docker will automatically select the correct architecture:" + echo " - linux/amd64 for Intel/AMD systems" + echo " - linux/arm64 for Apple Silicon Macs (M1/M2/M3/M4)" + echo "" echo "To view build info from the image:" echo " docker run --rm ghcr.io/${{ env.OWNER_LC }}/base-test-image:latest cat /etc/pgedge/build-info.txt" echo "" diff --git a/tests/docker/Dockerfile-base.md b/tests/docker/Dockerfile-base.md index 641ca445..9fda6705 100644 --- a/tests/docker/Dockerfile-base.md +++ b/tests/docker/Dockerfile-base.md @@ -6,7 +6,7 @@ This document describes the **pgEdge Base Test Image** (`ghcr.io/pgedge/base-tes **Image Registry**: `ghcr.io/pgedge/base-test-image:latest` **Base OS**: Rocky Linux 9 -**Platforms**: linux/amd64 (x86_64) +**Platforms**: linux/amd64 (x86_64), linux/arm64 (Apple Silicon) **Purpose**: Development and testing environment for PostgreSQL extension development ## Purpose @@ -166,19 +166,30 @@ jobs: ## Platform Support -The image is currently built for **linux/amd64** (x86_64) architecture. +The image is built as a **multiplatform manifest** supporting: +- **linux/amd64** - Intel/AMD x86_64 systems +- **linux/arm64** - Apple Silicon Macs (M1/M2/M3/M4), AWS Graviton -### Usage on ARM64 Systems - -On ARM64 systems (Apple Silicon Macs, AWS Graviton, etc.), Docker will use QEMU emulation to run the amd64 image. While this works, performance will be slower than a native ARM64 build. +Docker automatically selects the appropriate architecture when pulling the image: ```bash -# Pull and run on any platform (emulated on ARM64) +# Docker automatically selects correct architecture docker pull ghcr.io/pgedge/base-test-image:latest + +# On Apple Silicon Mac → pulls linux/arm64 (native performance) +# On Intel/AMD → pulls linux/amd64 (native performance) docker run -it ghcr.io/pgedge/base-test-image:latest /bin/bash ``` -**Note**: For native ARM64 support, the build workflow would need to be enhanced with Docker Buildx multiplatform capabilities and QEMU setup. +To explicitly pull a specific platform: + +```bash +# Force ARM64 variant +docker pull --platform linux/arm64 ghcr.io/pgedge/base-test-image:latest + +# Force AMD64 variant +docker pull --platform linux/amd64 ghcr.io/pgedge/base-test-image:latest +``` ## Build Reproducibility From beeea8d69f187476d435db894890475c3984ee1f Mon Sep 17 00:00:00 2001 From: "Andrei V. Lepikhov" Date: Fri, 9 Jan 2026 11:45:56 +0100 Subject: [PATCH 5/6] Fix hallucinations of Claude --- .github/workflows/spockbench.yml | 1 - tests/docker/Dockerfile-base.el9 | 80 ++++++++++++++------------------ 2 files changed, 35 insertions(+), 46 deletions(-) diff --git a/.github/workflows/spockbench.yml b/.github/workflows/spockbench.yml index cc0876a3..fa478b0f 100644 --- a/.github/workflows/spockbench.yml +++ b/.github/workflows/spockbench.yml @@ -6,7 +6,6 @@ on: pull_request: types: [opened, synchronize, reopened] - permissions: contents: read diff --git a/tests/docker/Dockerfile-base.el9 b/tests/docker/Dockerfile-base.el9 index 1d56314c..45b86be9 100644 --- a/tests/docker/Dockerfile-base.el9 +++ b/tests/docker/Dockerfile-base.el9 @@ -43,49 +43,49 @@ LABEL org.opencontainers.image.title="pgEdge Dev Base Test Image" \ # ============================================================================== # Build Information for Reproducibility # ============================================================================== -RUN set -eux; \ - mkdir -p /etc/pgedge; \ - printf "pgEdge Base Test Image Build Information\n" > /etc/pgedge/build-info.txt; \ - printf "=========================================\n\n" >> /etc/pgedge/build-info.txt; \ - printf "Build Date: %s\n" "${BUILD_DATE:-unknown}" >> /etc/pgedge/build-info.txt; \ - printf "Git Commit: %s\n" "${GIT_COMMIT:-unknown}" >> /etc/pgedge/build-info.txt; \ - printf "Git Branch: %s\n" "${GIT_BRANCH:-unknown}" >> /etc/pgedge/build-info.txt; \ - printf "Rocky Linux: %s\n" "${ROCKYLINUX_VERSION:-unknown}" >> /etc/pgedge/build-info.txt; \ - printf "\nTo reproduce this exact build:\n" >> /etc/pgedge/build-info.txt; \ - printf " git clone https://github.com/pgedge/spock.git\n" >> /etc/pgedge/build-info.txt; \ - printf " git checkout %s\n" "${GIT_COMMIT:-unknown}" >> /etc/pgedge/build-info.txt; \ - printf " docker build -f tests/docker/Dockerfile-base.el9 \\\\\n" >> /etc/pgedge/build-info.txt; \ - printf " --build-arg BUILD_DATE=%s \\\\\n" "${BUILD_DATE:-unknown}" >> /etc/pgedge/build-info.txt; \ - printf " --build-arg GIT_COMMIT=%s \\\\\n" "${GIT_COMMIT:-unknown}" >> /etc/pgedge/build-info.txt; \ - printf " --build-arg GIT_BRANCH=%s .\n" "${GIT_BRANCH:-unknown}" >> /etc/pgedge/build-info.txt; \ - printf "\n" >> /etc/pgedge/build-info.txt; \ +RUN set -eux && \ + mkdir -p /etc/pgedge && \ + printf "pgEdge Base Test Image Build Information\n" > /etc/pgedge/build-info.txt && \ + printf "=========================================\n\n" >> /etc/pgedge/build-info.txt && \ + printf "Build Date: %s\n" "${BUILD_DATE:-unknown}" >> /etc/pgedge/build-info.txt && \ + printf "Git Commit: %s\n" "${GIT_COMMIT:-unknown}" >> /etc/pgedge/build-info.txt && \ + printf "Git Branch: %s\n" "${GIT_BRANCH:-unknown}" >> /etc/pgedge/build-info.txt && \ + printf "Rocky Linux: %s\n" "${ROCKYLINUX_VERSION:-unknown}" >> /etc/pgedge/build-info.txt && \ + printf "\nTo reproduce this exact build:\n" >> /etc/pgedge/build-info.txt && \ + printf " git clone https://github.com/pgedge/spock.git\n" >> /etc/pgedge/build-info.txt && \ + printf " git checkout %s\n" "${GIT_COMMIT:-unknown}" >> /etc/pgedge/build-info.txt && \ + printf " docker build -f tests/docker/Dockerfile-base.el9 \\\\\n" >> /etc/pgedge/build-info.txt && \ + printf " --build-arg BUILD_DATE=%s \\\\\n" "${BUILD_DATE:-unknown}" >> /etc/pgedge/build-info.txt && \ + printf " --build-arg GIT_COMMIT=%s \\\\\n" "${GIT_COMMIT:-unknown}" >> /etc/pgedge/build-info.txt && \ + printf " --build-arg GIT_BRANCH=%s .\n" "${GIT_BRANCH:-unknown}" >> /etc/pgedge/build-info.txt && \ + printf "\n" >> /etc/pgedge/build-info.txt && \ cat /etc/pgedge/build-info.txt # ============================================================================== # System Update and Base Development Tools Installation # ============================================================================== -RUN set -eux; \ - dnf -y update; \ - dnf -y upgrade; \ - dnf -y groupinstall "Development Tools"; \ - dnf clean all; \ +RUN set -eux && \ + dnf -y upgrade && \ + dnf -y install sudo && \ + dnf -y groupinstall "Development Tools" && \ + dnf clean all && \ rm -rf /var/cache/dnf/* # ============================================================================== # User Creation and Configuration # ============================================================================== -RUN set -eux; \ - useradd -m -s /bin/bash ${PGEDGE_USER}; \ - echo "${PGEDGE_USER}:asdf" | chpasswd; \ - echo "${PGEDGE_USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${PGEDGE_USER}; \ - chmod 0440 /etc/sudoers.d/${PGEDGE_USER}; \ +RUN set -eux && \ + useradd -m -s /bin/bash ${PGEDGE_USER} && \ + echo "${PGEDGE_USER}:asdf" | chpasswd && \ + echo "${PGEDGE_USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${PGEDGE_USER} && \ + chmod 0440 /etc/sudoers.d/${PGEDGE_USER} && \ chown -R ${PGEDGE_USER}:${PGEDGE_USER} /home/${PGEDGE_USER} # ============================================================================== # PostgreSQL Build Dependencies and Testing Tools # ============================================================================== # Install all required packages in a single RUN to minimize layers -RUN set -eux; \ +RUN set -eux && \ dnf install --allowerasing --enablerepo=crb -y \ bind-utils \ bison \ @@ -114,42 +114,32 @@ RUN set -eux; \ openssl-devel \ pam-devel \ perl \ - perl-App-cpanminus \ perl-devel \ perl-IPC-Run \ + perl-Test-Simple \ pkgconfig \ procps \ python3 \ python3-devel \ readline-devel \ - sudo \ systemd-devel \ unzip \ uuid-devel \ - vim \ zlib-devel \ - zstd-devel; \ - dnf clean all; \ + libzstd-devel && \ + dnf clean all && \ rm -rf /var/cache/dnf/* /tmp/* /var/tmp/* -# ============================================================================== -# Perl Testing Dependencies -# ============================================================================== -# Note: perl-IPC-Run is already installed via dnf above, only installing Test::More -RUN set -eux; \ - cpanm Test::More; \ - rm -rf /root/.cpanm - # ============================================================================== # SSH Configuration for Testing # ============================================================================== # Switch to pgedge user for SSH setup (security best practice) USER ${PGEDGE_USER} -RUN set -eux; \ - mkdir -p ~/.ssh; \ - ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519 -C "${PGEDGE_USER}@localhost"; \ - cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys; \ - chmod 700 ~/.ssh; \ +RUN set -eux && \ + mkdir -p ~/.ssh && \ + ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519 -C "${PGEDGE_USER}@localhost" && \ + cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys && \ + chmod 700 ~/.ssh && \ chmod 600 ~/.ssh/authorized_keys ~/.ssh/id_ed25519 # ============================================================================== From b07487e84bdb81953e257529df2c6010f8bb46d8 Mon Sep 17 00:00:00 2001 From: "Andrei V. Lepikhov" Date: Mon, 12 Jan 2026 13:18:02 +0100 Subject: [PATCH 6/6] Let a user to vary the base image name - it is needed for testing purposes --- .github/workflows/cache-base-image.yml | 44 ++++++++++++++++++++------ 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/.github/workflows/cache-base-image.yml b/.github/workflows/cache-base-image.yml index 79ed766d..e0a5e250 100644 --- a/.github/workflows/cache-base-image.yml +++ b/.github/workflows/cache-base-image.yml @@ -10,6 +10,17 @@ run-name: Updating the base OS and install necessary extra packages (multiplatfo on: workflow_dispatch: + inputs: + image_name: + description: 'Base image name (without registry/owner prefix)' + required: false + default: 'base-test-image' + type: string + image_tag: + description: 'Additional custom tag (besides :latest and :)' + required: false + default: '' + type: string permissions: contents: read @@ -47,11 +58,14 @@ jobs: echo "GIT_COMMIT=$(git rev-parse HEAD)" >> ${GITHUB_ENV} echo "GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)" >> ${GITHUB_ENV} echo "ROCKYLINUX_VERSION=$(docker run --rm rockylinux:9 cat /etc/rocky-release)" >> ${GITHUB_ENV} + echo "IMAGE_NAME=${{ inputs.image_name || 'base-test-image' }}" >> ${GITHUB_ENV} # Print build information echo "=========================================" echo "pgEdge Base Image Build Information" echo "=========================================" + echo "Image Name: ${{ inputs.image_name || 'base-test-image' }}" + echo "Custom Tag: ${{ inputs.image_tag }}" echo "Build Date: $(date -u +'%Y-%m-%dT%H:%M:%SZ')" echo "Git Commit: $(git rev-parse HEAD)" echo "Git Branch: $(git rev-parse --abbrev-ref HEAD)" @@ -87,8 +101,9 @@ jobs: platforms: linux/amd64,linux/arm64 push: true tags: | - ghcr.io/${{ env.OWNER_LC }}/base-test-image:latest - ghcr.io/${{ env.OWNER_LC }}/base-test-image:${{ env.GIT_COMMIT }} + ghcr.io/${{ env.OWNER_LC }}/${{ env.IMAGE_NAME }}:latest + ghcr.io/${{ env.OWNER_LC }}/${{ env.IMAGE_NAME }}:${{ env.GIT_COMMIT }} + ${{ inputs.image_tag && format('ghcr.io/{0}/{1}:{2}', env.OWNER_LC, env.IMAGE_NAME, inputs.image_tag) || '' }} build-args: | BUILD_DATE=${{ env.BUILD_DATE }} GIT_COMMIT=${{ env.GIT_COMMIT }} @@ -102,7 +117,7 @@ jobs: # Verify multiplatform manifest - name: Inspect multiplatform manifest run: | - docker buildx imagetools inspect ghcr.io/${{ env.OWNER_LC }}/base-test-image:latest + docker buildx imagetools inspect ghcr.io/${{ env.OWNER_LC }}/${{ env.IMAGE_NAME }}:latest # Verify and display build information from the image - name: Display build information @@ -111,37 +126,46 @@ jobs: echo "=========================================" echo "Multiplatform Image Built Successfully!" echo "=========================================" + echo "Image Name: ${{ env.IMAGE_NAME }}" echo "Platforms: linux/amd64, linux/arm64" echo "" echo "Image Tags:" - echo " - ghcr.io/${{ env.OWNER_LC }}/base-test-image:latest" - echo " - ghcr.io/${{ env.OWNER_LC }}/base-test-image:${{ env.GIT_COMMIT }}" + echo " - ghcr.io/${{ env.OWNER_LC }}/${{ env.IMAGE_NAME }}:latest" + echo " - ghcr.io/${{ env.OWNER_LC }}/${{ env.IMAGE_NAME }}:${{ env.GIT_COMMIT }}" + if [ -n "${{ inputs.image_tag }}" ]; then + echo " - ghcr.io/${{ env.OWNER_LC }}/${{ env.IMAGE_NAME }}:${{ inputs.image_tag }}" + fi echo "" echo "To pull this specific build:" - echo " docker pull ghcr.io/${{ env.OWNER_LC }}/base-test-image:${{ env.GIT_COMMIT }}" + echo " docker pull ghcr.io/${{ env.OWNER_LC }}/${{ env.IMAGE_NAME }}:${{ env.GIT_COMMIT }}" echo "" echo "Docker will automatically select the correct architecture:" echo " - linux/amd64 for Intel/AMD systems" echo " - linux/arm64 for Apple Silicon Macs (M1/M2/M3/M4)" echo "" echo "To view build info from the image:" - echo " docker run --rm ghcr.io/${{ env.OWNER_LC }}/base-test-image:latest cat /etc/pgedge/build-info.txt" + echo " docker run --rm ghcr.io/${{ env.OWNER_LC }}/${{ env.IMAGE_NAME }}:latest cat /etc/pgedge/build-info.txt" echo "" echo "To inspect image labels:" - echo " docker inspect ghcr.io/${{ env.OWNER_LC }}/base-test-image:latest | jq '.[0].Config.Labels'" + echo " docker inspect ghcr.io/${{ env.OWNER_LC }}/${{ env.IMAGE_NAME }}:latest | jq '.[0].Config.Labels'" echo "=========================================" # ============================================================================== # Build Complete! # ============================================================================== # The base image is available at: -# https://github.com/pgedge/spock/pkgs/container/base-test-image +# https://github.com/pgedge/spock/pkgs/container/ +# +# Workflow Inputs (configurable through Actions UI): +# - image_name: Base image name (default: 'base-test-image') +# - image_tag: Optional custom tag in addition to :latest and : # # Each build includes comprehensive reproducibility metadata: # 1. /etc/pgedge/build-info.txt - Embedded build information file # 2. OCI image labels with build timestamp, commit SHA, and branch # 3. Commit-specific tag (:) for immutable references -# 4. Workflow output with all build parameters +# 4. Optional custom tag (:) if provided +# 5. Workflow output with all build parameters # # To reproduce any build: # 1. Check workflow output or image labels for git commit SHA