• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cliffano / nestor / 16776642241

06 Aug 2025 12:16PM UTC coverage: 96.448% (+19.8%) from 76.661%
16776642241

push

github

cliffano
Remove proxyquire dep. Fix test args, str assertions.

236 of 245 branches covered (96.33%)

1602 of 1661 relevant lines covered (96.45%)

9.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.01
/lib/jenkins.js
1
"use strict";
3✔
2
import cron from 'cron';
3✔
3
import jenkins from './api/jenkins.js';
3✔
4
import job from './api/job.js';
3✔
5
import Swaggy from 'swaggy-jenkins';
3✔
6
import _url from 'url';
3✔
7
import util from './cli/util.js';
3✔
8
import view from './api/view.js';
3✔
9
import fs from 'fs';
3✔
10

3✔
11
function _authFail(result, cb) {
3✔
12
  cb(new Error('Authentication failed - incorrect username and/or password'));
3✔
13
}
3✔
14

3✔
15
function _authRequire(result, cb) {
3✔
16
  cb(new Error('Jenkins requires authentication - please set username and password'));
3✔
17
}
3✔
18

3✔
19
/**
3✔
20
 * class Jenkins
3✔
21
 *
3✔
22
 * @param {String} url: Jenkins URL, fallback to JENKINS_URL environment variable, otherwise default to http://localhost:8080
3✔
23
 */
3✔
24
class Jenkins {
3✔
25

3✔
26
  constructor(url) {
3✔
27

144✔
28
    this.url = url || process.env.JENKINS_URL || 'http://localhost:8080';
144✔
29

144✔
30
    // base URL is needed for functionalities that operate against base Jenkins path
144✔
31
    // this is needed for retrieving Jenkins crumb where the URL might contain paths to folders,
144✔
32
    // but the the Jenkins crumb retrieval still needs to be executed against the base Jenkins path
144✔
33
    const parsedUrl = _url.parse(this.url);
144✔
34
    this.baseUrl = this.url.replace(parsedUrl.path, '');
144✔
35

144✔
36
    const cert = process.env.JENKINS_CERT;
144✔
37
    const ca = process.env.JENKINS_CA;
144✔
38
    const key = process.env.JENKINS_KEY;
144✔
39
    this.opts = {
144✔
40
      handlers: {
144✔
41
        401: _authFail,
144✔
42
        403: _authRequire
144✔
43
      }
144✔
44
    };
144✔
45

144✔
46
    this.remoteAccessApi = new Swaggy.RemoteAccessApi();
144✔
47
    this.remoteAccessApi.apiClient.basePath = this.url;
144✔
48

144✔
49
    // a new Swaggy.ApiClient must be created here in order
144✔
50
    // to force baseApi to not share the same ApiClient as
144✔
51
    // remoteAccessApi
144✔
52
    // this is necessary because baseApi uses a URL that could be
144✔
53
    // different to remoteAccessApi
144✔
54
    this.baseApi = new Swaggy.BaseApi(new Swaggy.ApiClient());
144✔
55
    this.baseApi.apiClient.basePath = this.baseUrl;
144✔
56

144✔
57

144✔
58
    if (cert) {
144!
59
      const key_path = key.split(':')[0];
×
60
      const passphrase = key.split(':')[1];
×
61
      this.opts.agentOptions = {
×
62
        passphrase: passphrase,
×
63
        secureProtocol: 'TLSv1_method'
×
64
      };
×
65
      if(cert && fs.statSync(cert)){
×
66
        this.opts.agentOptions.cert = fs.readFileSync(cert);
×
67
      }
×
68
      if(key_path && fs.statSync(key_path)){
×
69
        this.opts.agentOptions.key = fs.readFileSync(key_path);
×
70
      }
×
71
      if(ca && fs.statSync(ca)){
×
72
        this.opts.agentOptions.ca = fs.readFileSync(ca);
×
73
      }
×
74
    }
×
75
  }
144✔
76
}
3✔
77

3✔
78
/**
3✔
79
 * Add Jenkins crumb header to instance.
3✔
80
 * This is needed for Jenkins installations that have CSRF protection enabled.
3✔
81
 * New installations of Jenkins starting version 2.x enables CSRF protection by default.
3✔
82
 * https://wiki.jenkins-ci.org/display/JENKINS/CSRF+Protection
3✔
83
 *
3✔
84
 * @param {Function} cb: standard cb(err, result) callback
3✔
85
 */
3✔
86
function csrf(cb) {
18✔
87
  const self = this;
18✔
88

18✔
89
  this.opts.headers = this.opts.headers || {};
18✔
90

18✔
91
  function resultCb(err, data, response) {
18✔
92
    if (!err) {
18✔
93
      self.opts.headers[data.crumbRequestField] = data.crumb;
15✔
94
      self.opts.headers.jenkinsCrumb = data.crumb;
15✔
95
    }
15✔
96
    cb(err, data);
18✔
97
  }
18✔
98

18✔
99
  this.crumb(resultCb);
18✔
100
}
18✔
101

3✔
102
/**
3✔
103
 * Summarise executor information from computers array.
3✔
104
 *
3✔
105
 * @param {Array} computers: computers array, part of Jenkins#computer result
3✔
106
 * @return executor summary object
3✔
107
 */
3✔
108
function executorSummary(computers) {
3✔
109

3✔
110
  const data = {};
3✔
111

3✔
112
  computers.forEach(function (computer) {
3✔
113

9✔
114
    let idleCount   = 0;
9✔
115
    let activeCount = 0;
9✔
116

9✔
117
    data[computer.displayName] = { executors: [] };
9✔
118

9✔
119
    computer.executors.forEach(function (executor) {
9✔
120
      data[computer.displayName].executors.push({
12✔
121
        idle: executor.idle,
12✔
122
        stuck: executor.likelyStuck,
12✔
123
        progress: executor.progress,
12✔
124
        name: (!executor.idle && executor.currentExecutable.url) ?
12✔
125
          executor.currentExecutable.url.replace(/.*\/job\//, '').replace(/\/.*/, '') :
12✔
126
          undefined
6✔
127
      });
12✔
128

12✔
129
      if (executor.idle) {
12✔
130
        idleCount += 1;
6✔
131
      } else {
6✔
132
        activeCount += 1;
6✔
133
      }
6✔
134
    });
9✔
135

9✔
136
    const summary = [];
9✔
137
    if (activeCount > 0) {
9✔
138
      summary.push(`${activeCount} active`);
6✔
139
    }
6✔
140
    if (idleCount > 0) {
9✔
141
      summary.push(`${idleCount} idle`);
6✔
142
    }
6✔
143

9✔
144
    data[computer.displayName].summary = summary.join(', ');
9✔
145
  });
3✔
146

3✔
147
  return data;
3✔
148
}
3✔
149

3✔
150
/**
3✔
151
 * Monitor Jenkins latest build status on a set interval.
3✔
152
 *
3✔
153
 * @param {Object} opts: optional
3✔
154
 * - job: Jenkins job name
3✔
155
 * - view: Jenkins view name
3✔
156
 * - schedule: cron scheduling definition in standard * * * * * * format, default: 0 * * * * * (every minute)
3✔
157
 * @param {Function} cb: standard cb(err, result) callback
3✔
158
 */
3✔
159
function monitor(opts, cb) {
15✔
160
  const self = this;
15✔
161

15✔
162
  function singleJobResultCb(err, result) {
15✔
163
    if (!err) {
3✔
164
      result = util.statusByColor(result.color);
3✔
165
    }
3✔
166
    cb(err, result);
3✔
167
  }
3✔
168

15✔
169
  // when there are multiple jobs, status is derived following these rules:
15✔
170
  // - fail if any job has Jenkins color red
15✔
171
  // - warn if any job has Jenkins color yellow but no red
15✔
172
  // - non-success (e.g. notbuilt) if any job has Jenkins color status but no red and yellow
15✔
173
  // - success only if all jobs are either blue or green
15✔
174
  function multiJobsResultCb(err, result) {
15✔
175
    if (!err) {
12✔
176

12✔
177
      let hasRed        = false;
12✔
178
      let hasYellow     = false;
12✔
179
      let hasNonSuccess = false;
12✔
180
      let hasSuccess    = false;
12✔
181

12✔
182
      let successColor;
12✔
183
      let nonSuccessColor;
12✔
184

12✔
185
      result.jobs.forEach(function (job) {
12✔
186
        if (job.color === 'red') {
36✔
187
          hasRed = true;
3✔
188
        }
3✔
189
        if (job.color === 'yellow') {
36✔
190
          hasYellow = true;
6✔
191
        }
6✔
192
        if (['red', 'yellow', 'blue', 'green'].indexOf(job.color) === -1) {
36✔
193
          hasNonSuccess   = true;
6✔
194
          nonSuccessColor = job.color;
6✔
195
        }
6✔
196
        if (job.color === 'blue' || job.color === 'green') {
36✔
197
          hasSuccess   = true;
21✔
198
          successColor = job.color;
21✔
199
        }
21✔
200
      });
12✔
201

12✔
202
      let resultColor;
12✔
203
      if (hasRed) {
12✔
204
        resultColor = 'red';
3✔
205
      } else if (hasYellow) {
12✔
206
        resultColor = 'yellow';
3✔
207
      } else if (hasNonSuccess) {
9✔
208
        resultColor = nonSuccessColor;
3✔
209
      } else {
3✔
210
        resultColor = successColor;
3✔
211
      }
3✔
212

12✔
213
      result = util.statusByColor(resultColor);
12✔
214
    }
12✔
215
    cb(err, result);
12✔
216
  }
12✔
217

15✔
218
  function _notify() {
15✔
219
    if (opts.job) {
15✔
220
      self.readJob(opts.job, singleJobResultCb);
3✔
221
    } else if (opts.view) {
15✔
222
      self.readView(opts.view, multiJobsResultCb);
3✔
223
    } else {
12✔
224
      self.info(multiJobsResultCb);
9✔
225
    }
9✔
226
  }
15✔
227

15✔
228
  _notify();
15✔
229
  new cron.CronJob(opts.schedule || '0 * * * * *', _notify).start();
15!
230
}
15✔
231

3✔
232
Jenkins.prototype.csrf = csrf;
3✔
233
Jenkins.prototype.discover = jenkins.discover;
3✔
234
Jenkins.prototype.computer = jenkins.computer;
3✔
235
Jenkins.prototype.crumb = jenkins.crumb;
3✔
236
Jenkins.prototype.info = jenkins.info;
3✔
237
Jenkins.prototype.monitor = monitor;
3✔
238
Jenkins.prototype.parseFeed = jenkins.parseFeed;
3✔
239
Jenkins.prototype.queue = jenkins.queue;
3✔
240
Jenkins.prototype.version = jenkins.version;
3✔
241

3✔
242
Jenkins.prototype.createJob = job.create;
3✔
243
Jenkins.prototype.readJob = job.read;
3✔
244
Jenkins.prototype.readLatestJob = job.readLatest;
3✔
245
Jenkins.prototype.updateJob = job.update;
3✔
246
Jenkins.prototype.deleteJob = job.delete;
3✔
247
Jenkins.prototype.buildJob = job.build;
3✔
248
Jenkins.prototype.checkBuildStarted = job.checkStarted;
3✔
249
Jenkins.prototype.stopJob = job.stop;
3✔
250
Jenkins.prototype.streamJobConsole = job.streamConsole;
3✔
251
Jenkins.prototype.enableJob = job.enable;
3✔
252
Jenkins.prototype.disableJob = job.disable;
3✔
253
Jenkins.prototype.copyJob = job.copy;
3✔
254
Jenkins.prototype.fetchJobConfig = job.fetchConfig;
3✔
255
Jenkins.prototype.parseJobFeed = job.parseFeed;
3✔
256

3✔
257
Jenkins.prototype.createView = view.create;
3✔
258
Jenkins.prototype.readView = view.read;
3✔
259
Jenkins.prototype.updateView = view.update;
3✔
260
Jenkins.prototype.fetchViewConfig = view.fetchConfig;
3✔
261
Jenkins.prototype.parseViewFeed = view.parseFeed;
3✔
262

3✔
263
Jenkins.executorSummary = executorSummary;
3✔
264

3✔
265
export {
3✔
266
  Jenkins as default
3✔
267
};
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc