Gradle plugin which helps you analyze the size of your Android apps.

Related tags

Plugin android gradle
Overview

Build Status Maven Release License

Ruler

Ruler is a Gradle plugin which helps you analyze the size of your Android apps.

Motivation

App size is an important metric which directly correlates with business metrics like install conversion rate. Measuring app size is straightforward, but knowing what contributes to it is not. Especially in bigger projects with hundreds or thousands of modules and third-party dependencies. Ruler provides a convenient way to find out how much each module and dependency contributes to the total size of your app by running a single Gradle task.

HTML report

Usage

Follow the following steps to start using Ruler in your project.

Adding the plugin

First you need to add the Ruler Gradle plugin to the buildscript classpath in your top-level build.gradle file:

buildscript {
    repositories {
        maven {
            google()
            mavenCentral()
        }
    }
    dependencies {
        classpath("com.spotify.ruler:ruler-gradle-plugin:0.2.0")
    }
}

You also have to apply the plugin in the build.gradle of your application module:

plugins {
    id("com.android.application")
    id("com.spotify.ruler")
}

Configuring the plugin

When using app bundles, Google Play will generate optimized APKs for each device. This means that the size of an APK depends on the specifications of the device that's downloading it. You can configure which device specifications should be used for the analysis in the build.gradle of your application module:

ruler {
    abi.set("arm64-v8a")
    locale.set("en")
    screenDensity.set(480)
    sdkVersion.set(27)
}

Running the task

Once this is done, analyze<VariantName>Bundle tasks will be added for each of your app variants. Running this task will build the app and generate a HTML report, which you can use to analyze your app size. It will also generate a JSON report, in case you want to further process the data.

Project structure

Ruler is built with Kotlin and contains multiple modules:

  • ruler-gradle-plugin: Core Gradle plugin where the APK parsing, dependency handling and attribution logic lives.
  • ruler-frontend: React template used for the HTML report, built with Kotlin JS.
  • ruler-models: Common models shared between the Gradle plugin and the frontend, built with Kotlin Multiplatform.

Working with this project

The project is set up like a standard Gradle project. You can build it using ./gradlew assemble and run the tests with ./gradlew test.

There is also a sample project, which shows the usage of the plugin. Because the way this sample project is set up, the initial build can fail if you bump the plugin version. To fix this, you have to publish the plugin to your local Maven repository by running ./gradlew publishToMavenLocal -PwithoutSample.

When working on the frontend, you can start a development server by running ./gradlew browserRun, which will show a report filled with dummy data to make development easier.

Code of conduct

This project adheres to the Open Code of Conduct. By participating, you are expected to honor this code.

License

Copyright 2021 Spotify AB

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Comments
  • C++ code report is not stripped

    C++ code report is not stripped

    Current behavior

    Shows native library more than 80mb

    Expected behavior

    Strip C++ code

    Steps to reproduce

    Run project analyze<VariantName>Bundle, which contains C++ code.

    Versions

    1.2.0

    Anything else?

    No response

    bug 
    opened by artakhnoyan 11
  • Incompatibility with DexGuard

    Incompatibility with DexGuard

    Current behavior

    Running the analysis on a project with DexGuard enabled generates an incorrect report.

    A table showing size report differences between Ruler and manually calculated values

    Upon unpacking the .aab generated by analyzeVarianBundle and observing the count of .dex files within, it's also apparent that it hasn't been subjected to DexGuard's obfuscation/minification process.

    Expected behavior

    There should not be a difference between the .aab generated by analyzeVariantBundle and bundleVariant; Ruler should be able to detect if a project is being protected by DexGuard and configure itself accordingly.

    Steps to reproduce

    Execute analyzeVariantBundle on a DexGuard-enabled project.

    Versions

    • DexGuard 9.3.12, configured with overwriteUnprotected = true
    • Ruler 1.3.0
    • Android Gradle Plugin 7.1.2
    • Gradle 7.4.2

    Anything else?

    • https://github.com/getsentry/sentry-android-gradle-plugin/issues/60
    bug 
    opened by MrHadiSatrio 9
  • Granular resource file categorization

    Granular resource file categorization

    Feature description

    Hi! First off, thank you for open-sourcing such an amazing tool. The ease of integration and the amount of insight Ruler provides is just chef's kiss. Kudos!

    That said, I feel like FileType.RESOURCE is too broad of a category. Would it be feasible to make it more granular? So instead of having one umbrella type that is FileType.RESOURCE, we'd have FileType.DRAWABLE, FileType.LAYOUT, FileType.FONT, etc.

    Reasoning

    In a setup where size is being periodically tracked through Ruler, having granular resource types could help addressing regression faster.

    enhancement 
    opened by MrHadiSatrio 7
  • RulerPlugin not Found

    RulerPlugin not Found

    Current behavior

    I have follow the steps that you are mention in your documentation(Adding the Plugin) but the following error occurs:

    A problem occurred evaluating project ':app'.

    Could not find implementation class 'com.spotify.ruler.plugin.RulerPlugin' for plugin 'com.spotify.ruler' specified in jar:file:/C:/Users/miqbal/.gradle/caches/modules-2/files-2.1/com.spotify.ruler/ruler-gradle-plugin/0.2.0/fb28a820c38790c294247a7da63c024430b29c3c/ruler-gradle-plugin-0.2.0-sources.jar!/META-INF/gradle-plugins/com.spotify.ruler.properties. Exception is: org.gradle.api.GradleScriptException: A problem occurred evaluating project ':app'. at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:93) at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl$2.run(DefaultScriptPluginFactory.java:237) at org.gradle.configuration.ProjectScriptTarget.addConfiguration(ProjectScriptTarget.java:77) at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl.apply(DefaultScriptPluginFactory.java:242) at org.gradle.configuration.BuildOperationScriptPlugin$1$1.run(BuildOperationScriptPlugin.java:69) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394) at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158) at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92) at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31) at org.gradle.configuration.BuildOperationScriptPlugin$1.execute(BuildOperationScriptPlugin.java:66) at org.gradle.configuration.BuildOperationScriptPlugin$1.execute(BuildOperationScriptPlugin.java:63) at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.apply(DefaultUserCodeApplicationContext.java:49) at org.gradle.configuration.BuildOperationScriptPlugin.apply(BuildOperationScriptPlugin.java:63) at org.gradle.configuration.project.BuildScriptProcessor$1.run(BuildScriptProcessor.java:45) at org.gradle.internal.Factories$1.create(Factories.java:26) at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:212) at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:193) at org.gradle.configuration.project.BuildScriptProcessor.execute(BuildScriptProcessor.java:42) at org.gradle.configuration.project.BuildScriptProcessor.execute(BuildScriptProcessor.java:26) at org.gradle.configuration.project.ConfigureActionsProjectEvaluator.evaluate(ConfigureActionsProjectEvaluator.java:35) at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject$1.run(LifecycleProjectEvaluator.java:107) at org.gradle.internal.Factories$1.create(Factories.java:26) at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:189) at org.gradle.internal.work.StopShieldingWorkerLeaseService.withLocks(StopShieldingWorkerLeaseService.java:40) at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withProjectLock(DefaultProjectStateRegistry.java:238) at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:232) at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:193) at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject.run(LifecycleProjectEvaluator.java:96) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394) at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158) at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92) at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31) at org.gradle.configuration.project.LifecycleProjectEvaluator.evaluate(LifecycleProjectEvaluator.java:68) at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:699) at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:142) at org.gradle.execution.TaskPathProjectEvaluator.configure(TaskPathProjectEvaluator.java:36) at org.gradle.execution.TaskPathProjectEvaluator.configureHierarchy(TaskPathProjectEvaluator.java:64) at org.gradle.configuration.DefaultProjectsPreparer.prepareProjects(DefaultProjectsPreparer.java:61) at org.gradle.configuration.BuildOperatingFiringProjectsPreparer$ConfigureBuild.run(BuildOperatingFiringProjectsPreparer.java:52) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394) at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158) at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92) at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31) at org.gradle.configuration.BuildOperatingFiringProjectsPreparer.prepareProjects(BuildOperatingFiringProjectsPreparer.java:40) at org.gradle.initialization.DefaultGradleLauncher.prepareProjects(DefaultGradleLauncher.java:207) at org.gradle.initialization.DefaultGradleLauncher.doClassicBuildStages(DefaultGradleLauncher.java:145) at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:130) at org.gradle.initialization.DefaultGradleLauncher.executeTasks(DefaultGradleLauncher.java:110) at org.gradle.internal.invocation.GradleBuildController$1.execute(GradleBuildController.java:60) at org.gradle.internal.invocation.GradleBuildController$1.execute(GradleBuildController.java:57) at org.gradle.internal.invocation.GradleBuildController$3.create(GradleBuildController.java:85) at org.gradle.internal.invocation.GradleBuildController$3.create(GradleBuildController.java:78) at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:189) at org.gradle.internal.work.StopShieldingWorkerLeaseService.withLocks(StopShieldingWorkerLeaseService.java:40) at org.gradle.internal.invocation.GradleBuildController.doBuild(GradleBuildController.java:78) at org.gradle.internal.invocation.GradleBuildController.run(GradleBuildController.java:57) at org.gradle.tooling.internal.provider.runner.ClientProvidedPhasedActionRunner.run(ClientProvidedPhasedActionRunner.java:60) at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35) at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35) at org.gradle.launcher.exec.BuildOutcomeReportingBuildActionRunner.run(BuildOutcomeReportingBuildActionRunner.java:63) at org.gradle.tooling.internal.provider.ValidatingBuildActionRunner.run(ValidatingBuildActionRunner.java:32) at org.gradle.launcher.exec.BuildCompletionNotifyingBuildActionRunner.run(BuildCompletionNotifyingBuildActionRunner.java:39) at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:51) at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:45) at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:416) at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:406) at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158) at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:102) at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36) at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner.run(RunAsBuildOperationBuildActionRunner.java:45) at org.gradle.launcher.exec.InProcessBuildActionExecuter$1.transform(InProcessBuildActionExecuter.java:50) at org.gradle.launcher.exec.InProcessBuildActionExecuter$1.transform(InProcessBuildActionExecuter.java:47) at org.gradle.composite.internal.DefaultRootBuildState.run(DefaultRootBuildState.java:80) at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:47) at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:31) at org.gradle.launcher.exec.BuildTreeScopeBuildActionExecuter.execute(BuildTreeScopeBuildActionExecuter.java:42) at org.gradle.launcher.exec.BuildTreeScopeBuildActionExecuter.execute(BuildTreeScopeBuildActionExecuter.java:28) at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:78) at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:52) at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:60) at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:38) at org.gradle.tooling.internal.provider.SessionScopeBuildActionExecuter.execute(SessionScopeBuildActionExecuter.java:68) at org.gradle.tooling.internal.provider.SessionScopeBuildActionExecuter.execute(SessionScopeBuildActionExecuter.java:38) at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:37) at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:26) at org.gradle.tooling.internal.provider.ParallelismConfigurationBuildActionExecuter.execute(ParallelismConfigurationBuildActionExecuter.java:43) at org.gradle.tooling.internal.provider.ParallelismConfigurationBuildActionExecuter.execute(ParallelismConfigurationBuildActionExecuter.java:29) at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:60) at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:32) at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:55) at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:41) at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:48) at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:32) at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:68) at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:39) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:27) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:35) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:78) at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:75) at org.gradle.util.Swapper.swap(Swapper.java:38) at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:75) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:63) at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:82) at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:52) at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:297) at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48) at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56) Caused by: org.gradle.api.plugins.InvalidPluginException: Could not find implementation class 'com.spotify.ruler.plugin.RulerPlugin' for plugin 'com.spotify.ruler' specified in jar:file:/C:/Users/miqbal/.gradle/caches/modules-2/files-2.1/com.spotify.ruler/ruler-gradle-plugin/0.2.0/fb28a820c38790c294247a7da63c024430b29c3c/ruler-gradle-plugin-0.2.0-sources.jar!/META-INF/gradle-plugins/com.spotify.ruler.properties. at org.gradle.api.internal.plugins.DefaultPluginRegistry$1.load(DefaultPluginRegistry.java:73) at org.gradle.api.internal.plugins.DefaultPluginRegistry$1.load(DefaultPluginRegistry.java:51) at com.google.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3445) at com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2194) at com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2153) at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2043) at com.google.common.cache.LocalCache.get(LocalCache.java:3851) at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:3875) at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4800) at org.gradle.api.internal.plugins.DefaultPluginRegistry.uncheckedGet(DefaultPluginRegistry.java:151) at org.gradle.api.internal.plugins.DefaultPluginRegistry.lookup(DefaultPluginRegistry.java:146) at org.gradle.api.internal.plugins.DefaultPluginRegistry.lookup(DefaultPluginRegistry.java:129) at org.gradle.api.internal.plugins.DefaultPluginRegistry.lookup(DefaultPluginRegistry.java:123) at org.gradle.api.internal.plugins.DefaultPluginManager.apply(DefaultPluginManager.java:132) at org.gradle.api.internal.plugins.DefaultObjectConfigurationAction.applyType(DefaultObjectConfigurationAction.java:162) at org.gradle.api.internal.plugins.DefaultObjectConfigurationAction.access$200(DefaultObjectConfigurationAction.java:41) at org.gradle.api.internal.plugins.DefaultObjectConfigurationAction$3.run(DefaultObjectConfigurationAction.java:96) at org.gradle.api.internal.plugins.DefaultObjectConfigurationAction.execute(DefaultObjectConfigurationAction.java:185) at org.gradle.api.internal.project.AbstractPluginAware.apply(AbstractPluginAware.java:49) at org.gradle.api.internal.project.ProjectScript.apply(ProjectScript.java:37) at org.gradle.api.Script$apply.callCurrent(Unknown Source) at build_37zbdjgmjonvgnucz3ukwjmpm.run(D:\Xiq Android App\Andriod\app\build.gradle:17) at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:91) ... 130 more Caused by: java.lang.ClassNotFoundException: com.spotify.ruler.plugin.RulerPlugin at org.gradle.api.internal.plugins.DefaultPluginRegistry$1.load(DefaultPluginRegistry.java:71) ... 152 more

    Expected behavior

    No response

    Steps to reproduce

    No response

    Versions

    No response

    Anything else?

    No response

    bug 
    opened by miqbal1997 6
  • Add support to reverse DexGuard's resource file name obfuscation

    Add support to reverse DexGuard's resource file name obfuscation

    What has changed

    • We now support reversing DexGuard's resource file name obfuscation.

    Why was it changed

    • Because otherwise attribution for resource files will be broken for projects configured with DexGuard.

    Related issues

    • #87
    • #88
    opened by MrHadiSatrio 5
  • Optimize HTML's Breakdown performance

    Optimize HTML's Breakdown performance

    Signed-off-by: baebae33 [email protected]

    What has changed

    Hi, Im not good at React. But I've tried to optimize HTML's breakdown performance with kotlin-react-virtual. Please review my code.

    Why was it changed

    HTML report performs not well with large list.

    Related issues

    Html file is slow and stopping to respond https://github.com/spotify/ruler/issues/74

    opened by BaeBae33 5
  •  Could not create plugin of type 'RulerPlugin'.

    Could not create plugin of type 'RulerPlugin'.

    Current behavior

    A problem occurred evaluating project ':app'.

    Failed to apply plugin [id 'com.spotify.ruler'] Could not create plugin of type 'RulerPlugin'. > Could not generate a decorated class for type RulerPlugin. > com/android/build/api/variant/ApplicationVariant

    • Try: Run with --info or --debug option to get more log output. Run with --scan to get full insights.

    • Exception is: org.gradle.api.GradleScriptException: A problem occurred evaluating project ':app'. at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:93) at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl$2.run(DefaultScriptPluginFactory.java:237) at org.gradle.configuration.ProjectScriptTarget.addConfiguration(ProjectScriptTarget.java:77) at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl.apply(DefaultScriptPluginFactory.java:242) at org.gradle.configuration.BuildOperationScriptPlugin$1$1.run(BuildOperationScriptPlugin.java:69) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394) at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158) at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92) at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31) at org.gradle.configuration.BuildOperationScriptPlugin$1.execute(BuildOperationScriptPlugin.java:66) at org.gradle.configuration.BuildOperationScriptPlugin$1.execute(BuildOperationScriptPlugin.java:63) at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.apply(DefaultUserCodeApplicationContext.java:49) at org.gradle.configuration.BuildOperationScriptPlugin.apply(BuildOperationScriptPlugin.java:63) at org.gradle.configuration.project.BuildScriptProcessor$1.run(BuildScriptProcessor.java:45) at org.gradle.internal.Factories$1.create(Factories.java:26) at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:245) at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:226) at org.gradle.configuration.project.BuildScriptProcessor.execute(BuildScriptProcessor.java:42) at org.gradle.configuration.project.BuildScriptProcessor.execute(BuildScriptProcessor.java:26) at org.gradle.configuration.project.ConfigureActionsProjectEvaluator.evaluate(ConfigureActionsProjectEvaluator.java:35) at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject$1.run(LifecycleProjectEvaluator.java:102) at org.gradle.internal.Factories$1.create(Factories.java:26) at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:189) at org.gradle.internal.work.StopShieldingWorkerLeaseService.withLocks(StopShieldingWorkerLeaseService.java:40) at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withProjectLock(DefaultProjectStateRegistry.java:271) at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:265) at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:226) at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject.run(LifecycleProjectEvaluator.java:91) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394) at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158) at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92) at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31) at org.gradle.configuration.project.LifecycleProjectEvaluator.evaluate(LifecycleProjectEvaluator.java:63) at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:707) at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:143) at org.gradle.execution.TaskPathProjectEvaluator.configure(TaskPathProjectEvaluator.java:36) at org.gradle.execution.TaskPathProjectEvaluator.configureHierarchy(TaskPathProjectEvaluator.java:64) at org.gradle.configuration.DefaultProjectsPreparer.prepareProjects(DefaultProjectsPreparer.java:61) at org.gradle.configuration.BuildOperatingFiringProjectsPreparer$ConfigureBuild.run(BuildOperatingFiringProjectsPreparer.java:52) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394) at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158) at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92) at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31) at org.gradle.configuration.BuildOperatingFiringProjectsPreparer.prepareProjects(BuildOperatingFiringProjectsPreparer.java:40) at org.gradle.initialization.DefaultGradleLauncher.prepareProjects(DefaultGradleLauncher.java:218) at org.gradle.initialization.DefaultGradleLauncher.doClassicBuildStages(DefaultGradleLauncher.java:155) at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:140) at org.gradle.initialization.DefaultGradleLauncher.executeTasks(DefaultGradleLauncher.java:120) at org.gradle.internal.invocation.GradleBuildController$1.create(GradleBuildController.java:74) at org.gradle.internal.invocation.GradleBuildController$1.create(GradleBuildController.java:67) at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:189) at org.gradle.internal.work.StopShieldingWorkerLeaseService.withLocks(StopShieldingWorkerLeaseService.java:40) at org.gradle.internal.invocation.GradleBuildController.doBuild(GradleBuildController.java:67) at org.gradle.internal.invocation.GradleBuildController.run(GradleBuildController.java:56) at org.gradle.tooling.internal.provider.runner.ClientProvidedPhasedActionRunner.run(ClientProvidedPhasedActionRunner.java:60) at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35) at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35) at org.gradle.launcher.exec.BuildOutcomeReportingBuildActionRunner.run(BuildOutcomeReportingBuildActionRunner.java:63) at org.gradle.tooling.internal.provider.ValidatingBuildActionRunner.run(ValidatingBuildActionRunner.java:32) at org.gradle.launcher.exec.BuildCompletionNotifyingBuildActionRunner.run(BuildCompletionNotifyingBuildActionRunner.java:39) at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:51) at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:45) at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:416) at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:406) at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158) at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:102) at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36) at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner.run(RunAsBuildOperationBuildActionRunner.java:45) at org.gradle.launcher.exec.InProcessBuildActionExecuter$1.transform(InProcessBuildActionExecuter.java:50) at org.gradle.launcher.exec.InProcessBuildActionExecuter$1.transform(InProcessBuildActionExecuter.java:47) at org.gradle.composite.internal.DefaultRootBuildState.run(DefaultRootBuildState.java:80) at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:47) at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:31) at org.gradle.launcher.exec.BuildTreeScopeBuildActionExecuter.execute(BuildTreeScopeBuildActionExecuter.java:42) at org.gradle.launcher.exec.BuildTreeScopeBuildActionExecuter.execute(BuildTreeScopeBuildActionExecuter.java:28) at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:78) at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:52) at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:60) at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:38) at org.gradle.tooling.internal.provider.SessionScopeBuildActionExecuter.execute(SessionScopeBuildActionExecuter.java:68) at org.gradle.tooling.internal.provider.SessionScopeBuildActionExecuter.execute(SessionScopeBuildActionExecuter.java:38) at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:37) at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:26) at org.gradle.tooling.internal.provider.ParallelismConfigurationBuildActionExecuter.execute(ParallelismConfigurationBuildActionExecuter.java:43) at org.gradle.tooling.internal.provider.ParallelismConfigurationBuildActionExecuter.execute(ParallelismConfigurationBuildActionExecuter.java:29) at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:60) at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:32) at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:55) at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:41) at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:48) at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:32) at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:68) at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:39) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:29) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:35) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:78) at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:75) at org.gradle.util.Swapper.swap(Swapper.java:38) at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:75) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:63) at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:82) at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:52) at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:297) at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48) at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56) Caused by: org.gradle.api.internal.plugins.PluginApplicationException: Failed to apply plugin [id 'com.spotify.ruler'] at org.gradle.api.internal.plugins.DefaultPluginManager.doApply(DefaultPluginManager.java:167) at org.gradle.api.internal.plugins.DefaultPluginManager.apply(DefaultPluginManager.java:136) at org.gradle.api.internal.plugins.DefaultObjectConfigurationAction.applyType(DefaultObjectConfigurationAction.java:160) at org.gradle.api.internal.plugins.DefaultObjectConfigurationAction.access$200(DefaultObjectConfigurationAction.java:42) at org.gradle.api.internal.plugins.DefaultObjectConfigurationAction$3.run(DefaultObjectConfigurationAction.java:97) at org.gradle.api.internal.plugins.DefaultObjectConfigurationAction.execute(DefaultObjectConfigurationAction.java:183) at org.gradle.api.internal.project.AbstractPluginAware.apply(AbstractPluginAware.java:49) at org.gradle.api.internal.project.ProjectScript.apply(ProjectScript.java:37) at org.gradle.api.Script$apply$1.callCurrent(Unknown Source)

    Expected behavior

    No response

    Steps to reproduce

    No response

    Versions

    distributionUrl=https://services.gradle.org/distributions/gradle-6.3-all.zip build_tools: "com.android.tools.build:gradle:3.6.1"

    Anything else?

    No response

    bug 
    opened by king-ma1993 5
  • File level ownership matching

    File level ownership matching

    Feature description

    Hi! Would it be feasible to support more granular ownership matching? I.e. down to the file level, such as in a GitHub CODEOWNERS file?

    Maybe even accept a CODEOWNERS file as input for ownership?

    Reasoning

    • Being able to deal with modules with split ownership
    • Not having to maintain two separate lists of ownership
    enhancement 
    opened by mohlson 5
  • Possibility to exclude the lists of files in order to have smaller reports

    Possibility to exclude the lists of files in order to have smaller reports

    What has changed

    Possibility to exclude the list of files of each component or dynamic feature in order to decrease the size of the report (both HTML and JSON).

    The PR is not small so I will try to explain how I did it:

    • Decouple the calculus of insights and ownership from the frontend and let the JsonReporter to precalculate that data, that way, the data will remain the same even if we do not pass any list of files. I have done that in two main commits:
      • 8f3492a42befadaa72e6ef314d7a1b52c8bee0ed: Calculate insights before rendering
      • 6ce88b3e641a2eeaaeffa72237599fd3f57f75c9: Calculate ownership before rendering
    • Add a new property in the RulerExtension class (false by default) to expose a way to exclude files when desired. Done in fbe92f7b4d6210476a299a35c9551b9d9e447dcd
    • Update UI so we show non-expandable rows if there are no files to show. Done in d08998c8ee8d9a277c28cb3d1c17f9ce2407f271

    Files exclusion would be configured like this:

    ruler {
        // … other ruler configuration.
        shouldExcludeFileListing.set(true) // false by default
    }
    
    

    Why was it changed

    There are a couple of open issues at the time this PR is being opened regarding the size of the report. Maybe we could try to improve the generation of the Javascript and HTML code but it would be a small optimization since the main reason behind this problem seems to be the huge amount of files that belongs to the application components.

    Removing that file listing entirely is not an option because it is a very useful information if we are looking for a file that may be heavily increasing our app size.

    The solution to that problem that I’m proposing here is to give the developers the chance to ignore that files if they are struggling with report size on their CI machines (for example). The data reported (overview, breakdown, charts, etc) is not affected by this exclusion, the only difference when enabling this option is that it won’t be possible to expand a component and see its files.

    In case some report without files shows some unexpected download/install size for a component or the whole app, a new execution of the task without this flag would be enough to have the complete report.

    I have tested my proposal in a project with 286 components and the report has been reduced from 15.6MB down to 2.1MB.

    Next steps: If you think this is something that makes sense, an iteration would be to allow a whitelist of components whose files won't be excluded.

    I hope you find this useful :)

    Related issues

    https://github.com/spotify/ruler/issues/96 https://github.com/spotify/ruler/issues/74 (this specific issue is not directly related with file size, but with this new optional config, performance is improved as well)

    opened by yamal-coding 4
  • Allow partial match for ownership identifier

    Allow partial match for ownership identifier

    Feature description

    It would be really convenient to be able to use a partial match of the identifier inside the ownership.yaml.

    For example, I would expect this ownership.yaml:

    - identifier: :modules:core
      owner: core
    
    - identifier: :modules:feature
      owner: feature
    
    - identifier: androidx
      owner: androidx
    
    - identifier: com.google
      owner: google
    

    to match all the dependencies that start with the group androidx to the owner androidx like, for example androidx.compose.ui:ui and androidx.core:core, and all the module that start with :modules:core to the owner core: image

    Reasoning

    Keeping track of all the AndroidX and Google library would require too much effort and would be prone to error. It would be way better to be able to a partial matching to quickly group together these kind of libraries. It would be also nice to use a similar logic for the gradle modules.

    enhancement 
    opened by leinardi 4
  • File/directory does not exist: build/intermediates/runtime_library_classes_jar/debug/classes.jar

    File/directory does not exist: build/intermediates/runtime_library_classes_jar/debug/classes.jar

    Current behavior

    Command ./gradlew analyzeBundle fails on 99% because: File/directory does not exist: build/intermediates/runtime_library_classes_jar/debug/classes.jar

    This is happening with local modules.

    In app build.gradle, I am using implementation project(':')

    Why gradle doesn't create runtime_library_classes_jar in this case? Thanks!

    Expected behavior

    The command shouldn't fail with an error

    Steps to reproduce

    Run command ./gradlew analyzeBundle

    Versions

    'com.android.tools.build:gradle:7.2.1' 'com.spotify.ruler:ruler-gradle-plugin:1.4.0'

    Anything else?

    No response

    bug 
    opened by marko-twineahlth 3
  • Execution failed for IdentityTransform, File/directory does not exist: intermediates/runtime_library_classes_jar/release/classes.jar

    Execution failed for IdentityTransform, File/directory does not exist: intermediates/runtime_library_classes_jar/release/classes.jar

    Current behavior

    Plugin cannot find classes.jar

    Failed to transform classes.jar (project :features:checkout-v2-data:cash-drawer:public) to match attributes {artifactType=android-classes, com.android.build.api.attributes.AgpVersionAttr=7.2.2, com.android.build.api.attributes.BuildTypeAttr=release, com.android.build.gradle.internal.attributes.VariantAttr=release, org.gradle.category=library, org.gradle.jvm.environment=android, org.gradle.libraryelements=jar, org.gradle.usage=java-runtime, org.jetbrains.kotlin.platform.type=androidJvm}.
          > Execution failed for IdentityTransform: /Users/malcolmn/Development/android-register/features/checkout-v2-data/cash-drawer/public/build/intermediates/runtime_library_classes_jar/release/classes.jar.
             > File/directory does not exist: /Users/malcolmn/Development/android-register/features/checkout-v2-data/cash-drawer/public/build/intermediates/runtime_library_classes_jar/release/classes.jar
    

    Expected behavior

    Plugin should be able to find classes.jar

    Steps to reproduce

    ./gradlew analyzeReleaseBundle & ./gradlew analyzeDebugBundle

    Versions

    AGP = 7.2.2 Gradle = 7.5 Ruler = 1.4.0

    Anything else?

    It looks like DependencyParser is looking at RuntimeClasspath, but it seems like for our project and sub projects we are only generating CompileClasspath for certain modules. Should we update DependencyParser to look at other places for classes.jar, or change our build scripts to also generate the runtimeClasspath jars for each module?

    bug 
    opened by malcolmn-sq 0
  • Support shared ownership of components

    Support shared ownership of components

    Feature description

    When a component is declared to have multiple owners, Ruler would report it as such. On the JSON, owners can be outputted as an array. While on the HTML, we can present them in a way so that the component size would be divided equally amongst the teams that own it.

    Here's how it could look like on the HTML:

    | Breakdown | Ownership (main-team) | Ownership (lib-team) | Ownership (dynamic-team) | | --- | --- | --- | --- | | Breakdown | Main Team Ownership | Lib Team Ownership | Dynamic Team Ownership |

    As for the JSON:

    {
        "name": "com.spotify.ruler.sample",
        "version": "1.0",
        "variant": "debug",
        "downloadSize": 632328,
        "installSize": 635818,
        "components": [
            {
                "name": "org.jetbrains.kotlin:kotlin-stdlib:1.6.20",
                "type": "EXTERNAL",
                "downloadSize": 614171,
                "installSize": 616811,
                "files": [
                    {
                        "name": "kotlin.collections.ArraysKt___ArraysKt",
                        "type": "CLASS",
                        "downloadSize": 151781,
                        "installSize": 152428,
                        "owners": [
                            "lib-team",
                            "main-team",
                            "dynamic-team"
                        ]
                    }
                ],
                "owners": [
                    "lib-team",
                    "main-team",
                    "dynamic-team"
                ]
            }
        ]
    }
    

    Reasoning

    Not every organization has the privilege of having a dedicated team to maintain third-party dependencies / libraries. In such cases, it'd be best to attribute them as something that every teams would own.

    enhancement 
    opened by MrHadiSatrio 5
  • Size of HTML reports

    Size of HTML reports

    Current behavior

    Every ruler report we generate is 18 MB+. Doing this frequently in CI for multiple apps uses up a lot of disk space.

    Expected behavior

    Small and neat HTML reports. :)

    Steps to reproduce

    No response

    Versions

    No response

    Anything else?

    No response

    bug help wanted 
    opened by pocmo 3
  • Dynamic feature

    Dynamic feature "install-time" not included on report

    Current behavior

    dynamic feature module install-time not included on report.

    Expected behavior

    Dynamic feature module install time should also visible on report and adding app size.

    Steps to reproduce

    on DFM module change

           <dist:delivery>
               <dist:on-demand/>
           </dist:delivery>
    

    to

           <dist:delivery>
               <dist:install-time />
           </dist:delivery>
    

    Please find my attachment for sample MyApplication20.zip

    Versions

    1.4.0

    Anything else?

    No response

    bug 
    opened by doniwinata0309 2
  • Discrepancy in install size

    Discrepancy in install size

    Current behavior

    There is a discrepancy in readings for install size. Running sample:app:analyzeDebug yields 620.9 KB on the report, while "App size" readings on device shows 676 KB.

    Device configurations are synced with the result of bundletool get-device-spec.

    | Ruler Report | On Device | | --- | --- | | Screen Shot 2022-08-19 at 10 39 45 | Screen Shot 2022-08-19 at 10 39 45 |

    I'm also observing a bigger diff of almost 3x (62.8 MB reported vs 192 MB on-device) on my project.

    Expected behavior

    There shouldn't be that big of a difference between the number Ruler reports for Install Size and the actual readings on a device.

    Steps to reproduce

    1. Ensure that Ruler is properly setup to run with the target device configuration
    2. Run ./gradlew sample:app:analyzeDebugBundle
    3. Run ./gradlew sample:app:installDebug
    4. Observe the numbers reported and one from App Info screen

    Versions

    • Ruler 1.3.0
    • Android Gradle Plugin 7.2.1
    • Gradle 7.5

    Anything else?

    No response

    bug 
    opened by MrHadiSatrio 3
  • Please publish to Gradle Plugin Repository

    Please publish to Gradle Plugin Repository

    Feature description

    Please publish to Gradle plugin repo so we can use id correctly. Or correct the plugin name so it can be resolved correctly: com.spotify.ruler:com.spotify.ruler:version

    Thanks!

    Reasoning

    Work around:

    settings.gradle.kts

    pluginManagement {
        resolutionStrategy {
            eachPlugin {
                //https://github.com/spotify/ruler/issues/85
                if (requested.id.id == "com.spotify.ruler") {
                    useModule("com.spotify.ruler:ruler-gradle-plugin:1.3.0")
                }
            }
        }
    }
    
    enhancement 
    opened by chrisjenx 3
Releases(v1.4.0)
Owner
Spotify
Spotify
gradle-android-scala-plugin adds scala language support to official gradle android plugin

gradle-android-scala-plugin gradle-android-scala-plugin adds scala language support to official gradle android plugin. See also sample projects at htt

saturday06 345 Dec 10, 2022
Ownership-gradle-plugin - Gradle code ownership verification plugin

Gradle code ownership verification plugin A gradle plugin that will verify owner

null 4 Dec 15, 2022
Gradle plugin which validates the licenses of your dependency graph match what you expect

Licensee ?? ?? A Gradle plugin which validates the licenses of your dependency graph match what you expect, or it fails your build! Jump to: Introduct

Cash App 506 Jan 8, 2023
Helper to upload Gradle Android Artifacts, Gradle Java Artifacts and Gradle Kotlin Artifacts to Maven repositories (JCenter, Maven Central, Corporate staging/snapshot servers and local Maven repositories).

GradleMavenPush Helper to upload Gradle Android Artifacts, Gradle Java Artifacts and Gradle Kotlin Artifacts to Maven repositories (JCenter, Maven Cen

 Vorlonsoft LLC 21 Oct 3, 2022
Gradle plugin which downloads and manages your Android SDK.

DEPRECATED This plugin is deprecated and is no longer being developed. Tools and dependencies are automatically downloaded using version 2.2.0 of the

Jake Wharton 1.4k Dec 29, 2022
A Gradle plugin to support the Groovy language for building Android apps

Groovy language support for Android Deprecated: This plugin has been deprecated in favor of Kotlin which has the full support of JetBrains and Google.

Groovy programming language 853 Dec 21, 2022
Add a different ribbon to each of your Android app variants using this gradle plugin. Of course, configure it as you will

Easylauncher gradle plugin for Android Modify the launcher icon of each of your app-variants using simple gradle rules. Add ribbons of any color, over

Mikel 960 Dec 18, 2022
A powerful Gradle Plugin to help you demonstrate your android app

English | 简体中文 sample-gradle-plugin ?? A powerful Gradle Plugin to help you demonstrate your android app. We often write demo applications that contai

Yoo Zhou 12 Nov 5, 2022
A Gradle plugin for providing your secrets to your Android project.

Secrets Gradle Plugin for Android A Gradle plugin for providing your secrets securely to your Android project. This Gradle plugin reads secrets from a

Google 560 Jan 8, 2023
GPP is Android's unofficial release automation Gradle Plugin. It can do anything from building, uploading, and then promoting your App Bundle or APK to publishing app listings and other metadata.

Gradle Play Publisher Gradle Play Publisher is Android's unofficial release automation Gradle Plugin. It can do anything from building, uploading, and

null 3.9k Dec 30, 2022
A Gradle plugin to report the number of method references in your APK on every build.

Dexcount Gradle Plugin A Gradle plugin to report the number of method references in your APK, AAR, or java module. This helps you keep tabs on the gro

Keepsafe 3k Dec 29, 2022
This plugin helps tp build GraphQL applications in Java using the DGS framework

dgs-intellij-plugin This plugin helps tp build GraphQL applications in Java using the DGS framework. The DGS Framework is open sourced by Netflix and

Netflix, Inc. 16 Nov 3, 2022
A flutter plugin through which you can get audios, videos, or images from the local storage.

Getter Made flutter easier. Description A flutter plugin through which you can get audios, videos or images from the local storage. Dependencies Befor

Younes Lagmah 3 Sep 11, 2021
K6-intellij-plugin - IntelliJ-based Plugin to run k6 tests locally or in the k6 Cloud from your IntelliJ IDE

IntelliJ-based Plugin to run k6 tests locally or in the k6 Cloud from your Intel

Mikhail Bolotov 8 Jan 2, 2023
This is an android studio plugin that allows you to creates new color in hex format based on a percentage (0-100) and a base color you specify.

alpha-color Description This is an android studio plugin that allows you to creates new color in hex format based on a percentage (0-100) and a base c

null 1 Nov 12, 2021
A Gradle Plugin that removes unused resources in Android projects.

#Lint Cleaner Plugin Removes unused resources reported by Android lint including strings, colors and dimensions. Depracated As of Android Studio 2.0+

Marco RS 705 Nov 25, 2022
Gradle plugin for Google Protocol Buffers

Gradle Protobuf Plugin Author: Andrew Kroh Download: See maven central License: Apache License, Version 2.0 Requirements: Java 1.5+ Google Protocol Bu

Andrew Kroh 36 May 29, 2022
IntelliJ plugin that provides some useful utilities to support the daily work with Gradle.

IntelliJ Gradle Utilities Plugin This IntelliJ plugin provides some useful utilities to support the daily work with Gradle. It's available on the offi

Marcel Kliemannel 6 Jul 29, 2022