Extracting part of a string on jenkins pipeline - shell

I am having some trouble with the syntax in my pipeline script.
I am trying to capture everything after the last forward slash "/" and before the last period "." in this string git#github.com:project/access-server-pd.git (access-server-pd)
Here (below) is how I would like to set it up
MYVAR="git#github.com:project/access-server-pd.git"
NAME=${MYVAR%.*} # retain the part before the colon
NAME=${NAME##*/} # retain the part after the last slash
echo $NAME
I have it current set up with triple quotes on the pipeline script:
stage('Git Clone') {
MYVAR="$GIT_REPO"
echo "$MYVAR"
NAME="""${MYVAR%.*}"""
echo "$NAME"
But I am receiving an unexpected token on "." error. How might I write this so that I can get this to work?
UPDATE: This command does the trick:
echo "git#github.com:project/access-server-pd.git" | sed 's#.*/\([^.]*\).*#\1#'
Now I just need to find the proper syntax to create a variable to store that value.

In this case, it looks like using a few Groovy/Java methods on the String can extract the parts.
final beforeColon = url.substring(0, url.indexOf(':')) // git#github.com
final afterLastSlash = url.substring(url.lastIndexOf('/') + 1, url.length()) // project/access-server-pd.git
This uses a few different methods:
public int String.indexOf(String str, int fromIndex)
public String String.substring(int beginIndex, int endIndex)
public int String.length()
public int String.lastIndexOf(String str)
You do need to be careful about the code you use in your pipeline. If it is sandboxed it will run in a protected domain where every invocation is security checked. For example, the whitelist in the Script Security Plugin whitelists all of the calls used above (for example, method java.lang.String lastIndexOf java.lang.String).
Performing String manipulation in your pipeline code is perfectly reasonable as you might make decisions and change your orchestration based on it.

Related

How to make Get Request with Request param in Postman

I have created an endpoint that accepts a string in its request param
#GetMapping(value = "/validate")
private void validateExpression(#RequestParam(value = "expression") String expression) {
System.out.println(expression);
// code to validate the input string
}
While sending the request from postman as
https://localhost:8443/validate?expression=Y07607=Curr_month:Y07606/Curr_month:Y07608
// lets say this is a valid input
console displays as
Y07607=Curr_month:Y07606/Curr_month:Y07608 Valid
But when i send
https://localhost:8443/validate?expression=Y07607=Curr_month:Y07606+Curr_month:Y07608
//which is also an valid input
console displays as
Y07607=Curr_month:Y07606 Curr_month:Y07608 Invalid
I am not understanding why "+" is not accepted as parameter.
"+" just vanishes till it reaches the api! Why?
I suggest to add this regular expression to your code to handle '+' char :
#GetMapping(value = "/validate")
private void validateExpression(#RequestParam(value = "expression:.+") String expression) {
System.out.println(expression);
// code to validate the input string
}
I didn't find any solution but the reason is because + is a special character in a URL escape for spaces. Thats why it is replacing + with a " " i.e. a space.
So apparently I have to encode it from my front-end
Its wise to encode special characters in a URL. Characters like \ or :, etc.
For + the format or value is %2. You can read more about URL encoding here. This is actually the preferred method because these special characters can sometimes cause unintended events to occur, like / or = which can mean something else in the URL.
And you need not worry about manually decoding it in the backend or server because it is automatically decoded, in most cases and frameworks. In your case, I assume you are using Spring Boot, so you don't need to worry about decoding.

Jenkins. Inserting environment variable in the file

I have a .env file that contains the following data
API_URL=${API_URL}
API_KEY=${API_KEY}
API_SECRET=${API_SECRET}
Setting environment variables in Jenkins and passing them to the pipeline is clear. But it is not clear how do I replace ${API_URL}, ${API_KEY} & ${API_SECRET} in the .env file with their values in the Jenkins environment variable? Plus, how do I loop through all the Jenkins variables?
This basically requires two steps:
Get all environment variables
Replace values of environment variables in the template (.env) file
Let's start with #2, because it dictates which kind of data #1 must produce.
2. Replace variables in a template
We can use Groovy's SimpleTemplateEngine for this task.
def result = new SimpleTemplateEngine().createTemplate( templateStr ).make( dataMap )
Here templateStr is the template string (content of your .env file) and dataMap must be a Map consisting of string keys and values (the actual values of the environment variables). Getting the template string is trivial (use Jenkins readFile step), reading the environment variables into a Map is slightly more involved.
1. Read environment variables into a Map
I wrote "slightly more involved" because Groovy goodness makes this task quite easy aswell.
#Chris has already shown how to read environment variables into a string. What we need to do is split this string, first into separate lines and then each line into key and value. Fortunately, Groovy provides the member function splitEachLine of the String class, which can do both steps with a single call!
There is a little caveat, because splitEachLine is one of the functions that doesn't behave well in Jenkins pipeline context - it would only return the first line. Moving the critical code into a separate function, annotated with #NonCPS works around this problem.
#NonCPS
Map<String,String> envStrToMap( String envStr ) {
def envMap = [:]
envStr.splitEachLine('=') {
envMap[it[0]] = it[1]
}
return envMap
}
Finally
Now we have all ingredients for letting Jenkins cook us a tasty template soup!
Here is a complete pipeline demo. It uses scripted style, but it should be easy to use in declarative style as well. Just replace node with a script block.
import groovy.text.SimpleTemplateEngine
node {
// TODO: Replace the hardcoded string with:
// def tmp = readFile file: 'yourfile.env'
def tmp = '''\
API_URL=${API_URL}
API_KEY=${API_KEY}
API_SECRET=${API_SECRET}'''
withEnv(['API_URL=http://someurl', 'API_KEY=123', 'API_SECRET=456']) {
def envMap = getEnvMap()
echo "envMap:\n$envMap"
def tmpResolved = new SimpleTemplateEngine().createTemplate( tmp ).make( envMap )
writeFile file: 'test.env', text: tmpResolved.toString()
// Just for demo, to let me see the result
archiveArtifacts artifacts: 'test.env'
}
}
// Read all environment variables into a map.
// Here, #NonCPS must NOT be used, because we are calling a Jenkins step.
Map<String,String> getEnvMap() {
def envStr = sh(script: 'env', returnStdout: true)
return envStrToMap( envStr )
}
// Split a multiline string, where each line consists of key and value separated by '='.
// It is critical to use #NonCPS to make splitEachLine() work!
#NonCPS
Map<String,String> envStrToMap( String envStr ) {
def envMap = [:]
envStr.splitEachLine('=') {
envMap[it[0]] = it[1]
}
return envMap
}
The pipeline creates an artifact "test.env" with this content:
API_URL=http://someurl
API_KEY=123
API_SECRET=456
You can access variables by executing simple shell in scripted pipeline:
def variables = sh(script: 'env|sort', returnStdout: true)
Then programatically in Groovy convert it to list and iterate using each loop.
According to replacing variables, if you're not using any solution which can access env variables then you can use simple text operations like executing sed on that file.

Arduino Yun run shell command with variable

I need help on this part of a project I am making, I need to run a shell command and pass a variable either string or char to it.
p.runShellCommand("madplay /mnt/sda1/");
Above is my shell command which works, however I want to put a variable after the last slash
p.runShellCommand("madplay /mnt/sda1/variable");
The above code is what I have tried, replacing variable with my variable and didn't seem to work.
I have also tried this which seems to work
String hey = "madplay /mnt/sda1/worldOfTomorrow.mp3";
p.runShellCommand(hey);
It's just string concatenation:
String command= "madplay /mnt/sda1/";
String var = "worldOfTomorrow.mp3";
p.runShellCommand(command+var);
Now, write in var with desired function:
String command= "madplay /mnt/sda1/";
String var = getNameOfFile();
p.runShellCommand(command+var);
...
public String getNameOfFile(){
//code retrieving your desired variable by Serial/SDcard/Ethernet....
}

tmux titles-string not executing shell command

I have the following lines in my ~/.tmux.conf
set-option -g set-titles on
set-option -g set-titles-string "#(whoami)##H: $PWD \"#S\" (#W)#F [#I:#P]"
This has worked in the past but after upgrading to 2.0 shell commands are no longer executed. I now see in my title:
#(whoami)#myhostname.local: /Users/lander [..rest..]
According to the man page, this should work:
status-left string
Display string (by default the session name) to the left of the status
bar. string will be passed through strftime(3) and formats (see
FORMATS) will be expanded. It may also contain any of the following
special character sequences:
Character pair Replaced with
#(shell-command) First line of the command's output
#[attributes] Colour or attribute change
## A literal `#'
Well done for reading the code, it's simple really: set-titles-string switched to using formats which don't expand #(). Patching this is easy, and no, it's not good enough to reinstate status_print() to tmux.h, instead, job expansion should be a separate function and used from status_replace() and format_expand(). No idea when this will get done.
Thanks for playing.
The code that performed the task your are mentioning is no longer present in version 2.0. That's the short answer to the question above. Either the documentation hasn't been updated to reflect this, or this was done accidentally and is a bug.
What follows is exactly why I think this is the case. I'm at the end of my lunch break, so I can't create a patch for this right now. If no one else gets around to fixing this from here, I will take a stab at it this weekend.
I checkout out the git repository and took a look at code changes from version 1.9a -> 2.0.
The function that actually does this replacement is status_replace() in status.c. This still seems to work, as the command processing works in the status lines, which still call this function.
In version 1.9a, this was also called from server_client_set_title() in server-client.c, around line 770. It looks like this:
void
server_client_set_title(struct client *c)
{
struct session *s = c->session;
const char *template;
char *title;
template = options_get_string(&s->options, "set-titles-string");
title = status_replace(c, NULL, NULL, NULL, template, time(NULL), 1);
if (c->title == NULL || strcmp(title, c->title) != 0) {
free(c->title);
c->title = xstrdup(title);
tty_set_title(&c->tty, c->title);
}
free(title);
}
In version 2.0, this call has been replaced with (now down around line 947):
void
server_client_set_title(struct client *c)
{
struct session *s = c->session;
const char *template;
char *title;
struct format_tree *ft;
template = options_get_string(&s->options, "set-titles-string");
ft = format_create();
format_defaults(ft, c, NULL, NULL, NULL);
title = format_expand_time(ft, template, time(NULL));
if (c->title == NULL || strcmp(title, c->title) != 0) {
free(c->title);
c->title = xstrdup(title);
tty_set_title(&c->tty, c->title);
}
free(title);
format_free(ft);
}
It looks like calls to format_expand_time() and status_replace() might be mutually exclusive. That is the part that might take a bit of effort to fix -- getting the old function call back in there without breaking whatever new functionality they've just added.

What is use of '%s' in xpath

I have tried to know the reason in online but i didnt get it.
I want to know the reason why '%s' used in xpath instead of giving text message
I hope some one can help me on this.
see my scenario:
By.xpath(("//div[contains(text(),'%s')]/following-sibling::div//input"))
It's called wildcard.
E.g. you have
private final String myId = "//*[contains(#id,'%s')]";
private WebElement idSelect(String text) {
return driver.findElement(By.xpath(String.format(myId, text)));
}
Then, you can make a function like:
public void clickMyId(idName){
idSelect(idName.click();
}
And call
clickMyId('testId');
The overall goal of the %s is not using the string concatenation, but to use it injected into a string.
Sometimes, there are many locators for web elements which are of same kind, only they vary with a small difference say in index or String.
For e.g., //div[#id='one']/span[text()='Mahesh'] and
//div[#id='one']/span[text()='Jonny']
As it can been seen in the above example that the id is same for both the element but the text vary.
In that case, you can use %s instead of text. Like,
String locator = "//div[#id='one']//span[text()='%s']";
private By pageLocator(String name)
{
return By.xpath(String.format(locator, name));
}
So in your case,
By.xpath(("//div[contains(text(),'%s')]/following-sibling::div//input"))
the text is passed at runtime as only the text vary in the locator.
'%s' in XPath is used as String Replacement.
Example:
exampleXpath = "//*[contains(#id,'%s')]"
void findElement(String someText)
{
driver.findElement(By.xpath(String.format(exampleXpath, someText)));
}
So it will replace %s with someText passed by user.

Resources