How to find gradle subproject tasks from different working directory - gradle

I'm running into the problem that my gradle wrapper will only find subprojects if I execute it whilst being in the same working directory. For example:
Let's say the project structure is as follows:
.
├── app
│   ├── build.gradle
│   ├── ...
├── build.gradle
├── gradlew
├── settings.gradle
└── ...
It makes a difference whether I run gradlew from it's directory or from a different directory. If I run:
$ ./gradlew projects
> Task :projects
------------------------------------------------------------
Root project
------------------------------------------------------------
Root project 'com.name'
+--- Project ':app'
it has no problem finding :app. However, if I navigate and execute gradlew from a folder up, it cannot find it:
$ cd ..
$ ./android/gradlew projects
> Task :projects
------------------------------------------------------------
Root project
------------------------------------------------------------
Root project 'com'
No sub-projects
It can't find the projects. This is problematic for me since I need to run a task in :app from a pipeline from a different working directory, e.g. ./xx/yy/gradlew app:publishTask. However doing it this way, gradle can't find the task because it can't find the project. Is there a way to run these commands from any location?

Yes, it is. You have to:
store your current location in an temporary variable
change location to project directory
run ./gradlew
restore directory from variable
ex:
TMP_DIR=`pwd`
cd /path/to/project
./gradlew projects
cd $TMP_DIR

Related

How to COPY files from outside of the Earthfile's directory?

Suppose I have a monorepo. Apps (app1, app2) use a Gradle as a build system and share some build-logic with includeBuild("../shared-build-logic") which is outside of the root of each app.
├── shared-build-logic
│   └── src/...
└── app1
├── Earthfile
├── build.gradle
├── src/...
└── app2
├── Earthfile
├── build.gradle
├── src/...
Is it possible for Earthfile to access the files from outside of its root folder or Earthly has the same restrictions as Dockerfile?
I get the following error on attempt to COPY ../shared-build-logic ./:
============================ ❌ FAILURE [2. Build 🔧] ============================
Repeating the output of the command that caused the failure
+compile *failed* | --> COPY ../shared-build-logic ./
+compile *failed* | [no output]
+compile *failed* | ERROR Earthfile line 22:4
+compile *failed* | The command
+compile *failed* | COPY ../shared-build-logic ./
+compile *failed* | failed: "/shared-build-logic": not found
I would also like to perform integration testing with the docker-compose.yaml file located one level above the Eartfile root, but facing the similar problem:
integration-tests:
FROM earthly/dind:alpine
COPY ../docker-compose.yaml ./ # <------- does not work
WITH DOCKER --compose docker-compose.yaml --load build-setup=+compile --allow-privileged
RUN docker run -e SPRING_DATA_MONGODB_URI=mongodb://mongodb:27017/test-db --network=default_dev-local build-setup ./gradlew test
END
Is my the only solution to the problem to move Earthfile itself one level upper?
While you can't directly access targets outside of your Earthfile directory you can reference targets.
This allows you to write a target in an Earthfile under shared-build-logic that saves an artifact containing those files
You can expose the files you need by using a target.
shared-build-logic/Earthfile
files:
WORKDIR files
# Copy all of files you want to share
SAVE ARTIFACT ./*
app/Earthfile
use-files:
COPY ../shared-build-logic+files/* .
# do stuff
You should be able to do something similar with you integration-test target.
Earthfile
files:
WORKDIR files
SAVE ARTIFACT docker-compose.yaml
folder-with-integration-tests/Earthfile
integration-tests:
FROM earthly/dind:alpine
COPY ../+files/docker-compose.yaml ./
WITH DOCKER --compose docker-compose.yaml --load build-setup=+compile --allow-privileged
RUN docker run -e SPRING_DATA_MONGODB_URI=mongodb://mongodb:27017/test-db --network=default_dev-local build-setup ./gradlew test
END

Go workspace dependencies

I'm trying to get to grips with working with workspaces in go 1.18, and how to make it work well in a monorepo.
As a minimum example, I have the following project structure:
.
├── docker-compose.yml
├── go.work
├── makefile
└── project
├── go.mod
├── module1
│ ├── Dockerfile
│ ├── go.mod
│ └── main.go
├── module2
│ ├── Dockerfile
│ ├── go.mod
│ └── main.go
└── shared-module
├── go.mod
└── shared.go
module1 and module2 can both be built into binaries with their respective main.go files. However, module1 uses shared-module, but module2 does not.
When building the binaries without Docker, I can cd into module1 or module2 and run go build and everything works fine. However problems occur when I want to use Docker for the builds.
Here is the Dockerfile for module1:
# FIRST STAGE
FROM golang:1.18 AS builder
WORKDIR /app/
# copy workfiles
COPY go.* ./
WORKDIR /app/project/
# dependency management files
COPY project/go.* ./
COPY project/module1/go.* ./module1/
COPY project/shared-module/go.* ./shared-module/
WORKDIR /app/project/module1/
RUN go mod download
WORKDIR /app/project/
# copy shared module
COPY project/shared-module/ ./shared-module/
# copy module to compile
COPY project/module1/ ./module1/
WORKDIR /app/project/module1/
RUN CGO_ENABLED=0 GOOS=linux go build -o bin/module1
# SECOND STAGE
FROM alpine:3.14.2
WORKDIR /app
COPY --from=builder /app/project/module1/bin/module1 /app
ENTRYPOINT [ "./module1" ]
With this build i'm trying to maximise caching by specifying files which change infrequently first. I'm also excluding any files which I don't need for the module (like module2).
Running docker-compose build module1 to build the image using that Dockerfile, the build fails with error:
go: open /app/project/module2/go.mod: no such file or directory
This initially surprised me, as module2 is not a dependency of either module1, or shared-module, but after a bit of consideration I realised it was because of the go.work file which specifies ./project/module2. Removing the line in go.work that specifies that module allows the image to be built successfully.
My question is therefore, if I want to have streamlined image builds, do I have to create multiple go.work files for each of the modules I want to build in Docker? For example, I would need another go.work file for module2 which omits module1 and shared-module.

Make gradle point to subdirectory and treat it as a rootProject

I encountered a problem with gradle project structure. I have a task that needs to be realized and some tests are meant to be executed to check whether my project structure is correct and the tasks in gradle execute correctly. However I think I misunderstood instruction a bit and I'm wondering whether I can do something with my current folders structure or If I will have to rewrite the whole project. My current project structure looks like this:
main-repo-folder/
├── docker-related-file
├── rootProject
│ ├── sub-project-1
│ ├── build(output from tasks is created here)
│ ├── build.gradle
│ ├── sub-project-2
│ ├── gradle
│ ├── gradlew
│ ├── gradlew.bat
│ ├── settings.gradle
│ └── src
As you can see, the root project is a directory inside a repo. In order for my tests to execute I think the repo itself must be a root folder (or act as one) because the tests seem to be trying executing there. And here is my question, is it possible to add f.e settings.gradle file in main-repo-folder (at the same level as rootProject folder) to "point" gradle to build from rootProject and treat that folder as the root?(I mean f.e if I call gradle clean build task_name in main-repo-folder I want to make gradle execute it as I would be in rootProject folder)
I've tried to find some information but I'm at the path of learning gradle and I don't know if it is even possible :/ .
Rename main-repo-folder/rootProject to main-repo-folder.

gradle could not find other gradle script

/--common
/--common/build.gradle
/--common/deploy.gradle
/--project1
/--project1/build.gradle
I have a multi-project structure and have extracted repeating code from my build.gradle file and placed this into another file deploy.gradle.
I have placed the deploy.gradle file into the common project at the same folder level as the build.gradle file. The folder structure is shown above.
In the build.gradle file of the common project I can reference the file using the statement,
apply from: 'deploy.gradle'
This works like a dream and the common project build works perfectly calling in the tasks from the deploy.gradle file.
The problem comes when I try to reference deploy.gradle file from one of the other projects. When I add the apply... statement to the build.gradle of project1 I get the compilation error,
Error:(23, 0) Could not read script
'C:\path-to-project1-script-file\deploy.gradle' as it does not exist.
So Gradle is looking for the deploy.gradle file in project1 only even though I have a dependency set to the common project in the project1 build.gradle file.
Question is how can I make deploy.gradle from common project visible to project1.
We successfully use the following project layout
├── a
│   └── build.gradle
├── b
│   └── build.gradle
├── build.gradle
├── gradle-scripts
│   └── deploy.gradle
└── settings.gradle
The rootproject's build.gradle defines
ext.gradleScript = { scriptName ->
file("${rootProject.projectDir}/gradle-scripts/${scriptName}.gradle")
}
Subprojects use the scripts within gradle-scripts this way
apply from: gradleScript('deploy')
Whole content of the project:
$> find . -type f | while read file; do echo "--- $file ---" && cat $file; done
--- ./a/build.gradle ---
apply from: gradleScript('deploy')
--- ./b/build.gradle ---
apply from: gradleScript('deploy')
--- ./build.gradle ---
// Where common build logic is found
ext.gradleScript = { scriptName ->
file("${rootProject.projectDir}/gradle-scripts/${scriptName}.gradle")
}
--- ./gradle-scripts/deploy.gradle ---
task myDeployTask {
doLast { println 'common deploy logic goes here' }
}
--- ./settings.gradle ---
include 'a', 'b'
$> gradle -q b:myDeployTask
common deploy logic goes here
$>
Here is an example project1/build.gradle that references common/deploy.gradle:
// import myDeploy task
apply from: "${rootDir}/common/deploy.gradle"
task myProject1Task(dependsOn: 'myDeploy') {
doLast {
println 'TRACER myProject1Task'
}
}
It is often important to distinguish projectDir from rootDir in multi-project builds. projectDir is the specific subproject; rootDir is where settings.gradle lives.

How to include a Gradle distribution in the project to be used for the wrapper

I want to include a specific version of Gradle in the project folder so that when I use the Gradle wrapper it doesn't download it from the remote repository.
I downloaded the version of Gradle I need (gradle-4.0-bin.zip) and I put that zip fine inside of gradle/wrapper/ folder of the project (created with the gradle wrapper command).
Then I edited the gradle-wrapper.properties file in this way:
distributionUrl=file:///Users/pathj/to/the/project/gradle/wrapper/gradle-4.0-bin.zip
But when I run the first command, such as gradle task it returns:
What went wrong: A problem occurred configuring root project '03-gradle-wrapper-local'.
java.io.FileNotFoundException: /Users/myself/.gradle/wrapper/dists/gradle-4.0-bin/3p92xsbhik5vmig8i90n16yxc/gradle-4.0/lib/plugins/gradle-diagnostics-4.0.jar
(No such file or directory)
How do I tell Gradle to get the zip file from the current project folder, with a relative path, instead of downloading it, and to use that zip file to create a wrapper to be used in my builds?
Apart from storing gradle wrapper locally make sense or not it is possible. I assume that gradle-4.0-rc-3-bin distro is used.
Here is the project structure:
.
├── gradle
│   └── wrapper
│   ├── gradle-4.0-rc-3-bin.zip
│   ├── gradle-wrapper.jar
│   └── gradle-wrapper.properties
├── gradlew
└── gradlew.bat
And here the content of gradle-wrapper.properties:
distributionBase=PROJECT
distributionPath=gradle
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=gradle-4.0-rc-3-bin.zip
Since wrapper files will be downloaded to the project dir adding gradle/gradle-4.0-rc-3-bin to SCM ignore file is recommended.
Demo can be found here.

Resources