Skip to content

Read build properties from local.properties before user-level gradle.properties#3568

Open
avazirna wants to merge 6 commits intomasterfrom
prefer-local-properties-over-gradle-properties
Open

Read build properties from local.properties before user-level gradle.properties#3568
avazirna wants to merge 6 commits intomasterfrom
prefer-local-properties-over-gradle-properties

Conversation

@avazirna
Copy link
Contributor

@avazirna avazirna commented Feb 25, 2026

Product Description

This stemmed from a retro following an android secret was accidentally leaked in a PR. During the retro, it was noted that developers, particularly new team members, sometimes get confused about where secrets should be stored, so it was suggested to read from the 'local.properties' first. Sincelocal.properties is gitignored by default in Android projects, so it is safe for per-developer overrides without risk of committing secrets. Build server behaviour is unchanged since build servers do not have a local.properties file.

Technical Summary

Previously, all build properties (API keys, signing config, test credentials) were read exclusively from project.properties, which resolves user-level ~/.gradle/gradle.properties with higher priority than the project-level file. This made it inconvenient to override properties locally without touching the shared gradle.properties.

Safety Assurance

Safety story

Built successfully locally.

Labels and Review

  • Do we need to enhance the manual QA test coverage ? If yes, the "QA Note" label is set correctly
  • Does the PR introduce any major changes worth communicating ? If yes, the "Release Note" label is set and a "Release Note" is specified in PR description.
  • Risk label is set correctly
  • The set of people pinged as reviewers is appropriate for the level of risk of the change

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2026

📝 Walkthrough

Walkthrough

The pull request modifies the Gradle build configuration in app/build.gradle to support loading sensitive properties and API keys from a local.properties file. A helper function loadProp is introduced to resolve properties first from local.properties, then from project.properties, with fallback defaults. Fifteen properties including API keys, credentials, and keystore information are updated to use this new mechanism instead of direct project.properties lookups. The TRUSTED_SOURCE_PUBLIC_KEY default value is expanded to include its full multi-line string constant.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and accurately summarizes the main change: read build properties from local.properties before user-level gradle.properties, which is the primary objective of this PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed PR description covers Product Description, Technical Summary, and Safety Assurance sections with rationale and local testing confirmation, but lacks explicit QA Plan details and automated test coverage explanation.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch prefer-local-properties-over-gradle-properties

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/build.gradle`:
- Around line 173-175: The loader closure loadProp currently prefers
localProperties when localProperties.containsKey(key) is true even if its value
is an empty string, which shadows project.properties; change the logic in
loadProp to read the value = localProperties.getProperty(key) and if that value
is null or empty/blank (use trim emptiness check) then fall back to
project.properties[key] ?: defaultValue, otherwise return the non-empty local
value—ensure you reference loadProp, localProperties.getProperty(key), and
project.properties[key] in the fix.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 56c2347 and 2dfe94b.

📒 Files selected for processing (1)
  • app/build.gradle

Comment on lines +173 to +175
def loadProp = { String key, def defaultValue = '' ->
localProperties.containsKey(key) ? localProperties.getProperty(key) : (project.properties[key] ?: defaultValue)
}
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 25, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Empty value in local.properties silently shadows project.properties.

localProperties.containsKey(key) is true even when the stored value is an empty string (e.g., MAPBOX_SDK_API_KEY= in local.properties). In that case getProperty(key) returns "" and the project.properties fallback is never reached.

The old code project.properties['KEY'] ?: default relied on Groovy truth — an empty string was falsy and would fall through to the default. The new code intentionally inverts priority, but a developer who writes an empty entry in local.properties will silently receive "" even if a real value is set in ~/.gradle/gradle.properties.

Consider guarding against empty values to preserve the fallback chain:

🛠️ Proposed fix
 def loadProp = { String key, def defaultValue = '' ->
-    localProperties.containsKey(key) ? localProperties.getProperty(key) : (project.properties[key] ?: defaultValue)
+    def localVal = localProperties.getProperty(key)
+    (localVal != null && !localVal.isEmpty()) ? localVal : (project.properties[key] ?: defaultValue)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def loadProp = { String key, def defaultValue = '' ->
localProperties.containsKey(key) ? localProperties.getProperty(key) : (project.properties[key] ?: defaultValue)
}
def loadProp = { String key, def defaultValue = '' ->
def localVal = localProperties.getProperty(key)
(localVal != null && !localVal.isEmpty()) ? localVal : (project.properties[key] ?: defaultValue)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/build.gradle` around lines 173 - 175, The loader closure loadProp
currently prefers localProperties when localProperties.containsKey(key) is true
even if its value is an empty string, which shadows project.properties; change
the logic in loadProp to read the value = localProperties.getProperty(key) and
if that value is null or empty/blank (use trim emptiness check) then fall back
to project.properties[key] ?: defaultValue, otherwise return the non-empty local
value—ensure you reference loadProp, localProperties.getProperty(key), and
project.properties[key] in the fix.

Copy link
Contributor

Choose a reason for hiding this comment

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

@avazirna seems like this would be a nice-to-have tweak as well?

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Contributor Author

@avazirna avazirna Feb 26, 2026

Choose a reason for hiding this comment

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

@conroy-ricketts let's think about it together. There is another perspective to this, If in the local.properties the value is an empty string, i.e. an empty String can be a valid value, and that's the value that should prevail. what do you think? I'm inclined to leave as-is

Copy link
Contributor

Choose a reason for hiding this comment

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

@avazirna Ah yeah that's a good point. I think it makes sense to leave this as-is because if the value is an empty string then that is likely either intentional or a mistake by the developer

@avazirna avazirna marked this pull request as ready for review February 25, 2026 16:18
shubham1g5
shubham1g5 previously approved these changes Feb 25, 2026
Comment on lines +173 to +175
def loadProp = { String key, def defaultValue = '' ->
localProperties.containsKey(key) ? localProperties.getProperty(key) : (project.properties[key] ?: defaultValue)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

@avazirna seems like this would be a nice-to-have tweak as well?

avazirna and others added 2 commits February 26, 2026 18:20
Replace double-quoted string literals that contain no GString interpolation
with single-quoted strings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@avazirna avazirna dismissed stale reviews from conroy-ricketts and shubham1g5 via 389b585 February 27, 2026 08:37
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.

3 participants