├── .gitattributes ├── sources ├── add2vals.py ├── calc.py └── test_calc.py ├── README.md ├── jenkins └── Jenkinsfile └── .gitignore /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto -------------------------------------------------------------------------------- /sources/add2vals.py: -------------------------------------------------------------------------------- 1 | ''' 2 | A simple command line tool that takes 2 values and adds them together using 3 | the calc.py library's 'add2' function. 4 | ''' 5 | 6 | import sys 7 | import calc 8 | 9 | argnumbers = len(sys.argv) - 1 10 | 11 | if argnumbers == 2 : 12 | print("") 13 | print("The result is " + str(calc.add2(str(sys.argv[1]), str(sys.argv[2])))) 14 | print("") 15 | sys.exit(0) 16 | 17 | if argnumbers != 2 : 18 | print("") 19 | print("You entered " + str(argnumbers) + " value/s.") 20 | print("") 21 | print("Usage: 'add2vals X Y' where X and Y are individual values.") 22 | print(" If add2vals is not in your path, usage is './add2vals X Y'.") 23 | print(" If unbundled, usage is 'python add2vals.py X Y'.") 24 | print("") 25 | sys.exit(1) 26 | -------------------------------------------------------------------------------- /sources/calc.py: -------------------------------------------------------------------------------- 1 | ''' 2 | The 'calc' library contains the 'add2' function that takes 2 values and adds 3 | them together. If either value is a string (or both of them are) 'add2' ensures 4 | they are both strings, thereby resulting in a concatenated result. 5 | NOTE: If a value submitted to the 'add2' function is a float, it must be done so 6 | in quotes (i.e. as a string). 7 | ''' 8 | 9 | # If 'value' is not an integer, convert it to a float and failing that, a string. 10 | def conv(value): 11 | try: 12 | return int(value) 13 | except ValueError: 14 | try: 15 | return float(value) 16 | except ValueError: 17 | return str(value) 18 | 19 | # The 'add2' function itself 20 | def add2(arg1, arg2): 21 | # Convert 'arg1' and 'arg2' to their appropriate types 22 | arg1conv = conv(arg1) 23 | arg2conv = conv(arg2) 24 | # If either 'arg1' or 'arg2' is a string, ensure they're both strings. 25 | if isinstance(arg1conv, str) or isinstance(arg2conv, str): 26 | arg1conv = str(arg1conv) 27 | arg2conv = str(arg2conv) 28 | return arg1conv + arg2conv 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # simple-python-pyinstaller-app 2 | 3 | This repository is for the 4 | [Build a Python app with PyInstaller](https://jenkins.io/doc/tutorials/build-a-python-app-with-pyinstaller/) 5 | tutorial in the [Jenkins User Documentation](https://jenkins.io/doc/). 6 | 7 | The repository contains a simple Python application which is a command line tool "add2vals" that outputs the addition of two values. If at least one of the 8 | values is a string, "add2vals" treats both values as a string and instead 9 | concatenates the values. The "add2" function in the "calc" library (which 10 | "add2vals" imports) is accompanied by a set of unit tests. These are tested with pytest to check that this function works as expected and the results are saved 11 | to a JUnit XML report. 12 | 13 | The delivery of the "add2vals" tool through PyInstaller converts this tool into 14 | a standalone executable file for Linux, which you can download through Jenkins 15 | and execute at the command line on Linux machines without Python. 16 | 17 | The `jenkins` directory contains an example of the `Jenkinsfile` (i.e. Pipeline) 18 | you'll be creating yourself during the tutorial. 19 | -------------------------------------------------------------------------------- /jenkins/Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent none 3 | stages { 4 | stage('Build') { 5 | agent { 6 | docker { 7 | image 'python:2-alpine' 8 | } 9 | } 10 | steps { 11 | sh 'python -m py_compile sources/add2vals.py sources/calc.py' 12 | } 13 | } 14 | stage('Test') { 15 | agent { 16 | docker { 17 | image 'qnib/pytest' 18 | } 19 | } 20 | steps { 21 | sh 'py.test --verbose --junit-xml test-reports/results.xml sources/test_calc.py' 22 | } 23 | post { 24 | always { 25 | junit 'test-reports/results.xml' 26 | } 27 | } 28 | } 29 | stage('Deliver') { 30 | agent { 31 | docker { 32 | image 'cdrx/pyinstaller-linux:python2' 33 | } 34 | } 35 | steps { 36 | sh 'pyinstaller --onefile sources/add2vals.py' 37 | } 38 | post { 39 | success { 40 | archiveArtifacts 'dist/add2vals' 41 | } 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /sources/test_calc.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import calc 3 | 4 | class TestCalc(unittest.TestCase): 5 | """ 6 | Test the add function from the calc library 7 | """ 8 | 9 | def test_add_integers(self): 10 | """ 11 | Test that the addition of two integers returns the correct total 12 | """ 13 | result = calc.add2(1, 2) 14 | self.assertEqual(result, 3) 15 | 16 | def test_add_floats(self): 17 | """ 18 | Test that the addition of two floats returns the correct result 19 | """ 20 | result = calc.add2('10.5', 2) 21 | self.assertEqual(result, 12.5) 22 | 23 | def test_add_strings(self): 24 | """ 25 | Test the addition of two strings returns the two strings as one 26 | concatenated string 27 | """ 28 | result = calc.add2('abc', 'def') 29 | self.assertEqual(result, 'abcdef') 30 | 31 | def test_add_string_and_integer(self): 32 | """ 33 | Test the addition of a string and an integer returns them as one 34 | concatenated string (in which the integer is converted to a string) 35 | """ 36 | result = calc.add2('abc', 3) 37 | self.assertEqual(result, 'abc3') 38 | 39 | def test_add_string_and_number(self): 40 | """ 41 | Test the addition of a string and a float returns them as one 42 | concatenated string (in which the float is converted to a string) 43 | """ 44 | result = calc.add2('abc', '5.5') 45 | self.assertEqual(result, 'abc5.5') 46 | 47 | if __name__ == '__main__': 48 | unittest.main() 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_store 2 | # Byte-compiled / optimized / DLL files 3 | __pycache__/ 4 | *.py[cod] 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # Environments 83 | .env 84 | .venv 85 | env/ 86 | venv/ 87 | ENV/ 88 | 89 | # Spyder project settings 90 | .spyderproject 91 | .spyproject 92 | 93 | # Rope project settings 94 | .ropeproject 95 | 96 | # mkdocs documentation 97 | /site 98 | 99 | # mypy 100 | .mypy_cache/ 101 | --------------------------------------------------------------------------------