JS:
#!/usr/bin/env node
const util = require("util"),
inquirer = require("inquirer")
function promptUser() {
inquirer.prompt([
{
type: "list",
name: "scheme",
message: "How would you like to build",
choices: [
{
name: "build develop",
value: "develop"
}, {
name: "build product",
value: "product"
}
]
}
], earlyAnswers => {
console.log(earlyAnswers.scheme);
});
}
promptUser();
it will ask a question and I can select a answer, log answer, like:
I want to exec this js in shell:
#!/bin/sh
echo 123
a=`node ./test2.js`
let a = answer, but can't exec like:
how do I exec this JS?
Your javascript is actually running, but your backticks in the command
a=`node ./test2.js`
cause the shell to capture standard out and assign it to the variable a. All of stdout is captured, not just the last line, so nothing is presented to the user.
One possible way to solve this is to use the node.js process's exit code.
Here is a generic script that will accept the prompt an options on the command line, and set its exit code to the zero-based index of the selection.
#! /usr/bin/env node --harmony_destructuring
util = require("util");
inquirer = require("inquirer");
var [program, file, textprompt, ...args] = process.argv
var choices = [];
args.forEach(function(E) {
var [name, value] = E.split(":",2);
choices.push({name: name, value: value});
});
function promptUser() {
inquirer.prompt([
{
type: "list",
name: "scheme",
message: textprompt,
choices: choices
}
]).then(function(answer) {
if (answer && answer.scheme) {
position = choices.findIndex(function(E) { return E.value == answer.scheme; });
process.exit(position);
} else {
process.exit(-1);
}
})
}
if (choices.length > 0)
promptUser();
Which could be used like this:
#! /bin/bash
./test2.js "How would you like to build?" "build develop:develop" "build product:product"
selected=$?
case $selected in
0) echo "Selected develop";;
1) echo "Selected product";;
*) echo "Invalid selection";;
esac
Related
I have a pipeline where among other parameters like machine name, client it is supposed to get latest ami from aws for that branch for example and then put it the clien.json which terraform would use to create the machine but als I want to enable the user to be able to provide a value for the parameter and when there is no value for that parameter to have the value picked from latest ami in develop for example:
#!/usr/bin/env groovy
pipeline {
agent { label 'new' }
parameters {
string(name: 'AMI_ID', defaultValue: '', description: '[Mandatory]')
}
stages {
stage('Retrieve latest AMI.') {
when {
expression { ${AMI_ID} == '' }
}
steps {
script {
AMI_ID = sh(script: "aws ec2 describe-images --region region1 --owners 123456 --filters \"Name=tag:type,Values=develop\" --query 'sort_by(Images, &CreationDate)[-1].ImageId' | jq -r '.'", returnStdout: true).trim()
echo "AMI retrieved: " + $ { AMI_ID }
}
}
}
stage("Updating client data") {
environment {
TERRAHELP_KEY = credentials('some-key')
}
steps {
dir("data/clients/") {
clientJson = readJSON file: "${CLIENT}.json"
clientJson.put("client_ec2_eda_ami_id", ${ AMI_ID })
writeJSON file: "${CLIENT}.json", json: clientJson, pretty: 4
echo "Following data will be applied:"
sh "cat ${CLIENT}.json"
}
}
}
}
}
Found the root cause was that I was passing a shell script variable to groovy where I say:
clientJson.put("client_ec2_eda_ami_id", ${ AMI_ID })
Instead I should have passed the AMI_ID to a groovy variable above and say like:
clientJson.put("client_ec2_eda_ami_id", currentAmi)
Peace of the code with the aws query dimmed:
stage('Retrieve latest AMI.') {
}
steps {
script {
currentAmi = params.AMI_ID
if (currentAmi.isEmpty())
currentAmi = sh(script: "aws ec2 query blah blah")
echo "Ami retrieved is: ${currentAmi}"
}
}
}
stage("Updating client data") {
environment {
TERRAHELP_KEY = credentials('some-key')
}
steps {
dir("data/clients/") {
clientJson = readJSON file: "${CLIENT}.json"
clientJson.put("client_ec2_eda_ami_id", currentAmi)
writeJSON file: "${CLIENT}.json", json: clientJson, pretty: 4
echo "Following data will be applied:"
sh "cat ${CLIENT}.json"
}
}
}
}
}
Folks,
I have the below Jenkins pipeline. I want to run all the stages, hence I got catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') in place.
I have a script minion_notification.sh which produces file called "jk_buildpass.txt". Now, I want my build to got to postFailure block whenever condition if [ ! -s jk_buildpass.txt ] is matched. With exit 1 the job is not failing and the consequent stages are getting executed. Any idea?
pipeline = {
ansiColor('xterm') {
FILEVERSION = params.FILEVERSION.trim()
def remote = [:]
remote.name = 'xxx'
remote.host = '10.xxx.xx.x'
remote.allowAnyHosts = true
withCredentials([usernamePassword(credentialsId: 'saltmaster', passwordVariable: 'password', usernameVariable: 'ops')]) {
remote.user = 'ops'
remote.password = password
stage (' Backup') {
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
sh './minion_notification.sh "${FILEVERSION}" "Validate" "backupdb"'
sh(returnStdout: true, script: '''#!/bin/bash
if [ ! -s jk_buildpass.txt ];then
exit 1
else
echo "jk_buildpass is not empty"
fi
'''.stripIndent())
}
}
stage (' Uninstall') {
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
sh './minion_notification.sh "${FILEVERSION}" "Validate" "IRMUninstall"'
sh(returnStdout: true, script: '''#!/bin/bash
if [ ! -s jk_buildpass.txt ];then
exit 1
else
echo "jk_buildpass is not empty"
fi
'''.stripIndent())
}
}
stage('Run DDMU') {
sh './minion_notification.sh "${FILEVERSION}" "jenkins-display"'
}
}
// sh './slk_notify.sh "Upgrade successfully completed for - ${FILEVERSION} " "*********** " "good" ":jenkins:"
}
}
postFailure = {
sh './slk_notify.sh " Upgarde failed for - ${FILEVERSION} " "danger" ":jenkinserror:"'
}
postAlways = {
echo 'Cleaning Workspace now'
env.WORKSPACE = pwd()
//sh "rm ${env.WORKSPACE}/* -fr"
}
node{
properties([
parameters([
string(name: 'FILEVERSION', defaultValue: '', description: 'Enter the file name to be process ')
])
])
try {
pipeline()
} catch (e) {
postFailure()
throw e
} finally {
postAlways()
}
}
I am working on a declarative jenkins pipeline where I am trying to login to remote host and execute shell script. Below is the sample snippet. I want to know How to pass a parameter to my script.sh script.
#!/bin/bash
echo "argument $1"
below is the pipeline script
hosts = [“x.x.x”, “x.x.x”]
pipeline {
agent { node { label 'docker' } }
parameters {
choice(name: 'stageparam', choices: ['build', 'deploy'], description: ‘xyz’)
string(name: 'Username', defaultValue: ‘abc’, description: 'enter username')
}
stages {
stage('Setup') {
steps {
script {
pom = getPom(effective: false)
}
}
}
stage('Deploy') {
steps {
script {
def targetServers = null
if (stageparam == "deploy") {
targetServers = hosts
}
targetServers.each { server ->
echo "Server : ${server}"
def remote = [:]
remote.name = ‘server’
remote.host = server
remote.user = Username
def pass = passwordParameter description: "Enter password for user ${remote.user} "
remote.password = pass
remote.allowAnyHosts = true
stage('Remote SSH') {
sshPut remote: remote, from: ‘./script.sh', into: '.'
sshScript remote: remote, script: "doc.sh ${Username}"
}
}
}
}
}
}
}
getting below error while executing the script
/home/jenkins/workspace/script.sh Username does not exist.
I have written a simple pipeline script to show the usage of variable in bash script.
Pipeline script:
pipeline {
agent any
parameters {
choice(name: 'stageparam', choices: ['build', 'deploy'], description: 'enter stageparam')
string(name: 'USERNAME', defaultValue: 'abc', description: 'enter username')
}
stages {
stage('Git Pull')
{
steps {
checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'gitlab-test', url: 'http://localhost:8076/test-upgrade.git']]])
}
}
stage('Run the python script') {
steps {
sh 'chmod 777 test.py'
sh "python test.py ${env.WORKSPACE}"
}
}
stage('Run the bash script') {
steps {
sh 'chmod 777 run.sh'
sh "./run.sh ${env.USERNAME}"
}
}
}
}
Output:
I have managed to get my snippets working with the basic autocompletion:
ace.define("ace/snippets/bosun",["require","exports","module"], function(require, exports, module) {
exports.snippets = [
/* Sections */
{
name: "alert definition",
tabTrigger: "alertdef",
content:
"alert ${1:alertname} {\n\
warn =\n\
}\n"
},
{
name: "template def",
tabTrigger: "templatedef",
content:
"template ${1:templatename} {\n\
subject =\n\
}\n"
},
/* Funcs */
{
name: "avg reduction function",
tabTrigger: "avg",
content: "avg(${1:seriesSet})"
}
]
exports.scope = "bosun";
});
In the documentation on snippets it says:
When triggering a snippet through a menu or command you can configure
it to use the text selected prior to inserting the snippet in the
resulting code.
But I'm not clear on how I would create menu to list the snippets? (Ideally a menu that has submenus for each category of snippets, but happy to crawl first...)
Perhaps someone has a better way. But from reading the code in https://github.com/ajaxorg/ace/blob/master/lib/ace/snippets.js I have come up with:
$scope.aceLoaded = function (_editor) {
editor = _editor;
$scope.editor = editor;
editor.$blockScrolling = Infinity;
editor.focus();
editor.getSession().setUseWrapMode(true);
$scope.snippetManager = ace.require("ace/snippets").snippetManager;
$scope.bosunSnippets = $scope.snippetManager.snippetNameMap["bosun"];
editor.on("blur", function () {
$scope.$apply(function () {
$scope.items = parseItems();
});
});
};
$scope.listSnippets = function() {
var snips = $scope.snippetManager.snippetNameMap["bosun"];
if (snips) {
return Object.keys(snips)
}
return {};
}
$scope.insertSnippet = function(snippetName) {
$scope.snippetManager.insertSnippetForSelection($scope.editor, $scope.snippetManager.snippetNameMap["bosun"][snippetName].content);
$scope.editor.focus();
$scope.editor.tabstopManager.tabNext()
}
Which seems to work, perhaps there is a better way.
How can I do the following with the Ace Editor.
User types the '#' character
Autocomplete pops up
User makes a selection from the dropdown
The '#' gets removed now that the selection has been made
I basically want the # as a trigger for the autocomplete, but I don't want it hanging around after.
Thank you
addAutoComplete() {
var me = this;
ace.require('./config').loadModule('ace/ext/language_tools', () => {
this.aceEditor.setOptions({
enableBasicAutocompletion: true,
enableSnippets: true,
enableLiveAutocompletion: false
});
me.include = /[a-z.]/i;
this.aceEditor.on('change', (obj, editor) => {
switch (obj.action) {
case 'insert':
let lines = obj.lines;
let char = lines[0];
if ((lines.length === 1) && (char.length === 1) && me.include.test(char)) {
setTimeout(() => {
me.aceEditor.commands.byName.startAutocomplete.exec(me.aceEditor);
}, 50);
}
break;
}
});
});
}
that's the demo. works fine
We can bind the '#' keyword and trigger the autocomplete:
editor.commands.addCommand({
name: "myCommand",
bindKey: { win: "#", mac: "#" },
exec: function (editor) {
autocomplete();
}
});
autocomplete: function () {
staticWordCompleter = {
var getWordList = function(editor, session, pos, prefix, callback, isRHSEditor) {
var wordList = []; // add your words to this list
callback(null, wordList.map(function(word) {
return {
caption: word,
value: word
};
}));
editor.completers = [staticWordCompleter];
}
None of the above approaches worked for me. I ended up rolling my own autocomplete/autosuggest for this to work. It's hard to contrive an example but in short the steps are:
Capture the onChange of the editor
If the editor action meets the criteria (i.e. the # symbol is pressed), display a box at the cursor position. This can be done by setting the box to position absolute, and setting the top/left attributes:
const {row, column} = window.editor.getCursorPosition();
const {pageX, pageY} = window.editor.renderer.textToScreenCoordinates(row, column)
const autosuggest = document.getElementById("ELEMENT_NAME")
autosuggest.style.left = pageX
autosuggest.style.top = pageY
Add commands to disable/redirect any actions such as the arrow keys/enter key, and re-enable when a selection is made.
function disableEnterKeyInEditor() {
editor.commands.addCommand(commmand_enter);
}
function enableEnterKeyInEditor() {
editor.commands.removeCommands(commmand_enter);
}
command_enter = {
name: "enterKeyPressedCommand",
bindKey: {win: "Enter", mac: "Enter"},
exec: function (editor) {
return true;
}
};
This was much easier than working with ace's autocomplete.
You can check with below code:
var getWordList = function(editor, session, pos, prefix, callback, isRHSEditor) {
var wordList = [];
if(prefix === '#') {
wordList.push('add you're name list here');
}
callback(null, wordList.map(function(word) {
return {
caption: word,
value: word
};
}));
}