Skip to content

Commit

Permalink
Prepare for configuration cache (#118)
Browse files Browse the repository at this point in the history
  • Loading branch information
marchermans committed Feb 19, 2024
1 parent 62f5d5d commit da5be81
Show file tree
Hide file tree
Showing 15 changed files with 482 additions and 63 deletions.
57 changes: 57 additions & 0 deletions .github/gradle/gradle.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
if (!project.hasProperty('githubCiTesting') && System.env['GITHUB_OUTPUT'] == null) {
// If the property is not set and we are not running in the CI environment then we will do nothing
return
}

static def outputUnitTest(Project project, Task test) {
project.getLogger().lifecycle("<< TEST >>${test.path}")
}

static def outputClassTest(Project project, Task test, String className) {
project.getLogger().lifecycle("<< TEST >>${test.path} --tests \"${className}\"")
}

subprojects.forEach { Project subProject ->
subProject.tasks.register('determineTests') { Task it ->
it.group = 'infrastructure'
it.doLast {
subProject.tasks.withType(Test).forEach { Task test ->
def testSourceSetCandidate = test.extensions.findByName('test-source-set')
if (testSourceSetCandidate != null) {
SourceSet testSourceSet = testSourceSetCandidate as SourceSet

testSourceSet.java.srcDirs
.collect { File file ->
subProject.fileTree(file.parentFile)
}
.collect { FileTree fileTree ->
return fileTree.matching {
include '**/*Test.java'
include '**/*Tests.java'
include '**/*Test.groovy'
include '**/*Tests.groovy'
}
}
.forEach {
it.visit { FileVisitDetails details ->
if (details.isDirectory())
return;

String className = details.relativePath.pathString
.replace("groovy/", "")
.replace("java/", "")
.replace(".groovy", "")
.replace(".java", "")
.replace("/", ".")

outputClassTest(subProject, test, className)
}
}

} else {
outputUnitTest(subProject, test)
}
}
}
}
}
11 changes: 11 additions & 0 deletions .github/scripts/collect-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env bash

./gradlew determineTests | grep -e '<< TEST >>' | sed -e 's/<< TEST >>//g' > ./tasks
TESTS=$(cat tasks | jq --raw-input . | jq --compact-output --slurp .)
# Check if the GITHUB_OUTPUT is set
if [ -z "$GITHUB_OUTPUT" ]; then
# We do not have github output, then use the set output command
echo "::set-output name=tests-to-run::$TESTS"
exit 0
fi
echo "tests-to-run=$TESTS" >> "$GITHUB_OUTPUT"
115 changes: 108 additions & 7 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,37 @@ on:
- reopened

jobs:
setup:
name: Setup
runs-on: ubuntu-latest
outputs:
tests-to-run: ${{ steps.test.outputs.tests-to-run }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1000
fetch-tags: true

- name: Setup JDK
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'microsoft'

- name: Setup Gradle
uses: gradle/gradle-build-action@v3

# Runs the collect-tests shell script and sets the output variable
- name: Determine tests to run
id: test
run: |
#!/bin/bash
./.github/scripts/collect-tests.sh
build:
name: Build and Test
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout repository
Expand All @@ -21,28 +50,100 @@ jobs:
fetch-tags: true

- name: Setup JDK
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'microsoft'

- name: Setup Gradle
uses: gradle/gradle-build-action@v2
uses: gradle/gradle-build-action@v3

- name: Build
run: ./gradlew --info -s -x check build

- name: Test
run: ./gradlew --info -s check
run: ./gradlew --info -s -x assemble

- name: Publish artifacts
uses: neoforged/action-pr-publishing/upload@v1

test:
name: Test
runs-on: ubuntu-latest
needs: setup
strategy:
fail-fast: false
matrix:
test: ${{ fromJSON(needs.setup.outputs.tests-to-run) }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1000
fetch-tags: true

- name: Setup JDK
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'microsoft'

- name: Setup Gradle
uses: gradle/gradle-build-action@v3

- name: Test
run: ./gradlew --info -s ${{ matrix.test }}

# Always upload test results
- name: Merge Test Reports
if: success() || failure()
run: npx junit-report-merger junit.xml "**/TEST-*.xml"

- name: Format test run name as artifact name
id: format-artifact-name
# Use the GITHUB_OUTPUT mechanic to set the output variable
run: |
# We have two cases here, one with a gradle task path, there we replace the : with a - and strip the leading -
# The other case is complexer, again we replace the : with a - and strip the leading - but now we also remove the ' --tests ' part
# Remove the '"' from the string and replace the '.' with a '-'
NAME=$(echo "${{ matrix.test }}" | sed 's/:/-/g' | sed 's/^-//' | sed 's/ --tests /-/g' | sed 's/"//g' | sed 's/\./-/g')
# Check if the GITHUB_OUTPUT is set
if [ -z "$GITHUB_OUTPUT" ]; then
# We do not have github output, then use the set output command
echo "::set-output name=artifact-name::$NAME"
exit 0
fi
echo "artifact-name=$NAME" >> "$GITHUB_OUTPUT"
- uses: actions/upload-artifact@v4
if: success() || failure()
with:
if-no-files-found: ignore
name: test-results-${{ steps.format-artifact-name.outputs.artifact-name }}
path: junit.xml
retention-days: 1

process-test-data:
runs-on: ubuntu-latest
needs: test
if: success() || failure()
steps:
- uses: actions/checkout@v3

- name: Download reports' artifacts
uses: actions/download-artifact@v4
with:
pattern: test-results-**
path: downloaded_artifacts

- name: Publish Test Report
uses: mikepenz/action-junit-report@v4
if: success() || failure() # always run even if the previous step fails
with:
report_paths: '**/*.xml'

- name: Merge Test Reports
if: success() || failure()
run: npx junit-report-merger junit.xml "**/*.xml"

- uses: actions/upload-artifact@v4
if: success() || failure()
with:
Expand Down
86 changes: 86 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# NeoGradle
[![Release](https://github.com/neoforged/NeoGradle/actions/workflows/release.yml/badge.svg?branch=NG_7.0)](https://github.com/neoforged/NeoGradle/actions/workflows/release.yml)

---

Minecraft mod development framework, used by NeoForge and FML for the Gradle build system.

Expand Down Expand Up @@ -128,3 +131,86 @@ using [Gradle properties](https://docs.gradle.org/current/userguide/project_prop
| `neogradle.subsystems.recompiler.maxMemory` | How much heap memory is given to the decompiler. Can be specified either in gigabyte (`4g`) or megabyte (`4096m`). Defaults to `1g`. |
| `neogradle.subsystems.recompiler.jvmArgs` | Pass arbitrary JVM arguments to the forked Gradle process that runs the compiler. I.e. `-XX:+HeapDumpOnOutOfMemoryError` |
| `neogradle.subsystems.recompiler.args` | Pass additional command line arguments to the Java compiler. |

## Run specific dependency management
This implements run specific dependency management for the classpath of a run.
In the past this had to happen via a manual modification of the "minecraft_classpath" token, however tokens don't exist anymore as a component that can be configured on a run.
It was as such not possible to add none FML aware libraries to your classpath of a run.
This PR enables this feature again.


### Usage:
#### Direct
```groovy
dependencies {
implementation 'some:library:1.2.3'
}
runs {
testRun {
dependencies {
runtime 'some:library:1.2.3'
}
}
}
```
#### Configuration
```groovy
configurations {
libraries {}
implementation.extendsFrom libraries
}
dependencies {
libraries 'some:library:1.2.3'
}
runs {
testRun {
dependencies {
runtime project.configurations.libraries
}
}
}
```
#### Run Dependency Handler
The dependency handler on a run works very similar to a projects own dependency handler, however it has only one "configuration" available to add dependencies to: "runtime". Additionally, it provides a method to use when you want to turn an entire configuration into a runtime dependency.

## Handling of None-NeoGradle sibling projects
In general, we suggest, no strongly encourage, to **not** use fat jars for this solution.
The process of creating a fat jar with all the code from your sibling projects is difficult to model in a way that is both correct and efficient for a dev project, especially if the sibling project does not use NeoGradle.

### Sibling project uses a NeoGradle module
If it is possible to use a NeoGradle module (for example the Vanilla module, instead of VanillaGradle) then you can use the source-set's mod identifier:
```groovy
sourceSets {
main {
run {
modIdentifier '<some string that all projects in your fat jar have in common>'
}
}
}
```
The value of the modIdentifier does not matter here, all projects with the same source-set mod identifier will be included in the same fake fat jar when running your run.

### Sibling project does not use NeoGradle
If the sibling project does not use NeoGradle, then you have to make sure that its Manifest is configured properly:
```text
FMLModType: GAMELIBRARY #Or any other mod type that is not a mod, like LIBRARY
Automatic-Module-Name: '<some string that is unique to this project>'
```
> [!CAUTION]
> If you do this, then your sibling projects are not allowed to contain a class in the same package! This is because no two modules are allowed to contain the same package.
> If you have two sibling projects with a class in the same package, then you will need to move one of them!
### Including the sibling project in your run
To include the sibling project in your run, you need to add it as a modSource to your run:
```groovy
runs {
someRun {
modSource sourceSets.main
modSource project(':siblingProject').sourceSets.main
}
}
```
No other action is needed.
9 changes: 7 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ changelog {

///First setup all the subprojects
///Configure them in such a way that we can run test and have all the plugins applied.
subprojects.forEach { subProject ->
subprojects.forEach { Project subProject ->
//All base plugins.
subProject.apply plugin: 'net.neoforged.gradleutils'
subProject.apply plugin: 'java-library'
Expand All @@ -54,7 +54,7 @@ subprojects.forEach { subProject ->
//General project metadata. Everything has the same version and group.
subProject.version = subProject.rootProject.version
subProject.group = 'net.neoforged.gradle'
subProject.archivesBaseName = "ng-${subProject.name.toLowerCase()}"
subProject.base.archivesName = "ng-${subProject.name.toLowerCase()}"

//Setup the java toolchain
subProject.java.toolchain.languageVersion = JavaLanguageVersion.of(project.java_version)
Expand Down Expand Up @@ -185,6 +185,8 @@ subprojects.forEach { subProject ->

testClassesDirs = evalSubProject.sourceSets.functionalTest.output.classesDirs
classpath = evalSubProject.sourceSets.functionalTest.runtimeClasspath

it.extensions.add('test-source-set', evalSubProject.sourceSets.functionalTest)
}

//Wire them up so they run as part of the check task (and as such through build, but not through test!)
Expand Down Expand Up @@ -232,3 +234,6 @@ subprojects.forEach { subProject ->
}
}
}

//Apply the custom gradle file for handling the github ci infrastructure.
apply from: '.github/gradle/gradle.gradle'
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ public ListLibraries() {
return new File(arguments.get("bundle").get());
}));
getOutputFileName().set("libraries.txt");
getIsOffline().convention(getProject().getGradle().getStartParameter().isOffline());
}

@Input
public abstract Property<Boolean> getIsOffline();

@ServiceReference(CommonProjectPlugin.LIBRARIES_SERVICE)
public abstract Property<CentralCacheService> getLibrariesCache();
Expand Down Expand Up @@ -154,7 +158,7 @@ private Set<File> downloadAndListJsonLibraries() throws IOException {
params.getShouldValidateHash().set(true);
params.getSha1().set(libraryCoordinate.hash);
params.getOutputFile().set(outputFile);
params.getIsOffline().set(getProject().getGradle().getStartParameter().isOffline());
params.getIsOffline().set(getIsOffline());
});
result.add(outputFile);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ public <S extends T> NamedDomainObjectSet<S> withType(Class<S> type) {
return delegate.withType(type);
}

@Override
public NamedDomainObjectSet<T> named(Spec<String> nameFilter) {
return delegate.named(nameFilter);
}

@Override
public NamedDomainObjectSet<T> matching(Spec<? super T> spec) {
return delegate.matching(spec);
Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
3 changes: 2 additions & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading

0 comments on commit da5be81

Please sign in to comment.