How do I read a multiline value using the Ant 'input' task? - ant

Anyone know how I can enter a multiline value in an Ant script? I'm prompting the user for a Subversion commit comment using the input task, and I'd like to be able to support multiple lines of text.
I'm running the standalone version of Ant at the Windows command prompt.
I thought I might be able to do a search and replace for \n, but I can't see any easy way to do a replace from property value to property value in Ant. It looks like I'd have to write a file, replace in the file, and then load the file into another property. I don't want it that badly.

I'm not 100% positive about this, but I took a look at the Ant source code, and it just does a readLine():
From /org/apache/tools/ant/input/DefaultInputHandler.java:
/**
* Prompts and requests input. May loop until a valid input has
* been entered.
* #param request the request to handle
* #throws BuildException if not possible to read from console
*/
public void handleInput(InputRequest request) throws BuildException {
String prompt = getPrompt(request);
BufferedReader r = null;
try {
r = new BufferedReader(new InputStreamReader(getInputStream()));
do {
System.err.println(prompt);
System.err.flush();
try {
String input = r.readLine();
request.setInput(input);
} catch (IOException e) {
throw new BuildException("Failed to read input from"
+ " Console.", e);
}
} while (!request.isInputValid());
} finally {
if (r != null) {
try {
r.close();
} catch (IOException e) {
throw new BuildException("Failed to close input.", e);
}
}
}
}
Here is what I would do if I were you:
If you are using Ant 1.7, then try implementing your own InputHandler, as described in the documentation. The Apache License permits you to basically copy-and-paste the above code as a starting point.
If you are using Ant 1.6 or earlier, then just create your own MultiLineInput task. You can extend the existing Input class and just read multiple lines.
In either case, you would need to decide how the user indicates "I'm done." You could use a blank line or a period or something.
Good luck!
P.S. When I did a Google search for "ant multi-line input", this page was the first hit :-). Pretty impressive for a question that was asked less than an hour ago.

Related

Quit a specflow scenario, or avoid execution based on a precondition

I want to be able to avoid a specflow scenario execution, or quit at the first step, depending on external configuration. Is that possible?
Like
Background:
Given we have a satisfied precondition
the precondition fails and the scenario stops without returning an error
IUnitTestRuntimeProvider interface should be injected
https://docs.specflow.org/projects/specflow/en/latest/Execution/SkippingScenarios.html
however, I then run into the fact that this interface can not be resolved
https://github.com/SpecFlowOSS/SpecFlow/issues/2076
the resolution of this does not work for me in a .Net 5, XUnit project, with Visual Studio 2019
If a step does not throw an exception, then the step passes. You could refactor that particular step into a private method, then simply wrap the method call in a try-catch inside an if statement:
[Given(#"we have a satisfied precondition")]
public void GivenWeHaveASatisfiedPrecondition()
{
if (/* check your external config here */)
{
try
{
PerformPrecondition();
}
catch (Exception ex)
{
Console.WriteLine("Step failed, but continuing scenario." + Environment.NewLine + Environment.NewLine + ex);
}
}
else
{
// Failure in this step should fail the scenario.
PerformPrecondition();
}
}
private void PerformPrecondition()
{
// Your step logic goes here
}
You haven't specified where your external configuration is at, but there are a thousand ways to do this.

Jenkinsfile (Groovy) URI validator

I'm fairly new to Groovy and Jenkins, so hopefully this question is coherent.
I have a Jenkinsfile written in Groovy and would like to validate one of the params as a valid URI. Without writing my own regex check, is there a library I could easily invoke during Jenkins startup?
You can try this:
try {
def foo = new java.net.URL("yourURI").openStream()
if (foo.getClass() == sun.net.www.protocol.http.HttpURLConnection$HttpInputStream) {
println 'valid'
foo.close()
}
}
catch (java.io.FileNotFoundException e) {
println 'not valid'
return
}
Unfortunately URL.toUri is not allowed at least in our setup. (It could possibly be allowed with a separate config.) Apparently opening the url (trying to connect to the host) could be possible, but that feels like it could cause other problems.
I ended up with this:
// Validation URLs in Jenkins script is hard (URL.toUri is banned). This regex roughly matches the subset of
// URLs we might want to use (and some invalid but harmless URLs). You can get a rough sense what
// this matches with a generation tool like https://www.browserling.com/tools/text-from-regex .
def saneUrlPattern = ~/^https:\/\/[-\w]{1,32}(\.[-\w]{1,32}){0,4}(:[0-9]{1,5})?(\/|(\/[-\w]{1,32}){1,10})?(\?([-\w]{1,32}=[-\w]{0,40}(&[-\w]{1,32}=[-\w]{0,40}){1,8})?)?(#[-\w]{0,40})?$/
if (!(params.sourceUrl =~ saneUrlPattern)) {
return [error: "Invalid url ${params.sourceUrl}. A simple https URL is expected."]
}
I realise that trying to validate URLs with a regular expression is difficult. I tried to strike a balance between strict and correct enough validation and a regular expression that has some hope of being understood by looking at it and being reasonably convinced as to what it actually matches.

TFS Build (2013) - Build Stop Handling

I have custom codeactivities in TFS Build. One of them is a background thread that TFS Build does not know about.
I want to find out if there is a way for this thread to check if a "stop build" has been requested. (i.e. check on the current build status WITHOUT needing a CodeActivityContext)
(NB: I can't use the AsyncCodeActivity (and its cancel mechanism) because this still blocks subsequent tasks)
I am currently using a heartbeat system and relying on a timeout of the heatbeat from the TFS Build flow loop but this is not fool proof.
IBuildDetail.BuildFinished exists but there is the catch 22 of if the build is finished, how do you get iBuildDetail?
Because code activities are "stateless", then using a previous "CodeActivityContext" to get iBuildDetail does not work, i.e. the context no longer exists.
I can get to a code path of _buildServer.GetBuild(_buildUri)
but can't find out how to establish your current builduri (not to be confused with build definition, server, agent or number)
Thanks
Good news, I found a solution
I was caught up in the BuildUri term, it turns out at the bottom of iBuildDetails, is "Uri"
This turns out to be the BuildUri
Thus code like this works ...
static private Uri _buildUri;
static private IBuildServer _buildServer;
...
protected override void Execute(CodeActivityContext context)
{
// Obtain the runtime value of the Text input argument
string ScriptPath = context.GetValue(this.InScriptPath);
_thisContext = context;
IBuildDetail buildDetail = _thisContext.GetExtension<IBuildDetail>();
_buildUri = buildDetail.Uri; // This is the complete string
_buildServer = buildDetail.BuildServer;
......
In background tthread
try
{
IBuildDetail buildDetail = _buildServer.GetBuild(_buildUri); // this does not work as it is not the BuildUri
if (buildDetail != null)
{
if (buildDetail.Status == BuildStatus.Stopped)
{
TerminateProcess();
}
}
}
catch (Exception Ex)
{
TerminateProcess();
}

Calling build.doStop() in prebuild is not stopping fast enough

I am trying to write a Jenkins plugin that can automatically abort a build if that build is triggered on a holiday (or just a given input day/s). The user can configure the days, and each job gets a checkbox that allows the user to decide if they want their job aborted on a holiday or not. Currently my plugin extends JobProperty and utilizes global configuration where I have a list of blacklisted dates. If today is on my list of blacklisted days, then I do not want my job to run. The plugin "works" but with a few annoying caveats.
My main issue is that I can only FAIL the build if it happens to be triggered on a day that's one of my blacklisted days. This is a problem for me because there's no actual error. The job is operating as it should be and I don't want to receive emails full of errors just because the job was halted on a day I didn't want it to run (e.g: a holiday)
When my plugin decides to abort a build, I want to be able to end the job with an "Aborted" status. (In fact - I'd like to be able to control the status and leave it as a potential parameter.)
Below is my prebuild() code.
#Override
public boolean prebuild(AbstractBuild build, BuildListener listener) {
boolean stopped = false;
if(checkIfClosed) {
LocalDate today = LocalDate.now();
listener.getLogger().println("Checking the date for " + DateFormats.yyyyMMdd.print(today));
if (getDescriptor().getUseCalculatedDateChecker()) {
if (!NyseHolidayChecker.isMarketOpen(today)) {
listener.getLogger().println("Closed (From auto calculation)!");
stopped = true;
}
}
if (getDescriptor().getListOfClosedDates() != null && !getDescriptor().getListOfClosedDates().isEmpty()) {
if (getDescriptor().getListOfClosedDates().contains(DateFormats.yyyyMMdd.print(today))) {
listener.getLogger().println("Closed. Date is in the Closed Date List. " +
"If this is wrong check global configuration.");
stopped = true;
}
}
}
if(stopped) {
try {
if(build.doStop() == null) {
return false;
}
} catch (IOException e) {
listener.getLogger().println(e.getMessage());
return false;
} catch (ServletException e) {
listener.getLogger().println(e.getMessage());
return false;
}
//throw new RuntimeException("This job has been told not to run when marked as Closed!");
//throw new AbortException("This job has been told not to run when marked as Closed!");
}
return true;
}
I've tried several different ways to get the job to abort immediately and not have the build marked as failed.
The doc tells me that I should throw an AbortException but that does not appear to be supported by the function I'm overriding.
I also tried to call doStop() but then my first build step (out of 2) still runs at least a little. This isn't desirable because I will never know what state my job will be in when it will be aborted (it could have already sshed somewhere and killed a process...or made something live etc)
What am I missing? I feel like I'm hacking around to get what I need. I am hoping someone can point me in the right direction on how to best do this.
Digging further into Jenkins code showed the Build.doRun() method will execute the build steps in a do while loop which was allowing for a small bit of the build step to get through. The AbortException which Jenkins documentation recommends will also mark a build as failure (undesirable). The only means I found to cancel a job and have it properly mark as just aborted was to throw an InterruptedException. The JobProperties prebuild() function does not allow for any throws (other than Runtime which will mark as failure).
The plugin now extends BuildWrapper. This has both a setUp and preCheckout method that will run prior to build steps executing. These two methods can also throw InterruptedExceptions. Now if my checks pass and the date is blacklisted an InterruptedException is thrown from the interruptOnHoliday(...) method. The BuildWrapper also utilizes a BuildWrapperDescriptor which will place a checkbox in the job configuration based on the name provided in the overridden getDisplayName() function (Acts sort of as a jetty optionalBlock). With this box checked the setUp function can get called, otherwise it will not. This effectively makes the holiday check plugin optional (desired).
To give help to a user on what the Plugin does, include a help.html file in the resources for the plugin, this will allow the programmer to explain to the user how and why to use the Plugin. The BuildWrapperDescriptor class will programmatically know how to use that file.
Hope this helps everyone. Below is the new way to kill :
#Override
public void preCheckout(AbstractBuild build, Launcher launcher, BuildListener listener)
throws InterruptedException, IOException {
interruptOnHoliday(build, listener);
}
/**
* Run a check against todays date and dates from the {#link NyseHolidayChecker}
* which is an automatic calculator for New York Stock Exchange holidays and also a check against the manually
* created user date list. If either one is not checked to be used, then they will be ignored.
*
* #param build {#link hudson.model.AbstractBuild} that we are on, will kill the executor
* #param listener {#link hudson.model.BuildListener} that we will log to
*
* #throws InterruptedException
*/
private void interruptOnHoliday(AbstractBuild build, BuildListener listener) throws InterruptedException {
boolean stopped = false;
LocalDate today = LocalDate.now();
listener.getLogger().println("Checking the date for " + DateFormats.yyyyMMdd.print(today));
//if the NYSE calculator is checked then let's use it
if (getDescriptor().getUseNyseCalculatedDateChecker()) {
if (!NyseHolidayChecker.isMarketOpen(today)) {
listener.getLogger().println("The NYSE is not Open today (From auto calculation)" +
" and this job is marked to abort. Stopping the build!");
stopped = true;
}
}
//If we have inserted manual dates into the list we want to check them
if (getDescriptor().getListOfClosedDates() != null && !getDescriptor().getListOfClosedDates().isEmpty()) {
if (getDescriptor().getListOfClosedDates().contains(DateFormats.yyyyMMdd.print(today))) {
listener.getLogger().println("This date is blacklisted, and this job is marked to abort. " +
"Stopping the job! If this date should not be on the list check Jenkins Settings.");
stopped = true;
}
}
//if we should stop the job then we call doStop() on the build and we also throw an InterruptedException
//The InterruptedException is the only way to abort a build without it failing.
if (stopped) {
try {
build.doStop();
throw new InterruptedException(DateFormats.yyyyMMdd.print(today) + " is a blacklisted date.");
} catch (IOException e) {
listener.getLogger().println(e.getMessage());
} catch (ServletException e) {
listener.getLogger().println(e.getMessage());
}
}
}
#Override
public BuildWrapper.Environment setUp(AbstractBuild build, Launcher launcher, BuildListener listener)
throws InterruptedException, IOException {
interruptOnHoliday(build, listener);
return new CloseCheckerEnvironment();
}

How to change output of ant <input> task?

Output of task by default look like that:
target name:
[input] some message:
your input
[next task]
I'd like to see something like this:
target name:
[input] some message: your input
[next task]
How can I make, that task does not put cursor to the new line after message?
It can be done, but it is a little bit involved. There is no option on the input task itself for easily doing what you want.
However, in Ant 1.7 or greater, you can control the output (and input) of the input task by providing an input handler. Ant comes shipped with a few input handlers, for example one for secure input that does not echo what you type to the screen. You can, if you want, write your own input handler, and in that way gain full control of what input and output looks like.
To write an input handler you must write a class that implements the InputHandler interface. I recommend you download the Ant source code and take a look at the DefaultInputHandler and create your own version of that, modifying it to suit your needs. In the source for Ant v1.8.3, the prompt and input is implemented like this:
r = new BufferedReader(new InputStreamReader(getInputStream()));
do {
System.err.println(prompt);
System.err.flush();
try {
String input = r.readLine();
request.setInput(input);
} catch (IOException e) {
throw new BuildException("Failed to read input from"
+ " Console.", e);
}
} while (!request.isInputValid());
I haven't tried it, but changing the println to a print seems like a good idea.
When done, you can point Ant's input task to your compiled input handler using the classname and (for instance) classpath parameters.

Resources