I am trying to understand how exactly is the following Gradle script executed:
task loadTestData(dependsOn: ['fakeTask', createSchema])
I assume that:
loadTestData is a method call
dependsOn is a named argument
But on which object is the method called?
Actually a Task is being executed as part of gradle build workflow. Tasks in gradle get no parameters but can operate on the system/environment/build variables.
Then dependsOn which is a property of the Task gets the tasks the declared defined task is dependent on.
In this case you declare that task loadTestData is dependent on tasks fakeTask and createSchema.
Related
I am working with an Azure Pipeline in which I need to conditionally set the environment property. I am invoking the pipeline from a rest API call by passing Parameters in the body which is documented here.. When I try to access that parameter at compile time to set the environment conditionally though the variable is coming through as empty (assuming it is not accessible at compile time?)
Does anybody know a good way to solve for this via the pipeline or the API call?
After some digging I have found the answer to my question and I hope this helps someone else in the future.
As it turns out the Build REST API does support template parameters that can be used at compile time, the documentation just doesn't explicitly tell you. This is also supported in the Runs endpoint as well.
My payload for my request ended up looking like:
{
"Parameters": "{\"Env\":\"QA\"}",
"templateParameters": {"SkipApproval" : "Y"},
"Definition": {
"Id": 123
},
"SourceBranch": "main"
}
and my pipeline consumed those changes at compile time via the following (abbreviated version) of my pipeline
parameters:
- name: SkipApproval
default: ''
type: string
...
${{if eq(parameters.SkipApproval, 'Y')}}:
environment: NoApproval-All
${{if ne(parameters.SkipApproval, 'Y')}}:
environment: digitalCloud-qa
This is a common area of confusion for YAML pipelines. Run-time variables need to be accessed using a different syntax.
$[ variable ]
YAML pipelines go through several phases.
Compilation - This is where all of the YAML documents (templates, etc) comprising the final pipelines are compiled into a single document. Final values for parameters and variables using ${{}} syntax are inserted into the document.
Runtime - Run-time variables using the $[] syntax are plugged in.
Execution - The final pipeline is run by the agents.
This is a simplification, another explanation from Microsoft is a bit better:
First, expand templates and evaluate template expressions.
Next, evaluate dependencies at the stage level to pick the first stage(s) to run.
For each stage selected to run, two things happen:
All resources used in all jobs are gathered up and validated for authorization to run.
Evaluate dependencies at the job level to pick the first job(s) to run.
For each job selected to run, expand multi-configs (strategy: matrix or strategy: parallel in YAML) into multiple runtime jobs.
For each runtime job, evaluate conditions to decide whether that job is eligible to run.
Request an agent for each eligible runtime job.
...
This ordering helps answer a common question: why can't I use certain variables in my template parameters? Step 1, template expansion, operates solely on the text of the YAML document. Runtime variables don't exist during that step. After step 1, template parameters have been resolved and no longer exist.
[ref: https://learn.microsoft.com/en-us/azure/devops/pipelines/process/runs?view=azure-devops]
I have a Behat FeatureContext with some custom steps, and I'd like to do the following:
Initialize a variable/array/object when the test suite starts,
Have that variable/array/object available within the scope of each Scenario in the test suite,
Have values stored in the variable/array/object updated after all of the steps in each Scenario execute,
When test suite execution is complete, persist the values (writing out to a log file would be perfect).
Can this be done, and how would you go about structuring this? I'm attempting to do this using #BeforeSuite, #BeforeScenario and #AfterScenario hooks but have not yet been successful (my OOP is a little rusty).
I want to get the remaining early start and remaining early finish properties of an Activity while reading a Primavera .xer file. I have seen that the class PrimaveraPMFileReader in the mpxj library sets these properties to the task object, but am trying to import xer file and so the reader used is PrimaveraXERFileReader class. With the XER reader, the task instances are not set any properties that am looking for.
After going through the code of PrimaveraPMFileWriter and also some methods of the Task class of mpxj library, I have realized that to get remaining early start of a task in primavera, you need to use the getResume() API of the Task class of mpxj.
I'm pretty new to both gradle and groovy.
The Problem
I've got a very simple multi-project structure just like below:
Root project 'gradle_test'
+--- Project ':sub1'
\--- Project ':sub2'
This is what the 'build.grade' file looks like for sub1 project:
// build.gradle of sub1 project
task testConfiguration {
println project(':sub2').configurations.sub2FooConfiguration
}
And finally, this is the 'build.grade' file of sub2 project:
// build.gradle of sub2 project
configurations {
sub2FooConfiguration
}
Very minimum. Now, if I run gradle :sub1:testConfiguration, I got the following error:
A problem occurred evaluating project ':sub1'.
> Could not find property 'sub2FooConfiguration' on configuration container.
However, everything just works if the testConfiguration task in sub1 project is modified like this:
// notice the "<<" (I believe this is calling the 'doLast' method on the task instance)
task testConfiguration << {
println project(':sub2').configurations.sub2FooConfiguration
}
The Question
What I assume the difference between the two versions of the 'testConfiguration' task is that in the first instance, a configuration closure is passed to the task whereas in the modified version a 'normal' closure is passed to the 'doLast' method.
So, first of all, is my assumption correct?
Secondly, why I don't have access to 'sub2' project in the first instance?
And finally, is it possible to access 'sub2' project in the first instance (i.e. in the configuration closure)?
[Update] A Further Question
Given the accepted answer provided by "Invisible Arrow", I'd like to ask a further question regarding the best practice of referencing a configuration of another project (i.e. a task in sub1 needs to use an archive produced by sub2 project).
Should I declare evaluation dependency between the two projects?
Or Should I only reference sub2's configuration at execution time (e.g. in doLast())?
Or, should I create a dependency configuration between the two projects?
Yes, there is a difference between the two.
There are essentially 3 phases to a build which are Initialization, Configuration and Execution. This is described in detail under the Build Lifecycle chapter in the Gradle documentation.
In your case, the first instance falls under the Configuration phase, which is always evaluated irrespective of whether the task is executed or not. That means all statements within the closure are executed when you start a build.
task testConfiguration {
// This always runs during a build,
// irrespective of whether the task is executed or not
println project(':sub2').configurations.sub2FooConfiguration
}
The second instance falls under the Execution phase. Note that << is a shorthand for doLast, and this closure is called when the task is executed.
task testConfiguration << {
// Called during actual execution of the task,
// and called only if the task was scheduled to be executed.
// Note that Configuration phase for both projects are complete at this point,
// which is why :sub1 is able to access :sub2's configurations.sub2FooConfiguration
println project(':sub2').configurations.sub2FooConfiguration
}
Now coming to why the first instance gave the error. This was because the Configuration phase of sub2 project was not evaluated yet. Hence the sub2FooConfiguration wasn't yet created.
Why? Because there is no explicit evaluation dependency between sub1 and sub2. In your case, sub1 needs sub2 as an evaluation dependency, hence we can add that dependency in sub1's build.gradle before the task declaration, as follows:
evaluationDependsOn(':sub2')
task testConfiguration {
println project(':sub2').configurations.sub2FooConfiguration
}
This will ensure that sub2 is always evaluated before sub1 (evaluation meaning the Configuration phase for the projects). sub1 will now be able to access configurations.sub2FooConfiguration in the task declaration closure. This is explained in detail in the Multi-project Builds chapter.
In the second instance, configurations.sub2FooConfiguration was accessible, as the call was in the execution block of the task (which is after the Configuration phase for both projects).
PS: Note that if you reversed the names of the projects, then the first instance might actually work, as Gradle configures projects alphabetically if there are no explicit dependencies. But, of course, you should never rely on this and ensure that the dependencies between projects are declared explicitly.
Consider the following method invokation containing in the gradle build script:
gradle.taskGraph.whenReady{taskGraph ->
println gradle.toString()
println "Ready"
}
It prints
build 'task_graph'
Ready
I thought we work in the scope of Project object, since gradle should be a property of that Project object. But there is neither property nor even method with such name. Why can we use it in the build script?
I may be wrong but I think your confusion is that there exists a getGradle() method on the Project interface but no such public field named gradle. This is a Groovy feature. In Groovy, getter and setter methods can by referenced as properties. For example:
println project.description // same as project.getDescription()
project.description = 'My java project' // same as project.setDescription('My java project')
I'd highly suggest familiarizing yourself with Groovy by checking out their documentation. You'll see a lot of differing syntax in Gradle examples simply because there are many different ways to accomplish the same thing in Groovy.