Skip to content

fix(sync-dirs): self-heal libs.versions.toml when upstream adds build-logic aliases#163

Merged
therajanmaurya merged 2 commits into
openMF:devfrom
therajanmaurya:feat/verify-libs-resolution
May 22, 2026
Merged

fix(sync-dirs): self-heal libs.versions.toml when upstream adds build-logic aliases#163
therajanmaurya merged 2 commits into
openMF:devfrom
therajanmaurya:feat/verify-libs-resolution

Conversation

@therajanmaurya

@therajanmaurya therajanmaurya commented May 22, 2026

Copy link
Copy Markdown
Member

Summary

Makes sync-dirs.yaml + sync-dirs.sh self-healing: when upstream adds new libs.<X> accessors to build-logic/ along with their matching catalog entries, sync-dirs now also pulls the matching gradle/libs.versions.toml aliases (and any version-keys they reference) into the consumer fork — all in the same sync PR.

The bug class this fixes

build-logic/ is in sync-dirs's default DIRS=(...) list — every consumer fork pulls the latest template build-logic on the Monday cron (or workflow_dispatch).

But gradle/libs.versions.toml is intentionally not in the sync list: it carries project-local data (package namespaces, version overrides, project-specific lib pins). When upstream adds new libs.<X> accessors in build-logic with their matching alias entries in the catalog (e.g. PR #160 added libs.plugins.kover.convention), sync-dirs brings the build-logic over but leaves the consumer's catalog behind. Result: silent broken state — gradle won't complain until the alias is actually evaluated by a downstream build, hours to weeks after the bad sync ran.

This has been hand-fixed at least 3 times in 4 months on the field-officer fork:

What this PR ships

Both sync-dirs entry points get the same heal logic inlined directly — no new workflow files, no new scripts.

.github/workflows/sync-dirs.yaml

Adds a new step "Heal libs.versions.toml" between the existing "Sync directories and files" and "Clean up temporary branch" steps. The step:

  1. Greps every libs.<X> reference inside the freshly-synced build-logic/
  2. Normalises each to its TOML alias key per Gradle's version-catalog accessor convention (dots → hyphens), with namespace handling:
    • libs.<a>[libraries] key <a>
    • libs.plugins.<a>[plugins] key <a>
    • libs.versions.<a>[versions] key <a>
    • Skips method-call accessors (libs.findLibrary(\"…\"), etc.) and the catalog filename libs.versions.toml
  3. For each unresolved alias, pulls the matching entry from upstream's gradle/libs.versions.toml (accessed via $TEMP_BRANCH) and inserts it into the appropriate section of the consumer's catalog
  4. If an alias version.refs an undeclared version key, that version key is also pulled and inserted into [versions] before the alias
  5. The peter-evans/create-pull-request action auto-stages the modified catalog → it lands in the same sync PR

sync-dirs.sh

Adds the same heal logic as a heal_libs_versions_toml() function invoked between the sync loop and the temp-branch cleanup. Local CLI use of sync-dirs (developer running bash sync-dirs.sh manually) gets the same self-healing.

The follow-up git add is widened to include gradle/libs.versions.toml so the heal's additions ride into the commit.

Verified end-to-end

Tested against field-officer's pre-fix state (catalog reverted to commit 408d9cba — the broken state from ~24h ago, before manual catch-up):

═══ Pre-heal: missing aliases ═══
  ❌ missing: androidx-room-gradle-plugin ([libraries])
  ❌ missing: kmp-product-flavors-plugin ([libraries])
  ❌ missing: kover-gradlePlugin ([libraries])
  ❌ missing: kover-convention ([plugins])
  ❌ missing: spotless-gradle ([libraries])

═══ Running heal logic ═══
  ✅ Added 'androidx-room-gradle-plugin' to [libraries]
  ✅ Added version 'kmpProductFlavors' to [versions]
  ✅ Added 'kmp-product-flavors-plugin' to [libraries]
  ✅ Added version 'kover' to [versions]
  ✅ Added 'kover-gradlePlugin' to [libraries]
  ✅ Added 'kover-convention' to [plugins]
  ✅ Added 'spotless-gradle' to [libraries]

  Healed: 7 entries

═══ Verify ALL aliases now resolve ═══
  ✅ All references resolve!

Net diff

File Delta
.github/workflows/sync-dirs.yaml +121 lines (new heal step)
sync-dirs.sh +127 lines (heal function + invocation + widened git add)

No new files. Workflow becomes self-healing — discovery + remediation in one atomic sync PR.

Test plan

  • Local: heal logic correctly identifies + pulls 5 missing aliases + 2 referenced version keys against field-officer's pre-fix state
  • Idempotent: re-running heal on already-synced state produces no changes ("already in sync")
  • Handles namespace prefixes: [libraries] / [plugins] / [versions]
  • Skips method-call accessors (findLibrary / findVersion / findBundle / findPlugin)
  • Skips libs.versions.toml catalog filename string-literal
  • CI: verify sync-dirs workflow run succeeds on this template (which is already self-consistent) — should output "✅ libs.versions.toml already in sync with build-logic — no heal needed"
  • Future: when this merges + sync-dirs runs on field-officer's fork against a hypothetical future template change that adds a new build-logic alias, the consumer's catalog auto-heals in the same sync PR

Summary by CodeRabbit

  • Chores
    • Added automatic detection and repair of missing Gradle version-catalog entries in the build synchronization process. The system now identifies missing catalog references and automatically inserts them from upstream sources, reducing manual configuration errors during build-logic synchronization.

Review Change Stack

Every `libs.<name>` accessor inside `build-logic/` must resolve to an alias
declared in `gradle/libs.versions.toml`. When `build-logic/` is auto-synced into
a consumer fork via the `sync-dirs.yaml` workflow but the matching version-catalog
entries are NOT (because `gradle/libs.versions.toml` is intentionally project-
local — it carries the consumer's package namespace, version overrides, etc.),
the synced build-logic ends up referencing aliases that don't exist in the
consumer's catalog. Result: silent broken state that only surfaces on the first
build attempt against the synced state, hours to weeks after the bad sync ran.

This change adds a fast PR-time gate that catches the gap immediately. Two files:

- `scripts/verify_libs_resolved.sh` — greps every `libs.<name>` reference inside
  `build-logic/`, normalises each to its expected TOML alias key per Gradle's
  version-catalog accessor convention (dots → hyphens), and verifies the key is
  declared in `gradle/libs.versions.toml`. Handles namespace prefixes correctly:
    libs.<a>          → [libraries] key `<a>` (dots → hyphens)
    libs.plugins.<a>  → [plugins]   key `<a>`
    libs.versions.<a> → [versions]  key `<a>`
  Skips method-call accessors that take string args, not property paths:
    libs.findLibrary("…"), libs.findVersion("…"), libs.findBundle("…"),
    libs.findPlugin("…").
  Also whitelists `libs.versions.toml` as a string-literal reference to the
  catalog file name itself.

- `.github/workflows/verify-libs-resolved.yml` — invokes the script on every PR
  that touches `build-logic/**`, `gradle/libs.versions.toml`, or the gate itself.
  No Gradle setup needed — pure bash gate, runs in seconds.

Both files ship to consumer forks via `sync-dirs.yaml` (`.github/` + `scripts/`
are in the default sync list), so every fork picks up the same guarantee with
zero per-fork configuration.

Verified locally:
- Template current state: 17 libs.* refs all resolve → ✅ exit 0.
- Field-officer pre-fix simulation (libs.versions.toml at the broken state from
  ~24h ago, before manual catch-up): catches all 5 unresolved refs including
  `libs.plugins.kover.convention → kover-convention` in [plugins] section, exit 1.

This is the recurring class of failure that's been hand-fixed at least 3 times in
4 months on the field-officer fork:
- #2572 (2026-01-30) chore(build-logic): add missing dependencies to version
  catalog & missing build logic files
- #2604 (2026-02-18) chore: update versions parity with kmp-project-template
- Manual catch-up after #2682 sync (2026-05-21) → fixed today on field-officer

The gate makes the failure mode impossible to silently re-occur on this template
and on any fork that consumes the synced `.github/` + `scripts/` directories.
@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR extends the sync-dirs workflow and script with self-healing: when syncing build-logic from upstream, the scripts now automatically detect libs.* catalog references that are missing locally and copy the corresponding alias entries (plus any referenced version pins) from upstream gradle/libs.versions.toml into the consumer's catalog, committing the changes together with the sync.

Changes

Self-healing sync for missing Gradle catalog entries

Layer / File(s) Summary
Script healing function implementation
sync-dirs.sh
heal_libs_versions_toml() extracts all libs.<ref> patterns from build-logic/ Kotlin sources, compares them against the local catalog structure, fetches the upstream catalog from the synced temp branch, and inserts missing entries (and referenced version.* keys) into gradle/libs.versions.toml, tracking healed entries and reporting warnings when upstream is also missing a referenced key.
Workflow healing step setup and implementation
.github/workflows/sync-dirs.yaml
A new "Heal libs.versions.toml" GitHub Actions step validates preconditions (consumer catalog and build-logic/ exist), extracts libs.* references from synced sources, loads upstream catalog content via git show from the temp branch, defines a TOML insertion helper for correct section placement, iterates through discovered refs to insert missing entries, and fails the workflow if upstream lacks any referenced aliases or succeeds quietly if no changes needed.
Script integration and commit updates
sync-dirs.sh
The script calls the healing function during sync (before cleanup) when not in dry-run mode, conditionally stages the modified catalog file, and updates the multi-line commit message to include an "Auto-heal: gradle/libs.versions.toml" line documenting the auto-healed behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A rabbit hops through the gradle maze,
Healing lost libs with healing gaze—
Missing aliases found and mended,
Catalogs synced, conflicts amended!
Thump thump goes the build, now whole.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a self-healing mechanism to automatically fix missing libs.versions.toml entries when upstream build-logic adds new aliases during directory synchronization.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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: 1

🧹 Nitpick comments (2)
.github/workflows/verify-libs-resolved.yml (1)

48-49: ⚡ Quick win

Add security hardening to checkout action.

The checkout step should disable credential persistence to prevent token leakage. Additionally, consider pinning the action to a specific commit hash instead of a tag for supply chain security.

🔒 Proposed security improvements
       - name: Checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683  # v4.2.2
+        with:
+          persist-credentials: false
🤖 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/verify-libs-resolved.yml around lines 48 - 49, Update the
Checkout step (currently using "uses: actions/checkout@v4") to harden security
by adding "persist-credentials: false" under its "with:" block to prevent token
credential persistence, and replace the tag-based reference
"actions/checkout@v4" with a pinned commit SHA (actions/checkout@<commit-sha>)
to lock the action to a specific commit for supply-chain security; keep other
settings (e.g., fetch-depth) unchanged unless you intentionally need shallow
fetch.
scripts/verify_libs_resolved.sh (1)

94-94: 💤 Low value

Consider using bash parameter expansion instead of sed.

The dot-to-hyphen conversion can be done more efficiently with bash built-in parameter expansion.

⚡ Proposed optimization
-    key=$(echo "$ref" | sed 's/\./-/g')
+    key="${ref//./-}"
🤖 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 `@scripts/verify_libs_resolved.sh` at line 94, The current assignment to key
uses an external sed call to replace dots with hyphens; replace that sed
pipeline in the key assignment with bash's built-in parameter expansion to
perform an in-shell global substitution on the variable ref (i.e., use the
parameter-expansion form that replaces all '.' with '-') so you avoid spawning
sed and improve performance — update the line assigning key (which references
ref) accordingly and ensure the script has a bash-compatible shebang.
🤖 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 `@scripts/verify_libs_resolved.sh`:
- Around line 43-53: Add a prerequisite-check block near the top (around where
REPO_ROOT is determined) that verifies required commands are present before
proceeding: at minimum validate git, grep, sed plus the other mandated tools
(gh, firebase, keytool, openssl, python3); if any are missing, print a clear
error and exit non‑zero. Use the same pattern used for git detection (command -v
...) and reference the REPO_ROOT initialization context so failures occur before
using LIBS_TOML or BUILD_LOGIC.

---

Nitpick comments:
In @.github/workflows/verify-libs-resolved.yml:
- Around line 48-49: Update the Checkout step (currently using "uses:
actions/checkout@v4") to harden security by adding "persist-credentials: false"
under its "with:" block to prevent token credential persistence, and replace the
tag-based reference "actions/checkout@v4" with a pinned commit SHA
(actions/checkout@<commit-sha>) to lock the action to a specific commit for
supply-chain security; keep other settings (e.g., fetch-depth) unchanged unless
you intentionally need shallow fetch.

In `@scripts/verify_libs_resolved.sh`:
- Line 94: The current assignment to key uses an external sed call to replace
dots with hyphens; replace that sed pipeline in the key assignment with bash's
built-in parameter expansion to perform an in-shell global substitution on the
variable ref (i.e., use the parameter-expansion form that replaces all '.' with
'-') so you avoid spawning sed and improve performance — update the line
assigning key (which references ref) accordingly and ensure the script has a
bash-compatible shebang.
🪄 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

Run ID: 8bae9098-cce2-4b5c-a97a-302422cd1756

📥 Commits

Reviewing files that changed from the base of the PR and between e80e14c and ceb1a79.

📒 Files selected for processing (2)
  • .github/workflows/verify-libs-resolved.yml
  • scripts/verify_libs_resolved.sh

Comment thread scripts/verify_libs_resolved.sh Outdated
Comment on lines +43 to +53
# Resolve repo root: git top-level is the truth when available (handles CI checkout
# layouts + the case where this script is sync'd into a consumer fork and runs
# against the CONSUMER's catalog, not the template's). Falls back to CWD for
# environments without git (very rare).
if command -v git >/dev/null 2>&1 && REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)"; then
:
else
REPO_ROOT="$(pwd)"
fi
LIBS_TOML="$REPO_ROOT/gradle/libs.versions.toml"
BUILD_LOGIC="$REPO_ROOT/build-logic"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Validate all required prerequisites per coding guidelines.

The script validates git availability but also depends on grep and sed commands that are used later without validation. As per coding guidelines, bash scripts must validate prerequisites before executing main logic.

✅ Proposed prerequisite validation
 set -uo pipefail
 
+# Validate prerequisites
+for cmd in grep sed; do
+    if ! command -v "$cmd" >/dev/null 2>&1; then
+        echo "❌ Required command '$cmd' not found. Please install it and try again." >&2
+        exit 1
+    fi
+done
+
 # Resolve repo root: git top-level is the truth when available (handles CI checkout
 # layouts + the case where this script is sync'd into a consumer fork and runs
 # against the CONSUMER's catalog, not the template's). Falls back to CWD for
 # environments without git (very rare).

As per coding guidelines, Bash scripts must validate prerequisites (git, gh, firebase, keytool, openssl, python3) before executing main logic.

🤖 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 `@scripts/verify_libs_resolved.sh` around lines 43 - 53, Add a
prerequisite-check block near the top (around where REPO_ROOT is determined)
that verifies required commands are present before proceeding: at minimum
validate git, grep, sed plus the other mandated tools (gh, firebase, keytool,
openssl, python3); if any are missing, print a clear error and exit non‑zero.
Use the same pattern used for git detection (command -v ...) and reference the
REPO_ROOT initialization context so failures occur before using LIBS_TOML or
BUILD_LOGIC.

…ript) — no new files

Per review feedback: the right place for this fix is the sync-dirs flow itself,
not a separate workflow that runs after-the-fact. Removed the dedicated workflow
+ companion script from the previous commit; integrated the logic inline into
both .github/workflows/sync-dirs.yaml and sync-dirs.sh.

What changed vs the previous commit on this branch (ceb1a79):

  - DELETED .github/workflows/verify-libs-resolved.yml (-52 lines)
  - DELETED scripts/verify_libs_resolved.sh (-180 lines)

  + .github/workflows/sync-dirs.yaml — new step "Heal libs.versions.toml" between
    "Sync directories and files" and "Clean up temporary branch". Runs the same
    detection logic, but instead of just failing the gate, it AUTO-PULLS the
    missing alias entries (and any version.ref keys they depend on) directly from
    the upstream catalog accessible via $TEMP_BRANCH. The healed catalog is then
    committed as part of the same sync PR — no separate fix-up PR ever needed.

  + sync-dirs.sh — new heal_libs_versions_toml() function invoked between the
    sync loop and temp-branch cleanup. Same heal semantics for local CLI use.
    The downstream `git add` is widened to include gradle/libs.versions.toml
    when the heal touched it.

Behavior:

  1. Sync-dirs pulls build-logic from upstream (existing behavior, unchanged)
  2. Heal step inspects every libs.<X> reference in the freshly-synced build-logic
  3. For each ref whose alias is missing from consumer's gradle/libs.versions.toml,
     pull the matching line from upstream (TEMP_BRANCH) and insert it into the
     appropriate [versions] / [libraries] / [plugins] section
  4. If an alias version.refs an undeclared version key, that version key is also
     pulled and inserted into [versions] BEFORE the alias
  5. Healed entries land in the same sync PR — discovery + remediation in one
     atomic workflow run, instead of a "fails-now-fix-tomorrow" split

Verified against field-officer's pre-fix state (catalog reverted to commit
408d9cba — the broken state from ~24h ago, before the manual #2691 catch-up):
  - Pre-heal: 5 missing aliases — kover-gradlePlugin, kover-convention,
    androidx-room-gradle-plugin, kmp-product-flavors-plugin, spotless-gradle
  - Post-heal: 7 entries inserted (5 aliases + 2 referenced versions kmpProductFlavors,
    kover). All build-logic libs.<X> refs resolve.

Eliminates the failure mode that's been hand-fixed at least 3 times in 4 months
on the field-officer fork (openMF/mifos-x-field-officer-app#2572, #2604, #2691).
@therajanmaurya therajanmaurya changed the title feat(ci): add build-logic ↔ libs.versions.toml resolution gate fix(sync-dirs): self-heal libs.versions.toml when upstream adds build-logic aliases May 22, 2026

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
sync-dirs.sh (1)

681-688: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Stage the healed catalog before checking for changes.

heal_libs_versions_toml() only updates the working tree. If a run only needs gradle/libs.versions.toml healing, git diff --cached is still clean here, so the script drops the branch and loses the fix.

Suggested fix
-    # Check for changes
-    if ! git diff --quiet --exit-code --cached; then
-        print_step "Committing changes..."
-        git add "${SYNC_DIRS[@]}" "${SYNC_FILES[@]}"
-        # Include any catalog heal additions made by heal_libs_versions_toml.
-        if [ -f "gradle/libs.versions.toml" ]; then
-            git add "gradle/libs.versions.toml"
-        fi
+    git add "${SYNC_DIRS[@]}" "${SYNC_FILES[@]}"
+    if [ -f "gradle/libs.versions.toml" ]; then
+        git add "gradle/libs.versions.toml"
+    fi
+
+    # Check for changes
+    if ! git diff --quiet --exit-code --cached; then
+        print_step "Committing changes..."
🤖 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 `@sync-dirs.sh` around lines 681 - 688, The change-detection uses "git diff
--quiet --exit-code --cached" before staging the healed catalog, so a run that
only modifies gradle/libs.versions.toml via heal_libs_versions_toml() will be
missed; move or add a staging step for that file before the cached-diff check:
after calling heal_libs_versions_toml() (or before the existing "if ! git diff
... --cached" block) detect if "gradle/libs.versions.toml" exists and git add it
(same as the later add), ensuring the healed catalog is staged prior to the git
diff --cached check so the script will see changes and commit them.
🤖 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/sync-dirs.yaml:
- Around line 332-347: The current grep checks for "^${key}=" across the whole
TOML which can match identical keys in other sections; instead, determine which
TOML section the key belongs to (e.g., libraries/plugins/versions) and extract
only that section from both "$LIBS_TOML" and "$upstream_toml" before running the
greps. Concretely: compute a section name for the current key (based on how key
is built for libs.$ref), extract section_local=$(sed -n
'/^\[<section>\]/,/^\[/{p}' "$LIBS_TOML") and section_upstream=$(echo
"$upstream_toml" | sed -n '/^\[<section>\]/,/^\[/{p}'), then replace the three
greps that use "$LIBS_TOML" or "$upstream_toml" (the exists check, upstream_line
lookup, and ref_version presence check that sets ver_line) to search only within
section_local or section_upstream as appropriate (and when ref_version refers to
a version key, search the [versions] section contents). Ensure variable names
from the diff (key, LIBS_TOML, upstream_toml, ref_version, ver_line) are used so
you update the exact checks.

In `@sync-dirs.sh`:
- Around line 624-641: The current grep checks on $libs_toml and $upstream_toml
are scanning entire files and can match keys from other TOML sections; update
the lookup logic so both the alias lookup (when computing $upstream_line) and
the version lookup (when computing $ver_line using $ref_version) first extract
the specific TOML section block (e.g. the computed libraries/plugins section
that contains the alias) and then run the grep on that block only. Concretely:
derive the section header for the alias, extract the block from $libs_toml and
$upstream_toml (using sed/awk to get lines between the header and the next
[...]) into temporary variables and use those variables in the existing
grep/grep -oE commands that produce $upstream_line, $ref_version and $ver_line
so matches are section-scoped.

---

Outside diff comments:
In `@sync-dirs.sh`:
- Around line 681-688: The change-detection uses "git diff --quiet --exit-code
--cached" before staging the healed catalog, so a run that only modifies
gradle/libs.versions.toml via heal_libs_versions_toml() will be missed; move or
add a staging step for that file before the cached-diff check: after calling
heal_libs_versions_toml() (or before the existing "if ! git diff ... --cached"
block) detect if "gradle/libs.versions.toml" exists and git add it (same as the
later add), ensuring the healed catalog is staged prior to the git diff --cached
check so the script will see changes and commit them.
🪄 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

Run ID: 61ce282f-dcec-4f41-b627-9b3fec76ff0e

📥 Commits

Reviewing files that changed from the base of the PR and between ceb1a79 and a73f119.

📒 Files selected for processing (2)
  • .github/workflows/sync-dirs.yaml
  • sync-dirs.sh

Comment on lines +332 to +347
if grep -qE "^${key}[[:space:]]*=" "$LIBS_TOML"; then
continue
fi

# Pull the matching entry from upstream.
upstream_line=$(echo "$upstream_toml" | grep -E "^${key}[[:space:]]*=" | head -1)
if [[ -z "$upstream_line" ]]; then
echo " ⚠️ Missing alias '$key' (for libs.$ref) not present in upstream catalog either — manual fix needed."
warnings=$((warnings + 1))
continue
fi

# If the alias references a version.ref, ensure that version key is also present.
ref_version=$(echo "$upstream_line" | grep -oE 'version\.ref = "[^"]+"' | sed -E 's/version\.ref = "([^"]+)"/\1/' | head -1)
if [[ -n "$ref_version" ]] && ! grep -qE "^${ref_version}[[:space:]]*=" "$LIBS_TOML"; then
ver_line=$(echo "$upstream_toml" | grep -E "^${ref_version}[[:space:]]*=" | head -1)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Make the workflow's catalog matching section-aware.

This step also searches the entire TOML for ^${key}=. If the same key exists in another section, the workflow can incorrectly skip a missing alias or fetch the wrong upstream line. Match within the resolved [libraries], [plugins], or [versions] block instead.

🤖 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/sync-dirs.yaml around lines 332 - 347, The current grep
checks for "^${key}=" across the whole TOML which can match identical keys in
other sections; instead, determine which TOML section the key belongs to (e.g.,
libraries/plugins/versions) and extract only that section from both "$LIBS_TOML"
and "$upstream_toml" before running the greps. Concretely: compute a section
name for the current key (based on how key is built for libs.$ref), extract
section_local=$(sed -n '/^\[<section>\]/,/^\[/{p}' "$LIBS_TOML") and
section_upstream=$(echo "$upstream_toml" | sed -n '/^\[<section>\]/,/^\[/{p}'),
then replace the three greps that use "$LIBS_TOML" or "$upstream_toml" (the
exists check, upstream_line lookup, and ref_version presence check that sets
ver_line) to search only within section_local or section_upstream as appropriate
(and when ref_version refers to a version key, search the [versions] section
contents). Ensure variable names from the diff (key, LIBS_TOML, upstream_toml,
ref_version, ver_line) are used so you update the exact checks.

Comment thread sync-dirs.sh
Comment on lines +624 to +641
if grep -qE "^${key}[[:space:]]*=" "$libs_toml"; then
continue
fi

local upstream_line
upstream_line=$(echo "$upstream_toml" | grep -E "^${key}[[:space:]]*=" | head -1)
if [ -z "$upstream_line" ]; then
print_warning "Missing alias '$key' (for libs.$ref) not present in upstream catalog either — manual fix needed."
warnings=$((warnings + 1))
continue
fi

# Pull the version key too, if the alias version.refs something missing.
local ref_version
ref_version=$(echo "$upstream_line" | grep -oE 'version\.ref = "[^"]+"' | sed -E 's/version\.ref = "([^"]+)"/\1/' | head -1)
if [ -n "$ref_version" ] && ! grep -qE "^${ref_version}[[:space:]]*=" "$libs_toml"; then
local ver_line
ver_line=$(echo "$upstream_toml" | grep -E "^${ref_version}[[:space:]]*=" | head -1)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Scope catalog lookups to the computed TOML section.

These grep checks scan the whole file, so a key that already exists in [versions] can make a missing [plugins] or [libraries] entry look resolved, and the upstream lookup can copy the wrong line for the same reason. The heal logic needs section-aware matching for both the alias lookup and the version.ref lookup.

🤖 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 `@sync-dirs.sh` around lines 624 - 641, The current grep checks on $libs_toml
and $upstream_toml are scanning entire files and can match keys from other TOML
sections; update the lookup logic so both the alias lookup (when computing
$upstream_line) and the version lookup (when computing $ver_line using
$ref_version) first extract the specific TOML section block (e.g. the computed
libraries/plugins section that contains the alias) and then run the grep on that
block only. Concretely: derive the section header for the alias, extract the
block from $libs_toml and $upstream_toml (using sed/awk to get lines between the
header and the next [...]) into temporary variables and use those variables in
the existing grep/grep -oE commands that produce $upstream_line, $ref_version
and $ver_line so matches are section-scoped.

@therajanmaurya therajanmaurya merged commit 03dce44 into openMF:dev May 22, 2026
8 checks passed
therajanmaurya pushed a commit to therajanmaurya/mifos-x-field-officer-app that referenced this pull request May 22, 2026
…adle script compilation

CI failure on PR openMF#2691 commit 463426d (Test Coverage Floor workflow): script
compilation errors in cmp-android/build.gradle.kts — unresolved `libs.plugins.
android.application.convention`, `libs.plugins.android.application.compose.
convention`, `libs.plugins.aboutLibraries`. Root cause: build.gradle.kts files
across the project (cmp-android, core-base/*, libs/*) were sync'd from upstream
template via sync-dirs.yaml but the matching catalog entries in gradle/libs.
versions.toml weren't included in the sync (the toml is intentionally consumer-
local — it carries the project's package namespaces + version overrides).

This is the SAME failure mode that openMF/kmp-project-template#163 fixes
upstream by making sync-dirs self-healing. Until that PR merges + the next
sync-dirs run propagates the heal logic, field-officer needs a manual catch-up
of every catalog entry referenced by sync'd build.gradle.kts files.

Audit: project-wide grep of every `libs.<X>` reference in `**/*.kts` + `**/*.kt`
against gradle/libs.versions.toml found 16 unresolved aliases (180 total refs
scanned, 16 missing).

Added to [versions]:
  kmptoolkit = "3.2.3"
  aboutLibraries = "13.2.1"
  androidxHiltNavigationCompose = "1.2.0"
  androidxSecurityCrypto = "1.1.0-alpha06"
  bouncycastle = "1.78.1"
  sqliteWeb = "2.6.2"
  store = "5.1.0-alpha08"
  turbine = "1.2.1"

Added to [libraries]:
  aboutlibraries-core, aboutlibraries-compose-core, aboutlibraries-compose-m3
  androidx-hilt-navigation-compose
  androidx-security-crypto
  androidx-sqlite-web
  androidx-test-ext-junit (uses existing junitVersion = "1.2.1" which is
    actually the ext-junit version, not junit4)
  bouncycastle
  cmp-network-monitor + cmp-network-monitor-compose (both use kmptoolkit version)
  junit (uses existing junit4 = "4.13.2" — separate from junitVersion)
  kermit-koin
  store5 + store5-cache
  turbine

Added to [plugins]:
  aboutLibraries = { id = "com.mikepenz.aboutlibraries.plugin", … }
  android-application-convention = { id = "org.convention.android.application" }
  android-application-compose-convention = { id = "org.convention.android.application.compose" }

The 3 plugin convention aliases match plugin IDs registered in
build-logic/convention/build.gradle.kts — the registration was already there;
only the alias entry was missing.

Verification: full re-run of the libs-resolution audit (the exact algorithm
that the upstream PR openMF#163 ships as a sync-dirs heal step) now reports 0
unresolved references project-wide:
  scanned: 188 libs.* refs
  missing: 0

Categories of the 188 refs:
  - 14 in build-logic/convention/build.gradle.kts (fixed in commit 4077e85)
  - ~50 in cmp-android, cmp-shared, cmp-navigation, cmp-desktop, cmp-web, cmp-ios
  - ~80 in core-base/*, libs/* (vendored modules sync'd from template)
  - rest in feature/*, core/*

This unblocks the next CI run on PR openMF#2691. Expected outcome: kover coverage
job progresses past script compilation; surfaces whatever non-catalog compile
issues remain (likely a smaller surface from B4/B6/B6.10 cross-tier moves).
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