diff --git a/CHANGELOG.md b/CHANGELOG.md index c91380b..ea6400b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- Fix (`play build` / `check` versionCode source warning): a variable set from the property first + (`def code = (project.findProperty('versionCode') ?: ...).toInteger()`, then `versionCode code`; also `ext.` and Kotlin + `val`) is followed to its definition and no longer flagged. + - Fix (`apple iap-submit`, `apple resubmit`): after the owner canceled a submission, every IAP version was DEVELOPER_REJECTED and the plan skipped them all ("change it in App Store Connect (or --new-version)", then "nothing to submit"). Apple accepts the same version again (201 READY_FOR_REVIEW, then the submit 200), so diff --git a/cli/lib/gradle.ts b/cli/lib/gradle.ts index b1bcd90..7ecfc8c 100644 --- a/cli/lib/gradle.ts +++ b/cli/lib/gradle.ts @@ -128,8 +128,15 @@ export function versionSourceIssues(file: string, src = readFileSync(file, 'utf8 continue; } const h = hit as { line: number; text: string; value: string }; - const reads = new RegExp(`(?:findProperty|property|hasProperty|getProperty|gradleProperty)\\s*\\(\\s*['"]${field}['"]|properties\\s*\\[\\s*['"]${field}['"]`).test(h.value); - if (reads) continue; + const readsRe = new RegExp(`(?:findProperty|property|hasProperty|getProperty|gradleProperty)\\s*\\(\\s*['"]${field}['"]|properties\\s*\\[\\s*['"]${field}['"]`); + if (readsRe.test(h.value)) continue; + // a variable set from the property first (`def code = (project.findProperty('versionCode') ?: ...).toInteger()`, + // then `versionCode code`): follow the name to its definition anywhere in the file + const id = /^\(?\s*(?:project\.)?(?:ext\.)?([A-Za-z_]\w*)\s*(?:\.\s*\w+\s*\(\s*\)\s*)*(?:as\s+\w+\s*)?\)?$/.exec(h.value)?.[1]; + if (id && !/^\d/.test(id)) { + const def = new RegExp(`(?:\\b(?:def|val|var|final)\\s+(?:\\w+\\s+)?|\\bext\\.|\\bextra\\[\\s*["']|^\\s*)${id}\\b["']?\\s*\\]?\\s*(?::\\s*\\w+\\??\\s*)?=\\s*([^\\n]+)`, 'm').exec(code); + if (def && readsRe.test(def[1]!)) continue; + } const lit = /^\(?\s*(\d+|"[^"]*"|'[^']*')\s*\)?$/.exec(h.value); const current = lit ? lit[1]!.replace(/^['"]|['"]$/g, '') : field === 'versionCode' ? (/(?:\?:|\|\||,)\s*['"]?(\d+)['"]?/.exec(h.value)?.[1] ?? /\b(\d+)\b/.exec(h.value)?.[1] ?? fallback) diff --git a/tests/build-sign.test.ts b/tests/build-sign.test.ts index 83b2bc3..6d186f1 100644 --- a/tests/build-sign.test.ts +++ b/tests/build-sign.test.ts @@ -319,6 +319,16 @@ describe('versionCode / versionName source in the build file', () => { // commented out lines do not count; nothing set at all is said too expect(kinds('app/build.gradle', gv(" // versionCode 1\n /* versionName '1.0' */")).map(k => [k[0], k[1]])).toEqual([['versionCode', 'missing'], ['versionName', 'missing']]); }); + test('a variable read from the property first, then used, is not flagged (STAR DASH pattern); an env-only variable is', () => { + const top = (def: string, body: string) => `plugins { id 'com.android.application' }\n${def}\nandroid {\n defaultConfig {\n${body}\n }\n}\n`; + const ok = top("def erVersionCode = (project.findProperty('versionCode') ?: System.getenv('ER_ANDROID_VERSION_CODE') ?: '1').toString().toInteger()\ndef erVersionName = project.findProperty('versionName') ?: '1.0.0'", + ' versionCode erVersionCode\n versionName erVersionName'); + expect(kinds('app/build.gradle', ok)).toEqual([]); + expect(kinds('app/build.gradle', top("ext.vc = (project.findProperty('versionCode') ?: 1) as Integer\next.vn = project.findProperty('versionName') ?: '1.0'", ' versionCode vc\n versionName vn'))).toEqual([]); + expect(kinds('app/build.gradle.kts', `val vc = (project.findProperty("versionCode") as String?)?.toInt() ?: 1\nval vn: String = project.findProperty("versionName") as String? ?: "1.0"\nandroid {\n defaultConfig {\n versionCode = vc\n versionName = vn\n }\n}\n`)).toEqual([]); + const env = top("def code = (System.getenv('VERSION_CODE') ?: '1').toInteger()", " versionCode code\n versionName project.findProperty('versionName') ?: '1.0'"); + expect(kinds('app/build.gradle', env).map(k => [k[0], k[1]])).toEqual([['versionCode', 'other']]); + }); test('Kotlin: literal and env var warn with the exact line; findProperty does not', () => { expect(kinds('app/build.gradle.kts', kv(' versionCode = 5\n versionName = "2.0"'))).toEqual([ ['versionCode', 'literal', 6, '5', 'versionCode = (project.findProperty("versionCode") as String?)?.toInt() ?: 5'],