5 minute read

Here are a few examples of using a combination of Nyx flavors together in the same build process.

The following use cases are just examples and can be inverted and recombined in any case of multi-stage builds.

GitHub Actions and Gradle

Here we show the usage of a GitHub Actions pipeline, running a combination of tasks defined in Gradle scripts (also using the Nyx Gradle plugin) and other Actions.

In this scenario we need Nyx both within Gradle scripts and the Actions pipeline because both need the version attribute inferred from Nyx and also use conditionals for jobs to run only when a new release has to be published.

Let’s give Nyx a configuration, using the standard name .nyx.yaml so we don’t need to pass a custom configurationFile.

resume: true
stateFile: "build/.nyx-state.yml"

here we just give the two options that are relevant for a combined use.

Now take a look at the Gradle scripts. Here we apply the plugin to the settings.gradle file (using the settings.gradle runs Infer implicitly in the early stage of the build):

plugins {
  id "com.mooltiverse.oss.nyx" version "3.0.0"
}

and here in the build.gradle file we define a job that reads the version attribute from the global project properties but runs only when a new release has to be published (reading the flag from the nyxState):

// This task is just an example
task preRelease() {
    // This task will not run unless "newRelease" is set to true by Nyx
    onlyIf { rootProject.nyxState.newRelease }
    doLast {
        // Access the project version property
        logger.info("Project version is: ${rootProject.version}")
        // Run any other task you may need here
        ...
    }
}

The rootProject prefix is always safe to use but you can mot it unless you’re working on a multi-project build.

So far nothing new, we have a Gradle build script that we can run locally or on any CI/CD platform. Now let’s see what we can do in the GitHub Actions workflow:

name: My workflow
on: [push]

jobs:
  one:
    name: Initialize
    runs-on: ubuntu-latest
    steps:
    # Checkout the entire repository and tags to let Nyx inspect the whole repository
    - name: Git checkout
      uses: actions/checkout@v3
      with:
        fetch-depth: 0
    # Java is required for Gradle, don't pay attention to the version
    - name: Set up JDK 20
      uses: actions/setup-java@v3
      with:
        distribution: 'temurin'
        java-version: 20
    # This job only runs nyxInfer to produce the initial version and write the state file
    # Since Nyx is applied as a settings plugin the nyxInfer task is not even needed, but Gradle needs a task to run
    - name: Nyx Infer
      run: ./gradlew nyxInfer
    # Here we use the Nyx GitHub Action just to read the data from its state file (generated in the above step)
    - name: Load Nyx data
      id: nyx
      uses: mooltiverse/nyx-github-action@main
      # The following parameters are here just to show but 'infer' is the default command while the other options are set in the configuration file
      #with:
      #  command: infer
      #  resume: true
      #  stateFile: .nyx-state.json
    # Run the Gradle task. This will not run Infer again because the state file has been produced in the previous step
    - name: Pre release
      run: ./gradlew preRelease
    # Run another task
    - name: Post release
      # This step will run only if Nyx has determined that a new release must be issued
      if: $
      run: <some other command here>

And here is a slightly modified version, showing the same steps but spread into multiple jobs. As you can see, using different jobs adds additional needs like a cache to bring the Nyx state ahead from one job to another and the declaration of job outputs:

name: My workflow
on: [push]

jobs:
  one:
    name: Initialize
    runs-on: ubuntu-latest
    outputs:
      # Make the 'newRelease' outputs from the Nyx action available for other jobs
      newRelease: $
    steps:
    # Checkout the entire repository and tags to let Nyx inspect the whole repository
    - name: Git checkout
      uses: actions/checkout@v3
      with:
        fetch-depth: 0
    # Java is required for Gradle, don't pay attention to the version
    - name: Set up JDK 20
      uses: actions/setup-java@v3
      with:
        distribution: 'temurin'
        java-version: 20
    # Set up a cache that stores the Nyx state so that other jobs can retrieve it
    - name: Set up the Nyx state cache
      uses: actions/cache@v3
      with:
        path: |
          build/
        key: nyx-state
        #restore-keys: not used here, start from scratch
    # This job only runs nyxInfer to produce the initial version and write the state file
    # Since Nyx is applied as a settings plugin the nyxInfer task is not even needed, but Gradle needs a task to run
    - name: Nyx Infer
      run: ./gradlew nyxInfer
    # Here we use the Nyx GitHub Action just to read the data from its state file (generated in the above step)
    - name: Load Nyx data
      id: nyx
      uses: mooltiverse/nyx-github-action@main
      # The following parameters are here just to show but 'infer' is the default command while the other options are set in the configuration file
      #with:
      #  command: infer
      #  resume: true
      #  stateFile: .nyx-state.json

  two:
    name: Pre release
    needs: one
    # We don't actually need this conditional statement here because the gradle task uses the 'onlyIf' clause
    #if: $
    runs-on: ubuntu-latest
    steps:
    # Checkout the entire repository and tags to let Nyx inspect the whole repository
    - name: Git checkout
      uses: actions/checkout@v3
      with:
        fetch-depth: 0
    # Java is required for Gradle, don't pay attention to the version
    - name: Set up JDK 20
      uses: actions/setup-java@v3
      with:
        distribution: 'temurin'
        java-version: 20
    # Set up a cache that stores the Nyx state so that other jobs can retrieve it
    - name: Set up the Nyx state cache
      uses: actions/cache@v3
      with:
        path: |
          build/
        key: nyx-state
        restore-keys: |
          nyx-state
    # Run the Gradle task. This will not run Infer again because the state file, produced in the previous job, is retrieved from the cache
    - name: Pre release
      run: ./gradlew preRelease

  three:
    name: Post release
    needs: one
    # This job will run only if Nyx has determined that a new release must be issued
    if: $
    runs-on: ubuntu-latest
    steps:
    # Run another task
    - name: Post release
      run: <some other command here>

The two steps Nyx Infer and Load Nyx data in the above examples could be applied in any order as the first one actually creates the State file while the second loads it.

When using conditionals in GitHub Actions you may also find the dorny/paths-filter Action useful.

Command line and Gradle

Here we run Nyx primarily from the command line but then we need an easy way to use its outputs from Gradle build scripts in a structured way instead of scraping the output or parsing the output file.

The Nyx and Gradle settings look just like the above example, so:

.nyx.yaml:

resume: true
stateFile: "build/.nyx-state.yml"

settings.gradle:

plugins {
  id "com.mooltiverse.oss.nyx" version "3.0.0"
}

build.gradle:

// This task is just an example
task preRelease() {
    // This task will not run unless "newRelease" is set to true by Nyx
    onlyIf { rootProject.nyxState.newRelease }
    doLast {
        // Access the project version property
        logger.info("Project version is: ${rootProject.version}")
        // Run any other task you may need here
        ...
    }
}

Then, when running:

$ nyx
$ ./gradlew preRelease
Project version is: 1.2.3