Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Setting Android SDK filter #1367

Merged
merged 16 commits into from
Jun 23, 2023
Merged
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Fixes

- Fixed an Android build issue where Sentry options would be cached with the first build until Editor restart ([#1379](https://github.com/getsentry/sentry-unity/pull/1379))
- Adding a remote repository filter to the gradle project ([#1367](https://github.com/getsentry/sentry-unity/pull/1367))
- Setting Android SDK version explicit to prevent version conflicts with remote repositories ([#1378](https://github.com/getsentry/sentry-unity/pull/1387))
- Set debug symbol upload logging to debug verbosity ([#1373](https://github.com/getsentry/sentry-unity/pull/1373))
- The SDK no longer causes an exception during initialiation on Android API level 32 and newer ([#1365](https://github.com/getsentry/sentry-unity/pull/1365))
Expand Down
15 changes: 11 additions & 4 deletions src/Sentry.Unity.Editor/Android/AndroidManifestConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,17 @@
namespace Sentry.Unity.Editor.Android
{
// https://github.com/getsentry/sentry-java/blob/d3764bfc97eed22564a1e23ba96fa73ad2685498/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java#L83-L217
public class AndroidManifestConfiguration : IPostGenerateGradleAndroidProject
public class PostGenerateGradleAndroidProject : IPostGenerateGradleAndroidProject
{
public int callbackOrder { get; } = 1;
public void OnPostGenerateGradleAndroidProject(string basePath)
{
var androidManifestConfiguration = new AndroidManifestConfiguration();
androidManifestConfiguration.OnPostGenerateGradleAndroidProject(basePath);
}
}

public class AndroidManifestConfiguration
{
private readonly SentryUnityOptions? _options;
private readonly SentryCliOptions? _sentryCliOptions;
Expand All @@ -21,9 +31,6 @@ public class AndroidManifestConfiguration : IPostGenerateGradleAndroidProject
private readonly bool _isDevelopmentBuild;
private readonly ScriptingImplementation _scriptingImplementation;

// Lower levels are called first.
public int callbackOrder => 1;

public AndroidManifestConfiguration()
: this(
SentryScriptableObject.ConfiguredBuildTimeOptions,
Expand Down
108 changes: 73 additions & 35 deletions src/Sentry.Unity.Editor/Android/GradleSetup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ internal class GradleSetup
public const string RepositoryScopeName = "repositories";
public static readonly string SdkDependencies = $"implementation ('io.sentry:sentry-android:{GetAndroidSdkVersion()}') {{ exclude group: 'androidx.core' exclude group: 'androidx.lifecycle' }}";
public const string DependencyScopeName = "dependencies";
public const string MavenCentralWithoutFilter = "mavenCentral()";
public const string MavenCentralWithFilter = "mavenCentral { content { excludeGroupByRegex \"io\\\\.sentry.*\" } }";
public static readonly List<string> ScopesToSkip = new() { "buildscript", "pluginManagement" };

private readonly string _rootGradle;
Expand All @@ -39,19 +41,20 @@ public void UpdateGradleProject(IApplication? application = null)

// Starting with 2022.3.0f1 the root build.gradle updated to use the "new" way of importing plugins via `id`
// Instead, dependency repositories get handled in the `settings.gradle` at the root
var gradleFilePath = SentryUnityVersion.IsNewerOrEqualThan("2022.3", application)
? _settingsGradle
: _rootGradle;
var rootGradleFile = (SentryUnityVersion.IsNewerOrEqualThan("2022.3", application)) ? _settingsGradle : _rootGradle;

_logger.LogDebug("Updating the gradle file at '{0}'", gradleFilePath);
_logger.LogDebug("Updating the gradle file at '{0}'", rootGradleFile);

var gradleContent = LoadGradleScript(gradleFilePath);
var gradleContent = LoadGradleScript(rootGradleFile);
gradleContent = InsertIntoScope(gradleContent, RepositoryScopeName, LocalRepository);
File.WriteAllText(gradleFilePath, gradleContent);
gradleContent = ApplyMavenCentralFilter(gradleContent);
File.WriteAllText(rootGradleFile, gradleContent);

_logger.LogDebug("Updating the gradle file at '{0}'", _unityLibraryGradle);

var unityLibraryGradleContent = LoadGradleScript(_unityLibraryGradle);
unityLibraryGradleContent = InsertIntoScope(unityLibraryGradleContent, DependencyScopeName, SdkDependencies);
unityLibraryGradleContent = ApplyMavenCentralFilter(unityLibraryGradleContent);
File.WriteAllText(_unityLibraryGradle, unityLibraryGradleContent);
}

Expand All @@ -61,18 +64,19 @@ public void ClearGradleProject(IApplication? application = null)

// Starting with 2022.3.0f1 the root build.gradle updated to use the "new" way of importing plugins via `id`
// Instead, dependency repositories get handled in the `settings.gradle` at the root
var gradleFilePath = SentryUnityVersion.IsNewerOrEqualThan("2022.3", application)
? _settingsGradle
: _rootGradle;
var rootGradleFile = (SentryUnityVersion.IsNewerOrEqualThan("2022.3", application)) ? _settingsGradle : _rootGradle;

_logger.LogDebug("Removing modifications from '{0}'", rootGradleFile);

_logger.LogDebug("Removing modifications from '{0}'", gradleFilePath);
var gradleContent = LoadGradleScript(gradleFilePath);
var gradleContent = LoadGradleScript(rootGradleFile);
gradleContent = RemoveFromGradleContent(gradleContent, LocalRepository);
File.WriteAllText(gradleFilePath, gradleContent);
gradleContent = gradleContent.Replace(MavenCentralWithFilter, MavenCentralWithoutFilter);
File.WriteAllText(rootGradleFile, gradleContent);

_logger.LogDebug("Removing modifications from the 'build.gradle' file at {0}", _unityLibraryGradle);
var unityLibraryGradleContent = LoadGradleScript(_unityLibraryGradle);
unityLibraryGradleContent = RemoveFromGradleContent(unityLibraryGradleContent, SdkDependencies);
unityLibraryGradleContent = unityLibraryGradleContent.Replace(MavenCentralWithFilter, MavenCentralWithoutFilter);
File.WriteAllText(_unityLibraryGradle, unityLibraryGradleContent);
}

Expand All @@ -85,12 +89,65 @@ internal string InsertIntoScope(string gradleContent, string scope, string inser
}

var lines = gradleContent.Split('\n');
var scopeStart = -1;
var scopeStart = FindBeginningOfScope(lines, scope);
if (scopeStart == -1)
{
throw new BuildFailedException($"Failed to find scope '{scope}'.");
}

var modifiedLines = new List<string>(lines);

var lineToInsert = string.Empty;
var whiteSpaceCount = lines[scopeStart].IndexOf(scope, StringComparison.Ordinal);
for (var i = 0; i < whiteSpaceCount; i++)
{
lineToInsert += " ";
}

lineToInsert += " " + insertion;
var lineOffset = lines[scopeStart].Contains("{") ? 1 : 2; // to make sure we're inside the scope
modifiedLines.Insert(scopeStart + lineOffset, lineToInsert);

return string.Join("\n", modifiedLines.ToArray());
}

internal string ApplyMavenCentralFilter(string gradleContent)
{
_logger.LogDebug("Applying MavenFilter.");

if (gradleContent.Contains(MavenCentralWithFilter))
{
_logger.LogDebug("The filter for maven central has already been set. Skipping.");
return gradleContent;
}

var lines = gradleContent.Split('\n');

var scopeStart = FindBeginningOfScope(lines, RepositoryScopeName);
if (scopeStart == -1)
{
_logger.LogDebug($"Did not find the scope '{RepositoryScopeName}' to apply the filter for maven central. Skipping.");
return gradleContent;
}

for (var i = scopeStart; i < lines.Length; i++)
{
if (lines[i].Contains(MavenCentralWithoutFilter))
{
lines[i] = lines[i].Replace(MavenCentralWithoutFilter, MavenCentralWithFilter);
break;
}
}

return string.Join("\n", lines.ToArray());
}

private int FindBeginningOfScope(string[] lines, string scope)
{
for (var i = 0; i < lines.Length; i++)
{
var line = lines[i];
// There are potentially multiple, nested scopes. We cannot add ourselves to the ones within 'buildscript'
// There are potentially multiple, nested scopes. We cannot add ourselves to the ones listed in `ScopesToSkip`
if (ScopesToSkip.Any(line.Contains))
{
var startIndex = i;
Expand All @@ -105,30 +162,11 @@ internal string InsertIntoScope(string gradleContent, string scope, string inser
}
else if (lines[i].Contains(scope))
{
scopeStart = i;
break;
return i;
}
}

if (scopeStart == -1)
{
throw new BuildFailedException($"Failed to find scope '{scope}'.");
}

var modifiedLines = new List<string>(lines);

var lineToInsert = string.Empty;
var whiteSpaceCount = lines[scopeStart].IndexOf(scope, StringComparison.Ordinal);
for (var i = 0; i < whiteSpaceCount; i++)
{
lineToInsert += " ";
}

lineToInsert += " " + insertion;
var lineOffset = lines[scopeStart].Contains("{") ? 1 : 2; // to make sure we're inside the scope
modifiedLines.Insert(scopeStart + lineOffset, lineToInsert);

return string.Join("\n", modifiedLines.ToArray());
return -1;
}

private static int FindClosingBracket(string[] lines, int startIndex)
Expand Down
78 changes: 60 additions & 18 deletions test/Sentry.Unity.Editor.Tests/Android/GradleSetupTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Reflection;
using NUnit.Framework;
using Sentry.Unity.Editor.Android;
using Sentry.Unity.Integrations;
using Sentry.Unity.Tests.SharedClasses;
using Sentry.Unity.Tests.Stubs;

Expand Down Expand Up @@ -44,20 +43,37 @@ public void LoadGradleScript_FileNotFound_ThrowsFileNotFoundException()
}

[Test]
[TestCase("2019.3", "build.gradle")]
[TestCase("2020.3", "build.gradle")]
[TestCase("2021.3", "build.gradle")]
[TestCase("2022.3", "settings.gradle")]
public void UpdateGradleProject_ModifiesGradleFiles(string unityVersion, string rootGradleFileName)
[TestCase("2019.3")]
[TestCase("2020.3")]
[TestCase("2021.3")]
public void UpdateGradleProject_2021_AndOlder_ModifiesGradleFiles(string unityVersion)
{
var sut = new GradleSetup(Logger, GradleProjectPath);

sut.UpdateGradleProject(new TestApplication(unityVersion: unityVersion));

var rootGradleFilePath = Path.Combine(GradleProjectPath, rootGradleFileName);
var rootGradleFilePath = Path.Combine(GradleProjectPath, "build.gradle");
var rootGradleContent = File.ReadAllText(rootGradleFilePath);
StringAssert.Contains(GradleSetup.LocalRepository, rootGradleContent);

var unityLibraryGradleFilePath = Path.Combine(GradleProjectPath, "unityLibrary", "build.gradle");
var unityLibraryGradleContent = File.ReadAllText(unityLibraryGradleFilePath);
StringAssert.Contains(GradleSetup.SdkDependencies, unityLibraryGradleContent);
StringAssert.Contains(GradleSetup.MavenCentralWithFilter, unityLibraryGradleContent);
}

[Test]
public void UpdateGradleProject_2022_AndNewer_ModifiesGradleFiles()
{
var sut = new GradleSetup(Logger, GradleProjectPath);

sut.UpdateGradleProject(new TestApplication(unityVersion: "2022.3"));

var settingsGradleFilePath = Path.Combine(GradleProjectPath, "settings.gradle");
var settingsGradleContent = File.ReadAllText(settingsGradleFilePath);
StringAssert.Contains(GradleSetup.LocalRepository, settingsGradleContent);
StringAssert.Contains(GradleSetup.MavenCentralWithFilter, settingsGradleContent);

var unityLibraryGradleFilePath = Path.Combine(GradleProjectPath, "unityLibrary", "build.gradle");
var unityLibraryGradleContent = File.ReadAllText(unityLibraryGradleFilePath);
StringAssert.Contains(GradleSetup.SdkDependencies, unityLibraryGradleContent);
Expand All @@ -68,7 +84,8 @@ public void UpdateGradleProject_ModifiesGradleFiles(string unityVersion, string
[TestCase("2020.3", "build.gradle")]
[TestCase("2021.3", "build.gradle")]
[TestCase("2022.3", "settings.gradle")]
public void UpdateGradleProject_GradleAlreadyModified_LogsAndReturns(string unityVersion, string rootGradleFileName)
public void UpdateGradleProject_GradleAlreadyModified_LogsAndReturns(string unityVersion,
string rootGradleFileName)
{
var sut = new GradleSetup(Logger, GradleProjectPath);
sut.UpdateGradleProject(new TestApplication(unityVersion: unityVersion));
Expand All @@ -93,7 +110,8 @@ public void UpdateGradleProject_GradleAlreadyModified_LogsAndReturns(string unit
[TestCase("2020.3", "build.gradle")]
[TestCase("2021.3", "build.gradle")]
[TestCase("2022.3", "settings.gradle")]
public void ClearGradleProject_GradleFilesModified_RemovesModification(string unityVersion, string rootGradleFileName)
public void ClearGradleProject_GradleFilesModified_RemovesModification(string unityVersion,
string rootGradleFileName)
{
var sut = new GradleSetup(Logger, GradleProjectPath);
sut.UpdateGradleProject(new TestApplication(unityVersion: unityVersion));
Expand All @@ -110,28 +128,52 @@ public void ClearGradleProject_GradleFilesModified_RemovesModification(string un

rootGradleContent = File.ReadAllText(rootGradleFilePath);
StringAssert.DoesNotContain(GradleSetup.LocalRepository, rootGradleContent); // Sanity check
StringAssert.DoesNotContain(GradleSetup.MavenCentralWithFilter, rootGradleContent);

unityLibraryGradleContent = File.ReadAllText(unityLibraryGradleFilePath);
StringAssert.DoesNotContain(GradleSetup.SdkDependencies, unityLibraryGradleContent); // Sanity check
StringAssert.DoesNotContain(GradleSetup.MavenCentralWithFilter, unityLibraryGradleContent);
}

[Test]
[TestCase("InsertIntoScope/build.gradle_test_1")]
[TestCase("InsertIntoScope/build.gradle_test_2")]
[TestCase("InsertIntoScope/build.gradle_test_3")]
[TestCase("InsertIntoScope/build.gradle_test_4")]
[TestCase("InsertIntoScope/build.gradle_test_5")]
public void InsertIntoScope_ResultMatchesExpected(string testCaseFileName)
{
var assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var testCasePath = Path.Combine(assemblyPath, "TestFiles", "Android", testCaseFileName + ".txt");
var testCaseExpectedPath =
Path.Combine(assemblyPath, "TestFiles", "Android", testCaseFileName + "_expected.txt");
var expectedGradleContent = File.ReadAllText(testCaseExpectedPath);

var gradleContent = File.ReadAllText(testCasePath);
var sut = new GradleSetup(Logger, GradleProjectPath);

var actualResult = sut.InsertIntoScope(gradleContent, GradleSetup.RepositoryScopeName,
GradleSetup.LocalRepository);

StringAssert.AreEqualIgnoringCase(actualResult, expectedGradleContent);
}

[Test]
[TestCase("build.gradle_test_1.txt", "build.gradle_test_1_expected.txt")]
[TestCase("build.gradle_test_2.txt", "build.gradle_test_2_expected.txt")]
[TestCase("build.gradle_test_3.txt", "build.gradle_test_3_expected.txt")]
[TestCase("build.gradle_test_4.txt", "build.gradle_test_4_expected.txt")]
[TestCase("build.gradle_test_5.txt", "build.gradle_test_5_expected.txt")]
public void InsertIntoScope_ResultMatchesExpected(string testCaseFileName, string testCaseExpectedFileName)
[TestCase("ApplyMavenCentralFilter/build.gradle_test_1")]
[TestCase("ApplyMavenCentralFilter/build.gradle_test_2")]
[TestCase("ApplyMavenCentralFilter/build.gradle_test_3")]
public void ApplyMavenCentralFilter_ResultMatchesExpected(string testCaseFileName)
{
var assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var testCasePath = Path.Combine(assemblyPath, "TestFiles", "Android", testCaseFileName);
var testCaseExpectedPath = Path.Combine(assemblyPath, "TestFiles", "Android", testCaseExpectedFileName);
var testCasePath = Path.Combine(assemblyPath, "TestFiles", "Android", testCaseFileName + ".txt");
var testCaseExpectedPath =
Path.Combine(assemblyPath, "TestFiles", "Android", testCaseFileName + "_expected.txt");
var expectedGradleContent = File.ReadAllText(testCaseExpectedPath);

var gradleContent = File.ReadAllText(testCasePath);
var sut = new GradleSetup(Logger, GradleProjectPath);

var actualResult = sut.InsertIntoScope(gradleContent, GradleSetup.RepositoryScopeName, GradleSetup.LocalRepository);
var actualResult = sut.ApplyMavenCentralFilter(gradleContent);

StringAssert.AreEqualIgnoringCase(actualResult, expectedGradleContent);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
allprojects
{
buildscript
{
dependencies
{
// If you are changing the Android Gradle Plugin version, make sure it is compatible with the Gradle version preinstalled with Unity
// See which Gradle version is preinstalled with Unity here https://docs.unity3d.com/Manual/android-gradle-overview.html
// See official Gradle and Android Gradle Plugin compatibility table here https://developer.android.com/studio/releases/gradle-plugin#updating-gradle
// To specify a custom Gradle version in Unity, go do "Preferences > External Tools", uncheck "Gradle Installed with Unity (recommended)" and specify a path to a custom Gradle version
classpath 'com.android.tools.build:gradle:4.0.1'
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.31'
}

repositories
{
google()
mavenCentral()
jcenter()
}
}

repositories
{
google()
mavenCentral()
jcenter()
flatDir
{
dirs "${project(':unityLibrary').projectDir}/libs"
}
}
}
Loading