Skip to content

Latest commit

 

History

History
338 lines (237 loc) · 14.4 KB

gradle.md

File metadata and controls

338 lines (237 loc) · 14.4 KB
title description
Gradle Tutorial
In this tutorial you'll add Nx to an existing Gradle repo

Gradle Tutorial

In this tutorial, you'll learn how to add Nx to a repository with an existing Gradle setup. You'll see how Nx can provide immediate value.

Prerequisites

Make sure that you have Gradle installed on your system. Consult Gradle's installation guide for instruction that are specific to your operating system.

To verify that Gradle was installed correctly, run this command:

gradle --version

Nx also requires NodeJS to be installed. If you do not have NodeJS installed, you can install it from the NodeJS website.

node -v

Getting Started

This tutorial picks up where Spring framework's guide for Multi-Module Projects leaves off. Check out the sample repository on your local machine:

git clone https://github.com/nrwl/gradle-tutorial.git

The Multi-Module Spring Tutorial left us with 2 projects:

  • The main application project which contains the Spring DemoApplication
  • A library project which contains a Service used in the DemoApplication

You can see the above 2 projects by running ./gradlew projects

> Task :projects

------------------------------------------------------------
Root project 'gradle-tutorial'
------------------------------------------------------------

Root project 'gradle-tutorial'
+--- Project ':application'
\--- Project ':library'

Add Nx

Nx is a build system with built in tooling and advanced CI capabilities. It helps you maintain and scale monorepos, both locally and on CI. We will explore the features of Nx in this tutorial by adding it to the Gradle workspace above.

To add Nx, run npx nx@latest init.

This command will download the latest version of Nx and help set up your repository to take advantage of it. Nx will also detect Gradle is used in the repo so it will propose adding the @nx/gradle plugin to integrate Gradle with Nx. Select the plugin and continue with the setup.

Similar to Gradle, Nx can be run with the nx or nx.bat executables. We will learn about some of the Nx commands in the following sections.

Explore Your Workspace

Like Gradle, Nx understands your workspace as a graph of projects. Nx uses this graph for many things which we will learn about in following sections. To visualize this graph in your browser, Run the following command and click the "Show all projects" button in the left sidebar.

You will recognize that the projects which are shown, are the same projects which Gradle shows. The @nx/gradle plugin reflects the graph of projects in Gradle into the Nx Project Graph. As projects are created, deleted, and change their dependencies, Nx will automatically recalculate the graph. Exploring this graph visually is vital to understanding how your code is structured and how Nx and Gradle behaves.

{% tabs %} {% tab label="Mac/Linux" %}

./nx graph

{% /tab %} {% tab label="Windows" %}

./nx.bat graph

{% /tab %} {% /tabs %}

{% graph title="Gradle Projects" height="200px" jsonFile="shared/tutorials/gradle-project-graph.json" %} {% /graph %}

Running Tasks

Nx is a task runner built for monorepos. It can run a single task for a single project, a task for all projects, and even intelligently run a subset of tasks based on the changes you've made in your repository. Nx also has sophisticated computation caching to reuse the results of tasks. We will explore how Nx adds to the task running Gradle provides.

Before we start running tasks, let's explore the tasks available for the application project. The @nx/gradle plugin that we've installed allows Nx to run any of the Gradle tasks defined for that project. You can do this either through Nx Console or from the terminal:

./nx show project application --web

{% project-details title="Project Details View" jsonFile="shared/tutorials/gradle-pdv.json" %} {% /project-details %}

The Nx command to run the build task for the application project is:

./nx run application:build

When Nx runs a Gradle task, it hands off the execution of that task to Gradle, so all task dependencies and configuration settings in the Gradle configuration are still respected.

By running the task via Nx, however, the task computation was cached for reuse. Now, running ./nx run application:build again, will complete almost instantly as the result from the previous execution will be used.


   ✔  1/1 dependent project tasks succeeded [1 read from cache]

   Hint: you can run the command with --verbose to see the full dependent project outputs

—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————


> nx run application:classes  [existing outputs match the cache, left as is]

> ./gradlew :application:classes

> Task :library:compileJava UP-TO-DATE
> Task :library:processResources NO-SOURCE
> Task :library:classes UP-TO-DATE
> Task :library:jar UP-TO-DATE
> Task :application:compileJava UP-TO-DATE
> Task :application:processResources UP-TO-DATE
> Task :application:classes UP-TO-DATE

BUILD SUCCESSFUL in 647ms
4 actionable tasks: 4 up-to-date

> nx run application:build  [existing outputs match the cache, left as is]

> ./gradlew :application:build


Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.

You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.

For more on this, please refer to https://docs.gradle.org/8.5/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.

BUILD SUCCESSFUL in 768ms
9 actionable tasks: 1 executed, 8 up-to-date

—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

 NX   Successfully ran target build for project application and 3 tasks it depends on (30ms)

Nx read the output from the cache instead of running the command for 4 out of 4 tasks.

Now that we've run one task, let's run all the build tasks in the repository with the Nx run-many command. This is similar to Gradle's ./gradlew build command.


   ✔  nx run library:classes  [existing outputs match the cache, left as is]
   ✔  nx run library:build  [existing outputs match the cache, left as is]
   ✔  nx run application:classes  [existing outputs match the cache, left as is]
   ✔  nx run application:build  [existing outputs match the cache, left as is]
   ✔  nx run gradle-tutorial:classes (1s)
   ✔  nx run gradle-tutorial:build (1s)

—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

 NX   Successfully ran target build for 3 projects and 3 tasks they depend on (2s)

Nx read the output from the cache instead of running the command for 4 out of 6 tasks.

Again, because Nx cached the tasks when the application was built, most of the tasks here were near instant. The only ones which needed to be done is the root project's build. Running the command one more time, will be near instant as then all the tasks will be restored from the cache.

Run Tasks for Affected Projects

Nx doesn't just cache your task results, it can also eliminate the need to run unnecessary tasks.

First, commit any outstanding changes to the main branch locally:

git commit -am "changes"

Next make a small change to the application code:

package com.example.multimodule.application;

import com.example.multimodule.service.MyService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication(scanBasePackages = "com.example.multimodule")
@RestController
public class DemoApplication {

    private final MyService myService;

    public DemoApplication(MyService myService) {
        this.myService = myService;
    }

    @GetMapping("/")
    public String home() {
        return myService.message() + " changed!";
    }

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

As a developer, we know that this change only affects the application project, not the library project. We would run ./nx run application:test to verify our changes. In CI, teams often run all test tasks rerunning the library:test task unnecessarily.

For a repository with only a few projects, you can manually calculate which projects are affected. As the repository grows, it becomes critical to have a tool like Nx that understands the project dependency graph and eliminates wasted time in CI.

The ./nx affected command solves this problem. Nx uses its project graph in conjunction with git history to only run tasks for projects that may have been affected by the changes that you made.

Set up CI with Nx

This tutorial walked you through how Nx can improve the developer experience for local development, but Nx also makes a huge difference in CI. Without adequate tooling, CI times tend to grow exponentially with the size of the codebase. Nx helps reduce wasted time in CI with the affected command and Nx Replay's remote caching. Nx also efficiently parallelizes tasks across machines with Nx Agents.

To set up Nx Replay run:

./nx connect

And click the link provided. You'll need to follow the instructions on the website to sign up for your account.

Then you can set up your CI by running the @nx/gradle:ci-workflow generator:

./nx generate @nx/gradle:ci-workflow --ci=github

This generator creates the following file:

name: CI
on:
  push:
    branches:
      - main
  pull_request:
permissions:
  actions: read
  contents: read
jobs:
  main:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      # Connect your workspace on nx.app and uncomment this to enable task distribution.
      # The "--stop-agents-after" is optional, but allows idle agents to shut down once the "build" targets have been requested
      # - run: npx nx-cloud start-ci-run --distribute-on="5 linux-medium-jvm" --stop-agents-after="build"
      - name: Set up JDK 17 for x64
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'
          architecture: x64
      - name: Setup Gradle
        uses: gradle/gradle-build-action@v2
      - uses: nrwl/nx-set-shas@v4
      - run: ./nx affected -t test build

This is a default CI configuration that sets up Nx Cloud to use nx affected. This will only run the tasks that are needed for a particular PR.

You can also enable distributed task execution by uncommenting the nx-cloud start-ci-run line. This will automatically run all tasks on separate machines in parallel wherever possible, without requiring you to manually coordinate copying the output from one machine to another.

Check out one of these detailed tutorials on setting up CI with Nx:

Summary

Now that you have added Nx to this sample Gradle repository, you have learned several ways that Nx can help your organization:

  • Nx reflects the Gradle graph into the Nx graph
  • Nx's dependency graph visualisation helps you understand your codebase
  • Nx caches task results and reuses them when the same task is rerun later
  • Nx intelligently determines which tasks are affected by code changes to reduce waste in CI
  • Nx Cloud provides remote caching and distributed task execution to speed up CI

Next Steps

Connect with the rest of the Nx community with these resources: