Skip to content

Firebird Docker v2#36

Open
fdcastel wants to merge 25 commits intoFirebirdSQL:masterfrom
fdcastel:feature/v2
Open

Firebird Docker v2#36
fdcastel wants to merge 25 commits intoFirebirdSQL:masterfrom
fdcastel:feature/v2

Conversation

@fdcastel
Copy link
Copy Markdown
Member

@fdcastel fdcastel commented Apr 7, 2026

TL;DR: Test images available here.


This is a complete rewrite of the Firebird Docker build system.

  • Addresses multiple security issues, bugs, and feature requests
  • while modernizing the CI/CD pipeline; and
  • adding fork publishing support.

Closes:

Key Changes

Security Fixes

  • SQL injection prevention in entrypoint.sh: Passwords with single quotes no longer break or inject SQL (escape_sql_string)
  • Template engine replacement: Replaced ExpandString (arbitrary PowerShell code execution) with safe {{VAR}} string replacement
  • No more .bashrc overwrite: Environment file written to /opt/firebird/.firebird_env instead of clobbering user's .bashrc

Bug Fixes

  • Fixed chmod 644chmod 755 on data directory (was preventing file creation)
  • Fixed libtommath symlink for multi-arch (was hardcoded to x86_64-linux-gnu)
  • Fixed unquoted array expansion in process_sql
  • Fixed init_db glob that would fail when no init scripts present
  • Fixed Wait-Port/Test-Port using hardcoded values instead of parameters
  • Removed redundant Start-Sleep before port polling

New Features

  • Native ARM64 builds on ubuntu-24.04-arm runners (no QEMU)
  • Deterministic tag algorithm with -{distro} suffix tags (e.g., 5-bookworm, 5.0.3-bookworm)
  • Snapshot images from FirebirdSQL/snapshots pre-release builds, including Firebird 6 snapshots
  • Single Dockerfile template replaces 4 per-distro templates
  • Tini as PID 1 for proper signal handling and zombie reaping
  • STOPSIGNAL SIGTERM for clean Firebird shutdown
  • Fork-friendly CI: Forks build only latest version + default distro on amd64
  • workflow_dispatch inputs for version and distro filtering
  • Fork publishing to GHCR: Forks can publish images to ghcr.io in addition to Docker Hub
  • Test-Published task: Validates images after publishing to public registries

Architecture

  • assets.json restructured with config section (distro settings, blocked variants)
  • Shared functions extracted to src/functions.ps1
  • PSFirebird v1.0.0+ used for release discovery (Find-FirebirdRelease, Find-FirebirdSnapshotRelease)
  • CI matrix: amd64 (ubuntu-latest) + arm64 (ubuntu-24.04-arm) with manifest creation
  • Publish workflow split into per-arch build + manifest creation jobs

CI Workflows

Workflow Trigger Runners Notes
ci.yaml Push/PR (all branches) amd64 + arm64 (official repo); amd64 only (forks) Standard build & test
publish.yaml Push to master / weekly amd64 + arm64, then manifest creation Official Docker Hub publication
publish-fork.yaml Manual dispatch (forks) amd64 + arm64, then manifest creation Fork publication to Docker Hub & GHCR
snapshot.yaml Daily / manual dispatch amd64 + arm64 Pre-release images from FirebirdSQL/snapshots

Test Results

  • Integration tests pass (core image functionality)
  • Tag unit tests pass (Pester)
  • Test-Published task validates published images from Docker Hub and GHCR
  • New tests: SQL injection resistance, graceful SIGTERM shutdown, tini PID 1, _FILE variants, mutual exclusion, tag correctness

fdcastel added 20 commits April 7, 2026 06:10
- Add LICENSE file (IDPLv1, matching upstream Firebird)
- Clean up .github/FUNDING.yml (keep only custom donation link)
- Update .gitignore (ignore tmp/, generated/, logs)
- Add AGENTS.md (AI agent guidelines)
- PSFirebird v1.0.0 from PSGallery added as dependency (in CI steps)
Dockerfile:
- Replace 4 per-distro templates with single parameterized Dockerfile.template
- Use {{VAR}} placeholders instead of <% %> ExpandString syntax
- Use dpkg --print-architecture for runtime arch detection (no ARCH_ARM64 hack)
- Use dpkg-architecture for multi-arch libtommath symlink (fixes B6)
- Fix chmod 644 -> 755 on data directory (fixes B5)
- Add tini as PID 1 init for proper signal handling
- Add STOPSIGNAL SIGTERM for clean Firebird shutdown
- OCI labels now passed dynamically at build time (fixes I8)

Entrypoint:
- Fix SQL injection: escape single quotes in passwords/usernames (fixes S1)
- Fix %s format placeholders in error messages (fixes S2)
- Fix .bashrc overwrite: use /opt/firebird/.firebird_env with append (fixes B9)
- Fix unquoted array in process_sql (fixes B8)
- Fix init_db glob with compgen guard (fixes I5)
- Fix UUOC: use redirection instead of cat pipe (fixes SI3)
Build script:
- Replace Expand-Template (ExpandString) with safe {{VAR}} string replacement
- Rewrite Update-Assets to use PSFirebird Find-FirebirdRelease (PSGallery v1.0.0+)
- Rewrite Prepare task: single Dockerfile.template with distro config from assets.json
- Remove per-image image.build.ps1.template (centralized build/test/publish)
- Fix Publish task to use FilteredAssets (was unfiltered)
- Build and test only host architecture locally (CI handles cross-arch)
- Use UTF-8 encoding consistently

assets.json:
- Add config section: distros (baseImage, icuPackage, extraPackages), blockedVariants
- Normalize releases to uniform {amd64: {}, arm64?: {}} objects
- Remove generated/ from git tracking (now fully gitignored)
- Extract shared functions to src/functions.ps1 (sourced by build script and tests)
- Implement deterministic Get-ImageTags with full tag hierarchy:
  {version}-{distro}, {major}-{distro}, {distro}, {version}, {major}, latest
- Add -bookworm suffix tags (fixes upstream Issue FirebirdSQL#34)
- Add Pester unit tests for tag generation (6 test cases)
- Remove duplicated function definitions from build script
ci.yaml:
- Add workflow_dispatch inputs for version-filter and distro-filter
- Forks: build only latest version + default distro (fast feedback)
- Official repo: build full matrix
- Install PSFirebird as dependency
- Remove QEMU setup (no cross-arch builds locally)
- Add tag unit tests step (Pester)

publish.yaml:
- Install PSFirebird as dependency
- Remove QEMU setup (native builds only)
- Add tag unit tests step
- Remove log upload (generated/ is gitignored)
- Add Build-Snapshot task using PSFirebird Find-FirebirdSnapshotRelease
- Supports branches: master (FB6), v5.0-release, v4.0
- Tags: 6-snapshot, 5-snapshot, 4-snapshot
- Add snapshot.yaml workflow (daily schedule + manual dispatch)
- Add -Branch parameter to build script
- FIREBIRD_ROOT_PASSWORD_with_special_characters: SQL injection resistance (single quotes)
- FIREBIRD_USER_PASSWORD_with_special_characters: user passwords with quotes
- Graceful_shutdown_via_SIGTERM: verify clean shutdown on SIGTERM
- Tini_is_PID_1: verify tini is running as PID 1
All 20 tests pass.
- Create DECISIONS.md with 11 architecture decisions and rationale
- Regenerate README.md from template (with full tag table)
- Wait-Port: use $ContainerName and $Port params instead of hardcoded $cId/3050
- Use-Container: remove Start-Sleep before Wait-Port (let polling handle warmup)
- Fixes tasks 6.1 and 6.2 from V2 plan
- ci.yaml: matrix strategy with amd64 (ubuntu-latest) + arm64 (ubuntu-24.04-arm)
- ci.yaml: forks skip arm64 to save CI minutes; official repo runs both
- ci.yaml: scope determination moved to separate job for matrix reuse
- publish.yaml: split into build-and-test (per-arch) + create-manifests jobs
- publish.yaml: Publish-Arch pushes arch-specific images, Publish-Manifests creates OCI manifests
- snapshot.yaml: add arm64 matrix dimension
- All workflows: add FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 env
- Build script: add Publish-Arch and Publish-Manifests tasks
- Completes tasks 4.2 and 4.3
- Snapshot images section with tag table and usage warning
- Architecture availability section (arm64 for FB5+ only)
- Regenerated README.md from template
- Completes task 5.5
…ion test

- FIREBIRD_ROOT_PASSWORD_FILE: loads SYSDBA password from file
- FIREBIRD_PASSWORD_FILE: loads user password from file
- Mutual exclusion: setting both _FILE and env var fails with clear error
- Tag correctness: verifies OCI version label matches semver pattern
- All 24 tests pass
- Completes tasks 6.6, 6.8, 6.11 (ARM64 smoke via task 4.3)
- Prerequisites, quick start, project structure overview
- Key rules for contributors
- Instructions for following new Firebird releases
- Completes task 7.2
- Build script: add $Registry parameter (default: 'firebirdsql')
  Override with 'ghcr.io/<owner>' to publish to GitHub Packages
- All tasks (Build, Test, Publish-Arch, Publish-Manifests, Publish,
  Build-Snapshot) use $script:imagePrefix from the parameter
- New workflow: publish-fork.yaml
  - workflow_dispatch only (manual trigger, no push events)
  - Uses built-in GITHUB_TOKEN — no extra secrets needed
  - Defaults to latest version + default distro (bookworm) for fast testing
  - Optional: include-arm64, include-snapshots inputs
  - Builds, tests, pushes to ghcr.io/<owner>/firebird
  - Creates multi-arch manifests when both arches are built
  - Job summary with ready-to-use pull commands
publish-fork.yaml:
- amd64-only (default): Publish-Direct pushes final image name directly
  (ghcr.io/owner/firebird) — produces exactly ONE package, no staging
- multi-arch (include-arm64=true): Publish-Arch + create-manifests as before
  (staging packages firebird-amd64/-arm64 exist as necessary side effect)
- Workflow summary moved to build-and-push job for amd64-only case
- create-manifests job skipped entirely when include-arm64=false

firebird-docker.build.ps1:
- Add Publish-Direct task: retags -arch staging images to final name, pushes
  only the final image (no staging in registry)

CONTRIBUTING.md:
- Full developer documentation: Building, Testing, Maintenance, Release process
- Added -Registry parameter documentation for fork builds
- Added tag unit tests section
- Updated Project Structure and Key Rules

src/README.md.template / README.md:
- Removed 'Development notes' section (moved to CONTRIBUTING.md)
- README is now end-user only
…n to v4

Publish-Arch / Publish-Manifests / Publish:
- Change staging image naming from 'firebird-amd64:tag' to 'firebird:tag-amd64'
  This keeps all images (staging + final) within the single 'firebird' package
  instead of creating separate 'firebird-amd64' and 'firebird-arm64' packages
- amd64-only (Publish-Direct / default fork path): unchanged, pushes final name
- Legacy Publish task: same staging tag convention

Workflows (publish-fork.yaml, publish.yaml, snapshot.yaml):
- Update docker/login-action@v3 -> @v4 (Node 24 runtime, no deprecation warnings)

publish-fork.yaml summary:
- Remove 'staging packages can be deleted' note (no separate packages anymore)
- Remove 'make package public' note (public repos -> public packages by default)
FIREBIRD_DATABASE_PAGE_SIZE test:
- Change from 4096 to 8192 as the small page size under test
  Firebird 6 raised the minimum page size to 8192 (4096 is rounded up silently)

FIREBIRD_CONF_can_change_any_setting test:
- Replace FileSystemCacheThreshold (removed in FB6) with TempCacheLimit
  TempCacheLimit exists in all supported versions (3, 4, 5, 6)
- Also remove the duplicate DefaultDbCachePages env var in the container params

Tag_correctness_via_docker_inspect test:
- Accept snapshot version labels (e.g. '5-snapshot') in addition to semver
  Snapshot images have FIREBIRD_VERSION set to the tag name, not a semver
firebird-docker.build.ps1:
- New task Test-Published: runs the full test suite against final published
  images (ghcr.io/<owner>/firebird:tag) — same images end users pull.
  Pulls each image before testing. Requires -Registry.
  Unlike Test (which targets local arch-specific staging images), this
  exercises the actual multi-arch manifest from the registry.

CONTRIBUTING.md:
- Document Test-Published with usage examples
- Document snapshot testing via FULL_IMAGE_NAME override
@fdcastel
Copy link
Copy Markdown
Member Author

fdcastel commented Apr 7, 2026

Pushed a new version. But now the CI times went from ...

image

to...

image

What if, when working on forks, after each push, we build and test only the latest release for each major Firebird version? For example, just four images:

  • the latest 3.x,
  • latest 4.x,
  • latest 5.x, and
  • the most recent snapshot.

For the official repository, keep the current behavior unchanged (i.e., always perform a full build).

@mrotteveel
Copy link
Copy Markdown
Member

I did a test run with Jaybird (master) against ghcr.io/fdcastel/firebird:6-snapshot, and it seems to be working. I did have some test failures, but those were caused by tests assuming a default port (I mapped to another port) that I need to fix or exclude.

@mrotteveel
Copy link
Copy Markdown
Member

I've also run against ghcr.io/fdcastel/firebird:latest, and that also works fine. I have a failing test with ghcr.io/fdcastel/firebird:5-snapshot, but based on the failure, I think that is a problem in the snapshot itself.

@fdcastel
Copy link
Copy Markdown
Member Author

What if, when working on forks, after each push, we build and test only the latest release for each major Firebird version?

Working on it.

@fdcastel
Copy link
Copy Markdown
Member Author

All done! Changed:

  1. firebird-docker.build.ps1: Added [switch]$LatestPerMajor parameter. In FilteredAssets, when set, it groups versions by major and keeps only the first (latest) entry per group:

    $result = $result | Group-Object { ([version]$_.version).Major } | ForEach-Object { $_.Group[0] }

    This reduces 17 versions to 3 (5.0.3, 4.0.6, 3.0.13).

  2. ci.yaml: Added a lightweight determine-scope job (pure bash, no modules needed) that sets latest-per-major=true when the repo is a fork and the trigger is not workflow_dispatch. That output is then passed to both Build and Test steps as $params['LatestPerMajor'] = $true.

  3. DECISIONS.md — D-008 updated to document the policy: forks build latest-per-major; official repo and manual dispatch always build everything.

  4. AGENTS.md — CI/CD description updated to match.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants