├── .gitattributes ├── 9781484242803.jpg ├── Contributing.md ├── LICENSE.txt ├── README.md ├── binary-search-master ├── 01-search.py └── 02-search-corrected.py ├── deployment-utils-master ├── README.md ├── add-package ├── ansible │ ├── ansible.cfg │ ├── deploy-package-info.yml │ ├── deploy-python-matheval.yml │ ├── production │ └── testing ├── debian-autobuild ├── gocd │ ├── 00-agent.xml │ ├── 01-first-build-pipeline.xml │ ├── 02-build-versioning.xml │ ├── 03-build+upload.xml │ ├── 04-buil+upload+deployment.xml │ ├── 05-to-produciton.xml │ ├── 06-specific-version.xml │ ├── 07-params.xml │ ├── 08-template.xml │ ├── 09-material-ignore.xml │ └── 10-material-whitelist.xml ├── playground │ ├── Vagrantfile │ ├── ansible.cfg │ ├── files │ │ ├── guid.txt │ │ ├── key-control-file │ │ └── lighttpd.conf │ ├── hosts │ └── setup.yml └── smoke-tests │ └── python-matheval ├── errata.md ├── python-matheval-master ├── .gitignore ├── README.md ├── README.txt ├── conftest.py ├── debian │ ├── changelog │ ├── compat │ ├── control │ ├── python-matheval.service │ └── rules ├── matheval │ ├── __init__.py │ ├── evaluator.py │ └── frontend.py ├── requirements.txt ├── setup.cfg ├── setup.py └── test │ └── test_evaluator.py └── python-webcount-master ├── MANIFEST.in ├── conftest.py ├── requirements.txt ├── setup.py ├── test ├── __init__.py ├── functions.py └── test_functions.py ├── tox.ini └── webcount ├── __init__.py └── functions.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /9781484242803.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/python-continuous-integration-delivery/46f6edacb2d3222a56eb7a14c485e0b812236b7b/9781484242803.jpg -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to Apress Source Code 2 | 3 | Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers. 4 | 5 | ## How to Contribute 6 | 7 | 1. Make sure you have a GitHub account. 8 | 2. Fork the repository for the relevant book. 9 | 3. Create a new branch on which to make your change, e.g. 10 | `git checkout -b my_code_contribution` 11 | 4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted. 12 | 5. Submit a pull request. 13 | 14 | Thank you for your contribution! -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Freeware License, some rights reserved 2 | 3 | Copyright (c) 2019 Moritz Lenz 4 | 5 | Permission is hereby granted, free of charge, to anyone obtaining a copy 6 | of this software and associated documentation files (the "Software"), 7 | to work with the Software within the limits of freeware distribution and fair use. 8 | This includes the rights to use, copy, and modify the Software for personal use. 9 | Users are also allowed and encouraged to submit corrections and modifications 10 | to the Software for the benefit of other users. 11 | 12 | It is not allowed to reuse, modify, or redistribute the Software for 13 | commercial use in any way, or for a user’s educational materials such as books 14 | or blog articles without prior permission from the copyright holder. 15 | 16 | The above copyright notice and this permission notice need to be included 17 | in all copies or substantial portions of the software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS OR APRESS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apress Source Code 2 | 3 | This repository accompanies [*Python Continuous Integration and Delivery*](https://www.apress.com/9781484242803) by Moritz Lenz (Apress, 2019). 4 | 5 | [comment]: #cover 6 | ![Cover image](9781484242803.jpg) 7 | 8 | Download the files as a zip using the green button, or clone the repository to your machine using Git. 9 | 10 | ## Releases 11 | 12 | Release v1.0 corresponds to the code in the published book, without corrections or updates. 13 | 14 | ## Contributions 15 | 16 | See the file Contributing.md for more information on how you can contribute to this repository. -------------------------------------------------------------------------------- /binary-search-master/01-search.py: -------------------------------------------------------------------------------- 1 | def search(needle, haystack): 2 | left = 0 3 | right = len(haystack) - 1 4 | 5 | while left <= right: 6 | middle = left + (right - left) // 2 7 | middle_element = haystack[middle] 8 | if middle_element == needle: 9 | return middle 10 | elif middle_element < needle: 11 | left = middle 12 | else: 13 | right = middle 14 | raise ValueError("Value not in haystack") 15 | 16 | def test_search(): 17 | assert search(2, [1, 2, 3, 4]) == 1, \ 18 | 'found needle somewhere in the haystack' 19 | 20 | def test_search_first_element(): 21 | assert search(1, [1, 2, 3, 4]) == 0, \ 22 | 'search first element' 23 | 24 | -------------------------------------------------------------------------------- /binary-search-master/02-search-corrected.py: -------------------------------------------------------------------------------- 1 | def search(needle, haystack): 2 | left = 0 3 | right = len(haystack) - 1 4 | 5 | while left <= right: 6 | middle = left + (right - left) // 2 7 | middle_element = haystack[middle] 8 | if middle_element == needle: 9 | return middle 10 | elif middle_element < needle: 11 | left = middle + 1 12 | else: 13 | right = middle - 1 14 | raise ValueError("Value not in haystack") 15 | 16 | def test_search(): 17 | assert search(2, [1, 2, 3, 4]) == 1, \ 18 | 'found needle somewhere in the haystack' 19 | 20 | def test_search_first_element(): 21 | assert search(1, [1, 2, 3, 4]) == 0, \ 22 | 'search first element' 23 | 24 | def test_search_last_element(): 25 | assert search(4, [1, 2, 3, 4]) == 3, \ 26 | 'search last element' 27 | 28 | def test_exception_not_found(): 29 | from pytest import raises 30 | 31 | with raises(ValueError, message="left out of bound"): 32 | search(-1, [1, 2, 3, 4]) 33 | 34 | with raises(ValueError, message="right out of bound"): 35 | search(5, [1, 2, 3, 4]) 36 | 37 | with raises(ValueError, message="not found in middle"): 38 | search(2, [1, 3, 4]) 39 | 40 | -------------------------------------------------------------------------------- /deployment-utils-master/README.md: -------------------------------------------------------------------------------- 1 | Some small deployment utils used in [a book on automating 2 | deployments](https://deploybook.com/) (WIP). 3 | -------------------------------------------------------------------------------- /deployment-utils-master/add-package: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import json 3 | import os 4 | import os.path 5 | import subprocess 6 | import sys 7 | 8 | 9 | assert len(sys.argv) >= 4, \ 10 | 'Usage: add-package <.deb-file>+' 11 | 12 | env, distribution = sys.argv[1:3] 13 | packages = sys.argv[3:] 14 | 15 | base_path = os.path.expanduser('~') + '/aptly' 16 | repo_path = '/'.join((base_path, env, distribution)) 17 | config_file = '{}/{}-{}.conf'.format(base_path, env, distribution) 18 | 19 | def run_aptly(*args): 20 | aptly_cmd = ['aptly', '-config=' + config_file] 21 | subprocess.call(aptly_cmd + list(args)) 22 | 23 | def init_config(): 24 | os.makedirs(base_path, exist_ok=True) 25 | contents = { 26 | 'rootDir': repo_path, 27 | 'architectures': ['amd64', 'all'], 28 | } 29 | with open(config_file, 'w') as conf: 30 | json.dump(contents, conf) 31 | 32 | def init_repo(): 33 | if os.path.exists(repo_path + '/db'): 34 | return 35 | os.makedirs(repo_path, exist_ok=True) 36 | run_aptly('repo', 'create', '-distribution=' + distribution, 'myrepo') 37 | run_aptly('publish', 'repo', 'myrepo') 38 | 39 | def add_packages(): 40 | for pkg in packages: 41 | run_aptly('repo', 'add', 'myrepo', pkg) 42 | run_aptly('publish', 'update', distribution) 43 | 44 | if __name__ == '__main__': 45 | init_config(); 46 | init_repo(); 47 | add_packages(); 48 | -------------------------------------------------------------------------------- /deployment-utils-master/ansible/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | remote_user = root 3 | host_key_checking = False 4 | -------------------------------------------------------------------------------- /deployment-utils-master/ansible/deploy-package-info.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: web 3 | serial: 1 4 | max_fail_percentage: 1 5 | tasks: 6 | - apt: update_cache=yes package=package-info={{package_version}} state=present force=yes 7 | -------------------------------------------------------------------------------- /deployment-utils-master/ansible/deploy-python-matheval.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: web 3 | serial: 1 4 | max_fail_percentage: 1 5 | tasks: 6 | - apt: update_cache=yes package=python-matheval={{package_version}} state=present force=yes 7 | - local_action: command ../smoke-tests/python-matheval "{{ansible_ssh_host}}" 8 | changed_when: False 9 | -------------------------------------------------------------------------------- /deployment-utils-master/ansible/production: -------------------------------------------------------------------------------- 1 | [web] 2 | production.local ansible_ssh_host=172.28.128.4 3 | 4 | 5 | -------------------------------------------------------------------------------- /deployment-utils-master/ansible/testing: -------------------------------------------------------------------------------- 1 | [web] 2 | testing.local ansible_ssh_host=172.28.128.3 3 | 4 | -------------------------------------------------------------------------------- /deployment-utils-master/debian-autobuild: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -o pipefail 5 | version=$(git describe --long |sed 's/-g[A-Fa-f0-9]*$//') 6 | version="$version.${GO_PIPELINE_COUNTER:-0}.${GO_STAGE_COUNTER:-0}" 7 | 8 | echo $version 9 | echo $version > ../version 10 | 11 | export DEBFULLNAME='Go Debian Build Agent' 12 | export DEBEMAIL='go-noreply@example.com' 13 | debchange --newversion=$version --force-distribution -b \ 14 | --distribution="${DISTRIBUTION:-jessie}" 'New Version' 15 | dpkg-buildpackage -b -us -uc 16 | 17 | -------------------------------------------------------------------------------- /deployment-utils-master/gocd/00-agent.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | debian-stretch 5 | build 6 | aptly 7 | 8 | 9 | -------------------------------------------------------------------------------- /deployment-utils-master/gocd/01-first-build-pipeline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -c 12 | dpkg-buildpackage -b -us -uc 13 | 14 | 15 | 16 | 18 | 19 | 20 | debian-stretch 21 | build 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /deployment-utils-master/gocd/02-build-versioning.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | debian-stretch 23 | build 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /deployment-utils-master/gocd/03-build+upload.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | debian-stretch 15 | build 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -c 33 | deployment-utils/add-package testing stretch *.deb 34 | 35 | 36 | 37 | aptly 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /deployment-utils-master/gocd/04-buil+upload+deployment.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | debian-stretch 15 | build 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -c 33 | deployment-utils/add-package testing stretch *.deb 34 | 35 | 36 | 37 | aptly 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | --inventory-file=testing 48 | web 49 | -m 50 | apt 51 | -a 52 | name=python-matheval state=latest update_cache=yes 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /deployment-utils-master/gocd/05-to-produciton.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | debian-stretch 15 | build 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -c 33 | deployment-utils/add-package testing stretch *.deb 34 | 35 | 36 | 37 | aptly 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | --inventory-file=testing 48 | web 49 | -m 50 | apt 51 | -a 52 | name=python-matheval state=latest update_cache=yes 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -c 69 | deployment-utils/add-package production stretch *.deb 70 | 71 | 72 | 73 | aptly 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | --inventory-file=production 84 | web 85 | -m 86 | apt 87 | -a 88 | name=python-matheval state=latest update_cache=yes 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /deployment-utils-master/gocd/06-specific-version.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | debian-stretch 15 | build 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -c 33 | deployment-utils/add-package testing stretch *.deb 34 | 35 | 36 | 37 | aptly 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -c 49 | ansible-playbook --inventory-file=testing --extra-vars="package_version=$(< ../../version)" deploy-python-matheval.yml 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -c 65 | deployment-utils/add-package production stretch *.deb 66 | 67 | 68 | 69 | aptly 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -c 81 | ansible-playbook --inventory-file=production --extra-vars="package_version=$(< ../../version)" deploy-python-matheval.yml 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /deployment-utils-master/gocd/07-params.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | stretch 5 | deploy-python-matheval.yml 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | debian-stretch 19 | build 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -c 37 | deployment-utils/add-package testing #{distribution} *.deb 38 | 39 | 40 | 41 | aptly 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -c 53 | ansible-playbook --inventory-file=testing --extra-vars="package_version=$(< ../../version)" #{deployment_playbook} 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -c 69 | deployment-utils/add-package production #{distribution} *.deb 70 | 71 | 72 | 73 | aptly 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -c 85 | ansible-playbook --inventory-file=production --extra-vars="package_version=$(< ../../version)" #{deployment_playbook} 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /deployment-utils-master/gocd/08-template.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | stretch 5 | deploy-python-matheval.yml 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | debian-stretch 23 | build 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -c 41 | deployment-utils/add-package testing #{distribution} *.deb 42 | 43 | 44 | 45 | aptly 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -c 57 | ansible-playbook --inventory-file=testing --extra-vars="package_version=$(< ../../version)" #{deployment_playbook} 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -c 73 | deployment-utils/add-package production #{distribution} *.deb 74 | 75 | 76 | 77 | aptly 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -c 89 | ansible-playbook --inventory-file=production --extra-vars="package_version=$(< ../../version)" #{deployment_playbook} 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /deployment-utils-master/gocd/09-material-ignore.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | stretch 5 | deploy-python-matheval.yml 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | debian-stretch 28 | build 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -c 46 | deployment-utils/add-package testing #{distribution} *.deb 47 | 48 | 49 | 50 | aptly 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -c 62 | ansible-playbook --inventory-file=testing --extra-vars="package_version=$(< ../../version)" #{deployment_playbook} 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -c 78 | deployment-utils/add-package production #{distribution} *.deb 79 | 80 | 81 | 82 | aptly 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -c 94 | ansible-playbook --inventory-file=production --extra-vars="package_version=$(< ../../version)" #{deployment_playbook} 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /deployment-utils-master/gocd/10-material-whitelist.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | stretch 5 | deploy-python-matheval.yml 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | debian-stretch 29 | build 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -c 47 | deployment-utils/add-package testing #{distribution} *.deb 48 | 49 | 50 | 51 | aptly 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -c 63 | ansible-playbook --inventory-file=testing --extra-vars="package_version=$(< ../../version)" #{deployment_playbook} 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -c 79 | deployment-utils/add-package production #{distribution} *.deb 80 | 81 | 82 | 83 | aptly 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -c 95 | ansible-playbook --inventory-file=production --extra-vars="package_version=$(< ../../version)" #{deployment_playbook} 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /deployment-utils-master/playground/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure(2) do |config| 5 | config.vm.box = "debian/stretch64" 6 | 7 | { 8 | 'testing' => "172.28.128.3", 9 | 'production' => "172.28.128.4", 10 | 'go-agent' => "172.28.128.5", 11 | }.each do |name, ip| 12 | config.vm.define name do |instance| 13 | instance.vm.network "private_network", ip: ip, 14 | auto_config: false 15 | instance.vm.hostname = name + '.local' 16 | end 17 | end 18 | 19 | config.vm.provision "shell" do |s| 20 | ssh_pub_key = File.readlines("#{Dir.home}/.ssh/id_rsa.pub") 21 | .first.strip 22 | s.inline = <<-SHELL 23 | mkdir -p /root/.ssh 24 | echo #{ssh_pub_key} >> /root/.ssh/authorized_keys 25 | SHELL 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /deployment-utils-master/playground/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | host_key_checking = False 3 | inventory = hosts 4 | pipelining=True 5 | -------------------------------------------------------------------------------- /deployment-utils-master/playground/files/guid.txt: -------------------------------------------------------------------------------- 1 | 51ff989b-1a4e-4772-a6d2-b5df1df02143 2 | -------------------------------------------------------------------------------- /deployment-utils-master/playground/files/key-control-file: -------------------------------------------------------------------------------- 1 | %echo Generating a basic OpenPGP key 2 | Key-Type: RSA 3 | Key-Length: 1024 4 | Subkey-Type: RSA 5 | Name-Real: Go Build Agent 6 | Name-Email: go-noreply@go-agent.local 7 | Expire-Date: 0 8 | %commit 9 | %echo done 10 | -------------------------------------------------------------------------------- /deployment-utils-master/playground/files/lighttpd.conf: -------------------------------------------------------------------------------- 1 | dir-listing.encoding = "utf-8" 2 | server.dir-listing = "enable" 3 | alias.url = ( 4 | "/debian/testing/jessie/" => "/var/go/aptly/testing/jessie/public/", 5 | "/debian/production/jessie/" => "/var/go/aptly/production/jessie/public/", 6 | "/debian/testing/stretch/" => "/var/go/aptly/testing/stretch/public/", 7 | "/debian/production/stretch/" => "/var/go/aptly/production/stretch/public/", 8 | ) 9 | 10 | -------------------------------------------------------------------------------- /deployment-utils-master/playground/hosts: -------------------------------------------------------------------------------- 1 | [all:vars] 2 | ansible_ssh_user=root 3 | 4 | [go-agent] 5 | go-agent.local ansible_ssh_host=172.28.128.5 6 | 7 | [aptly] 8 | go-agent.local 9 | 10 | [target] 11 | testing.local ansible_ssh_host=172.28.128.3 12 | production.local ansible_ssh_host=172.28.128.4 13 | 14 | [testing] 15 | testing.local 16 | 17 | [production] 18 | production.local 19 | -------------------------------------------------------------------------------- /deployment-utils-master/playground/setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: go-agent 3 | vars: 4 | go_server: 172.28.128.1 5 | tasks: 6 | - group: name=go system=yes 7 | - name: Make sure the go user has an SSH key 8 | user: > 9 | name=go system=yes group=go generate_ssh_key=yes 10 | home=/var/go 11 | - name: Fetch the ssh public key, so we can distribute it. 12 | fetch: 13 | src: /var/go/.ssh/id_rsa.pub 14 | dest: go-rsa.pub 15 | fail_on_missing: yes 16 | flat: yes 17 | - apt: > 18 | package=apt-transport-https state=present 19 | update_cache=yes 20 | - apt_key: 21 | url: https://download.gocd.org/GOCD-GPG-KEY.asc 22 | state: present 23 | validate_certs: no 24 | - apt_repository: 25 | repo: 'deb https://download.gocd.org /' 26 | state: present 27 | - apt: package={{item}} state=present force=yes 28 | with_items: 29 | - openjdk-8-jre-headless 30 | - go-agent 31 | - git 32 | 33 | - file: 34 | path: /var/lib/go-agent/config 35 | state: directory 36 | owner: go 37 | group: go 38 | - copy: 39 | src: files/guid.txt 40 | dest: /var/lib/go-agent/config/guid.txt 41 | owner: go 42 | group: go 43 | - name: Go agent configuration for versions 16.8 and above 44 | lineinfile: 45 | dest: /etc/default/go-agent 46 | regexp: ^GO_SERVER_URL= 47 | line: GO_SERVER_URL=https://{{ go_server }}:8154/go 48 | - service: name=go-agent enabled=yes state=started 49 | 50 | - hosts: aptly 51 | tasks: 52 | - apt: package={{item}} state=present 53 | with_items: 54 | - ansible 55 | - aptly 56 | - build-essential 57 | - curl 58 | - devscripts 59 | - dh-systemd 60 | - dh-virtualenv 61 | - gnupg2 62 | - libjson-perl 63 | - python-setuptools 64 | - lighttpd 65 | - rng-tools 66 | - copy: 67 | src: files/key-control-file-gpg2 68 | dest: /var/go/key-control-file 69 | - command: killall rngd 70 | ignore_errors: yes 71 | changed_when: False 72 | - command: rngd -r /dev/urandom 73 | changed_when: False 74 | - command: gpg --gen-key --batch /var/go/key-control-file 75 | args: 76 | creates: /var/go/.gnupg/pubring.gpg 77 | become_user: go 78 | become: true 79 | changed_when: False 80 | - shell: gpg --export --armor > /var/go/pubring.asc 81 | args: 82 | creates: /var/go/pubring.asc 83 | become_user: go 84 | become: true 85 | - fetch: 86 | src: /var/go/pubring.asc 87 | dest: deb-key.asc 88 | fail_on_missing: yes 89 | flat: yes 90 | - name: Bootstrap aptly repos on the `target` machines 91 | copy: 92 | src: ../add-package 93 | dest: /usr/local/bin/add-package 94 | mode: 0755 95 | - name: Download an example package to fill the repo with 96 | get_url: 97 | url: https://perlgeek.de/static/dummy.deb 98 | dest: /tmp/dummy.deb 99 | - command: > 100 | /usr/local/bin/add-package {{item}} 101 | stretch /tmp/dummy.deb 102 | with_items: 103 | - testing 104 | - production 105 | become_user: go 106 | become: true 107 | - user: name=www-data groups=go 108 | 109 | - name: Configure lighttpd to serve the aptly directories 110 | copy: 111 | src: files/lighttpd.conf 112 | dest: /etc/lighttpd/conf-enabled/30-aptly.conf 113 | - service: name=lighttpd state=restarted enabled=yes 114 | 115 | - hosts: target 116 | tasks: 117 | - authorized_key: 118 | user: root 119 | key: "{{ lookup('file', 'go-rsa.pub') }}" 120 | - apt_key: 121 | data: "{{ lookup('file', 'deb-key.asc') }}" 122 | state: present 123 | 124 | - hosts: production 125 | tasks: 126 | - apt_repository: 127 | repo: > 128 | deb http://172.28.128.5/debian/production/stretch 129 | stretch main 130 | state: present 131 | 132 | - hosts: testing 133 | tasks: 134 | - apt_repository: 135 | repo: 136 | deb http://172.28.128.5/debian/testing/stretch 137 | stretch main 138 | state: present 139 | 140 | - hosts: go-agent 141 | tasks: 142 | - name: 'Checking SSH connectivity to {{item}}' 143 | become: True 144 | become_user: go 145 | command: > 146 | ssh -o StrictHostkeyChecking=No 147 | root@"{{ hostvars[item]['ansible_ssh_host'] }}" true 148 | changed_when: false 149 | with_items: 150 | - testing.local 151 | - production.local 152 | -------------------------------------------------------------------------------- /deployment-utils-master/smoke-tests/python-matheval: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | curl --silent -H "Accept: application/json" --data '["+", 37, 5]' -XPOST http://$1:8800/ | grep ^42$ 3 | -------------------------------------------------------------------------------- /errata.md: -------------------------------------------------------------------------------- 1 | # Errata for *Book Title* 2 | 3 | On **page xx** [Summary of error]: 4 | 5 | Details of error here. Highlight key pieces in **bold**. 6 | 7 | *** 8 | 9 | On **page xx** [Summary of error]: 10 | 11 | Details of error here. Highlight key pieces in **bold**. 12 | 13 | *** -------------------------------------------------------------------------------- /python-matheval-master/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.swp 3 | dist 4 | MANIFEST 5 | debian/debhelper-build-stamp 6 | debian/files 7 | debian/python-matheval.debhelper.log 8 | debian/python-matheval.*.debhelper 9 | debian/python-matheval.substvars 10 | debian/python-matheval/ 11 | src/matheval.egg-info/ 12 | 13 | -------------------------------------------------------------------------------- /python-matheval-master/README.md: -------------------------------------------------------------------------------- 1 | python-matheval is the second example project for [my book in progress on Continuous Delivery](https://deploybook.com/). 2 | 3 | It is a python-based web service that evaluates a tree of mathematical expresions. For example it turns the JSON tree 4 | 5 | ["*", ["+", 2, 3], 4] 6 | 7 | which corresponds to (2 + 3) * 4, into the result, 20. 8 | 9 | It is a flask application, packaged with [dh-virtualenv](https://github.com/spotify/dh-virtualenv), run by [gunicorn](http://gunicorn.org/) and controlled through systemd. 10 | -------------------------------------------------------------------------------- /python-matheval-master/README.txt: -------------------------------------------------------------------------------- 1 | This is an example project for a Continuous Delivery project, see https://deploybook.com/ 2 | 3 | It evaluates trees of mathematical expression, for example 2 * (4 + 1) would be 4 | expressed as ['*', 2, ['+', 4, 1]], and evaluates to 10. 5 | -------------------------------------------------------------------------------- /python-matheval-master/conftest.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/python-continuous-integration-delivery/46f6edacb2d3222a56eb7a14c485e0b812236b7b/python-matheval-master/conftest.py -------------------------------------------------------------------------------- /python-matheval-master/debian/changelog: -------------------------------------------------------------------------------- 1 | python-matheval (0.1) stable; urgency=medium 2 | 3 | * Initial Release. 4 | 5 | -- Moritz Lenz Sun, 12 Jun 2016 13:29:12 +0200 6 | -------------------------------------------------------------------------------- /python-matheval-master/debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /python-matheval-master/debian/control: -------------------------------------------------------------------------------- 1 | Source: python-matheval 2 | Section: main 3 | Priority: optional 4 | Maintainer: Moritz Lenz 5 | Build-Depends: debhelper (>=9), dh-virtualenv, dh-systemd, python-setuptools 6 | Standards-Version: 3.9.6 7 | 8 | Package: python-matheval 9 | Architecture: any 10 | Depends: ${shlibs:Depends}, ${misc:Depends}, python3 (>= 3.4) 11 | Description: A web service that evaluates trees of mathematical expressions. 12 | -------------------------------------------------------------------------------- /python-matheval-master/debian/python-matheval.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Evaluates mathematical expression 3 | Requires=network.target 4 | After=network.target 5 | 6 | [Service] 7 | Type=simple 8 | SyslogIdentifier=python-matheval 9 | User=nobody 10 | ExecStart=/usr/share/python-custom/python-matheval/bin/gunicorn --bind 0.0.0.0:8800 matheval.frontend:app 11 | 12 | PrivateTmp=yes 13 | InaccessibleDirectories=/home 14 | ReadOnlyDirectories=/bin /sbin /usr /lib /etc 15 | 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /python-matheval-master/debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | export DH_VIRTUALENV_INSTALL_ROOT=/usr/share/python-custom 3 | 4 | 5 | %: 6 | dh $@ --with python-virtualenv --with systemd 7 | 8 | override_dh_virtualenv: 9 | dh_virtualenv --python=/usr/bin/python3 --setuptools-test 10 | -------------------------------------------------------------------------------- /python-matheval-master/matheval/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/python-continuous-integration-delivery/46f6edacb2d3222a56eb7a14c485e0b812236b7b/python-matheval-master/matheval/__init__.py -------------------------------------------------------------------------------- /python-matheval-master/matheval/evaluator.py: -------------------------------------------------------------------------------- 1 | from functools import reduce 2 | import operator 3 | 4 | ops = { 5 | '+': operator.add, 6 | '-': operator.add, 7 | '*': operator.mul, 8 | '/': operator.truediv, 9 | } 10 | 11 | def math_eval(tree): 12 | if not isinstance(tree, list): 13 | return tree 14 | op = ops[tree.pop(0)] 15 | return reduce(op, map(math_eval, tree)) 16 | -------------------------------------------------------------------------------- /python-matheval-master/matheval/frontend.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | from flask import Flask, request 4 | 5 | from matheval.evaluator import math_eval 6 | 7 | app = Flask(__name__) 8 | 9 | @app.route('/', methods=['GET', 'POST']) 10 | def index(): 11 | tree = request.get_json(force=True) 12 | result = math_eval(tree); 13 | return str(result) + "\n" 14 | 15 | if __name__ == '__main__': 16 | app.run(debug=True) 17 | -------------------------------------------------------------------------------- /python-matheval-master/requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | pytest 3 | gunicorn 4 | -------------------------------------------------------------------------------- /python-matheval-master/setup.cfg: -------------------------------------------------------------------------------- 1 | [aliases] 2 | test=pytest 3 | -------------------------------------------------------------------------------- /python-matheval-master/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup 4 | 5 | setup(name='matheval', 6 | version='0.1', 7 | description='Evaluation of expression trees', 8 | author='Moritz Lenz', 9 | author_email='moritz.lenz@gmail.com', 10 | url='https://deploybook.com/', 11 | requires=['flask', 'pytest', 'gunicorn'], 12 | setup_requires=['pytest-runner'], 13 | packages=['matheval'] 14 | ) 15 | -------------------------------------------------------------------------------- /python-matheval-master/test/test_evaluator.py: -------------------------------------------------------------------------------- 1 | from matheval.evaluator import math_eval 2 | 3 | def test_identity(): 4 | assert math_eval(5) == 5, 'identity' 5 | 6 | def test_single_element(): 7 | assert math_eval(['+', 5]) == 5, 'single element' 8 | 9 | def test_addition(): 10 | assert math_eval(['+', 5, 7]) == 12, 'adding two numbers' 11 | 12 | def test_nested(): 13 | assert math_eval(['*', ['+', 5, 4], 2]) == 18 14 | -------------------------------------------------------------------------------- /python-webcount-master/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include conftest.py 2 | -------------------------------------------------------------------------------- /python-webcount-master/conftest.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/python-continuous-integration-delivery/46f6edacb2d3222a56eb7a14c485e0b812236b7b/python-webcount-master/conftest.py -------------------------------------------------------------------------------- /python-webcount-master/requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | -------------------------------------------------------------------------------- /python-webcount-master/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name = "webcount", 5 | version = "0.1", 6 | license = "BSD", 7 | packages=['webcount', 'test'], 8 | install_requires=['requests'], 9 | ) 10 | -------------------------------------------------------------------------------- /python-webcount-master/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/python-continuous-integration-delivery/46f6edacb2d3222a56eb7a14c485e0b812236b7b/python-webcount-master/test/__init__.py -------------------------------------------------------------------------------- /python-webcount-master/test/functions.py: -------------------------------------------------------------------------------- 1 | from unittest.mock import Mock, patch 2 | 3 | from webcount import most_common_word_in_web_page 4 | 5 | def test_with_patch(): 6 | mock_requests = Mock() 7 | mock_requests.get.return_value.text = 'aa bbb c' 8 | with patch('webcount.functions.requests', mock_requests): 9 | result = most_common_word_in_web_page( 10 | ['a', 'b', 'c'], 11 | 'https://python.org/', 12 | ) 13 | assert result == 'b', \ 14 | 'most_common_word_in_web_page tested with test double' 15 | assert mock_requests.get.call_count == 1 16 | assert mock_requests.get.call_args[0][0] \ 17 | == 'https://python.org/', 'called with right URL' 18 | 19 | -------------------------------------------------------------------------------- /python-webcount-master/test/test_functions.py: -------------------------------------------------------------------------------- 1 | from unittest.mock import Mock, patch 2 | 3 | from webcount import most_common_word_in_web_page 4 | 5 | def test_with_patch(): 6 | mock_requests = Mock() 7 | mock_requests.get.return_value.text = 'aa bbb c' 8 | with patch('webcount.functions.requests', mock_requests): 9 | result = most_common_word_in_web_page( 10 | ['a', 'b', 'c'], 11 | 'https://python.org/', 12 | ) 13 | assert result == 'b', \ 14 | 'most_common_word_in_web_page tested with test double' 15 | assert mock_requests.get.call_count == 1 16 | assert mock_requests.get.call_args[0][0] \ 17 | == 'https://python.org/', 'called with right URL' 18 | 19 | -------------------------------------------------------------------------------- /python-webcount-master/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py35 3 | 4 | [testenv] 5 | deps = pytest 6 | requests 7 | commands = pytest --junitxml=junit-{envname}.xml 8 | -------------------------------------------------------------------------------- /python-webcount-master/webcount/__init__.py: -------------------------------------------------------------------------------- 1 | from .functions import most_common_word_in_web_page 2 | -------------------------------------------------------------------------------- /python-webcount-master/webcount/functions.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | def most_common_word_in_web_page(words, url): 4 | """ 5 | finds the most common word from a list of words 6 | in a web page, identified by its URL 7 | """ 8 | response = requests.get(url) 9 | return most_common_word(words, response.text) 10 | 11 | 12 | def most_common_word(words, text): 13 | """ 14 | finds the most common word from a list of words 15 | in a piece of text 16 | """ 17 | word_frequency = {w: text.count(w) for w in words} 18 | return sorted(words, key=word_frequency.get)[-1] 19 | --------------------------------------------------------------------------------