feat(auto-derive): scaffold ip_map + demoLibrary derivation bot (dry-run)#135
Conversation
… 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
📝 WalkthroughWalkthroughAdds an auto-derive workflow, a Bash generator that rewrites ChangesAuto-derive workflow and release mapping
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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.mdmarkdownlint-cli2 wrapper config was not available before execution Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (83)
.github/workflows/derive-ip-map.ymltools/auto-derive/README.mdtools/auto-derive/derive.shtools/auto-derive/fixtures-and-tests/fixtures/basic/current/docker/scripts/demoLibrary.sourcetools/auto-derive/fixtures-and-tests/fixtures/basic/current/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/basic/expected/docker/scripts/demoLibrary.sourcetools/auto-derive/fixtures-and-tests/fixtures/basic/expected/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/.github/release-targets.ymltools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/ci/apache_82_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/ci/apache_83_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/ci/apache_84_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/ci/apache_85_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/rel-704/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/rel-800/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/rel-810/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/current/docker/scripts/demoLibrary.sourcetools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/current/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/expected/fail.txttools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/master/.github/release-targets.ymltools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/master/ci/apache_82_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/master/ci/apache_83_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/master/ci/apache_84_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/master/ci/apache_85_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/master/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/rel-704/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/rel-800/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/rel-810/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/rel-820/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/current/docker/scripts/demoLibrary.sourcetools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/current/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/expected/docker/scripts/demoLibrary.sourcetools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/expected/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/master/.github/release-targets.ymltools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/master/ci/apache_82_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/master/ci/apache_83_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/master/ci/apache_84_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/master/ci/apache_85_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/master/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/rel-704/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/rel-800/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/rel-810/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/current/docker/scripts/demoLibrary.sourcetools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/current/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/expected/docker/scripts/demoLibrary.sourcetools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/expected/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/master/.github/release-targets.ymltools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/master/ci/apache_82_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/master/ci/apache_83_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/master/ci/apache_84_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/master/ci/apache_85_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/master/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/rel-704/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/rel-800/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/rel-810/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/rel-820/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/current/docker/scripts/demoLibrary.sourcetools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/current/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/expected/docker/scripts/demoLibrary.sourcetools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/expected/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/master/.github/release-targets.ymltools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/master/ci/apache_82_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/master/ci/apache_83_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/master/ci/apache_84_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/master/ci/apache_85_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/master/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/rel-704/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/rel-800/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/rel-810/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/current/docker/scripts/demoLibrary.sourcetools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/current/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/expected/docker/scripts/demoLibrary.sourcetools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/expected/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/master/.github/release-targets.ymltools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/master/ci/apache_82_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/master/ci/apache_83_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/master/ci/apache_84_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/master/ci/apache_85_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/master/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/rel-704/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/rel-800/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/rel-810/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/test.sh
| runs-on: ubuntu-24.04 | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v7 |
There was a problem hiding this comment.
🔒 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
…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>
There was a problem hiding this comment.
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
📒 Files selected for processing (42)
demo_build.ship_map_branch.txttools/auto-derive/README.mdtools/auto-derive/derive.shtools/auto-derive/fixtures-and-tests/fixtures/basic/current/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/basic/expected/docker/scripts/demoLibrary.sourcetools/auto-derive/fixtures-and-tests/fixtures/basic/expected/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/.github/workflows/docker-build-322.ymltools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/.github/workflows/docker-build-323.ymltools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/.github/workflows/docker-build-edge.ymltools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/.github/workflows/docker-build-flex-core.ymltools/auto-derive/fixtures-and-tests/fixtures/basic/upstream/master/docker/flex/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/current/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/master/.github/workflows/docker-build-322.ymltools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/master/.github/workflows/docker-build-323.ymltools/auto-derive/fixtures-and-tests/fixtures/empty-parked-fail/upstream/master/docker/flex/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/current/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/expected/docker/scripts/demoLibrary.sourcetools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/expected/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/master/.github/workflows/docker-build-322.ymltools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/master/.github/workflows/docker-build-323.ymltools/auto-derive/fixtures-and-tests/fixtures/multirow-rel810/upstream/master/docker/flex/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/current/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/expected/docker/scripts/demoLibrary.sourcetools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/expected/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/master/.github/workflows/docker-build-322.ymltools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/master/.github/workflows/docker-build-323.ymltools/auto-derive/fixtures-and-tests/fixtures/new-rel-branch/upstream/master/docker/flex/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/current/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/expected/docker/scripts/demoLibrary.sourcetools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/expected/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/master/.github/workflows/docker-build-322.ymltools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/master/.github/workflows/docker-build-323.ymltools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/upstream/master/docker/flex/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/current/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/expected/docker/scripts/demoLibrary.sourcetools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/expected/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/master/.github/workflows/docker-build-322.ymltools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/master/.github/workflows/docker-build-323.ymltools/auto-derive/fixtures-and-tests/fixtures/unreleased-skip/upstream/master/docker/flex/Dockerfiletools/release/src/IpMapBumper.phptools/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
* 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>
There was a problem hiding this comment.
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
📒 Files selected for processing (42)
.github/workflows/derive-ip-map.ymldemo_build.shtools/auto-derive/README.mdtools/auto-derive/derive.shtools/auto-derive/fixtures-and-tests/fixtures/php-added/current/docker/scripts/demoLibrary.sourcetools/auto-derive/fixtures-and-tests/fixtures/php-added/current/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/php-added/expected/docker/scripts/demoLibrary.sourcetools/auto-derive/fixtures-and-tests/fixtures/php-added/expected/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/.github/release-targets.ymltools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/.github/workflows/docker-build-322.ymltools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/.github/workflows/docker-build-323.ymltools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/.github/workflows/docker-build-edge.ymltools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/.github/workflows/docker-build-flex-core.ymltools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/ci/apache_82_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/ci/apache_83_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/ci/apache_84_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/ci/apache_85_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/ci/apache_86_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/docker/flex/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/master/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/rel-704/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/rel-800/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/php-added/upstream/rel-810/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/php-retired/current/docker/scripts/demoLibrary.sourcetools/auto-derive/fixtures-and-tests/fixtures/php-retired/current/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/php-retired/expected/docker/scripts/demoLibrary.sourcetools/auto-derive/fixtures-and-tests/fixtures/php-retired/expected/ip_map_branch.txttools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/master/.github/release-targets.ymltools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/master/.github/workflows/docker-build-322.ymltools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/master/.github/workflows/docker-build-323.ymltools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/master/.github/workflows/docker-build-edge.ymltools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/master/.github/workflows/docker-build-flex-core.ymltools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/master/ci/apache_83_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/master/ci/apache_84_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/master/ci/apache_85_118/.gitkeeptools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/master/docker/flex/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/master/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/rel-704/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/rel-800/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/php-retired/upstream/rel-810/docker/release/Dockerfiletools/auto-derive/fixtures-and-tests/fixtures/retired-rel-branch/expected/docker/scripts/demoLibrary.sourcetools/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
| 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) |
There was a problem hiding this comment.
🎯 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.
| 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" |
There was a problem hiding this comment.
🩺 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.
…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>
… 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>
… 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)
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
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:
bump-tag.ymlortools/release/PHP toolchain (PR Add development demos for Alpine 3.5 and 3.6 #3 scope)Goal
Replace manual maintenance of
ip_map_branch.txt+demoLibrary.sourcestartDemoWrappercase statement with a dailyreconciliation bot that derives the full state from upstream
openemr/openemrmaster +release-targets.yml+ per-rel-branchDockerfile 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.,
eight→eight.openemr.io) referencedfrom external surfaces (wiki, social, mail). Reassigning a cluster's
meaning would break those external references. The bot reads the
CURRENT
ip_map_branch.txtas state, applies a reconciliation diffagainst the desired state computed from upstream, and writes the new
file preserving cluster→subdomain stability.
Section ownership matrix
branch)five+ aliases)openemr_version_ref(typically a tag)four+ aliases)masterALPINE_VERSION, drops to lower Alpine if matrix lacks the cluster's PHPopenemr_version_ref(tag OR branch)col 3 derivation rules
openemr_version_reffrom 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 withlatestindocker_tagsadditionally 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 sameopenemr_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):
openemr_version_ref→ reusebranchfield (single-row lifecycle:rel-810→v8_1_0after release) → reuse, col 3 rolls forwardWhen 8.1.0 ships from rel-810: cluster
eightrolls forward (col 3rel-810→v8_1_0, labelDevelopment Demo→Release 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;eightstays on thev8_1_0row.Description regeneration
Always regenerated from template (no preservation), so stale versions auto-correct:
[Alternate/Another Alternate] Public OpenEMR <ver> Production DemoPublic OpenEMR <ver> Release Demo on Alpine <A> (with PHP <P>)Public OpenEMR <ver> Development Demo on Alpine <A> (with PHP <P>)_aaliases insertWith Demo Data On<ver>= shortest numeric tag from that row'sdocker_tags(e.g.,8.1.1,next→8.1.1)Live dry-run example: cluster
eight's description auto-corrected from stale8.1.0to current8.1.1(sourced from rel-810'sdocker_tags: 8.1.1,next).branch_tagcolumn (col 13) deprecatedThe col-13 enum (
branch/tag) that tolddemo_build.shhow to clone is now unnecessary —git clone --branch <ref> --depth 1works for both branches and tags. Header is nowbranch_tag(not used); all values are0(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 1works againstv8_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
latestholder inrelease-targets.ymlci/apache_*directory listing empty on upstream masterdocker_tagsfield contains no numeric version tagFiles added
.github/workflows/derive-ip-map.yml(78 lines): PR + schedule(
0 7 * * *UTC, 1h after openemr's docker-release-orchestrator)workflow_dispatchtriggers; runsderive.shin dry-run; emitsdiff 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 + usagetools/auto-derive/fixtures-and-tests/test.sh: fixture runner; diffs actual vs expected per scenariobasic— canonical no-diff round-tripunreleased-skip— release-targets has unreleased rownew-rel-branch— rel-820 added; cluster taken from parkedretired-rel-branch— rel-704 dropped; cluster parksmultirow-rel810— two rows for rel-810 (latest-pinned + dev-pinned); each gets its own clusterempty-parked-fail— new branch needed but parked empty; failsEach fixture's
upstream/master/now includesdocker/flex/Dockerfile(for the master-demos Alpine anchor + up-for-grabs derivation) and.github/workflows/docker-build-NN.ymlstubs (for the flex Alpine→PHP matrix).Verified locally
six/_a: col 3rel-704→v7_0_4(tag-pinned now per release-targets), label flips Development → Release Demothree/_a: col 3rel-800→v8_0_0_3, label flips Development → Release Demoeight/_a: col 3 staysrel-810, description version auto-corrects 8.1.0 → 8.1.1[8.2, 8.3, 8.4, 8.5]fromci/apache_*twocorrectly resolves to Alpine 3.22 (not 3.23) because PHP 8.2 isn't in 3.23's matrix perdocker-build-323.ymlTest plan
derive-ip-map.ymlrun renders the dry-run output instep summary + uploads artifact
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