├── .babelrc ├── .editorconfig ├── Gruntfile.js ├── .gitignore ├── test ├── test_config.xml ├── test_with_params_config.xml ├── test_with_report_config.xml └── main.js ├── LICENSE ├── package.json ├── testjenkinsapi.js ├── .eslintrc ├── README.md ├── src └── main.js └── lib └── main.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"], 3 | "plugins": ["transform-function-bind"] 4 | } 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | // Project configuration. 4 | grunt.initConfig({ 5 | pkg: grunt.file.readJSON('package.json'), 6 | }); 7 | 8 | 9 | grunt.loadNpmTasks('grunt-release'); 10 | 11 | }; 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 27 | node_modules 28 | 29 | #IDE configuration data 30 | .idea/ 31 | -------------------------------------------------------------------------------- /test/test_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | development 5 | false 6 | 7 | 8 | true 9 | false 10 | false 11 | false 12 | 13 | false 14 | 15 | 16 | sleep 60 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Shawn Jansepar 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE 20 | -------------------------------------------------------------------------------- /test/test_with_params_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | development 5 | false 6 | 7 | 8 | 9 | 10 | sleep_time 11 | How long to wait? 12 | 60 13 | 14 | 15 | 16 | 17 | 18 | true 19 | false 20 | false 21 | false 22 | 23 | false 24 | 25 | 26 | sleep $sleep_time 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jenkins-api", 3 | "version": "0.3.1", 4 | "description": "Jenkins API written in Node.js", 5 | "author": "Shawn Jansepar ", 6 | "contributors": [ 7 | "Jakub Podlaha (https://github.com/kub1x)" 8 | ], 9 | "license": "MIT", 10 | "keywords": [ 11 | "github", 12 | "jenkins" 13 | ], 14 | "engine": { 15 | "node": ">=0.4" 16 | }, 17 | "main": "./lib/main.js", 18 | "scripts": { 19 | "build": "babel ./src --out-dir ./lib", 20 | "audit": "nsp check package", 21 | "lint": "#eslint src test", 22 | "check": "npm run audit && ncu && npm outdated --depth 0", 23 | "test": "npm run build && mocha ./test/main.js", 24 | "validate": "npm run lint && npm test", 25 | "prepare": "npm run build && npm run validate && npm run check", 26 | "major": "pmm major", 27 | "minor": "pmm minor", 28 | "patch": "pmm patch" 29 | }, 30 | "pre-commit": [ 31 | "validate" 32 | ], 33 | "dependencies": { 34 | "request": "^2.81.0" 35 | }, 36 | "devDependencies": { 37 | "async": "^2.5.0", 38 | "babel-cli": "^6.24.1", 39 | "babel-eslint": "^7.2.3", 40 | "babel-plugin-transform-function-bind": "^6.22.0", 41 | "babel-preset-es2015": "^6.24.1", 42 | "bluebird": "^3.5.0", 43 | "chai": "^4.1.1", 44 | "chai-like": "^0.2.14", 45 | "chai-things": "^0.2.0", 46 | "eslint": "^4.4.0", 47 | "mocha": "^3.5.0", 48 | "ncu": "^0.2.1", 49 | "nsp": "^2.7.0", 50 | "pmm": "^1.3.1" 51 | }, 52 | "repository": { 53 | "type": "git", 54 | "url": "git@github.com:jansepar/node-jenkins-api.git" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /test/test_with_report_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | junit report 5 | false 6 | 7 | 8 | true 9 | false 10 | false 11 | false 12 | 13 | false 14 | 15 | 16 | echo '<?xml version="1.0" encoding="UTF-8"?> 17 | <testsuites> 18 | <testsuite name="JUnitXmlReporter" errors="0" skipped="0" tests="1" failures="0" time="0.006" timestamp="2013-05-24T10:23:58"> 19 | <properties> 20 | <property name="java.vendor" value="Sun Microsystems Inc." /> 21 | <property name="compiler.debug" value="on" /> 22 | <property name="project.jdk.classpath" value="jdk.classpath.1.6" /> 23 | </properties> 24 | <testcase classname="JUnitXmlReporter" name="should do" time="0" /> 25 | </testsuite> 26 | </testsuites>' > report.xml 27 | 28 | 29 | 30 | 31 | *.xml 32 | false 33 | 1.0 34 | false 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /testjenkinsapi.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This is my poor excuse of a test file. One day, I will write some unit tests. 3 | * Until then, this is all I have :) 4 | */ 5 | 6 | var jenkinsapi = require('./lib/main'); 7 | 8 | 9 | var jenkins = jenkinsapi.init("http://localhost:8080"); 10 | 11 | /* 12 | jenkins.build('test', function(error, data) { 13 | if (error) { 14 | console.log(error); 15 | } 16 | console.log(data); 17 | }); 18 | */ 19 | 20 | /* 21 | var view_name = "test"; 22 | jenkins.all_jobs_in_view(view_name, function (error, data) { 23 | if (error) { 24 | console.log(error); 25 | } 26 | console.log(data); 27 | }); 28 | */ 29 | 30 | /* 31 | jenkins.all_jobs(function(error, data) { console.log(data)}); 32 | */ 33 | 34 | /* 35 | jenkins.job_info('test-madness', function(error, data) { 36 | if(!error) { 37 | console.log(data); 38 | } 39 | 40 | }); 41 | */ 42 | 43 | /* 44 | jenkins.job_info('test', function(error, data) { 45 | if (!error) { 46 | console.log(data); 47 | } 48 | 49 | }); 50 | */ 51 | 52 | //jenkins.last_build_info('test-development', function(error, data) { console.log(data); }); 53 | //jenkins.last_build_report('test-development', function(error, data) { console.log(data); }); 54 | 55 | /* 56 | jenkins.get_config_xml('test-development', function(error, data) { 57 | //console.log(data); 58 | jenkins.create_job('test-copy', data, function(error, data) { 59 | //console.log(data); 60 | }); 61 | }); 62 | */ 63 | /* 64 | jenkins.copy_job('test-development' 65 | ,'test-new' 66 | ,function(data) { 67 | return data.replace('development','feature-branch'); 68 | } 69 | ,function(error, data) { 70 | //console.log(data); 71 | jenkins.delete_job('test-new', function(error, data) { 72 | if(error) { 73 | console.log("error!"); 74 | } 75 | console.log(data); 76 | }); 77 | }); 78 | */ 79 | /* 80 | jenkins.delete_job('test-new', function(error, data) { 81 | if(error) { 82 | console.log("error!"); 83 | console.log(data.body, data.statusCode); 84 | */ 85 | 86 | //jenkins.computers(function(error, data) { console.log(data)}); 87 | 88 | //jenkins.queue(function(error, data) { console.log(data)}); 89 | 90 | jenkins.build 91 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "es6": true, 5 | "node": true, 6 | "mocha": true, 7 | "browser": true 8 | }, 9 | "parserOptions": { 10 | "ecmaVersion": 6, 11 | "sourceType": "module", 12 | "ecmaFeatures": { 13 | "impliedStrict": true 14 | }, 15 | }, 16 | "rules": { 17 | // possible errors 18 | "comma-dangle": [2, "never"], 19 | "no-cond-assign": [2, "always"], 20 | "no-console": [2], 21 | "no-constant-condition": [2], 22 | "no-control-regex": [2], 23 | "no-debugger": [2], 24 | "no-dupe-args": [2], 25 | "no-dupe-keys": [2], 26 | "no-duplicate-case": [2], 27 | "no-empty": [2], 28 | "no-empty-character-class": [2], 29 | "no-ex-assign": [2], 30 | "no-extra-boolean-cast": [2], 31 | "no-extra-parens": [0], 32 | "no-extra-semi": [2], 33 | "no-func-assign": [2], 34 | "no-inner-declarations": [2], 35 | "no-invalid-regexp": [2], 36 | "no-irregular-whitespace": [2], 37 | "no-negated-in-lhs": [2], 38 | "no-obj-calls": [2], 39 | "no-regex-spaces": [2], 40 | "no-sparse-arrays": [2], 41 | "no-unexpected-multiline": [2], 42 | "no-unreachable": [2], 43 | "use-isnan": [2], 44 | "valid-jsdoc": [2, { 45 | "requireReturn": false, 46 | "requireParamDescription": false, 47 | "requireReturnDescription": false 48 | }], 49 | "valid-typeof": [2], 50 | 51 | // best practices 52 | "accessor-pairs": [2], 53 | "array-callback-return": [2], 54 | "block-scoped-var": [2], 55 | "complexity": [1, 8], 56 | "consistent-return": [0], 57 | "curly": [2, "multi-line"], 58 | "default-case": [2], 59 | "dot-location": [2, "property"], 60 | "dot-notation": [2], 61 | "eqeqeq": [2], 62 | "guard-for-in": [0], 63 | "no-alert": [2], 64 | "no-caller": [2], 65 | "no-case-declarations": [2], 66 | "no-div-regex": [2], 67 | "no-else-return": [2], 68 | "no-empty-function": [2], 69 | "no-empty-pattern": [2], 70 | "no-eq-null": [2], 71 | "no-eval": [2], 72 | "no-extend-native": [2], 73 | "no-extra-bind": [2], 74 | "no-extra-label": [2], 75 | "no-fallthrough": [2], 76 | "no-floating-decimal": [2], 77 | "no-implicit-coercion": [0], 78 | "no-implicit-globals": [2], 79 | "no-implied-eval": [2], 80 | "no-invalid-this": [2], 81 | "no-iterator": [2], 82 | "no-labels": [2], 83 | "no-lone-blocks": [2], 84 | "no-loop-func": [2], 85 | "no-magic-numbers": [1, { "ignoreArrayIndexes": true }], 86 | "no-multi-spaces": [2], 87 | "no-multi-str": [2], 88 | "no-native-reassign": [2], 89 | "no-new": [2], 90 | "no-new-func": [2], 91 | "no-new-wrappers": [2], 92 | "no-octal": [2], 93 | "no-octal-escape": [2], 94 | "no-param-reassign": [0], 95 | "no-process-env": [0], 96 | "no-proto": [2], 97 | "no-redeclare": [2], 98 | "no-return-assign": [2], 99 | "no-script-url": [2], 100 | "no-self-assign": [2], 101 | "no-self-compare": [2], 102 | "no-sequences": [2], 103 | "no-throw-literal": [2], 104 | "no-unmodified-loop-condition": [2], 105 | "no-unused-expressions": [2, { "allowShortCircuit": true }], 106 | "no-unused-labels": [2], 107 | "no-useless-call": [2], 108 | "no-useless-concat": [2], 109 | "no-void": [2], 110 | "no-warning-comments": [0], 111 | "no-with": [2], 112 | "radix": [2], 113 | "vars-on-top": [0], 114 | "wrap-iife": [2, "inside"], 115 | "yoda": [2, "never"], 116 | 117 | // strict 118 | "strict": [0], 119 | 120 | // variables 121 | "init-declarations": [0], 122 | "no-catch-shadow": [2], 123 | "no-delete-var": [2], 124 | "no-label-var": [2], 125 | "no-shadow": [2], 126 | "no-shadow-restricted-names": [2], 127 | "no-undef": [2], 128 | "no-undef-init": [2], 129 | "no-undefined": [2], 130 | "no-unused-vars": [2], 131 | "no-use-before-define": [2], 132 | 133 | // node.js 134 | "callback-return": [0], 135 | "global-require": [0], 136 | "handle-callback-err": [2], 137 | "no-mixed-requires": [2], 138 | "no-new-require": [2], 139 | "no-path-concat": [2], 140 | "no-process-exit": [2], 141 | "no-restricted-imports": [0], 142 | "no-restricted-modules": [0], 143 | "no-sync": [0], 144 | 145 | // stylistic 146 | "array-bracket-spacing": [2, "never"], 147 | "block-spacing": [2], 148 | "brace-style": [2, "1tbs", { "allowSingleLine": true }], 149 | "camelcase": [2, { "properties": "never" }], 150 | "comma-spacing": [2, { 151 | "before": false, 152 | "after": true 153 | }], 154 | "comma-style": [2, "last"], 155 | "computed-property-spacing": [2, "never"], 156 | "consistent-this": [2, "_this"], 157 | "eol-last": [2], 158 | "func-names": [0], 159 | "func-style": [0], 160 | "indent": [2, 2], 161 | "jsx-quotes": [2, "prefer-single"], 162 | "key-spacing": [2, { 163 | "beforeColon": false, 164 | "afterColon": true 165 | }], 166 | "keyword-spacing": [2], 167 | "linebreak-style": [2, "unix"], 168 | "lines-around-comment": [2, { 169 | "beforeBlockComment": true 170 | }], 171 | "max-depth": [2, 4], 172 | "max-len": [2, 120, 4], 173 | "max-nested-callbacks": [2, 4], 174 | "max-params": [0], 175 | "max-statements": [0], 176 | "new-cap": [2], 177 | "new-parens": [2], 178 | "newline-after-var": [2], 179 | "newline-per-chained-call": [0], 180 | "no-array-constructor": [2], 181 | "no-bitwise": [0], 182 | "no-continue": [0], 183 | "no-inline-comments": [0], 184 | "no-lonely-if": [2], 185 | "no-mixed-spaces-and-tabs": [2], 186 | "no-multiple-empty-lines": [2], 187 | "no-negated-condition": [0], 188 | "no-nested-ternary": [2], 189 | "no-new-object": [2], 190 | "no-plusplus": [0], 191 | "no-spaced-func": [2], 192 | "no-ternary": [0], 193 | "no-trailing-spaces": [2], 194 | "no-underscore-dangle": [0], 195 | "no-unneeded-ternary": [2], 196 | "no-whitespace-before-property": [2], 197 | "object-curly-spacing": [2, "always"], 198 | "one-var": [2, { 199 | "uninitialized": "always", 200 | "initialized": "never" 201 | }], 202 | "one-var-declaration-per-line": [0], 203 | "operator-assignment": [0], 204 | "operator-linebreak": [2, "before"], 205 | "padded-blocks": [0], 206 | "quote-props": [2, "as-needed"], 207 | "quotes": [2, "single"], 208 | "require-jsdoc": [0], 209 | "semi": [2, "always"], 210 | "semi-spacing": [2, { 211 | "before": false, 212 | "after": true 213 | }], 214 | "sort-imports": [0], 215 | "sort-vars": [0], 216 | "space-before-blocks": [2, "always"], 217 | "space-before-function-paren": [2, { 218 | "anonymous": "always", 219 | "named": "never" 220 | }], 221 | "space-in-parens": [2, "never"], 222 | "space-infix-ops": [2], 223 | "space-unary-ops": [2], 224 | "spaced-comment": [2, "always"], 225 | "wrap-regex": [2], 226 | 227 | // ecmascript6 228 | "arrow-body-style": [0], 229 | "arrow-parens": [2], 230 | "arrow-spacing": [2, { "before": true, "after": true }], 231 | "constructor-super": [2], 232 | "generator-star-spacing": [2, "after"], 233 | "no-class-assign": [2], 234 | "no-confusing-arrow": [0], 235 | "no-const-assign": [2], 236 | "no-dupe-class-members": [2], 237 | "no-new-symbol": [2], 238 | "no-this-before-super": [2], 239 | "no-useless-constructor": [2], 240 | "no-var": [2], 241 | "object-shorthand": [0], 242 | "prefer-arrow-callback": [0], 243 | "prefer-const": [2], 244 | "prefer-reflect": [0], 245 | "prefer-rest-params": [0], 246 | "prefer-spread": [0], 247 | "prefer-template": [2], 248 | "require-yield": [2], 249 | "template-curly-spacing": [2, "never"] 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | nodejs-jenkins-api 2 | ================= 3 |

4 | 5 | NPM Version 6 | 7 | 8 | NPM downloads 9 | 10 | 11 | License 12 | 13 | 14 | Github Issues 15 | 16 |

17 | 18 | ## Install 19 | 20 |
 21 | npm install jenkins-api
 22 | 
23 | 24 | ## Usage 25 | 26 | ### Setup 27 | 28 | ```javascript 29 | var jenkinsapi = require('jenkins-api'); 30 | 31 | // no auth 32 | var jenkins = jenkinsapi.init("http://jenkins.yoursite.com"); 33 | 34 | // username/password 35 | var jenkins = jenkinsapi.init("http://username:password@jenkins.yoursite.com"); 36 | 37 | // API Token 38 | var jenkins = jenkinsapi.init('https://username:token@jenkins.company.com'); 39 | 40 | // Password that needs to be %-encoded 41 | const { URL } = require('url'); 42 | const jenkinsUrl = new URL('https://username@jenkins.company.com'); 43 | jenkinsUrl.password = 'some_weirdPASSWORD123!@#$%^&*()~`\\/;\''; 44 | var jenkins = jenkinsapi.init(jenkinsUrl.href); 45 | ``` 46 | 47 | If you need additional request parameters you can add them as explained in 'optional' section. 48 | 49 | 50 | ### Builds 51 | 52 | #### build 53 | ```javascript 54 | jenkins.build('job-in-jenkins', (optional){token: 'jenkins-token', ...}, function(err, data) { 55 | if (err){ return console.log(err); } 56 | console.log(data) 57 | }); 58 | ``` 59 | 60 | #### build_with_params 61 | ```javascript 62 | jenkins.build_with_params('job-in-jenkins', (optional){depth: 1, :, token: 'jenkins-token',...}, function(err, data) { 63 | if (err){ return console.log(err); } 64 | console.log(data) 65 | }); 66 | ``` 67 | 68 | #### stop build 69 | ```javascript 70 | jenkins.stop_build('job-in-jenkins', 'build-number', (optional){token: 'jenkins-token', ...}, function(err, data) { 71 | if (err){ return console.log(err); } 72 | console.log(data) 73 | }); 74 | ``` 75 | 76 | #### console output 77 | ```javascript 78 | jenkins.console_output('job-in-jenkins', 'buildname', (optional) {depth: 1, :, ...}, function(err, data) { 79 | if (err){ return console.log(err); } 80 | console.log(data) 81 | }); 82 | ``` 83 | 84 | #### build info 85 | ```javascript 86 | jenkins.build_info('job-in-jenkins', 'build-number', (optional) {depth: 1, :, ...}, function(err, data) { 87 | if (err){ return console.log(err); } 88 | console.log(data) 89 | }); 90 | ``` 91 | 92 | #### last build info 93 | ```javascript 94 | jenkins.last_build_info('job-in-jenkins', (optional) {depth: 1, :, ...}, function(err, data) { 95 | if (err){ return console.log(err); } 96 | console.log(data) 97 | }); 98 | ``` 99 | 100 | #### last completed build info 101 | ```javascript 102 | jenkins.last_completed_build_info('job-in-jenkins', (optional) {depth: 1, :, ...}, function(err, data) { 103 | if (err){ return console.log(err); } 104 | console.log(data) 105 | }); 106 | ``` 107 | 108 | #### all builds 109 | ```javascript 110 | jenkins.all_builds('job-in-jenkins', (optional) {depth: 1, :, ...}, function(err, data) { 111 | if (err){ return console.log(err); } 112 | console.log(data) 113 | }); 114 | ``` 115 | 116 | #### test result/report 117 | ```javascript 118 | jenkins.test_result('job-in-jenkins', 'build-number', (optional) {depth: 1, :, ...}, function(err, data) { 119 | if (err){ return console.log(err); } 120 | console.log(data) 121 | }); 122 | ``` 123 | 124 | #### last build report - OBSOLET use `last_build_info` 125 | ```javascript 126 | // jenkins.last_build_report('job-in-jenkins', (optional) {depth: 1, :, ...}, function(err, data) { 127 | // if (err){ return console.log(err); } 128 | // console.log(data) 129 | // }); 130 | ``` 131 | 132 | #### delete build data for job 133 | ```javascript 134 | jenkins.delete_build('job-in-jenkins', 'build-number', (optional) {depth: 1, :, ...}, function(err, data) { 135 | if (err){ return console.log(err); } 136 | console.log(data) 137 | }); 138 | ``` 139 | 140 | 141 | ### Jobs 142 | 143 | #### all jobs 144 | ```javascript 145 | jenkins.all_jobs((optional){token: 'jenkins-token', ...}, function(err, data) { 146 | if (err){ return console.log(err); } 147 | console.log(data) 148 | }); 149 | ``` 150 | 151 | #### get config xml 152 | ```javascript 153 | jenkins.get_config_xml('job-in-jenkins', (optional) {depth: 1, :, ...}, function(err, data) { 154 | if (err){ return console.log(err); } 155 | console.log(data) 156 | }); 157 | ``` 158 | 159 | ### update existing job configuration 160 | ```javascript 161 | jenkins.update_config('job-to-update' 162 | ,function(config) { 163 | // function which takes the config.xml, and returns 164 | // the new config xml for the new job 165 | return config.replace('development','feature-branch'); 166 | } 167 | ,(optional){token: 'jenkins-token', ...} 168 | ,function(err, data) { 169 | // if no error, job was copied 170 | if (err){ return console.log(err); } 171 | console.log(data) 172 | }); 173 | ``` 174 | 175 | #### update job 176 | ```javascript 177 | jenkins.update_job('job-to-update', xmlConfigString, (optional){token: 'jenkins-token', ...}, function(err, data) { 178 | // if no error, job was copied 179 | if (err){ return console.log(err); } 180 | console.log(data) 181 | }); 182 | ``` 183 | 184 | #### job info 185 | ```javascript 186 | jenkins.job_info('job-in-jenkins', (optional) {depth: 1, :, ...}, function(err, data) { 187 | if (err){ return console.log(err); } 188 | console.log(data) 189 | }); 190 | ``` 191 | 192 | #### create job 193 | ```javascript 194 | jenkins.create_job('job-in-jenkins', xmlConfigString, (optional) {depth: 1, :, ...}, function(err, data) { 195 | if (err){ return console.log(err); } 196 | console.log(data) 197 | }); 198 | ``` 199 | 200 | #### copy job 201 | ```javascript 202 | jenkins.copy_job('job-to-copy' 203 | ,'new-job-title' 204 | ,function(config) { 205 | // function which takes the config.xml, and returns 206 | // the new config xml for the new job 207 | return config.replace('development','feature-branch'); 208 | } 209 | ,(optional){token: 'jenkins-token', ...} 210 | ,function(err, data) { 211 | // if no error, job was copied 212 | if (err){ return console.log(err); } 213 | console.log(data) 214 | }); 215 | ``` 216 | 217 | #### delete job 218 | ```javascript 219 | jenkins.delete_job('job-in-jenkins', (optional) {depth: 1, :, ...}, function(err, data) { 220 | if (err){ return console.log(err); } 221 | console.log(data) 222 | }); 223 | ``` 224 | 225 | #### enable job 226 | ```javascript 227 | jenkins.enable_job('job-in-jenkins', (optional) {depth: 1, :, ...}, function(err, data) { 228 | if (err){ return console.log(err); } 229 | console.log(data) 230 | }); 231 | ``` 232 | 233 | #### disable job 234 | ```javascript 235 | jenkins.disable_job('job-in-jenkins', (optional) {depth: 1, :, ...}, function(err, data) { 236 | if (err){ return console.log(err); } 237 | console.log(data) 238 | }); 239 | ``` 240 | 241 | #### last success 242 | ```javascript 243 | jenkins.last_success('job-in-jenkins', (optional) {depth: 1, :, ...}, function(err, data) { 244 | if (err){ return console.log(err); } 245 | console.log(data) 246 | }); 247 | ``` 248 | 249 | #### last result 250 | ```javascript 251 | jenkins.last_result('job-in-jenkins', (optional) {depth: 1, :, ...}, function(err, data) { 252 | if (err){ return console.log(err); } 253 | console.log(data) 254 | }); 255 | ``` 256 | 257 | 258 | ### Queue 259 | 260 | #### get all queued items 261 | ```javascript 262 | jenkins.queue((optional){token: 'jenkins-token', ...}, function(err, data) { 263 | if (err){ return console.log(err); } 264 | console.log(data) 265 | }); 266 | ``` 267 | 268 | #### get one queued item 269 | ```javascript 270 | jenkins.queue_item('queue-item-number', (optional) {depth: 1, :, ...}, function(err, data) { 271 | if (err){ return console.log(err); } 272 | console.log(data) 273 | }); 274 | ``` 275 | 276 | #### cancel queued item 277 | ```javascript 278 | jenkins.cancel_item('queue-item-number', (optional) {depth: 1, :, ...}, function(err, data) { 279 | if (err){ return console.log(err); } 280 | console.log(data) 281 | }); 282 | ``` 283 | 284 | #### get all jenkins computers (aka workers) 285 | ```javascript 286 | jenkins.computers((optional){token: 'jenkins-token', ...}, function(err, data) { 287 | if (err){ return console.log(err); } 288 | console.log(data) 289 | }); 290 | ``` 291 | 292 | 293 | ### Views 294 | 295 | #### get all views 296 | ```javascript 297 | jenkins.all_views((optional) {depth: 1, :, ...}, function(err, data) { 298 | if (err){ return console.log(err); } 299 | console.log(data) 300 | }); 301 | ``` 302 | 303 | #### create view 304 | ```javascript 305 | jenkins.create_view('new-view-name', (optional)viewMode = 'hudson.model.ListView', (optional){token: 'jenkins-token', ...}, function(err, data) { 306 | if (err){ return console.log(err); } 307 | console.log(data) 308 | }); 309 | ``` 310 | 311 | #### view info 312 | ```javascript 313 | jenkins.create_view('view-name', (optional){token: 'jenkins-token', ...}, function(err, data) { 314 | if (err){ return console.log(err); } 315 | console.log(data) 316 | }); 317 | ``` 318 | 319 | #### update view 320 | ```javascript 321 | var viewConfig = { 322 | name: "view-in-jenkins", 323 | "description": "This is the view-in-jenkins View", 324 | "statusFilter": "", 325 | "job-in-jenkins": true, 326 | "useincluderegex": true, 327 | "includeRegex": "prefix.*", 328 | "columns": [{"stapler-class": "hudson.views.StatusColumn", "$class": "hudson.views.StatusColumn"}, {"stapler-class": "hudson.views.WeatherColumn", "$class": "hudson.views.WeatherColumn"}, {"stapler-class": "hudson.views.JobColumn", "$class": "hudson.views.JobColumn"}, {"stapler-class": "hudson.views.LastSuccessColumn", "$class": "hudson.views.LastSuccessColumn"}, {"stapler-class": "hudson.views.LastFailureColumn", "$class": "hudson.views.LastFailureColumn"}, {"stapler-class": "hudson.views.LastDurationColumn", "$class": "hudson.views.LastDurationColumn"}, {"stapler-class": "hudson.views.BuildButtonColumn", "$class": "hudson.views.BuildButtonColumn"}] 329 | }; 330 | 331 | jenkins.update_view('view-in-jenkins', viewConfig, (optional){token: 'jenkins-token', ...}, function(err, data) { 332 | if (err){ return console.log(err); } 333 | console.log(data) 334 | }); 335 | ``` 336 | 337 | #### delete view 338 | ```javascript 339 | jenkins.delete_view('view-in-jenkins', (optional){token: 'jenkins-token', ...}, function(err, data) { 340 | if (err){ return console.log(err); } 341 | console.log(data) 342 | }); 343 | ``` 344 | 345 | #### add job to view 346 | ```javascript 347 | jenkins.add_job_to_view('view-in-jenkins', 'job-in-jenkins', (optional) {depth: 1, :, ...}, function(err, data) { 348 | if (err){ return console.log(err); } 349 | console.log(data) 350 | }); 351 | ``` 352 | 353 | #### remove job from view 354 | ```javascript 355 | jenkins.remove_job_from_view('view-in-jenkins', 'job-in-jenkins', (optional) {depth: 1, :, ...}, function(err, data) { 356 | if (err){ return console.log(err); } 357 | console.log(data) 358 | }); 359 | ``` 360 | 361 | #### get all jobs in view 362 | ```javascript 363 | jenkins.all_jobs_in_view('view-in-jenkins', (optional) {depth: 1, :, ...}, function(err, data) { 364 | if (err){ return console.log(err); } 365 | console.log(data) 366 | }); 367 | ``` 368 | 369 | 370 | ### Plugins 371 | 372 | #### get all installed plugins 373 | ```javascript 374 | jenkins.all_installed_plugins((optional){token: 'jenkins-token', ...}, function(err, data){ 375 | if (err){ return console.log(err); } 376 | console.log(data) 377 | }) 378 | ``` 379 | 380 | #### install a plugin 381 | ```javascript 382 | // var plugin = 'copyartifact@1.3.8'; 383 | var plugin = 'copyartifact@current'; 384 | jenkins.install_plugin(plugin, (optional){token: 'jenkins-token', ...}, function(err, data){ 385 | if (err){ return console.log(err, data); } 386 | console.log(data) 387 | }); 388 | ``` 389 | NOTE: It will report successful even if the plugin is already installed. 390 | NOTE: Prevent Cross Site Request Forgery exploits need be disabled in Configure Global Security. 391 | 392 | 393 | 394 | ## Default configuration 395 | 396 | You can set the default configuration which will be use in all HTTP requests by calling init with the additional options parameter: 397 | 398 | ```javascript 399 | // default request options 400 | var jenkins = jenkinsapi.init("http://jenkins.yoursite.com", {request: {strictSSL: false}}); 401 | ``` 402 | 403 | Futhermore, you can set your remote job token for authentication (as well as any other default url params): 404 | 405 | ```javascript 406 | // default request options 407 | var jenkins = jenkinsapi.init("http://jenkins.yoursite.com", {request: {strictSSL: false}}, {token: ''}); 408 | ``` 409 | 410 | Since node-jenkins-api uses [request/request](https://github.com/request/request) as HTTP client, please refer to the documentation for available options. Default values can be found in the source code (search for "default"). 411 | 412 | 413 | ## Notes 414 | 415 | Modeled after the [Python Jenkins API](https://github.com/txels/autojenkins) 416 | 417 | -------------------------------------------------------------------------------- /test/main.js: -------------------------------------------------------------------------------- 1 | const async = require('async'); 2 | const fs = require('fs'); 3 | const chai = require('chai'); 4 | const expect = chai.expect; 5 | 6 | const chaiLike = require('chai-like'); 7 | const chaiThings = require('chai-things'); 8 | 9 | chai.use(chaiLike); 10 | chai.use(chaiThings); 11 | 12 | const jenkinsapi = require('../lib/main'); 13 | const JENKINS_URL = 'http://localhost:8080'; 14 | 15 | // var USER = 'jenkins'; 16 | // var TOKEN = 'a5af9f12a1723c2a45c4c6bfd88d0044'; 17 | // var JENKINS_URL = "http://" + USER + ":" + TOKEN + "@localhost:8080"; 18 | 19 | const JOB_NAME_TEST = 'asrwqersfdzdraser-test'; 20 | const JOB_NAME_NEW = 'asrwqersfdzdraser-test-new'; 21 | const JOB_NAME_COPY = 'asrwqersfdzdraser-test-copy'; 22 | const JOB_NAME_REPORT = 'asrwqersfdzdraser-test-with-report'; 23 | 24 | const TEST_CONFIG_XML_FILE = 'test/test_config.xml'; 25 | const TEST_WITH_PARAMS_CONFIG_XML_FILE = 'test/test_with_params_config.xml'; 26 | const TEST_WITH_REPORT_CONFIG_XML_FILE = 'test/test_with_report_config.xml'; 27 | const DEVELOPMENT_PROJECT_XML_CONFIG 28 | = 'development'; 29 | const ORIGINAL_DESCRIPTION = 'development'; 30 | const REPLACED_DESCRIPTION = 'feature'; 31 | 32 | describe('Node Jenkins API', function () { 33 | 34 | let jenkins; 35 | 36 | before(function (done) { 37 | 38 | it('Should exist', function () { 39 | expect(jenkinsapi.init).to.be.a('function'); 40 | }); 41 | 42 | jenkins = jenkinsapi.init(JENKINS_URL); 43 | 44 | expect(jenkins.delete_job).to.be.a('function'); 45 | expect(jenkins.all_jobs).to.be.a('function'); 46 | 47 | async.waterfall([ 48 | (next) => { 49 | // Ignoring 404 errors 50 | async.parallel([ 51 | (cb) => jenkins.delete_job(JOB_NAME_TEST, () => cb()), 52 | (cb) => jenkins.delete_job(JOB_NAME_NEW, () => cb()), 53 | (cb) => jenkins.delete_job(JOB_NAME_COPY, () => cb()) 54 | ], () => next()); 55 | }, 56 | (next) => { 57 | fs.readFile(TEST_CONFIG_XML_FILE, 'utf8', next); 58 | }, 59 | (xmlConfig, next) => { 60 | jenkins.create_job(JOB_NAME_TEST, xmlConfig, next); 61 | }, 62 | (data, next) => { 63 | jenkins.all_jobs(next); 64 | }, 65 | (data, next) => { 66 | expect(data).to.be.an('array').that.contains.something.like({ name: JOB_NAME_TEST }); 67 | next(); 68 | } 69 | ], done); 70 | }); 71 | 72 | it('Should show all jobs', function (done) { 73 | expect(jenkins.all_jobs).to.be.a('function'); 74 | 75 | jenkins.all_jobs(function (error, data) { 76 | expect(data).to.be.an('array').that.contains.something.like({ name: JOB_NAME_TEST }); 77 | done(error); 78 | }); // all_jobs 79 | }); 80 | 81 | 82 | it('Should read xml of existing job', function (done) { 83 | expect(jenkins.get_config_xml).to.be.a('function'); 84 | 85 | jenkins.get_config_xml(JOB_NAME_TEST, function (error, data) { 86 | expect(data).to.be.a('string').that.contains(ORIGINAL_DESCRIPTION); 87 | done(error); 88 | }); // get_config_xml 89 | }); 90 | 91 | 92 | it('Should show job info', function (done) { 93 | expect(jenkins.job_info).to.be.a('function'); 94 | 95 | jenkins.job_info(JOB_NAME_TEST, function (error, data) { 96 | expect(data).to.be.an('object').like({ name: JOB_NAME_TEST, description: ORIGINAL_DESCRIPTION }); 97 | done(error); 98 | }); // job_info 99 | }); 100 | 101 | 102 | it('Should create and delete job', function (done) { 103 | expect(jenkins.create_job).to.be.a('function'); 104 | expect(jenkins.delete_job).to.be.a('function'); 105 | 106 | async.waterfall([ 107 | (next) => { 108 | jenkins.create_job(JOB_NAME_NEW, DEVELOPMENT_PROJECT_XML_CONFIG, next); 109 | }, 110 | (data, next) => { 111 | expect(data).to.be.an('object').like({ name: JOB_NAME_NEW }); 112 | jenkins.all_jobs(next); 113 | }, 114 | (data, next) => { 115 | expect(data).to.be.an('array').that.contains.something.like({ name: JOB_NAME_NEW }); 116 | jenkins.delete_job(JOB_NAME_NEW, next); 117 | }, 118 | (data, next) => { 119 | expect(data).to.be.an('object').like({ name: JOB_NAME_NEW }); 120 | jenkins.all_jobs(next); 121 | }, 122 | (data, next) => { 123 | expect(data).to.be.an('array').that.does.not.contain.something.like({ name: JOB_NAME_NEW }); 124 | next(); 125 | } 126 | ], done); 127 | }); 128 | 129 | 130 | it('Should copy job', function (done) { 131 | expect(jenkins.copy_job).to.be.a('function'); 132 | 133 | async.waterfall([ 134 | (next) => { 135 | jenkins.copy_job( 136 | JOB_NAME_TEST, 137 | JOB_NAME_COPY, 138 | (data) => data.replace(ORIGINAL_DESCRIPTION, REPLACED_DESCRIPTION), 139 | next); 140 | }, 141 | (data, next) => { 142 | expect(data).to.be.an('object').like({ name: JOB_NAME_COPY }); 143 | jenkins.get_config_xml(JOB_NAME_COPY, next); 144 | }, 145 | (data, next) => { 146 | expect(data).to.be.a('string').that.contains(REPLACED_DESCRIPTION); 147 | jenkins.delete_job(JOB_NAME_COPY, next); 148 | } 149 | ], done); 150 | }); 151 | 152 | 153 | it('Should update job config', function (done) { 154 | expect(jenkins.update_config).to.be.a('function'); 155 | 156 | async.waterfall([ 157 | (next) => { 158 | jenkins.copy_job(JOB_NAME_TEST, JOB_NAME_COPY, (data) => data, () => next()); 159 | }, 160 | (next) => { 161 | jenkins.update_config(JOB_NAME_COPY, (data) => data.replace(ORIGINAL_DESCRIPTION, REPLACED_DESCRIPTION), next); 162 | }, 163 | (data, next) => { 164 | expect(data).to.be.an('object').like({ name: JOB_NAME_COPY }); 165 | jenkins.get_config_xml(JOB_NAME_COPY, next); 166 | }, 167 | (data, next) => { 168 | expect(data).to.be.a('string').that.contains(REPLACED_DESCRIPTION); 169 | jenkins.delete_job(JOB_NAME_COPY, next); 170 | } 171 | ], done); 172 | }); 173 | 174 | it('Should disable/enable job', function (done) { 175 | expect(jenkins.disable_job).to.be.a('function'); 176 | expect(jenkins.enable_job).to.be.a('function'); 177 | 178 | async.waterfall([ 179 | (next) => { 180 | jenkins.copy_job(JOB_NAME_TEST, JOB_NAME_COPY, (data) => data, next); 181 | }, 182 | (data, next) => { 183 | jenkins.disable_job(JOB_NAME_COPY, next); 184 | }, 185 | (data, next) => { 186 | expect(data).to.be.an('object').like({ name: JOB_NAME_COPY, color: 'disabled', buildable: false }); 187 | jenkins.enable_job(JOB_NAME_COPY, next); 188 | }, 189 | (data, next) => { 190 | expect(data).to.be.an('object').like({ name: JOB_NAME_COPY }).and.not.like({ color: 'disabled' }); 191 | jenkins.delete_job(JOB_NAME_COPY, next); 192 | } 193 | ], done); 194 | }); 195 | 196 | 197 | const TEST_VIEW_NAME = 'ewoiurewlkjr-test-view'; 198 | const TEST_VIEW_CONF = { 199 | name: TEST_VIEW_NAME, 200 | description: `This is the ${TEST_VIEW_NAME} job-in-jenkins View`, 201 | statusFilter: '', 202 | 'job-in-jenkins': true, 203 | useincluderegex: true, 204 | includeRegex: 'prefix.*', 205 | columns: [{ 206 | 'stapler-class': 'hudson.views.StatusColumn', 207 | $class: 'hudson.views.StatusColumn' 208 | }, 209 | { 210 | 'stapler-class': 'hudson.views.WeatherColumn', 211 | $class: 'hudson.views.WeatherColumn' 212 | }, 213 | { 214 | 'stapler-class': 'hudson.views.JobColumn', 215 | $class: 'hudson.views.JobColumn' 216 | }, { 217 | 'stapler-class': 'hudson.views.LastSuccessColumn', 218 | $class: 'hudson.views.LastSuccessColumn' 219 | }, 220 | { 221 | 'stapler-class': 'hudson.views.LastFailureColumn', 222 | $class: 'hudson.views.LastFailureColumn' 223 | }, 224 | { 225 | 'stapler-class': 'hudson.views.LastDurationColumn', 226 | $class: 'hudson.views.LastDurationColumn' 227 | }, 228 | { 229 | 'stapler-class': 'hudson.views.BuildButtonColumn', 230 | $class: 'hudson.views.BuildButtonColumn' 231 | }] 232 | }; 233 | 234 | it('Should CRUD a view', function (done) { 235 | expect(jenkins.create_view).to.be.a('function'); 236 | expect(jenkins.view_info).to.be.a('function'); 237 | expect(jenkins.all_views).to.be.a('function'); 238 | expect(jenkins.update_view).to.be.a('function'); 239 | expect(jenkins.delete_view).to.be.a('function'); 240 | 241 | 242 | async.waterfall([ 243 | (next) => { 244 | jenkins.delete_view(TEST_VIEW_NAME, () => next()); // Ignoring error 245 | }, 246 | (next) => { 247 | jenkins.create_view(TEST_VIEW_NAME, next); 248 | }, 249 | (data, next) => { 250 | expect(data).to.be.an('object').like({ name: TEST_VIEW_NAME }); 251 | jenkins.view_info(TEST_VIEW_NAME, next); 252 | }, 253 | (data, next) => { 254 | expect(data).to.be.an('object').like({ name: TEST_VIEW_NAME }); 255 | jenkins.all_views(next); 256 | }, 257 | (data, next) => { 258 | expect(data).to.be.an('array').that.contains.something.like({ name: TEST_VIEW_NAME }); 259 | jenkins.update_view(TEST_VIEW_NAME, TEST_VIEW_CONF, next); 260 | }, 261 | (data, next) => { 262 | expect(data).to.be.an('object').like({ name: TEST_VIEW_NAME }); 263 | jenkins.delete_view(TEST_VIEW_NAME, next); 264 | }, 265 | (data, next) => { 266 | expect(data).to.be.an('object').like({ name: TEST_VIEW_NAME }); 267 | jenkins.all_views(next); 268 | }, 269 | (data, next) => { 270 | expect(data).to.be.an('array').that.does.not.contain.something.like({ name: TEST_VIEW_NAME }); 271 | next(); 272 | } 273 | ], done); 274 | }); 275 | 276 | it('Should add/remove and list jobs in view', function (done) { 277 | expect(jenkins.all_jobs_in_view).to.be.a('function'); 278 | expect(jenkins.add_job_to_view).to.be.a('function'); 279 | expect(jenkins.remove_job_from_view).to.be.a('function'); 280 | 281 | async.waterfall([ 282 | (next) => { 283 | jenkins.delete_view(TEST_VIEW_NAME, () => next()); // Ignoring error 284 | }, 285 | (next) => { 286 | jenkins.create_view(TEST_VIEW_NAME, next); 287 | }, 288 | (data, next) => { 289 | expect(data).to.be.an('object').like({ name: TEST_VIEW_NAME }); 290 | jenkins.add_job_to_view(TEST_VIEW_NAME, JOB_NAME_TEST, next); 291 | }, 292 | (data, next) => { 293 | jenkins.all_jobs_in_view(TEST_VIEW_NAME, next); 294 | }, 295 | (data, next) => { 296 | expect(data).to.be.an('array').that.contains.something.like({ name: JOB_NAME_TEST }); 297 | jenkins.remove_job_from_view(TEST_VIEW_NAME, JOB_NAME_TEST, next); 298 | }, 299 | (data, next) => { 300 | jenkins.all_jobs_in_view(TEST_VIEW_NAME, next); 301 | }, 302 | (data, next) => { 303 | expect(data).to.be.an('array').that.does.not.contain.something.like({ name: JOB_NAME_TEST }); 304 | jenkins.delete_view(TEST_VIEW_NAME, next); 305 | }, 306 | (data, next) => { 307 | expect(data).to.be.an('object').like({ name: TEST_VIEW_NAME }); 308 | next(); 309 | } 310 | ], done); 311 | }); 312 | 313 | 314 | const APPROX_JOB_RUN_TIME = 11000; 315 | const APPROX_JOB_STOP_TIME = 2000; 316 | const START_TO_END_JOB_TIME = 14000; 317 | 318 | it('Should start/stop and list builds', function (done) { 319 | expect(jenkins.build).to.be.a('function'); 320 | expect(jenkins.job_info).to.be.a('function'); 321 | expect(jenkins.queue_item).to.be.a('function'); 322 | expect(jenkins.all_builds).to.be.a('function'); 323 | expect(jenkins.build_info).to.be.a('function'); 324 | expect(jenkins.stop_build).to.be.a('function'); 325 | expect(jenkins.console_output).to.be.a('function'); 326 | expect(jenkins.last_build_info).to.be.a('function'); 327 | expect(jenkins.delete_build).to.be.a('function'); 328 | 329 | let queueId, buildId; 330 | 331 | async.waterfall([ 332 | (next) => { 333 | jenkins.build(JOB_NAME_TEST, next); 334 | }, 335 | (data, next) => { 336 | expect(data).to.be.an('object'); 337 | expect(data.queueId).to.be.a('number'); 338 | // Store queueId 339 | queueId = data.queueId; 340 | jenkins.job_info(JOB_NAME_TEST, next); 341 | }, 342 | (data, next) => { 343 | expect(data).to.be.an('object').like({ name: JOB_NAME_TEST }); 344 | expect(data.queueItem).to.be.an('object').like({ _class: 'hudson.model.Queue$WaitingItem', id: queueId }); 345 | jenkins.queue_item(queueId, next); 346 | }, 347 | (data, next) => { 348 | expect(data).to.be.an('object').like({ id: queueId }); 349 | setTimeout(next, APPROX_JOB_RUN_TIME); 350 | }, 351 | (next) => { 352 | jenkins.queue_item(queueId, next); 353 | }, 354 | (data, next) => { 355 | expect(data).to.be.an('object').like({ id: queueId }); 356 | expect(data.executable).to.be.an('object'); 357 | expect(data.executable.number).to.be.a('number'); 358 | // Store buildId 359 | buildId = data.executable.number; 360 | jenkins.job_info(JOB_NAME_TEST, next); 361 | }, 362 | (data, next) => { 363 | expect(data).to.be.an('object').like({ name: JOB_NAME_TEST }); 364 | expect(data.lastBuild).to.be.an('object').like({ _class: 'hudson.model.FreeStyleBuild' }); 365 | expect(data.lastBuild.number).to.equal(buildId); 366 | jenkins.all_builds(JOB_NAME_TEST, next); 367 | }, 368 | (data, next) => { 369 | expect(data).to.be.an('array').that.contains.something.like({ id: `${buildId}` }); 370 | jenkins.build_info(JOB_NAME_TEST, buildId, next); 371 | }, 372 | (data, next) => { 373 | expect(data).to.be.an('object').like({ number: buildId, building: true, result: null }); 374 | jenkins.stop_build(JOB_NAME_TEST, buildId, next); 375 | }, 376 | (data, next) => { 377 | expect(data).to.be.an('object').like({ body: `Build ${buildId} stopped.` }); 378 | setTimeout(next, APPROX_JOB_STOP_TIME); 379 | }, 380 | (next) => { 381 | jenkins.build_info(JOB_NAME_TEST, buildId, next); 382 | }, 383 | (data, next) => { 384 | expect(data).to.be.an('object').like({ number: buildId, building: false, result: 'ABORTED' }); 385 | jenkins.console_output(JOB_NAME_TEST, buildId, next); 386 | }, 387 | (data, next) => { 388 | expect(data).to.be.an('object'); 389 | expect(data.body).to.be.a('string').that.contains('sleep 60'); 390 | jenkins.last_build_info(JOB_NAME_TEST, next); 391 | }, 392 | (data, next) => { 393 | expect(data).to.be.an('object').like({ number: buildId, building: false, result: 'ABORTED' }); 394 | jenkins.delete_build(JOB_NAME_TEST, buildId, next); 395 | }, 396 | (data, next) => { 397 | jenkins.all_builds(JOB_NAME_TEST, next); 398 | }, 399 | (data, next) => { 400 | expect(data).to.be.an('array').that.does.not.contain.something.like({ id: `${buildId}` }); 401 | next(); 402 | } 403 | ], done); 404 | }).timeout(START_TO_END_JOB_TIME); 405 | 406 | 407 | it('Should build with params', function (done) { 408 | const SLEEP_TIME = 123; 409 | let queueId, buildId; 410 | 411 | async.waterfall([ 412 | (next) => { 413 | fs.readFile(TEST_WITH_PARAMS_CONFIG_XML_FILE, 'utf8', next); 414 | }, 415 | (xmlConfig, next) => { 416 | jenkins.create_job(JOB_NAME_NEW, xmlConfig, next); 417 | }, 418 | (data, next) => { 419 | jenkins.build_with_params(JOB_NAME_NEW, { sleep_time: SLEEP_TIME }, next); 420 | }, 421 | (data, next) => { 422 | expect(data).to.be.an('object'); 423 | expect(data.queueId).to.be.a('number'); 424 | // Store queueId 425 | queueId = data.queueId; 426 | setTimeout(next, APPROX_JOB_RUN_TIME); 427 | }, 428 | (next) => { 429 | jenkins.queue_item(queueId, next); 430 | }, 431 | (data, next) => { 432 | expect(data).to.be.an('object').like({ id: queueId }); 433 | expect(data.executable).to.be.an('object'); 434 | expect(data.executable.number).to.be.a('number'); 435 | // Store buildId 436 | buildId = data.executable.number; 437 | jenkins.console_output(JOB_NAME_NEW, buildId, next); 438 | }, 439 | (data, next) => { 440 | expect(data).to.be.an('object'); 441 | expect(data.body).to.be.a('string').that.contains(`sleep ${SLEEP_TIME}`); 442 | jenkins.delete_job(JOB_NAME_NEW, next); 443 | } 444 | ], done); 445 | }).timeout(START_TO_END_JOB_TIME); 446 | 447 | // Use for onetime tasks do this 448 | it('Should show a test report', function (done) { 449 | let queueId, buildId; 450 | 451 | async.waterfall([ 452 | (next) => { 453 | fs.readFile(TEST_WITH_REPORT_CONFIG_XML_FILE, 'utf8', next); 454 | }, 455 | (xmlConfig, next) => { 456 | jenkins.create_job(JOB_NAME_REPORT, xmlConfig, next); 457 | }, 458 | (data, next) => { 459 | jenkins.build(JOB_NAME_REPORT, next); 460 | }, 461 | (data, next) => { 462 | expect(data).to.be.an('object'); 463 | expect(data.queueId).to.be.a('number'); 464 | // Store queueId 465 | queueId = data.queueId; 466 | // Wait for the job to finish 467 | setTimeout(next, APPROX_JOB_RUN_TIME); 468 | }, 469 | (next) => { 470 | jenkins.last_build_info(JOB_NAME_REPORT, next); 471 | }, 472 | (data, next) => { 473 | expect(data).to.be.an('object').like({ queueId: queueId, result: 'SUCCESS' }); 474 | expect(data.number).to.be.a('number'); 475 | // Store buildId 476 | buildId = data.number; 477 | jenkins.test_result(JOB_NAME_REPORT, buildId, next); 478 | }, 479 | (data, next) => { 480 | expect(data).to.be.an('object') 481 | .like({ duration: 0.006, empty: false, failCount: 0, passCount: 1, skipCount: 0 }); 482 | jenkins.delete_job(JOB_NAME_REPORT, next); 483 | } 484 | ], done); 485 | }).timeout(START_TO_END_JOB_TIME); 486 | 487 | // For onetime tasks do this 488 | // it.only('should do this', function(done) { 489 | // jenkins.last_build_report(JOB_NAME_REPORT, function (error, data) { 490 | // done(); 491 | // }); // queue_item 492 | // }); 493 | 494 | }); 495 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const qs = require('querystring'); 5 | const request = require('request'); 6 | 7 | const API = '/api/json'; 8 | const LIST = API; 9 | const CREATE = `/createItem${API}`; 10 | 11 | const BUILD_START = `/job/%s/build${API}`; 12 | const BUILD_START_WITHPARAMS = '/job/%s/buildWithParameters'; // TODO how to handle this? 13 | const BUILD_STOP = '/job/%s/%s/stop'; 14 | const BUILD_INFO = `/job/%s/%s${API}`; 15 | const BUILD_DELETE = '/job/%s/%s/doDelete'; 16 | 17 | const ALL_BUILDS = `/job/%s${API}?tree=allBuilds[%s]`; 18 | const LAST_SUCCESS = `/job/%s/lastSuccessfulBuild${API}`; 19 | const TEST_REPORT = `/job/%s/%s/testReport${API}`; 20 | const LAST_BUILD = `/job/%s/lastBuild${API}`; 21 | const LAST_COMPLETED_BUILD = `/job/%s/lastCompletedBuild${API}`; 22 | // const LAST_REPORT = '/job/%s/lastBuild' + API; //TODO is there url for lastTestReport? 23 | 24 | const COMPUTERS = `/computer${API}`; 25 | 26 | const VIEW_LIST = LIST; 27 | const VIEW_INFO = `/view/%s${API}`; 28 | const VIEW_CREATE = `/createView${API}`; 29 | const VIEW_CONFIG = '/view/%s/configSubmit'; // NOTE form encoded not called via /api/json, TODO fix 30 | const VIEW_DELETE = `/view/%s/doDelete${API}`; 31 | const VIEW_ADD_JOB = `/view/%s/addJobToView${API}`; 32 | const VIEW_REMOVE_JOB = `/view/%s/removeJobFromView${API}`; 33 | 34 | const JOB_LIST = LIST; 35 | const JOB_CREATE = CREATE; 36 | const JOB_INFO = `/job/%s${API}`; 37 | const JOB_CONFIG = `/job/%s/config.xml${API}`; 38 | const JOB_OUTPUT = `/job/%s/%s/consoleText${API}`; 39 | const JOB_DELETE = `/job/%s/doDelete${API}`; 40 | const JOB_DISABLE = '/job/%s/disable'; 41 | const JOB_ENABLE = '/job/%s/enable'; 42 | 43 | const QUEUE = `/queue${API}`; 44 | const QUEUE_ITEM = `/queue/item/%s${API}`; 45 | const QUEUE_CANCEL_ITEM = `/queue/cancelItem${API}`; // TODO verify this works with API 46 | 47 | const PLUGINS = `/pluginManager${API}`; 48 | const INSTALL_PLUGIN = `/pluginManager/installNecessaryPlugins${API}`; 49 | 50 | const NEWFOLDER = CREATE; 51 | 52 | const HTTP_CODE_200 = 200; 53 | const HTTP_CODE_201 = 201; 54 | const HTTP_CODE_302 = 302; 55 | 56 | // ----------------------------------------------------------------------------- 57 | // Helper Functions 58 | 59 | /** 60 | * @param {any} value - variable to detect type of. 61 | * @return {string} - typeof the value param, but 'array' for arrays and 'null' for nulls. 62 | */ 63 | function getType(value) { 64 | if (Array.isArray(value)) { 65 | return 'array'; 66 | } else if (value === null) { 67 | return 'null'; 68 | } 69 | return typeof value; 70 | 71 | } 72 | 73 | /** 74 | * Processes arguments according to the rules defined by the types array. 75 | * Types of arguments are checked, optional arguments are replaced by their 76 | * default values or left undefined. 77 | * 78 | * @param {arguments} values: The arguments object. 79 | * @param {array} types: Array of types, see below. 80 | * @returns {array} the processed arguments in an array. 81 | * @throws {Error} if arguments don't fit, throws an error. 82 | * 83 | * Examples of types: 84 | * ['string'] - function requires one string 85 | * ['string', ['number']] - function requires one string and expects an optional number 86 | * [['object', {}]] - function expects an optional object defaulting to empty object 87 | * ['string|array'] - function requires string or array 88 | * 89 | * Inspired by 90 | * See: https://www.npmjs.com/package/assert-args 91 | * See: https://www.npmjs.com/package/ensurethat 92 | */ 93 | function doArgs(values, types) { 94 | let value, type, carry, optional, defaultValue; 95 | const result = []; 96 | 97 | for (type of types) { 98 | // Load value if we don't have one already 99 | if (!carry) { 100 | value = Array.prototype.shift.call(values); 101 | } 102 | 103 | // Handle optional type 104 | if (Array.isArray(type)) { 105 | optional = true; 106 | defaultValue = type[1]; 107 | type = type[0]; 108 | } else { 109 | optional = false; 110 | } 111 | 112 | // Handle multitype 113 | type = type.split('|'); 114 | 115 | // Is it a correct value? 116 | if (type.includes(getType(value))) { 117 | result.push(value); 118 | carry = false; 119 | } else if (optional) { 120 | result.push(defaultValue); 121 | carry = true; 122 | } else { 123 | throw Error('Invalid arguments'); 124 | } 125 | } 126 | if (values.length) { 127 | // TODO maybe do some warning..? 128 | // throw Error('Extra arguments ' + values.length, values); 129 | } 130 | return result; 131 | } 132 | 133 | /** 134 | * Parse the passed string as JSON and extract or "manipulate" it using the property. 135 | * 136 | * @param {string} body - string response body 137 | * @param {string|array|function} property - property to get from body or modificator function 138 | * @param {function} callback function to call when all done 139 | */ 140 | function tryParseJson(body, property, callback) { 141 | [body, property, callback] = doArgs(arguments, ['string', 'string|array|function|null', 'function']); 142 | 143 | try { 144 | // Won't harm if we replace escape sequence 145 | body = body.replace(/\x1b/g, ''); 146 | 147 | // Try to parse 148 | let data = JSON.parse(body.toString()); 149 | 150 | // Get the prop name if specified 151 | if (property) { 152 | const type = getType(property); 153 | 154 | if (type === 'array') { 155 | const newData = {}; 156 | 157 | for (const p of property) { 158 | newData[p] = data[p]; 159 | } 160 | data = newData; 161 | } 162 | if (type === 'string') { 163 | data = data[property]; 164 | } 165 | if (type === 'function') { 166 | data = property(data); 167 | } 168 | } 169 | 170 | callback(null, data); 171 | } catch (e) { 172 | callback(e, body); 173 | } 174 | } 175 | 176 | 177 | // ----------------------------------------------------------------------------- 178 | 179 | exports.init = function (host, defaultOptions, defaultParams) { 180 | 181 | /** 182 | * Builds the URL to Jenkins from formatstring pattern and params. 183 | * 184 | * @param {string} pattern - URL format string patern. 185 | * @param {string|number} arguments for the formatstring pattern. 186 | * @returns {string} url - the URL formated according to arguments. 187 | */ 188 | function formatUrl() { 189 | return host + util.format.apply(null, arguments); 190 | } 191 | 192 | /** 193 | * Build REST params and correctly append them to URL. 194 | * 195 | * @param {string} url to be extended with params. 196 | * @param {object} specificParams key/value pair of params. 197 | * @returns {string} the extended url. 198 | */ 199 | const appendParams = function (url, specificParams) { 200 | // Assign default and specific parameters 201 | const params = Object.assign({}, defaultParams, specificParams); 202 | 203 | // Stringify the querystring params 204 | const paramsString = qs.stringify(params); 205 | 206 | // Empty params 207 | if (paramsString === '') { 208 | return url; 209 | } 210 | 211 | // Does url contain parameters already? 212 | const delim = (url.includes('?') ? '&' : '?'); 213 | 214 | return url + delim + paramsString; 215 | }; 216 | 217 | /** 218 | * Just helper funckion to build the request URL. 219 | * 220 | * @param {array} urlPattern array in format of [urlFormat, arg1, arg2] used to build the URL. 221 | * @param {object} customParams key/value pair of params. 222 | * @returns {string} the URL built. 223 | */ 224 | function buildUrl(urlPattern, customParams) { 225 | let url = formatUrl.apply(null, urlPattern); 226 | 227 | url = appendParams(url, customParams); 228 | return url; 229 | } 230 | 231 | /** 232 | * Run the actual HTTP request. 233 | * 234 | * @param {object} specificOptions - options object overriding the default options below. 235 | * @param {object} customParams - custom url params to be added to url. 236 | * @param {function} callback - the callback function to be called when request is finished. 237 | */ 238 | const doRequest = function (specificOptions, customParams, callback) { 239 | 240 | // Options - Default values 241 | const options = Object.assign({}, { 242 | urlPattern: ['/'], 243 | method: 'GET', 244 | successStatusCodes: [HTTP_CODE_200], 245 | failureStatusCodes: [], 246 | bodyProp: null, 247 | noparse: false, 248 | request: {} 249 | }, defaultOptions, specificOptions); 250 | 251 | // Create the url 252 | const url = buildUrl(options.urlPattern, customParams); 253 | 254 | // Build the actual request options 255 | const requestOptions = Object.assign({ 256 | method: options.method, 257 | url: url, 258 | headers: [], 259 | followAllRedirects: true, 260 | form: null, 261 | body: null 262 | }, options.request); 263 | 264 | // Do the request 265 | request(requestOptions, function (error, response, body) { 266 | if (error) { 267 | callback(error, response); 268 | return; 269 | } 270 | 271 | if ((Array.isArray(options.successStatusCodes) && !options.successStatusCodes.includes(response.statusCode)) 272 | || (Array.isArray(options.failureStatusCodes) && options.failureStatusCodes.includes(response.statusCode))) { 273 | callback(`Server returned unexpected status code: ${response.statusCode}`, response); 274 | return; 275 | } 276 | 277 | if (options.noparse) { 278 | // Wrap body in the response object 279 | if (typeof body === 'string') { 280 | body = { body: body }; 281 | } 282 | 283 | // Add location 284 | const location = response.headers.Location || response.headers.location; 285 | 286 | if (location) { 287 | body.location = location; 288 | } 289 | 290 | // Add status code 291 | body.statusCode = response.statusCode; 292 | 293 | callback(null, body); 294 | } else { 295 | tryParseJson(body, options.bodyProp, callback); 296 | } 297 | }); 298 | }; 299 | 300 | return { 301 | 302 | /** ***********************************\ 303 | |* Builds *| 304 | \*************************************/ 305 | 306 | /** Trigger Jenkins to build. 307 | * 308 | * Return queue location of newly-created job as per 309 | * https://issues.jenkins-ci.org/browse/JENKINS-12827?focusedCommentId=201381#comment-201381 310 | * 311 | * @param {string} jobName 312 | * @param {object|undefined} customParams is optional 313 | * @param {function} callback 314 | */ 315 | build: function (jobName, customParams, callback) { 316 | [jobName, customParams, callback] = doArgs(arguments, ['string', ['object', {}], 'function']); 317 | 318 | doRequest({ 319 | method: 'POST', 320 | urlPattern: [BUILD_START, jobName], 321 | successStatusCodes: [HTTP_CODE_201, HTTP_CODE_302], 322 | noparse: true 323 | }, customParams, function (error, data) { 324 | if (error) { 325 | callback(error, data); 326 | return; 327 | } 328 | 329 | const queueIdRe = /\/queue\/item\/(\d+)/; 330 | const id = +queueIdRe.exec(data.location)[1]; 331 | 332 | data.queueId = id; 333 | 334 | callback(null, data); 335 | }); 336 | }, 337 | 338 | /** 339 | * 340 | * @param {string} jobName 341 | * @param {object|undefined} customParams is optional 342 | * @param {function} callback 343 | */ 344 | build_with_params: function (jobName, customParams, callback) { 345 | [jobName, customParams, callback] = doArgs(arguments, ['string', ['object', {}], 'function']); 346 | 347 | doRequest({ 348 | method: 'POST', 349 | urlPattern: [BUILD_START_WITHPARAMS, jobName], 350 | successStatusCodes: [HTTP_CODE_201, HTTP_CODE_302], 351 | noparse: true 352 | }, customParams, function (error, data) { 353 | if (error) { 354 | callback(error, data); 355 | return; 356 | } 357 | 358 | const queueIdRe = /\/queue\/item\/(\d+)/; 359 | const id = +queueIdRe.exec(data.location)[1]; 360 | 361 | data.queueId = id; 362 | 363 | callback(null, data); 364 | }); 365 | }, 366 | 367 | /** 368 | * 369 | * @param {string} jobName 370 | * @param {string} buildNumber 371 | * @param {object|undefined} customParams is optional 372 | * @param {function} callback 373 | */ 374 | stop_build: function (jobName, buildNumber, customParams, callback) { 375 | [jobName, buildNumber, customParams, callback] = doArgs(arguments, ['string', 'string|number', ['object', {}], 'function']); 376 | 377 | doRequest({ 378 | method: 'POST', 379 | urlPattern: [BUILD_STOP, jobName, buildNumber], 380 | noparse: true 381 | }, customParams, function (error, data) { 382 | if (error) { 383 | callback(error, data); 384 | return; 385 | } 386 | 387 | data.body = `Build ${buildNumber} stopped.`; 388 | 389 | callback(null, data); 390 | }); 391 | }, 392 | 393 | /** 394 | * Get the output for a job's build 395 | * 396 | * @param {string} jobName 397 | * @param {string} buildNumber 398 | * @param {object|undefined} customParams is optional 399 | * @param {function} callback 400 | */ 401 | console_output: function (jobName, buildNumber, customParams, callback) { 402 | [jobName, buildNumber, customParams, callback] = doArgs(arguments, ['string', 'string|number', ['object', {}], 'function']); 403 | 404 | doRequest({ 405 | urlPattern: [JOB_OUTPUT, jobName, buildNumber], 406 | noparse: true 407 | }, customParams, callback); 408 | }, 409 | 410 | /** 411 | * Get information for the last build of a job 412 | * 413 | * @param {string} jobName 414 | * @param {object|undefined} customParams is optional 415 | * @param {function} callback 416 | */ 417 | last_build_info: function (jobName, customParams, callback) { 418 | [jobName, customParams, callback] = doArgs(arguments, ['string', ['object', {}], 'function']); 419 | 420 | doRequest({ 421 | urlPattern: [LAST_BUILD, jobName] 422 | }, customParams, callback); 423 | }, 424 | 425 | /** 426 | * Get information for the last completed build of a job 427 | * 428 | * @param {string} jobName 429 | * @param {object|undefined} customParams is optional 430 | * @param {function} callback 431 | */ 432 | last_completed_build_info: function (jobName, customParams, callback) { 433 | [jobName, customParams, callback] = doArgs(arguments, ['string', ['object', {}], 'function']); 434 | 435 | doRequest({ 436 | urlPattern: [LAST_COMPLETED_BUILD, jobName] 437 | }, customParams, callback); 438 | }, 439 | 440 | /** 441 | * Get information for the build number of a job 442 | * 443 | * @param {string} jobName 444 | * @param {string} buildNumber 445 | * @param {object|undefined} customParams is optional 446 | * @param {function} callback 447 | */ 448 | build_info: function (jobName, buildNumber, customParams, callback) { 449 | [jobName, buildNumber, customParams, callback] = doArgs(arguments, ['string', 'string|number', ['object', {}], 'function']); 450 | 451 | doRequest({ 452 | urlPattern: [BUILD_INFO, jobName, buildNumber] 453 | }, customParams, callback); 454 | }, 455 | 456 | /** 457 | * Get information for the all builds 458 | * 459 | * @param {string} jobName 460 | * @param {string} param 461 | * @param {object|undefined} customParams is optional 462 | * @param {function} callback 463 | */ 464 | all_builds: function (jobName, param, customParams, callback) { 465 | [jobName, param, customParams, callback] = doArgs(arguments, ['string', ['string', 'id,timestamp,result,duration'], ['object', {}], 'function']); 466 | 467 | // TODO better name and handle the "param" ??? 468 | doRequest({ 469 | urlPattern: [ALL_BUILDS, jobName, param], 470 | bodyProp: 'allBuilds' 471 | }, customParams, callback); 472 | }, 473 | 474 | /** 475 | * Get the test results for the build number of a job 476 | * 477 | * @param {string} jobName 478 | * @param {string} buildNumber 479 | * @param {object|undefined} customParams is optional 480 | * @param {function} callback 481 | */ 482 | test_result: function (jobName, buildNumber, customParams, callback) { 483 | [jobName, buildNumber, customParams, callback] = doArgs(arguments, ['string', 'string|number', ['object', {}], 'function']); 484 | 485 | doRequest({ 486 | urlPattern: [TEST_REPORT, jobName, buildNumber] 487 | }, customParams, callback); 488 | }, 489 | 490 | /** 491 | * Get the last build report for a job. 492 | * @obsolete Use last_build_info instead. 493 | * Probly will make this to return the test result. 494 | * 495 | * @param {string} jobName 496 | * @param {object|undefined} customParams is optional 497 | * @param {function} callback 498 | */ 499 | last_build_report: function (jobName, customParams, callback) { 500 | this.last_build_info(jobName, customParams, callback); 501 | // doRequest({ 502 | // urlPattern: [LAST_REPORT, jobName], 503 | // }, customParams, callback); 504 | }, 505 | 506 | /** 507 | * Deletes build data for certain job 508 | * 509 | * @param {string} jobName 510 | * @param {string} buildNumber 511 | * @param {object|undefined} customParams is optional 512 | * @param {function} callback 513 | */ 514 | delete_build: function (jobName, buildNumber, customParams, callback) { 515 | [jobName, buildNumber, customParams, callback] = doArgs(arguments, ['string', 'string|number', ['object', {}], 'function']); 516 | 517 | doRequest({ 518 | method: 'POST', 519 | urlPattern: [BUILD_DELETE, jobName, buildNumber], 520 | noparse: true 521 | }, customParams, function (error, data) { 522 | if (error) { 523 | callback(error, data); 524 | } else { 525 | data.body = `Build ${buildNumber} deleted.`; 526 | callback(null, data); 527 | } 528 | }); 529 | }, 530 | 531 | /** ***********************************\ 532 | |* Jobs *| 533 | \*************************************/ 534 | 535 | /** 536 | * Return a list of object literals containing the name and color of all jobs on the Jenkins server 537 | * 538 | * @param {object|undefined} customParams is optional 539 | * @param {function} callback 540 | */ 541 | all_jobs: function (customParams, callback) { 542 | [customParams, callback] = doArgs(arguments, [['object', {}], 'function']); 543 | 544 | doRequest({ 545 | urlPattern: [JOB_LIST], 546 | bodyProp: 'jobs' 547 | }, customParams, callback); 548 | }, 549 | 550 | /** 551 | * Get jobs config in xml 552 | * 553 | * @param {string} jobName 554 | * @param {object|undefined} customParams is optional 555 | * @param {function} callback 556 | */ 557 | get_config_xml: function (jobName, customParams, callback) { 558 | [jobName, customParams, callback] = doArgs(arguments, ['string', ['object', {}], 'function']); 559 | 560 | doRequest({ 561 | urlPattern: [JOB_CONFIG, jobName], 562 | noparse: true 563 | }, customParams, function (error, data) { 564 | // Get only the XML response body 565 | if (error) { 566 | callback(error, data); 567 | } else { 568 | callback(null, data.body); 569 | } 570 | }); 571 | }, 572 | 573 | /** 574 | * Update a job config xml by passing it through your modifyFunction. 575 | * 576 | * @param {string} jobName 577 | * @param {function} modifyFunction 578 | * @param {object|undefined} customParams is optional 579 | * @param {function} callback 580 | */ 581 | update_config: function (jobName, modifyFunction, customParams, callback) { 582 | [jobName, modifyFunction, customParams, callback] = doArgs(arguments, ['string', 'function', ['object', {}], 'function']); 583 | 584 | const self = this; 585 | 586 | self.get_config_xml(jobName, customParams, function (error, data) { 587 | if (error) { 588 | callback(error, data); 589 | return; 590 | } 591 | 592 | // Modify the config data 593 | data = modifyFunction(data); 594 | 595 | self.update_job(jobName, data, customParams, callback); 596 | }); 597 | }, 598 | 599 | /** 600 | * Update a existing job based on a jobConfig xml string 601 | * 602 | * @param {string} jobName 603 | * @param {string} jobConfig 604 | * @param {object|undefined} customParams is optional 605 | * @param {function} callback 606 | */ 607 | update_job: function (jobName, jobConfig, customParams, callback) { 608 | [jobName, jobConfig, customParams, callback] = doArgs(arguments, ['string', 'string', ['object', {}], 'function']); 609 | 610 | doRequest({ 611 | method: 'POST', 612 | urlPattern: [JOB_CONFIG, jobName], 613 | request: { 614 | body: jobConfig, 615 | headers: { 'Content-Type': 'application/xml' } 616 | }, 617 | noparse: true 618 | }, customParams, function (error, data) { 619 | if (error) { 620 | callback(error, data); 621 | return; 622 | } 623 | // TODO rather return job_info ??? 624 | // const data = {name: jobName, location: response.headers['Location'] || response.headers['location']}; 625 | callback(null, { name: jobName }); 626 | }); 627 | }, 628 | 629 | /** 630 | * Get all information for a job 631 | * 632 | * @param {string} jobName 633 | * @param {object|undefined} customParams is optional 634 | * @param {function} callback 635 | */ 636 | job_info: function (jobName, customParams, callback) { 637 | [jobName, customParams, callback] = doArgs(arguments, ['string', ['object', {}], 'function']); 638 | 639 | doRequest({ 640 | urlPattern: [JOB_INFO, jobName] 641 | }, customParams, callback); 642 | }, 643 | 644 | /** 645 | * Create a new job based on a jobConfig string 646 | * 647 | * @param {string} jobName 648 | * @param {string} jobConfig 649 | * @param {object|undefined} customParams is optional 650 | * @param {function} callback 651 | */ 652 | create_job: function (jobName, jobConfig, customParams, callback) { 653 | [jobName, jobConfig, customParams, callback] = doArgs(arguments, ['string', 'string', ['object', {}], 'function']); 654 | 655 | // Set the created job name! 656 | customParams.name = jobName; 657 | 658 | const self = this; 659 | 660 | doRequest({ 661 | method: 'POST', 662 | urlPattern: [JOB_CREATE], 663 | request: { 664 | body: jobConfig, 665 | headers: { 'Content-Type': 'application/xml' } 666 | }, 667 | noparse: true 668 | }, customParams, function (error, data) { 669 | if (error) { 670 | callback(error, data); 671 | return; 672 | } 673 | self.job_info(jobName, customParams, callback); 674 | }); 675 | }, 676 | 677 | /** 678 | * Copies a job and allows you to pass in a function to modify the configuration 679 | * of the job you would like to copy 680 | * 681 | * @param {string} jobName 682 | * @param {string} newJobName 683 | * @param {function} modifyFunction 684 | * @param {object|undefined} customParams is optional 685 | * @param {function} callback 686 | */ 687 | copy_job: function (jobName, newJobName, modifyFunction, customParams, callback) { 688 | [jobName, newJobName, modifyFunction, customParams, callback] = doArgs(arguments, ['string', 'string', 'function', ['object', {}], 'function']); 689 | 690 | const self = this; 691 | 692 | this.get_config_xml(jobName, customParams, function (error, data) { 693 | if (error) { 694 | callback(error, data); 695 | return; 696 | } 697 | 698 | // Modify the data 699 | data = modifyFunction(data); 700 | 701 | self.create_job(newJobName, data, customParams, callback); 702 | }); 703 | }, 704 | 705 | /** 706 | * Deletes a job 707 | * 708 | * @param {string} jobName 709 | * @param {object|undefined} customParams is optional 710 | * @param {function} callback 711 | */ 712 | delete_job: function (jobName, customParams, callback) { 713 | [jobName, customParams, callback] = doArgs(arguments, ['string', ['object', {}], 'function']); 714 | 715 | doRequest({ 716 | method: 'POST', 717 | urlPattern: [JOB_DELETE, jobName], 718 | noparse: true 719 | }, customParams, function (error, data) { 720 | if (error) { 721 | callback(error, data); 722 | return; 723 | } 724 | callback(null, { name: jobName }); 725 | }); 726 | }, 727 | 728 | /** 729 | * Disables a job 730 | * 731 | * @param {string} jobName 732 | * @param {object|undefined} customParams is optional 733 | * @param {function} callback 734 | */ 735 | disable_job: function (jobName, customParams, callback) { 736 | [jobName, customParams, callback] = doArgs(arguments, ['string', ['object', {}], 'function']); 737 | 738 | const self = this; 739 | 740 | doRequest({ 741 | method: 'POST', 742 | urlPattern: [JOB_DISABLE, jobName], 743 | noparse: true 744 | }, customParams, function (error, data) { 745 | if (error) { 746 | callback(error, data); 747 | return; 748 | } 749 | self.job_info(jobName, customParams, callback); 750 | }); 751 | }, 752 | 753 | /** 754 | * Enables a job 755 | * 756 | * @param {string} jobName 757 | * @param {object|undefined} customParams is optional 758 | * @param {function} callback 759 | */ 760 | enable_job: function (jobName, customParams, callback) { 761 | [jobName, customParams, callback] = doArgs(arguments, ['string', ['object', {}], 'function']); 762 | 763 | const self = this; 764 | 765 | doRequest({ 766 | method: 'POST', 767 | urlPattern: [JOB_ENABLE, jobName], 768 | noparse: true 769 | }, customParams, function (error, data) { 770 | if (error) { 771 | callback(error, data); 772 | return; 773 | } 774 | self.job_info(jobName, customParams, callback); 775 | }); 776 | }, 777 | 778 | /** 779 | * Get the last build report for a job 780 | * 781 | * @param {string} jobName 782 | * @param {object|undefined} customParams is optional 783 | * @param {function} callback 784 | */ 785 | last_success: function (jobName, customParams, callback) { 786 | [jobName, customParams, callback] = doArgs(arguments, ['string', ['object', {}], 'function']); 787 | 788 | doRequest({ 789 | method: 'POST', 790 | urlPattern: [LAST_SUCCESS, jobName] 791 | }, customParams, callback); 792 | }, 793 | 794 | /** 795 | * Get the last result for a job 796 | * 797 | * @param {string} jobName 798 | * @param {object|undefined} customParams is optional 799 | * @param {function} callback 800 | */ 801 | last_result: function (jobName, customParams, callback) { 802 | [jobName, customParams, callback] = doArgs(arguments, ['string', ['object', {}], 'function']); 803 | 804 | this.job_info(jobName, function (error, data) { 805 | if (error) { 806 | callback(error, data); 807 | return; 808 | } 809 | 810 | const lastResultUrl = data.lastBuild.url; 811 | 812 | doRequest({ 813 | urlPattern: [lastResultUrl + API, jobName] 814 | }, customParams, callback); 815 | }); 816 | }, 817 | 818 | /** ***********************************\ 819 | |* Queues *| 820 | \*************************************/ 821 | 822 | /** 823 | * Get all queued items 824 | * 825 | * @param {object|undefined} customParams is optional 826 | * @param {function} callback 827 | */ 828 | queue: function (customParams, callback) { 829 | [customParams, callback] = doArgs(arguments, [['object', {}], 'function']); 830 | 831 | doRequest({ 832 | urlPattern: [QUEUE] 833 | }, customParams, callback); 834 | }, 835 | 836 | /** 837 | * Get one queued item 838 | * 839 | * @param {string} queueNumber 840 | * @param {object|undefined} customParams is optional 841 | * @param {function} callback 842 | */ 843 | queue_item: function (queueNumber, customParams, callback) { 844 | [queueNumber, customParams, callback] = doArgs(arguments, ['string|number', ['object', {}], 'function']); 845 | 846 | doRequest({ 847 | urlPattern: [QUEUE_ITEM, queueNumber] 848 | }, customParams, callback); 849 | }, 850 | 851 | /** 852 | * Cancel a queued item 853 | * 854 | * @param {string} itemId 855 | * @param {object|undefined} customParams is optional 856 | * @param {function} callback 857 | */ 858 | cancel_item: function (itemId, customParams, callback) { 859 | [itemId, customParams, callback] = doArgs(arguments, ['string|number', ['object', {}], 'function']); 860 | 861 | customParams.id = itemId; 862 | 863 | doRequest({ 864 | method: 'POST', 865 | urlPattern: [QUEUE_CANCEL_ITEM] 866 | }, customParams, callback); 867 | }, 868 | 869 | /** ***********************************\ 870 | |* Computers *| 871 | \*************************************/ 872 | 873 | /** 874 | * Get info about all jenkins workers including currently executing jobs 875 | * 876 | * @param {object|undefined} customParams is optional 877 | * @param {function} callback 878 | */ 879 | computers: function (customParams, callback) { 880 | [customParams, callback] = doArgs(arguments, [['object', {}], 'function']); 881 | 882 | doRequest({ 883 | urlPattern: [COMPUTERS] 884 | }, customParams, callback); 885 | }, 886 | 887 | /** ***********************************\ 888 | |* Views *| 889 | \*************************************/ 890 | 891 | /** 892 | * Return a list of all the views on the Jenkins server 893 | * 894 | * @param {object|undefined} customParams is optional 895 | * @param {function} callback 896 | */ 897 | all_views: function (customParams, callback) { 898 | [customParams, callback] = doArgs(arguments, [['object', {}], 'function']); 899 | 900 | doRequest({ 901 | urlPattern: [VIEW_LIST], 902 | bodyProp: 'views' 903 | }, customParams, callback); 904 | }, 905 | 906 | /** 907 | * 908 | * @param {string} viewName 909 | * @param {string|undefined} mode 910 | * @param {object|undefined} customParams is optional 911 | * @param {function} callback 912 | */ 913 | create_view: function (viewName, mode, customParams, callback) { 914 | [viewName, mode, customParams, callback] = doArgs(arguments, ['string', ['string', 'hudson.model.ListView'], ['object', {}], 'function']); 915 | 916 | const formData = { name: viewName, mode: mode }; 917 | 918 | formData.json = JSON.stringify(formData); 919 | 920 | const self = this; 921 | 922 | doRequest({ 923 | method: 'POST', 924 | urlPattern: [VIEW_CREATE], 925 | request: { 926 | form: formData 927 | }, 928 | noparse: true 929 | }, customParams, function (error, data) { 930 | if (error) { 931 | callback(error, data); 932 | return; 933 | } 934 | self.view_info(viewName, customParams, callback); 935 | }); 936 | }, 937 | 938 | /** 939 | * @param {string} viewName 940 | * @param {object|undefined} customParams is optional 941 | * @param {function} callback 942 | */ 943 | view_info: function (viewName, customParams, callback) { 944 | [viewName, customParams, callback] = doArgs(arguments, ['string', ['object', {}], 'function']); 945 | 946 | doRequest({ 947 | urlPattern: [VIEW_INFO, viewName] 948 | }, customParams, callback); 949 | }, 950 | 951 | /** 952 | * Update a view based on a viewConfig object 953 | * 954 | * @param {string} viewName 955 | * @param {object} viewConfig 956 | * @param {object|undefined} customParams is optional 957 | * @param {function} callback 958 | */ 959 | update_view: function (viewName, viewConfig, customParams, callback) { 960 | [viewName, viewConfig, customParams, callback] = doArgs(arguments, ['string', 'object', ['object', {}], 'function']); 961 | 962 | viewConfig.json = JSON.stringify(viewConfig); 963 | 964 | const self = this; 965 | 966 | doRequest({ 967 | method: 'POST', 968 | urlPattern: [VIEW_CONFIG, viewName], 969 | request: { 970 | form: viewConfig 971 | // headers: {'content-type': 'application/x-www-form-urlencoded'}, 972 | }, 973 | noparse: true 974 | }, customParams, function (error, data) { 975 | if (error) { 976 | callback(error, data); 977 | return; 978 | } 979 | self.view_info(viewName, customParams, callback); 980 | }); 981 | }, 982 | 983 | /** 984 | * @param {string} viewName 985 | * @param {object|undefined} customParams is optional 986 | * @param {function} callback 987 | */ 988 | delete_view: function (viewName, customParams, callback) { 989 | [viewName, customParams, callback] = doArgs(arguments, ['string', ['object', {}], 'function']); 990 | 991 | doRequest({ 992 | method: 'POST', 993 | urlPattern: [VIEW_DELETE, viewName], 994 | noparse: true 995 | }, customParams, function (error, data) { 996 | if (error) { 997 | callback(error, data); 998 | return; 999 | } 1000 | callback(null, { name: viewName }); 1001 | }); 1002 | }, 1003 | 1004 | /** 1005 | * @param {string} viewName 1006 | * @param {string} jobName 1007 | * @param {object|undefined} customParams is optional 1008 | * @param {function} callback 1009 | */ 1010 | add_job_to_view: function (viewName, jobName, customParams, callback) { 1011 | [viewName, jobName, customParams, callback] = doArgs(arguments, ['string', 'string', ['object', {}], 'function']); 1012 | 1013 | customParams.name = jobName; 1014 | 1015 | doRequest({ 1016 | method: 'POST', 1017 | urlPattern: [VIEW_ADD_JOB, viewName], 1018 | noparse: true 1019 | }, customParams, callback); 1020 | }, 1021 | 1022 | /** 1023 | * @param {string} viewName 1024 | * @param {string} jobName 1025 | * @param {object|undefined} customParams is optional 1026 | * @param {function} callback 1027 | */ 1028 | remove_job_from_view: function (viewName, jobName, customParams, callback) { 1029 | [viewName, jobName, customParams, callback] = doArgs(arguments, ['string', 'string', ['object', {}], 'function']); 1030 | 1031 | customParams.name = jobName; 1032 | 1033 | doRequest({ 1034 | method: 'POST', 1035 | urlPattern: [VIEW_REMOVE_JOB, viewName], 1036 | noparse: true 1037 | }, customParams, callback); 1038 | }, 1039 | 1040 | /** 1041 | * Return a list of objet literals containing the name and color of all the jobs for a view on the Jenkins server 1042 | * 1043 | * @param {string} viewName 1044 | * @param {object|undefined} customParams is optional 1045 | * @param {function} callback 1046 | */ 1047 | all_jobs_in_view: function (viewName, customParams, callback) { 1048 | [viewName, customParams, callback] = doArgs(arguments, ['string', ['object', {}], 'function']); 1049 | 1050 | doRequest({ 1051 | urlPattern: [VIEW_INFO, viewName], 1052 | bodyProp: 'jobs' 1053 | }, customParams, callback); 1054 | }, 1055 | 1056 | /** ***********************************\ 1057 | |* Plugins *| 1058 | \*************************************/ 1059 | 1060 | /** 1061 | * Get all installed plugins 1062 | * 1063 | * @param {object|undefined} customParams is optional 1064 | * @param {function} callback 1065 | */ 1066 | all_installed_plugins: function (customParams, callback) { 1067 | [customParams, callback] = doArgs(arguments, [['object', {}], 'function']); 1068 | 1069 | customParams.depth = 1; 1070 | 1071 | doRequest({ 1072 | urlPattern: [PLUGINS], 1073 | failureStatusCodes: [HTTP_CODE_302], 1074 | noparse: true, 1075 | bodyProp: 'plugins' 1076 | }, customParams, callback); 1077 | }, 1078 | 1079 | /** 1080 | * Install a plugin 1081 | * 1082 | * @param {string} pluginName 1083 | * @param {object|undefined} customParams is optional 1084 | * @param {function} callback 1085 | */ 1086 | install_plugin: function (pluginName, customParams, callback) { 1087 | [pluginName, customParams, callback] = doArgs(arguments, ['string', ['object', {}], 'function']); 1088 | 1089 | const body = ``; 1090 | 1091 | doRequest({ 1092 | method: 'POST', 1093 | urlPattern: [INSTALL_PLUGIN], 1094 | request: { 1095 | body: body, 1096 | headers: { 'Content-Type': 'text/xml' } 1097 | }, 1098 | noparse: true, 1099 | bodyProp: 'plugins' 1100 | }, customParams, callback); 1101 | }, 1102 | 1103 | /** 1104 | * Create a new folder with given name 1105 | * 1106 | * Requires Folder plugin in Jenkins: 1107 | * @see https://wiki.jenkins-ci.org/display/JENKINS/CloudBees+Folders+Plugin 1108 | * @see https://gist.github.com/stuart-warren/7786892 1109 | * 1110 | * @param {string} folderName 1111 | * @param {object|undefined} customParams is optional 1112 | * @param {function} callback 1113 | */ 1114 | create_folder: function (folderName, customParams, callback) { 1115 | [folderName, customParams, callback] = doArgs(arguments, ['string', ['object', {}], 'function']); 1116 | 1117 | const mode = 'com.cloudbees.hudson.plugins.folder.Folder'; 1118 | 1119 | customParams.name = folderName; 1120 | customParams.mode = mode; 1121 | customParams.Submit = 'OK'; 1122 | 1123 | doRequest({ 1124 | method: 'POST', 1125 | urlPattern: [NEWFOLDER] 1126 | }, customParams, callback); 1127 | } 1128 | }; 1129 | }; 1130 | -------------------------------------------------------------------------------- /lib/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); 4 | 5 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; 6 | 7 | var util = require('util'); 8 | var qs = require('querystring'); 9 | var request = require('request'); 10 | 11 | var API = '/api/json'; 12 | var LIST = API; 13 | var CREATE = '/createItem' + API; 14 | 15 | var BUILD_START = '/job/%s/build' + API; 16 | var BUILD_START_WITHPARAMS = '/job/%s/buildWithParameters'; // TODO how to handle this? 17 | var BUILD_STOP = '/job/%s/%s/stop'; 18 | var BUILD_INFO = '/job/%s/%s' + API; 19 | var BUILD_DELETE = '/job/%s/%s/doDelete'; 20 | 21 | var ALL_BUILDS = '/job/%s' + API + '?tree=allBuilds[%s]'; 22 | var LAST_SUCCESS = '/job/%s/lastSuccessfulBuild' + API; 23 | var TEST_REPORT = '/job/%s/%s/testReport' + API; 24 | var LAST_BUILD = '/job/%s/lastBuild' + API; 25 | var LAST_COMPLETED_BUILD = '/job/%s/lastCompletedBuild' + API; 26 | // const LAST_REPORT = '/job/%s/lastBuild' + API; //TODO is there url for lastTestReport? 27 | 28 | var COMPUTERS = '/computer' + API; 29 | 30 | var VIEW_LIST = LIST; 31 | var VIEW_INFO = '/view/%s' + API; 32 | var VIEW_CREATE = '/createView' + API; 33 | var VIEW_CONFIG = '/view/%s/configSubmit'; // NOTE form encoded not called via /api/json, TODO fix 34 | var VIEW_DELETE = '/view/%s/doDelete' + API; 35 | var VIEW_ADD_JOB = '/view/%s/addJobToView' + API; 36 | var VIEW_REMOVE_JOB = '/view/%s/removeJobFromView' + API; 37 | 38 | var JOB_LIST = LIST; 39 | var JOB_CREATE = CREATE; 40 | var JOB_INFO = '/job/%s' + API; 41 | var JOB_CONFIG = '/job/%s/config.xml' + API; 42 | var JOB_OUTPUT = '/job/%s/%s/consoleText' + API; 43 | var JOB_DELETE = '/job/%s/doDelete' + API; 44 | var JOB_DISABLE = '/job/%s/disable'; 45 | var JOB_ENABLE = '/job/%s/enable'; 46 | 47 | var QUEUE = '/queue' + API; 48 | var QUEUE_ITEM = '/queue/item/%s' + API; 49 | var QUEUE_CANCEL_ITEM = '/queue/cancelItem' + API; // TODO verify this works with API 50 | 51 | var PLUGINS = '/pluginManager' + API; 52 | var INSTALL_PLUGIN = '/pluginManager/installNecessaryPlugins' + API; 53 | 54 | var NEWFOLDER = CREATE; 55 | 56 | var HTTP_CODE_200 = 200; 57 | var HTTP_CODE_201 = 201; 58 | var HTTP_CODE_302 = 302; 59 | 60 | // ----------------------------------------------------------------------------- 61 | // Helper Functions 62 | 63 | /** 64 | * @param {any} value - variable to detect type of. 65 | * @return {string} - typeof the value param, but 'array' for arrays and 'null' for nulls. 66 | */ 67 | function getType(value) { 68 | if (Array.isArray(value)) { 69 | return 'array'; 70 | } else if (value === null) { 71 | return 'null'; 72 | } 73 | return typeof value === 'undefined' ? 'undefined' : _typeof(value); 74 | } 75 | 76 | /** 77 | * Processes arguments according to the rules defined by the types array. 78 | * Types of arguments are checked, optional arguments are replaced by their 79 | * default values or left undefined. 80 | * 81 | * @param {arguments} values: The arguments object. 82 | * @param {array} types: Array of types, see below. 83 | * @returns {array} the processed arguments in an array. 84 | * @throws {Error} if arguments don't fit, throws an error. 85 | * 86 | * Examples of types: 87 | * ['string'] - function requires one string 88 | * ['string', ['number']] - function requires one string and expects an optional number 89 | * [['object', {}]] - function expects an optional object defaulting to empty object 90 | * ['string|array'] - function requires string or array 91 | * 92 | * Inspired by 93 | * See: https://www.npmjs.com/package/assert-args 94 | * See: https://www.npmjs.com/package/ensurethat 95 | */ 96 | function doArgs(values, types) { 97 | var value = void 0, 98 | type = void 0, 99 | carry = void 0, 100 | optional = void 0, 101 | defaultValue = void 0; 102 | var result = []; 103 | 104 | var _iteratorNormalCompletion = true; 105 | var _didIteratorError = false; 106 | var _iteratorError = undefined; 107 | 108 | try { 109 | for (var _iterator = types[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { 110 | type = _step.value; 111 | 112 | // Load value if we don't have one already 113 | if (!carry) { 114 | value = Array.prototype.shift.call(values); 115 | } 116 | 117 | // Handle optional type 118 | if (Array.isArray(type)) { 119 | optional = true; 120 | defaultValue = type[1]; 121 | type = type[0]; 122 | } else { 123 | optional = false; 124 | } 125 | 126 | // Handle multitype 127 | type = type.split('|'); 128 | 129 | // Is it a correct value? 130 | if (type.includes(getType(value))) { 131 | result.push(value); 132 | carry = false; 133 | } else if (optional) { 134 | result.push(defaultValue); 135 | carry = true; 136 | } else { 137 | throw Error('Invalid arguments'); 138 | } 139 | } 140 | } catch (err) { 141 | _didIteratorError = true; 142 | _iteratorError = err; 143 | } finally { 144 | try { 145 | if (!_iteratorNormalCompletion && _iterator.return) { 146 | _iterator.return(); 147 | } 148 | } finally { 149 | if (_didIteratorError) { 150 | throw _iteratorError; 151 | } 152 | } 153 | } 154 | 155 | if (values.length) { 156 | // TODO maybe do some warning..? 157 | // throw Error('Extra arguments ' + values.length, values); 158 | } 159 | return result; 160 | } 161 | 162 | /** 163 | * Parse the passed string as JSON and extract or "manipulate" it using the property. 164 | * 165 | * @param {string} body - string response body 166 | * @param {string|array|function} property - property to get from body or modificator function 167 | * @param {function} callback function to call when all done 168 | */ 169 | function tryParseJson(body, property, callback) { 170 | var _doArgs = doArgs(arguments, ['string', 'string|array|function|null', 'function']); 171 | 172 | var _doArgs2 = _slicedToArray(_doArgs, 3); 173 | 174 | body = _doArgs2[0]; 175 | property = _doArgs2[1]; 176 | callback = _doArgs2[2]; 177 | 178 | 179 | try { 180 | // Won't harm if we replace escape sequence 181 | body = body.replace(/\x1b/g, ''); 182 | 183 | // Try to parse 184 | var data = JSON.parse(body.toString()); 185 | 186 | // Get the prop name if specified 187 | if (property) { 188 | var type = getType(property); 189 | 190 | if (type === 'array') { 191 | var newData = {}; 192 | 193 | var _iteratorNormalCompletion2 = true; 194 | var _didIteratorError2 = false; 195 | var _iteratorError2 = undefined; 196 | 197 | try { 198 | for (var _iterator2 = property[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { 199 | var p = _step2.value; 200 | 201 | newData[p] = data[p]; 202 | } 203 | } catch (err) { 204 | _didIteratorError2 = true; 205 | _iteratorError2 = err; 206 | } finally { 207 | try { 208 | if (!_iteratorNormalCompletion2 && _iterator2.return) { 209 | _iterator2.return(); 210 | } 211 | } finally { 212 | if (_didIteratorError2) { 213 | throw _iteratorError2; 214 | } 215 | } 216 | } 217 | 218 | data = newData; 219 | } 220 | if (type === 'string') { 221 | data = data[property]; 222 | } 223 | if (type === 'function') { 224 | data = property(data); 225 | } 226 | } 227 | 228 | callback(null, data); 229 | } catch (e) { 230 | callback(e, body); 231 | } 232 | } 233 | 234 | // ----------------------------------------------------------------------------- 235 | 236 | exports.init = function (host, defaultOptions, defaultParams) { 237 | 238 | /** 239 | * Builds the URL to Jenkins from formatstring pattern and params. 240 | * 241 | * @param {string} pattern - URL format string patern. 242 | * @param {string|number} arguments for the formatstring pattern. 243 | * @returns {string} url - the URL formated according to arguments. 244 | */ 245 | function formatUrl() { 246 | return host + util.format.apply(null, arguments); 247 | } 248 | 249 | /** 250 | * Build REST params and correctly append them to URL. 251 | * 252 | * @param {string} url to be extended with params. 253 | * @param {object} specificParams key/value pair of params. 254 | * @returns {string} the extended url. 255 | */ 256 | var appendParams = function appendParams(url, specificParams) { 257 | // Assign default and specific parameters 258 | var params = Object.assign({}, defaultParams, specificParams); 259 | 260 | // Stringify the querystring params 261 | var paramsString = qs.stringify(params); 262 | 263 | // Empty params 264 | if (paramsString === '') { 265 | return url; 266 | } 267 | 268 | // Does url contain parameters already? 269 | var delim = url.includes('?') ? '&' : '?'; 270 | 271 | return url + delim + paramsString; 272 | }; 273 | 274 | /** 275 | * Just helper funckion to build the request URL. 276 | * 277 | * @param {array} urlPattern array in format of [urlFormat, arg1, arg2] used to build the URL. 278 | * @param {object} customParams key/value pair of params. 279 | * @returns {string} the URL built. 280 | */ 281 | function buildUrl(urlPattern, customParams) { 282 | var url = formatUrl.apply(null, urlPattern); 283 | 284 | url = appendParams(url, customParams); 285 | return url; 286 | } 287 | 288 | /** 289 | * Run the actual HTTP request. 290 | * 291 | * @param {object} specificOptions - options object overriding the default options below. 292 | * @param {object} customParams - custom url params to be added to url. 293 | * @param {function} callback - the callback function to be called when request is finished. 294 | */ 295 | var doRequest = function doRequest(specificOptions, customParams, callback) { 296 | 297 | // Options - Default values 298 | var options = Object.assign({}, { 299 | urlPattern: ['/'], 300 | method: 'GET', 301 | successStatusCodes: [HTTP_CODE_200], 302 | failureStatusCodes: [], 303 | bodyProp: null, 304 | noparse: false, 305 | request: {} 306 | }, defaultOptions, specificOptions); 307 | 308 | // Create the url 309 | var url = buildUrl(options.urlPattern, customParams); 310 | 311 | // Build the actual request options 312 | var requestOptions = Object.assign({ 313 | method: options.method, 314 | url: url, 315 | headers: [], 316 | followAllRedirects: true, 317 | form: null, 318 | body: null 319 | }, options.request); 320 | 321 | // Do the request 322 | request(requestOptions, function (error, response, body) { 323 | if (error) { 324 | callback(error, response); 325 | return; 326 | } 327 | 328 | if (Array.isArray(options.successStatusCodes) && !options.successStatusCodes.includes(response.statusCode) || Array.isArray(options.failureStatusCodes) && options.failureStatusCodes.includes(response.statusCode)) { 329 | callback('Server returned unexpected status code: ' + response.statusCode, response); 330 | return; 331 | } 332 | 333 | if (options.noparse) { 334 | // Wrap body in the response object 335 | if (typeof body === 'string') { 336 | body = { body: body }; 337 | } 338 | 339 | // Add location 340 | var location = response.headers.Location || response.headers.location; 341 | 342 | if (location) { 343 | body.location = location; 344 | } 345 | 346 | // Add status code 347 | body.statusCode = response.statusCode; 348 | 349 | callback(null, body); 350 | } else { 351 | tryParseJson(body, options.bodyProp, callback); 352 | } 353 | }); 354 | }; 355 | 356 | return { 357 | 358 | /** ***********************************\ 359 | |* Builds *| 360 | \*************************************/ 361 | 362 | /** Trigger Jenkins to build. 363 | * 364 | * Return queue location of newly-created job as per 365 | * https://issues.jenkins-ci.org/browse/JENKINS-12827?focusedCommentId=201381#comment-201381 366 | */ 367 | build: function build(jobName, customParams, callback) { 368 | var _doArgs3 = doArgs(arguments, ['string', ['object', {}], 'function']); 369 | 370 | var _doArgs4 = _slicedToArray(_doArgs3, 3); 371 | 372 | jobName = _doArgs4[0]; 373 | customParams = _doArgs4[1]; 374 | callback = _doArgs4[2]; 375 | 376 | 377 | doRequest({ 378 | method: 'POST', 379 | urlPattern: [BUILD_START, jobName], 380 | successStatusCodes: [HTTP_CODE_201, HTTP_CODE_302], 381 | noparse: true 382 | }, customParams, function (error, data) { 383 | if (error) { 384 | callback(error, data); 385 | return; 386 | } 387 | 388 | var queueIdRe = /\/queue\/item\/(\d+)/; 389 | var id = +queueIdRe.exec(data.location)[1]; 390 | 391 | data.queueId = id; 392 | 393 | callback(null, data); 394 | }); 395 | }, 396 | 397 | /** */ 398 | build_with_params: function build_with_params(jobName, customParams, callback) { 399 | var _doArgs5 = doArgs(arguments, ['string', ['object', {}], 'function']); 400 | 401 | var _doArgs6 = _slicedToArray(_doArgs5, 3); 402 | 403 | jobName = _doArgs6[0]; 404 | customParams = _doArgs6[1]; 405 | callback = _doArgs6[2]; 406 | 407 | 408 | doRequest({ 409 | method: 'POST', 410 | urlPattern: [BUILD_START_WITHPARAMS, jobName], 411 | successStatusCodes: [HTTP_CODE_201, HTTP_CODE_302], 412 | noparse: true 413 | }, customParams, function (error, data) { 414 | if (error) { 415 | callback(error, data); 416 | return; 417 | } 418 | 419 | var queueIdRe = /\/queue\/item\/(\d+)/; 420 | var id = +queueIdRe.exec(data.location)[1]; 421 | 422 | data.queueId = id; 423 | 424 | callback(null, data); 425 | }); 426 | }, 427 | 428 | /** */ 429 | stop_build: function stop_build(jobName, buildNumber, customParams, callback) { 430 | var _doArgs7 = doArgs(arguments, ['string', 'string|number', ['object', {}], 'function']); 431 | 432 | var _doArgs8 = _slicedToArray(_doArgs7, 4); 433 | 434 | jobName = _doArgs8[0]; 435 | buildNumber = _doArgs8[1]; 436 | customParams = _doArgs8[2]; 437 | callback = _doArgs8[3]; 438 | 439 | 440 | doRequest({ 441 | method: 'POST', 442 | urlPattern: [BUILD_STOP, jobName, buildNumber], 443 | noparse: true 444 | }, customParams, function (error, data) { 445 | if (error) { 446 | callback(error, data); 447 | return; 448 | } 449 | 450 | data.body = 'Build ' + buildNumber + ' stopped.'; 451 | 452 | callback(null, data); 453 | }); 454 | }, 455 | 456 | /** Get the output for a job's build */ 457 | console_output: function console_output(jobName, buildNumber, customParams, callback) { 458 | var _doArgs9 = doArgs(arguments, ['string', 'string|number', ['object', {}], 'function']); 459 | 460 | var _doArgs10 = _slicedToArray(_doArgs9, 4); 461 | 462 | jobName = _doArgs10[0]; 463 | buildNumber = _doArgs10[1]; 464 | customParams = _doArgs10[2]; 465 | callback = _doArgs10[3]; 466 | 467 | 468 | doRequest({ 469 | urlPattern: [JOB_OUTPUT, jobName, buildNumber], 470 | noparse: true 471 | }, customParams, callback); 472 | }, 473 | 474 | /** Get information for the last build of a job */ 475 | last_build_info: function last_build_info(jobName, customParams, callback) { 476 | var _doArgs11 = doArgs(arguments, ['string', ['object', {}], 'function']); 477 | 478 | var _doArgs12 = _slicedToArray(_doArgs11, 3); 479 | 480 | jobName = _doArgs12[0]; 481 | customParams = _doArgs12[1]; 482 | callback = _doArgs12[2]; 483 | 484 | 485 | doRequest({ 486 | urlPattern: [LAST_BUILD, jobName] 487 | }, customParams, callback); 488 | }, 489 | 490 | /** Get information for the last completed build of a job */ 491 | last_completed_build_info: function last_completed_build_info(jobName, customParams, callback) { 492 | var _doArgs13 = doArgs(arguments, ['string', ['object', {}], 'function']); 493 | 494 | var _doArgs14 = _slicedToArray(_doArgs13, 3); 495 | 496 | jobName = _doArgs14[0]; 497 | customParams = _doArgs14[1]; 498 | callback = _doArgs14[2]; 499 | 500 | 501 | doRequest({ 502 | urlPattern: [LAST_COMPLETED_BUILD, jobName] 503 | }, customParams, callback); 504 | }, 505 | 506 | /** Get information for the build number of a job */ 507 | build_info: function build_info(jobName, buildNumber, customParams, callback) { 508 | var _doArgs15 = doArgs(arguments, ['string', 'string|number', ['object', {}], 'function']); 509 | 510 | var _doArgs16 = _slicedToArray(_doArgs15, 4); 511 | 512 | jobName = _doArgs16[0]; 513 | buildNumber = _doArgs16[1]; 514 | customParams = _doArgs16[2]; 515 | callback = _doArgs16[3]; 516 | 517 | 518 | doRequest({ 519 | urlPattern: [BUILD_INFO, jobName, buildNumber] 520 | }, customParams, callback); 521 | }, 522 | 523 | /** Get information for the all builds */ 524 | all_builds: function all_builds(jobName, param, customParams, callback) { 525 | 526 | // TODO better name and handle the "param" ??? 527 | var _doArgs17 = doArgs(arguments, ['string', ['string', 'id,timestamp,result,duration'], ['object', {}], 'function']); 528 | 529 | var _doArgs18 = _slicedToArray(_doArgs17, 4); 530 | 531 | jobName = _doArgs18[0]; 532 | param = _doArgs18[1]; 533 | customParams = _doArgs18[2]; 534 | callback = _doArgs18[3]; 535 | doRequest({ 536 | urlPattern: [ALL_BUILDS, jobName, param], 537 | bodyProp: 'allBuilds' 538 | }, customParams, callback); 539 | }, 540 | 541 | /** Get the test results for the build number of a job */ 542 | test_result: function test_result(jobName, buildNumber, customParams, callback) { 543 | var _doArgs19 = doArgs(arguments, ['string', 'string|number', ['object', {}], 'function']); 544 | 545 | var _doArgs20 = _slicedToArray(_doArgs19, 4); 546 | 547 | jobName = _doArgs20[0]; 548 | buildNumber = _doArgs20[1]; 549 | customParams = _doArgs20[2]; 550 | callback = _doArgs20[3]; 551 | 552 | 553 | doRequest({ 554 | urlPattern: [TEST_REPORT, jobName, buildNumber] 555 | }, customParams, callback); 556 | }, 557 | 558 | /** Get the last build report for a job. 559 | * @obsolete Use last_build_info instead. 560 | * Probly will make this to return the test result. */ 561 | last_build_report: function last_build_report(jobName, customParams, callback) { 562 | this.last_build_info(jobName, customParams, callback); 563 | // doRequest({ 564 | // urlPattern: [LAST_REPORT, jobName], 565 | // }, customParams, callback); 566 | }, 567 | 568 | /** Deletes build data for certain job */ 569 | delete_build: function delete_build(jobName, buildNumber, customParams, callback) { 570 | var _doArgs21 = doArgs(arguments, ['string', 'string|number', ['object', {}], 'function']); 571 | 572 | var _doArgs22 = _slicedToArray(_doArgs21, 4); 573 | 574 | jobName = _doArgs22[0]; 575 | buildNumber = _doArgs22[1]; 576 | customParams = _doArgs22[2]; 577 | callback = _doArgs22[3]; 578 | 579 | 580 | doRequest({ 581 | method: 'POST', 582 | urlPattern: [BUILD_DELETE, jobName, buildNumber], 583 | noparse: true 584 | }, customParams, function (error, data) { 585 | if (error) { 586 | callback(error, data); 587 | } else { 588 | data.body = 'Build ' + buildNumber + ' deleted.'; 589 | callback(null, data); 590 | } 591 | }); 592 | }, 593 | 594 | /** ***********************************\ 595 | |* Jobs *| 596 | \*************************************/ 597 | 598 | /** Return a list of object literals containing the name and color of all jobs on the Jenkins server */ 599 | all_jobs: function all_jobs(customParams, callback) { 600 | var _doArgs23 = doArgs(arguments, [['object', {}], 'function']); 601 | 602 | var _doArgs24 = _slicedToArray(_doArgs23, 2); 603 | 604 | customParams = _doArgs24[0]; 605 | callback = _doArgs24[1]; 606 | 607 | 608 | doRequest({ 609 | urlPattern: [JOB_LIST], 610 | bodyProp: 'jobs' 611 | }, customParams, callback); 612 | }, 613 | 614 | /** Get jobs config in xml */ 615 | get_config_xml: function get_config_xml(jobName, customParams, callback) { 616 | var _doArgs25 = doArgs(arguments, ['string', ['object', {}], 'function']); 617 | 618 | var _doArgs26 = _slicedToArray(_doArgs25, 3); 619 | 620 | jobName = _doArgs26[0]; 621 | customParams = _doArgs26[1]; 622 | callback = _doArgs26[2]; 623 | 624 | 625 | doRequest({ 626 | urlPattern: [JOB_CONFIG, jobName], 627 | noparse: true 628 | }, customParams, function (error, data) { 629 | // Get only the XML response body 630 | if (error) { 631 | callback(error, data); 632 | } else { 633 | callback(null, data.body); 634 | } 635 | }); 636 | }, 637 | 638 | /** Update a job config xml by passing it through your modifyFunction. */ 639 | update_config: function update_config(jobName, modifyFunction, customParams, callback) { 640 | var _doArgs27 = doArgs(arguments, ['string', 'function', ['object', {}], 'function']); 641 | 642 | var _doArgs28 = _slicedToArray(_doArgs27, 4); 643 | 644 | jobName = _doArgs28[0]; 645 | modifyFunction = _doArgs28[1]; 646 | customParams = _doArgs28[2]; 647 | callback = _doArgs28[3]; 648 | 649 | 650 | var self = this; 651 | 652 | self.get_config_xml(jobName, customParams, function (error, data) { 653 | if (error) { 654 | callback(error, data); 655 | return; 656 | } 657 | 658 | // Modify the config data 659 | data = modifyFunction(data); 660 | 661 | self.update_job(jobName, data, customParams, callback); 662 | }); 663 | }, 664 | 665 | /** Update a existing job based on a jobConfig xml string */ 666 | update_job: function update_job(jobName, jobConfig, customParams, callback) { 667 | var _doArgs29 = doArgs(arguments, ['string', 'string', ['object', {}], 'function']); 668 | 669 | var _doArgs30 = _slicedToArray(_doArgs29, 4); 670 | 671 | jobName = _doArgs30[0]; 672 | jobConfig = _doArgs30[1]; 673 | customParams = _doArgs30[2]; 674 | callback = _doArgs30[3]; 675 | 676 | 677 | doRequest({ 678 | method: 'POST', 679 | urlPattern: [JOB_CONFIG, jobName], 680 | request: { 681 | body: jobConfig, 682 | headers: { 'Content-Type': 'application/xml' } 683 | }, 684 | noparse: true 685 | }, customParams, function (error, data) { 686 | if (error) { 687 | callback(error, data); 688 | return; 689 | } 690 | // TODO rather return job_info ??? 691 | // const data = {name: jobName, location: response.headers['Location'] || response.headers['location']}; 692 | callback(null, { name: jobName }); 693 | }); 694 | }, 695 | 696 | /** Get all information for a job */ 697 | job_info: function job_info(jobName, customParams, callback) { 698 | var _doArgs31 = doArgs(arguments, ['string', ['object', {}], 'function']); 699 | 700 | var _doArgs32 = _slicedToArray(_doArgs31, 3); 701 | 702 | jobName = _doArgs32[0]; 703 | customParams = _doArgs32[1]; 704 | callback = _doArgs32[2]; 705 | 706 | 707 | doRequest({ 708 | urlPattern: [JOB_INFO, jobName] 709 | }, customParams, callback); 710 | }, 711 | 712 | /** Create a new job based on a jobConfig string */ 713 | create_job: function create_job(jobName, jobConfig, customParams, callback) { 714 | 715 | // Set the created job name! 716 | var _doArgs33 = doArgs(arguments, ['string', 'string', ['object', {}], 'function']); 717 | 718 | var _doArgs34 = _slicedToArray(_doArgs33, 4); 719 | 720 | jobName = _doArgs34[0]; 721 | jobConfig = _doArgs34[1]; 722 | customParams = _doArgs34[2]; 723 | callback = _doArgs34[3]; 724 | customParams.name = jobName; 725 | 726 | var self = this; 727 | 728 | doRequest({ 729 | method: 'POST', 730 | urlPattern: [JOB_CREATE], 731 | request: { 732 | body: jobConfig, 733 | headers: { 'Content-Type': 'application/xml' } 734 | }, 735 | noparse: true 736 | }, customParams, function (error, data) { 737 | if (error) { 738 | callback(error, data); 739 | return; 740 | } 741 | self.job_info(jobName, customParams, callback); 742 | }); 743 | }, 744 | 745 | /** Copies a job and allows you to pass in a function to modify the configuration of the job you would like to copy */ 746 | copy_job: function copy_job(jobName, newJobName, modifyFunction, customParams, callback) { 747 | var _doArgs35 = doArgs(arguments, ['string', 'string', 'function', ['object', {}], 'function']); 748 | 749 | var _doArgs36 = _slicedToArray(_doArgs35, 5); 750 | 751 | jobName = _doArgs36[0]; 752 | newJobName = _doArgs36[1]; 753 | modifyFunction = _doArgs36[2]; 754 | customParams = _doArgs36[3]; 755 | callback = _doArgs36[4]; 756 | 757 | 758 | var self = this; 759 | 760 | this.get_config_xml(jobName, customParams, function (error, data) { 761 | if (error) { 762 | callback(error, data); 763 | return; 764 | } 765 | 766 | // Modify the data 767 | data = modifyFunction(data); 768 | 769 | self.create_job(newJobName, data, customParams, callback); 770 | }); 771 | }, 772 | 773 | /** Deletes a job */ 774 | delete_job: function delete_job(jobName, customParams, callback) { 775 | var _doArgs37 = doArgs(arguments, ['string', ['object', {}], 'function']); 776 | 777 | var _doArgs38 = _slicedToArray(_doArgs37, 3); 778 | 779 | jobName = _doArgs38[0]; 780 | customParams = _doArgs38[1]; 781 | callback = _doArgs38[2]; 782 | 783 | 784 | doRequest({ 785 | method: 'POST', 786 | urlPattern: [JOB_DELETE, jobName], 787 | noparse: true 788 | }, customParams, function (error, data) { 789 | if (error) { 790 | callback(error, data); 791 | return; 792 | } 793 | callback(null, { name: jobName }); 794 | }); 795 | }, 796 | 797 | /** Disables a job */ 798 | disable_job: function disable_job(jobName, customParams, callback) { 799 | var _doArgs39 = doArgs(arguments, ['string', ['object', {}], 'function']); 800 | 801 | var _doArgs40 = _slicedToArray(_doArgs39, 3); 802 | 803 | jobName = _doArgs40[0]; 804 | customParams = _doArgs40[1]; 805 | callback = _doArgs40[2]; 806 | 807 | 808 | var self = this; 809 | 810 | doRequest({ 811 | method: 'POST', 812 | urlPattern: [JOB_DISABLE, jobName], 813 | noparse: true 814 | }, customParams, function (error, data) { 815 | if (error) { 816 | callback(error, data); 817 | return; 818 | } 819 | self.job_info(jobName, customParams, callback); 820 | }); 821 | }, 822 | 823 | /** Enables a job */ 824 | enable_job: function enable_job(jobName, customParams, callback) { 825 | var _doArgs41 = doArgs(arguments, ['string', ['object', {}], 'function']); 826 | 827 | var _doArgs42 = _slicedToArray(_doArgs41, 3); 828 | 829 | jobName = _doArgs42[0]; 830 | customParams = _doArgs42[1]; 831 | callback = _doArgs42[2]; 832 | 833 | 834 | var self = this; 835 | 836 | doRequest({ 837 | method: 'POST', 838 | urlPattern: [JOB_ENABLE, jobName], 839 | noparse: true 840 | }, customParams, function (error, data) { 841 | if (error) { 842 | callback(error, data); 843 | return; 844 | } 845 | self.job_info(jobName, customParams, callback); 846 | }); 847 | }, 848 | 849 | /** Get the last build report for a job */ 850 | last_success: function last_success(jobName, customParams, callback) { 851 | var _doArgs43 = doArgs(arguments, ['string', ['object', {}], 'function']); 852 | 853 | var _doArgs44 = _slicedToArray(_doArgs43, 3); 854 | 855 | jobName = _doArgs44[0]; 856 | customParams = _doArgs44[1]; 857 | callback = _doArgs44[2]; 858 | 859 | 860 | doRequest({ 861 | method: 'POST', 862 | urlPattern: [LAST_SUCCESS, jobName] 863 | }, customParams, callback); 864 | }, 865 | 866 | /** Get the last result for a job */ 867 | last_result: function last_result(jobName, customParams, callback) { 868 | var _doArgs45 = doArgs(arguments, ['string', ['object', {}], 'function']); 869 | 870 | var _doArgs46 = _slicedToArray(_doArgs45, 3); 871 | 872 | jobName = _doArgs46[0]; 873 | customParams = _doArgs46[1]; 874 | callback = _doArgs46[2]; 875 | 876 | 877 | this.job_info(jobName, function (error, data) { 878 | if (error) { 879 | callback(error, data); 880 | return; 881 | } 882 | 883 | var lastResultUrl = data.lastBuild.url; 884 | 885 | doRequest({ 886 | urlPattern: [lastResultUrl + API, jobName] 887 | }, customParams, callback); 888 | }); 889 | }, 890 | 891 | /** ***********************************\ 892 | |* Queues *| 893 | \*************************************/ 894 | 895 | /** Get all queued items */ 896 | queue: function queue(customParams, callback) { 897 | var _doArgs47 = doArgs(arguments, [['object', {}], 'function']); 898 | 899 | var _doArgs48 = _slicedToArray(_doArgs47, 2); 900 | 901 | customParams = _doArgs48[0]; 902 | callback = _doArgs48[1]; 903 | 904 | 905 | doRequest({ 906 | urlPattern: [QUEUE] 907 | }, customParams, callback); 908 | }, 909 | 910 | /** Get one queued item */ 911 | queue_item: function queue_item(queueNumber, customParams, callback) { 912 | var _doArgs49 = doArgs(arguments, ['string|number', ['object', {}], 'function']); 913 | 914 | var _doArgs50 = _slicedToArray(_doArgs49, 3); 915 | 916 | queueNumber = _doArgs50[0]; 917 | customParams = _doArgs50[1]; 918 | callback = _doArgs50[2]; 919 | 920 | 921 | doRequest({ 922 | urlPattern: [QUEUE_ITEM, queueNumber] 923 | }, customParams, callback); 924 | }, 925 | 926 | /** Cancel a queued item */ 927 | cancel_item: function cancel_item(itemId, customParams, callback) { 928 | var _doArgs51 = doArgs(arguments, ['string|number', ['object', {}], 'function']); 929 | 930 | var _doArgs52 = _slicedToArray(_doArgs51, 3); 931 | 932 | itemId = _doArgs52[0]; 933 | customParams = _doArgs52[1]; 934 | callback = _doArgs52[2]; 935 | 936 | 937 | customParams.id = itemId; 938 | 939 | doRequest({ 940 | method: 'POST', 941 | urlPattern: [QUEUE_CANCEL_ITEM] 942 | }, customParams, callback); 943 | }, 944 | 945 | /** ***********************************\ 946 | |* Computers *| 947 | \*************************************/ 948 | 949 | /** Get info about all jenkins workers including currently executing jobs */ 950 | computers: function computers(customParams, callback) { 951 | var _doArgs53 = doArgs(arguments, [['object', {}], 'function']); 952 | 953 | var _doArgs54 = _slicedToArray(_doArgs53, 2); 954 | 955 | customParams = _doArgs54[0]; 956 | callback = _doArgs54[1]; 957 | 958 | 959 | doRequest({ 960 | urlPattern: [COMPUTERS] 961 | }, customParams, callback); 962 | }, 963 | 964 | /** ***********************************\ 965 | |* Views *| 966 | \*************************************/ 967 | 968 | /** Return a list of all the views on the Jenkins server */ 969 | all_views: function all_views(customParams, callback) { 970 | var _doArgs55 = doArgs(arguments, [['object', {}], 'function']); 971 | 972 | var _doArgs56 = _slicedToArray(_doArgs55, 2); 973 | 974 | customParams = _doArgs56[0]; 975 | callback = _doArgs56[1]; 976 | 977 | 978 | doRequest({ 979 | urlPattern: [VIEW_LIST], 980 | bodyProp: 'views' 981 | }, customParams, callback); 982 | }, 983 | 984 | /** */ 985 | create_view: function create_view(viewName, mode, customParams, callback) { 986 | var _doArgs57 = doArgs(arguments, ['string', ['string', 'hudson.model.ListView'], ['object', {}], 'function']); 987 | 988 | var _doArgs58 = _slicedToArray(_doArgs57, 4); 989 | 990 | viewName = _doArgs58[0]; 991 | mode = _doArgs58[1]; 992 | customParams = _doArgs58[2]; 993 | callback = _doArgs58[3]; 994 | 995 | 996 | var formData = { name: viewName, mode: mode }; 997 | 998 | formData.json = JSON.stringify(formData); 999 | 1000 | var self = this; 1001 | 1002 | doRequest({ 1003 | method: 'POST', 1004 | urlPattern: [VIEW_CREATE], 1005 | request: { 1006 | form: formData 1007 | }, 1008 | noparse: true 1009 | }, customParams, function (error, data) { 1010 | if (error) { 1011 | callback(error, data); 1012 | return; 1013 | } 1014 | self.view_info(viewName, customParams, callback); 1015 | }); 1016 | }, 1017 | 1018 | /** */ 1019 | view_info: function view_info(viewId, customParams, callback) { 1020 | var _doArgs59 = doArgs(arguments, ['string', ['object', {}], 'function']); 1021 | 1022 | var _doArgs60 = _slicedToArray(_doArgs59, 3); 1023 | 1024 | viewId = _doArgs60[0]; 1025 | customParams = _doArgs60[1]; 1026 | callback = _doArgs60[2]; 1027 | 1028 | 1029 | doRequest({ 1030 | urlPattern: [VIEW_INFO, viewId] 1031 | }, customParams, callback); 1032 | }, 1033 | 1034 | /** Update a view based on a viewConfig object */ 1035 | update_view: function update_view(viewName, viewConfig, customParams, callback) { 1036 | var _doArgs61 = doArgs(arguments, ['string', 'object', ['object', {}], 'function']); 1037 | 1038 | var _doArgs62 = _slicedToArray(_doArgs61, 4); 1039 | 1040 | viewName = _doArgs62[0]; 1041 | viewConfig = _doArgs62[1]; 1042 | customParams = _doArgs62[2]; 1043 | callback = _doArgs62[3]; 1044 | 1045 | 1046 | viewConfig.json = JSON.stringify(viewConfig); 1047 | 1048 | var self = this; 1049 | 1050 | doRequest({ 1051 | method: 'POST', 1052 | urlPattern: [VIEW_CONFIG, viewName], 1053 | request: { 1054 | form: viewConfig 1055 | // headers: {'content-type': 'application/x-www-form-urlencoded'}, 1056 | }, 1057 | noparse: true 1058 | }, customParams, function (error, data) { 1059 | if (error) { 1060 | callback(error, data); 1061 | return; 1062 | } 1063 | self.view_info(viewName, customParams, callback); 1064 | }); 1065 | }, 1066 | 1067 | /** */ 1068 | delete_view: function delete_view(viewName, customParams, callback) { 1069 | var _doArgs63 = doArgs(arguments, ['string', ['object', {}], 'function']); 1070 | 1071 | var _doArgs64 = _slicedToArray(_doArgs63, 3); 1072 | 1073 | viewName = _doArgs64[0]; 1074 | customParams = _doArgs64[1]; 1075 | callback = _doArgs64[2]; 1076 | 1077 | 1078 | doRequest({ 1079 | method: 'POST', 1080 | urlPattern: [VIEW_DELETE, viewName], 1081 | noparse: true 1082 | }, customParams, function (error, data) { 1083 | if (error) { 1084 | callback(error, data); 1085 | return; 1086 | } 1087 | callback(null, { name: viewName }); 1088 | }); 1089 | }, 1090 | 1091 | /** */ 1092 | add_job_to_view: function add_job_to_view(viewId, jobName, customParams, callback) { 1093 | var _doArgs65 = doArgs(arguments, ['string', 'string', ['object', {}], 'function']); 1094 | 1095 | var _doArgs66 = _slicedToArray(_doArgs65, 4); 1096 | 1097 | viewId = _doArgs66[0]; 1098 | jobName = _doArgs66[1]; 1099 | customParams = _doArgs66[2]; 1100 | callback = _doArgs66[3]; 1101 | 1102 | 1103 | customParams.name = jobName; 1104 | 1105 | doRequest({ 1106 | method: 'POST', 1107 | urlPattern: [VIEW_ADD_JOB, viewId], 1108 | noparse: true 1109 | }, customParams, callback); 1110 | }, 1111 | 1112 | /** */ 1113 | remove_job_from_view: function remove_job_from_view(viewId, jobName, customParams, callback) { 1114 | var _doArgs67 = doArgs(arguments, ['string', 'string', ['object', {}], 'function']); 1115 | 1116 | var _doArgs68 = _slicedToArray(_doArgs67, 4); 1117 | 1118 | viewId = _doArgs68[0]; 1119 | jobName = _doArgs68[1]; 1120 | customParams = _doArgs68[2]; 1121 | callback = _doArgs68[3]; 1122 | 1123 | 1124 | customParams.name = jobName; 1125 | 1126 | doRequest({ 1127 | method: 'POST', 1128 | urlPattern: [VIEW_REMOVE_JOB, viewId], 1129 | noparse: true 1130 | }, customParams, callback); 1131 | }, 1132 | 1133 | /* Return a list of objet literals containing the name and color of all the jobs for a view on the Jenkins server */ 1134 | all_jobs_in_view: function all_jobs_in_view(viewId, customParams, callback) { 1135 | var _doArgs69 = doArgs(arguments, ['string', ['object', {}], 'function']); 1136 | 1137 | var _doArgs70 = _slicedToArray(_doArgs69, 3); 1138 | 1139 | viewId = _doArgs70[0]; 1140 | customParams = _doArgs70[1]; 1141 | callback = _doArgs70[2]; 1142 | 1143 | 1144 | doRequest({ 1145 | urlPattern: [VIEW_INFO, viewId], 1146 | bodyProp: 'jobs' 1147 | }, customParams, callback); 1148 | }, 1149 | 1150 | /** ***********************************\ 1151 | |* Plugins *| 1152 | \*************************************/ 1153 | 1154 | /* Get all installed plugins */ 1155 | all_installed_plugins: function all_installed_plugins(customParams, callback) { 1156 | var _doArgs71 = doArgs(arguments, [['object', {}], 'function']); 1157 | 1158 | var _doArgs72 = _slicedToArray(_doArgs71, 2); 1159 | 1160 | customParams = _doArgs72[0]; 1161 | callback = _doArgs72[1]; 1162 | 1163 | 1164 | customParams.depth = 1; 1165 | 1166 | doRequest({ 1167 | urlPattern: [PLUGINS], 1168 | failureStatusCodes: [HTTP_CODE_302], 1169 | noparse: true, 1170 | bodyProp: 'plugins' 1171 | }, customParams, callback); 1172 | }, 1173 | 1174 | /* Install a plugin */ 1175 | install_plugin: function install_plugin(plugin, customParams, callback) { 1176 | var _doArgs73 = doArgs(arguments, ['string', ['object', {}], 'function']); 1177 | 1178 | var _doArgs74 = _slicedToArray(_doArgs73, 3); 1179 | 1180 | plugin = _doArgs74[0]; 1181 | customParams = _doArgs74[1]; 1182 | callback = _doArgs74[2]; 1183 | 1184 | 1185 | var body = ''; 1186 | 1187 | doRequest({ 1188 | method: 'POST', 1189 | urlPattern: [INSTALL_PLUGIN], 1190 | request: { 1191 | body: body, 1192 | headers: { 'Content-Type': 'text/xml' } 1193 | }, 1194 | noparse: true, 1195 | bodyProp: 'plugins' 1196 | }, customParams, callback); 1197 | }, 1198 | 1199 | /* Create a new folder 1200 | * Needs Folder plugin in Jenkins: https://wiki.jenkins-ci.org/display/JENKINS/CloudBees+Folders+Plugin 1201 | * curl -XPOST 'http://jenkins/createItem?name=FolderName&mode=com.cloudbees.hudson.plugins.folder.Folder&from=&json=%7B%22name%22%3A%22FolderName%22%2C%22mode%22%3A%22com.cloudbees.hudson.plugins.folder.Folder%22%2C%22from%22%3A%22%22%2C%22Submit%22%3A%22OK%22%7D&Submit=OK' --user user.name:YourAPIToken -H "Content-Type:application/x-www-form-urlencoded" 1202 | * https://gist.github.com/stuart-warren/7786892 1203 | */ 1204 | create_folder: function create_folder(folderName, customParams, callback) { 1205 | var _doArgs75 = doArgs(arguments, ['string', ['object', {}], 'function']); 1206 | 1207 | var _doArgs76 = _slicedToArray(_doArgs75, 3); 1208 | 1209 | folderName = _doArgs76[0]; 1210 | customParams = _doArgs76[1]; 1211 | callback = _doArgs76[2]; 1212 | 1213 | 1214 | var mode = 'com.cloudbees.hudson.plugins.folder.Folder'; 1215 | 1216 | customParams.name = folderName; 1217 | customParams.mode = mode; 1218 | customParams.Submit = 'OK'; 1219 | 1220 | doRequest({ 1221 | method: 'POST', 1222 | urlPattern: [NEWFOLDER] 1223 | }, customParams, callback); 1224 | } 1225 | }; 1226 | }; --------------------------------------------------------------------------------