Skip to content

feat(auto-derive): scaffold ip_map + demoLibrary derivation bot (dry-run)#135

Merged
bradymiller merged 6 commits into
openemr:masterfrom
bradymiller:demo-farm-pr1-scaffold-derive
Jun 28, 2026
Merged

feat(auto-derive): scaffold ip_map + demoLibrary derivation bot (dry-run)#135
bradymiller merged 6 commits into
openemr:masterfrom
bradymiller:demo-farm-pr1-scaffold-derive

Conversation

@bradymiller

@bradymiller bradymiller commented Jun 27, 2026

Copy link
Copy Markdown
Member

Summary

PR #1 of three for the G6 auto-generation effort (full design in
openemr/openemr docs/release-mechanism-gaps.md).
Implements the reconciliation script + fixture tests in dry-run
mode only
. This PR does NOT:

Goal

Replace manual maintenance of ip_map_branch.txt +
demoLibrary.source startDemoWrapper case statement with a daily
reconciliation bot that derives the full state from upstream
openemr/openemr master + release-targets.yml + per-rel-branch
Dockerfile ARGs + flex Dockerfile ARGs + docker-build-*.yml (Alpine/PHP matrix)

  • ci/apache_* directory listing (supported PHP set).

Reconciliation, not from-scratch render

Cluster identity is sticky across runs because each cluster name
maps to a subdomain (e.g., eighteight.openemr.io) referenced
from external surfaces (wiki, social, mail). Reassigning a cluster's
meaning would break those external references. The bot reads the
CURRENT ip_map_branch.txt as state, applies a reconciliation diff
against the desired state computed from upstream, and writes the new
file preserving cluster→subdomain stability.

Section ownership matrix

Section Cluster identity col 3 (branch) Flex image
Production (five + aliases) fixed latest row's openemr_version_ref (typically a tag) latest holder's release Dockerfile ARGs
Up-for-grabs (four + aliases) fixed preserved (community claims) from master's flex Dockerfile ARGs (both Alpine + PHP), always
Master demos sticky; new from parked master anchored at master's flex Dockerfile ALPINE_VERSION, drops to lower Alpine if matrix lacks the cluster's PHP
Release demos sticky; new from parked each release-target row's openemr_version_ref (tag OR branch) rel branch's release Dockerfile ARGs
Parked dynamic bench preserved preserved
Misc hand-curated preserved preserved (bot never touches)

col 3 derivation rules

openemr_version_ref from release-targets.yml is the single source of truth for col 3 in production + release demos. Each non-master row in release-targets drives exactly one release demo; the row with latest in docker_tags additionally drives the production demos. Side effect: the latest holder gets both a production demo (e.g. five=v8_0_0_3) and a release demo with the same openemr_version_ref (e.g. three=v8_0_0_3) — different hostnames / demo-data configs, same source.

Sticky cluster assignment for release demos (per row, 3-tier):

  1. Existing cluster whose current col 3 exactly matches row's openemr_version_ref → reuse
  2. Else existing cluster whose current col 3 matches row's branch field (single-row lifecycle: rel-810v8_1_0 after release) → reuse, col 3 rolls forward
  3. Else claim from parked (new row case)

When 8.1.0 ships from rel-810: cluster eight rolls forward (col 3 rel-810v8_1_0, label Development DemoRelease Demo), same cluster identity. When 8.1.1 staging adds a second rel-810 row (openemr_version_ref: rel-810): the new row claims a parked cluster; eight stays on the v8_1_0 row.

Description regeneration

Always regenerated from template (no preservation), so stale versions auto-correct:

  • Production: [Alternate/Another Alternate] Public OpenEMR <ver> Production Demo
  • Release demo (col 3 looks like a tag): Public OpenEMR <ver> Release Demo on Alpine <A> (with PHP <P>)
  • Release demo (col 3 looks like a branch): Public OpenEMR <ver> Development Demo on Alpine <A> (with PHP <P>)
  • _a aliases insert With Demo Data On
  • <ver> = shortest numeric tag from that row's docker_tags (e.g., 8.1.1,next8.1.1)

Live dry-run example: cluster eight's description auto-corrected from stale 8.1.0 to current 8.1.1 (sourced from rel-810's docker_tags: 8.1.1,next).

branch_tag column (col 13) deprecated

The col-13 enum (branch/tag) that told demo_build.sh how to clone is now unnecessary — git clone --branch <ref> --depth 1 works for both branches and tags. Header is now branch_tag(not used); all values are 0 (matches the existing "(not used)" column convention). Position is preserved (cols 14+ unaffected).

  • demo_build.sh: dropped the branch/tag conditional clone block in favor of a single shallow clone (verified --branch <tag> --depth 1 works against v8_0_0_3).
  • IpMapBumper.php: now identifies tag rows via regex on col 3 (^v\d+_\d+...) instead of reading col 13. Will be retired entirely in PR Add development demos for Alpine 3.5 and 3.6 #3.

Fail-loud edge cases

  • No latest holder in release-targets.yml
  • Parked bench empty when a new cluster is needed
  • Dockerfile / flex Dockerfile ARG parse failure
  • ci/apache_* directory listing empty on upstream master
  • docker_tags field contains no numeric version tag
  • No Alpine in flex matrix supports a cluster's required PHP

Files added

  • .github/workflows/derive-ip-map.yml (78 lines): PR + schedule
    (0 7 * * * UTC, 1h after openemr's docker-release-orchestrator)
    • workflow_dispatch triggers; runs derive.sh in dry-run; emits
      diff to step summary + artifact; runs fixture tests as a separate
      job
  • tools/auto-derive/derive.sh: reconciliation script. bash + yq + curl + awk. No PHP. Supports both live mode (raw.githubusercontent.com) and fixture mode (file://) for tests.
  • tools/auto-derive/README.md: design + usage
  • tools/auto-derive/fixtures-and-tests/test.sh: fixture runner; diffs actual vs expected per scenario
  • 6 fixture scenarios:
    • basic — canonical no-diff round-trip
    • unreleased-skip — release-targets has unreleased row
    • new-rel-branch — rel-820 added; cluster taken from parked
    • retired-rel-branch — rel-704 dropped; cluster parks
    • multirow-rel810 — two rows for rel-810 (latest-pinned + dev-pinned); each gets its own cluster
    • empty-parked-fail — new branch needed but parked empty; fails

Each fixture's upstream/master/ now includes docker/flex/Dockerfile (for the master-demos Alpine anchor + up-for-grabs derivation) and .github/workflows/docker-build-NN.yml stubs (for the flex Alpine→PHP matrix).

Verified locally

  • ✅ All 6 fixture tests pass
  • ✅ Live dry-run against current upstream produces sensible deltas:
    • six/_a: col 3 rel-704v7_0_4 (tag-pinned now per release-targets), label flips Development → Release Demo
    • three/_a: col 3 rel-800v8_0_0_3, label flips Development → Release Demo
    • eight/_a: col 3 stays rel-810, description version auto-corrects 8.1.0 → 8.1.1
    • All sticky cluster identities preserved; no parked churn
  • ✅ Supported PHP matrix correctly resolves to [8.2, 8.3, 8.4, 8.5] from ci/apache_*
  • ✅ Cluster two correctly resolves to Alpine 3.22 (not 3.23) because PHP 8.2 isn't in 3.23's matrix per docker-build-323.yml

Test plan

  • CI passes (workflow lint + fixture tests)
  • PR's derive-ip-map.yml run renders the dry-run output in
    step summary + uploads artifact
  • Maintainer reviews the dry-run output against current state →
    iterate on any algorithmic surprises before PR Use --branch and --depth 1 when clone repo to improve efficiency #2 wires the
    open-PR-on-diff behavior

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added an automated, dry-run workflow that generates and validates demo mapping outputs on pull requests, daily schedule, and manual trigger.
    • Added/updated tooling to generate and reconcile demo mapping data and Docker demo library content, with fixture-based test coverage.
  • Bug Fixes
    • Improved version/tag handling to consistently detect releasable rows from the version-formatted branch column.
    • Updated demo update/bumping logic and fixtures for the revised mapping header conventions.
  • Documentation
    • Added a README describing how the demo reconciliation process works and how to run it locally.

… bot (dry-run)

PR openemr#1 of three for the G6 auto-generation effort (full design in
openemr/openemr docs/release-mechanism-gaps.md). Implements the
reconciliation script in dry-run mode + fixture tests; does NOT
open PRs yet (PR openemr#2 scope) and does NOT retire the existing
bump-tag.yml (PR openemr#3 scope).

Goal: replace manual maintenance of ip_map_branch.txt +
demoLibrary.source startDemoWrapper case statement with a daily
reconciliation bot that derives the full state from upstream
openemr/openemr master + per-rel-branch Dockerfile ARGs +
ci/apache_* directory listing (canonical PHP-versions source).

Reconciliation, not from-scratch render. Cluster identity is
sticky across runs because each cluster name maps to a subdomain
(e.g. eight -> eight.openemr.io) referenced from external surfaces
(wiki, social, mail). The bot reads CURRENT ip_map_branch.txt as
state, applies a diff against desired state computed from
upstream, and writes the new file preserving cluster -> subdomain
stability.

Section ownership matrix:
- Production (five+aliases): fixed cluster IDs; branch from latest
  holder's openemr_version_ref; flex from latest holder's
  Dockerfile ARGs
- Up-for-grabs (four+aliases): fixed cluster IDs; branch preserved
  (community claims); flex always from master's Dockerfile
- Master demos: sticky cluster IDs (new from parked); pinned to
  master; flex from master's Alpine + cluster's assigned PHP
- Release demos: sticky cluster IDs (new from parked); pinned to
  rel branch; flex from that rel branch's Dockerfile ARGs
- Parked: dynamic bench (overflow + retired-from-active)
- Misc: hand-curated; bot never touches

Fail-loud edge cases: no latest holder; parked bench empty when
new cluster needed; Dockerfile ARG parse failure; ci/apache_*
listing empty.

Files added:

- .github/workflows/derive-ip-map.yml (78 lines): PR + schedule
  (0 7 * * * UTC) + workflow_dispatch triggers; runs derive.sh in
  dry-run; emits diff to step summary + artifact; runs fixture
  tests as a separate job
- tools/auto-derive/derive.sh (1031 lines, 733 non-comment): main
  reconciliation script. bash + yq + curl + awk. No PHP. Supports
  both live mode (https://raw.githubusercontent.com) and fixture
  mode (file:// for tests).
- tools/auto-derive/README.md (86 lines): brief design + usage
- tools/auto-derive/fixtures-and-tests/test.sh (112 lines):
  fixture runner; diffs actual vs expected per scenario
- 6 fixture scenarios under
  tools/auto-derive/fixtures-and-tests/fixtures/:
  - basic (canonical no-diff round-trip)
  - unreleased-skip (release-targets has unreleased row)
  - new-rel-branch (rel-820 added; cluster taken from parked)
  - retired-rel-branch (rel-704 dropped; cluster parks)
  - multirow-rel810 (dev pointer + prior-shipped row both present)
  - empty-parked-fail (new branch needed but parked empty; fails)

Verified locally:
- All 6 fixture tests pass
- Live dry-run against current upstream produces sensible deltas:
  - two/two_a Alpine 3.22 -> 3.23 (drift catch)
  - two_a description case normalization
  - three/three_a release-demo -> parked (rel-800 is now latest,
    so its coverage lives in production rows; release-demo slot
    redundant)
- Supported PHP matrix correctly resolves to [8.2, 8.3, 8.4, 8.5]
  from ci/apache_* (no 8.6 leak from forward-looking workflow
  matrix files)

Out of PR openemr#1 scope (tracked for PR openemr#2 + PR openemr#3):
- PR openemr#2: open actual PR on detected diff (force-push pattern)
- PR openemr#3: atomic flip — wire repository_dispatch consumers
  (openemr-tag/-rel-cut/-update) + unreleased skip + retire
  bump-tag.yml + delete tools/release/ PHP toolchain

Assisted-by: Claude Code
@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds an auto-derive workflow, a Bash generator that rewrites ip_map_branch.txt and demoLibrary.source, fixture workspaces for validation, and release-mapping updates that switch IpMapBumper and demo_build.sh to branch-based handling.

Changes

Auto-derive workflow and release mapping

Layer / File(s) Summary
Workflow, docs, and test harness
.github/workflows/derive-ip-map.yml, tools/auto-derive/README.md, tools/auto-derive/fixtures-and-tests/test.sh
The workflow adds PR, cron, and manual triggers; the README documents derive.sh and fixture layout; test.sh runs the generator across fixture workspaces and checks expected failures or diffs.
Generator inputs and rendering
tools/auto-derive/derive.sh
derive.sh fetches release targets, Dockerfile build args, and supported PHP versions, then parses the current map and demoLibrary case table, computes the desired state, renders both outputs, and reports or writes diffs.
Fixture workspaces and upstream inputs
tools/auto-derive/fixtures-and-tests/fixtures/basic/..., tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/..., tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/..., tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/..., tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/..., tools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/...
The fixture workspaces provide current, upstream, and expected data for the baseline, expanded, retired, unreleased, and empty-parked scenarios.
Bumper and schema tests
demo_build.sh, ip_map_branch.txt, tools/release/src/IpMapBumper.php, tools/release/tests/IpMapBumperTest.php
demo_build.sh stops using branchOrTag, ip_map_branch.txt normalizes branch_tag, and IpMapBumper plus its tests switch to regex-based branch handling.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

A rabbit read YAML by moonbeam light,
then hopped through fixtures left and right.
With branch tags hushed and maps made neat,
the build went crunch with tiny feet.
🐇✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.82% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: scaffolding a dry-run auto-derive bot for ip_map and demoLibrary generation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 markdownlint-cli2 (0.22.1)
tools/auto-derive/README.md

markdownlint-cli2 wrapper config was not available before execution


Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/derive-ip-map.yml:
- Around line 32-33: Both Checkout steps currently leave Git credentials
persisted after `actions/checkout`, even though the jobs only run
repository-controlled shell and do not need authenticated Git access. Update
each `actions/checkout` usage in the workflow to set `persist-credentials:
false`, including both checkout steps referenced by the jobs so credentials are
not retained after checkout.
- Line 33: The workflow is using mutable GitHub Actions tags instead of pinned
SHAs, so update each `uses:` entry in the derive-ip-map workflow to the reviewed
commit SHA form. Replace the `actions/checkout` and `actions/upload-artifact`
references in the job steps with their corresponding SHA-pinned identifiers so
the workflow is reproducible and not tied to a moving major tag.

In `@tools/auto-derive/derive.sh`:
- Around line 464-465: The default count parsing in derive.sh is masking bad
input by silently falling back to 1, so update the logic around
CUR_DEMOLIB_DEFAULT_COUNT to fail like the existing CUR_DEMOLIB_DEFAULT_IMAGE
check instead of rewriting malformed or missing values. Locate the
parsing/validation blocks in the startDemoWrapper handling and the related
section later in the script, and make both paths call fail with a clear message
when the default count cannot be parsed.
- Line 345: The section parser in derive.sh is only skipping comment lines that
match a very specific pattern, so other comment lines can still be treated as
TSV data. Update the comment-filtering logic around the line-processing block to
ignore all non-section comment lines, not just ones with two spaces after the
hash, so the section parsing/rendering in the script stays clean.
- Around line 167-183: The branch collection in derive.sh is too broad and
currently includes any non-master, non-unreleased branch, not just release
branches. Update the yq filtering used to populate branches (and the subsequent
loop that fills RT_BRANCH_VERSION_REF and RT_REL_BRANCHES) so it only includes
rel-* branches before synthesis, while keeping the existing
dev-pointer/version_ref resolution logic intact.

In `@tools/auto-derive/fixtures-and-tests/test.sh`:
- Around line 87-90: The diff output in the test.sh comparison loop uses a
predictable shared temp path, which can be interfered with by other local
processes. Update the diff capture logic in the loop over f (the same block that
runs diff -u and sed) to create a unique temporary file with mktemp, use that
path for redirecting diff output, and clean it up after printing so each run is
isolated.

In `@tools/auto-derive/README.md`:
- Line 15: The “G6 matrix” reference in the README is a dead empty anchor, so
update the markdown link in the Section ownership text to point to the real
section or URL, or remove the link markup entirely. Locate the reference by the
G6 matrix link text in the README and make sure the target is valid so
markdownlint no longer flags it.
- Around line 72-77: The bootstrap example in the README points to the wrong
fixture location, so update the copy/write command to use the same fixture path
convention referenced elsewhere in the docs and PR, namely the
fixtures-and-tests/fixtures/<name>/current and upstream/expected structure. Make
sure the example in the README uses the correct placeholder path consistently
across the cp and --upstream-base parts so the command works as documented.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c89ec858-9fbe-43f0-9998-8ca9c803705d

📥 Commits

Reviewing files that changed from the base of the PR and between 864d862 and 7935bc3.

📒 Files selected for processing (83)
  • .github/workflows/derive-ip-map.yml
  • tools/auto-derive/README.md
  • tools/auto-derive/derive.sh
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/current/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/current/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/expected/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/expected/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/.github/release-targets.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/ci/apache_82_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/ci/apache_83_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/ci/apache_84_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/ci/apache_85_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/rel-704/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/rel-800/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/rel-810/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/current/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/current/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/expected/fail.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/master/.github/release-targets.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/master/ci/apache_82_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/master/ci/apache_83_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/master/ci/apache_84_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/master/ci/apache_85_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/master/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/rel-704/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/rel-800/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/rel-810/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/rel-820/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/current/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/current/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/expected/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/expected/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/master/.github/release-targets.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/master/ci/apache_82_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/master/ci/apache_83_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/master/ci/apache_84_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/master/ci/apache_85_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/master/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/rel-704/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/rel-800/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/rel-810/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/current/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/current/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/expected/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/expected/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/master/.github/release-targets.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/master/ci/apache_82_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/master/ci/apache_83_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/master/ci/apache_84_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/master/ci/apache_85_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/master/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/rel-704/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/rel-800/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/rel-810/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/rel-820/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/current/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/current/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/expected/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/expected/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/master/.github/release-targets.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/master/ci/apache_82_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/master/ci/apache_83_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/master/ci/apache_84_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/master/ci/apache_85_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/master/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/rel-704/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/rel-800/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/rel-810/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/current/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/current/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/expected/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/expected/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/master/.github/release-targets.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/master/ci/apache_82_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/master/ci/apache_83_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/master/ci/apache_84_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/master/ci/apache_85_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/master/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/rel-704/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/rel-800/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/rel-810/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/test.sh

Comment thread .github/workflows/derive-ip-map.yml
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v7

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== workflow file ==\n'
nl -ba .github/workflows/derive-ip-map.yml | sed -n '1,220p'

printf '\n== action pinning policy search ==\n'
rg -n --hidden --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' \
  'pin(ning)?|commit sha|actions/checkout|actions/upload-artifact|zizmor|workflow policy|github actions' \
  .github README* docs* . 2>/dev/null | sed -n '1,220p'

Repository: openemr/demo_farm_openemr

Length of output: 224


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== workflow file with line numbers ==\n'
awk 'NR>=1 && NR<=220 { printf "%4d  %s\n", NR, $0 }' .github/workflows/derive-ip-map.yml

printf '\n== pinned action / policy search ==\n'
python3 - <<'PY'
import os
from pathlib import Path

targets = []
for p in [Path('.github'), Path('.')]:
    if p.exists():
        for cand in p.rglob('*'):
            if cand.is_file() and cand.suffix.lower() in {'.md', '.yml', '.yaml', '.txt'}:
                targets.append(cand)

patterns = ['pin', 'sha', 'zizmor', 'actions/checkout', 'actions/upload-artifact', 'workflow policy']
for path in sorted(set(targets)):
    try:
        text = path.read_text(errors='ignore')
    except Exception:
        continue
    hits = [pat for pat in patterns if pat.lower() in text.lower()]
    if hits:
        print(f"{path}: {', '.join(hits)}")
PY

printf '\n== direct search for action refs ==\n'
rg -n --hidden --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' \
  'actions/(checkout|upload-artifact)@|persist-credentials|zizmor|pin.*SHA|commit SHA' \
  .github README* docs* . 2>/dev/null | sed -n '1,220p'

Repository: openemr/demo_farm_openemr

Length of output: 50382


Pin the GitHub Actions to commit SHAs. actions/checkout@v7 and actions/upload-artifact@v7 are still mutable major tags at lines 33, 52, and 65. Replace each uses: reference with the reviewed SHA form.

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 32-33: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 33-33: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/derive-ip-map.yml at line 33, The workflow is using
mutable GitHub Actions tags instead of pinned SHAs, so update each `uses:` entry
in the derive-ip-map workflow to the reviewed commit SHA form. Replace the
`actions/checkout` and `actions/upload-artifact` references in the job steps
with their corresponding SHA-pinned identifiers so the workflow is reproducible
and not tied to a moving major tag.

Source: Linters/SAST tools

Comment thread tools/auto-derive/derive.sh
Comment thread tools/auto-derive/derive.sh Outdated
Comment thread tools/auto-derive/derive.sh
Comment thread tools/auto-derive/fixtures-and-tests/test.sh Outdated
Comment thread tools/auto-derive/README.md
Comment thread tools/auto-derive/README.md Outdated
…branch_tag

Apply user-driven refinements to the auto-derive bot:

* Master demos: anchor at master flex Dockerfile's ALPINE_VERSION,
  drop to next-lower Alpine if matrix doesn't support a desired PHP.
  Up-for-grabs flex uses both alpine + php from master flex Dockerfile.
* Production + release demos: col 3 sourced from release-targets.yml
  openemr_version_ref of the matching row. The "latest" row drives
  BOTH the production demos and a release demo. Each non-master row
  drives one release demo cluster with 3-tier sticky assignment
  (openemr_version_ref match > branch match > parked claim).
* Release demos: label is shape-derived ("Release Demo" if col 3
  looks like a tag, else "Development Demo"). Cluster identity is
  preserved across the dev->latest->old lifecycle.
* Descriptions regenerated from template using docker_tags' shortest
  numeric tag for version, fixing stale "8.1.0" to current "8.1.1".
* branch_tag column (col 13) deprecated: header is now
  "branch_tag(not used)", all values are 0. demo_build.sh switches
  to single shallow clone via --branch (works for both branches and
  tags). IpMapBumper.php identifies tag rows via regex on col 3.

Live dry-run against current state: six=v7_0_4 Release Demo,
three=v8_0_0_3 Release Demo, eight=rel-810 Development Demo (version
auto-corrected 8.1.0->8.1.1 from docker_tags). All clusters
identity-preserved; no parked churn.

6/6 fixture tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@demo_build.sh`:
- Around line 274-276: The git clone invocation in demo_build.sh is using the
wrong argument order, which can break shallow checkout for branches and tags.
Update the clone command so the option flags are passed before the repository
URL, and keep the existing branch/tag handling logic intact. Use the surrounding
git clone block and the branch/tag checkout path as the place to fix this.

In `@tools/auto-derive/derive.sh`:
- Around line 1402-1403: The master flex Dockerfile tuple is derived without
verifying it exists in the workflow matrix, so `MASTER_FLEX_ALPINE` and
`MASTER_FLEX_PHP` can drift from what the docker-build workflows actually build.
Add a validation step near `fetch_flex_matrix` / `fetch_flex_dockerfile_args` in
`derive.sh` that checks the exact master tuple against the parsed
`.github/workflows/docker-build-*.yml` matrix, and fail fast with a clear error
if the pair is missing.

In `@tools/auto-derive/README.md`:
- Line 6: The README’s background reference points to the repository root
instead of the actual G6 design document, so update the link in the opening
description to target the specific release-mechanism-gaps doc or remove the
hyperlink if no stable destination exists. Use the existing “See the G6 design”
text in the README and adjust that markdown link so readers land on the
referenced background doc rather than the repo root.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 62a9939b-9efb-4752-a748-116f05486237

📥 Commits

Reviewing files that changed from the base of the PR and between 7935bc3 and 183de9d.

📒 Files selected for processing (42)
  • demo_build.sh
  • ip_map_branch.txt
  • tools/auto-derive/README.md
  • tools/auto-derive/derive.sh
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/current/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/expected/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/expected/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/.github/workflows/docker-build-322.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/.github/workflows/docker-build-323.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/.github/workflows/docker-build-edge.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/.github/workflows/docker-build-flex-core.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/docker/flex/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/current/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/master/.github/workflows/docker-build-322.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/master/.github/workflows/docker-build-323.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/master/docker/flex/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/current/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/expected/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/expected/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/master/.github/workflows/docker-build-322.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/master/.github/workflows/docker-build-323.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/master/docker/flex/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/current/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/expected/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/expected/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/master/.github/workflows/docker-build-322.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/master/.github/workflows/docker-build-323.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/master/docker/flex/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/current/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/expected/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/expected/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/master/.github/workflows/docker-build-322.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/master/.github/workflows/docker-build-323.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/master/docker/flex/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/current/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/expected/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/expected/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/master/.github/workflows/docker-build-322.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/master/.github/workflows/docker-build-323.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/master/docker/flex/Dockerfile
  • tools/release/src/IpMapBumper.php
  • tools/release/tests/IpMapBumperTest.php
✅ Files skipped from review due to trivial changes (16)
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/master/.github/workflows/docker-build-323.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/.github/workflows/docker-build-323.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/.github/workflows/docker-build-322.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/master/.github/workflows/docker-build-323.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/current/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/current/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/.github/workflows/docker-build-flex-core.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/expected/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/current/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/expected/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/expected/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/expected/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/current/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/current/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/expected/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/current/ip_map_branch.txt
🚧 Files skipped from review as they are similar to previous changes (5)
  • tools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/expected/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/expected/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/expected/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/expected/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/basic/expected/docker/scripts/demoLibrary.source

Comment thread demo_build.sh
Comment thread tools/auto-derive/derive.sh
Comment thread tools/auto-derive/README.md Outdated
@bradymiller
bradymiller marked this pull request as draft June 27, 2026 11:20
bradymiller and others added 4 commits June 27, 2026 11:28
* Workflow hardening: pin actions/checkout + actions/upload-artifact
  to commit SHAs; set persist-credentials: false on both checkouts.
* Filter release-targets branches to ^rel-[0-9]+$ before synthesis;
  warn-skip anything else.
* Fix comment skip pattern (was matching only two-space comments).
* Fail loud when demoLibrary.source default count cannot be parsed
  (was silently falling back to 1).
* Use mktemp for diff output in fixture test runner (was /tmp/diff.out).
* Add validate_master_flex_pair() to fail fast if master flex
  Dockerfile (ALPINE, PHP) isn't built by docker-build-*.yml matrix.
* Fix bootstrap fixture path in README (fixtures-and-tests/fixtures/...).
* Fix background link in README to point at the actual migration doc.
* Move --branch/--depth before URL in demo_build.sh git clone
  (defensive; works either way but options-before is more portable).

6/6 fixture tests pass; bash + yaml syntax clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Restore major-tag refs (actions/checkout@v7, actions/upload-artifact@v7).
SHA-pinning is not the openemr-ecosystem project standard yet.
Keep persist-credentials: false on checkouts -- that hardening stays.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Exercise the bot's dynamic handling of supported-PHP-set changes
(openemr adding/removing ci/apache_NN_*/ directories). The bot's
sticky-mapping logic already handled this -- master clusters claim
from parked on a new PHP, and retire to parked when a PHP drops out
-- but the existing 6 fixtures all had the same PHP set so the
behavior wasn't fixture-verified.

* php-added: openemr adds PHP 8.6 (apache_86_118/). Bot claims first
  parked cluster (nine) for the new PHP. docker-build-323.yml's
  matrix extended to include 8.6 so the flex image resolves.
* php-retired: openemr removes PHP 8.2 (apache_82_118/ deleted).
  Bot releases cluster two to parked (description -> [parked],
  branch -> master). demoLibrary case-arm preserved (only dropped
  when cluster is fully removed, not parked).

Test suite now 8/8 PASS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Enforce the invariant: every parked cluster row has col 3 = master
and demoLibrary image = master flex Dockerfile's tag (same as
up-for-grabs derivation). Applies to both:

* Pre-existing parked: drift-correct branch/description/image
  (was preserving CUR_ROW verbatim, allowing drift to linger).
* Retired-to-parked: also reset image (previously kept the old
  master-demo image, e.g., flex-3.23-php-8.2 for retired PHP 8.2).

Fixture updates: php-retired's cluster two image flips
flex-3.23-php-8.2 -> flex-3.23-php-8.5; retired-rel-branch's
cluster six image flips flex-3.22-php-8.4 -> flex-3.23-php-8.5.
8/8 fixtures pass. Canonical demo_farm state already conformant
(no live diff).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bradymiller
bradymiller marked this pull request as ready for review June 28, 2026 04:19

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@tools/auto-derive/fixtures-and-tests/fixtures/php-added/current/ip_map_branch.txt`:
- Around line 20-21: The php-added current fixture already includes the PHP 8.5
lane, so this test no longer exercises the reconciler’s new-cluster path. Update
the current fixture inputs for the ip map branch scenario to the pre-8.5 state
by removing the already-added `eleven` and `eleven_a` rows, and keep the added
lane represented only in the derived output for this case. Also make sure the
paired `demoLibrary.source` current fixture matches that pre-add state so the
reconciliation is not a no-op.

In
`@tools/auto-derive/fixtures-and-tests/fixtures/php-added/expected/docker/scripts/demoLibrary.source`:
- Around line 131-147: The startup flow in startDemo() is cloning
demo_farm_openemr from live HEAD, and restartSubdemo() is pulling from origin
master, which can drift from the checked-out fixture state. Update the docker
run bootstrap command and the restart path to use the already-derived repo
contents or a pinned ref/commit instead of fetching latest remote changes, so
the behavior matches ip_map_branch.txt and demoLibrary.source. Use the startDemo
and restartSubdemo symbols to locate the affected repo update logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 13eb6e78-9eb1-4f4b-9eb5-eaf194910c9a

📥 Commits

Reviewing files that changed from the base of the PR and between 183de9d and 2da0708.

📒 Files selected for processing (42)
  • .github/workflows/derive-ip-map.yml
  • demo_build.sh
  • tools/auto-derive/README.md
  • tools/auto-derive/derive.sh
  • tools/auto-derive/fixtures-and-tests/fixtures/php-added/current/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/php-added/current/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/php-added/expected/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/php-added/expected/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/.github/release-targets.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/.github/workflows/docker-build-322.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/.github/workflows/docker-build-323.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/.github/workflows/docker-build-edge.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/.github/workflows/docker-build-flex-core.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/ci/apache_82_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/ci/apache_83_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/ci/apache_84_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/ci/apache_85_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/ci/apache_86_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/docker/flex/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/rel-704/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/rel-800/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/rel-810/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/php-retired/current/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/php-retired/current/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/php-retired/expected/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/fixtures/php-retired/expected/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/master/.github/release-targets.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/master/.github/workflows/docker-build-322.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/master/.github/workflows/docker-build-323.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/master/.github/workflows/docker-build-edge.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/master/.github/workflows/docker-build-flex-core.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/master/ci/apache_83_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/master/ci/apache_84_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/master/ci/apache_85_118/.gitkeep
  • tools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/master/docker/flex/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/master/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/rel-704/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/rel-800/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/rel-810/docker/release/Dockerfile
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/expected/docker/scripts/demoLibrary.source
  • tools/auto-derive/fixtures-and-tests/test.sh
✅ Files skipped from review due to trivial changes (5)
  • tools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/.github/workflows/docker-build-323.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/php-added/expected/ip_map_branch.txt
  • tools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/master/.github/release-targets.yml
  • tools/auto-derive/fixtures-and-tests/fixtures/php-retired/expected/ip_map_branch.txt
  • tools/auto-derive/README.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • .github/workflows/derive-ip-map.yml
  • tools/auto-derive/fixtures-and-tests/test.sh
  • demo_build.sh
  • tools/auto-derive/derive.sh
  • tools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/expected/docker/scripts/demoLibrary.source

Comment on lines +20 to +21
eleven https://github.com/openemr/openemr.git master 0 1 0 0 0 0 1 eleven.openemr.io hey 0 0 2 0 0 Public OpenEMR Development Demo on Alpine 3.23 (with PHP 8.5)
eleven_a https://github.com/openemr/openemr.git master 0 1 0 0 demo_5_0_0_5.sql 0 1 eleven.openemr.io/a hey 0 5.0.0 2 0 0 Public OpenEMR Development Demo With Demo Data On Alpine 3.23 (with PHP 8.5)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

The php-added current fixture already contains the added PHP lane.

This scenario is supposed to prove the reconciler allocates a new sticky master cluster when PHP 8.5 appears upstream, but eleven / eleven_a are already present here. That turns the fixture into a no-op reconciliation case, and current/docker/scripts/demoLibrary.source already mirrors the same pre-added lane. Roll the current inputs back to the pre-8.5 state and keep eleven only in the derived output for this scenario.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@tools/auto-derive/fixtures-and-tests/fixtures/php-added/current/ip_map_branch.txt`
around lines 20 - 21, The php-added current fixture already includes the PHP 8.5
lane, so this test no longer exercises the reconciler’s new-cluster path. Update
the current fixture inputs for the ip map branch scenario to the pre-8.5 state
by removing the already-added `eleven` and `eleven_a` rows, and keep the added
lane represented only in the derived output for this case. Also make sure the
paired `demoLibrary.source` current fixture matches that pre-add state so the
reconciliation is not a no-op.

Comment on lines +131 to +147
docker run --detach --name ${1}-openemr \
--env "DOCKERDEMO=${1}" \
--env "DOCKERMYSQLHOST=${2}" \
--env "DOCKERNUMBERDEMOS=${4}" \
--env "OPENEMR_DOCKER_ENV_TAG=demo-farm" \
--env "THROTTLE_DOWN_WAIT_MILLISECONDS=700" \
--env "THROTTLE_DOWN_DIE_MILLISECONDS=10000" \
-v ~/capsules:/home/openemr/capsules:ro \
-v ~/translations_development_openemr:/home/openemr/git/translations_development_openemr:ro \
-v ~/demo_farm_openemr/docker/stunnel/stunnel.conf:/etc/stunnel/stunnel.conf:ro \
-v ~/demo_farm_openemr/docker/postfix/alpine/main.cf:/etc/postfix/main.cf:ro \
-v ~/cred/sasl_passwd:/etc/postfix/sasl_passwd:ro \
-v ~/cred/sasl_passwd.db:/etc/postfix/sasl_passwd.db:ro \
-v ~/cred/github-key:/home/openemr/github-key:ro \
--net mynet \
${3} \
bash -c "mkdir -p /home/openemr/git && cd /home/openemr/git && git clone https://github.com/openemr/demo_farm_openemr.git && bash /home/openemr/git/demo_farm_openemr/demo_build.sh"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Pin the demo_farm_openemr checkout instead of pulling live HEAD.

startDemo() clones the repo at container start, and restartSubdemo() later does git pull origin master. That lets the running demo drift independently of the derived ip_map_branch.txt / demoLibrary.source state and makes GitHub availability part of the startup path. Please bootstrap from the checked-out repo state or a pinned ref/commit instead.

Also applies to: 165-169

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@tools/auto-derive/fixtures-and-tests/fixtures/php-added/expected/docker/scripts/demoLibrary.source`
around lines 131 - 147, The startup flow in startDemo() is cloning
demo_farm_openemr from live HEAD, and restartSubdemo() is pulling from origin
master, which can drift from the checked-out fixture state. Update the docker
run bootstrap command and the restart path to use the already-derived repo
contents or a pinned ref/commit instead of fetching latest remote changes, so
the behavior matches ip_map_branch.txt and demoLibrary.source. Use the startDemo
and restartSubdemo symbols to locate the affected repo update logic.

@bradymiller
bradymiller merged commit e421a2e into openemr:master Jun 28, 2026
3 checks passed
bradymiller added a commit that referenced this pull request Jun 28, 2026
…138)

* feat(auto-derive): open reconciliation PR on diff (write + PR mode)

Replaces dry-run-only with active reconciliation: daily 07:00 UTC run
executes derive.sh --write, and on any diff in ip_map_branch.txt or
docker/scripts/demoLibrary.source vs master, force-pushes the stable
branch auto-derive/reconciliation and opens (or updates) a PR. On
no-diff with an existing open reconciliation PR, the PR is closed and
the remote branch is deleted -- upstream drift reverted.

Diff detection is scoped to those two output files so unrelated edits
to tools/auto-derive/ or other paths can't trigger a bot PR for changes
the bot didn't make.

workflow_dispatch defaults to reconcile; choose `dry-run` from the input
dropdown for a manual preview with artifact upload and no side effects.
Fixture test suite job is unchanged from PR #135.

Caveat: PRs are created via GITHUB_TOKEN, so downstream workflows
(rabbit, CI) don't auto-run on bot PRs -- the in-workflow fixture suite
is the authoritative algorithm validator. Empty commit forces CI if
needed.

* fix(auto-derive workflow): gate reconcile on test, pin to master, fix --stat, add PR trigger

- reconcile job now needs: test and requires test success, so a broken
  algorithm can't force-push or open a PR (rabbit #3487382063).
- reconcile checkout pinned to ref: master so workflow_dispatch from a
  topic branch can't carry unrelated commits into the bot branch
  (rabbit #3487382069).
- Move --stat before -- in PR-body git diff so it's parsed as a flag
  rather than a pathspec; the stat output was previously the full
  patch (rabbit #3487382072).
- Add pull_request: trigger with path filter so PRs touching bot files
  re-run the fixture suite (restores PR #1's CI coverage). Reconcile
  guard restructured to positive-list schedule + dispatch-reconcile
  so PR events can't accidentally satisfy it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bradymiller added a commit that referenced this pull request Jun 28, 2026
… PHP toolchain (#141)

The auto-derive bot (PRs #135, #138) is now the canonical writer of
ip_map_branch.txt + docker/scripts/demoLibrary.source. The legacy
PHP-based per-tag bumper is fully redundant; remove it.

Changes:
- Delete .github/workflows/bump-tag.yml (repository_dispatch openemr-tag
  consumer).
- Delete tools/release/ (composer/PHPUnit/IpMapBumper.php toolchain).
- Add `repository_dispatch types=release-targets-changed` listener to
  derive-ip-map.yml so the bot reconciles immediately when upstream
  pushes release-targets.yml instead of waiting for the next 07:00 UTC
  tick.
- Update reconcile job's if-guard to allow repository_dispatch while
  preserving the existing schedule + workflow_dispatch-reconcile paths
  and the repo-owner/test-success gates. pull_request is still excluded
  from reconcile by the explicit allowlist.
- README + workflow header comments updated to reflect retired-state.

Cross-repo follow-up (tracked separately): openemr/openemr companion PR
to (a) dispatch `release-targets-changed` from a workflow that watches
.github/release-targets.yml on master and (b) stop dispatching the now
unlistened-for `openemr-tag` event from release-prep.yml:328-337.
During the transition the openemr-tag dispatches land here with no
listener — harmless no-op.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bradymiller added a commit that referenced this pull request Jun 28, 2026
… link (#144)

* docs(auto-derive): document cross-repo dispatch trigger + fix dead G6 link

Two updates:

* Fix the dead `[G6 matrix](#)` empty-anchor link (rabbit had
  flagged in #135 review; the original fix targeted README:6 not
  README:19; replaced with prose since the matrix is in the design
  doc linked above, not anywhere in this repo).
* Rewrite the "Daily reconcile + auto-PR" section as "Triggers +
  reconcile + auto-PR" — enumerates all 3 fire triggers (daily cron,
  release-targets-changed repository_dispatch added by openemr#12657,
  manual workflow_dispatch). Existing reconciliation behavior moved
  under the "in all three trigger paths" framing. Trimmed the
  redundant workflow_dispatch paragraph that now duplicates the
  trigger enumeration.

* docs(auto-derive): scope reconcile-job description (dry-run is a separate job)
bradymiller added a commit that referenced this pull request Jul 1, 2026
The README hadn't been touched to reflect several years of demo_farm
work. Rewritten to match current reality:

Workflow badges added at top:
  - ShellCheck (post-cleanup: rc has zero disable= lines)
  - build-tests (dry-run)  -- per-PR fixture suite from #147
  - build-tests (live integration) -- nightly + per-PR matrix from #148
  - Derive ip_map (reconcile) -- auto-derive bot from #135

ip_map_branch.txt column docs:
  - Converted from bullet list to table for readability.
  - Marked `branch_tag` (col 13) as deprecated -- matches the header
    annotation, the demo_build.sh comment ("col 13 (branch_tag) is
    deprecated; ... `git clone --branch` works for both"), and the
    fact that every production row now carries `0` there.
  - `fun_stuff`: documented actual code behavior. `1` = 36-theme
    picker, `2` = 6-theme picker, any other non-zero value falls
    through to the 36-theme picker via the `else` branch. Previously
    the README said "1 for <6.0, 2 for 6.0+" which reads like a
    version gate but is really a picker-selection.
  - `pass_reset`: documented that set_pass.php actually supports
    modes 1-4 (3/4 are 6.0.0+ variants), production uses 4, AND
    the mechanism is currently disabled pending
    openemr/openemr#5991. Column values are kept aligned with
    intended re-enable.
  - `capsule`: added the path-traversal guard mention (added in
    #145) as a footnote-style note.

New sections:
  - "Regression tests" -- describes the dry-run + live-integration
    suites from #147/#148 and the auto-derive fixtures.
  - "Auto-derive bot" -- describes the daily reconcile flow from
    #135 including the release-targets-changed cross-repo dispatch.
  - "ShellCheck" -- notes the ratchet cleanup completion (PRs #149
    through #157) and the current-clean state.

Kept intact:
  - "UP FOR GRABS" onboarding steps (5-step process; still current).
  - "How to restart the demo farm after a host reboot" section
    (order + scripts still current). Trimmed the "watch this
    YouTube video 15 times" line since it dated the page.

Intro paragraph rewritten to name the actual URL structure
(demo.openemr.io etc.) and describe what a cluster/subdemo is,
rather than the previous single-line description.

License section preserved.

Assisted-by: Claude Code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant