diff --git a/jenkins_backend/README.md b/jenkins_backend/README.md new file mode 100644 index 0000000..1e9ea8a --- /dev/null +++ b/jenkins_backend/README.md @@ -0,0 +1,69 @@ +toaster.do Jenkins backend +========================== + +These python modules serve as the Jenkins backend to the Devuan SDK's +Web interface. They are to be executed by the web CGI when a new build +is requested. + + +Configuration +------------- + +This backend is configured using two files. `config.py` and +`jenkins_creds.py`. The former contains variables that tell the backend +in what way to work and where to look for files. The latter - +`jenkins_creds.py` is not included in the git repository as it contains +important credentials which allow the backend to work with Jenkins' API. + +The `jenkins_creds.py` file should look like the following: +``` +jenkins_host = 'https://sdk.dyne.org:4443' +jenkins_user = 'toaster' +jenkins_pass = 'thetoasterpassword' +``` + +These files will be read and imported by `sync_jobs.py` when ran. + + +Usage +----- + +``` +usage: sync_jobs.py [-h] [-a] [-d] [-n] jobname + +positional arguments: + jobname + +optional arguments: + -h, --help show this help message and exit + -a, --add + -d, --delete + -n, --dryrun +``` + +The `jobname` argument should be in a specific format. It should contain +the requester's email, which sdk was chosen, the requested architecture, +and a timestamp. + +In case of vm-sdk or live-sdk, these would look like: + +``` +parazyd@dyne.org-vm_amd64-1537977964 +parazyd@dyne.org-live_amd64-1537977964 +``` + +In case of arm-sdk, we also need to know the board we're building for: + +``` +parazyd@dyne.org-arm_armhf_sunxi-1537977964 +``` + +All of this combined, the required command to add a new job to Jenkins +would look something like the following: + +``` +sync_jobs.py -a parazyd@dyne.org-vm_amd64-1537977964 +``` + +In case of removing an existing job, all of the above applies the same +way. You just have to use `-d` instead of `-a`. diff --git a/jenkins_backend/config.py b/jenkins_backend/config.py new file mode 100644 index 0000000..77cb17c --- /dev/null +++ b/jenkins_backend/config.py @@ -0,0 +1,4 @@ +#!/usr/bin/env python3 + +# Physical path to where jobs are held +jobpath = '/srv/toaster' diff --git a/jenkins_backend/sync_jobs.py b/jenkins_backend/sync_jobs.py new file mode 100755 index 0000000..ead073b --- /dev/null +++ b/jenkins_backend/sync_jobs.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +""" +Module for backend talk with Jenkins executed by the web/CGI +""" + +from argparse import ArgumentParser +from jenkins import Jenkins + +from config import jobpath +from jenkins_creds import (jenkins_host, jenkins_user, jenkins_pass) + + +def html_escape(string): + """ + Function for escaping certain symbols to XML-compatible sequences. + """ + html_codes = [("'", '''), ('"', '"'), ('&', '&'), + ('<', '<'), ('>', '>')] + for i in html_codes: + string = string.replace(i[0], i[1]) + + return string + + +def add_job(japi, jobname): + """ + Function for adding a job to Jenkins. + """ + info = jobname.split('-') + desc = 'WebSDK build for: %s\nStarted: %s' % (info[0], info[2]) + sdk = info[1].split('_')[0] + arch = info[1].split('_')[1] + blendfile = '%s/%s/Dockerfile' % (jobpath, jobname) + + if sdk == 'arm': + board = info[1].split('_')[2] + zshcmd = 'load devuan %s %s' % (board, blendfile) + elif sdk == 'live': + zshcmd = 'load devuan %s %s' % (arch, blendfile) + elif sdk == 'vm': + zshcmd = 'load devuan %s' % (blendfile) + + command = "zsh -f -c 'source sdk && %s && build_image_dist'" % zshcmd + command = html_escape(command) + + replacements = [('DESC', desc), + ('SDK', sdk), + ('ARCH', arch), + ('COMMAND', command)] + + sdk_job = open('toasterbuild.xml', encoding='utf-8').read() + + for i in replacements: + sdk_job = sdk_job.replace('{{{%s}}}' % i[0], i[1]) + + return japi.create_job(jobname, sdk_job) + + +def del_job(japi, jobname): + """ + Function for deleting a Jenkins job. + """ + return japi.delete_job(jobname) + + +def main(): + """ + Main routine. + """ + parser = ArgumentParser() + parser.add_argument('-a', '--add', action='store_true') + parser.add_argument('-d', '--delete', action='store_true') + parser.add_argument('-n', '--dryrun', action='store_true') + parser.add_argument('jobname') + # NOTE: jobname should be email-arch-date, and a predefined directory + # somewhere on the filesystem. e.g.: + # - parazyd@dyne.org-vm_amd64-198374198 + # - parazyd@dyne.org-arm_armhf_raspi2-2198361991 + + args = parser.parse_args() + + japi = Jenkins(jenkins_host, username=jenkins_user, password=jenkins_pass) + + if args.add: + if args.dryrun: + print('Would add:', args.jobname) + return + print('Adding job:', args.jobname) + add_job(japi, args.jobname) + elif args.delete: + if args.dryrun: + print('Would remove:', args.jobname) + return + print('Removing job:', args.jobname) + del_job(japi, args.jobname) + + +if __name__ == '__main__': + main()