[PostGIS] #6087: Add repository-owned CI status checker and static dashboard generator

PostGIS trac at osgeo.org
Thu Jun 25 22:53:50 PDT 2026


#6087: Add repository-owned CI status checker and static dashboard generator
---------------------+---------------------------
 Reporter:  komzpa   |      Owner:  pramsey
     Type:  defect   |     Status:  new
 Priority:  medium   |  Milestone:  PostGIS 3.6.5
Component:  postgis  |    Version:  3.6.x
 Keywords:           |
---------------------+---------------------------
 == Background ==

 PostGIS currently does not have a simple, machine-readable answer to:

 {{{
 Is every supported CI green?
 }}}

 The current CI status page is mostly a manually maintained list of badge
 images. There is also `utils/ci-trac-line.sh`, which generates Trac table
 markup with hard-coded badge URLs. That is useful for presentation, but
 not good enough for automation or local debugging.

 The common maintainer workflow is simpler:

 {{{
 I pushed something. Did I break anything?
 }}}

 We need a small repository-owned script that answers that question locally
 and can also generate a static HTML page for `postgis.net/ci`.

 The script should query CI provider APIs directly. It must not parse badge
 images.

 == Goal ==

 Add a Python script that can:

 * show CI status for all non-EOL PostGIS branches by default;
 * print a compact terminal report with colored Unicode status markers;
 * generate a static HTML status page;
 * include links to the failing or relevant CI runs for debugging;
 * preserve the previous known status when a CI run is currently in
 progress;
 * avoid making the default view too crowded.

 The default experience should answer:

 {{{
 Did I break anything?
 }}}

 It should not primarily answer:

 {{{
 Who broke what, when, on which runner, with which dependency matrix?
 }}}

 Those details should be available when something is wrong, but not
 dominate the normal view.

 == Proposed location ==

 Add a script under `utils/`, replacing the role currently served by `utils
 /ci-trac-line.sh`:

 {{{
 utils/ci-status.py
 }}}

 Add a repository-owned configuration file. Exact location is flexible, but
 one of these would be fine:

 {{{
 utils/ci-status.json
 }}}

 or:

 {{{
 ci/status-config.json
 }}}

 Do not commit generated HTML or JSON output files. They are build
 artifacts produced by the script and published by cron or another
 deployment job.

 Do not add a test directory just for this service script unless
 maintainers specifically want it later. The script should be simple enough
 to review directly, and it should support enough local dry-run/debug
 output to be useful without a test harness.

 == Default command ==

 Running with no arguments should show all non-EOL version statuses:

 {{{
 python3 utils/ci-status.py
 }}}

 Default behavior:

 * check every configured non-EOL branch;
 * show compact terminal output;
 * include details only for failures, unknown states, stale results, or in-
 progress jobs;
 * include direct links for debugging when something is not green;
 * exit with a useful status code.

 Example output:

 {{{
 ✅ master      all required CI OK
 ✅ 3.6         all required CI OK
 🔄 3.5         previous OK, new run in progress
 ❌ 3.4         2 failures

 Failures:
 3.4 / GitHub Actions / FreeBSD
 status: failure
 url: https://github.com/postgis/postgis/actions/runs/...

 3.4 / Jenkins / bessie32
 status: failure
 url: https://debbie.postgis.net/...
 }}}

 Use colored Unicode on terminals that support it. Use plain text when
 output is not a TTY or when color is disabled.

 Suggested symbols:

 * ✅ success
 * ❌ failure
 * 🔄 in progress
 * ⚠️ unknown, stale, or unavailable
 * ➖ not applicable / disabled

 The exact glyphs are not critical, but they should be readable and not
 rely on color alone.

 == Outputs ==

 The script should support two output modes:

 {{{
 terminal
 html
 }}}

 Terminal is the default.

 Examples:

 {{{
 python3 utils/ci-status.py
 python3 utils/ci-status.py html
 }}}

 `html` should generate a static page and a machine-readable JSON data file
 into an output directory. These generated files should not be checked into
 the PostGIS source tree.

 Example:

 {{{
 python3 utils/ci-status.py html --output-dir /var/www/postgis/ci
 }}}

 This should create something like:

 {{{
 /var/www/postgis/ci/index.html
 /var/www/postgis/ci/status.json
 }}}

 Avoid a large argument surface. Optional flags are fine for debugging and
 deployment, but the normal user should not need them.

 Reasonable optional flags:

 {{{
 --branch BRANCH
 --output-dir DIR
 --json
 --no-color
 --verbose
 }}}

 Do not make users choose provider names, job names, workflow IDs, or
 version lists in normal use.

 == Branch selection ==

 By default, check all configured non-EOL PostGIS versions.

 The list of non-EOL versions should live in the repository configuration,
 not in Trac.

 Example configuration concept:

 {{{
 {
 "branches": [
 { "name": "master", "label": "master", "eol": false },
 { "name": "stable-3.6", "label": "3.6", "eol": false },
 { "name": "stable-3.5", "label": "3.5", "eol": false },
 { "name": "stable-3.4", "label": "3.4", "eol": false },
 { "name": "stable-3.3", "label": "3.3", "eol": true }
 ]
 }
 }}}

 EOL branches may be shown only with an explicit option later. They should
 not appear in the default answer.

 == In-progress behavior ==

 In-progress jobs should not make the default view useless.

 If a new CI run is in progress and the previous completed run was green,
 show:

 {{{
 🔄 3.5 previous OK, new run in progress
 }}}

 If a new CI run is in progress and the previous completed run failed,
 show:

 {{{
 🔄 3.5 previous failed, new run in progress
 }}}

 If no previous completed result is known, show:

 {{{
 🔄 3.5 in progress, no previous result known
 }}}

 This means the status model needs to keep both:

 * current state;
 * previous completed state, when available.

 For example:

 {{{
 {
 "current": {
 "status": "in_progress",
 "url": "https://..."
 },
 "previous_completed": {
 "status": "success",
 "revision": "abc123",
 "url": "https://..."
 }
 }
 }}}

 The aggregate branch status should prefer current failures. But a purely
 running branch should be displayed as in progress with previous known
 state, not as a scary failure.

 == HTML page behavior ==

 The HTML page should be compact by default.

 Top-level view:

 {{{
 PostGIS CI status

 ✅ master
 ✅ 3.6
 🔄 3.5 previous OK, new run in progress
 ❌ 3.4 2 failures
 }}}

 Detailed rows should be visible for:

 * failed checks;
 * unknown checks;
 * stale checks;
 * in-progress checks;
 * optionally all checks after expanding a section.

 The page should not default to a huge matrix of every CI job for every
 branch. That makes the common case worse.

 The common case is:

 {{{
 Everything is fine. Go back to work.
 }}}

 When something is not fine, the page should immediately show where to
 click.

 For every failed, stale, unknown, or in-progress check, include:

 * branch;
 * check name;
 * status;
 * previous completed status if applicable;
 * tested revision if known;
 * target revision if known;
 * age or completion time;
 * direct link to the CI run or job.

 The page should be static. It should not use iframes and should not make
 browser-side requests to GitHub, Woodpecker, Jenkins, or badge URLs.

 A cron job can regenerate the page periodically.

 == Local vs published use ==

 The same script should be usable locally and for publishing.

 Local:

 {{{
 python3 utils/ci-status.py
 }}}

 Publish:

 {{{
 python3 utils/ci-status.py html --output-dir /var/www/postgis/ci
 }}}

 The publishing job should run from cron or an existing trusted CI host
 such as debbie.

 It should take a snapshot and exit. It should not wait for all CI jobs to
 finish.

 A failed CI status must not make page generation fail. Red CI is valid
 page content.

 Generation should use temporary files and atomic replacement so the
 website is never left half-written.

 == Providers ==

 The initial implementation should support the currently relevant PostGIS
 CI sources:

 * GitHub Actions;
 * Woodpecker;
 * Jenkins.

 The script should query native JSON APIs where possible.

 It must not parse SVG badge images.

 Existing legacy CI entries should be handled explicitly:

 * supported;
 * disabled with reason;
 * retired with reason;
 * unsupported with follow-up ticket.

 Do not silently drop a current CI entry.

 == GitHub Actions ==

 Support configured workflows from `.github/workflows/`.

 For each configured workflow and branch:

 * find the current run for the branch head when possible;
 * detect running, success, failure, cancelled, skipped, missing, and stale
 states;
 * include a link to the workflow run;
 * include the tested commit SHA when available.

 GitHub token use should be optional locally, but documented for cron to
 avoid rate limits.

 == Woodpecker ==

 Support the Woodpecker API for `postgis/postgis`.

 For each branch:

 * find the relevant latest pipeline;
 * detect success, failure, running, pending, blocked, killed, cancelled,
 and unknown states;
 * include a link to the pipeline;
 * include the tested commit SHA when available.

 == Jenkins ==

 Jenkins jobs are irregular, so Jenkins job URLs and branch matching rules
 should be explicit in config.

 Support:

 * direct job API URLs;
 * branch/version substitutions;
 * build parameters where needed;
 * finding the newest matching build from recent builds;
 * detecting building, success, failure, unstable, aborted, missing, and
 unknown states.

 Jenkins badge images must not be used as the status source.

 == Status model ==

 Each check should normalize to:

 * success
 * failure
 * in_progress
 * unknown
 * stale
 * disabled
 * not_applicable

 Each check may also have:

 * previous_completed_status;
 * previous_completed_url;
 * previous_completed_revision;
 * message;
 * debug URL.

 Overall branch status:

 * failure if any required current check failed;
 * unknown if any required check is unknown or stale;
 * in_progress if no current required check failed or is unknown, but at
 least one is running;
 * success if all required checks are currently successful;
 * disabled and informational checks do not affect aggregate status.

 The terminal and HTML output should show previous completed status for in-
 progress checks.

 == Exit codes ==

 For default terminal mode:

 * 0: all required checks for all non-EOL branches are OK;
 * 1: at least one required check failed;
 * 2: no failure is known, but some required check is in progress, stale,
 or unknown;
 * another non-zero code: invalid configuration or script error.

 For HTML generation:

 * 0: page generated successfully;
 * non-zero: page generation failed.

 A failed CI run should not make HTML generation return non-zero.

 == Configuration ==

 Add one repository-owned JSON config file.

 It should define:

 * non-EOL branches;
 * checks;
 * providers;
 * whether each check is required;
 * branch applicability;
 * provider URLs and workflow/job identifiers;
 * disabled or retired checks with reasons.

 The config should be the source of truth for CI inventory.

 Do not keep a separate maintained CI matrix in Trac.

 == Implementation notes ==

 Keep the implementation boring.

 Preferred constraints:

 * Python 3 standard library only;
 * no runtime dependency on requests, PyYAML, Jinja, or pytest;
 * no daemon;
 * no database;
 * no committed generated status files;
 * no browser-side API calls;
 * no iframe dashboard;
 * no badge image parsing.

 It is fine for the script to be a single readable file.

 The generated HTML can be assembled with simple string templates and
 proper HTML escaping.

 The generated JSON should be deterministic enough for other scripts to
 consume.

 == Documentation ==

 Document briefly in comments or a small README section:

 * how to run locally;
 * how to generate HTML locally;
 * how cron can publish the page;
 * how to add a new CI check;
 * how to retire an old CI check;
 * how to mark a branch EOL.

 Update README and release documentation to point to the new page.

 After deployment, the Trac ContinuousIntegration page should become a
 short pointer to:

 {{{
 https://postgis.net/ci/
 }}}

 It should not remain a second manually maintained CI matrix.

 == Acceptance criteria ==

 * `python3 utils/ci-status.py` with no arguments shows all non-EOL branch
 statuses.
 * The default terminal output is compact and uses colored Unicode when
 possible.
 * Failures include direct links to the relevant CI runs or jobs.
 * In-progress checks include previous completed status when available.
 * `python3 utils/ci-status.py html --output-dir DIR` generates
 `index.html` and `status.json` in `DIR`.
 * Generated HTML and JSON are not committed to the repository.
 * The HTML page is a compact static dashboard.
 * The HTML page is not a huge default matrix.
 * The HTML page shows detailed rows only for failures, unknowns, stale
 checks, and in-progress checks, or behind expansion.
 * The script does not parse badge images.
 * The script does not use iframes.
 * The script does not require browser-side API calls.
 * The script can be run from cron to publish `postgis.net/ci`.
 * Failed CI does not prevent publishing the red status page.
 * The CI inventory lives in the repository, not in Trac.
 * Runtime uses Python standard library only.

 == Non-goals ==

 This ticket does not need to implement:

 * a live web service;
 * a full incident management system;
 * historical uptime charts;
 * time-lapse videos;
 * notifications;
 * automatic reruns;
 * waiting for all CI to finish;
 * a test suite for the helper script.

 A later ticket can add history by saving periodic snapshots.
-- 
Ticket URL: <https://trac.osgeo.org/postgis/ticket/6087>
PostGIS <http://trac.osgeo.org/postgis/>
The PostGIS Trac is used for bug, enhancement & task tracking, a user and developer wiki, and a view into the subversion code repository of PostGIS project.


More information about the postgis-tickets mailing list