├── src ├── __init__.py └── arrayPartition1.py ├── runTests.py ├── res ├── howToFork.png ├── githubWebhook1.png ├── githubWebhook2.png ├── jenkinsNewItem.png ├── findTheCloneLink.png ├── jenkinsNewItemConfig.png ├── jenkinsPostbuildJUnit.png ├── jenkinsProjectGitConfig.png ├── jenkinsProjectBuildTrigger.png └── jenkinsProjectConfigBuild1.png ├── test └── test.py ├── .gitignore └── README.md /src/__init__.py: -------------------------------------------------------------------------------- 1 | # empty 2 | -------------------------------------------------------------------------------- /runTests.py: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | 3 | cd "$(dirname "$0")" 4 | python2.7 test/test.py 5 | -------------------------------------------------------------------------------- /res/howToFork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InsightDataScience/jenkinsTemplate-python/master/res/howToFork.png -------------------------------------------------------------------------------- /res/githubWebhook1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InsightDataScience/jenkinsTemplate-python/master/res/githubWebhook1.png -------------------------------------------------------------------------------- /res/githubWebhook2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InsightDataScience/jenkinsTemplate-python/master/res/githubWebhook2.png -------------------------------------------------------------------------------- /res/jenkinsNewItem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InsightDataScience/jenkinsTemplate-python/master/res/jenkinsNewItem.png -------------------------------------------------------------------------------- /res/findTheCloneLink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InsightDataScience/jenkinsTemplate-python/master/res/findTheCloneLink.png -------------------------------------------------------------------------------- /res/jenkinsNewItemConfig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InsightDataScience/jenkinsTemplate-python/master/res/jenkinsNewItemConfig.png -------------------------------------------------------------------------------- /res/jenkinsPostbuildJUnit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InsightDataScience/jenkinsTemplate-python/master/res/jenkinsPostbuildJUnit.png -------------------------------------------------------------------------------- /res/jenkinsProjectGitConfig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InsightDataScience/jenkinsTemplate-python/master/res/jenkinsProjectGitConfig.png -------------------------------------------------------------------------------- /res/jenkinsProjectBuildTrigger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InsightDataScience/jenkinsTemplate-python/master/res/jenkinsProjectBuildTrigger.png -------------------------------------------------------------------------------- /res/jenkinsProjectConfigBuild1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InsightDataScience/jenkinsTemplate-python/master/res/jenkinsProjectConfigBuild1.png -------------------------------------------------------------------------------- /src/arrayPartition1.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def ArrayPairSum(self, nums): 3 | """ 4 | :type nums: List[int] 5 | :rtype: int 6 | """ 7 | 8 | # your solution here 9 | 10 | return 0 11 | 12 | -------------------------------------------------------------------------------- /test/test.py: -------------------------------------------------------------------------------- 1 | import unittest, sys, os 2 | sys.path.append(os.path.abspath("./src")) 3 | from arrayPartition1 import Solution 4 | 5 | class TestArrayPartition1(unittest.TestCase): 6 | def setUp(self): 7 | self.sln = Solution() 8 | 9 | def test_basic(self): 10 | self.assertEqual(self.sln.ArrayPairSum([1,4,2,3]), 4) 11 | 12 | 13 | if __name__ == '__main__': 14 | import xmlrunner 15 | unittest.main(testRunner=xmlrunner.XMLTestRunner(output='test-reports')) 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | test-reports/ 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | wheels/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | .hypothesis/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # dotenv 85 | .env 86 | 87 | # virtualenv 88 | .venv 89 | venv/ 90 | ENV/ 91 | 92 | # Spyder project settings 93 | .spyderproject 94 | .spyproject 95 | 96 | # Rope project settings 97 | .ropeproject 98 | 99 | # mkdocs documentation 100 | /site 101 | 102 | # mypy 103 | .mypy_cache/ 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jenkinsTemplate-python 2 | A testable Python project for the Jenkins CI workshop @ Insight Data Engineering. 3 | 4 | * [Part 0: Getting Started](#part-0-getting-started) 5 | * [Part 1: Problem Statement](#part-1-problem-statement) 6 | * [Part 2: Implement and Test](#part-2-implement-and-test) 7 | * [Part 3: Jenkins](#part-3-jenkins-continuous-integration) 8 | * [Part 4: Test-Driven Development](#part-4-test-driven-development) 9 | 10 | ---- 11 | 12 | ## Part 0: Getting Started 13 | 14 | If you have not set up SSH keys for your github account yet, please follow these links to [Create an SSH Key](https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/#generating-a-new-ssh-key) 15 | and to [Associate it with your GitHub account](https://help.github.com/articles/adding-a-new-ssh-key-to-your-github-account/). 16 | 17 | You will also need python2 with `xmlrunner` installed. After installing python and pip for your local machine, you can install `xmlrunner` with: 18 | 19 | ```bash 20 | pip install xmlrunner 21 | ``` 22 | 23 | Or if you have both python3 and python2 installed, you want to install `xmlrunner` for python2: 24 | 25 | 26 | ```bash 27 | pip2 install xmlrunner 28 | ``` 29 | 30 | To begin, fork this repo on GitHub *\([image](res/howToFork.png)\)* 31 | then clone it to your local machine by copying the clone link *\([image](res/findTheCloneLink.png)\)*. 32 | Finally, use `git clone` at your shell / command line with the clone link you just copied. The command will look like this: 33 | 34 | ```bash 35 | git clone git@github.com:[MY_USER]/jenkinsTemplate-python.git 36 | ``` 37 | 38 | 39 | 40 | 41 | 42 | ## Part 1: Problem Statement 43 | 44 | Given an array of *2n* integers, your task is to group these integers into *n* pairs of integers, say *(a1, b1), (a2, b2), ..., (an, bn)* which makes the sum of *min(ai, bi)* for all *i* from *1* to *n* as large as possible. 45 | 46 | ### Example 47 | 48 | * **Input:** [1,4,3,2] 49 | * **Output:** 4 50 | * **Explanation:** n is 2, and the maximum sum of pairs is 4. 51 | 52 | ### Notes 53 | 54 | * n is a positive integer, which is in the range of [1, 10000]. 55 | * All the integers in the array will be in the range of [-10000, 10000]. 56 | 57 | 58 | 59 | 60 | ## Part 2: Implement and Test 61 | 62 | Your first job is to implement a working solution that passes the given test. To do so, modify the code in `src/arrayPartition1.py`. 63 | 64 | Testing is performed by calling `./runTests.py` from your shell / command line. 65 | 66 | If you've implemented a correct solution, the output should look like this: 67 | 68 | ```bash 69 | $ ./runTests.py 70 | . 71 | ---------------------------------------------------------------------- 72 | Ran 1 test in 0.000s 73 | 74 | OK 75 | ``` 76 | 77 | 78 | 79 | 80 | When you're finished and your test passes, be sure to push your changes back up to GitHub. Here is a rough outline of how to do it: 81 | 82 | * `git status` will show you which files have changed or been added 83 | * `git add [FILENAME] ...` will mark specific files to be committed and sent back to github 84 | * `git commit` will commit your changes locally and allow you to describe what changed in your commit message 85 | * `git push` will send your locally committed changes back to GitHub 86 | 87 | 88 | 89 | 90 | 91 | ## Part 3: Jenkins Continuous Integration 92 | 93 | Now that you have a working solution, let's make sure that whenever you change your code and push it back to GitHub, it remains executable and that all of your tests still pass. 94 | 95 | Jenkins is a popular Open Source tool for Continuous Integration and Continuous Delivery. Since we're not working with collaborators or deploying our code to a server yet, we'll be using Jenkins for Continuous Testing alone. 96 | 97 | 98 | As part of this workshop, you should have received an email with your username, password, and link to our internal Jenkins server. Use the link to log in to Jenkins. On the homepage, click the link on the left that says **New Item** 99 | *\([image](res/jenkinsNewItem.png)\)*. 100 | 101 | In the New Item configuration screen, set the name of the project to `[MY_USERNAME]-jenkinsTest`, then click the box underneath titled **Freestyle project**, and finally click **OK** at the bottom of the page *\([image](res/jenkinsNewItemConfig.png)\)*. 102 | 103 | On the project configuration screen, under **Source Code Management**, select the **Git** list item and enter the URL of your github repository page (Note: *not* the git clone URL). This tells Jenkins where it can get your code from (GitHub) and how to do it (using `git clone`) *\([image](res/jenkinsProjectGitConfig.png)\)*. 104 | 105 | To run tests whenever you push changes to GitHub, you'll need to teach Jenkins and GitHub how to communicate with each other. For Jenkins, you'll need to navigate to the **Build Triggers** section and click the checkbox titled "GitHub hook trigger for GITScm polling" *\([image](res/jenkinsProjectBuildTrigger.png)\)*. On the GitHub side, you'll need to go into the project settings and add a new WebHook that points to your Jenkins server. The URL for the WebHook can be found in your email with your Jenkins credentials 106 | *\([image 1](res/githubWebhook1.png), 107 | [image 2](res/githubWebhook2.png)\)*. 108 | 109 | Now that Jenkins knows how and when to get a copy of your code, it needs to know how to test it. Further down the page, you'll find the **Build** section. Click on **Add build step**, then **Execute shell** *\([image](res/jenkinsProjectConfigBuild1.png)\)*. In the command window that appears, type: `./runTests.py`. 110 | 111 | One last thing we can do is teach Jenkins how to interpret our test results, giving us a better dashboard to examine our test results, and test history with some statistics. Under **Post-Build Actions**, click on **Add post-build action**, and select **Publish JUnit test result report**. You'll want to enter `**/test-reports/*.xml` in the *Test report XMLs* field, because that is where your python script prints out its test results *\([image](res/jenkinsPostbuildJUnit.png)\)*. 112 | 113 | Finally, click **Save** at the bottom. 114 | 115 | If all went well, you'll have just set up push-based continuous testing with GitHub and Jenkins. Now let's try it out! 116 | 117 | * Make some small change to your repository, 118 | * commit it, 119 | * push it back up to GitHub, 120 | * Go back to Jenkins to view your project, and 121 | * watch your Project Status page for a new build beginning on its own 122 | 123 | You should very quickly see a new build in the queue, and within a few seconds, the test results should be available. 124 | 125 | 126 | 127 | ## Part 4: Test-Driven Development 128 | 129 | Test-Driven Development (TDD) is a software development process wherein you write test cases for new software requirements, and only afterwards do you improve the software to pass the tests. Let's practice that. 130 | 131 | What happens when you call `sln.ArrayPairSum(['herp', 'derp'])`? How *should* your program behave? It most likely throws an error, but maybe that isn't how you'd like your program to behave. 132 | Let's say you have the requirement that your function return 0 for non-integral arrays. To implement this using a TDD process: 133 | 134 | * create a new test in `test/test.py` that ensures `ArrayPairSum` returns 0 when it is called with a string array. 135 | 136 | ```python 137 | def test_str(self): 138 | assertEqual(self.sln.ArrayPairSum(["herp", "derp"]), 0) 139 | ``` 140 | 141 | * run `./runTests.py` locally to show that this test (most likely) fails. 142 | * commit and push your new changes to GitHub, noting that this commit "Adds failing tests for returning 0 on string array". 143 | * ensure Jenkins picks up the changes, and that this tests fails when Jenkins tests it 144 | * now fix the code to pass this test. run `./runTests.py` locally to check 145 | * when you're ready, commit and push your fix to GitHub, then check Jenkins to ensure your build succeeds and that all tests pass 146 | 147 | --------------------------------------------------------------------------------