|
3 | 3 | using UnityEditor.Build; |
4 | 4 | using UnityEditor.Build.Reporting; |
5 | 5 | using System.IO; |
| 6 | +using System.Text.RegularExpressions; |
6 | 7 |
|
| 8 | +/// <summary> |
| 9 | +/// Keeps Unity's version fields in sync with files generated by CI: |
| 10 | +/// - version.txt -> PlayerSettings.bundleVersion (maps to Android versionName) |
| 11 | +/// - versionCode.txt (optional) -> PlayerSettings.Android.bundleVersionCode |
| 12 | +/// |
| 13 | +/// If versionCode.txt is missing, it will derive a versionCode from version.txt using: |
| 14 | +/// code = MAJOR*10000 + MINOR*100 + PATCH |
| 15 | +/// This ensures a monotonic integer for Google Play uploads. |
| 16 | +/// </summary> |
7 | 17 | public class SyncBundleVersionPreprocess : IPreprocessBuildWithReport |
8 | 18 | { |
9 | 19 | /// <summary> |
10 | | - /// Determines the order in which build preprocessors are called. |
11 | | - /// 0 means default/early execution. |
| 20 | + /// Determines the order in which build preprocessors are called. 0 means default/early execution. |
12 | 21 | /// </summary> |
13 | 22 | public int callbackOrder => 0; |
14 | 23 |
|
15 | 24 | /// <summary> |
16 | | - /// Keeps Unity's internal application version (Application.version) |
17 | | - /// in sync with CI/CD-managed versions. |
| 25 | + /// Keeps Unity's internal application version (Application.version) in sync with CI/CD-managed versions. |
18 | 26 | /// </summary> |
19 | 27 | public void OnPreprocessBuild(BuildReport report) |
20 | 28 | { |
21 | | - var versionPath = Path.Combine(Directory.GetCurrentDirectory(), "version.txt"); |
| 29 | + var root = Directory.GetCurrentDirectory(); |
| 30 | + |
| 31 | + // 1) Sync human-readable version |
| 32 | + var versionPath = Path.Combine(root, "version.txt"); |
22 | 33 | if (File.Exists(versionPath)) |
23 | 34 | { |
24 | | - // Read the file and trim extra whitespace/newlines |
25 | 35 | var v = File.ReadAllText(versionPath).Trim(); |
26 | 36 |
|
27 | 37 | // Update if the file is not empty and differs from current bundleVersion |
28 | | - if (!string.IsNullOrEmpty(v) && UnityEditor.PlayerSettings.bundleVersion != v) |
| 38 | + if (!string.IsNullOrEmpty(v) && PlayerSettings.bundleVersion != v) |
29 | 39 | { |
30 | | - UnityEditor.PlayerSettings.bundleVersion = v; // Update Unity's app version |
31 | | - |
| 40 | + PlayerSettings.bundleVersion = v; // Update Unity's app version |
32 | 41 | AssetDatabase.SaveAssets(); |
33 | | - UnityEngine.Debug.Log($"[Build] Synced PlayerSettings.bundleVersion -> {v}"); |
| 42 | + UnityEngine.Debug.Log($"[Build] Synced bundleVersion -> {v}"); |
| 43 | + } |
| 44 | + |
| 45 | + // 2) Android versionCode: prefer explicit file; else derive from semver |
| 46 | + |
| 47 | + |
| 48 | +#if UNITY_ANDROID |
| 49 | + |
| 50 | + // Local holder for the versionCode we want to apply to the Android build. |
| 51 | + int code; // Google Play requires this to be a monotonically increasing integer. |
| 52 | + var codePath = Path.Combine(root, "versionCode.txt"); |
| 53 | + |
| 54 | + // Preferred path: if versionCode.txt exists and contains a valid integer, use it as-is. |
| 55 | + if (File.Exists(codePath) && int.TryParse(File.ReadAllText(codePath).Trim(), out code)) |
| 56 | + { |
| 57 | + SetAndroidVersionCodeIfDifferent(code); |
| 58 | + } |
| 59 | + else |
| 60 | + { |
| 61 | + // Fallback path: derive an integer versionCode from the semver in version.txt (string 'v'). |
| 62 | + var m = Regex.Match(v, @"^(?<maj>\d+)\.(?<min>\d+)\.(?<pat>\d+)"); |
| 63 | + if (m.Success) |
| 64 | + { |
| 65 | + // This scheme reserves two digits for MINOR and two for PATCH (00–99 each). |
| 66 | + // If you expect MINOR or PATCH to exceed 99, switch to a wider encoding |
| 67 | + // (e.g., MAJOR*1_000_000 + MINOR*1_000 + PATCH). |
| 68 | + int maj = int.Parse(m.Groups["maj"].Value); |
| 69 | + int min = int.Parse(m.Groups["min"].Value); |
| 70 | + int pat = int.Parse(m.Groups["pat"].Value); |
| 71 | + code = maj * 10000 + min * 100 + pat; |
| 72 | + SetAndroidVersionCodeIfDifferent(code); |
| 73 | + } |
| 74 | + else |
| 75 | + { |
| 76 | + // If version.txt doesn't match MAJOR.MINOR.PATCH, we can't derive a versionCode. |
| 77 | + // Build will still proceed with the current PlayerSettings.Android.bundleVersionCode. |
| 78 | + UnityEngine.Debug.LogWarning($"[Build] Could not parse semver from '{v}' to derive Android versionCode."); |
| 79 | + } |
34 | 80 | } |
| 81 | +#endif |
35 | 82 | } |
36 | 83 | else |
37 | 84 | { |
38 | 85 | UnityEngine.Debug.LogWarning("[Build] version.txt not found; bundleVersion not changed."); |
39 | 86 | } |
40 | 87 | } |
| 88 | + |
| 89 | + |
| 90 | +#if UNITY_ANDROID && UNITY_EDITOR |
| 91 | + |
| 92 | + /// <summary> |
| 93 | + /// Writes <see cref="PlayerSettings.Android.bundleVersionCode"/> if the value differ. |
| 94 | + /// </summary> |
| 95 | + private static void SetAndroidVersionCodeIfDifferent(int code) |
| 96 | + { |
| 97 | + if (PlayerSettings.Android.bundleVersionCode != code) |
| 98 | + { |
| 99 | + PlayerSettings.Android.bundleVersionCode = code; // Updates ProjectSettings/ProjectSettings.asset |
| 100 | + AssetDatabase.SaveAssets(); // Keeps the change |
| 101 | + UnityEngine.Debug.Log($"[Build] Synced Android bundleVersionCode -> {code}"); |
| 102 | + } |
| 103 | + } |
| 104 | +#endif |
41 | 105 | } |
42 | 106 | #endif |
0 commit comments