├── README.md
├── backend
├── .gitignore
├── Dockerfile
├── bin
│ └── scheduler
├── index.js
├── lib
│ ├── builder.js
│ ├── jenkins_xml.js
│ └── xml_encode.js
├── package-lock.json
├── package.json
└── templates
│ ├── commands-linux.txt
│ ├── commands-macos.txt
│ ├── commands-solaris.txt
│ ├── commands-windows.txt
│ ├── jenkins.sh
│ ├── job-linux.xml
│ ├── job-macos.xml
│ ├── job-solaris.xml
│ ├── job-ssh-publish.txt
│ ├── job-windows.xml
│ └── job.xml
├── config.env
├── cron
├── .gitignore
├── Dockerfile
├── README.md
├── bin
│ └── scheduler
├── index.js
├── package-lock.json
└── package.json
├── dev
├── rhub
└── stack-custom.yml
├── digitalocean
├── .gitignore
├── rhub
└── stack-custom.yml
├── frontend
├── .dockerignore
├── .gitignore
├── CHECKS
├── Dockerfile
├── README.md
├── app.js
├── bin
│ └── www
├── data
│ ├── email-inlined.html
│ ├── email.html
│ ├── email.txt
│ └── styles.css
├── lib
│ ├── auth-ok.js
│ ├── check-token.js
│ ├── create-job.js
│ ├── email-notification.js
│ ├── filter-log.js
│ ├── get-image.js
│ ├── get-package-data.js
│ ├── get-pkg-from-filename.js
│ ├── get-user.js
│ ├── get-version-from-filename.js
│ ├── job-to-db.js
│ ├── mail-token.js
│ ├── mail-verification-code.js
│ ├── parse-rhub-log.js
│ ├── queue-job.js
│ ├── queue-this.js
│ ├── re-status.js
│ ├── send-email-mailgun.js
│ ├── send-email-smtp.js
│ ├── send-email.js
│ ├── update-log.js
│ └── urls.js
├── package-lock.json
├── package.json
├── public
│ ├── .well-known
│ │ └── acme-challenge
│ │ │ ├── bQS91jPgESpdE1y_YFko_5yLMgn8KGEdpGcEuDIE6Ng
│ │ │ ├── qZ8CC8EFZACYXcGVFpHxR4-w-iQWorI2vnx30rx_AD4
│ │ │ └── wyl61bg_fpnNdT2MAzovERs1ltM1FK0ySKh_t2Vl0DE
│ ├── android-chrome-192x192.png
│ ├── android-chrome-512x512.png
│ ├── apple-touch-icon.png
│ ├── browserconfig.xml
│ ├── data
│ │ └── platforms.json
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── favicon.ico
│ ├── javascripts
│ │ └── vendor
│ │ │ ├── bootstrap.min.js.gz
│ │ │ ├── jquery-1.11.2.min.js
│ │ │ └── modernizr-2.8.3-respond-1.4.2.min.js
│ ├── logo.png
│ ├── mstile-150x150.png
│ ├── rhub-header.png
│ ├── rhub-square-white.png
│ ├── safari-pinned-tab.svg
│ ├── site.webmanifest
│ └── stylesheets
│ │ ├── fonts
│ │ ├── glyphicons-halflings-regular.eot
│ │ ├── glyphicons-halflings-regular.svg
│ │ ├── glyphicons-halflings-regular.ttf
│ │ ├── glyphicons-halflings-regular.woff
│ │ └── glyphicons-halflings-regular.woff2
│ │ ├── logstyle.css
│ │ ├── style.css
│ │ └── vendor
│ │ ├── bootstrap-theme.min.css.gz
│ │ └── bootstrap.min.css.gz
├── routes
│ ├── api.js
│ ├── build.js
│ ├── check.js
│ ├── index.js
│ ├── job.js
│ ├── login.js
│ └── status.js
└── views
│ ├── about.hjs
│ ├── aboutdiv.hjs
│ ├── aboutdiv.md
│ ├── adv.hjs
│ ├── badpackage.hjs
│ ├── error.hjs
│ ├── footer.hjs
│ ├── header.hjs
│ ├── ie7notice.hjs
│ ├── index.hjs
│ ├── layout.hjs
│ ├── navbar.hjs
│ ├── ok.hjs
│ ├── simple.hjs
│ ├── status.hjs
│ ├── terms.hjs
│ └── verify.hjs
├── jenkins.pass
├── jenkins
├── Dockerfile
├── config.groovy
├── default-user.groovy
├── plugins.txt
└── scripts
│ ├── windows-2008-pre-setup.ps1
│ └── windows-2008-setup.ps1
├── linux-builder
├── Dockerfile
└── run.sh
├── nginx
├── Dockerfile
├── entrypoint.sh
├── nginx.conf.https.in
├── nginx.conf.in
└── wait-for
├── rhub
├── seed
├── Dockerfile
├── app.json
└── logdb.sh
├── stack.yml
└── web.pass
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # R-hub Server development version
3 |
4 | This repository contains a Docker Compose project, that describes
5 | most of R-hub's services.
6 |
7 | Note that the master branch a development version. For deployment,
8 | please see [Releases](../../releases) for the latest released version.
9 |
10 | ## License
11 |
12 | MIT @ R Consortium
13 |
--------------------------------------------------------------------------------
/backend/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 |
--------------------------------------------------------------------------------
/backend/Dockerfile:
--------------------------------------------------------------------------------
1 |
2 | FROM node:8
3 |
4 | WORKDIR /usr/src/app
5 |
6 | # Install app dependencies
7 | # A wildcard is used to ensure both package.json AND package-lock.json are
8 | # copied where available (npm@5+)
9 |
10 | COPY package*json ./
11 |
12 | RUN npm install
13 |
14 | ## Include the app's source code
15 | COPY . .
16 |
17 | CMD [ "npm", "start" ]
18 |
--------------------------------------------------------------------------------
/backend/bin/scheduler:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var fs = require('fs');
4 |
5 | var jenkins_url = process.env.JENKINS_URL;
6 | try {
7 | var jenkins_pass = fs.readFileSync("/run/secrets/jenkins.pass", 'utf8')
8 | .trim();
9 | jenkins_url = jenkins_url.replace("", jenkins_pass);
10 | } catch (e) {
11 | console.log("No jenkins.pass secret, JENKINS_URL is used as is");
12 | }
13 | process.env.JENKINS_URL = jenkins_url;
14 |
15 | var scheduler = require('../index');
16 | scheduler('job')
17 |
--------------------------------------------------------------------------------
/backend/index.js:
--------------------------------------------------------------------------------
1 | var debug = require('debug');
2 | var builder = require('./lib/builder');
3 | var amqp = require('amqplib');
4 |
5 | var broker_url = process.env.RABBITMQ_URL;
6 |
7 | function run(q) {
8 |
9 | return amqp.connect(broker_url).then(function(conn) {
10 | process.once('SIGINT', function() { conn.close(); });
11 | return conn.createChannel().then(function(ch) {
12 | var ok = ch.assertQueue(q, {durable: true});
13 | ok = ok.then(function() { ch.prefetch(1); });
14 | ok = ok.then(function() {
15 | ch.consume(q, doWork, {noAck: false});
16 | });
17 | return ok;
18 |
19 | function doWork(msg) {
20 | var msg_obj = JSON.parse(msg.content.toString());
21 | console.log("STARTED: " + msg_obj);
22 |
23 | builder(msg_obj, function(error) {
24 | if (!error) {
25 | console.log("DONE: " + msg_obj.package);
26 | ch.ack(msg);
27 | } else {
28 | console.log("ERROR: " + msg_obj.package);
29 | }
30 | })
31 | }
32 | })
33 | })
34 | }
35 |
36 | function run_robust(q) {
37 | var q2 = q;
38 |
39 | function run_loop() {
40 | run(q2).catch(function(err) {
41 | console.log(err);
42 | console.log("Waiting 1 sec and trying again");
43 | setTimeout(run_loop, 1000);
44 | });
45 | }
46 |
47 | run_loop();
48 | }
49 |
50 | module.exports = run_robust;
51 |
--------------------------------------------------------------------------------
/backend/lib/builder.js:
--------------------------------------------------------------------------------
1 | var jenkins_url = process.env.JENKINS_URL ||
2 | 'http://jenkins.rhub.me';
3 | var jenkins = require('jenkins');
4 | var jenkins_xml = require('../lib/jenkins_xml');
5 | var quote = require('shell-quote').quote;
6 |
7 | function builder(job, callback) {
8 |
9 | var conn = jenkins({ baseUrl: jenkins_url, crumbIssuer: true });
10 | add_pkg(conn, job, function(err) {
11 | if (err) { callback(err); return; }
12 | callback(null);
13 | })
14 | }
15 |
16 | function add_pkg(conn, job, callback) {
17 |
18 | add_jenkins_job(conn, job, function(err) {
19 | if (err) { console.log(err); callback(err); return; }
20 |
21 | build_jenkins_job(conn, job, function(err) {
22 | if (err) { console.log(err); callback(err); return; }
23 | callback(null);
24 | })
25 | })
26 | }
27 |
28 | function add_jenkins_job(conn, job, callback) {
29 | var job_name = jenkins_job_name(job);
30 | jenkins_xml(job, function(err, job_xml) {
31 | if (err) { callback(err); return; }
32 | conn.job.create(
33 | 'Jobs/' + job_name,
34 | job_xml,
35 | function(err) {
36 | if (err) { console.log(err); callback(err); return; }
37 | callback(null);
38 | }
39 | )
40 | })
41 | }
42 |
43 | function build_jenkins_job(conn, job, callback) {
44 | var job_name = jenkins_job_name(job);
45 | var env_vars = flatten_env_vars(job.envVars, job.ostype !== "Linux");
46 | var parameters = {
47 | 'package': job.package,
48 | 'filename': job.filename,
49 | 'url': job.url,
50 | 'image': job.image || "",
51 | 'checkArgs': job.checkArgs || "",
52 | 'envVars': env_vars || "",
53 | 'rversion': job.rversion || "r-release",
54 | 'startPingUrl': job.builder + '/build/in-progress/' + job_name,
55 | 'endPingUrl': job.builder + '/build',
56 | 'build': (job.options.build || false) + '',
57 | 'pkgname': job.pkg + ''
58 | };
59 |
60 | conn.job.build(
61 | 'Jobs/' + job_name,
62 | { 'parameters': parameters },
63 | function(err) {
64 | if (err) { console.log(err); callback(err); return; }
65 | callback(null)
66 | }
67 | )
68 | }
69 |
70 | function flatten_env_vars(x, should_quote) {
71 | if (x === null || x === undefined || x === "") return(null);
72 | return Object.keys(x)
73 | .map(function(key) {
74 | var k = String(key);
75 | var v = String(x[key]);
76 | if (should_quote) {
77 | return quote([k]) + "=" + quote([v]);
78 | } else {
79 | return k + "=" + v;
80 | }
81 | })
82 | .join("\n");
83 | }
84 |
85 | function jenkins_job_name(job) {
86 | return job.buildId;
87 | }
88 |
89 | module.exports = builder;
90 |
--------------------------------------------------------------------------------
/backend/lib/jenkins_xml.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var mustache = require('mustache');
3 | var xml_encode = require('../lib/xml_encode');
4 |
5 | var artifacts = process.env.RHUB_ARTIFACTS || "ssh";
6 |
7 | function jenkins_xml(job, callback) {
8 |
9 | // For the transition, we don't have ostype
10 | var os = job.ostype || 'Linux';
11 | os = os.toLowerCase();
12 | var template = './templates/job-' + os + '.xml';
13 |
14 | fs.readFile(
15 | template,
16 | 'utf8',
17 | function(err, template) {
18 | if (err) { console.log(err); callback(err); return; }
19 |
20 | fs.readFile(
21 | './templates/commands-' + os + '.txt',
22 | 'utf8',
23 | function(err, command) {
24 | if (err) { console.log(err); callback(err); return; }
25 | var labels = job.platforminfo['node-labels'] || [];
26 | labels.push('swarm');
27 | labels = labels.join(' && ');
28 | var data = { 'commands': xml_encode(command),
29 | 'email': xml_encode(job.email),
30 | 'labels': xml_encode(labels) };
31 |
32 | if (artifacts == "ssh") {
33 | fs.readFile('./templates/job-ssh-publish.txt',
34 | 'utf8',
35 | function(err, ssh) {
36 | if (err) {
37 | console.log(err);
38 | return callback(err);
39 | }
40 | data['ssh-publish'] = xml_encode(ssh);
41 | var res = mustache.render(template, data);
42 | callback(null, res);
43 | })
44 | } else {
45 | data['ssh-publish'] = '';
46 | var res = mustache.render(template, data);
47 | callback(null, res);
48 | }
49 | }
50 | )
51 | }
52 | )
53 | }
54 |
55 | module.exports = jenkins_xml;
56 |
--------------------------------------------------------------------------------
/backend/lib/xml_encode.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = function(str) {
3 | if (typeof str === 'string' || str instanceof String) {
4 | return str
5 | .replace(/&/g, '&')
6 | .replace(//g, '>')
8 | .replace(/"/g, '"')
9 | .replace(/'/g, ''');
10 | } else {
11 | return str;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/backend/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rhub-backend",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "amqplib": {
8 | "version": "0.5.2",
9 | "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.5.2.tgz",
10 | "integrity": "sha512-l9mCs6LbydtHqRniRwYkKdqxVa6XMz3Vw1fh+2gJaaVgTM6Jk3o8RccAKWKtlhT1US5sWrFh+KKxsVUALURSIA==",
11 | "requires": {
12 | "bitsyntax": "~0.0.4",
13 | "bluebird": "^3.4.6",
14 | "buffer-more-ints": "0.0.2",
15 | "readable-stream": "1.x >=1.1.9",
16 | "safe-buffer": "^5.0.1"
17 | }
18 | },
19 | "array-filter": {
20 | "version": "0.0.1",
21 | "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz",
22 | "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw="
23 | },
24 | "array-map": {
25 | "version": "0.0.0",
26 | "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz",
27 | "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI="
28 | },
29 | "array-reduce": {
30 | "version": "0.0.0",
31 | "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz",
32 | "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys="
33 | },
34 | "bitsyntax": {
35 | "version": "0.0.4",
36 | "resolved": "https://registry.npmjs.org/bitsyntax/-/bitsyntax-0.0.4.tgz",
37 | "integrity": "sha1-6xDMb4K4xJDj6FaY8H6D1G4MuoI=",
38 | "requires": {
39 | "buffer-more-ints": "0.0.2"
40 | }
41 | },
42 | "bluebird": {
43 | "version": "3.5.2",
44 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz",
45 | "integrity": "sha512-dhHTWMI7kMx5whMQntl7Vr9C6BvV10lFXDAasnqnrMYhXVCzzk6IO9Fo2L75jXHT07WrOngL1WDXOp+yYS91Yg=="
46 | },
47 | "buffer-more-ints": {
48 | "version": "0.0.2",
49 | "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-0.0.2.tgz",
50 | "integrity": "sha1-JrOIXRD6E9t/wBquOquHAZngEkw="
51 | },
52 | "core-util-is": {
53 | "version": "1.0.2",
54 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
55 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
56 | },
57 | "debug": {
58 | "version": "4.1.0",
59 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz",
60 | "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==",
61 | "requires": {
62 | "ms": "^2.1.1"
63 | }
64 | },
65 | "inherits": {
66 | "version": "2.0.3",
67 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
68 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
69 | },
70 | "isarray": {
71 | "version": "0.0.1",
72 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
73 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
74 | },
75 | "jenkins": {
76 | "version": "0.20.1",
77 | "resolved": "https://registry.npmjs.org/jenkins/-/jenkins-0.20.1.tgz",
78 | "integrity": "sha512-4vpnBYIyy995FaReWP3LAGaVsQgV9WayI1pjEHbF+oIM/nV5DyGSwa/xIojZ7U+ECk8ZcXsCJDOhuJQvCr0AUA==",
79 | "requires": {
80 | "papi": "^0.26.0"
81 | }
82 | },
83 | "jsonify": {
84 | "version": "0.0.0",
85 | "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
86 | "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM="
87 | },
88 | "ms": {
89 | "version": "2.1.1",
90 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
91 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
92 | },
93 | "mustache": {
94 | "version": "2.3.2",
95 | "resolved": "https://registry.npmjs.org/mustache/-/mustache-2.3.2.tgz",
96 | "integrity": "sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ=="
97 | },
98 | "papi": {
99 | "version": "0.26.0",
100 | "resolved": "https://registry.npmjs.org/papi/-/papi-0.26.0.tgz",
101 | "integrity": "sha1-1hNqFJIHXrwmRSvY4J5EmYDEfdM="
102 | },
103 | "readable-stream": {
104 | "version": "1.1.14",
105 | "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
106 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
107 | "requires": {
108 | "core-util-is": "~1.0.0",
109 | "inherits": "~2.0.1",
110 | "isarray": "0.0.1",
111 | "string_decoder": "~0.10.x"
112 | }
113 | },
114 | "safe-buffer": {
115 | "version": "5.1.2",
116 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
117 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
118 | },
119 | "shell-quote": {
120 | "version": "1.6.1",
121 | "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz",
122 | "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=",
123 | "requires": {
124 | "array-filter": "~0.0.0",
125 | "array-map": "~0.0.0",
126 | "array-reduce": "~0.0.0",
127 | "jsonify": "~0.0.0"
128 | }
129 | },
130 | "string_decoder": {
131 | "version": "0.10.31",
132 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
133 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
134 | }
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rhub-backend",
3 | "version": "1.0.0",
4 | "description": "The back-end of r-hub. Picks up submissions from the queue and adds the builds to Jenkins.",
5 | "main": "index.js",
6 | "license": "MIT",
7 | "private": true,
8 | "scripts": {
9 | "start": "node ./bin/scheduler"
10 | },
11 | "engines": {
12 | "node": "8.12.x"
13 | },
14 | "dependencies": {
15 | "amqplib": "^0.5.2",
16 | "debug": "^4.1.0",
17 | "jenkins": "^0.20.1",
18 | "mustache": "^2.2.1",
19 | "shell-quote": "^1.6.1"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/backend/templates/commands-linux.txt:
--------------------------------------------------------------------------------
1 |
2 | # Notify the frontend that we are starting the build
3 | wget ${startPingUrl}/$(date -u +%Y-%m-%dT%H:%M:%SZ) || true
4 |
5 | # Get the platform, the R package uses this to determine
6 | # the packages needed
7 | export RHUB_PLATFORM=$(docker run --user docker \
8 | --rm rhub/${image} \
9 | bash -c 'echo $RHUB_PLATFORM')
10 |
11 | # Look up system requirements
12 | # wget https://raw.githubusercontent.com/MangoTheCat/remotes/r-hub/install-github.R
13 | # R -e "source(\"install-github.R\")\$value(\"r-hub/sysreqs\")"
14 |
15 | echo ">>>>>==================== Downloading and unpacking package file"
16 |
17 | wget -O "$package" "$url"
18 | DESC=$(tar tzf "$package" | grep "^[^/]*/DESCRIPTION$")
19 | tar xzf "$package" "$DESC"
20 |
21 | echo ">>>>>==================== Querying system requirements"
22 |
23 | sysreqs=$(Rscript -e "library(sysreqs); cat(sysreq_commands(\"$DESC\"))")
24 | rm "$DESC"
25 |
26 | echo ">>>>>==================== Installing system requirements"
27 |
28 | # Install them, if there is anything to install
29 | if [ ! -z "${sysreqs}" ]; then
30 | echo "${sysreqs}" > sysreqs.sh
31 | docker create --user root --name ${JOB_BASE_NAME}-1 \
32 | rhub/${image} bash /root/sysreqs.sh
33 | # Copy over sysreqs.sh script
34 | docker cp sysreqs.sh "${JOB_BASE_NAME}-1:/root/sysreqs.sh"
35 | # Start it
36 | docker start -i -a ${JOB_BASE_NAME}-1
37 | # Save the container as an image
38 | newimage=$(docker commit ${JOB_BASE_NAME}-1)
39 | else
40 | # If there is nothing to install we just use the stock image
41 | newimage=rhub/${image}
42 | fi
43 |
44 | if [ -z "$RHUB_CRAN_MIRROR" ]; then
45 | RHUB_CRAN_MIRROR=http://jenkins.iotwjus4p5merbek114fbzbqud.dx.internal.cloudapp.net/
46 | fi
47 |
48 | # Inject env vars into container
49 |
50 | env=$(tempfile 2>/dev/null || mktemp)
51 | echo url=$url >> $env
52 | echo package=$package >> $env
53 | echo checkArgs=$checkArgs >> $env
54 | echo build=$build >> $env
55 | echo R_REMOTES_STANDALONE=true >> $env
56 | echo R_REMOTES_NO_ERRORS_FROM_WARNINGS=true >> $env
57 | echo TZ=Europe/London >> $env
58 | echo RHUB_CRAN_MIRROR="$RHUB_CRAN_MIRROR" >> $env
59 | echo "$envVars" >> $env
60 |
61 | # Run the check in the new image
62 |
63 | echo ">>>>>==================== Starting Docker container"
64 |
65 | cat >build.sh <<'EOF'
66 | ## The default might not be the home directory, but /
67 | cd ~
68 |
69 | ## Configure R, local package library, and also CRAN and BioConductor
70 | export PATH=$(ls /opt/R-* -d)/bin:$PATH
71 | if [[ -z "$RBINARY" ]]; then RBINARY="R"; fi
72 | export R_LIBS=~/R
73 | mkdir -p ~/R
74 | echo "options(repos = c(CRAN = \"$RHUB_CRAN_MIRROR\"))" >> ~/.Rprofile
75 | $RBINARY -e "source('https://bioconductor.org/biocLite.R')"
76 | echo "options(repos = BiocInstaller::biocinstallRepos())" >> ~/.Rprofile
77 | echo "unloadNamespace('BiocInstaller')" >> ~/.Rprofile
78 |
79 | cp "/tmp/${package}" .
80 |
81 | if [[ "${build}" == "true" ]]; then
82 | echo ">>>>>==================== Running R CMD build"
83 | mkdir build
84 | cd build
85 | tar xzf ../"${package}"
86 | pkgname=$(ls | head -1 | sed 's/\///')
87 | $RBINARY CMD build ${pkgname}
88 | package=$(ls *.tar.gz | head -1)
89 | cp "${package}" ..
90 | cd ..
91 | fi
92 |
93 | ## We put it here, so the main process can pick it up
94 | mkdir -p /tmp/output
95 | cp "${package}" /tmp/output/
96 | echo "${package}" > /tmp/output/filename
97 |
98 | echo ">>>>>==================== Querying package dependencies"
99 |
100 | ## Download the single file install script from mangothecat/remotes
101 | ## We cannot do this from R, because some R versions do not support
102 | ## HTTPS. Then we install a proper 'remotes' package with it.
103 | curl -O https://raw.githubusercontent.com/MangoTheCat/remotes/r-hub/install-github.R
104 | xvfb-run --server-args="-screen 0 1024x768x24" $RBINARY -e "source(\"install-github.R\")\$value(\"r-lib/remotes@r-hub\")"
105 |
106 | echo ">>>>>==================== Installing package dependencies"
107 |
108 | ## Print configuration information for compilers
109 | echo $PATH
110 | $RBINARY CMD config CC
111 | `$RBINARY CMD config CC` --version
112 | $RBINARY CMD config CXX
113 | `$RBINARY CMD config CXX` --version
114 |
115 | echo Pandoc:
116 | which pandoc
117 | ls -l `which pandoc`
118 | ls -l `which pandoc-citeproc`
119 |
120 | ## Install the package, so its dependencies will be installed
121 | ## This is a temporary solution, until remotes::install_deps works on a
122 | ## package bundle
123 | xvfb-run --server-args="-screen 0 1024x768x24" $RBINARY -e "remotes::install_local(\"$package\", dependencies = TRUE, INSTALL_opts = \"--build\")"
124 |
125 | ## If installation fails, then we do not run the check at all
126 | pkgname=$(echo $package | sed 's/_.*$//')
127 | if ! $RBINARY -q -e "library($pkgname)"; then exit 1; fi
128 |
129 | echo ">>>>>==================== Running R CMD check"
130 |
131 | ## We only override this if it was not set by the user
132 | if [ -z "${_R_CHECK_FORCE_SUGGESTS_}" ]; then
133 | export _R_CHECK_FORCE_SUGGESTS_=false
134 | fi
135 |
136 | if [ -z "$RHUB_CHECK_COMMAND" ]; then
137 | RHUB_CHECK_COMMAND="$RBINARY CMD check $checkArgs"
138 | fi
139 |
140 | echo About to run xvfb-run $RHUB_CHECK_COMMAND $package
141 | xvfb-run --server-args="-screen 0 1024x768x24" $RHUB_CHECK_COMMAND "$package"
142 |
143 | echo ">>>>>==================== Done with R CMD check"
144 |
145 | pkgname=$(echo $package | sed 's/_.*$//')
146 | mkdir -p $pkgname.Rcheck || true
147 | mv *.tar.gz $pkgname.Rcheck/ || true
148 | EOF
149 |
150 | docker create -i --user docker --env-file $env \
151 | --name ${JOB_BASE_NAME}-2 $newimage /bin/bash -l /tmp/build.sh
152 | docker cp build.sh "${JOB_BASE_NAME}-2:/tmp/build.sh"
153 | docker cp "${package}" "${JOB_BASE_NAME}-2:/tmp/${package}"
154 | docker start -i -a ${JOB_BASE_NAME}-2
155 |
156 | echo ">>>>>==================== Saving artifacts"
157 |
158 | # Save the artifacts
159 | rm -rf ${JOB_BASE_NAME}
160 | mkdir -p ${JOB_BASE_NAME}
161 |
162 | docker cp "${JOB_BASE_NAME}-2:/tmp/output/filename" . || true
163 | package=$(cat filename | head -1)
164 | docker cp "${JOB_BASE_NAME}-2:/tmp/${package}" \
165 | ${JOB_BASE_NAME}/ || true
166 | pkgname=$(echo $package | sed 's/_.*$//')
167 |
168 | docker cp "${JOB_BASE_NAME}-2:/home/docker/${pkgname}.Rcheck" \
169 | ${JOB_BASE_NAME}/ || true
170 | mv ${JOB_BASE_NAME}/${pkgname}.Rcheck/*.tar.gz \
171 | ${JOB_BASE_NAME}/ || true
172 |
173 | if [ "x$RHUB_ARTIFACTS" = "xlocal" ]; then
174 | cp -r ${JOB_BASE_NAME} /artifacts/
175 | fi
176 |
177 | # Destroy the new containers and the images
178 | # Only if we needed system installs, but not the stock image
179 | docker rm ${JOB_BASE_NAME}-1 || true
180 | docker rm ${JOB_BASE_NAME}-2 || true
181 | docker rm ${JOB_BASE_NAME}-3 || true
182 | if ! echo $newimage | grep -q 'rhub'; then
183 | docker rmi $newimage || true
184 | fi
185 |
--------------------------------------------------------------------------------
/backend/templates/commands-macos.txt:
--------------------------------------------------------------------------------
1 |
2 | # Notify the frontend that we are starting the build
3 |
4 | curl ${startPingUrl}/$(date -u +%Y-%m-%dT%H:%M:%SZ) || true
5 |
6 | cp ~/macoscheck/*.sh .
7 |
8 | ./run.sh "${package}" "${JOB_BASE_NAME}" "${url}" "${rversion}" "${checkArgs}" "${envVars}" "${build}" "${pkgname}"
9 |
--------------------------------------------------------------------------------
/backend/templates/commands-solaris.txt:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Notify the frontend that we are starting the build
4 |
5 | /opt/csw/bin/curl ${startPingUrl}/$(date -u +%Y-%m-%dT%H:%M:%SZ) || true
6 |
7 | cp /export/home/solarischeck/*.sh .
8 |
9 | ./run.sh "${package}" "${JOB_BASE_NAME}" "${url}" "${rversion}" "${checkArgs}" "${envVars}" "${build}" "${pkgname}"
10 |
--------------------------------------------------------------------------------
/backend/templates/commands-windows.txt:
--------------------------------------------------------------------------------
1 |
2 | # Notify the frontend that we are starting the build
3 |
4 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
5 |
6 | $d = (get-date).ToUniversalTime().toString('s')
7 | $u = ( $env:startPingUrl + '/' + $d )
8 | Invoke-WebRequest $u
9 |
10 | cp \Users\rhub\Documents\run.ps1 .\run.ps1
11 | cp \Users\rhub\Documents\slave.ps1 .\slave.ps1
12 |
13 | .\run.ps1 -verbose $env:package $env:JOB_BASE_NAME $env:url $env:rversion $env:checkArgs $env:envVars $env:build $env:pkgname
14 |
--------------------------------------------------------------------------------
/backend/templates/jenkins.sh:
--------------------------------------------------------------------------------
1 | job-linux.xml
--------------------------------------------------------------------------------
/backend/templates/job-linux.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | false
6 |
7 |
8 |
9 |
10 | package
11 |
12 |
13 |
14 |
15 | filename
16 |
17 |
18 |
19 |
20 | url
21 |
22 |
23 |
24 |
25 | image
26 |
27 |
28 |
29 |
30 | checkArgs
31 |
32 |
33 |
34 |
35 | envVars
36 |
37 |
38 |
39 |
40 | startPingUrl
41 |
42 |
43 |
44 |
45 | endPingUrl
46 |
47 |
48 |
49 |
50 | pre-check.json
51 |
52 |
53 |
54 | post-check.json
55 |
56 |
57 |
58 | build
59 | Whether to run R CMD build.
60 | false
61 |
62 |
63 | pkgname
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | {{{ labels }}}
72 | false
73 | false
74 | false
75 | false
76 |
77 | false
78 |
79 |
80 | {{{ commands }}}
81 |
82 |
83 | {{{ ssh-publish }}}
84 |
85 |
86 |
87 |
96 | false
97 |
98 | 0
99 | false
100 |
101 |
102 |
103 | false
104 | false
105 | true
106 | true
107 | true
108 | true
109 | true
110 | true
111 | false
112 |
113 | false
114 |
115 |
116 |
117 |
118 |
119 | 600
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/backend/templates/job-macos.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | false
6 |
7 |
8 |
9 |
10 | package
11 |
12 |
13 |
14 |
15 | filename
16 |
17 |
18 |
19 |
20 | url
21 |
22 |
23 |
24 |
25 | image
26 |
27 |
28 |
29 |
30 | checkArgs
31 |
32 |
33 |
34 |
35 | envVars
36 |
37 |
38 |
39 |
40 | startPingUrl
41 |
42 |
43 |
44 |
45 | endPingUrl
46 |
47 |
48 |
49 |
50 | pre-check.json
51 |
52 |
53 |
54 | post-check.json
55 |
56 |
57 |
58 | build
59 | Whether to run R CMD build.
60 | false
61 |
62 |
63 | pkgname
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | {{{ labels }}}
72 | false
73 | false
74 | false
75 | false
76 |
77 | false
78 |
79 |
80 | {{{ commands }}}
81 |
82 |
83 | {{{ ssh-publish }}}
84 |
85 |
86 |
87 |
96 | false
97 |
98 | 0
99 | false
100 |
101 |
102 |
103 | false
104 | false
105 | true
106 | true
107 | true
108 | true
109 | true
110 | true
111 | false
112 |
113 | false
114 |
115 |
116 |
117 |
118 |
119 | 600
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/backend/templates/job-solaris.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | false
6 |
7 |
8 |
9 |
10 | package
11 |
12 |
13 |
14 |
15 | filename
16 |
17 |
18 |
19 |
20 | url
21 |
22 |
23 |
24 |
25 | image
26 |
27 |
28 |
29 |
30 | checkArgs
31 |
32 |
33 |
34 |
35 | envVars
36 |
37 |
38 |
39 |
40 | startPingUrl
41 |
42 |
43 |
44 |
45 | endPingUrl
46 |
47 |
48 |
49 |
50 | pre-check.json
51 |
52 |
53 |
54 | post-check.json
55 |
56 |
57 |
58 | build
59 | Whether to run R CMD build.
60 | false
61 |
62 |
63 | pkgname
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | {{{ labels }}}
72 | false
73 | false
74 | false
75 | false
76 |
77 | false
78 |
79 |
80 | {{{ commands }}}
81 |
82 |
83 | {{{ ssh-publish }}}
84 |
85 |
86 |
87 |
96 | false
97 |
98 | 0
99 | false
100 |
101 |
102 |
103 | false
104 | false
105 | true
106 | true
107 | true
108 | true
109 | true
110 | true
111 | false
112 |
113 | false
114 |
115 |
116 |
117 |
118 |
119 | 600
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/backend/templates/job-ssh-publish.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 | SSH:
4 |
5 |
6 |
7 | files
8 | false
9 |
10 |
11 | ${JOB_BASE_NAME}
12 | ${JOB_BASE_NAME}/**/00install.out,${JOB_BASE_NAME}/**/00check.log,${JOB_BASE_NAME}/**/tests*/**/*.Rout*,${JOB_BASE_NAME}/*.tar.gz,${JOB_BASE_NAME}/*.tgz,${JOB_BASE_NAME}/*.zip
13 |
14 | ${JOB_BASE_NAME}/
15 | false
16 | false
17 | false
18 | false
19 | false
20 | [, ]+
21 |
22 | 120000
23 | false
24 |
25 |
26 | false
27 | false
28 |
29 |
30 | false
31 | false
32 | false
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/backend/templates/job-windows.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | false
6 |
7 |
8 |
9 |
10 | package
11 |
12 |
13 |
14 |
15 | filename
16 |
17 |
18 |
19 |
20 | url
21 |
22 |
23 |
24 |
25 | image
26 |
27 |
28 |
29 |
30 | checkArgs
31 |
32 |
33 |
34 |
35 | envVars
36 |
37 |
38 |
39 |
40 | rversion
41 |
42 | r-release
43 |
44 |
45 | startPingUrl
46 |
47 |
48 |
49 |
50 | endPingUrl
51 |
52 |
53 |
54 |
55 | pre-check.json
56 |
57 |
58 |
59 | post-check.json
60 |
61 |
62 |
63 | build
64 | Whether to run R CMD build.
65 | false
66 |
67 |
68 | pkgname
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | {{{ labels }}}
77 | false
78 | false
79 | false
80 | false
81 |
82 | false
83 |
84 |
85 | {{{ commands }}}
86 |
87 |
88 | {{{ ssh-publish }}}
89 |
90 |
91 |
92 |
101 | false
102 |
103 | 0
104 | false
105 |
106 |
107 |
108 | false
109 | false
110 | true
111 | true
112 | true
113 | true
114 | true
115 | true
116 | false
117 |
118 | false
119 |
120 |
121 |
122 |
123 |
124 | 1800
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 | WINDOWS R-DEVEL
134 |
135 |
136 |
137 |
138 |
--------------------------------------------------------------------------------
/backend/templates/job.xml:
--------------------------------------------------------------------------------
1 | job-linux.xml
--------------------------------------------------------------------------------
/config.env:
--------------------------------------------------------------------------------
1 |
2 | # ------------------------------------------------------------------
3 | # You can safely leave these untouched
4 | # ------------------------------------------------------------------
5 |
6 | RHUB_VERSION="0.10"
7 | RABBITMQ_URL="amqp://guest:guest@queue:5672"
8 | REDIS_URL="redis://redis:6379/0"
9 | REDIS_EMAIL_URL="redis://redis:6379/1"
10 | LOGDB_URL="http://logdb:5984/logs"
11 | RHUB_CRAN_MIRROR="https://cloud.r-project.org"
12 | RHUB_ARTIFACTS="local"
13 | JENKINS_ROOT_URL="http://localhost:8080/jenkins"
14 | JENKINS_USER="admin"
15 |
16 | # ------------------------------------------------------------------
17 | # HTTPS. You'll need to create the 'nginx.crt' and 'nginx.key'
18 | # secrets to make this work. Then set it to "true".
19 | # ------------------------------------------------------------------
20 |
21 | RHUB_HTTPS="false"
22 |
23 | # ------------------------------------------------------------------
24 | # GitHub auth. You can leave these untouched for deploying on 127.0.0.1,
25 | # but you'll probably need to update them for a non-local
26 | # deployment. See the digitalocean folder for an example.
27 | # ------------------------------------------------------------------
28 |
29 | # Create a GitHub app at https://github.com/settings/developers or
30 | # https://github.com/organizations//settings/applications
31 | # if you use a GitHub organization
32 |
33 | GITHUB_CLIENT_ID="d0b3649d316fca3ebc92"
34 | GITHUB_CLIENT_SECRET="68679254184d595b85b1f60772abc93a7f822bbf"
35 |
36 | # You need to set these to the host name of your server
37 |
38 | RHUB_BUILDER_EXTERNAL_URL="http://127.0.0.1"
39 | RHUB_ARTIFACTS_URL="http://127.0.0.1/artifacts/"
40 |
41 | # ------------------------------------------------------------------
42 | # Sending emails. You'll need to update this to be able to send
43 | # notification emails.
44 | # ------------------------------------------------------------------
45 |
46 | RHUB_EMAIL_FROM='"R-hub builder" '
47 |
48 | # This must be smtp or mailgun
49 | RHUB_EMAIL_MODE="${RHUB_EMAIL_MODE:-smtp}"
50 |
51 | # Send via mailgun HTTP API
52 | MAILGUN_DOMAIN="${MAILGUN_DOMAIN:-<-- put-mailgun-domain-here -->}"
53 | MAILGUN_API_KEY="${MAILGUN_API_KEY:-<-- put-mailgun-api-key-here -->}"
54 |
55 | # Send via a generic SMTP server
56 | # Here is an example that works with mailgun:
57 | # RHUB_EMAIL_MODE=smtp
58 | # RHUB_SMTP_SERVER=smtp.mailgun.org
59 | # RHUB_SMTP_USERNAME=postmaster@rhub.io
60 | # RHUB_SMTP_PASSWORD=<-- smtp-password -->
61 | # RHUB_SMTP_PORT=465
62 | # RHUB_SMTP_TLS_REQUIRED=true
63 |
64 | RHUB_SMTP_SERVER="<-- smtp-server -->"
65 | RHUB_SMTP_USERNAME="<-- smtp-username -->"
66 | RHUB_SMTP_PASSWORD="<-- smtp-password -->"
67 | RHUB_SMTP_PORT="25"
68 | RHUB_SMTP_TLS_REQUIRED="true"
69 |
--------------------------------------------------------------------------------
/cron/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 |
--------------------------------------------------------------------------------
/cron/Dockerfile:
--------------------------------------------------------------------------------
1 |
2 | FROM node:8
3 |
4 | WORKDIR /usr/src/app
5 |
6 | # Install app dependencies
7 | # A wildcard is used to ensure both package.json AND package-lock.json are
8 | # copied where available (npm@5+)
9 |
10 | COPY package*json ./
11 |
12 | RUN npm install
13 |
14 | ## Include the app's source code
15 | COPY . .
16 |
17 | CMD [ "npm", "start" ]
18 |
--------------------------------------------------------------------------------
/cron/README.md:
--------------------------------------------------------------------------------
1 | # rhub-cron
2 |
--------------------------------------------------------------------------------
/cron/bin/scheduler:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | var jobs = require('../index');
3 |
--------------------------------------------------------------------------------
/cron/index.js:
--------------------------------------------------------------------------------
1 | var CronJob = require('cron').CronJob;
2 | var jenkins = require('jenkins');
3 | var async = require('async');
4 | var fs = require('fs');
5 |
6 | var jenkins_url = process.env.JENKINS_URL;
7 |
8 | try {
9 | var jenkins_pass = fs.readFileSync("/run/secrets/jenkins.pass", 'utf8')
10 | .trim();
11 | jenkins_url = jenkins_url.replace("", jenkins_pass);
12 | } catch (e) {
13 | console.log("No jenkins.pass secret, JENKINS_URL is used as is");
14 | }
15 |
16 | // Time limit to delete job
17 | var TIME_LIMIT = 1000 /* ms */ * 60 /* s */ * 60 /* min */ * 24 * 3;
18 | // How often to run the job reaper, once an hour, at **:42:42
19 | var CRON_JOB_REAPER = '48 45 * * * *';
20 |
21 | var job = new CronJob(CRON_JOB_REAPER, function() {
22 |
23 | console.log("Running Jenkins job reaper");
24 | var jen = jenkins({ baseUrl: jenkins_url, crumbIssuer: true });
25 | jen.job.list(function(err, data) {
26 | if (err) { console.log('Cannot get Jenkins job list'); return; }
27 | async.eachLimit(
28 | data,
29 | 3,
30 | function(job, cb) { delete_if_old(jen, job, cb); }
31 | );
32 | });
33 |
34 | }, null, true, 'America/New_York');
35 |
36 | function delete_if_old(jen, job, callback) {
37 | jen.job.get(job.name, function(err, data) {
38 | if (err) {
39 | console.log('Cannot get Jenkins job ' + job.name);
40 | return callback(null);
41 | }
42 |
43 | if (data.actions) {
44 | for (i = 0; i < data.actions.length; i++) {
45 | var def = data.actions[i].parameterDefinitions;
46 | if (def === undefined) continue;
47 | for (j = 0; j < def.length; j++) {
48 | if (def[j].name == 'keep') {
49 | if (def[j].defaultParameterValue.value) {
50 | console.log('Keeping ' + job.name);
51 | return callback(null);
52 | }
53 | }
54 | }
55 | }
56 | }
57 |
58 | // No builds (yet?)
59 | if (! data.lastBuild || ! data.lastBuild.number) {
60 | return callback(null);
61 | }
62 |
63 | jen.build.get(job.name, data.lastBuild.number, function(err, data) {
64 | if (err) {
65 | console.log('Cannot get Jenkins build ' + job.name + ' ' +
66 | data.lastBuild.number);
67 | return callback(null);
68 | }
69 | var diff = new Date() - new Date(data.timestamp);
70 | // about three days
71 | if (diff > TIME_LIMIT) {
72 | return delete_job(jen, job, callback);
73 | }
74 | return callback(null);
75 | });
76 | });
77 | }
78 |
79 | function delete_job(jen, job, callback) {
80 | jen.job.destroy(job.name, function(err) {
81 | if (err) {
82 | console.log('Cannot delete Jenkins job ' + job.name);
83 | } else {
84 | console.log('Deleted Jenkins job ' + job.name);
85 | }
86 | return callback(null);
87 | });
88 | }
89 |
90 | module.exports = job;
91 |
--------------------------------------------------------------------------------
/cron/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rhub-cron",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "async": {
8 | "version": "2.6.1",
9 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
10 | "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
11 | "requires": {
12 | "lodash": "^4.17.10"
13 | }
14 | },
15 | "cron": {
16 | "version": "1.4.1",
17 | "resolved": "https://registry.npmjs.org/cron/-/cron-1.4.1.tgz",
18 | "integrity": "sha512-HlglwQUNh6bhgfoDR6aEzyHN2T4bc0XhxJxkNPp+Ry7lK7Noby94pHcngYf634+MtxplwZm8okFgNe+R9PGDjg==",
19 | "requires": {
20 | "moment-timezone": "^0.5.x"
21 | }
22 | },
23 | "jenkins": {
24 | "version": "0.19.0",
25 | "resolved": "https://registry.npmjs.org/jenkins/-/jenkins-0.19.0.tgz",
26 | "integrity": "sha1-kir5yYtz39coyskviti+4rkc1/I=",
27 | "requires": {
28 | "papi": "^0.26.0"
29 | }
30 | },
31 | "lodash": {
32 | "version": "4.17.11",
33 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
34 | "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
35 | },
36 | "moment": {
37 | "version": "2.22.2",
38 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz",
39 | "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y="
40 | },
41 | "moment-timezone": {
42 | "version": "0.5.21",
43 | "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.21.tgz",
44 | "integrity": "sha512-j96bAh4otsgj3lKydm3K7kdtA3iKf2m6MY2iSYCzCm5a1zmHo1g+aK3068dDEeocLZQIS9kU8bsdQHLqEvgW0A==",
45 | "requires": {
46 | "moment": ">= 2.9.0"
47 | }
48 | },
49 | "papi": {
50 | "version": "0.26.0",
51 | "resolved": "https://registry.npmjs.org/papi/-/papi-0.26.0.tgz",
52 | "integrity": "sha1-1hNqFJIHXrwmRSvY4J5EmYDEfdM="
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/cron/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rhub-cron",
3 | "version": "1.0.0",
4 | "description": "Scheduler for periodic JS tasks on r-hub",
5 | "scripts": {
6 | "start": "node ./bin/scheduler",
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/r-hub/rhub-cron.git"
12 | },
13 | "keywords": [
14 | "r-hub"
15 | ],
16 | "author": "Gabor Csardi",
17 | "license": "ISC",
18 | "bugs": {
19 | "url": "https://github.com/r-hub/rhub-cron/issues"
20 | },
21 | "homepage": "https://github.com/r-hub/rhub-cron#readme",
22 | "engines": {
23 | "node": "8.12.x"
24 | },
25 | "dependencies": {
26 | "async": "^2.0.0-rc.3",
27 | "cron": "^1.1.0",
28 | "jenkins": "^0.19.0"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/dev/rhub:
--------------------------------------------------------------------------------
1 | ../rhub
--------------------------------------------------------------------------------
/dev/stack-custom.yml:
--------------------------------------------------------------------------------
1 | version: '3.3'
2 |
3 | # Changes compared to base project:
4 | # - want to have direct access to services, for debugging
5 |
6 | services:
7 | frontend:
8 | ports:
9 | - "3000:3000"
10 |
11 | redis:
12 | ports:
13 | - "3001:6379"
14 |
15 | logdb:
16 | ports:
17 | - "3002:5984"
18 |
19 | queue:
20 | ports:
21 | - "3003:5672"
22 |
23 | jenkins:
24 | ports:
25 | - "8080:8080"
26 |
--------------------------------------------------------------------------------
/digitalocean/.gitignore:
--------------------------------------------------------------------------------
1 | /secrets
2 |
--------------------------------------------------------------------------------
/digitalocean/rhub:
--------------------------------------------------------------------------------
1 | ../rhub
--------------------------------------------------------------------------------
/digitalocean/stack-custom.yml:
--------------------------------------------------------------------------------
1 | version: '3.3'
2 |
3 | services:
4 |
5 | frontend:
6 | environment:
7 | - RHUB_BUILDER_EXTERNAL_URL=http://test.rhub.io
8 | - RHUB_ARTIFACTS_URL=http://test.rhub.io/artifacts/
9 | - RHUB_EMAIL_MODE=mailgun
10 | - MAILGUN_DOMAIN=${MAILGUN_DOMAIN:?rhub.io}
11 | - MAILGUN_API_KEY=${MAILGUN_API_KEY:?Set MAILGUN_API_KEY and MAILGUN_DOMAIN}
12 | - GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID:?Need to set GITHUB_CLIENT_ID}
13 | - GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET?:Set GITHUB_CLIENT_SECRET}
14 |
15 | nginx:
16 | environment:
17 | - RHUB_HTTPS=${RHUB_HTTPS}
18 | secrets:
19 | - nginx.crt
20 | - nginx.key
21 |
--------------------------------------------------------------------------------
/frontend/.dockerignore:
--------------------------------------------------------------------------------
1 | node_nodules
2 | npm-debug.log
3 |
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /.env
3 | .Rproj.user
4 | rhub-frontend.Rproj
5 |
--------------------------------------------------------------------------------
/frontend/CHECKS:
--------------------------------------------------------------------------------
1 | /check I am alive
2 |
--------------------------------------------------------------------------------
/frontend/Dockerfile:
--------------------------------------------------------------------------------
1 |
2 | FROM node:8
3 |
4 | WORKDIR /usr/src/app
5 |
6 | # Install app dependencies
7 | # A wildcard is used to ensure both package.json AND package-lock.json are
8 | # copied where available (npm@5+)
9 |
10 | COPY package*json ./
11 |
12 | RUN npm install
13 |
14 | ## Include the app's source code
15 | COPY . .
16 |
17 | EXPOSE 3000
18 |
19 | CMD [ "npm", "start" ]
20 |
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 |
2 | Source for the frontend of R-hub builder
3 | ========================================
4 |
5 | # Notes on how to run the web app
6 |
7 | ## Requirements
8 |
9 | You need a local Redis server. E.g. on macOS:
10 | ```
11 | brew install redis
12 | ```
13 |
14 | On Windows install Redis from
15 | https://github.com/MicrosoftArchive/redis/releases
16 |
17 | On Linux your distribution probably contains Redis. If not, e.g. on Ubuntu run
18 |
19 | ```
20 | sudo install redis-server
21 | ```
22 |
23 | ## Install dependencies
24 |
25 | ```
26 | npm install
27 | npm install -g supervisor
28 | ```
29 |
30 | On Ubuntu you might have to run some of these commands as root i.e. putting `sudo` before them.
31 |
32 | `supervisor` is not strictly required, but it is nice, because it
33 | automatically reloads the app if the source files change.
34 |
35 | ## Run the app
36 |
37 | Start Redis, and in another terminal start the app:
38 |
39 | ```
40 | redis-server
41 | ## In another terminal
42 | supervisor bin/www
43 | ## Or
44 | bin/www
45 | ```
46 |
47 | Browse http://localhost:3000/
48 |
--------------------------------------------------------------------------------
/frontend/app.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var path = require('path');
3 | var favicon = require('serve-favicon');
4 | var logger = require('morgan');
5 | var cookieParser = require('cookie-parser');
6 | var bodyParser = require('body-parser');
7 | var gzipStatic = require('connect-gzip-static');
8 | var session = require('express-session');
9 | var RedisStore = require('connect-redis')(session);
10 | var passport = require('passport');
11 | var GitHubStrategy = require('passport-github').Strategy;
12 | var uuid = require('uuid');
13 |
14 | var routes = require('./routes/index');
15 | var job = require('./routes/job');
16 | var login = require('./routes/login');
17 | var dokkucheck = require('./routes/check');
18 | var status = require('./routes/status');
19 | var api = require('./routes/api');
20 | var build = require('./routes/build');
21 |
22 | require('dotenv').config();
23 |
24 | var GITHUB_CLIENT_ID = process.env.GITHUB_CLIENT_ID || 'foo';
25 | var GITHUB_CLIENT_SECRET = process.env.GITHUB_CLIENT_SECRET || 'bar';
26 | var RHUB_BUILDER_URL = process.env.RHUB_BUILDER_URL ||
27 | 'http://127.0.0.1:3000';
28 | var RHUB_BUILDER_EXTERNAL_URL = process.env.RHUB_BUILDER_EXTERNAL_URL ||
29 | RHUB_BUILDER_URL;
30 | var REDIS_URL = process.env.REDIS_URL ||
31 | 'redis://redis:6379/0';
32 |
33 | passport.use(
34 | new GitHubStrategy(
35 | {
36 | clientID: GITHUB_CLIENT_ID,
37 | clientSecret: GITHUB_CLIENT_SECRET,
38 | callbackURL: RHUB_BUILDER_EXTERNAL_URL +
39 | '/login/github/callback'
40 | },
41 | function(accessToken, refreshToken, profile, cb) {
42 | return cb(null, 'github:' + JSON.stringify(profile.emails));
43 | }
44 | )
45 | );
46 |
47 | passport.serializeUser(function(user, cb) {
48 | cb(null, user);
49 | });
50 |
51 | passport.deserializeUser(function(obj, cb) {
52 | cb(null, obj);
53 | });
54 |
55 | var app = express();
56 |
57 | // view engine setup
58 | app.set('views', path.join(__dirname, 'views'));
59 | app.engine('hjs', require('hogan-express'));
60 | app.set('view engine', 'hjs');
61 | app.set('partials', {
62 | 'layout': 'layout',
63 | 'header': 'header',
64 | 'navbar': 'navbar',
65 | 'ie7notice': 'ie7notice',
66 | 'footer': 'footer',
67 | 'simple': 'simple',
68 | 'adv': 'adv',
69 | 'aboutdiv': 'aboutdiv'
70 | })
71 |
72 | // uncomment after placing your favicon in /public
73 | //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
74 | app.use(logger('dev'));
75 | app.use(bodyParser.json({ limit: '100mb'}));
76 | app.use(bodyParser.urlencoded({ extended: false }));
77 | app.use(cookieParser());
78 | app.use(gzipStatic(path.join(__dirname, 'public')));
79 |
80 | app.use(session({
81 | secret: 'r-hub magic going on',
82 | resave: true,
83 | saveUninitialized: true,
84 | genid: function(req) { return uuid.v4(); },
85 | name: "r-hub frontend",
86 | cookie: {
87 | maxAge: 36000000,
88 | httpOnly: false
89 | },
90 | store: new RedisStore({
91 | url: REDIS_URL
92 | })
93 | }));
94 |
95 | app.use(function (req, res, next) {
96 | if (!req.session) {
97 | return next(new Error('oh no')) // handle error
98 | }
99 | next() // otherwise continue
100 | })
101 |
102 | app.use(passport.initialize());
103 | app.use(passport.session());
104 |
105 | app.use('/', routes);
106 | app.use('/job', job);
107 |
108 | app.use('/', login);
109 | app.use('/status', status);
110 |
111 | // The JSON API
112 | app.use('/api', api);
113 |
114 | app.use('/build', build);
115 |
116 | app.use('/file', express.static('uploads'));
117 |
118 | app.use('/check', dokkucheck);
119 |
120 | // catch 404 and forward to error handler
121 | app.use(function(req, res, next) {
122 | var err = new Error('Not Found');
123 | err.status = 404;
124 | next(err);
125 | });
126 |
127 | // error handlers
128 |
129 | // development error handler
130 | // will print stacktrace
131 | if (app.get('env') === 'development') {
132 | app.use(function(err, req, res, next) {
133 | res.status(err.status || 500);
134 | res.render('error', {
135 | message: err.message,
136 | error: err
137 | });
138 | });
139 | }
140 |
141 | // production error handler
142 | // no stacktraces leaked to user
143 | app.use(function(err, req, res, next) {
144 | res.status(err.status || 500);
145 | res.render('error', {
146 | message: err.message,
147 | error: {}
148 | });
149 | });
150 |
151 |
152 | module.exports = app;
153 |
--------------------------------------------------------------------------------
/frontend/bin/www:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * Get the secrets from Docker Swarm
5 | */
6 | var fs = require('fs');
7 |
8 | var jenkins_url = process.env.JENKINS_URL;
9 | try {
10 | var jenkins_pass = fs.readFileSync("/run/secrets/jenkins.pass", 'utf8')
11 | .trim();
12 | jenkins_url = jenkins_url.replace("", jenkins_pass);
13 | } catch (e) {
14 | console.log("No jenkins.pass secret, JENKINS_URL is used as is");
15 | }
16 | process.env.JENKINS_URL = jenkins_url;
17 |
18 | /**
19 | * Module dependencies.
20 | */
21 |
22 | var app = require('../app');
23 | var debug = require('debug')('rhub-frontend:server');
24 | var http = require('http');
25 |
26 | /**
27 | * Get port from environment and store in Express.
28 | */
29 |
30 | var port = normalizePort(process.env.PORT || '3000');
31 | app.set('port', port);
32 |
33 | /**
34 | * Create HTTP server.
35 | */
36 |
37 | var server = http.createServer(app);
38 |
39 | /**
40 | * Listen on provided port, on all network interfaces.
41 | */
42 |
43 | server.listen(port);
44 | server.on('error', onError);
45 | server.on('listening', onListening);
46 |
47 | /**
48 | * Normalize a port into a number, string, or false.
49 | */
50 |
51 | function normalizePort(val) {
52 | var port = parseInt(val, 10);
53 |
54 | if (isNaN(port)) {
55 | // named pipe
56 | return val;
57 | }
58 |
59 | if (port >= 0) {
60 | // port number
61 | return port;
62 | }
63 |
64 | return false;
65 | }
66 |
67 | /**
68 | * Event listener for HTTP server "error" event.
69 | */
70 |
71 | function onError(error) {
72 | if (error.syscall !== 'listen') {
73 | throw error;
74 | }
75 |
76 | var bind = typeof port === 'string'
77 | ? 'Pipe ' + port
78 | : 'Port ' + port;
79 |
80 | // handle specific listen errors with friendly messages
81 | switch (error.code) {
82 | case 'EACCES':
83 | console.error(bind + ' requires elevated privileges');
84 | process.exit(1);
85 | break;
86 | case 'EADDRINUSE':
87 | console.error(bind + ' is already in use');
88 | process.exit(1);
89 | break;
90 | default:
91 | throw error;
92 | }
93 | }
94 |
95 | /**
96 | * Event listener for HTTP server "listening" event.
97 | */
98 |
99 | function onListening() {
100 | var addr = server.address();
101 | var bind = typeof addr === 'string'
102 | ? 'pipe ' + addr
103 | : 'port ' + addr.port;
104 | debug('Listening on ' + bind);
105 | }
106 |
--------------------------------------------------------------------------------
/frontend/data/email.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ title }}
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | {{#bgIsRed}}
20 | {{ header }}
21 | {{/bgIsRed}}
22 | {{#bgIsOrange}}
23 | {{ header }}
24 | {{/bgIsOrange}}
25 | {{#bgIsGreen}}
26 | {{ header }}
27 | {{/bgIsGreen}}
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | Build ID:
37 | {{ id }}
38 |
39 |
40 | Platform:
41 | {{ platform }}
42 |
43 |
44 | Submitted:
45 | {{ submitted }}
46 |
47 |
48 | Build time:
49 | {{ buildTime }}
50 |
51 |
52 |
53 |
54 |
55 |
56 | {{# anyErrors }}
57 | ERRORS:
58 | {{# errors }}
59 |
* {{.}}
60 | {{/ errors}}
61 | {{/ anyErrors }}
62 |
63 | {{# anyWarnings }}
64 | WARNINGS:
65 | {{# warnings }}
66 |
* {{.}}
67 | {{/ warnings }}
68 | {{/ anyWarnings }}
69 |
70 | {{# anyNotes }}
71 | NOTES:
72 | {{# notes }}
73 |
* {{.}}
74 | {{/ notes }}
75 | {{/ anyNotes }}
76 |
77 |
78 |
79 |
80 | See the full build log:
81 | HTML ,
82 | text ,
83 | artifacts .
84 |
85 |
86 |
87 |
88 | Have questions, suggestions or want to report a bug?
89 | Please file an issue ticket at
90 |
91 | GitHub .
92 | Thank You for using the R-hub builder.
93 |
94 |
95 |
96 |
97 |
98 |
99 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/frontend/data/email.txt:
--------------------------------------------------------------------------------
1 |
2 | {{{ title }}}
3 |
4 | Build ID: {{{ id }}}
5 | Platform: {{{ platform }}}
6 | Submitted: {{{ submitted }}}
7 | Build time: {{{ buildTime }}}
8 |
9 | {{# anyErrors }}
10 | ERRORS:
11 | -------
12 | {{# errors }}
13 | * {{{.}}}
14 | {{/ errors}}
15 | {{/ anyErrors }}
16 |
17 | {{# anyWarnings }}
18 | WARNINGS:
19 | ---------
20 | {{# warnings }}
21 | * {{{.}}}
22 | {{/ warnings }}
23 | {{/ anyWarnings }}
24 |
25 | {{# anyNotes }}
26 | NOTES:
27 | ------
28 | {{# notes }}
29 | * {{{.}}}
30 | {{/ notes }}
31 | {{/ anyNotes }}
32 |
33 | See the full build log:
34 | HTML: {{{ logHtml }}}
35 | Text: {{{ logText }}}
36 | Artifacts: {{{ artifactLink }}}
37 |
38 | Have questions, suggestions or want to report a bug?
39 | Please file an issue ticket at GitHub at
40 | https://github.com/r-hub/rhub/issues
41 |
42 | Thank You for using the R-hub builder.
43 |
44 | (c) 2016 The R Consortium
45 |
--------------------------------------------------------------------------------
/frontend/data/styles.css:
--------------------------------------------------------------------------------
1 | /* -------------------------------------
2 | GLOBAL
3 | A very basic CSS reset
4 | ------------------------------------- */
5 | * {
6 | margin: 0;
7 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
8 | box-sizing: border-box;
9 | font-size: 14px;
10 | }
11 |
12 | img {
13 | max-width: 100%;
14 | }
15 |
16 | body {
17 | -webkit-font-smoothing: antialiased;
18 | -webkit-text-size-adjust: none;
19 | width: 100% !important;
20 | height: 100%;
21 | line-height: 1.6em;
22 | /* 1.6em * 14px = 22.4px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
23 | /*line-height: 22px;*/
24 | }
25 |
26 | /* Let's make sure all tables have defaults */
27 | table td {
28 | vertical-align: top;
29 | }
30 |
31 | /* -------------------------------------
32 | BODY & CONTAINER
33 | ------------------------------------- */
34 | body {
35 | background-color: #f6f6f6;
36 | }
37 |
38 | .body-wrap {
39 | background-color: #f6f6f6;
40 | width: 100%;
41 | }
42 |
43 | .container {
44 | display: block !important;
45 | max-width: 600px !important;
46 | margin: 0 auto !important;
47 | /* makes it centered */
48 | clear: both !important;
49 | }
50 |
51 | .content {
52 | max-width: 600px;
53 | margin: 0 auto;
54 | display: block;
55 | padding: 20px;
56 | }
57 |
58 | /* -------------------------------------
59 | HEADER, FOOTER, MAIN
60 | ------------------------------------- */
61 | .main {
62 | background-color: #fff;
63 | border: 1px solid #e9e9e9;
64 | border-radius: 3px;
65 | }
66 |
67 | .content-wrap {
68 | padding: 20px;
69 | }
70 |
71 | .content-block {
72 | padding: 0 0 20px;
73 | }
74 |
75 | .header {
76 | width: 100%;
77 | margin-bottom: 20px;
78 | }
79 |
80 | .footer {
81 | width: 100%;
82 | clear: both;
83 | color: #999;
84 | padding: 20px;
85 | }
86 | .footer p, .footer a, .footer td {
87 | color: #999;
88 | font-size: 12px;
89 | }
90 |
91 | /* -------------------------------------
92 | TYPOGRAPHY
93 | ------------------------------------- */
94 | h1, h2, h3 {
95 | font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
96 | color: #000;
97 | margin: 40px 0 0;
98 | line-height: 1.2em;
99 | font-weight: 400;
100 | }
101 |
102 | h1 {
103 | font-size: 32px;
104 | font-weight: 500;
105 | /* 1.2em * 32px = 38.4px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
106 | /*line-height: 38px;*/
107 | }
108 |
109 | h2 {
110 | font-size: 24px;
111 | /* 1.2em * 24px = 28.8px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
112 | /*line-height: 29px;*/
113 | }
114 |
115 | h3 {
116 | font-size: 18px;
117 | /* 1.2em * 18px = 21.6px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
118 | /*line-height: 22px;*/
119 | }
120 |
121 | h4 {
122 | font-size: 14px;
123 | font-weight: 600;
124 | }
125 |
126 | p, ul, ol {
127 | margin-bottom: 10px;
128 | font-weight: normal;
129 | }
130 | p li, ul li, ol li {
131 | margin-left: 5px;
132 | list-style-position: inside;
133 | }
134 |
135 | /* -------------------------------------
136 | LINKS & BUTTONS
137 | ------------------------------------- */
138 | a {
139 | color: #348eda;
140 | text-decoration: underline;
141 | }
142 |
143 | .btn-primary {
144 | text-decoration: none;
145 | color: #FFF;
146 | background-color: #348eda;
147 | border: solid #348eda;
148 | border-width: 10px 20px;
149 | line-height: 2em;
150 | /* 2em * 14px = 28px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
151 | /*line-height: 28px;*/
152 | font-weight: bold;
153 | text-align: center;
154 | cursor: pointer;
155 | display: inline-block;
156 | border-radius: 5px;
157 | text-transform: capitalize;
158 | }
159 |
160 | /* -------------------------------------
161 | OTHER STYLES THAT MIGHT BE USEFUL
162 | ------------------------------------- */
163 | .last {
164 | margin-bottom: 0;
165 | }
166 |
167 | .first {
168 | margin-top: 0;
169 | }
170 |
171 | .aligncenter {
172 | text-align: center;
173 | }
174 |
175 | .alignright {
176 | text-align: right;
177 | }
178 |
179 | .alignleft {
180 | text-align: left;
181 | }
182 |
183 | .clear {
184 | clear: both;
185 | }
186 |
187 | /* -------------------------------------
188 | ALERTS
189 | Change the class depending on warning email, good email or bad email
190 | ------------------------------------- */
191 | .alert {
192 | font-size: 24px;
193 | color: #fff;
194 | font-weight: 500;
195 | padding: 20px;
196 | text-align: center;
197 | border-radius: 3px 3px 0 0;
198 | }
199 | .alert a {
200 | color: #fff;
201 | text-decoration: none;
202 | font-weight: 500;
203 | font-size: 16px;
204 | }
205 | .alert.alert-warning {
206 | background-color: #FF9F00;
207 | }
208 | .alert.alert-bad {
209 | background-color: #D0021B;
210 | }
211 | .alert.alert-good {
212 | background-color: #68B90F;
213 | }
214 | p.code pre {
215 | margin-top: 5px;
216 | padding: 5px;
217 | background: #eee;
218 | font-size: 12px;
219 | font-family: Menlo,Monaco,Consolas,"Courier New",monospace;
220 | line-height: 120%;
221 | white-space: pre;
222 | }
223 |
224 | /* -------------------------------------
225 | INVOICE
226 | Styles for the billing table
227 | ------------------------------------- */
228 | .invoice {
229 | margin: 40px auto;
230 | text-align: left;
231 | width: 80%;
232 | }
233 | .invoice td {
234 | padding: 5px 0;
235 | }
236 | .invoice .invoice-items {
237 | width: 100%;
238 | }
239 | .invoice .invoice-items td {
240 | border-top: #eee 1px solid;
241 | }
242 | .invoice .invoice-items .total td {
243 | border-top: 2px solid #333;
244 | border-bottom: 2px solid #333;
245 | font-weight: 700;
246 | }
247 |
248 | /* -------------------------------------
249 | RESPONSIVE AND MOBILE FRIENDLY STYLES
250 | ------------------------------------- */
251 | @media only screen and (max-width: 640px) {
252 | body {
253 | padding: 0 !important;
254 | }
255 |
256 | h1, h2, h3, h4 {
257 | font-weight: 800 !important;
258 | margin: 20px 0 5px !important;
259 | }
260 |
261 | h1 {
262 | font-size: 22px !important;
263 | }
264 |
265 | h2 {
266 | font-size: 18px !important;
267 | }
268 |
269 | h3 {
270 | font-size: 16px !important;
271 | }
272 |
273 | .container {
274 | padding: 0 !important;
275 | width: 100% !important;
276 | }
277 |
278 | .content {
279 | padding: 0 !important;
280 | }
281 |
282 | .content-wrap {
283 | padding: 10px !important;
284 | }
285 |
286 | .invoice {
287 | width: 100% !important;
288 | }
289 | }
290 |
291 | /*# sourceMappingURL=styles.css.map */
292 |
--------------------------------------------------------------------------------
/frontend/lib/auth-ok.js:
--------------------------------------------------------------------------------
1 |
2 | var get_user = require('../lib/get-user');
3 |
4 | function auth_ok(req, job) {
5 | if (!req.isAuthenticated()) { return false; }
6 |
7 | var user = get_user(req);
8 | if (!user) { return false; }
9 |
10 | return user.user == job.email;
11 | }
12 |
13 | module.exports = auth_ok;
14 |
--------------------------------------------------------------------------------
/frontend/lib/check-token.js:
--------------------------------------------------------------------------------
1 |
2 | function check_token(client, req, res, callback) {
3 |
4 | var token = (req.headers.authorization || '').split(' ');
5 |
6 | if (! req.params.email || token.length != 2 || token[0] != 'token' ||
7 | token[1] == '') {
8 | res.set('Content-Type', 'application/json; charset=utf-8')
9 | .status(400)
10 | .end(JSON.stringify({
11 | "result": "error",
12 | "message": "Authorization failed, email or token is missing." }));
13 | callback('No email or token');
14 | return;
15 | }
16 |
17 | client.get(req.params.email, function(err, dbtoken) {
18 | if (err || token[1] != dbtoken) {
19 | res.set('Content-Type', 'application/json; charset=utf-8')
20 | .status(401)
21 | .end(JSON.stringify({
22 | "result": "error",
23 | "message": "Email address not validated"
24 | }));
25 | callback('Email not validated');
26 | return;
27 | }
28 | callback(null, req.params.email);
29 | return;
30 | });
31 | }
32 |
33 | module.exports = check_token;
34 |
--------------------------------------------------------------------------------
/frontend/lib/create-job.js:
--------------------------------------------------------------------------------
1 |
2 | var get_package_data = require('../lib/get-package-data');
3 | var get_image = require('../lib/get-image');
4 | var r = require('rhub-node');
5 |
6 | function create_job(req, callback) {
7 |
8 | var baseurl = process.env.RHUB_BUILDER_URL ||
9 | req.protocol + '://' + req.get('host');
10 | var url = baseurl + "/file/" + req.file.filename;
11 | var logUrl = '/status/log/' + req.file.originalname + '-' +
12 | req.file.filename;
13 |
14 | var re_filename = new RegExp(
15 | '^' +
16 | r.valid_package_name +
17 | '_' +
18 | r.valid_package_version +
19 | '[.]tar[.]gz$');
20 |
21 | if (! req.body['build-package'] &&
22 | ! re_filename.test(req.file.originalname)) {
23 | return callback(
24 | "This does not look like an R package. " +
25 | "Did you build it using 'R CMD build'?"
26 | )
27 | }
28 |
29 | var job = {
30 | 'buildId': req.file.originalname + '-' + req.file.filename,
31 | 'package': req.file.originalname,
32 | 'filename': req.file.filename,
33 | 'url': url,
34 | 'size': req.file.size,
35 | 'email': null,
36 | 'logUrl': logUrl,
37 | 'submitted': new Date().toISOString(),
38 | 'builder': baseurl
39 | };
40 |
41 | // Get the image
42 | get_image(req.body.platform, function(err, platform) {
43 | if (err) { return callback(err); }
44 | job.platform = platform.name;
45 | job.image = platform["docker-image"];
46 | job.ostype = platform["os-type"];
47 | job.rversion = platform.rversion;
48 | job.platforminfo = platform;
49 |
50 | // Build options
51 | job.options = { };
52 | job.options.build = !! req.body['build-package'];
53 |
54 | // Fill in the maintainer, package name and version
55 | var filename = __dirname + '/../uploads/' + job.filename;
56 | get_package_data(filename, function(err, data) {
57 | if (err) { return callback(err); }
58 | job.email = data.maint;
59 | job.pkg = data.Package;
60 | job.version = data.Version;
61 | if (req.body['alternative-email']) {
62 | job.email = req.body['alternative-email'];
63 | }
64 | if (!job.email) { return(callback("Cannot find 'Maintainer'")); }
65 | if (!job.pkg) { return(callback("Cannot find package name")); }
66 | if (!job.version) { return(callback("Cannot find package version")); }
67 | callback(null, job);
68 | })
69 | });
70 | }
71 |
72 | module.exports = create_job;
73 |
--------------------------------------------------------------------------------
/frontend/lib/email-notification.js:
--------------------------------------------------------------------------------
1 |
2 | var fs = require('fs');
3 | var mustache = require('mustache');
4 | var pretty_ms = require('pretty-ms');
5 | var send_email = require('../lib/send-email');
6 |
7 | var RHUB_BUILDER_URL = process.env.RHUB_BUILDER_URL ||
8 | 'http://127.0.0.1:3000';
9 | var RHUB_BUILDER_EXTERNAL_URL = process.env.RHUB_BUILDER_EXTERNAL_URL ||
10 | RHUB_BUILDER_URL;
11 | var RHUB_ARTIFACTS_URL = process.env.RHUB_ARTIFACTS_URL ||
12 | 'https://artifacts.r-hub.io/';
13 | var RHUB_EMAIL_FROM = process.env.RHUB_EMAIL_FROM ||
14 | '"r-hub builder" ';
15 |
16 | function email_notification(build, callback) {
17 |
18 | fs.readFile(
19 | './data/email.txt',
20 | 'utf8',
21 | function(err, text_body) {
22 | if (err) { return callback(err); }
23 |
24 | fs.readFile(
25 | './data/email-inlined.html',
26 | 'utf8',
27 | function(err, html_body) {
28 | if (err) { return callback(err); }
29 |
30 | email_with_template(
31 | build,
32 | text_body,
33 | html_body,
34 | callback
35 | );
36 | }
37 | );
38 |
39 | }
40 | );
41 | }
42 |
43 | function email_with_template(build, text_body, html_body, callback) {
44 |
45 | var email = build.email;
46 | var subject = build.package + ' ' + build.version + ': ' +
47 | build.status.toUpperCase();
48 |
49 | var backgrounds = {
50 | 'ok': '#68B90F',
51 | 'note': '#FF9F00',
52 | 'warning': '#FF9F00',
53 | 'error': '#D0021B',
54 | 'preperror': '#D0021B'
55 | };
56 |
57 | var submitted = pretty_ms(
58 | new Date() - new Date(build.submitted),
59 | { verbose: true }
60 | ) + ' ago';
61 |
62 | var lcstatus = build.status.toLowerCase();
63 |
64 | var dict = {
65 | 'id': build.id,
66 | 'title': subject,
67 | 'header': subject,
68 | 'result': build.result.status,
69 | 'buildTime': pretty_ms(build.build_time, { verbose: true }),
70 | 'submitted': submitted,
71 | 'logHtml': RHUB_BUILDER_EXTERNAL_URL + '/status/' + build.id,
72 | 'logText': RHUB_BUILDER_EXTERNAL_URL + '/status/original/' + build.id,
73 | 'artifactLink': RHUB_ARTIFACTS_URL + '/' + build.id,
74 | 'anyErrors': build.result.errors.length > 0,
75 | 'errors': build.result.errors,
76 | 'anyWarnings': build.result.warnings.length > 0,
77 | 'warnings': build.result.warnings,
78 | 'anyNotes': build.result.notes.length > 0,
79 | 'notes': build.result.notes,
80 | 'platform': build.platform.description,
81 | 'bgIsRed': lcstatus == 'error' || lcstatus == 'preperror' ||
82 | lcstatus == "aborted",
83 | 'bgIsOrange': lcstatus == 'warning' || lcstatus == 'note',
84 | 'bgIsGreen': lcstatus == 'ok'
85 | };
86 |
87 | var mail = {
88 | from: RHUB_EMAIL_FROM,
89 | to: email,
90 | subject: subject,
91 | text: mustache.render(text_body, dict),
92 | html: mustache.render(html_body, dict)
93 | };
94 |
95 | send_email(mail, function(error, info) {
96 | if (error) {
97 | console.log(error);
98 | return callback(error);
99 | }
100 | console.log('Message sent: ' + info.response);
101 | callback(null, info.response);
102 | });
103 | }
104 |
105 | module.exports = email_notification;
106 |
--------------------------------------------------------------------------------
/frontend/lib/filter-log.js:
--------------------------------------------------------------------------------
1 |
2 | var stream = require('stream');
3 | var util = require('util');
4 | var left_pad = require('left-pad');
5 |
6 | // node v0.10+ use native Transform, else polyfill
7 | var Transform = stream.Transform ||
8 | require('readable-stream').Transform;
9 |
10 | function RHubLogFilter(options) {
11 | // allow use without new
12 | if (!(this instanceof RHubLogFilter)) {
13 | return new RHubLogFilter(options);
14 | }
15 |
16 | // init Transform
17 | Transform.call(this, options);
18 | }
19 | util.inherits(RHubLogFilter, Transform);
20 |
21 | RHubLogFilter.prototype._transform = function (chunk, enc, cb) {
22 |
23 | // Line numbers
24 | if (!this.line_number) this.line_number = 1;
25 | var line = this.line_number;
26 | this.line_number += 1;
27 | var sp_line = left_pad(line, 4);
28 |
29 | chunk = chunk.toString();
30 |
31 | // Are we printing input or output?
32 | if (!this.output_mode) {
33 | this.output_mode = 'header';
34 | this.push(
35 | 'Preparing ' +
36 | ''
37 | );
38 | }
39 |
40 | if (/echo >>>>>=====/.test(chunk)) {
41 | return cb();
42 |
43 | } else if (/^>>>>>=====/.test(chunk)) {
44 | chunk = chunk.replace(/^>>>>>=====* ?/, '');
45 | if (this.output_mode != 'header') { this.push('
'); }
46 | chunk = '' + chunk +
47 | ' ';
48 | this.output_mode = 'header';
49 |
50 | } else {
51 | if (/^\++R-HUB-R-HUB-R-HUB/.test(chunk)) {
52 | chunk = chunk.replace(/^\++R-HUB-R-HUB-R-HUB/, "");
53 | if (this.output_mode != 'input') {
54 | if (this.output_mode == 'output') this.push('
');
55 | this.push('');
56 | this.output_mode = 'input';
57 | }
58 |
59 | } else {
60 | chunk = '#> ' + chunk;
61 | if (this.output_mode != 'output') {
62 | if (this.output_mode == 'input') this.push('
');
63 | this.push('');
64 | this.output_mode = 'output';
65 | }
66 | }
67 |
68 | chunk = '
' +
69 | '' +
70 | '' +
71 | '' + sp_line + '
' +
72 | chunk + '
';
73 | }
74 |
75 | this.push(chunk + "\n");
76 | cb();
77 | };
78 |
79 | function SimpleLogFilter(options) {
80 | // allow use without new
81 | if (!(this instanceof SimpleLogFilter)) {
82 | return new SimpleLogFilter(options);
83 | }
84 |
85 | // init Transform
86 | Transform.call(this, options);
87 | }
88 | util.inherits(SimpleLogFilter, Transform);
89 |
90 | SimpleLogFilter.prototype._transform = function (chunk, enc, cb) {
91 |
92 | chunk = chunk.toString();
93 |
94 | if (/echo >>>>>=====/.test(chunk)) {
95 | return cb();
96 |
97 | } else if (/^>>>>>=====/.test(chunk)) {
98 | chunk = chunk.replace(/^>>>>>=====* ?/, '');
99 |
100 | } else {
101 | if (/^\++R-HUB-R-HUB-R-HUB/.test(chunk)) {
102 | chunk = chunk.replace(/^\++R-HUB-R-HUB-R-HUB/, "");
103 |
104 | } else {
105 | chunk = '#> ' + chunk;
106 | }
107 | }
108 |
109 | this.push(chunk + "\n");
110 | cb();
111 | };
112 |
113 | module.exports = RHubLogFilter;
114 | module.exports.SimpleLogFilter = SimpleLogFilter;
115 |
--------------------------------------------------------------------------------
/frontend/lib/get-image.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 |
3 | function get_image(platform, callback) {
4 | fs.readFile(
5 | './public/data/platforms.json',
6 | 'utf8',
7 | function(err, json) {
8 | if (err) { return callback(err); }
9 |
10 | var platforms = JSON.parse(json);
11 |
12 | // Default platform, this should be ideally declared in
13 | // platforms.json. Anyway.
14 | if (platform === null || platform === undefined) {
15 | return callback(
16 | null,
17 | platforms[0]
18 | );
19 | }
20 |
21 | var image = null;
22 | for (i = 0; i < platforms.length; i++) {
23 | if (platforms[i].name === platform) {
24 | return callback(
25 | null,
26 | platforms[i]
27 | );
28 | }
29 | }
30 | callback('Unknown platform');
31 | }
32 | );
33 | }
34 |
35 | module.exports = get_image;
36 |
--------------------------------------------------------------------------------
/frontend/lib/get-package-data.js:
--------------------------------------------------------------------------------
1 | var tar = require('tar-stream');
2 | var gunzip = require('gunzip-maybe');
3 | var fs = require('fs');
4 | var desc = require('rdesc-parser');
5 |
6 | function get_package_data(tarball, callback) {
7 |
8 | var extract = tar.extract();
9 | var done = false;
10 |
11 | extract.on('entry', function(header, stream, tarcb) {
12 | if (!done && header.name.match(/^[^\/]+\/DESCRIPTION$/)) {
13 | done = true;
14 | stream.setEncoding('utf8');
15 | desc(stream, function(err, description) {
16 | if (err) { return callback(err); }
17 | var maint = description.Maintainer;
18 | if (!!maint) {
19 | description.maint = maint.replace(/^.*<(.*)>.*$/, "$1");
20 | }
21 |
22 | callback(null, description);
23 | extract.destroy();
24 | })
25 | } else {
26 | tarcb()
27 | }
28 |
29 | stream.resume();
30 | });
31 |
32 | extract.on('finish', function() {
33 | if (!done) { callback('No DESCRIPTION file'); }
34 | })
35 |
36 | extract.on('error', function() {
37 | callback('Cannot get DESCRIPTION data, not an R package?');
38 | extract.destroy();
39 | })
40 |
41 | fs.createReadStream(tarball)
42 | .pipe(gunzip())
43 | .pipe(extract);
44 | }
45 |
46 | module.exports = get_package_data;
47 |
--------------------------------------------------------------------------------
/frontend/lib/get-pkg-from-filename.js:
--------------------------------------------------------------------------------
1 |
2 | var r = require('rhub-node');
3 |
4 | function get_pkg_from_filename(filename) {
5 | var re = new RegExp('^(' + r.valid_package_name + ')_');
6 | var match = re.exec(filename);
7 | if (match) { return match[1]; } else { return null; }
8 | }
9 |
10 | module.exports = get_pkg_from_filename;
11 |
--------------------------------------------------------------------------------
/frontend/lib/get-user.js:
--------------------------------------------------------------------------------
1 |
2 | function get_user(req) {
3 | try {
4 | if (! req.isAuthenticated()) { return null; }
5 |
6 | var user = req.session.passport.user;
7 |
8 | if (user.startsWith('github:')) {
9 | user = user.replace(/^github:/, '');
10 | user = JSON.parse(user);
11 | return { 'via': 'GitHub', 'user': user[0].value };
12 |
13 | } else if (user.startsWith('email:')) {
14 | user = user.replace(/^email:/, '');
15 | return { 'via': 'Email verification', 'user': user };
16 |
17 | } else {
18 | // Unknown login type, how did this happen?
19 | return null;
20 | }
21 | }
22 | catch(err) {
23 | return null;
24 | }
25 | }
26 |
27 | module.exports = get_user;
28 |
--------------------------------------------------------------------------------
/frontend/lib/get-version-from-filename.js:
--------------------------------------------------------------------------------
1 |
2 | var r = require('rhub-node');
3 |
4 | function get_version_from_filename(filename) {
5 | var re = new RegExp(
6 | '^' + r.valid_package_name +
7 | '_(' + r.valid_package_version + ')[.]tar[.]gz$'
8 | );
9 | var match = re.exec(filename);
10 | if (match) { return match[1]; } else { return null; }
11 | }
12 |
13 | module.exports = get_version_from_filename;
14 |
--------------------------------------------------------------------------------
/frontend/lib/job-to-db.js:
--------------------------------------------------------------------------------
1 |
2 | var urls = require('../lib/urls.js');
3 | var got = require('got');
4 | var url = require('url');
5 |
6 | function job_to_db(job, callback) {
7 | var doc = {
8 | id: job.buildId,
9 | group: job.group,
10 | email: job.email,
11 | package: job.pkg,
12 | version: job.version,
13 | submitted: job.submitted,
14 | platform: job.platforminfo,
15 | scripts: job.scripts || null,
16 | checkArgs: job.checkArgs,
17 | envVars: job.envVars,
18 |
19 | // to be updated
20 | status: 'created',
21 |
22 | // to be filled later
23 | started: null,
24 | build_time: null,
25 | builder_machine: null,
26 |
27 | // these are parsed from the output logs
28 | result: null,
29 | check_output: null,
30 | preperror_log: null
31 | };
32 |
33 | var fullurl = urls.logdb + '/' + doc.id;
34 | var _url = url.parse(fullurl);
35 | var dburl = _url.protocol + '//' + _url.host + _url.path;
36 |
37 | got.put(
38 | dburl,
39 | { body: JSON.stringify(doc), auth: _url.auth },
40 | function(err, reponse) {
41 | callback(err);
42 | });
43 | }
44 |
45 | module.exports = job_to_db;
46 |
--------------------------------------------------------------------------------
/frontend/lib/mail-token.js:
--------------------------------------------------------------------------------
1 |
2 | var multiline = require('multiline');
3 | var send_email = require('../lib/send-email');
4 |
5 | var text_body = multiline(function() { /*
6 | Dear R package developer!
7 |
8 | This is your verification code for your R-hub check submission:
9 |
10 | ${code}
11 |
12 | If you haven't submitted anything to R-hub, please ignore this email.
13 |
14 | Have questions, suggestions or want to report a bug?
15 | Please file an issue ticket at GitHub at
16 | https://github.com/r-hub/rhub/issues Thank You for using the R-hub builder.
17 |
18 | Sincerely,
19 | The R-hub team
20 |
21 | */ });
22 |
23 | function mail_token(email, token, callback) {
24 |
25 | var mail = {
26 | from: '"R-hub builder"
',
27 | to: email,
28 | subject: 'R-hub check email validation',
29 | text: text_body.replace("${code}", token)
30 | };
31 |
32 | send_email(mail, function(error, info) {
33 | if (error) {
34 | console.log(error);
35 | return callback(error);
36 | }
37 | console.log('Message sent: ' + info.response);
38 | callback(null, info.response);
39 | });
40 | }
41 |
42 | module.exports = mail_token;
43 |
--------------------------------------------------------------------------------
/frontend/lib/mail-verification-code.js:
--------------------------------------------------------------------------------
1 |
2 | var multiline = require('multiline');
3 | var uuid = require('uuid');
4 | var urls = require('../lib/urls');
5 | var send_email = require('../lib/send-email');
6 |
7 | var redis = require('redis');
8 | var cli_client = null;
9 |
10 | var text_body = multiline(function() { /*
11 | Dear R package developer!
12 |
13 | This is your verification code for your R-hub builder upload: ${code}
14 |
15 | If you haven't uploaded anything to R-hub, please ignore this email.
16 |
17 | Have questions, suggestions or want to report a bug?
18 | Please file an issue ticket at GitHub at
19 | https://github.com/r-hub/rhub/issues Thank You for using the R-hub builder.
20 |
21 | Sincerely,
22 | The R-hub team
23 |
24 | */ });
25 |
26 | function mail_verification_code(req, callback) {
27 |
28 | var code = uuid.v4().substring(0, 6);
29 |
30 | if (cli_client === null) {
31 | cli_client = redis.createClient(urls.validemail_url);
32 | }
33 |
34 | cli_client.set(req.session.job.email + '-pending', code, function(err) {
35 | if (err) {
36 | // Could not add code to CLI validation DB, but nevertheless
37 | // we continue, because it will still work with the web app
38 | console.log("cannot add validation code to CLI app DB")
39 | }
40 |
41 | req.session.verification = code;
42 |
43 | var mail = {
44 | from: '"R-hub builder" ',
45 | to: req.session.job.email,
46 | subject: 'R-hub builder verification',
47 | text: text_body.replace("${code}", code)
48 | };
49 |
50 | send_email(mail, function(error, info) {
51 | if (error) {
52 | console.log(error);
53 | return callback(error);
54 | }
55 | console.log('Message sent: ' + info.response);
56 | callback(null, info.response);
57 | });
58 | })
59 | }
60 |
61 | module.exports = mail_verification_code;
62 |
--------------------------------------------------------------------------------
/frontend/lib/parse-rhub-log.js:
--------------------------------------------------------------------------------
1 |
2 | // Parse a full R-hub log.
3 | // Returns two things:
4 | // * result: the check result, a JSON dict with entries:
5 | // - status (preperror, error, warning, note, ok)
6 | // - notes
7 | // - warnings
8 | // - errors
9 | // * check_output: the output of R CMD check. This is null if the build
10 | // fails before getting to runing R CMD check.
11 | // * preperror_log: the last 100 lines of the build, if preperror
12 |
13 | function parse_rhub_log(parser, log) {
14 |
15 | if (!parser || parser == "rcmdcheck") {
16 | return parse_rcmdcheck_log(log);
17 | } else if (parser == "sanitizers") {
18 | return parse_sanitizers_log(log);
19 | } else if (parser == "rchk") {
20 | return parse_rchk_log(log);
21 | } else {
22 |
23 | var last_lines = log.split(/\n/)
24 | .slice(-100)
25 | .join('\n');
26 |
27 | return {
28 | 'result': {
29 | 'status': 'parseerror',
30 | 'notes': [],
31 | 'warnings': [],
32 | 'errors': [] },
33 | 'check_output': log,
34 | 'preperror_log': last_lines
35 | };
36 | }
37 | }
38 |
39 | function parse_preperror_log(log) {
40 | var last_lines = log.split(/\n/)
41 | .slice(-100)
42 | .join('\n');
43 |
44 | return {
45 | result: {
46 | 'status': 'preperror',
47 | 'notes': [],
48 | 'warnings': [],
49 | 'errors': [] },
50 | check_output: null,
51 | preperror_log: last_lines
52 | };
53 | }
54 |
55 | // R CMD check logs ------------------------------------------------------
56 |
57 | function parse_rcmdcheck_log(log) {
58 |
59 | var check_start_regex = />>>>>======* Running R CMD check/;
60 | var check_done_regex = /\n[*] DONE[\n ]/;
61 |
62 | // If we don't have this in the log, then we never got to checking
63 | if (check_start_regex.test(log) && check_done_regex.test(log)) {
64 |
65 | // Not sure why it would appear multiple times, but we handle
66 | // it nervertheless
67 | var checklog = log.replace(/\r\n/g, '\n')
68 | .split(check_start_regex)
69 | .slice(1)
70 | .join('\n');
71 |
72 | return parse_rcmdcheck_log2(checklog);
73 |
74 | } else {
75 | return parse_preperror_log(log);
76 | }
77 | }
78 |
79 | function parse_rcmdcheck_log2(log) {
80 |
81 | var last_lines = log.split(/\n/)
82 | .slice(-100)
83 | .join('\n');
84 |
85 | // Drop stuff after the final DONE
86 | var mylog = log.replace(/\n[*] DONE\n\n?(.|\n)*$/, '\n* DONE\n\n');
87 |
88 | var pieces = mylog
89 | .replace(/^NOTE: There was .*\n$/, "")
90 | .replace(/^WARNING: There was .*\n$/, "")
91 | .split("\n* ");
92 |
93 | function filter(pattern) {
94 | var re = new RegExp(pattern);
95 | return pieces.filter(
96 | function(x) { return re.test(x); }
97 | );
98 | }
99 |
100 | var errors = filter(' ERROR(\n|$)');
101 | var warnings = filter(' WARNING(\n|$)');
102 | var notes = filter(' NOTE(\n|$)');
103 | var result;
104 |
105 | if (errors.length) {
106 | result = 'error'
107 | } else if (warnings.length) {
108 | result = 'warning'
109 | } else if (notes.length) {
110 | result = 'note'
111 | } else {
112 | result = 'ok'
113 | }
114 |
115 | return {
116 | 'result': {
117 | 'status': result,
118 | 'notes': notes,
119 | 'warnings': warnings,
120 | 'errors': errors },
121 | 'check_output': mylog,
122 | 'preperror_log': last_lines
123 | };
124 | }
125 |
126 | // Saniters log ----------------------------------------------------------
127 |
128 | function parse_sanitizers_log(log) {
129 |
130 | var check_start_regex = />>>>>======* Running R CMD check/;
131 | var check_done_regex = />>>>>======* Done with R CMD check/;
132 |
133 | if (check_start_regex.test(log) && check_done_regex.test(log)) {
134 | var mylog = log.replace(/\r\n/g, '\n')
135 | .split(check_start_regex)
136 | .slice(1)
137 | .join('\n')
138 | .split(check_done_regex)[0];
139 |
140 | return parse_sanitizers_log2(mylog);
141 | } else {
142 | return parse_preperror_log(log);
143 | }
144 | }
145 |
146 | function parse_sanitizers_log2(log) {
147 |
148 | var last_lines = log.split(/\n/)
149 | .slice(-100)
150 | .join('\n');
151 | var status;
152 |
153 | if (/runtime error:/.test(log)) {
154 | return {
155 | 'result': {
156 | 'status': 'error',
157 | 'notes': [],
158 | 'warnings': [],
159 | 'errors': log },
160 | 'check_output': log,
161 | 'preperror_log': last_lines
162 | };
163 |
164 | } else {
165 | return {
166 | 'result': {
167 | 'status': 'ok',
168 | 'notes': [],
169 | 'warnings': [],
170 | 'errors': [] },
171 | 'check_output': log,
172 | 'preperror_log': null
173 | };
174 | }
175 | }
176 |
177 | // rchk log --------------------------------------------------------------
178 |
179 | function parse_rchk_log(log) {
180 |
181 | var check_start_regex = />>>>>======* Running R CMD check/;
182 | var check_done_regex = />>>>>======* Done with R CMD check/;
183 |
184 | if (check_start_regex.test(log) && check_done_regex.test(log)) {
185 | var mylog = log.replace(/\r\n/g, '\n')
186 | .split(check_start_regex)
187 | .slice(1)
188 | .join('\n')
189 | .split(check_done_regex)[0];
190 |
191 | return parse_rchk_log2(mylog);
192 | } else {
193 | return parse_preperror_log(log);
194 | }
195 | }
196 |
197 | function parse_rchk_log2(log) {
198 |
199 | var last_lines = log.split(/\n/)
200 | .slice(-100)
201 | .join('\n');
202 | var status;
203 |
204 | if (/error/i.test(log) || /warning/i.test(log) ||
205 | /\n\nFunction/.test(log)) {
206 | return {
207 | 'result': {
208 | 'status': 'error',
209 | 'notes': [],
210 | 'warnings': [],
211 | 'errors': log },
212 | 'check_output': log,
213 | 'preperror_log': last_lines
214 | };
215 |
216 | } else {
217 | return {
218 | 'result': {
219 | 'status': 'ok',
220 | 'notes': [],
221 | 'warnings': [],
222 | 'errors': [] },
223 | 'check_output': log,
224 | 'preperror_log': null
225 | };
226 | }
227 | }
228 |
229 | module.exports = parse_rhub_log;
230 |
--------------------------------------------------------------------------------
/frontend/lib/queue-job.js:
--------------------------------------------------------------------------------
1 | var queue_this = require('../lib/queue-this');
2 | var job_to_db = require('../lib/job-to-db');
3 |
4 | function queue_job(job) {
5 | job_to_db(job, function(err) {
6 | // Report error, but continue, anyway. We'll try to add it later
7 | if (err) { console.log("Cannot add new job to DB") }
8 |
9 | // Add it to the queue as well
10 | queue_this('job', job );
11 | });
12 | }
13 |
14 | module.exports = queue_job;
15 |
--------------------------------------------------------------------------------
/frontend/lib/queue-this.js:
--------------------------------------------------------------------------------
1 | var amqp = require('amqplib');
2 | var when = require('when');
3 |
4 | var broker_url = process.env.RABBITMQ_URL ||
5 | 'amqp://q.rhub.me:5672/rhub';
6 |
7 | function queue_this(q, item) {
8 |
9 | amqp.connect(broker_url).then(function(conn) {
10 | return when(conn.createChannel().then(function(ch) {
11 | var ok = ch.assertQueue(q, { durable: true });
12 |
13 | item.added_at = new Date().toISOString();
14 |
15 | return ok.then(function() {
16 | var msg = JSON.stringify(item);
17 | ch.sendToQueue(q, new Buffer(msg), { deliveryMode: true });
18 | return ch.close();
19 | });
20 | })).ensure(function() { conn.close(); });
21 | }).then(null, console.warn);
22 | }
23 |
24 | module.exports = queue_this;
25 |
--------------------------------------------------------------------------------
/frontend/lib/re-status.js:
--------------------------------------------------------------------------------
1 |
2 | var r = require('rhub-node');
3 |
4 | var re_status =
5 | '(' +
6 | '(' + '[-a-zA-Z0-9\._]+' + ')' +
7 | '[.]tar[.]gz' + '-' +
8 | '([a-zA-Z0-9]+)' +
9 | ')';
10 |
11 | module.exports = re_status;
12 |
--------------------------------------------------------------------------------
/frontend/lib/send-email-mailgun.js:
--------------------------------------------------------------------------------
1 |
2 | var urls = require('../lib/urls');
3 |
4 | function send_email_mailgun(mail, callback) {
5 | var mailgun = require('mailgun-js')(
6 | { apiKey: urls.mailgun_api_key, domain: urls.mailgun_domain });
7 | mailgun.messages().send(mail, callback);
8 | }
9 |
10 | module.exports = send_email_mailgun;
11 |
--------------------------------------------------------------------------------
/frontend/lib/send-email-smtp.js:
--------------------------------------------------------------------------------
1 |
2 | var urls = require('../lib/urls');
3 | var nodemailer = require('nodemailer');
4 |
5 | function send_email_smtp(mail, callback) {
6 | if (! urls.smtp_server) { return callback("No mail server"); }
7 |
8 | var config = {
9 | host: urls.smtp_server,
10 | port: urls.smtp_port,
11 | secure: urls.smtp_tls_required
12 | }
13 |
14 | if (!! urls.smtp_username || !! urls.smtp_password) {
15 | config.auth = {
16 | user: urls.smtp_username || '',
17 | pass: urls.smtp_password || '' };
18 | }
19 |
20 | var transporter = nodemailer.createTransport(config);
21 | transporter.sendMail(mail, callback);
22 | }
23 |
24 | module.exports = send_email_smtp;
25 |
--------------------------------------------------------------------------------
/frontend/lib/send-email.js:
--------------------------------------------------------------------------------
1 |
2 | var urls = require('../lib/urls');
3 |
4 | function send_email(mail, callback) {
5 | if (urls.email_mode === 'mailgun') {
6 | var send_email_mailgun = require('../lib/send-email-mailgun');
7 | send_email_mailgun(mail, callback);
8 | } else {
9 | var send_email_smtp = require('../lib/send-email-smtp');
10 | send_email_smtp(mail, callback);
11 | }
12 | }
13 |
14 | module.exports = send_email;
15 |
--------------------------------------------------------------------------------
/frontend/lib/update-log.js:
--------------------------------------------------------------------------------
1 |
2 | var got = require('got');
3 | var urls = require('../lib/urls');
4 | var jenkins = require('jenkins')( {
5 | baseUrl: urls.jenkins, crumbIssuer: true });
6 | var parse_rhub_log = require('../lib/parse-rhub-log');
7 |
8 | function update_log(id, state, time, body, callback) {
9 | // Need to get the build metadata and the output from Jenkins
10 | jenkins.build.get(id, 'lastBuild', function(err, data) {
11 | if (err) { return callback("Cannot find Jenkins job"); }
12 |
13 | // We cannot use data.duration, because at this point the build
14 | // is still running, and it is set to 0
15 | body.build_time = new Date() - new Date(body.started);
16 | body.builder_machine = data.builtOn;
17 |
18 | jenkins.build.log('Jobs/' + id, 'lastBuild', function(err, log) {
19 | if (err) { return callback("Cannot get Jenkins log"); }
20 | var parsed = parse_rhub_log(body.platform["output-parser"], log);
21 | body.result = parsed.result;
22 | body.check_output = parsed.check_output;
23 | body.preperror_log = parsed.preperror_log;
24 |
25 | if (data.result == "ABORTED") {
26 | body.status = "aborted";
27 | body.result.status = "aborted"; // for consistency
28 | } else {
29 | body.status = parsed.result.status;
30 | }
31 |
32 | callback(null, body);
33 | });
34 | });
35 | }
36 |
37 | module.exports = update_log;
38 |
--------------------------------------------------------------------------------
/frontend/lib/urls.js:
--------------------------------------------------------------------------------
1 |
2 | require('dotenv').config();
3 |
4 | var urls = {
5 | 'jenkins': process.env.JENKINS_URL || 'http://jenkins.url',
6 | 'validemail_url':
7 | process.env.DOKKU_REDIS_GREEN_URL ||
8 | process.env.DOKKU_REDIS_PURPLE_URL ||
9 | process.env.REDIS_EMAIL_URL,
10 | 'logdb': process.env.LOGDB_URL || 'http://logdb.url',
11 |
12 | // Email settings
13 | 'email_mode': process.env.RHUB_EMAIL_MODE || 'mailgun',
14 | 'mailgun_api_key': process.env.MAILGUN_API_KEY || 'key',
15 | 'mailgun_domain': process.env.MAILGUN_DOMAIN || 'rhub.io',
16 | 'smtp_server': process.env.RHUB_SMTP_SERVER,
17 | 'smtp_username': process.env.RHUB_SMTP_USERNAME,
18 | 'smtp_password': process.env.RHUB_SMTP_PASSWORD,
19 | 'smtp_port': process.env.RHUB_SMTP_PORT || 25,
20 | // TLS required by default, set to 'false' (w/o quotes) to disable
21 | 'smtp_tls_required': ! (process.env.RHUB_SMTP_TLS_REQUIRED === 'false')
22 | };
23 |
24 | module.exports = urls;
25 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rhub-frontend",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "node ./bin/www"
7 | },
8 | "engines": {
9 | "node": "8.12.x"
10 | },
11 | "dependencies": {
12 | "amqplib": "^0.4.0",
13 | "array-flatten": "^2.1.1",
14 | "async": "^2.1.2",
15 | "body-parser": "^1.18.3",
16 | "byline": "^4.2.1",
17 | "connect-gzip-static": "^1.0.0",
18 | "connect-redis": "^3.0.2",
19 | "cookie-parser": "~1.3.5",
20 | "debug": "^4.0.0",
21 | "dotenv": "^5.0.1",
22 | "express": "^4.16.4",
23 | "express-session": "^1.13.0",
24 | "got": "^5.6.0",
25 | "gunzip-maybe": "^1.3.1",
26 | "hogan-express": "^0.5.2",
27 | "hogan.js": "^3.0.2",
28 | "is-array": "^1.0.1",
29 | "jenkins": "^0.19.0",
30 | "jenkins-log-stream": "^2.0.0",
31 | "left-pad": "^1.1.0",
32 | "mailgun-js": "^0.20.0",
33 | "morgan": "^1.9.1",
34 | "multer": "^1.1.0",
35 | "multiline": "^1.0.2",
36 | "mustache": "^2.2.1",
37 | "nano": "^6.2.0",
38 | "nodemailer": "^4.6.8",
39 | "passport": "^0.3.2",
40 | "passport-github": "^1.1.0",
41 | "pretty-ms": "^2.1.0",
42 | "rdesc-parser": "^3.0.1",
43 | "redis": "^2.6.2",
44 | "rhub-node": "^1.1.0",
45 | "serve-favicon": "^2.5.0",
46 | "tar-stream": "^1.3.2",
47 | "url": "^0.11.0",
48 | "uuid": "^2.0.2",
49 | "when": "^3.7.5"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/frontend/public/.well-known/acme-challenge/bQS91jPgESpdE1y_YFko_5yLMgn8KGEdpGcEuDIE6Ng:
--------------------------------------------------------------------------------
1 | bQS91jPgESpdE1y_YFko_5yLMgn8KGEdpGcEuDIE6Ng.Bw0n2-AR3k8OMKN0b_LhuUVpJlkG2O-O4mLtsuCdkno
2 |
--------------------------------------------------------------------------------
/frontend/public/.well-known/acme-challenge/qZ8CC8EFZACYXcGVFpHxR4-w-iQWorI2vnx30rx_AD4:
--------------------------------------------------------------------------------
1 | qZ8CC8EFZACYXcGVFpHxR4-w-iQWorI2vnx30rx_AD4.Bw0n2-AR3k8OMKN0b_LhuUVpJlkG2O-O4mLtsuCdkno
2 |
--------------------------------------------------------------------------------
/frontend/public/.well-known/acme-challenge/wyl61bg_fpnNdT2MAzovERs1ltM1FK0ySKh_t2Vl0DE:
--------------------------------------------------------------------------------
1 | wyl61bg_fpnNdT2MAzovERs1ltM1FK0ySKh_t2Vl0DE.Bw0n2-AR3k8OMKN0b_LhuUVpJlkG2O-O4mLtsuCdkno
2 |
--------------------------------------------------------------------------------
/frontend/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r-hub/rhub-server/9fd222423803e87de80fb964a796d62f514a7769/frontend/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/frontend/public/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r-hub/rhub-server/9fd222423803e87de80fb964a796d62f514a7769/frontend/public/android-chrome-512x512.png
--------------------------------------------------------------------------------
/frontend/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r-hub/rhub-server/9fd222423803e87de80fb964a796d62f514a7769/frontend/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/frontend/public/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #da532c
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/frontend/public/data/platforms.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "debian-gcc-devel",
4 | "description": "Debian Linux, R-devel, GCC",
5 | "cran-name": "r-devel-linux-x86_64-debian-gcc",
6 | "rversion": "r-devel",
7 | "os-type": "Linux",
8 | "cpu-type": "x86_64",
9 | "os-info": "Debian GNU/Linux testing",
10 | "compilers": "gcc version 8.3.0 (Debian 8.3.0-5)",
11 | "docker-image": "debian-gcc-devel",
12 | "sysreqs-platform": "linux-x86_64-debian-gcc",
13 | "categories": ["Linux"],
14 | "node-labels": ["linux"],
15 | "queue": "linux"
16 | },
17 |
18 | {
19 | "name": "debian-gcc-devel-nold",
20 | "description": "Debian Linux, R-devel, GCC, no long double",
21 | "cran-name": null,
22 | "rversion": "r-devel",
23 | "os-type": "Linux",
24 | "cpu-type": "x86_64",
25 | "os-info": "Debian GNU/Linux testing",
26 | "compilers": "gcc version 8.3.0 (Debian 8.3.0-5)",
27 | "docker-image": "debian-gcc-devel-nold",
28 | "sysreqs-platform": "linux-x86_64-debian-gcc",
29 | "categories": ["Linux"],
30 | "node-labels": ["linux"],
31 | "queue": "linux"
32 | },
33 |
34 | {
35 | "name": "debian-gcc-release",
36 | "description": "Debian Linux, R-release, GCC",
37 | "cran-name": "r-release-linux-x86_64",
38 | "rversion": "r-release",
39 | "os-type": "Linux",
40 | "cpu-type": "x86_64",
41 | "os-info": "Debian GNU/Linux testing",
42 | "compilers": "gcc version 8.3.0 (Debian 8.3.0-5)",
43 | "docker-image": "debian-gcc-release",
44 | "sysreqs-platform": "linux-x86_64-debian-gcc",
45 | "categories": ["Linux"],
46 | "node-labels": ["linux"],
47 | "queue": "linux"
48 | },
49 |
50 | {
51 | "name": "debian-gcc-patched",
52 | "description": "Debian Linux, R-patched, GCC",
53 | "cran-name": "r-patched-linux-x86_64",
54 | "rversion": "r-patched",
55 | "os-type": "Linux",
56 | "cpu-type": "x86_64",
57 | "os-info": "Debian GNU/Linux testing",
58 | "compilers": "gcc version 8.3.0 (Debian 8.3.0-5)",
59 | "docker-image": "debian-gcc-patched",
60 | "sysreqs-platform": "linux-x86_64-debian-gcc",
61 | "categories": ["Linux"],
62 | "node-labels": ["linux"],
63 | "queue": "linux"
64 | },
65 |
66 | {
67 | "name": "debian-clang-devel",
68 | "description": "Debian Linux, R-devel, clang, ISO-8859-15 locale",
69 | "cran-name": "r-devel-linux-x86_64-debian-clang",
70 | "rversion": "r-devel",
71 | "os-type": "Linux",
72 | "cpu-type": "x86_64",
73 | "os-info": "Debian GNU/Linux testing",
74 | "compilers": "clang version 7.0.1-8 (tags/RELEASE_701/final)",
75 | "docker-image": "debian-clang-devel",
76 | "sysreqs-platform": "linux-x86_64-debian-clang",
77 | "categories": ["Linux"],
78 | "node-labels": ["linux"],
79 | "queue": "linux"
80 | },
81 |
82 | {
83 | "name": "fedora-gcc-devel",
84 | "description": "Fedora Linux, R-devel, GCC",
85 | "cran-name": "r-devel-linux-x86_64-fedora-gcc",
86 | "rversion": "r-devel",
87 | "os-type": "Linux",
88 | "cpu-type": "x86_64",
89 | "os-info": "Fedora 24",
90 | "compilers": "GCC 6.1.1",
91 | "docker-image": "fedora-gcc-devel",
92 | "sysreqs-platform": "linux-x86_64-fedora-gcc",
93 | "categories": ["Linux"],
94 | "node-labels": ["linux"],
95 | "queue": "linux"
96 | },
97 |
98 | {
99 | "name": "fedora-clang-devel",
100 | "description": "Fedora Linux, R-devel, clang, gfortran",
101 | "cran-name": "r-devel-linux-x86_64-fedora-clang",
102 | "rversion": "r-devel",
103 | "os-type": "Linux",
104 | "cpu-type": "x86_64",
105 | "os-info": "Fedora 24",
106 | "compilers": "clang version 3.8.0; GNU Fortran 6.1.1",
107 | "docker-image": "fedora-clang-devel",
108 | "sysreqs-platform": "linux-x86_64-fedora-clang",
109 | "categories": ["Linux"],
110 | "node-labels": ["linux"],
111 | "queue": "linux"
112 | },
113 |
114 | {
115 | "name": "ubuntu-gcc-devel",
116 | "description": "Ubuntu Linux 16.04 LTS, R-devel, GCC",
117 | "cran-name": null,
118 | "rversion": "r-devel",
119 | "os-type": "Linux",
120 | "cpu-type": "x86_64",
121 | "os-info": "Ubuntu 16.04 LTS",
122 | "compilers": "GCC 5.3.1",
123 | "docker-image": "ubuntu-gcc-devel",
124 | "sysreqs-platform": "linux-x86_64-ubuntu-gcc",
125 | "categories": ["Linux"],
126 | "node-labels": ["linux"],
127 | "queue": "linux"
128 | },
129 |
130 | {
131 | "name": "ubuntu-gcc-release",
132 | "description": "Ubuntu Linux 16.04 LTS, R-release, GCC",
133 | "cran-name": null,
134 | "rversion": "r-release",
135 | "os-type": "Linux",
136 | "cpu-type": "x86_64",
137 | "os-info": "Ubuntu 16.04 LTS",
138 | "compilers": "GCC 5.3.1",
139 | "docker-image": "ubuntu-gcc-release",
140 | "sysreqs-platform": "linux-x86_64-ubuntu-gcc",
141 | "categories": ["Linux"],
142 | "node-labels": ["linux"],
143 | "queue": "linux"
144 | },
145 |
146 | {
147 | "name": "linux-x86_64-centos6-epel",
148 | "description": "CentOS 6, stock R from EPEL",
149 | "cran-name": null,
150 | "rversion": "r-release",
151 | "os-type": "Linux",
152 | "cpu-type": "x86_64",
153 | "os-info": "CentOS 6",
154 | "compilers": "GCC 4.4.x",
155 | "docker-image": "centos6-epel",
156 | "sysreqs-platform": "linux-x86_64-centos6-epel",
157 | "categories": ["Linux"],
158 | "node-labels": ["linux"],
159 | "queue": "linux"
160 | },
161 |
162 | {
163 | "name": "linux-x86_64-centos6-epel-rdt",
164 | "description": "CentOS 6 with Redhat Developer Toolset, R from EPEL",
165 | "cran-name": null,
166 | "rversion": "r-release",
167 | "os-type": "Linux",
168 | "cpu-type": "x86_64",
169 | "os-info": "CentOS 6",
170 | "compilers": "GCC 5.2.1",
171 | "docker-image": "centos6-epel-rdt",
172 | "sysreqs-platform": "linux-x86_64-centos6-epel",
173 | "categories": ["Linux"],
174 | "node-labels": ["linux"],
175 | "queue": "linux"
176 | },
177 |
178 | {
179 | "name": "linux-x86_64-rocker-gcc-san",
180 | "description": "Debian Linux, R-devel, GCC ASAN/UBSAN",
181 | "cran-name": null,
182 | "rversion": "r-devel",
183 | "os-type": "Linux",
184 | "cpu-type": "x86_64",
185 | "os-info": "Debian GNU/Linux testing",
186 | "compilers": "GCC 5.4.0 (Debian 5.4.0-4)",
187 | "docker-image": "rocker-gcc-san",
188 | "sysreqs-platform": "linux-x86_64-debian-gcc",
189 | "output-parser": "sanitizers",
190 | "categories": ["Checks for compiled code"],
191 | "node-labels": ["linux"],
192 | "queue": "linux"
193 | },
194 |
195 | {
196 | "name": "windows-x86_64-oldrel",
197 | "description": "Windows Server 2008 R2 SP1, R-oldrel, 32/64 bit",
198 | "cran-name": "r-oldrel-windows-ix86+x86_64",
199 | "rversion": "r-oldrel",
200 | "os-type": "Windows",
201 | "cpu-type": "x86_64",
202 | "os-info": "Windows Server 2008 R2 SP1",
203 | "compilers": "GCC 4.9.3, Rtools 3.5",
204 | "docker-image": null,
205 | "sysreqs-platform": "windows-2008",
206 | "categories": ["Windows"],
207 | "node-labels": ["windows", "rtools3"],
208 | "queue": "rtools3"
209 | },
210 |
211 | {
212 | "name": "windows-x86_64-release",
213 | "description": "Windows Server 2008 R2 SP1, R-release, 32/64 bit",
214 | "cran-name": "r-release-windows-ix86+x86_64",
215 | "rversion": "r-release",
216 | "os-type": "Windows",
217 | "cpu-type": "x86_64",
218 | "os-info": "Windows Server 2008 R2 SP1",
219 | "compilers": "GCC 8.3.0, Rtools 4.0",
220 | "docker-image": null,
221 | "sysreqs-platform": "windows-2008",
222 | "categories": ["Windows"],
223 | "node-labels": ["windows", "rtools4"],
224 | "queue": "rtools4"
225 | },
226 |
227 | {
228 | "name": "windows-x86_64-patched",
229 | "description": "Windows Server 2008 R2 SP1, R-patched, 32/64 bit",
230 | "cran-name": null,
231 | "rversion": "r-patched",
232 | "os-type": "Windows",
233 | "cpu-type": "x86_64",
234 | "os-info": "Windows Server 2008 R2 SP1",
235 | "compilers": "GCC 8.3.0, Rtools 4.0",
236 | "docker-image": null,
237 | "sysreqs-platform": "windows-2008",
238 | "categories": ["Windows"],
239 | "node-labels": ["windows", "rtools4"],
240 | "queue": "rtools4"
241 | },
242 |
243 | {
244 | "name": "windows-x86_64-devel",
245 | "description": "Windows Server 2008 R2 SP1, R-devel, 32/64 bit",
246 | "cran-name": "r-devel-windows-ix86+x86_64",
247 | "rversion": "r-devel",
248 | "os-type": "Windows",
249 | "cpu-type": "x86_64",
250 | "os-info": "Windows Server 2008 R2 SP1",
251 | "compilers": "GCC 8.3.0, Rtools 4.0",
252 | "docker-image": null,
253 | "sysreqs-platform": "windows-2008",
254 | "categories": ["Windows"],
255 | "node-labels": ["windows", "rtools4"],
256 | "queue": "rtools4"
257 | },
258 |
259 | {
260 | "name": "macos-elcapitan-release",
261 | "description": "macOS 10.11 El Capitan, R-release (experimental)",
262 | "cran-name": "r-release-osx-x86_64",
263 | "rversion": "r-release",
264 | "os-type": "macOS",
265 | "macos-version": "elcapitan",
266 | "cpu-type": "x86_64",
267 | "os-info": "Mac OS X 10.11.6 15G1217",
268 | "compilers": "Apple LLVM version 8.0 (clang-800.0.42.1); GNU Fortran 4.2.3",
269 | "docker-image": null,
270 | "sysreqs-platform": "osx-x86_64-clang",
271 | "categories": ["macOS"],
272 | "node-labels": ["macos", "elcapitan", "r-release"],
273 | "queue": "elcapitan"
274 | },
275 |
276 | {
277 | "name": "ubuntu-rchk",
278 | "description": "Ubuntu Linux 16.04 LTS, R-devel with rchk",
279 | "cran-name": null,
280 | "rversion": "r-devel",
281 | "os-type": "Linux",
282 | "cpu-type": "x86_64",
283 | "os-info": "Ubuntu 16.04 LTS",
284 | "compilers": "clang 3.8.0-2ubuntu4",
285 | "docker-image": "ubuntu-rchk",
286 | "sysreqs-platform": "linux-x86_64-ubuntu-gcc",
287 | "output-parser": "rchk",
288 | "categories": ["Checks for compiled code"],
289 | "node-labels": ["linux"],
290 | "queue": "linux"
291 | },
292 | ]
293 |
--------------------------------------------------------------------------------
/frontend/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r-hub/rhub-server/9fd222423803e87de80fb964a796d62f514a7769/frontend/public/favicon-16x16.png
--------------------------------------------------------------------------------
/frontend/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r-hub/rhub-server/9fd222423803e87de80fb964a796d62f514a7769/frontend/public/favicon-32x32.png
--------------------------------------------------------------------------------
/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r-hub/rhub-server/9fd222423803e87de80fb964a796d62f514a7769/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/frontend/public/javascripts/vendor/bootstrap.min.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r-hub/rhub-server/9fd222423803e87de80fb964a796d62f514a7769/frontend/public/javascripts/vendor/bootstrap.min.js.gz
--------------------------------------------------------------------------------
/frontend/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r-hub/rhub-server/9fd222423803e87de80fb964a796d62f514a7769/frontend/public/logo.png
--------------------------------------------------------------------------------
/frontend/public/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r-hub/rhub-server/9fd222423803e87de80fb964a796d62f514a7769/frontend/public/mstile-150x150.png
--------------------------------------------------------------------------------
/frontend/public/rhub-header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r-hub/rhub-server/9fd222423803e87de80fb964a796d62f514a7769/frontend/public/rhub-header.png
--------------------------------------------------------------------------------
/frontend/public/rhub-square-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r-hub/rhub-server/9fd222423803e87de80fb964a796d62f514a7769/frontend/public/rhub-square-white.png
--------------------------------------------------------------------------------
/frontend/public/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 | Created by potrace 1.11, written by Peter Selinger 2001-2013
9 |
10 |
12 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/frontend/public/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "short_name": "",
4 | "icons": [
5 | {
6 | "src": "/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#ffffff",
17 | "background_color": "#ffffff",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/frontend/public/stylesheets/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r-hub/rhub-server/9fd222423803e87de80fb964a796d62f514a7769/frontend/public/stylesheets/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/frontend/public/stylesheets/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r-hub/rhub-server/9fd222423803e87de80fb964a796d62f514a7769/frontend/public/stylesheets/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/frontend/public/stylesheets/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r-hub/rhub-server/9fd222423803e87de80fb964a796d62f514a7769/frontend/public/stylesheets/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/frontend/public/stylesheets/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r-hub/rhub-server/9fd222423803e87de80fb964a796d62f514a7769/frontend/public/stylesheets/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/frontend/public/stylesheets/logstyle.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r-hub/rhub-server/9fd222423803e87de80fb964a796d62f514a7769/frontend/public/stylesheets/logstyle.css
--------------------------------------------------------------------------------
/frontend/public/stylesheets/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 50px;
3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
4 | }
5 |
6 | a {
7 | color: #00B7FF;
8 | }
9 |
10 | .btn-file {
11 | position: relative;
12 | overflow: hidden;
13 | }
14 | .btn-file input[type=file] {
15 | position: absolute;
16 | top: 0;
17 | right: 0;
18 | min-width: 100%;
19 | min-height: 100%;
20 | font-size: 100px;
21 | text-align: right;
22 | filter: alpha(opacity=0);
23 | opacity: 0;
24 | outline: none;
25 | background: white;
26 | cursor: inherit;
27 | display: block;
28 | }
29 |
30 | input[readonly] {
31 | background-color: white !important;
32 | cursor: text !important;
33 | }
34 |
35 | .terms {
36 | font-size: 140%;
37 | }
38 |
39 | .content {
40 | margin-top: 30px;
41 | }
42 |
43 | .fluidIframe {
44 | position: relative;
45 | /* proportion value to aspect ratio 16:9 (9 / 16 = 0.5625 or 56.25%) */
46 | padding-bottom: 56.25%;
47 | padding-top: 30px;
48 | height: 0;
49 | overflow: hidden;
50 | }
51 |
52 | .fluidIframe iframe {
53 | position: absolute;
54 | top: 0;
55 | left: 0;
56 | width: 100%;
57 | height: 100%;
58 | border: 0;
59 | }
60 |
61 |
62 | .log-block {
63 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
64 | padding-left: 0px;
65 | line-height: 130%;
66 | }
67 |
68 | .log-h1 {
69 | font-weight: bold;
70 | font-size: 120%;
71 | margin-top: 20px;
72 | margin-bottom: 10px;
73 | }
74 |
75 | .log-output {
76 | color: #aaa;
77 | padding-top: 10px;
78 | padding-bottom: 10px;
79 | margin-bottom: 10px;
80 | }
81 |
82 | .log-input {
83 | margin-bottom: 10px;
84 | color: #006;
85 | }
86 |
87 | .log-lineno {
88 | color: rgba(0,0,0,0.4);
89 | white-space: pre;
90 | min-width: 30px;
91 | padding-right: 10px;
92 | padding-left: 0px;
93 | text-decoration: none;
94 | }
95 |
96 | .log-lineno:hover {
97 | color: rgba(0,0,0,0.7);
98 | }
99 |
100 | .log-line {
101 | margin-top: 0px;
102 | margin-bottom: 0px;
103 | padding-left: 58px;
104 | text-indent: -58px;
105 | }
106 |
107 | .verify-gh {
108 | text-align: center;
109 | }
110 |
111 | .huge-gh {
112 | font-size: 196px;
113 | }
114 |
115 | .large-gh {
116 | font-size: 64px;
117 | }
118 |
119 | .well-verify-1 {
120 | min-height: 150px;
121 | }
122 |
123 | .well-verify-2 {
124 | min-height: 300px;
125 | }
126 |
127 | .vcenter {
128 | display: table-cell;
129 | vertical-align: middle;
130 | }
131 |
132 | .vcenter-outer {
133 | display: table;
134 | }
135 |
--------------------------------------------------------------------------------
/frontend/public/stylesheets/vendor/bootstrap-theme.min.css.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r-hub/rhub-server/9fd222423803e87de80fb964a796d62f514a7769/frontend/public/stylesheets/vendor/bootstrap-theme.min.css.gz
--------------------------------------------------------------------------------
/frontend/public/stylesheets/vendor/bootstrap.min.css.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r-hub/rhub-server/9fd222423803e87de80fb964a796d62f514a7769/frontend/public/stylesheets/vendor/bootstrap.min.css.gz
--------------------------------------------------------------------------------
/frontend/routes/build.js:
--------------------------------------------------------------------------------
1 |
2 | var express = require('express');
3 | var router = express.Router();
4 | var re_status = require('../lib/re-status');
5 | var urls = require('../lib/urls');
6 | var db = require('nano')(urls.logdb);
7 | var update_log = require('../lib/update-log');
8 | var email_notification = require('../lib/email-notification');
9 |
10 | function keep(x) { return '(' + x + ')'; }
11 |
12 | var re_state = '[-A-Za-z]+';
13 | var re_time = '[-a-zA-Z0-9:\\.]+';
14 | var re = '^/' + keep(re_state) + '/' + re_status + '/' +
15 | keep(re_time) + '$';
16 |
17 | // The job reports its state here. E.g.
18 | // /in-progress/gpg_0.1.tar.gz-<...>/2016-10-10T02:59:00Z
19 | // /success/igraph-1.0.1.tar.gz-<...>/2016-10-10T07:38:39Z
20 | // Possible state values:
21 | // created, in-progress, success, failure, aborted, unstable, not_built
22 | // These are the Jenkins build status values, except for in-progress
23 | // and created.
24 | //
25 | // This should be ideally a POST request, but it is just easier to make
26 | // a GET from the post-build Groovy script.
27 |
28 | router.get(new RegExp(re), function(req, res) {
29 | var state = req.params[0];
30 | var id = req.params[1]; // re_status is captured already
31 | var time = req.params[4];
32 |
33 | function handle_error(err) {
34 | if (err) {
35 | error(500, "Cannot update build " + id);
36 | } else {
37 | ok(id + " updated");
38 | }
39 | }
40 |
41 | function error(status, message) {
42 | var msg = { "status": "error", "message": message };
43 | res.set(status)
44 | .end(JSON.stringify(msg));
45 | }
46 |
47 | function ok(message) {
48 | res.set(200)
49 | .end(JSON.stringify({ "status": "ok" }));
50 | }
51 |
52 | res.set('Content-Type', 'application/json');
53 |
54 | db.get(id, function(err, body) {
55 | if (err) { return error(res, 404, "Cannot find build " + id); }
56 |
57 | // We handle the simple 'start' update here
58 | if (state == "in-progress") {
59 | body.status = state;
60 | // This is to use the same machine's clock
61 | body.started = new Date();
62 | db.insert(body, function(err) {
63 | return handle_error(err);
64 | });
65 |
66 | // More complicated updates need getting the Jenkins job and log,
67 | // we handle these separately
68 | } else {
69 | update_log(id, state, time, body, function(err, body2) {
70 | if (err) { return handle_error(err); }
71 | db.insert(body2, function(err) {
72 | if (err) { return handle_error(err); }
73 | email_notification(body2, function(err) {
74 | return handle_error(err);
75 | });
76 | });
77 | });
78 | }
79 | });
80 | });
81 |
82 | module.exports = router;
83 |
--------------------------------------------------------------------------------
/frontend/routes/check.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var router = express.Router();
3 |
4 | router.get('/', function(req, res) {
5 | res.set('Content-Type', 'text/plain')
6 | .send('I am alive')
7 | .end();
8 | })
9 |
10 | module.exports = router;
11 |
--------------------------------------------------------------------------------
/frontend/routes/index.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var router = express.Router();
3 | var get_user = require('../lib/get-user');
4 | var fs = require('fs');
5 | var flatten = require('array-flatten');
6 |
7 | /* GET home page. */
8 | router.get('/', function(req, res, next) {
9 | res.render('index', { 'advanced': false, 'user': get_user(req) });
10 | });
11 |
12 | router.get('/advanced', function(req, res, next) {
13 | fs.readFile(
14 | './public/data/platforms.json',
15 | 'utf8',
16 | function(err, json) {
17 | if (err) { console.log(err); throw(err); }
18 | var platforms = JSON.parse(json);
19 | var user = null;
20 |
21 | platform_groups = get_platform_groups(platforms);
22 |
23 | res.render('index', {
24 | 'advanced': true,
25 | platforms: platforms,
26 | groups: platform_groups,
27 | user: get_user(req)
28 | });
29 | }
30 | );
31 | });
32 |
33 | router.get('/about.html', function(req, res) {
34 | res.render('about', { 'user': get_user(req) });
35 | });
36 |
37 | router.get('/terms.html', function(req, res) {
38 | res.render('terms', { 'user': get_user(req) });
39 | });
40 |
41 | function unique(value, index, self) {
42 | return self.indexOf(value) === index;
43 | }
44 |
45 | function get_platform_groups(platforms) {
46 | var groups = platforms
47 | .map(function(x) { return x.categories; });
48 | // This just happens to be OK
49 | var groupnames = flatten(groups).sort().reverse();
50 |
51 | return groupnames.filter(unique)
52 | .map(function(g) {
53 | return { "name": g,
54 | "value": platforms.filter(function(p) {
55 | return p.categories.indexOf(g) != -1
56 | })
57 | };
58 | });
59 | }
60 |
61 | module.exports = router;
62 |
--------------------------------------------------------------------------------
/frontend/routes/job.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var router = express.Router();
3 | var multer = require('multer');
4 | var rhub = require('rhub-node');
5 | var create_job = require('../lib/create-job');
6 | var queue_job = require('../lib/queue-job');
7 | var get_user = require('../lib/get-user');
8 | var auth_ok = require('../lib/auth-ok');
9 | var uploader = multer({
10 | dest: __dirname + '/../uploads/'
11 | })
12 |
13 | // POST request uploads the package
14 | //
15 | // If the session is authenticated, and the email
16 | // addresses match, we queue the job.
17 | //
18 | // Otherwise we store the job in the session and
19 | // go to authenticate. The authentication
20 | // will call back to /job again, but with a GET request.
21 | // The job data is still in the session. If the emails
22 | // match we queue the job, otherwise an error page is
23 | // returned.
24 |
25 | router.post(
26 | '/',
27 | uploader.single('package'),
28 | function(req, res, next) {
29 | create_job(req, function(err, job) {
30 | if (err) {
31 | res.render(
32 | "badpackage",
33 | { 'error': err, user: get_user(req) }
34 | );
35 | } else {
36 | if (auth_ok(req, job)) {
37 | queue_job(job);
38 | job.user = get_user(req);
39 | res.redirect("/status/" + job.buildId);
40 | } else {
41 | req.session.job = job;
42 | req.session.job.user = get_user(req)
43 | res.render('verify', req.session.job);
44 | }
45 | }
46 | })
47 | }
48 | );
49 |
50 | router.get(
51 | '/',
52 | function(req, res, next) {
53 | if (auth_ok(req, req.session.job)) {
54 | queue_job(req.session.job);
55 | req.session.job.user = get_user(req);
56 | res.redirect("/status/" + req.session.job.buildId);
57 | } else {
58 | res.render(
59 | 'badpackage',
60 | { 'error': 'cannot verify email address',
61 | 'package': req.session.job['package'],
62 | 'job': req.session.job,
63 | 'user': get_user(req)
64 | }
65 | );
66 | }
67 | }
68 | );
69 |
70 | module.exports = router;
71 |
--------------------------------------------------------------------------------
/frontend/routes/login.js:
--------------------------------------------------------------------------------
1 |
2 | var express = require('express');
3 | var router = express.Router();
4 | var passport = require('passport');
5 | var mail_verification_code = require('../lib/mail-verification-code');
6 | var get_user = require('../lib/get-user');
7 |
8 | var queue_job = require('../lib/queue-job');
9 |
10 | router.get('/login', function(req, res) {
11 | req.session.job.user = get_user(req);
12 | res.render("verify", req.session.job);
13 | });
14 |
15 | router.get('/login/github', passport.authenticate('github'));
16 |
17 | router.get(
18 | '/login/github/callback',
19 | function(req, res, next) {
20 | passport.authenticate(
21 | 'github',
22 | { successReturnToOrRedirect: '/job', failureRedirect: '/job' }
23 | )(req, res, next);
24 | }
25 | );
26 |
27 | router.get(
28 | '/logout',
29 | function(req, res) {
30 | req.logout();
31 | res.redirect('/');
32 | }
33 | );
34 |
35 | router.get(
36 | '/login/sendcode',
37 | function(req, res) {
38 | if (!req.session || !req.session.job) {
39 | return res.status(404)
40 | .send("No package upload?");
41 | }
42 | mail_verification_code(req, function(err, done) {
43 | if (err) {
44 | res.status(404)
45 | .send("Cannot send email");
46 | } else {
47 | res.send("OK");
48 | }
49 | })
50 | }
51 | );
52 |
53 | router.post(
54 | '/login/submitcode',
55 | function(req, res) {
56 | if (!req.session || !req.session.job) { res.redirect('/'); }
57 | var submitted = req.body.code;
58 | if (submitted == req.session.verification) {
59 | queue_job(req.session.job);
60 | if (!req.session.passport) { req.session.passport = { }; }
61 | req.session.passport.user = 'email:' + req.session.job.email;
62 | req.session.job.user = get_user(req);
63 | res.redirect("/status/" + req.session.job.buildId);
64 | delete req.session.job;
65 | } else {
66 | res.render('verify', req.session.job);
67 | }
68 | }
69 | );
70 |
71 | module.exports = router;
72 |
--------------------------------------------------------------------------------
/frontend/routes/status.js:
--------------------------------------------------------------------------------
1 |
2 | var express = require('express');
3 | var router = express.Router();
4 | var JenkinsLogStream = require('jenkins-log-stream');
5 | var get_user = require('../lib/get-user');
6 | var byline = require('byline');
7 | var LogFilter = require('../lib/filter-log');
8 | var SimpleLogFilter = require('../lib/filter-log').SimpleLogFilter;
9 | var re_status = require('../lib/re-status');
10 | const prettyMs = require('pretty-ms');
11 | var urls = require('../lib/urls');
12 |
13 | // This is the main page. The actual log will be in an IFrame
14 |
15 | router.get(new RegExp('^/' + re_status + '$'), function(req, res) {
16 | var name = req.params[0];
17 |
18 | var iframeUrl = req.originalUrl.replace('/status', '/status/log');
19 |
20 | res.render(
21 | 'status',
22 | { 'buildId': name,
23 | 'user': get_user(req),
24 | 'logUrl': iframeUrl
25 | }
26 | );
27 | });
28 |
29 | function stream(log, res, filter) {
30 | var log_by_line = byline(log);
31 | if (filter === undefined) { filter = new LogFilter(); }
32 |
33 | var st = byline(log);
34 |
35 | if (filter !== null) { st = st.pipe(filter); }
36 |
37 | st.pipe(res)
38 | .on("error", function(e) { res.set(404).end("No such job"); });
39 | }
40 |
41 | router.get(new RegExp('^/log/' + re_status + '$'), function(req, res) {
42 | var name = req.params[0];
43 | var log = new JenkinsLogStream({
44 | 'baseUrl': urls.jenkins,
45 | 'job': 'Jobs/job/' + name
46 | });
47 |
48 | res.header("Content-Type", "text/html; charset=utf-8")
49 | .write(
50 | "" +
51 | " " +
52 | " " +
53 | ""
54 | );
55 |
56 | stream(log, res);
57 | });
58 |
59 | router.get(new RegExp('^/embedded/' + re_status + '$'), function(req, res) {
60 | var name = req.params[0];
61 | var log = new JenkinsLogStream({
62 | 'baseUrl': urls.jenkins,
63 | 'job': 'Jobs/job/' + name
64 | });
65 |
66 | res.header("Content-Type", "text/html; charset=utf-8")
67 |
68 | stream(log, res);
69 | });
70 |
71 | router.get(new RegExp('^/raw/' + re_status + '$'), function(req, res) {
72 | var name = req.params[0];
73 | var log = new JenkinsLogStream({
74 | 'baseUrl': urls.jenkins,
75 | 'job': 'Jobs/job/' + name
76 | });
77 | var simpleLogFilter = new SimpleLogFilter();
78 |
79 | res.header("Content-Type", "text/plain")
80 | stream(log, res, simpleLogFilter);
81 | });
82 |
83 | router.get(new RegExp('^/original/' + re_status + '$'), function(req, res) {
84 | var name = req.params[0];
85 | var log = new JenkinsLogStream({
86 | 'baseUrl': urls.jenkins,
87 | 'job': 'Jobs/job/' + name
88 | });
89 |
90 | res.header("Content-Type", "text/plain")
91 | stream(log, res, null);
92 | });
93 |
94 | router.get(new RegExp('^/code/' + re_status + '$'), function(req, res) {
95 | var name = req.params[0];
96 |
97 | var jenkins_url = urls.jenkins;
98 | var jenkins = require('jenkins');
99 | var conn = jenkins({ baseUrl: jenkins_url, crumbIssuer: true });
100 |
101 | var info = {
102 | 'status': 'preparing',
103 | 'submitted': 'moments ago',
104 | 'duration': '...'
105 | };
106 |
107 | conn.build.get(name, 1, function(err, data) {
108 | res.set('Content-Type', 'application/json');
109 | if (err) {
110 | res.status(404)
111 | .end(JSON.stringify({
112 | "result": "error",
113 | "message": "Job not found"
114 | }));
115 |
116 | } else {
117 | info.duration = prettyMs(data.duration, { verbose: true });
118 | if (data.building) {
119 | info.status = 'in progress'
120 | } else if (data.result == 'SUCCESS') {
121 | info.status = 'success'
122 | } else {
123 | info.status = 'error'
124 | }
125 | res.end(JSON.stringify(info));
126 | }
127 | });
128 | });
129 |
130 | module.exports = router;
131 |
--------------------------------------------------------------------------------
/frontend/views/about.hjs:
--------------------------------------------------------------------------------
1 | {{< layout }}{{$ content }}
2 |
3 |
16 |
17 | {{/ content }}{{/ layout }}
18 |
--------------------------------------------------------------------------------
/frontend/views/aboutdiv.hjs:
--------------------------------------------------------------------------------
1 |
7 | About the R-hub builder
8 | The R-hub builder offers free R CMD Check as a service on different platforms. It is a project supported by the R Consortium.
9 | Why use the R-hub builder?
10 | The builder allows you to check your R package on several platforms, and R versions.
11 | Moreover, as a side-effect it also allows you to build your R package, i.e. its binaries and the binaries for all (source) dependencies, on several platforms, and R versions.
12 | How to use the R-hub builder?
13 | You can use the R-hub builder either
14 |
22 | You can see a live demo of both the website frontend and of the rhub
R package in this video .
23 | Please note that you can only verify your email address from GitHub if the address associated to your GitHub account listed as maintainer address in the DESCRIPTION of the package.
24 | Website or package interface to the R-hub builder?
25 | Advantages of using the rhub
package over the website are that it allows your not leaving R, and that the R package offers shortcut functions such as rhub::check_for_cran()
, as well as the listing of your recent and current builds , by email address or package.
26 | A bug, question or feature request?
27 | Your contributions are welcome. R-hub is developed entirely in the open in its own GitHub organization .
28 | You can post your bug report/feature request/question belongs file as an issue in the repo of the R package .
29 | You can also send an email to admin@r-hub.io.
30 |
31 |
--------------------------------------------------------------------------------
/frontend/views/aboutdiv.md:
--------------------------------------------------------------------------------
1 |
7 | # About the R-hub builder
8 |
9 | The R-hub builder offers free R CMD Check as a service on different platforms. It is a project supported by the R Consortium.
10 |
11 | ## Why use the R-hub builder?
12 |
13 | The builder allows you to check your R package on several platforms, and R versions.
14 |
15 | Moreover, as a side-effect it also allows you to _build_ your R package, i.e. its binaries and the binaries for all (source) dependencies, on several platforms, and R versions.
16 |
17 | ## How to use the R-hub builder?
18 |
19 | You can use the R-hub builder either
20 |
21 | * via the website (where you are now!), from the [main page](https://builder.r-hub.io/) or [the advanced page](https://builder.r-hub.io/advanced).
22 |
23 | * via its API, in particular by using the [`rhub` package](https://r-hub.github.io/rhub/).
24 |
25 | You can see a live demo of both the website frontend and of the `rhub` R package in [this video](https://www.r-consortium.org/events/2016/10/11/r-hub-public-beta).
26 |
27 | Please note that you can only verify your email address from GitHub if the address associated to your GitHub account listed as maintainer address in the DESCRIPTION of the package.
28 |
29 | ### Website or package interface to the R-hub builder?
30 |
31 | Advantages of using the `rhub` package over the website are that it allows your not leaving R, and that the R package offers [shortcut functions](https://r-hub.github.io/rhub/reference/index.html#section-check-shortcuts) such as [`rhub::check_for_cran()`](https://r-hub.github.io/rhub/reference/check_for_cran.html), as well as the [listing of your recent and current builds](https://r-hub.github.io/rhub/reference/index.html#section-check-management), by email address or package.
32 |
33 | ## A bug, question or feature request?
34 |
35 | Your contributions are welcome. R-hub is developed entirely in the open [in its own GitHub organization](https://github.com/r-hub).
36 |
37 | You can post your bug report/feature request/question belongs file as an issue in [the repo of the R package](https://github.com/r-hub/rhub).
38 |
39 | You can also send an email to admin@r-hub.io.
--------------------------------------------------------------------------------
/frontend/views/adv.hjs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
The R-hub builder
5 |
6 |
7 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/frontend/views/badpackage.hjs:
--------------------------------------------------------------------------------
1 | {{< layout}}{{$ content }}
2 |
3 | Thank you for submitting package {{ package }}.
4 | Unfortunately something went wrong. Please check that you uploaded a proper
5 | R package.
6 |
7 | Error:
8 |
9 | {{{ error }}}
10 |
11 |
12 | {{/ content }}{{/ layout }}
13 |
--------------------------------------------------------------------------------
/frontend/views/error.hjs:
--------------------------------------------------------------------------------
1 | {{< layout }}{{$ content }}
2 | {{ message }}
3 | {{ error.status }}
4 | {{ error.stack }}
5 | {{/ content }}{{/ layout }}
6 |
--------------------------------------------------------------------------------
/frontend/views/footer.hjs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/frontend/views/header.hjs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | R-hub package builder
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | {{ title }} {{^ title }} R-hub builder {{/ title }}
38 |
--------------------------------------------------------------------------------
/frontend/views/ie7notice.hjs:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/frontend/views/index.hjs:
--------------------------------------------------------------------------------
1 | {{< layout }}{{$ content }}
2 |
3 |
4 |
5 | {{^ advanced }}
6 | {{< simple }}{{/ simple }}
7 | {{/ advanced }}
8 |
9 | {{# advanced }}
10 | {{< adv }}{{/ adv }}
11 | {{/ advanced }}
12 |
13 |
14 |
15 | {{/ content }}{{/ layout }}
16 |
17 |
19 |
20 | {{$ scripts }}
21 |
47 | {{/ scripts }}
48 |
--------------------------------------------------------------------------------
/frontend/views/layout.hjs:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{> header }}{{$ extraheader }}{{/ extraheader }}
4 |
5 | {{> ie7notice }}
6 | {{> navbar }}
7 | {{$ content }}{{/ content }}
8 | {{> footer }}
9 | {{$ scripts }}{{/ scripts }}
10 |
11 |
12 |
--------------------------------------------------------------------------------
/frontend/views/navbar.hjs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
14 |
15 |
16 |
17 |
18 |
19 |
22 |
23 |
26 |
27 |
30 |
31 |
34 |
35 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/frontend/views/ok.hjs:
--------------------------------------------------------------------------------
1 | {{< layout}}{{$ content }}
2 | Thank you for submitting package {{ package }}.
3 | Once the package is checked, a notification email will be sent
4 | to the address of the maintainer: {{ email }}.
5 | {{/ content }}{{/ layout }}
6 |
--------------------------------------------------------------------------------
/frontend/views/simple.hjs:
--------------------------------------------------------------------------------
1 |
2 |
The R-hub builder
3 |
4 |
5 |
10 |
11 |
56 |
--------------------------------------------------------------------------------
/frontend/views/status.hjs:
--------------------------------------------------------------------------------
1 |
2 | {{< layout}}{{$ content }}
3 |
4 |
5 |
6 |
7 |
13 |
14 |
15 |
16 | Build id: ...
17 | Package: ...
18 | Version: ...
19 | Email: ...
20 | Status: ...
21 | Platform: ...
22 | Submitted: ...
23 | Started: hang tight
24 | Duration: ???
25 |
26 |
27 |
28 | Waiting for the build to start...
29 |
30 |
31 |
32 |
33 |
34 |
35 |
58 |
59 |
119 |
120 |
126 |
127 | {{/ content }}{{/ layout }}
128 |
--------------------------------------------------------------------------------
/frontend/views/terms.hjs:
--------------------------------------------------------------------------------
1 | {{< layout }}{{$ content }}
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 |
Introduction
12 |
13 |
The R-hub builder service is provided free of charge by the
14 | R consortium.
15 |
16 |
By uploading your R package, you agree to these terms and
17 | conditions.
18 |
19 |
Whereas there are currently no limits for R package
20 | submissions, we reserve the right to introduce rate limits
21 | for the number of packages uploaded per time period,
22 | in the future.
23 |
24 |
Cookies
25 |
26 |
This web site uses cookies to carry out the email address
27 | check, and to maintain a user session.
28 |
29 |
User tracking and data
30 |
31 |
We do not store any data about our users, apart from the
32 | session information. The session information is deleted
33 | once the user logs out.
34 |
35 |
We do not share the uploaded packages with anyone, and
36 | only store them temporarily, until the build and check
37 | process is completed.
38 |
39 |
We store the check results (the console output) for a
40 | short period of time, currently three days, for debugging
41 | purposes. We do not give out the check output to anyone,
42 | except for the package maintainer, as declared in the
43 | DESCRIPTION file of the package.
44 |
45 |
Disclaimer
46 |
47 |
You use this service at your own risk.
48 |
49 |
We are not responsible for the code your R package runs
50 | during the package build and check.
51 |
52 |
53 |
54 |
55 |
56 |
57 | {{/ content }}{{/ layout }}
58 |
--------------------------------------------------------------------------------
/frontend/views/verify.hjs:
--------------------------------------------------------------------------------
1 |
2 | {{< layout}}
3 |
4 | {{$ extraheader }}
5 |
7 | {{/ extraheader }}
8 |
9 | {{$ content }}
10 |
11 |
12 |
13 |
14 |
The R-hub builder
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | Package: {{ package }}
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | Maintainer: {{ email }}
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
55 |
56 |
57 |
58 |
59 | – OR –
60 |
61 |
62 |
63 |
64 |
86 |
87 |
88 |
89 |
96 |
97 | {{/content}}
98 |
99 | {{$ scripts }}
100 |
102 |
103 |
119 | {{/ scripts }}
120 |
121 | {{/ layout }}
122 |
--------------------------------------------------------------------------------
/jenkins.pass:
--------------------------------------------------------------------------------
1 | 44186ecbfec14
2 |
--------------------------------------------------------------------------------
/jenkins/Dockerfile:
--------------------------------------------------------------------------------
1 |
2 | FROM jenkins/jenkins:2.161
3 |
4 | ## Azure CLI, this is not always needed, but it does not hurt now...
5 |
6 | USER root
7 |
8 | RUN apt-get update
9 | RUN apt-get install -y apt-transport-https lsb-release \
10 | software-properties-common dirmngr
11 | RUN echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ $(lsb_release -cs) main" | \
12 | tee /etc/apt/sources.list.d/azure-cli.list
13 | RUN apt-key --keyring /etc/apt/trusted.gpg.d/Microsoft.gpg adv \
14 | --keyserver packages.microsoft.com \
15 | --recv-keys BC528686B50D79E339D3721CEB3E94ADBE1229CF
16 | RUN apt-get update && apt-get install -y azure-cli
17 |
18 | USER jenkins
19 |
20 | COPY plugins.txt /usr/share/jenkins/ref/plugins.txt
21 |
22 | RUN /usr/local/bin/install-plugins.sh < /usr/share/jenkins/ref/plugins.txt
23 |
24 | # Jenkins is fully configured, no configure wizard, please
25 | ENV JAVA_OPTS -Djenkins.install.runSetupWizard=false
26 | RUN echo 2.146 > /usr/share/jenkins/ref/jenkins.install.UpgradeWizard.state
27 | RUN echo 2.146 > /usr/share/jenkins/ref/jenkins.install.InstallUtil.lastExecVersion
28 |
29 | COPY default-user.groovy /usr/share/jenkins/ref/init.groovy.d/
30 | COPY config.groovy /usr/share/jenkins/ref/init.groovy.d/
31 |
32 |
--------------------------------------------------------------------------------
/jenkins/config.groovy:
--------------------------------------------------------------------------------
1 |
2 | import jenkins.model.Jenkins
3 | import jenkins.model.JenkinsLocationConfiguration
4 | import jenkins.security.s2m.*
5 |
6 | def env = System.getenv()
7 |
8 | // Disable CLI remoting
9 | Jenkins.instance.getDescriptor("jenkins.CLI").get().setEnabled(false)
10 |
11 | // Set Jenkins root url
12 | jlc = JenkinsLocationConfiguration.get()
13 | jlc.setUrl(env.JENKINS_ROOT_URL)
14 | jlc.save()
15 |
16 | // Turn on agent to master security subsystem
17 | Jenkins.instance.injector.getInstance(AdminWhitelistRule.class)
18 | .setMasterKillSwitch(false);
19 | Jenkins.instance.save()
20 |
21 | // Enable CSRF Protection
22 | import hudson.security.csrf.DefaultCrumbIssuer
23 | import jenkins.model.Jenkins
24 |
25 | def instance = Jenkins.instance
26 | instance.setCrumbIssuer(new DefaultCrumbIssuer(true))
27 | instance.save()
28 |
29 | // Quiet period
30 | instance = Jenkins.getInstance()
31 | instance.setQuietPeriod(0)
32 |
33 | // Env vars
34 | import hudson.EnvVars;
35 | import hudson.slaves.EnvironmentVariablesNodeProperty;
36 | import hudson.slaves.NodeProperty;
37 | import hudson.slaves.NodePropertyDescriptor;
38 | import hudson.util.DescribableList;
39 | import jenkins.model.Jenkins;
40 |
41 | public createGlobalEnvironmentVariables(String key, String value) {
42 |
43 | Jenkins instance = Jenkins.getInstance();
44 |
45 | DescribableList, NodePropertyDescriptor> globalNodeProperties =
46 | instance.getGlobalNodeProperties();
47 | List envVarsNodePropertyList =
48 | globalNodeProperties.getAll(EnvironmentVariablesNodeProperty.class);
49 |
50 | EnvironmentVariablesNodeProperty newEnvVarsNodeProperty = null;
51 | EnvVars envVars = null;
52 |
53 | if (envVarsNodePropertyList == null || envVarsNodePropertyList.size() == 0) {
54 | newEnvVarsNodeProperty =
55 | new hudson.slaves.EnvironmentVariablesNodeProperty();
56 | globalNodeProperties.add(newEnvVarsNodeProperty);
57 | envVars = newEnvVarsNodeProperty.getEnvVars();
58 | } else {
59 | envVars = envVarsNodePropertyList.get(0).getEnvVars();
60 | }
61 | envVars.put(key, value)
62 | instance.save()
63 | }
64 |
65 | createGlobalEnvironmentVariables('PS4','+R-HUB-R-HUB-R-HUB')
66 |
67 | // Create Folders
68 | def jobs_str = '''
69 |
70 | User submitted builds.
71 |
72 |
73 |
74 |
75 |
76 | All
77 | false
78 | false
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | false
87 |
88 |
89 |
90 |
91 | '''
92 | def jobs_stream = new ByteArrayInputStream(jobs_str.getBytes())
93 | Jenkins.instance.createProjectFromXML('Jobs', jobs_stream)
94 |
95 | def manage_str = '''
96 |
97 | Internal jobs for R-hub.
98 |
99 |
100 |
101 |
102 |
103 | All
104 | false
105 | false
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 | false
114 |
115 |
116 |
117 |
118 | '''
119 | def manage_stream = new ByteArrayInputStream(manage_str.getBytes())
120 | Jenkins.instance.createProjectFromXML('Management', manage_stream)
121 |
--------------------------------------------------------------------------------
/jenkins/default-user.groovy:
--------------------------------------------------------------------------------
1 |
2 | import jenkins.model.*
3 | import hudson.security.*
4 |
5 | def env = System.getenv()
6 | def pass = new File("/run/secrets/jenkins.pass").text.trim()
7 |
8 | def jenkins = Jenkins.getInstance()
9 | jenkins.setSecurityRealm(new HudsonPrivateSecurityRealm(false))
10 | jenkins.setAuthorizationStrategy(new GlobalMatrixAuthorizationStrategy())
11 |
12 | def user = jenkins.getSecurityRealm().createAccount(env.JENKINS_USER, pass);
13 | user.save()
14 |
15 | jenkins.getAuthorizationStrategy().add(Jenkins.ADMINISTER, env.JENKINS_USER)
16 | jenkins.save()
17 |
--------------------------------------------------------------------------------
/jenkins/plugins.txt:
--------------------------------------------------------------------------------
1 | elastic-axis:1.2
2 | groovy-postbuild:2.4.3
3 | matrix-auth:2.3
4 | publish-over-ssh:1.20.1
5 | swarm:3.15
6 | build-timeout:1.19
7 | cloudbees-folder:6.7
8 | ws-cleanup:0.37
9 |
--------------------------------------------------------------------------------
/jenkins/scripts/windows-2008-pre-setup.ps1:
--------------------------------------------------------------------------------
1 |
2 | ## This must be run first, and then the machine must be rebooted
3 | ## to make WMF5.1 available.
4 |
5 | $LocalTempDir = $env:TEMP;
6 | $url = "https://go.microsoft.com/fwlink/?linkid=839523"
7 | $dest = "$LocalTempDir\wmf51.zip"
8 |
9 | (new-object System.Net.WebClient).DownloadFile($url, $dest)
10 |
11 | function Expand-ZIPFile {
12 | [CmdletBinding()]
13 | param (
14 | [Parameter(Position=0, Mandatory=1)]
15 | [string]$file,
16 | [Parameter(Position=1, Mandatory=1)]
17 | [string]$destination
18 | )
19 |
20 | $shell = new-object -com shell.application
21 | $zip = $shell.NameSpace($file)
22 | foreach($item in $zip.items())
23 | {
24 | $shell.Namespace($destination).copyhere($item)
25 | }
26 | }
27 |
28 | $xdir = "$LocalTempDir\wmf51"
29 | mkdir "$xdir"
30 | Expand-ZIPFile "$dest" "$xdir"
31 |
32 | powershell.exe -ExecutionPolicy Bypass -Command "$xdir\Install-WMF5.1.ps1 -AcceptEula"
33 |
--------------------------------------------------------------------------------
/jenkins/scripts/windows-2008-setup.ps1:
--------------------------------------------------------------------------------
1 |
2 | ## Otherwise PowerShell defaults to older TLS, which is not supported
3 | ## by many web sites any more
4 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
5 |
6 | $LocalTempDir = $env:TEMP
7 |
8 | Function Download {
9 | [CmdletBinding()]
10 | param (
11 | [Parameter(Position=0, Mandatory=1)]
12 | [string]$Url,
13 | [Parameter(Position=1, Mandatory=1)]
14 | [string]$Dest
15 | )
16 |
17 | (new-object System.Net.WebClient).DownloadFile($Url, $Dest)
18 | }
19 |
20 | Function Set-Timezone {
21 | tzutil /g
22 | tzutil /s "GMT Standard Time"
23 | tzutil /g
24 | }
25 |
26 | Function Install-Git {
27 | $giturl = "https://github.com/git-for-windows/git/releases/download/v2.20.1.windows.1/Git-2.20.1-64-bit.exe"
28 | $gitfile = "$LocalTempDir\git-install.exe"
29 |
30 | Download "$giturl" "$gitfile"
31 |
32 | start-process "$gitfile" -argumentlist `
33 | "/VERYSILENT","/NORESTART","/NOCANCEL","/SP-","/CLOSEAPPLICATIONS","/RESTARTAPPLICATIONS","/COMPONENTS=`"icons,ext\reg\shellhere,assoc,assoc_sh`"" -wait
34 |
35 | $env:path = "C:\Program Files\Git\bin;$env:path"
36 | }
37 |
38 | Function Install-Java {
39 | $javaurl = "https://javadl.oracle.com/webapps/download/AutoDL?BundleId=236886_42970487e3af4f5aa5bca3f542482c60"
40 | $javafile = "$LocalTempDir\java-install.exe"
41 |
42 | Download "$javaurl" "$javafile"
43 |
44 | start-process "$javafile" -argumentlist "/s" -wait
45 | }
46 |
47 | Function Install-Chrome {
48 | $ChromeInstaller = "ChromeInstaller.exe";
49 | Download 'http://dl.google.com/chrome/install/375.126/chrome_installer.exe' `
50 | "$LocalTempDir\$ChromeInstaller"
51 | & "$LocalTempDir\$ChromeInstaller" /silent /install;
52 | $Process2Monitor = "ChromeInstaller";
53 | Do {
54 | $ProcessesFound = Get-Process |
55 | ?{$Process2Monitor -contains $_.Name} |
56 | Select-Object -ExpandProperty Name;
57 | If ($ProcessesFound) {
58 | "Still running: $($ProcessesFound -join ', ')" | Write-Host;
59 | Start-Sleep -Seconds 2
60 | } else {
61 | rm "$LocalTempDir\$ChromeInstaller" -ErrorAction SilentlyContinue -Verbose
62 | }
63 | } Until (!$ProcessesFound)
64 | }
65 |
66 | Function Updates-Off {
67 | & reg add HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU /v AUOptions /t REG_DWORD /d 3
68 | }
69 |
70 | Function Install-R {
71 | $CRAN = "https://cloud.r-project.org"
72 | $DevelUrl = $CRAN + "/bin/windows/base/R-devel-win.exe"
73 |
74 | $Version = $(ConvertFrom-JSON $(Invoke-WebRequest "http://rversions.r-pkg.org/r-release-win").Content).version
75 | If ($Version -eq "3.2.4") {
76 | $Version = "3.2.4revised"
77 | }
78 |
79 | $Oldrel = $(ConvertFrom-JSON $(Invoke-WebRequest "http://rversions.r-pkg.org/r-oldrel").Content).version
80 |
81 | $ReleaseUrl = $CRAN + "/bin/windows/base/R-" + $Version + "-win.exe"
82 | $PatchedUrl = $CRAN + "/bin/windows/base/R-" + $Version + `
83 | "patched-win.exe"
84 | $OldrelUrl = $CRAN + "/bin/windows/base/old/" + $Oldrel + `
85 | "/R-" + $Oldrel + "-win.exe"
86 |
87 | $DevelFile = "$LocalTempDir\R-devel-win.exe"
88 | $ReleaseFile = "$LocalTempDir\R-release-win.exe"
89 | $PatchedFile = "$LocalTempDir\R-patched-win.exe"
90 | $OldrelFile = "$LocalTempDir\R-oldrel-win.exe"
91 |
92 | Download "$DevelUrl" "$DevelFile"
93 | Download "$ReleaseUrl" "$ReleaseFile"
94 | Download "$PatchedUrl" "$PatchedFile"
95 | Download "$OldrelUrl" "$OldrelFile"
96 |
97 | Start-Process -FilePath "$ReleaseFile" -ArgumentList "/VERYSILENT" -NoNewWindow -Wait
98 | Start-Process -FilePath "$OldrelFile" -ArgumentList "/VERYSILENT" -NoNewWindow -Wait
99 | Start-Process -FilePath "$PatchedFile" -ArgumentList "/VERYSILENT" -NoNewWindow -Wait
100 | Start-Process -FilePath "$DevelFile" -ArgumentList "/VERYSILENT" -NoNewWindow -Wait
101 | }
102 |
103 | Function Install-7zip {
104 | $zipurl = "https://www.7-zip.org/a/7z1900-x64.msi"
105 | $zipfile = "$LocalTempDir\7zip.msi"
106 | Download "$zipurl" "$zipfile"
107 |
108 | msiexec /i "$zipfile" /q INSTALLDIR="C:\Program Files\7-Zip"
109 |
110 | $env:path = "C:\Program Files\7-Zip;$env:path"
111 | }
112 |
113 | Function Install-Rtools {
114 | $rtoolsver = $(Invoke-WebRequest ($CRAN + "/bin/windows/Rtools/VERSION.txt")).Content.Split(' ')[2].Split('.')[0..1] -Join ''
115 | $rtoolsurl = $CRAN + "/bin/windows/Rtools/Rtools$rtoolsver.exe"
116 |
117 | $rtoolsfile = "$LocalTempDir\Rtools.exe"
118 | Download "$rtoolsurl" "$rtoolsfile"
119 |
120 | Start-Process -FilePath "$rtoolsfile" -ArgumentList /VERYSILENT -NoNewWindow -Wait
121 | }
122 |
123 | Function Install-Latex {
124 | $latexurl = "https://miktex.org/download/ctan/systems/win32/miktex/setup/windows-x64/miktexsetup-2.9.6942-x64.zip"
125 | $latexfile = "$LocalTempDir\miktex.xip"
126 | Download $latexurl $latexfile
127 | $xdir = "$LocalTempDir\miktex"
128 | mkdir "$xdir" -force
129 | $repo = "https://ctan.math.illinois.edu/systems/win32/miktex/tm/packages/"
130 | & 7z x "$latexfile" -o"$xdir"
131 | & "$xdir\miktexsetup.exe" --quiet --package-set=complete `
132 | "--remote-package-repository=$repo" download
133 | & "$xdir\miktexsetup.exe" --quiet --package-set=complete install
134 | }
135 |
136 | Function Install-Pandoc {
137 | $url1 = "https://files.r-hub.io/pandoc/windows/pandoc-1.19.2.1.zip"
138 | $url2 = "https://files.r-hub.io/pandoc/windows/pandoc-citeproc-0.10.4.zip"
139 | $file1 = "$LocalTempDir\pandoc.zip"
140 | $file2 = "$LocalTempDir\pandoc-citeproc.zip"
141 |
142 | Download "$url1" "$file1"
143 | Download "$url2" "$file2"
144 |
145 | $xdir = "$LocalTempDir\pandoc"
146 | mkdir "$xdir" -force
147 | & 7z x "$file1" -aoa -o"$xdir"
148 | & 7z x "$file2" -aoa -o"$xdir"
149 |
150 | cp "$xdir\*.exe" c:\windows\
151 | }
152 |
153 | Function Install-Aspell {
154 | $spellurl = "http://ftp.gnu.org/gnu/aspell/w32/Aspell-0-50-3-3-Setup.exe"
155 | $spellfile = "$LocalTempDir\aspell-setup.exe"
156 |
157 | Download "$spellurl" "$spellfile"
158 |
159 | & "$spellfile" /VERYSILENT /NORESTART /NOCANCEL
160 | }
161 |
162 | Function Install-Jags {
163 | $jagsurl = "https://files.r-hub.io/jags/jags-4.3.0.zip"
164 | $jagsfile = "$LocalTempDir\jags-4.3.0.zip"
165 |
166 | Download "$jagsurl" "$jagsfile"
167 |
168 | $xdir = "c:/R/jags"
169 | mkdir "$xdir" -force
170 | & 7z x "$jagsfile" -o"$xdir"
171 |
172 | $mv = "c:\R\Makevars.win"
173 | $mv64 = "c:\R\Makevars.win64"
174 | "JAGS_ROOT=c:/R/jags/JAGS-4.3.0" | AC "$mv"
175 | "JAGS_ROOT=c:/R/jags/JAGS-4.3.0" | AC "$mv64"
176 |
177 | SETX JAGS_HOME "c:/R/jags/JAGS-4.3.0" /M
178 | }
179 |
180 | Function Get-Jenkins {
181 | $swarmversion = "3.9"
182 | $swarmurl = "https://repo.jenkins-ci.org/releases/org/jenkins-ci/plugins/swarm-client/$swarmversion/swarm-client-$swarmversion.jar"
183 | $swarmfile = "c:\Users\rhub\jenkins-swarm-client.jar"
184 |
185 | Download "$swarmurl" "$swarmfile"
186 | }
187 |
188 | Function Get-LocalSoft {
189 | $localurl = "https://github.com/rwinlib/local330/archive/master.zip"
190 | $localfile = "$LocalTempDir\local330.zip"
191 |
192 | Download "$localurl" "$localfile"
193 |
194 | mkdir -force c:\R
195 | ## We need the WMF5.1 update for this, powershell 2.0 does not have it
196 | expand-archive -path "$localfile" -destinationpath "c:\R"
197 | mv "c:\R\local330-master" "c:\R\local330"
198 |
199 | $glpk32url = "https://www.stats.ox.ac.uk/pub/Rtools/goodies/multilib/glpk32.zip"
200 | $glpk64url = "https://www.stats.ox.ac.uk/pub/Rtools/goodies/multilib/glpk64.zip"
201 | $glpk32file = "$LocalTempDir\glpk32.zip"
202 | $glpk64file = "$LocalTempDir\glpk64.zip"
203 |
204 | Download "$glpk32url" "$glpk32file"
205 | Download "$glpk64url" "$glpk64file"
206 |
207 | expand-archive -path "$glpk32file" -destinationpath "c:\R\glpk32"
208 | expand-archive -path "$glpk64file" -destinationpath "c:\R\glpk64"
209 |
210 | $sym32url = "https://www.stats.ox.ac.uk/pub/Rtools/goodies/multilib/symphony32.zip"
211 | $sym64url = "https://www.stats.ox.ac.uk/pub/Rtools/goodies/multilib/symphony64.zip"
212 | $sym32file = "$LocalTempDir\sym32.zip"
213 | $sym64file = "$LocalTempDir\sym64.zip"
214 |
215 | Download "$sym32url" "$sym32file"
216 | Download "$sym64url" "$sym64file"
217 |
218 | expand-archive -path "$sym32file" -destinationpath "c:\R\symphony32"
219 | expand-archive -path "$sym64file" -destinationpath "c:\R\symphony64"
220 |
221 | $mv = "c:\R\Makevars.win"
222 | "LIB_XML=c:/R/local330" | AC "$mv"
223 | "LIB_GSL=c:/R/local330" | AC "$mv"
224 | "GLPK_HOME=c:/R/glpk32" | AC "$mv"
225 | "SYMPHONY_HOME=c:/R/symphony32" | AC "$mv"
226 |
227 | $mv64 = "c:\R\Makevars.win64"
228 | "LIB_XML=c:/R/local330" | AC "$mv64"
229 | "LIB_GSL=c:/R/local330" | AC "$mv64"
230 | "GLPK_HOME=c:/R/glpk64" | AC "$mv64"
231 | "SYMPHONY_HOME=c:/R/symphony64" | AC "$mv64"
232 |
233 | SETX LOCAL_SOFT c:/R/local330 /M
234 | SETX R_MAKEVARS_WIN "$mv" /M
235 | SETX R_MAKEVARS_WIN64 "$mv64" /M
236 | }
237 |
238 | Function Get-Scripts {
239 | git clone https://github.com/r-hub/wincheck.git c:\users\rhub\wincheck
240 | cp c:\users\rhub\wincheck\*.ps1 c:\users\rhub\documents\
241 | }
242 |
243 | Function Install-Perl {
244 | $perlurl = "http://strawberryperl.com/download/5.28.1.1/strawberry-perl-5.28.1.1-64bit.msi"
245 | $perlfile = "$LocalTempDir\strawberry-perl-5.28.1.1-64bit.msi"
246 |
247 | Download "$perlurl" "$perlfile"
248 |
249 | start-process msiexec -ArgumentList "/I $perlfile /passive" -wait
250 | }
251 |
252 | Function Install-Rtools40 {
253 | # Experimental Rtools40 installation
254 | $rtoolsurl = "https://dl.bintray.com/rtools/installer/rtools40-x86_64.exe"
255 | $rtoolsfile = "$LocalTempDir\rtools40-x86_64.exe"
256 | Download "$rtoolsurl" "$rtoolsfile"
257 | Start-Process -FilePath "$rtoolsfile" -ArgumentList /VERYSILENT -NoNewWindow -Wait
258 |
259 | # Rtools40 path is hardcoded in R-testing for now
260 | # setx path "$c:\rtools40\usr\bin"
261 |
262 | # Special build of R for Rtools40
263 | $TestingUrl = "https://dl.bintray.com/rtools/installer/R-testing-win.exe"
264 | $TestingFile = "$LocalTempDir\R-testing-win.exe"
265 | Download "$TestingUrl" "$TestingFile"
266 | Start-Process -FilePath "$TestingFile" -ArgumentList "/VERYSILENT" -NoNewWindow -Wait
267 | }
268 |
269 | Function Update-Rtools40 {
270 | & C:\rtools40\usr\bin\pacman --noconfirm -Syyu
271 | & C:\rtools40\usr\bin\bash.exe --login -c `
272 | 'pacman -S --needed --noconfirm $(pacman -Slq | grep mingw-w64-)'
273 | }
274 |
275 |
276 | Updates-Off
277 | Set-Timezone
278 |
279 | Install-Git
280 | Install-Java
281 | Install-Chrome
282 | Install-7zip
283 | Install-R
284 | Install-Rtools
285 | Install-Latex
286 | Install-Pandoc
287 | Install-Aspell
288 | Install-Perl
289 |
290 | Get-Jenkins
291 | Get-LocalSoft
292 | Get-Scripts
293 |
294 | Install-Jags
295 |
--------------------------------------------------------------------------------
/linux-builder/Dockerfile:
--------------------------------------------------------------------------------
1 |
2 | FROM ubuntu:18.04
3 |
4 | ENV DEBIAN_FRONTEND noninteractive
5 |
6 | # en_US locale
7 | RUN apt-get update && \
8 | apt-get install -y locales && \
9 | localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
10 | ENV LANG en_US.utf8
11 |
12 | # R
13 |
14 | RUN echo "deb https://cloud.r-project.org/bin/linux/ubuntu bionic-cran35/" \
15 | > /etc/apt/sources.list.d/cran.list
16 |
17 | RUN apt-get install -yy default-jre-headless gnupg curl wget
18 |
19 | RUN apt-key adv --keyserver keyserver.ubuntu.com \
20 | --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9
21 |
22 | RUN apt-get update && apt-get install -yy \
23 | r-base-core r-base-dev
24 |
25 | RUN useradd jenkins && \
26 | mkdir /home/jenkins && \
27 | chown jenkins:jenkins /home/jenkins && \
28 | addgroup jenkins staff
29 |
30 | RUN apt-get install -yy docker.io
31 | RUN addgroup jenkins docker
32 |
33 | RUN apt-get clean
34 |
35 | ENV SWARM_CLIENT_VERSION 3.9
36 |
37 | RUN curl -s https://repo.jenkins-ci.org/releases/org/jenkins-ci/plugins/swarm-client/${SWARM_CLIENT_VERSION}/swarm-client-${SWARM_CLIENT_VERSION}.jar -o /home/jenkins/swarm-client-${SWARM_CLIENT_VERSION}.jar
38 |
39 | # Somewhat surprisingly, this persist after mounting the socket, so
40 | # the jenkins user will have access to Docker
41 | RUN touch /var/run/docker.sock && \
42 | chown root:docker /var/run/docker.sock
43 |
44 | RUN mkdir /artifacts && \
45 | chown jenkins:jenkins /artifacts
46 |
47 | COPY run.sh /home/jenkins/run.sh
48 | RUN chmod +x /home/jenkins/run.sh
49 |
50 | RUN rm -rf /var/lib/apt/lists/*
51 |
52 | USER jenkins
53 |
54 | # Get R packages we need
55 |
56 | RUN R -q -e 'source("https://install-github.me/r-hub/sysreqs")'
57 |
58 | # Need to switch back to root, because we need to change the group of
59 | # the docker socket, to be able to start sibling containers
60 | # run.sh will do su to start the jenkins swarm client as non-root
61 |
62 | USER root
63 |
64 | CMD [ "/home/jenkins/run.sh" ]
65 |
--------------------------------------------------------------------------------
/linux-builder/run.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | chgrp docker /var/run/docker.sock
4 |
5 | export JENKINS_PASSWORD="$(cat /run/secrets/jenkins.pass)"
6 |
7 | cd ~jenkins
8 |
9 | cat >jenkins.sh < /etc/nginx/nginx.conf
8 | else
9 | echo "Installing HTTP nginx config"
10 | cat /etc/nginx/nginx.conf.in | envsubst > /etc/nginx/nginx.conf
11 | fi
12 |
13 | wait-for frontend:3000 -- sleep 0
14 | wait-for jenkins:8080 -- nginx -g "daemon off;"
15 |
--------------------------------------------------------------------------------
/nginx/nginx.conf.https.in:
--------------------------------------------------------------------------------
1 |
2 | user nginx;
3 |
4 | events {
5 | worker_connections 768;
6 | }
7 |
8 | http {
9 |
10 | ssl_session_cache shared:SSL:10m;
11 | ssl_session_timeout 10m;
12 |
13 | server {
14 | listen 80;
15 | access_log off;
16 | return 301 https://${DOLLAR}host${DOLLAR}request_uri;
17 | }
18 |
19 | server {
20 | listen 443 ssl;
21 | keepalive_timeout 70;
22 |
23 | ssl_certificate /run/secrets/nginx.crt;
24 | ssl_certificate_key /run/secrets/nginx.key;
25 | ssl_protocols TLSv1.1 TLSv1.2;
26 |
27 | location ~ ^/artifacts/?${DOLLAR} {
28 | root /;
29 | autoindex off;
30 | }
31 |
32 | location ~ /artifacts/.* {
33 | root /;
34 | autoindex on;
35 | sendfile on;
36 | sendfile_max_chunk 1m;
37 | }
38 |
39 | location /jenkins/ {
40 | sendfile off;
41 |
42 | proxy_pass http://jenkins:8080;
43 | proxy_redirect http://jenkins:8080 ${DOLLAR}scheme://${DOLLAR}host:${DOLLAR}server_port/jenkins;
44 |
45 | proxy_connect_timeout 90;
46 | proxy_send_timeout 90;
47 | proxy_read_timeout 90;
48 | proxy_buffering off;
49 |
50 | proxy_set_header X-Forwarded-Host ${DOLLAR}host:${DOLLAR}server_port;
51 | proxy_set_header X-Forwarded-Server ${DOLLAR}host;
52 | proxy_set_header X-Forwarded-For ${DOLLAR}proxy_add_x_forwarded_for;
53 | proxy_set_header X-Forwarded-Proto ${DOLLAR}scheme;
54 | proxy_set_header X-Real-IP ${DOLLAR}remote_addr;
55 | proxy_request_buffering off;
56 | proxy_set_header Connection "";
57 | }
58 |
59 | location / {
60 | proxy_pass http://frontend:3000;
61 | proxy_set_header Host ${DOLLAR}host;
62 | proxy_set_header X-Real-IP ${DOLLAR}remote_addr;
63 | proxy_set_header X-Forwarded-For ${DOLLAR}proxy_add_x_forwarded_for;
64 | }
65 | }
66 |
67 | include /etc/nginx/conf.d/*.conf;
68 | }
69 |
--------------------------------------------------------------------------------
/nginx/nginx.conf.in:
--------------------------------------------------------------------------------
1 |
2 | user nginx;
3 |
4 | events {
5 | worker_connections 768;
6 | }
7 |
8 | http {
9 | server {
10 | listen 80;
11 | client_max_body_size 200M;
12 |
13 | location ~ ^/artifacts/?${DOLLAR} {
14 | root /;
15 | autoindex off;
16 | }
17 |
18 | location ~ /artifacts/.* {
19 | root /;
20 | autoindex on;
21 | sendfile on;
22 | sendfile_max_chunk 1m;
23 | }
24 |
25 | location /jenkins/ {
26 | sendfile off;
27 |
28 | proxy_pass http://jenkins:8080;
29 | proxy_redirect http://jenkins:8080 ${DOLLAR}scheme://${DOLLAR}host:${DOLLAR}server_port/jenkins;
30 |
31 | proxy_connect_timeout 90;
32 | proxy_send_timeout 90;
33 | proxy_read_timeout 90;
34 | proxy_buffering off;
35 |
36 | proxy_set_header X-Forwarded-Host ${DOLLAR}host:${DOLLAR}server_port;
37 | proxy_set_header X-Forwarded-Server ${DOLLAR}host;
38 | proxy_set_header X-Forwarded-For ${DOLLAR}proxy_add_x_forwarded_for;
39 | proxy_set_header X-Forwarded-Proto ${DOLLAR}scheme;
40 | proxy_set_header X-Real-IP ${DOLLAR}remote_addr;
41 | proxy_request_buffering off;
42 | proxy_set_header Connection "";
43 | }
44 |
45 | location / {
46 | proxy_pass http://frontend:3000;
47 | proxy_set_header Host ${DOLLAR}host;
48 | proxy_set_header X-Real-IP ${DOLLAR}remote_addr;
49 | proxy_set_header X-Forwarded-For ${DOLLAR}proxy_add_x_forwarded_for;
50 | }
51 | }
52 |
53 | include /etc/nginx/conf.d/*.conf;
54 | }
55 |
--------------------------------------------------------------------------------
/nginx/wait-for:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | TIMEOUT=15
4 | QUIET=0
5 |
6 | echoerr() {
7 | if [ "$QUIET" -ne 1 ]; then printf "%s\n" "$*" 1>&2; fi
8 | }
9 |
10 | usage() {
11 | exitcode="$1"
12 | cat << USAGE >&2
13 | Usage:
14 | $cmdname host:port [-t timeout] [-- command args]
15 | -q | --quiet Do not output any status messages
16 | -t TIMEOUT | --timeout=timeout Timeout in seconds, zero for no timeout
17 | -- COMMAND ARGS Execute command with args after the test finishes
18 | USAGE
19 | exit "$exitcode"
20 | }
21 |
22 | wait_for() {
23 | for i in `seq $TIMEOUT` ; do
24 | nc -z "$HOST" "$PORT" > /dev/null 2>&1
25 |
26 | result=$?
27 | if [ $result -eq 0 ] ; then
28 | if [ $# -gt 0 ] ; then
29 | exec "$@"
30 | fi
31 | exit 0
32 | fi
33 | sleep 1
34 | done
35 | echo "Operation timed out" >&2
36 | exit 1
37 | }
38 |
39 | while [ $# -gt 0 ]
40 | do
41 | case "$1" in
42 | *:* )
43 | HOST=$(printf "%s\n" "$1"| cut -d : -f 1)
44 | PORT=$(printf "%s\n" "$1"| cut -d : -f 2)
45 | shift 1
46 | ;;
47 | -q | --quiet)
48 | QUIET=1
49 | shift 1
50 | ;;
51 | -t)
52 | TIMEOUT="$2"
53 | if [ "$TIMEOUT" = "" ]; then break; fi
54 | shift 2
55 | ;;
56 | --timeout=*)
57 | TIMEOUT="${1#*=}"
58 | shift 1
59 | ;;
60 | --)
61 | shift
62 | break
63 | ;;
64 | --help)
65 | usage 0
66 | ;;
67 | *)
68 | echoerr "Unknown argument: $1"
69 | usage 1
70 | ;;
71 | esac
72 | done
73 |
74 | if [ "$HOST" = "" -o "$PORT" = "" ]; then
75 | echoerr "Error: you need to provide a host and port to test."
76 | usage 2
77 | fi
78 |
79 | wait_for "$@"
80 |
--------------------------------------------------------------------------------
/rhub:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | if [[ ! -z "$DOCKER_MACHINE_NAME" ]]; then
4 | echo "RHUB> Active Docker Machine: $DOCKER_MACHINE_NAME"
5 | else
6 | echo "RHUB> No active Docker Machine"
7 | fi
8 |
9 | if [[ -f stack.yml ]]; then
10 | echo "RHUB> Setting up base project"
11 |
12 | set -a
13 | . ./config.env
14 | set +a
15 |
16 | export COMPOSE_FILE=stack.yml
17 | export COMPOSE_ARG="-c stack.yml"
18 |
19 | else
20 | echo "RHUB> Setting up $(basename $(pwd)) project"
21 |
22 | set -a
23 | . ../config.env
24 | if [[ -f config-custom.env ]]; then . ./config-custom.env; fi
25 | set +a
26 |
27 | if [[ ! -f stack-custom.yml ]]; then
28 | export COMPOSE_FILE="../stack.yml"
29 | export COMPOSE_ARG="-c ../stack.yml"
30 | else
31 | export COMPOSE_PATH_SEPARATOR=":"
32 | export COMPOSE_FILE="../stack.yml:stack-custom.yml"
33 | export COMPOSE_ARG="-c ../stack.yml -c stack-custom.yml"
34 | fi
35 | fi
36 |
37 | if [[ "$1" == "docker" && "$2" == "stack" && "$3" == "deploy" ]]; then
38 | shift 3
39 | echo "RHUB> Calling docker stack deploy $COMPOSE_ARG $@"
40 | echo
41 | docker stack deploy $COMPOSE_ARG "$@"
42 | else
43 | echo "RHUB> Calling $@"
44 | echo
45 | "$@"
46 | fi
47 |
--------------------------------------------------------------------------------
/seed/Dockerfile:
--------------------------------------------------------------------------------
1 |
2 | FROM alpine:3.8
3 |
4 | RUN apk add --no-cache curl bash jq
5 |
6 | COPY . /seed
7 |
8 | CMD [ "echo" "Need to define entrypoint" ]
9 |
--------------------------------------------------------------------------------
/seed/app.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "views": {
4 | "email": {
5 | "map": "function (doc) { \n\temit([doc.email, doc.submitted], doc);\n\t}"
6 | },
7 | "emailpackage": {
8 | "map": "function (doc) {\n\temit([doc.email, doc.package, doc.submitted],doc)\n}\n"
9 | },
10 | "stats": {
11 | "map": "function (doc) {\n emit(\n doc.submitted,\n { email: doc.email,\n package: doc.package,\n platform: doc.platform.name,\n status: doc.status,\n submitted: doc.submitted,\n started: doc.started,\n build_time: doc.build_time\n });\n}\n"
12 | },
13 | "group": {
14 | "map": "function(doc) {\n if (!!doc.group) {\n emit([doc.email, doc.submitted, doc.group], doc.id );\n } else {\n emit([doc.email, doc.submitted, doc.id], doc.id);\n }\n}\n",
15 | "reduce": "function(keys, vals, re) {\n var a;\n\n var key = keys[0][2];\n for (a = 0; a < keys.length; a++)\n if (keys[a][2] !== key)\n return null;\n\n var result = [];\n for (a = 0; a < vals.length; a++)\n if (vals[a]) {\n result.push(vals[a]);\n }\n return result;\n}\n\n}\n"
16 | },
17 | "grouppackage": {
18 | "map": "function(doc) {\n if (!!doc.group) {\n emit([doc.email, doc.package, doc.submitted, doc.group], doc.id );\n } else {\n emit([doc.email, doc.package, doc.submitted, doc.id], doc.id);\n }\n}\n",
19 | "reduce": "function(keys, vals, re) {\n var a;\n\n var key = keys[0][3];\n for (a = 0; a < keys.length; a++)\n if (keys[a][3] !== key)\n return null;\n\n var result = [];\n for (a = 0; a < vals.length; a++)\n if (vals[a]) {\n result.push(vals[a]);\n }\n return result;\n}\n"
20 | },
21 | },
22 | "rewrites": [
23 | {
24 | "from": "/-/email/:email",
25 | "to": "_view/email",
26 | "query": {
27 | "start_key": [
28 | ":email",
29 | {}
30 | ],
31 | "end_key": [
32 | ":email"
33 | ],
34 | "descending": "true"
35 | }
36 | },
37 | {
38 | "from": "/-/package/:email/:package",
39 | "to": "_view/emailpackage",
40 | "query": {
41 | "start_key": [
42 | ":email",
43 | ":package",
44 | {}
45 | ],
46 | "end_key": [
47 | ":email",
48 | ":package"
49 | ],
50 | "descending": "true"
51 | }
52 | },
53 | {
54 | "from": "/-/group/email/:email",
55 | "to": "_view/group",
56 | "query": {
57 | "start_key": [
58 | ":email",
59 | {
60 | }
61 | ],
62 | "end_key": [
63 | ":email"
64 | ],
65 | "descending": "true",
66 | "group": "true"
67 | }
68 | },
69 | {
70 | "from": "/-/group/package/:email/:package",
71 | "to": "_view/grouppackage",
72 | "query": {
73 | "start_key": [
74 | ":email",
75 | ":package",
76 | {
77 | }
78 | ],
79 | "end_key": [
80 | ":email",
81 | ":package"
82 | ],
83 | "descending": "true",
84 | "group": "true"
85 | }
86 | }
87 | ]
88 | }
89 |
--------------------------------------------------------------------------------
/seed/logdb.sh:
--------------------------------------------------------------------------------
1 |
2 | echo DB is at $LOGDB_URL
3 |
4 | # The last part is the DB, so we remove that
5 | BASEURL="${LOGDB_URL%/*}"
6 |
7 | # Need to wait for the DB to be ready
8 | while true
9 | do
10 | echo "waiting for DB"
11 | if curl -s "$BASEURL"; then break; fi
12 | sleep 1
13 | done
14 |
15 | # Try creating a _users DB, to avoid error messages
16 | curl -s -X PUT "$BASEURL/_users" || true
17 |
18 | if curl -s -o /dev/null "$LOGDB_URL"; then
19 | echo Creating /logs DB
20 | curl -s -X PUT "$LOGDB_URL"
21 | else
22 | echo /logs DB already exists
23 | fi
24 |
25 | echo Adding/updating design document
26 | REV=$(curl -s "$LOGDB_URL"/_design/app | jq -r ._rev)
27 | if [[ "$REV" != "null" ]]; then
28 | curl --silent --request DELETE "$LOGDB_URL/_design/app?rev=$REV"
29 | fi
30 | curl --silent --header "Content-Type: application/json" \
31 | --request PUT --data @/seed/app.json \
32 | "$LOGDB_URL/_design/app"
33 |
--------------------------------------------------------------------------------
/stack.yml:
--------------------------------------------------------------------------------
1 | version: '3.3'
2 |
3 | volumes:
4 | uploads-data:
5 | session-data:
6 | log-data:
7 | queue-data:
8 | jenkins-data:
9 | artifacts-data:
10 |
11 | secrets:
12 | nginx.crt:
13 | external: true
14 | nginx.key:
15 | external: true
16 | jenkins.pass:
17 | file: ./jenkins.pass
18 | web.pass:
19 | file: ./web.pass
20 |
21 | services:
22 |
23 | frontend:
24 | build: ./frontend
25 | image: "rhub/frontend:${RHUB_VERSION}"
26 | environment:
27 | - GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID}
28 | - GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET}
29 | - JENKINS_URL=http://${JENKINS_USER}:@jenkins:8080/jenkins
30 | - LOGDB_URL=${LOGDB_URL}
31 | - MAILGUN_DOMAIN=${MAILGUN_DOMAIN}
32 | - MAILGUN_API_KEY=${MAILGUN_API_KEY}
33 | - RABBITMQ_URL=${RABBITMQ_URL}
34 | - REDIS_URL=${REDIS_URL}
35 | - REDIS_EMAIL_URL=${REDIS_EMAIL_URL}
36 | - RHUB_ARTIFACTS_URL=${RHUB_ARTIFACTS_URL}
37 | - RHUB_BUILDER_EXTERNAL_URL=${RHUB_BUILDER_EXTERNAL_URL}
38 | - RHUB_BUILDER_URL=http://frontend:3000
39 | - RHUB_EMAIL_FROM=${RHUB_EMAIL_FROM}
40 | - RHUB_EMAIL_MODE=${RHUB_EMAIL_MODE}
41 | - RHUB_SMTP_SERVER=${RHUB_SMTP_SERVER}
42 | - RHUB_SMTP_USERNAME=${RHUB_SMTP_USERNAME}
43 | - RHUB_SMTP_PASSWORD=${RHUB_SMTP_PASSWORD}
44 | - RHUB_SMTP_PORT=${RHUB_SMTP_PORT}
45 | - RHUB_SMTP_TLS_REQUIRED=${RHUB_SMTP_TLS_REQUIRED}
46 | volumes:
47 | - uploads-data:/usr/src/app/uploads
48 | depends_on:
49 | - redis
50 | - queue
51 | - logdb
52 | - jenkins
53 | secrets:
54 | - jenkins.pass
55 |
56 | backend:
57 | build: ./backend
58 | image: "rhub/backend:${RHUB_VERSION}"
59 | environment:
60 | - RABBITMQ_URL=${RABBITMQ_URL}
61 | - JENKINS_URL=http://${JENKINS_USER}:@jenkins:8080/jenkins
62 | - RHUB_ARTIFACTS=${RHUB_ARTIFACTS}
63 | depends_on:
64 | - queue
65 | - jenkins
66 | secrets:
67 | - jenkins.pass
68 |
69 | redis:
70 | image: "redis:4.0.11-alpine"
71 | volumes:
72 | - session-data:/data
73 |
74 | queue:
75 | image: "rabbitmq:3.7.8-alpine"
76 | volumes:
77 | - queue-data:/var/lib/rabbitmq
78 |
79 | logdb:
80 | image: "couchdb:2.2"
81 | volumes:
82 | - log-data:/opt/couchdb/data
83 |
84 | logdb-seed:
85 | build: ./seed
86 | image: "rhub/seed:${RHUB_VERSION}"
87 | environment:
88 | - LOGDB_URL=${LOGDB_URL}
89 | entrypoint:
90 | - bash
91 | - -c
92 | - /seed/logdb.sh
93 | depends_on:
94 | - logdb
95 | deploy:
96 | restart_policy:
97 | condition: on-failure
98 |
99 | jenkins:
100 | build: ./jenkins
101 | image: "rhub/jenkins:${RHUB_VERSION}"
102 | volumes:
103 | - jenkins-data:/var/jenkins_home
104 | environment:
105 | - JENKINS_ROOT_URL=${JENKINS_ROOT_URL}
106 | - JENKINS_USER=${JENKINS_USER}
107 | - JENKINS_OPTS="--prefix=/jenkins"
108 | secrets:
109 | - jenkins.pass
110 | ports:
111 | - 50000:50000
112 |
113 | linux-builder:
114 | hostname: linux-builder
115 | build: ./linux-builder
116 | image: "rhub/linux-builder:${RHUB_VERSION}"
117 | volumes:
118 | - /var/run/docker.sock:/var/run/docker.sock
119 | - artifacts-data:/artifacts
120 | environment:
121 | - JENKINS_URL=http://jenkins:8080/jenkins
122 | - JENKINS_USER=${JENKINS_USER}
123 | - RHUB_EXECUTORS=4
124 | - JENKINS_LABELS=swarm linux
125 | - RHUB_CRAN_MIRROR=${RHUB_CRAN_MIRROR}
126 | - RHUB_ARTIFACTS=${RHUB_ARTIFACTS}
127 | - RHUB_BUILDER_URL=http://frontend:3000
128 | secrets:
129 | - jenkins.pass
130 |
131 | cron:
132 | build: ./cron
133 | image: "rhub/cron:${RHUB_VERSION}"
134 | environment:
135 | - JENKINS_URL=http://${JENKINS_USER}:@jenkins:8080/jenkins
136 | secrets:
137 | - jenkins.pass
138 |
139 | nginx:
140 | build: ./nginx
141 | image: "rhub/nginx:${RHUB_VERSION}"
142 | volumes:
143 | - artifacts-data:/artifacts:ro
144 | ports:
145 | - "80:80"
146 | - "443:443"
147 | depends_on:
148 | - frontend
149 | - jenkins
150 | entrypoint:
151 | - sh
152 | - -c
153 | - /entrypoint.sh
154 |
--------------------------------------------------------------------------------
/web.pass:
--------------------------------------------------------------------------------
1 | no
2 |
--------------------------------------------------------------------------------