Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Keeping your Gradle builds in top shape

Keeping your Gradle builds in top shape

Which tasks are the slowest running tasks?
Who has the slowest builds on my team?
Which task is getting the most cache misses?
These questions and more can be easily answered with the new Gradle Enterprise API.
Come learn about how to automatically check your build's cacheability and find out who on your team is suffering the most painful builds.

Nelson Osacky

July 07, 2022
Tweet

More Decks by Nelson Osacky

Other Decks in Programming

Transcript

  1. Me • Previously Android Engineer • Large projects • SoundCloud

    • Square • Small startups • Gradle Plugin Maintainer • Fladle - Easily Scale Instrumentation Tests on Firebase https://github.com/runningcode/fladle • Gradle Doctor - Actionable Insights for your build https://github.com/runningcode/gradle-doctor Lead Solutions Engineer
  2. I see a lot of builds from 10 person teams

    to 1000+ Android, Gradle and Maven
  3. Cost of Builds 60s waste * 50 builds / day

    * 50 devs 
 = 42 hours lost / day
  4. Cost of Builds 60s waste * 50 builds / day

    * 50 devs 
 = 42 hours lost / day not including lost focus https://gradle.com/roi-calculator
  5. Cost of Builds 60s waste * 50 builds / day

    * 50 devs 
 = 42 hours lost / day hire 5 new people without paying them! no recruiting https://gradle.com/roi-calculator
  6. Slow builds are Tech Debt And it always pays off

    And is easy to justify working on it
  7. Build Validation Scripts 1. Verify up-to-date checking (incremental build) 2.

    Verify local cacheability 3. Verify cache relocateability 4. Verify CI to CI cache relocateability 5. Verify CI to Local machine cache relocateability
  8. ./01-validate-incremental-building.sh 1. Disable build caching completely 2. Run the build

    with a typical task invocation including the 'clean' task 3. Run the build with the same task invocation but without the 'clean' task 4. Determine which tasks are still executed in the second run and why 5. Assess which of the executed tasks are worth improving 6. Fix identified tasks Step 1-3 are automated. 4-6 are manual.
  9. ./01-validate-incremental-building.sh Summary ------- Project: android-cache-fix-gradle-plugin Git repo: [email protected]:gradle/android-cache-fix-gradle-plugin Git branch:

    main Git commit id: 88f52ce8c3b44a0563e21682866bc3f704107a52 Project dir: <root directory> Gradle tasks: assemble Gradle arguments: <none> Experiment: 01 Validate incremental building Experiment id: exp1-gradle Experiment run id: 6296b790 Experiment artifact dir: .data/01-validate-incremental-building/20220531T194920-6296b790 Build scan first build: https://ge.solutions-team.gradle.com/s/yj6btgctdvif6 Build scan second build: https://ge.solutions-team.gradle.com/s/qocthanshmjdm Investigation Quick Links ------------------------- Task execution overview: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/performance/execution Executed tasks timeline: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/timeline? outcome=SUCCESS,FAILED&sort=longest Task inputs comparison: https://ge.solutions-team.gradle.com/c/yj6btgctdvif6/qocthanshmjdm/task-inputs Command Line Invocation ----------------------- ./01-validate-incremental-building.sh -r https://github.com/gradle/android-cache-fix-gradle-plugin -t assemble Once you have addressed the issues surfaced in build scans and pushed the changes to your Git repository, you can rerun the experiment and start over the cycle of run → measure → improve → run.
  10. ./01-validate-incremental-building.sh Summary ------- Project: android-cache-fix-gradle-plugin Git repo: [email protected]:gradle/android-cache-fix-gradle-plugin Git branch:

    main Git commit id: 88f52ce8c3b44a0563e21682866bc3f704107a52 Project dir: <root directory> Gradle tasks: assemble Gradle arguments: <none> Experiment: 01 Validate incremental building Experiment id: exp1-gradle Experiment run id: 6296b790 Experiment artifact dir: .data/01-validate-incremental-building/20220531T194920-6296b790 Build scan first build: https://ge.solutions-team.gradle.com/s/yj6btgctdvif6 Build scan second build: https://ge.solutions-team.gradle.com/s/qocthanshmjdm
  11. ./01-validate-incremental-building.sh Summary ------- Project: android-cache-fix-gradle-plugin Git repo: [email protected]:gradle/android-cache-fix-gradle-plugin Git branch:

    main Git commit id: 88f52ce8c3b44a0563e21682866bc3f704107a52 Project dir: <root directory> Gradle tasks: assemble Gradle arguments: <none> Experiment: 01 Validate incremental building Experiment id: exp1-gradle Experiment run id: 6296b790 Experiment artifact dir: .data/01-validate-incremental-building/20220531T194920-6296b790 Build scan first build: https://ge.solutions-team.gradle.com/s/yj6btgctdvif6 Build scan second build: https://ge.solutions-team.gradle.com/s/qocthanshmjdm
  12. ./01-validate-incremental-building.sh Summary ------- Project: android-cache-fix-gradle-plugin Git repo: [email protected]:gradle/android-cache-fix-gradle-plugin Git branch:

    main Git commit id: 88f52ce8c3b44a0563e21682866bc3f704107a52 Project dir: <root directory> Gradle tasks: assemble Gradle arguments: <none> Experiment: 01 Validate incremental building Experiment id: exp1-gradle Experiment run id: 6296b790 Experiment artifact dir: .data/01-validate-incremental-building/20220531T194920-6296b790 Build scan first build: https://ge.solutions-team.gradle.com/s/yj6btgctdvif6 Build scan second build: https://ge.solutions-team.gradle.com/s/qocthanshmjdm
  13. ./01-validate-incremental-building.sh Summary ------- Project: android-cache-fix-gradle-plugin Git repo: [email protected]:gradle/android-cache-fix-gradle-plugin Git branch:

    main Git commit id: 88f52ce8c3b44a0563e21682866bc3f704107a52 Project dir: <root directory> Gradle tasks: assemble Gradle arguments: <none> Experiment: 01 Validate incremental building Experiment id: exp1-gradle Experiment run id: 6296b790 Experiment artifact dir: .data/01-validate-incremental-building/20220531T194920-6296b790 Build scan first build: https://ge.solutions-team.gradle.com/s/yj6btgctdvif6 Build scan second build: https://ge.solutions-team.gradle.com/s/qocthanshmjdm
  14. ./01-validate-incremental-building.sh Summary ------- Project: android-cache-fix-gradle-plugin Git repo: [email protected]:gradle/android-cache-fix-gradle-plugin Git branch:

    main Git commit id: 88f52ce8c3b44a0563e21682866bc3f704107a52 Project dir: <root directory> Gradle tasks: assemble Gradle arguments: <none> Experiment: 01 Validate incremental building Experiment id: exp1-gradle Experiment run id: 6296b790 Experiment artifact dir: .data/01-validate-incremental-building/20220531T194920-6296b790 Build scan first build: https://ge.solutions-team.gradle.com/s/yj6btgctdvif6 Build scan second build: https://ge.solutions-team.gradle.com/s/qocthanshmjdm
  15. ./01-validate-incremental-building.sh Summary ------- Project: android-cache-fix-gradle-plugin Git repo: [email protected]:gradle/android-cache-fix-gradle-plugin Git branch:

    main Git commit id: 88f52ce8c3b44a0563e21682866bc3f704107a52 Project dir: <root directory> Gradle tasks: assemble Gradle arguments: <none> Experiment: 01 Validate incremental building Experiment id: exp1-gradle Experiment run id: 6296b790 Experiment artifact dir: .data/01-validate-incremental-building/20220531T194920-6296b790 Build scan first build: https://ge.solutions-team.gradle.com/s/yj6btgctdvif6 Build scan second build: https://ge.solutions-team.gradle.com/s/qocthanshmjdm
  16. ./01-validate-incremental-building.sh Summary ------- Project: android-cache-fix-gradle-plugin Git repo: [email protected]:gradle/android-cache-fix-gradle-plugin Git branch:

    main Git commit id: 88f52ce8c3b44a0563e21682866bc3f704107a52 Project dir: <root directory> Gradle tasks: assemble Gradle arguments: <none> Experiment: 01 Validate incremental building Experiment id: exp1-gradle Experiment run id: 6296b790 Experiment artifact dir: .data/01-validate-incremental-building/20220531T194920-6296b790 Build scan first build: https://ge.solutions-team.gradle.com/s/yj6btgctdvif6 Build scan second build: https://ge.solutions-team.gradle.com/s/qocthanshmjdm
  17. ./01-validate-incremental-building.sh Summary ------- Project: android-cache-fix-gradle-plugin Git repo: [email protected]:gradle/android-cache-fix-gradle-plugin Git branch:

    main Git commit id: 88f52ce8c3b44a0563e21682866bc3f704107a52 Project dir: <root directory> Gradle tasks: assemble Gradle arguments: <none> Experiment: 01 Validate incremental building Experiment id: exp1-gradle Experiment run id: 6296b790 Experiment artifact dir: .data/01-validate-incremental-building/20220531T194920-6296b790 Build scan first build: https://ge.solutions-team.gradle.com/s/yj6btgctdvif6 Build scan second build: https://ge.solutions-team.gradle.com/s/qocthanshmjdm
  18. ./01-validate-incremental-building.sh Summary ------- Project: android-cache-fix-gradle-plugin Git repo: [email protected]:gradle/android-cache-fix-gradle-plugin Git branch:

    main Git commit id: 88f52ce8c3b44a0563e21682866bc3f704107a52 Project dir: <root directory> Gradle tasks: assemble Gradle arguments: <none> Experiment: 01 Validate incremental building Experiment id: exp1-gradle Experiment run id: 6296b790 Experiment artifact dir: .data/01-validate-incremental-building/20220531T194920-6296b790 Build scan first build: https://ge.solutions-team.gradle.com/s/yj6btgctdvif6 Build scan second build: https://ge.solutions-team.gradle.com/s/qocthanshmjdm
  19. ./01-validate-incremental-building.sh Summary ------- Project: android-cache-fix-gradle-plugin Git repo: [email protected]:gradle/android-cache-fix-gradle-plugin Git branch:

    main Git commit id: 88f52ce8c3b44a0563e21682866bc3f704107a52 Project dir: <root directory> Gradle tasks: assemble Gradle arguments: <none> Experiment: 01 Validate incremental building Experiment id: exp1-gradle Experiment run id: 6296b790 Experiment artifact dir: .data/01-validate-incremental-building/20220531T194920-6296b790 Build scan first build: https://ge.solutions-team.gradle.com/s/yj6btgctdvif6 Build scan second build: https://ge.solutions-team.gradle.com/s/qocthanshmjdm
  20. ./01-validate-incremental-building.sh Summary ------- Project: android-cache-fix-gradle-plugin Git repo: [email protected]:gradle/android-cache-fix-gradle-plugin Git branch:

    main Git commit id: 88f52ce8c3b44a0563e21682866bc3f704107a52 Project dir: <root directory> Gradle tasks: assemble Gradle arguments: <none> Experiment: 01 Validate incremental building Experiment id: exp1-gradle Experiment run id: 6296b790 Experiment artifact dir: .data/01-validate-incremental-building/20220531T194920-6296b790 Build scan first build: https://ge.solutions-team.gradle.com/s/yj6btgctdvif6 Build scan second build: https://ge.solutions-team.gradle.com/s/qocthanshmjdm
  21. ./01-validate-incremental-building.sh Summary ------- Project: android-cache-fix-gradle-plugin Git repo: [email protected]:gradle/android-cache-fix-gradle-plugin Git branch:

    main Git commit id: 88f52ce8c3b44a0563e21682866bc3f704107a52 Project dir: <root directory> Gradle tasks: assemble Gradle arguments: <none> Experiment: 01 Validate incremental building Experiment id: exp1-gradle Experiment run id: 6296b790 Experiment artifact dir: .data/01-validate-incremental-building/20220531T194920-6296b790 Build scan first build: https://ge.solutions-team.gradle.com/s/yj6btgctdvif6 Build scan second build: https://ge.solutions-team.gradle.com/s/qocthanshmjdm
  22. ./01-validate-incremental-building.sh Summary ------- Project: android-cache-fix-gradle-plugin Git repo: [email protected]:gradle/android-cache-fix-gradle-plugin Git branch:

    main Git commit id: 88f52ce8c3b44a0563e21682866bc3f704107a52 Project dir: <root directory> Gradle tasks: assemble Gradle arguments: <none> Experiment: 01 Validate incremental building Experiment id: exp1-gradle Experiment run id: 6296b790 Experiment artifact dir: .data/01-validate-incremental-building/20220531T194920-6296b790 Build scan first build: https://ge.solutions-team.gradle.com/s/yj6btgctdvif6 Build scan second build: https://ge.solutions-team.gradle.com/s/qocthanshmjdm
  23. .data/01-validate-incremental-building/20220531T194920-6296b790 ├── first-build_gradle-build-scan-quickstart │ ├── README.md │ ├── build │

    │ ├── classes │ │ │ └── java │ │ │ └── main │ │ │ └── example │ │ │ └── Example.class │ │ └── libs │ │ └── gradle-build-scan-quickstart.jar │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ ├── main │ │ └── java │ │ └── example │ │ └── Example.java │ └── test │ └── java │ └── example │ └── ExampleTest.java ├── second-build_gradle-build-scan-quickstart │ ├── README.md │ ├── build │ │ ├── classes │ │ │ └── java │ │ │ └── main │ │ │ └── example │ │ │ └── Example.class │ │ ├── libs > tree .data/01-validate-incremental-building/20220531T194920-6296b790
  24. > tree .data/01-validate-incremental-building/20220531T194920-6296b790 .data/01-validate-incremental-building/20220531T194920-6296b790 ├── first-build_gradle-build-scan-quickstart │ ├── README.md │

    ├── build │ │ ├── classes │ │ │ └── java │ │ │ └── main │ │ │ └── example │ │ │ └── Example.class │ │ └── libs │ │ └── gradle-build-scan-quickstart.jar │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ ├── main │ │ └── java │ │ └── example │ │ └── Example.java │ └── test │ └── java │ └── example │ └── ExampleTest.java ├── second-build_gradle-build-scan-quickstart │ ├── README.md │ ├── build │ │ ├── classes │ │ │ └── java │ │ │ └── main │ │ │ └── example │ │ │ └── Example.class │ │ ├── libs
  25. > tree .data/01-validate-incremental-building/20220531T194920-6296b790 .data/01-validate-incremental-building/20220531T194920-6296b790 ├── first-build_gradle-build-scan-quickstart │ ├── README.md │

    ├── build │ │ ├── classes │ │ │ └── java │ │ │ └── main │ │ │ └── example │ │ │ └── Example.class │ │ └── libs │ │ └── gradle-build-scan-quickstart.jar │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ ├── main │ │ └── java │ │ └── example │ │ └── Example.java │ └── test │ └── java │ └── example │ └── ExampleTest.java ├── second-build_gradle-build-scan-quickstart │ ├── README.md │ ├── build │ │ ├── classes │ │ │ └── java │ │ │ └── main │ │ │ └── example │ │ │ └── Example.class │ │ ├── libs
  26. > tree .data/01-validate-incremental-building/20220531T194920-6296b790 .data/01-validate-incremental-building/20220531T194920-6296b790 ├── first-build_gradle-build-scan-quickstart │ ├── README.md │

    ├── build │ │ ├── classes │ │ │ └── java │ │ │ └── main │ │ │ └── example │ │ │ └── Example.class │ │ └── libs │ │ └── gradle-build-scan-quickstart.jar │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ ├── main │ │ └── java │ │ └── example │ │ └── Example.java │ └── test │ └── java │ └── example │ └── ExampleTest.java ├── second-build_gradle-build-scan-quickstart │ ├── README.md │ ├── build │ │ ├── classes │ │ │ └── java │ │ │ └── main │ │ │ └── example │ │ │ └── Example.class │ │ ├── libs
  27. ./01-validate-incremental-building.sh Summary ------- Project: android-cache-fix-gradle-plugin Git repo: [email protected]:gradle/android-cache-fix-gradle-plugin Git branch:

    main Git commit id: 88f52ce8c3b44a0563e21682866bc3f704107a52 Project dir: <root directory> Gradle tasks: assemble Gradle arguments: <none> Experiment: 01 Validate incremental building Experiment id: exp1-gradle Experiment run id: 6296b790 Experiment artifact dir: .data/01-validate-incremental-building/20220531T194920-6296b790 Build scan first build: https://ge.solutions-team.gradle.com/s/yj6btgctdvif6 Build scan second build: https://ge.solutions-team.gradle.com/s/qocthanshmjdm
  28. ./01-validate-incremental-building.sh Summary ------- Project: android-cache-fix-gradle-plugin Git repo: [email protected]:gradle/android-cache-fix-gradle-plugin Git branch:

    main Git commit id: 88f52ce8c3b44a0563e21682866bc3f704107a52 Project dir: <root directory> Gradle tasks: assemble Gradle arguments: <none> Experiment: 01 Validate incremental building Experiment id: exp1-gradle Experiment run id: 6296b790 Experiment artifact dir: .data/01-validate-incremental-building/20220531T194920-6296b790 Build scan first build: https://ge.solutions-team.gradle.com/s/yj6btgctdvif6 Build scan second build: https://ge.solutions-team.gradle.com/s/qocthanshmjdm
  29. ./01-validate-incremental-building.sh Summary ------- Project: android-cache-fix-gradle-plugin Git repo: [email protected]:gradle/android-cache-fix-gradle-plugin Git branch:

    main Git commit id: 88f52ce8c3b44a0563e21682866bc3f704107a52 Project dir: <root directory> Gradle tasks: assemble Gradle arguments: <none> Experiment: 01 Validate incremental building Experiment id: exp1-gradle Experiment run id: 6296b790 Experiment artifact dir: .data/01-validate-incremental-building/20220531T194920-6296b790 Build scan first build: https://ge.solutions-team.gradle.com/s/yj6btgctdvif6 Build scan second build: https://ge.solutions-team.gradle.com/s/qocthanshmjdm
  30. ./01-validate-incremental-building.sh Summary ------- Project: android-cache-fix-gradle-plugin Git repo: [email protected]:gradle/android-cache-fix-gradle-plugin Git branch:

    main Git commit id: 88f52ce8c3b44a0563e21682866bc3f704107a52 Project dir: <root directory> Gradle tasks: assemble Gradle arguments: <none> Experiment: 01 Validate incremental building Experiment id: exp1-gradle Experiment run id: 6296b790 Experiment artifact dir: .data/01-validate-incremental-building/20220531T194920-6296b790 Build scan first build: https://ge.solutions-team.gradle.com/s/yj6btgctdvif6 Build scan second build: https://ge.solutions-team.gradle.com/s/qocthanshmjdm Investigation Quick Links ------------------------- Task execution overview: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/performance/execution Executed tasks timeline: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/timeline? outcome=SUCCESS,FAILED&sort=longest Task inputs comparison: https://ge.solutions-team.gradle.com/c/yj6btgctdvif6/qocthanshmjdm/task-inputs Command Line Invocation ----------------------- ./01-validate-incremental-building.sh -r https://github.com/gradle/android-cache-fix-gradle-plugin -t assemble Once you have addressed the issues surfaced in build scans and pushed the changes to your Git repository, you can rerun the experiment and start over the cycle of run → measure → improve → run.
  31. ./01-validate-incremental-building.sh Investigation Quick Links ------------------------- Task execution overview: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/ performance/execution

    Executed tasks timeline: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/ timeline?outcome=SUCCESS,FAILED&sort=longest Task inputs comparison: https://ge.solutions-team.gradle.com/c/yj6btgctdvif6/ qocthanshmjdm/task-inputs
  32. ./01-validate-incremental-building.sh Investigation Quick Links ------------------------- Task execution overview: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/ performance/execution

    Executed tasks timeline: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/ timeline?outcome=SUCCESS,FAILED&sort=longest Task inputs comparison: https://ge.solutions-team.gradle.com/c/yj6btgctdvif6/ qocthanshmjdm/task-inputs
  33. ./01-validate-incremental-building.sh Investigation Quick Links ------------------------- Task execution overview: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/ performance/execution

    Executed tasks timeline: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/ timeline?outcome=SUCCESS,FAILED&sort=longest Task inputs comparison: https://ge.solutions-team.gradle.com/c/yj6btgctdvif6/ qocthanshmjdm/task-inputs
  34. ./01-validate-incremental-building.sh Investigation Quick Links ------------------------- Task execution overview: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/ performance/execution

    Executed tasks timeline: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/ timeline?outcome=SUCCESS,FAILED&sort=longest Task inputs comparison: https://ge.solutions-team.gradle.com/c/yj6btgctdvif6/ qocthanshmjdm/task-inputs
  35. ./01-validate-incremental-building.sh Investigation Quick Links ------------------------- Task execution overview: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/ performance/execution

    Executed tasks timeline: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/ timeline?outcome=SUCCESS,FAILED&sort=longest Task inputs comparison: https://ge.solutions-team.gradle.com/c/yj6btgctdvif6/ qocthanshmjdm/task-inputs
  36. ./01-validate-incremental-building.sh Investigation Quick Links ------------------------- Task execution overview: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/ performance/execution

    Executed tasks timeline: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/ timeline?outcome=SUCCESS,FAILED&sort=longest Task inputs comparison: https://ge.solutions-team.gradle.com/c/yj6btgctdvif6/ qocthanshmjdm/task-inputs
  37. ./01-validate-incremental-building.sh Investigation Quick Links ------------------------- Task execution overview: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/ performance/execution

    Executed tasks timeline: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/ timeline?outcome=SUCCESS,FAILED&sort=longest Task inputs comparison: https://ge.solutions-team.gradle.com/c/yj6btgctdvif6/ qocthanshmjdm/task-inputs
  38. ./01-validate-incremental-building.sh Investigation Quick Links ------------------------- Task execution overview: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/ performance/execution

    Executed tasks timeline: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/ timeline?outcome=SUCCESS,FAILED&sort=longest Task inputs comparison: https://ge.solutions-team.gradle.com/c/yj6btgctdvif6/ qocthanshmjdm/task-inputs
  39. ./01-validate-incremental-building.sh Summary ------- Project: android-cache-fix-gradle-plugin Git repo: [email protected]:gradle/android-cache-fix-gradle-plugin Git branch:

    main Git commit id: 88f52ce8c3b44a0563e21682866bc3f704107a52 Project dir: <root directory> Gradle tasks: assemble Gradle arguments: <none> Experiment: 01 Validate incremental building Experiment id: exp1-gradle Experiment run id: 6296b790 Experiment artifact dir: .data/01-validate-incremental-building/20220531T194920-6296b790 Build scan first build: https://ge.solutions-team.gradle.com/s/yj6btgctdvif6 Build scan second build: https://ge.solutions-team.gradle.com/s/qocthanshmjdm Investigation Quick Links ------------------------- Task execution overview: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/performance/execution Executed tasks timeline: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/timeline? outcome=SUCCESS,FAILED&sort=longest Task inputs comparison: https://ge.solutions-team.gradle.com/c/yj6btgctdvif6/qocthanshmjdm/task-inputs Command Line Invocation ----------------------- ./01-validate-incremental-building.sh -r https://github.com/gradle/android-cache-fix-gradle-plugin -t assemble Once you have addressed the issues surfaced in build scans and pushed the changes to your Git repository, you can rerun the experiment and start over the cycle of run → measure → improve → run.
  40. ./01-validate-incremental-building.sh Command Line Invocation ----------------------- ./01-validate-incremental-building.sh -r https://github.com/gradle/android-cache-fix-gradle-plugin -t assemble

    Once you have addressed the issues surfaced in build scans and pushed the changes to your Git repository, you can rerun the experiment and start over the cycle of run → measure → improve → run.
  41. ./01-validate-incremental-building.sh Summary ------- Project: android-cache-fix-gradle-plugin Git repo: [email protected]:gradle/android-cache-fix-gradle-plugin Git branch:

    main Git commit id: 88f52ce8c3b44a0563e21682866bc3f704107a52 Project dir: <root directory> Gradle tasks: assemble Gradle arguments: <none> Experiment: 01 Validate incremental building Experiment id: exp1-gradle Experiment run id: 6296b790 Experiment artifact dir: .data/01-validate-incremental-building/20220531T194920-6296b790 Build scan first build: https://ge.solutions-team.gradle.com/s/yj6btgctdvif6 Build scan second build: https://ge.solutions-team.gradle.com/s/qocthanshmjdm Investigation Quick Links ------------------------- Task execution overview: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/performance/execution Executed tasks timeline: https://ge.solutions-team.gradle.com/s/qocthanshmjdm/timeline? outcome=SUCCESS,FAILED&sort=longest Task inputs comparison: https://ge.solutions-team.gradle.com/c/yj6btgctdvif6/qocthanshmjdm/task-inputs Command Line Invocation ----------------------- ./01-validate-incremental-building.sh -r https://github.com/gradle/android-cache-fix-gradle-plugin -t assemble Once you have addressed the issues surfaced in build scans and pushed the changes to your Git repository, you can rerun the experiment and start over the cycle of run → measure → improve → run.
  42. •Interactive mode with -i ./01-validate-incremental-building.sh -i •Command line invocation ./01-validate-incremental-building.sh

    -r https:// github.com/gradle/android-cache-fix-gradle-plugin -t assemble Build Validation Scripts
  43. ./02-validate-local-build-caching-same-location.sh 1. Enable only local build caching and use an

    empty local build cache 2. Run the build with a typical task invocation including the ‘clean’ task 3. Run the build with the same task invocation including the ‘clean’ task 4. Determine which cacheable tasks are still executed in the second run and why 5. Assess which of the executed, cacheable tasks are worth improving 6. Fix identified tasks Step 1-3 are automated. 4-6 are manual.
  44. ./02-validate-local-build-caching-same-location.sh Summary ------- Project: gradle-build-scan-quickstart Git repo: [email protected]:gradle/gradle-build-scan-quickstart.git Git branch:

    main Git commit id: 3157cfc52db2c1ac960dcb2c24c4cbe3603c92be Project dir: <root directory> Gradle tasks: assemble Gradle arguments: <none> Experiment: 02 Validate local build caching - same project location Experiment id: exp2-gradle Experiment run id: 6298cd0f Experiment artifact dir: .data/02-validate-local-build-caching-same-location/20220602T074535-6298cd0f Build scan first build: https://ge.solutions-team.gradle.com/s/zoloexid3t4ok Build scan second build: https://ge.solutions-team.gradle.com/s/a6uhbkyvlexug Investigation Quick Links ------------------------- Task execution overview: https://ge.solutions-team.gradle.com/s/a6uhbkyvlexug/performance/execution Executed tasks timeline: https://ge.solutions-team.gradle.com/s/a6uhbkyvlexug/timeline?outcome=SUCCESS,FAILED&sort=longest Executed cacheable tasks: https://ge.solutions-team.gradle.com/s/a6uhbkyvlexug/timeline? cacheability=cacheable,overlapping_outputs,validation_failure&outcome=SUCCESS,FAILED&sort=longest Executed non-cacheable tasks: https://ge.solutions-team.gradle.com/s/a6uhbkyvlexug/timeline?cacheability=any_non- cacheable&outcome=SUCCESS,FAILED&sort=longest Build caching statistics: https://ge.solutions-team.gradle.com/s/a6uhbkyvlexug/performance/build-cache Task inputs comparison: https://ge.solutions-team.gradle.com/c/zoloexid3t4ok/a6uhbkyvlexug/task-inputs?cacheability=cacheable Command Line Invocation ----------------------- ./02-validate-local-build-caching-same-location.sh -r [email protected]:gradle/gradle-build-scan-quickstart.git -t assemble -s https:// ge.solutions-team.gradle.com
  45. ./02-validate-local-build-caching-same-location.sh Investigation Quick Links ------------------------- Task execution overview: https://ge.solutions-team.gradle.com/s/a6uhbkyvlexug/performance/execution Executed

    tasks timeline: https://ge.solutions-team.gradle.com/s/a6uhbkyvlexug/timeline?outcome=SUCCESS,FAILED&sort=longest Executed cacheable tasks: https://ge.solutions-team.gradle.com/s/a6uhbkyvlexug/timeline? cacheability=cacheable,overlapping_outputs,validation_failure&outcome=SUCCESS,FAILED&sort=longest Executed non-cacheable tasks: https://ge.solutions-team.gradle.com/s/a6uhbkyvlexug/timeline?cacheability=any_non- cacheable&outcome=SUCCESS,FAILED&sort=longest Build caching statistics: https://ge.solutions-team.gradle.com/s/a6uhbkyvlexug/performance/build-cache Task inputs comparison: https://ge.solutions-team.gradle.com/c/zoloexid3t4ok/a6uhbkyvlexug/task-inputs?cacheability=cacheable
  46. ./02-validate-local-build-caching-same-location.sh Investigation Quick Links ------------------------- Task execution overview: https://ge.solutions-team.gradle.com/s/a6uhbkyvlexug/performance/execution Executed

    tasks timeline: https://ge.solutions-team.gradle.com/s/a6uhbkyvlexug/timeline?outcome=SUCCESS,FAILED&sort=longest Executed cacheable tasks: https://ge.solutions-team.gradle.com/s/a6uhbkyvlexug/timeline? cacheability=cacheable,overlapping_outputs,validation_failure&outcome=SUCCESS,FAILED&sort=longest Executed non-cacheable tasks: https://ge.solutions-team.gradle.com/s/a6uhbkyvlexug/timeline?cacheability=any_non- cacheable&outcome=SUCCESS,FAILED&sort=longest Build caching statistics: https://ge.solutions-team.gradle.com/s/a6uhbkyvlexug/performance/build-cache Task inputs comparison: https://ge.solutions-team.gradle.com/c/zoloexid3t4ok/a6uhbkyvlexug/task-inputs?cacheability=cacheable
  47. 1. Enable only local build caching and use an empty

    local build cache 2. Run the build with a typical task invocation including the ‘clean’ task 3. Run the build from a different location with the same task invocation including the ‘clean’ task 4. Determine which cacheable tasks are still executed in the second run and why 5. Assess which of the executed, cacheable tasks are worth improving 6. Fix identified tasks ./03-validate-local-build-caching-different-locations.sh Step 1-3 are automated. 4-6 are manual.
  48. 1. Enable only remote build caching and use an empty

    remote build cache 2. On a given CI agent, run a typical CI configuration from a fresh checkout 3. On another CI agent, run the same CI configuration with the same commit id from a fresh checkout 4. Determine which cacheable tasks are still executed in the second run and why 5. Assess which of the executed, cacheable tasks are worth improving 6. Fix identified tasks ./04-validate-remote-build-caching-ci-ci.sh All steps are manual
  49. 1. Enable only remote build caching and use an empty

    remote build cache 2. On a given CI agent, run a typical CI configuration from a fresh checkout 3. On a developer machine, run the build with the same task invocation including the ‘clean’ task with the same commit id 4. Determine which cacheable tasks are still executed in the second run and why 5. Assess which of the executed, cacheable tasks are worth improving 6. Fix identified tasks ./05-validate-remote-build-caching-ci-local.sh All steps are manual except step 3
  50. steps: # Download the latest version of the build validation

    scripts - uses: gradle/gradle-enterprise-build-validation-scripts/.github/actions/gradle/[email protected] with: token: ${{ secrets.GITHUB_TOKEN }} Github Action to Verify Build
  51. steps: # Download the latest version of the build validation

    scripts - uses: gradle/gradle-enterprise-build-validation-scripts/.github/actions/gradle/[email protected] with: token: ${{ secrets.GITHUB_TOKEN }} # Run experiment 1 - uses: gradle/gradle-enterprise-build-validation-scripts/.github/actions/gradle/[email protected] with: gitRepo: <PROJECT_GIT_URL> gitBranch: <PROJECT_BRANCH> tasks: <PROJECT_BUILD_TASK> Github Action to Verify Build
  52. steps: # Download the latest version of the build validation

    scripts - uses: gradle/gradle-enterprise-build-validation-scripts/.github/actions/gradle/[email protected] with: token: ${{ secrets.GITHUB_TOKEN }} # Run experiment 1 - uses: gradle/gradle-enterprise-build-validation-scripts/.github/actions/gradle/[email protected] with: gitRepo: <PROJECT_GIT_URL> gitBranch: <PROJECT_BRANCH> tasks: <PROJECT_BUILD_TASK> # Run experiment 2 - uses: gradle/gradle-enterprise-build-validation-scripts/.github/actions/gradle/[email protected] with: gitRepo: <PROJECT_GIT_URL> gitBranch: <PROJECT_BRANCH> tasks: <PROJECT_BUILD_TASK> ... Github Action to Verify Build
  53. steps: # Download the latest version of the build validation

    scripts - uses: gradle/gradle-enterprise-build-validation-scripts/.github/actions/gradle/[email protected] with: token: ${{ secrets.GITHUB_TOKEN }} # Run experiment 1 - uses: gradle/gradle-enterprise-build-validation-scripts/.github/actions/gradle/[email protected] with: gitRepo: <PROJECT_GIT_URL> gitBranch: <PROJECT_BRANCH> tasks: <PROJECT_BUILD_TASK> # Run experiment 2 - uses: gradle/gradle-enterprise-build-validation-scripts/.github/actions/gradle/[email protected] with: gitRepo: <PROJECT_GIT_URL> gitBranch: <PROJECT_BRANCH> tasks: <PROJECT_BUILD_TASK> # Run experiment 3 - uses: gradle/gradle-enterprise-build-validation-scripts/.github/actions/gradle/[email protected] with: gitRepo: <PROJECT_GIT_URL> gitBranch: <PROJECT_BRANCH> tasks: <PROJECT_BUILD_TASK> ... Github Action to Verify Build
  54. steps: # Download the latest version of the build validation

    scripts - uses: gradle/gradle-enterprise-build-validation-scripts/.github/actions/gradle/[email protected] with: token: ${{ secrets.GITHUB_TOKEN }} # Run experiment 1 - uses: gradle/gradle-enterprise-build-validation-scripts/.github/actions/gradle/[email protected] with: gitRepo: <PROJECT_GIT_URL> gitBranch: <PROJECT_BRANCH> tasks: <PROJECT_BUILD_TASK> # Run experiment 2 - uses: gradle/gradle-enterprise-build-validation-scripts/.github/actions/gradle/[email protected] with: gitRepo: <PROJECT_GIT_URL> gitBranch: <PROJECT_BRANCH> tasks: <PROJECT_BUILD_TASK> # Run experiment 3 - uses: gradle/gradle-enterprise-build-validation-scripts/.github/actions/gradle/[email protected] with: gitRepo: <PROJECT_GIT_URL> gitBranch: <PROJECT_BRANCH> tasks: <PROJECT_BUILD_TASK> ... Github Action to Verify Build
  55. Summary ------- Project: biometric-playground Git repo: https://github.com/gradle/androidx Git branch: androidx-main

    Git commit id: e1f0de767e6002ead5488f35152fe4a97036d1e5 Project dir: biometric Gradle tasks: buildOnServer zipTestConfigsWithApks test Gradle arguments: -x ktlint Experiment: 03 Validate local build caching - different project locations Experiment id: exp3-gradle Experiment run id: 62c15afb Experiment artifact dir: .data/03-validate-local-build-caching-different-locations/20220703T090147-62c15afb Build scan first build: https://ge.solutions-team.gradle.com/s/dh6hbujtzul6s Build scan second build: https://ge.solutions-team.gradle.com/s/qlijsh5r36vqa Investigation Quick Links ------------------------- Task execution overview: https://ge.solutions-team.gradle.com/s/qlijsh5r36vqa/performance/execution Executed tasks timeline: https://ge.solutions-team.gradle.com/s/qlijsh5r36vqa/timeline? outcome=SUCCESS,FAILED&sort=longest Executed cacheable tasks: https://ge.solutions-team.gradle.com/s/qlijsh5r36vqa/timeline? cacheability=cacheable,overlapping_outputs,validation_failure&outcome=SUCCESS,FAILED&sort=longest Executed non-cacheable tasks: https://ge.solutions-team.gradle.com/s/qlijsh5r36vqa/timeline?cacheability=any_non- cacheable&outcome=SUCCESS,FAILED&sort=longest Build caching statistics: https://ge.solutions-team.gradle.com/s/qlijsh5r36vqa/performance/build-cache Task inputs comparison: https://ge.solutions-team.gradle.com/c/dh6hbujtzul6s/qlijsh5r36vqa/task-inputs? cacheability=cacheable https://github.com/gradle/gradle-enterprise-oss-projects/runs/7167693674?check_suite_focus=true#step:9:3214
  56. Old Export API • SSE based • Real time streaming

    • JSON events • No event model • Clunky
  57. Gradle Enterprise API • REST style API • JSON •

    OpenAPI - allows model and client generation • Build API • Build Cache Node API • Will replace export API - missing raw build events
  58. Use cases • Find projects with longest build times •

    Rank teammates by slowest average build time • Find builds with remote caching errors • Find slowest tasks on average • Find longest non-cacheable tasks in all projects
  59. plugins { kotlin("jvm") version "1.7.0" id("org.openapi.generator") version "6.0.0" } val

    apiSpecificationFile = resources.text.fromUri( "https://docs.gradle.com/enterprise/api-manual/ref/gradle-enterprise-2022.2.4- api.yaml" ).asFile() val outputDirName = "${buildDir}/generated/gradle_enterprise_api" openApiGenerate { generatorName.set("kotlin") inputSpec.set(apiSpecificationFile.absolutePath) outputDir.set(outputDirName) modelPackage.set("com.gradle.enterprise.api.model") apiPackage.set("com.gradle.enterprise.api") invokerPackage.set("com.gradle.enterprise.api.client") configOptions.set(mapOf( "library" to "jvm-retrofit2", "useCoroutines" to "true", )) } build.gradle.kts
  60. plugins { kotlin("jvm") version "1.7.0" id("org.openapi.generator") version "6.0.0" } val

    apiSpecificationFile = resources.text.fromUri( "https://docs.gradle.com/enterprise/api-manual/ref/gradle-enterprise-2022.2.4- api.yaml" ).asFile() val outputDirName = "${buildDir}/generated/gradle_enterprise_api" openApiGenerate { generatorName.set("kotlin") inputSpec.set(apiSpecificationFile.absolutePath) outputDir.set(outputDirName) modelPackage.set("com.gradle.enterprise.api.model") apiPackage.set("com.gradle.enterprise.api") invokerPackage.set("com.gradle.enterprise.api.client") configOptions.set(mapOf( "library" to "jvm-retrofit2", "useCoroutines" to "true", )) } build.gradle.kts
  61. plugins { kotlin("jvm") version "1.7.0" id("org.openapi.generator") version "6.0.0" } val

    apiSpecificationFile = resources.text.fromUri( "https://docs.gradle.com/enterprise/api-manual/ref/gradle-enterprise-2022.2.4- api.yaml" ).asFile() val outputDirName = "${buildDir}/generated/gradle_enterprise_api" openApiGenerate { generatorName.set("kotlin") inputSpec.set(apiSpecificationFile.absolutePath) outputDir.set(outputDirName) modelPackage.set("com.gradle.enterprise.api.model") apiPackage.set("com.gradle.enterprise.api") invokerPackage.set("com.gradle.enterprise.api.client") configOptions.set(mapOf( "library" to "jvm-retrofit2", "useCoroutines" to "true", )) } build.gradle.kts
  62. plugins { kotlin("jvm") version "1.7.0" id("org.openapi.generator") version "6.0.0" } val

    apiSpecificationFile = resources.text.fromUri( "https://docs.gradle.com/enterprise/api-manual/ref/gradle-enterprise-2022.2.4- api.yaml" ).asFile() val outputDirName = "${buildDir}/generated/gradle_enterprise_api" openApiGenerate { generatorName.set("kotlin") inputSpec.set(apiSpecificationFile.absolutePath) outputDir.set(outputDirName) modelPackage.set("com.gradle.enterprise.api.model") apiPackage.set("com.gradle.enterprise.api") invokerPackage.set("com.gradle.enterprise.api.client") configOptions.set(mapOf( "library" to "jvm-retrofit2", "useCoroutines" to "true", )) } build.gradle.kts
  63. plugins { kotlin("jvm") version "1.7.0" id("org.openapi.generator") version "6.0.0" } val

    apiSpecificationFile = resources.text.fromUri( "https://docs.gradle.com/enterprise/api-manual/ref/gradle-enterprise-2022.2.4- api.yaml" ).asFile() val outputDirName = "${buildDir}/generated/gradle_enterprise_api" openApiGenerate { generatorName.set("kotlin") inputSpec.set(apiSpecificationFile.absolutePath) outputDir.set(outputDirName) modelPackage.set("com.gradle.enterprise.api.model") apiPackage.set("com.gradle.enterprise.api") invokerPackage.set("com.gradle.enterprise.api.client") configOptions.set(mapOf( "library" to "jvm-retrofit2", "useCoroutines" to "true", )) } build.gradle.kts
  64. plugins { kotlin("jvm") version "1.7.0" id("org.openapi.generator") version "6.0.0" } val

    apiSpecificationFile = resources.text.fromUri( "https://docs.gradle.com/enterprise/api-manual/ref/gradle-enterprise-2022.2.4- api.yaml" ).asFile() val outputDirName = "${buildDir}/generated/gradle_enterprise_api" openApiGenerate { generatorName.set("kotlin") inputSpec.set(apiSpecificationFile.absolutePath) outputDir.set(outputDirName) modelPackage.set("com.gradle.enterprise.api.model") apiPackage.set("com.gradle.enterprise.api") invokerPackage.set("com.gradle.enterprise.api.client") configOptions.set(mapOf( "library" to "jvm-retrofit2", "useCoroutines" to "true", )) } build.gradle.kts
  65. plugins { kotlin("jvm") version "1.7.0" id("org.openapi.generator") version "6.0.0" } val

    apiSpecificationFile = resources.text.fromUri( "https://docs.gradle.com/enterprise/api-manual/ref/gradle-enterprise-2022.2.4- api.yaml" ).asFile() val outputDirName = "${buildDir}/generated/gradle_enterprise_api" openApiGenerate { generatorName.set("kotlin") inputSpec.set(apiSpecificationFile.absolutePath) outputDir.set(outputDirName) modelPackage.set("com.gradle.enterprise.api.model") apiPackage.set("com.gradle.enterprise.api") invokerPackage.set("com.gradle.enterprise.api.client") configOptions.set(mapOf( "library" to "jvm-retrofit2", "useCoroutines" to "true", )) } build.gradle.kts
  66. plugins { kotlin("jvm") version "1.7.0" id("org.openapi.generator") version "6.0.0" } val

    apiSpecificationFile = resources.text.fromUri( "https://docs.gradle.com/enterprise/api-manual/ref/gradle-enterprise-2022.2.4- api.yaml" ).asFile() val outputDirName = "${buildDir}/generated/gradle_enterprise_api" openApiGenerate { generatorName.set("kotlin") inputSpec.set(apiSpecificationFile.absolutePath) outputDir.set(outputDirName) modelPackage.set("com.gradle.enterprise.api.model") apiPackage.set("com.gradle.enterprise.api") invokerPackage.set("com.gradle.enterprise.api.client") configOptions.set(mapOf( "library" to "jvm-retrofit2", "useCoroutines" to "true", )) } build.gradle.kts
  67. plugins { kotlin("jvm") version "1.7.0" id("org.openapi.generator") version "6.0.0" } val

    apiSpecificationFile = resources.text.fromUri( "https://docs.gradle.com/enterprise/api-manual/ref/gradle-enterprise-2022.2.4- api.yaml" ).asFile() val outputDirName = "${buildDir}/generated/gradle_enterprise_api" openApiGenerate { generatorName.set("kotlin") inputSpec.set(apiSpecificationFile.absolutePath) outputDir.set(outputDirName) modelPackage.set("com.gradle.enterprise.api.model") apiPackage.set("com.gradle.enterprise.api") invokerPackage.set("com.gradle.enterprise.api.client") configOptions.set(mapOf( "library" to "jvm-retrofit2", "useCoroutines" to "true", )) }
  68. plugins { kotlin("jvm") version "1.7.0" id("org.openapi.generator") version "6.0.0" } val

    apiSpecificationFile = resources.text.fromUri( "https://docs.gradle.com/enterprise/api-manual/ref/gradle-enterprise-2022.2.4- api.yaml" ).asFile() val outputDirName = "${buildDir}/generated/gradle_enterprise_api" openApiGenerate { generatorName.set("kotlin") inputSpec.set(apiSpecificationFile.absolutePath) outputDir.set(outputDirName) modelPackage.set("com.gradle.enterprise.api.model") apiPackage.set("com.gradle.enterprise.api") invokerPackage.set("com.gradle.enterprise.api.client") configOptions.set(mapOf( "library" to "jvm-retrofit2", "useCoroutines" to "true", )) }
  69. fun main(args: Array<String>) = runBlocking<Unit> { val apiClient = ApiClient(

    baseUrl = "https://ge.solutions-team.gradle.com", authName = "GradleEnterpriseAccessKey", bearerToken = "my-secret-token" ) val buildApi = apiClient.createService(BuildsApi::class.java) val query = BuildsQuery(since = oneHourAgo) val builds = buildApi.getBuilds(query) builds.forEach { build -> println(build) } } Main.kt
  70. fun main(args: Array<String>) = runBlocking<Unit> { val apiClient = ApiClient(

    baseUrl = "https://ge.solutions-team.gradle.com", authName = "GradleEnterpriseAccessKey", bearerToken = "my-secret-token" ) val buildApi = apiClient.createService(BuildsApi::class.java) val query = BuildsQuery(since = oneHourAgo) val builds = buildApi.getBuilds(query) builds.forEach { build -> println(build) } } Main.kt
  71. fun main(args: Array<String>) = runBlocking<Unit> { val apiClient = ApiClient(

    baseUrl = "https://ge.solutions-team.gradle.com", authName = "GradleEnterpriseAccessKey", bearerToken = "my-secret-token" ) val buildApi = apiClient.createService(BuildsApi::class.java) val query = BuildsQuery(since = oneHourAgo) val builds = buildApi.getBuilds(query) builds.forEach { build -> println(build) } } Main.kt
  72. fun main(args: Array<String>) = runBlocking<Unit> { val apiClient = ApiClient(

    baseUrl = "https://ge.solutions-team.gradle.com", authName = "GradleEnterpriseAccessKey", bearerToken = "my-secret-token" ) val buildApi = apiClient.createService(BuildsApi::class.java) val query = BuildsQuery(since = oneHourAgo) val builds = buildApi.getBuilds(query) builds.forEach { build -> println(build) } } Main.kt
  73. fun main(args: Array<String>) = runBlocking<Unit> { val apiClient = ApiClient(

    baseUrl = "https://ge.solutions-team.gradle.com", authName = "GradleEnterpriseAccessKey", bearerToken = "my-secret-token" ) val buildApi = apiClient.createService(BuildsApi::class.java) val query = BuildsQuery(since = oneHourAgo) val builds = buildApi.getBuilds(query) builds.forEach { build -> println(build) } } Main.kt
  74. fun main(args: Array<String>) = runBlocking<Unit> { val apiClient = ApiClient(

    baseUrl = "https://ge.solutions-team.gradle.com", authName = "GradleEnterpriseAccessKey", bearerToken = "my-secret-token" ) val buildApi = apiClient.createService(BuildsApi::class.java) val query = BuildsQuery(since = oneHourAgo) val builds = buildApi.getBuilds(query) builds.forEach { build -> println(build) } } Main.kt
  75. fun main(args: Array<String>) = runBlocking<Unit> { val apiClient = ApiClient(

    baseUrl = "https://ge.solutions-team.gradle.com", authName = "GradleEnterpriseAccessKey", bearerToken = "my-secret-token" ) val buildApi = apiClient.createService(BuildsApi::class.java) val query = BuildsQuery(since = oneHourAgo) val builds = buildApi.getBuilds(query) builds.forEach { build -> println(build) } } Main.kt
  76. Build(id=wsj4fsafsadte, availableAt=1654106260326, buildToolType=gradle, buildToolVersion=7.4.2, buildAgentVersion=3.10.1) Build(id=qsj4euf5g3dff, availableAt=1654106224428, buildToolType=gradle, buildToolVersion=7.4.2, buildAgentVersion=3.10.1)

    Build(id=dsjaadff5g3te, availableAt=1654101266539, buildToolType=gradle, buildToolVersion=7.4.2, buildAgentVersion=3.10.1) Build(id=yweroijiwerjr, availableAt=1654109460322, buildToolType=gradle, buildToolVersion=7.4.2, buildAgentVersion=3.10.1) Build(id=wsj4fsafsadte, availableAt=1654106260326, buildToolType=gradle, buildToolVersion=7.4.2, buildAgentVersion=3.10.1) Build(id=qsj4euf5g3dff, availableAt=1654106224428, buildToolType=gradle, buildToolVersion=7.4.2, buildAgentVersion=3.10.1) Build(id=dsjaadff5g3te, availableAt=1654101266539, buildToolType=gradle, buildToolVersion=7.4.2, buildAgentVersion=3.10.1) Build(id=yweroijiwerjr, availableAt=1654109460322, buildToolType=gradle, buildToolVersion=7.4.2, buildAgentVersion=3.10.1) Build(id=qwerwerwerwer, availableAt=1654106260326, buildToolType=gradle, buildToolVersion=7.4.2, buildAgentVersion=3.10.1) Build(id=qsj4euf5g3dff, availableAt=1654106224428, buildToolType=gradle, buildToolVersion=7.4.2, buildAgentVersion=3.10.1) Build(id=dsjaadff5g3te, availableAt=1654101266539, buildToolType=gradle, buildToolVersion=7.4.2, buildAgentVersion=3.10.1) Build(id=yweroijiwerjr, availableAt=1654109460322, buildToolType=gradle, buildToolVersion=7.4.2, buildAgentVersion=3.10.1) Build(id=qweorijweroiw, availableAt=1654106260326, buildToolType=gradle, buildToolVersion=7.4.2, buildAgentVersion=3.10.1) Build(id=qsj4euf5g3dff, availableAt=1654106224428, buildToolType=gradle, buildToolVersion=7.4.2, buildAgentVersion=3.10.1) Build(id=dsjaadff5g3te, availableAt=1654101266539, buildToolType=gradle, buildToolVersion=7.4.2, buildAgentVersion=3.10.1) Build(id=yweroijiwerjr, availableAt=1654109460322, buildToolType=gradle, buildToolVersion=7.4.2, buildAgentVersion=3.10.1) Build(id=wsj4fsafsadte, availableAt=1654106260326, buildToolType=gradle, buildToolVersion=7.4.2, buildAgentVersion=3.10.1) Build(id=qsj4euf5g3dff, availableAt=1654106224428, buildToolType=gradle, buildToolVersion=7.4.2, buildAgentVersion=3.10.1) Build(id=dsjaadff5g3te, availableAt=1654101266539, buildToolType=gradle, buildToolVersion=7.4.2, buildAgentVersion=3.10.1) Build(id=qweroijwerijr, availableAt=1654109324464, buildToolType=gradle, buildToolVersion=7.4.2, buildAgentVersion=3.10.1) builds.forEach { build -> println(build) }
  77. buildApi.getGradleAttributes(build.id, null) { • "id": "9r4d13f0r3v3r", • "buildStartTime": 1637316480, •

    "buildDuration": 5000, • "gradleVersion": "7.3", • "pluginVersion": "3.7.2", • "rootProjectName": "example-project", • "requestedTasks": [ • "clean", • "build" • ], • "hasFailed": false, • "tags": [ • "CI", • "feature-branch" • ], • "values": [ • {}, • {} • ], • "links": [ • {}, • {} • ], • "gradleEnterpriseSettings": { • "backgroundPublicationEnabled": true, • "buildOutputCapturingEnabled": true, • "taskInputsFileCapturingEnabled": true, • "testOutputCapturingEnabled": true • }, • "buildOptions": { • "buildCacheEnabled": true, • "configurationCacheEnabled": true, • "configurationOnDemandEnabled": true, https://docs.gradle.com/enterprise/api-manual/ref/2022.2.html#operation/GetGradleAttributes
  78. buildApi.getGradleBuildCachePerformance(build.id, null) { • "id": "9r4d13f0r3v3r", • "buildTime": 5000, •

    "effectiveTaskExecutionTime": 4000, • "serialTaskExecutionTime": 8000, • "serializationFactor": 2, • "taskExecution": [ • {}, • {} • ], • "taskFingerprintingSummary": { • "count": 1, • "serialDuration": 8 • }, • "avoidanceSavingsSummary": { • "total": 10000, • "ratio": 0.55556, • "upToDate": 0, • "localBuildCache": 6000, • "remoteBuildCache": 4000 • }, • "buildCaches": { • "local": {}, • "remote": {}, • "overhead": {} • } } https://docs.gradle.com/enterprise/api-manual/ref/2022.2.html#operation/GetGradleBuildCacheP
  79. builds.map { build -> buildApi.getGradleAttributes(build.id, null) } .groupBy { it.rootProjectName

    } .map { (projectName, projectBuilds) -> projectName to projectBuilds.map { it.buildDuration }.average() } .sortedByDescending { pair -> pair.second } .forEach { pair -> val seconds = TimeUnit.MILLISECONDS.toSeconds(pair.second.toLong()) println("Project ${pair.first} has average build time of ${seconds}s") } Project with slowest average build time
  80. builds.map { build -> buildApi.getGradleAttributes(build.id, null) } .groupBy { it.rootProjectName

    } .map { (projectName, projectBuilds) -> projectName to projectBuilds.map { it.buildDuration }.average() } .sortedByDescending { pair -> pair.second } .forEach { pair -> val seconds = TimeUnit.MILLISECONDS.toSeconds(pair.second.toLong()) println("Project ${pair.first} has average build time of ${seconds}s") } Project with slowest average build time
  81. builds.map { build -> buildApi.getGradleAttributes(build.id, null) } .groupBy { it.rootProjectName

    } .map { (projectName, projectBuilds) -> projectName to projectBuilds.map { it.buildDuration }.average() } .sortedByDescending { pair -> pair.second } .forEach { pair -> val seconds = TimeUnit.MILLISECONDS.toSeconds(pair.second.toLong()) println("Project ${pair.first} has average build time of ${seconds}s") } Project with slowest average build time
  82. builds.map { build -> buildApi.getGradleAttributes(build.id, null) } .groupBy { it.rootProjectName

    } .map { (projectName, projectBuilds) -> projectName to projectBuilds.map { it.buildDuration }.average() } .sortedByDescending { pair -> pair.second } .forEach { pair -> val seconds = TimeUnit.MILLISECONDS.toSeconds(pair.second.toLong()) println("Project ${pair.first} has average build time of ${seconds}s") } Project with slowest average build time
  83. builds.map { build -> buildApi.getGradleAttributes(build.id, null) } .groupBy { it.rootProjectName

    } .map { (projectName, projectBuilds) -> projectName to projectBuilds.map { it.buildDuration }.average() } .sortedByDescending { pair -> pair.second } .forEach { pair -> val seconds = TimeUnit.MILLISECONDS.toSeconds(pair.second.toLong()) println("Project ${pair.first} has average build time of ${seconds}s") } Project with slowest average build time
  84. builds.map { build -> buildApi.getGradleAttributes(build.id, null) } .groupBy { it.rootProjectName

    } .map { (projectName, projectBuilds) -> projectName to projectBuilds.map { it.buildDuration }.average() } .sortedByDescending { pair -> pair.second } .forEach { pair -> val seconds = TimeUnit.MILLISECONDS.toSeconds(pair.second.toLong()) println("Project ${pair.first} has average build time of ${seconds}s") } Project with slowest average build time
  85. Project with slowest average build time builds.map { build ->

    buildApi.getGradleAttributes(build.id, null) } .groupBy { it.rootProjectName } .map { (projectName, projectBuilds) -> projectName to projectBuilds.map { it.buildDuration }.average() } .sortedByDescending { pair -> pair.second } .forEach { pair -> val seconds = TimeUnit.MILLISECONDS.toSeconds(pair.second.toLong()) println("Project ${pair.first} has average build time of ${seconds}s") }
  86. Project with slowest average build time .forEach { pair ->

    val seconds = TimeUnit.MILLISECONDS.toSeconds(pair.second.toLong()) println("Project ${pair.first} has average build time of ${seconds}s") } Project customer-app has average build time of 184s Project internal-app has average build time of 55s Project time-lib has average build time of 20s
  87. User with slowest average build time builds.map { build ->

    buildApi.getGradleAttributes(build.id, null) } .groupBy { it.environment.username } .map { (username, userBuilds) -> username to userBuilds.map { it.buildDuration }.average() } .sortedByDescending { pair -> pair.second } .forEach { pair -> val seconds = TimeUnit.MILLISECONDS.toSeconds(pair.second.toLong()) println("Username: ${pair.first}, average build time is ${seconds}s.") }
  88. User with slowest average build time builds.map { build ->

    buildApi.getGradleAttributes(build.id, null) } .groupBy { it.environment.username } .map { (username, userBuilds) -> username to userBuilds.map { it.buildDuration }.average() } .sortedByDescending { pair -> pair.second } .forEach { pair -> val seconds = TimeUnit.MILLISECONDS.toSeconds(pair.second.toLong()) println("Username: ${pair.first}, average build time is ${seconds}s.") }
  89. User with slowest average build time builds.map { build ->

    buildApi.getGradleAttributes(build.id, null) } .groupBy { it.environment.username } .map { (username, userBuilds) -> username to userBuilds.map { it.buildDuration }.average() } .sortedByDescending { pair -> pair.second } .forEach { pair -> val seconds = TimeUnit.MILLISECONDS.toSeconds(pair.second.toLong()) println("Username: ${pair.first}, average build time is ${seconds}s.") }
  90. User with slowest average build time builds.map { build ->

    buildApi.getGradleAttributes(build.id, null) } .groupBy { it.environment.username } .map { (username, userBuilds) -> username to userBuilds.map { it.buildDuration }.average() } .sortedByDescending { pair -> pair.second } .forEach { pair -> val seconds = TimeUnit.MILLISECONDS.toSeconds(pair.second.toLong()) println("Username: ${pair.first}, average build time is ${seconds}s.") }
  91. User with slowest average build time builds.map { build ->

    buildApi.getGradleAttributes(build.id, null) } .groupBy { it.environment.username } .map { (username, userBuilds) -> username to userBuilds.map { it.buildDuration }.average() } .sortedByDescending { pair -> pair.second } .forEach { pair -> val seconds = TimeUnit.MILLISECONDS.toSeconds(pair.second.toLong()) println("Username: ${pair.first}, average build time is ${seconds}s.") }
  92. User with slowest average build time builds.map { build ->

    buildApi.getGradleAttributes(build.id, null) } .groupBy { it.environment.username } .map { (username, userBuilds) -> username to userBuilds.map { it.buildDuration }.average() } .sortedByDescending { pair -> pair.second } .forEach { pair -> val seconds = TimeUnit.MILLISECONDS.toSeconds(pair.second.toLong()) println("Username: ${pair.first}, average build time is ${seconds}s.") }
  93. User with slowest average build time builds.map { build ->

    buildApi.getGradleAttributes(build.id, null) } .groupBy { it.environment.username } .map { (username, userBuilds) -> username to userBuilds.map { it.buildDuration }.average() } .sortedByDescending { pair -> pair.second } .forEach { pair -> val seconds = TimeUnit.MILLISECONDS.toSeconds(pair.second.toLong()) println("Username: ${pair.first}, average build time is ${seconds}s.") }
  94. User with slowest average build time .forEach { pair ->

    val seconds = TimeUnit.MILLISECONDS.toSeconds(pair.second.toLong()) println("Username: ${pair.first}, average build time is ${seconds}s.") } Username: sorin, average build time is 233s Username: daz, average build time is 122s Username: gary, average build time is 33s Username: alexis, average build time is 30s Username: jerome, average build time is 29s Username: clay, average build time is 20s Username: etienne, average build time is 20s
  95. Tasks with highest negative avoidance savings builds.map { build ->

    buildApi.getGradleBuildCachePerformance(build.id, null) } .flatMap { it.taskExecution.filter { (it.avoidanceSavings ?: 0) < 0 } } .groupBy { it.taskPath } .map { (taskPath, taskExecutions) -> taskPath to taskExecutions.map { it.avoidanceSavings!! }.average() } .sortedByDescending { pair -> pair.second } .forEach { (taskPath, averageAvoidanceSavings) -> println("Task $taskPath has average avoidance savings $averageAvoidanceSavings") }
  96. Tasks with highest negative avoidance savings builds.map { build ->

    buildApi.getGradleBuildCachePerformance(build.id, null) } .flatMap { it.taskExecution.filter { (it.avoidanceSavings ?: 0) < 0 } } .groupBy { it.taskPath } .map { (taskPath, taskExecutions) -> taskPath to taskExecutions.map { it.avoidanceSavings!! }.average() } .sortedByDescending { pair -> pair.second } .forEach { (taskPath, averageAvoidanceSavings) -> println("Task $taskPath has average avoidance savings $averageAvoidanceSavings") }
  97. Tasks with highest negative avoidance savings builds.map { build ->

    buildApi.getGradleBuildCachePerformance(build.id, null) } .flatMap { it.taskExecution.filter { (it.avoidanceSavings ?: 0) < 0 } } .groupBy { it.taskPath } .map { (taskPath, taskExecutions) -> taskPath to taskExecutions.map { it.avoidanceSavings!! }.average() } .sortedByDescending { pair -> pair.second } .forEach { (taskPath, averageAvoidanceSavings) -> println("Task $taskPath has average avoidance savings $averageAvoidanceSavings") }
  98. Tasks with highest negative avoidance savings builds.map { build ->

    buildApi.getGradleBuildCachePerformance(build.id, null) } .flatMap { it.taskExecution.filter { (it.avoidanceSavings ?: 0) < 0 } } .groupBy { it.taskPath } .map { (taskPath, taskExecutions) -> taskPath to taskExecutions.map { it.avoidanceSavings!! }.average() } .sortedByDescending { pair -> pair.second } .forEach { (taskPath, averageAvoidanceSavings) -> println("Task $taskPath has average avoidance savings $averageAvoidanceSavings") }
  99. Tasks with highest negative avoidance savings builds.map { build ->

    buildApi.getGradleBuildCachePerformance(build.id, null) } .flatMap { it.taskExecution.filter { (it.avoidanceSavings ?: 0) < 0 } } .groupBy { it.taskPath } .map { (taskPath, taskExecutions) -> taskPath to taskExecutions.map { it.avoidanceSavings!! }.average() } .sortedByDescending { pair -> pair.second } .forEach { (taskPath, averageAvoidanceSavings) -> println("Task $taskPath has average avoidance savings $averageAvoidanceSavings") }
  100. Tasks with highest negative avoidance savings builds.map { build ->

    buildApi.getGradleBuildCachePerformance(build.id, null) } .flatMap { it.taskExecution.filter { (it.avoidanceSavings ?: 0) < 0 } } .groupBy { it.taskPath } .map { (taskPath, taskExecutions) -> taskPath to taskExecutions.map { it.avoidanceSavings!! }.average() } .sorted { pair -> pair.second } .forEach { (taskPath, averageAvoidanceSavings) -> println("Task $taskPath has average avoidance savings $averageAvoidanceSavings") }
  101. Tasks with highest negative avoidance savings builds.map { build ->

    buildApi.getGradleBuildCachePerformance(build.id, null) } .flatMap { it.taskExecution.filter { (it.avoidanceSavings ?: 0) < 0 } } .groupBy { it.taskPath } .map { (taskPath, taskExecutions) -> taskPath to taskExecutions.map { it.avoidanceSavings!! }.average() } .sorted { pair -> pair.second } .forEach { (taskPath, averageAvoidanceSavings) -> println("Task $taskPath has average avoidance savings $averageAvoidanceSavings") }
  102. Tasks with highest negative avoidance savings builds.map { build ->

    buildApi.getGradleBuildCachePerformance(build.id, null) } .flatMap { it.taskExecution.filter { (it.avoidanceSavings ?: 0) < 0 } } .groupBy { it.taskPath } .map { (taskPath, taskExecutions) -> taskPath to taskExecutions.map { it.avoidanceSavings!! }.average() } .sorted { pair -> pair.second } .forEach { (taskPath, averageAvoidanceSavings) -> println("Task $taskPath has average avoidance savings $averageAvoidanceSavings") }
  103. Tasks with highest negative avoidance savings .forEach { (taskPath, averageAvoidanceSavings)

    -> println("Task $taskPath has average avoidance savings $averageAvoidanceSavings") } Task :app:mergeNativeDebugLibs has average avoidance savings -23s Task :app:dataBindingMergeGenClassesDebug has average avoidance savings -10s Task :app:compileDebugAidl has average avoidance savings -2s Task :lib:mergeDebugConsumerProguardFiles has average avoidance savings -1s
  104. User with the most build cache errors builds.map { buildApi.getGradleAttributes(it.id,

    null) to buildApi.getGradleBuildCachePerformance(it.id, null) } .groupBy {it.first.environment.username} .map { (username, buildPairs) -> username to buildPairs.count { it.second.buildCaches?.remote?.isDisabledDueToError ?: false } } .sortedByDescending { pair -> pair.second } .forEach { pair -> println("Username: ${pair.first}, total builds disabled due to errors: $ {pair.second}") }
  105. Project with least avoidance savings builds.map { buildApi.getGradleAttributes(it.id, null) to

    buildApi.getGradleBuildCachePerformance(it.id, null) } .groupBy { it.first.rootProjectName } .map { (projectName, projectBuilds) -> projectName to projectBuilds.map { it.second.avoidanceSavingsSummary.ratio }.average() } .sortedBy { pair -> pair.second } .forEach { pair -> val ratio = pair.second println("Project ${pair.first} with average avoidance savings ratio of ${ratio}") }
  106. builds.filter { it.buildToolType == "gradle" }.map { build -> buildApi.getGradleAttributes(build.id,

    null) }.filter { it.tags.contains("doctor-negative-avoidance-savings") } Find builds with certain tags
  107. Usage: gradle-enterprise-project-summary A compact utility that gathers project metrics using

    Gradle Enterprise API Options: --access-key-file=<accessKeyFile> The path to the file containing the access key --days=<days> The total number of days to process from the past (if set will ignore hours) --format-duration Formatting the durations by HH:mm:ss --hours=<hours> The total number of hours to process from the past (if days set this will be ignored) --project-name=<projectName> The name of the project to show the builds of (if omitted, all builds are shown) --server-url=<serverUrl> The address of the Gradle Enterprise server --work-units=<nrOfWorkUnits> The number of tasks/goals to display Project summary
  108. { "summary" : { "totalNumberOfBuilds" : "3170", "totalBuildTime" : "631261486",

    "totalNumberOfUsers" : "16", "totalNumberOfProjects" : "6" }, "projects" : { "main-app" : { "totalNumberOfBuilds" : "3130", "buildTime" : "627827767", "numOfUsers" : "14", "totalTaskExecutionTime" : "487443385", "totalTaskAvoidanceTime" : "2805217043", "totalTaskAvoidableTime" : "138022728", "totalTaskNonAvoidableTime" : "370605501", "tasks" : { "test" : { "totalExecutions" : "7365", "totalTaskExecutionTime" : "68277014", "totalTaskAvoidanceTime" : "235514732", "totalTaskAvoidableTime" : "46062541", "totalTaskNonAvoidableTime" : "21259161", "cacheMissRate" : "9" }, "test_Maven_3_8_5_daily_JDK_18" : { "totalExecutions" : "12", "totalTaskExecutionTime" : "12751161", "totalTaskAvoidanceTime" : "0", "totalTaskAvoidableTime" : "12751161", "totalTaskNonAvoidableTime" : "0", "cacheMissRate" : "100" }, "test_Maven_3_8_5" : { "totalExecutions" : "41", "totalTaskExecutionTime" : "14975383", "totalTaskAvoidanceTime" : "0", "totalTaskAvoidableTime" : "12439278", "totalTaskNonAvoidableTime" : "2536105", Project summary
  109. Gradle Enterprise Android Features • Find your slowest tests in

    the test dashboard • Learn which tests are flaky with flaky test insights • Android Instrumentation Test results in Build Scans! Check it out! https://gradle.com/enterprise-customers/oss-projects/
  110. •Absolute path sensitivity •Overlapping inputs and outputs •Non exclusive output

    directory •Kapt removes contents of output directories
  111. More Resources Gradle Training https://gradle.com/training/ https://gradle.com/training/introduction-to-gradle https://gradle.com/training/build-cache-deep-dive Gradle Community Slack

    https://gradle.com/slack-invite https://github.com/gradle/gradle-enterprise-build-validation-scripts https://docs.gradle.com/enterprise/api-manual/ https://github.com/gradle/gradle-enterprise-api-samples
  112. Thank you! Gradle Enterprise Trial osacky.com https://gradle.com/enterprise/trial/ https://go.gradle.com/solutions-engineer https://gradle.com/careers/ Gradle

    Solutions is hiring #fixroom @nellyspageli https://github.com/gradle/gradle-enterprise-build-validation-scripts https://docs.gradle.com/enterprise/api-manual/ https://github.com/gradle/gradle-enterprise-api-samples https://speakerdeck.com/runningcode/keeping-your-gradle-builds-in-top-shape Room bug Slides
  113. Predictive Test Selection • Machine learning selects tests which provide

    useful feedback • 99% accuracy, save 60% of testing time • Predictive Test Selection Simulator • See why tests were selected in dashboard • Not yet available for Android Instrumentation Tests https://ge.micronaut.io/scans/test-selection