diff --git a/slides.ipynb b/slides.ipynb
index a1ec934..589391b 100644
--- a/slides.ipynb
+++ b/slides.ipynb
@@ -1,743 +1,742 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Switched to branch 'master'\n",
"Your branch is up to date with 'origin/master'.\n",
"Already up to date.\n"
]
}
],
"source": [
"cd ../so-workshop\n",
"git checkout master\n",
"git pull"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# So, you want to go open Science ?\n",
"## Testing, testing, testing !\n",
"\n",
"
\n",
"\n",
"- https://c4science.ch/source/so-workshop/\n",
"- https://c4science.ch/source/so-slides/\n",
"\n",
"
\n",
"SCITAS training with expertise from the EPFL Library
\n",
"Nicolas Richart and Jean-Baptiste Aubort - SCITAS"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Continuous Integration (CI)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"## What is CI ?\n",
"\n",
"- Automation of build and tests\n",
"- Run tests at each commit\n",
"- You need to write tests !"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"## Why use CI ?\n",
"\n",
"- Detect bugs when they are introduced\n",
"- Detect when fixed bugs appear again (regression)\n",
"- Publish code that works"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Ensure code has a certain quality level\n",
"- Linters\n",
"- Test Coverage"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Jenkins on c4science"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## https://c4science.ch/jobs\n",
"![](images/c4s-jenkinsjob.png)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## https://jenkins.c4science.ch/\n",
"![](images/c4s-jenkins.png)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Example\n",
"## Jenkins base"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Switched to branch 'feature/jenkins-base'\n",
"Your branch is up to date with 'origin/feature/jenkins-base'.\n",
"\u001b[01;34m.\u001b[00m\n",
"├── \u001b[01;34mcode\u001b[00m\n",
"│ ├── mylib.py\n",
"│ └── \u001b[01;32mtest.py\u001b[00m\n",
"├── Jenkinsfile\n",
"└── README\n",
"\n",
"1 directory, 4 files\n"
]
}
],
"source": [
"git checkout feature/jenkins-base\n",
"tree"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"#!/usr/bin/env python3\n",
"\n",
"class MyLib():\n",
"\n",
" def __init__(self):\n",
" pass\n",
"\n",
" def MyFunc(self):\n",
" return 'test'\n",
"\n",
"if __name__ == '__main__':\n",
" pass\n"
]
}
],
"source": [
"cat code/mylib.py"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"#!/usr/bin/env python3\n",
"import unittest, pytest\n",
"import mylib\n",
"\n",
"class Test(unittest.TestCase):\n",
"\n",
" def test_return(self):\n",
" m = mylib.MyLib()\n",
" self.assertTrue(m.MyFunc() == 'test')\n",
"\n",
"if __name__ == '__main__':\n",
" pytest.main(['./test.py'])\n"
]
}
],
"source": [
"cat code/test.py"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"pipeline {\n",
"\n",
" agent {\n",
" docker {\n",
" image 'python:3.7'\n",
" }\n",
" }\n",
"\n",
" environment {\n",
" HOME = \"$WORKSPACE\"\n",
" }\n",
"\n",
" stages {\n",
" stage('install dependencies') {\n",
" steps {\n",
" sh 'pip3 install --user pytest'\n",
" }\n",
" }\n",
" stage('run tests') {\n",
" steps {\n",
" sh '$HOME/.local/bin/pytest code/test.py'\n",
" }\n",
" }\n",
" }\n",
"\n",
"}\n"
]
}
],
"source": [
"cat Jenkinsfile"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"![](images/jenkins-base.png)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Example\n",
"## Jenkins dockerfile"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Switched to branch 'feature/jenkins-dockerfile'\n",
"Your branch is up to date with 'origin/feature/jenkins-dockerfile'.\n",
"\u001b[01;34m.\u001b[00m\n",
"├── \u001b[01;34mcode\u001b[00m\n",
"│ ├── mylib.py\n",
"│ └── \u001b[01;32mtest.py\u001b[00m\n",
"├── Dockerfile\n",
"├── Jenkinsfile\n",
"└── README\n",
"\n",
"1 directory, 5 files\n"
]
}
],
"source": [
"git checkout feature/jenkins-dockerfile\n",
"tree"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"FROM python:3.7\n",
"RUN pip3 install --no-cache-dir pytest\n"
]
}
],
"source": [
"cat Dockerfile"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"pipeline {\n",
"\n",
" agent {\n",
" dockerfile true \n",
" }\n",
"\n",
" stages {\n",
" stage('run tests') {\n",
" steps {\n",
" sh 'pytest code/test.py'\n",
" }\n",
" }\n",
" }\n",
"\n",
"}\n"
]
}
],
"source": [
"cat Jenkinsfile"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Example\n",
"### Jenkins junit"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Branch 'feature/jenkins-junit' set up to track remote branch 'feature/jenkins-junit' from 'origin'.\n",
"Switched to a new branch 'feature/jenkins-junit'\n",
"\u001b[01;34m.\u001b[00m\n",
"├── \u001b[01;34mcode\u001b[00m\n",
"│ ├── mylib.py\n",
"│ └── \u001b[01;32mtest.py\u001b[00m\n",
"├── Dockerfile\n",
"├── Jenkinsfile\n",
"└── README\n",
"\n",
"1 directory, 5 files\n"
]
}
],
"source": [
"git checkout feature/jenkins-junit\n",
"tree"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"pipeline {\n",
"\n",
" agent {\n",
" dockerfile true \n",
" }\n",
"\n",
" stages {\n",
" stage('run tests') {\n",
" steps {\n",
" sh 'pytest --junitxml results.xml code/test.py'\n",
" }\n",
" }\n",
" }\n",
"\n",
" post {\n",
" always {\n",
" junit 'results.xml'\n",
" }\n",
" }\n",
"\n",
"}\n"
]
}
],
"source": [
"cat Jenkinsfile"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"![](images/jenkins-junit.png)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"![](images/jenkins-junit2.png)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# C4science\n",
- "## Arcanist\n",
- "### Linters"
+ "## Arcanist"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
- "slide_type": "slide"
+ "slide_type": "fragment"
}
},
"source": [
"### Linters\n",
"\n",
"> A linter or lint refers to tools that analyze source code to flag programming errors, bugs, stylistic errors, and suspicious constructs.\n",
"\n",
"- https://en.wikipedia.org/wiki/Lint_(software)\n",
"- https://c4science.ch/w/c4science/lint/"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Switched to branch 'feature/arcanist-linter'\n",
"Your branch is up to date with 'origin/feature/arcanist-linter'.\n",
"\u001b[01;34m.\u001b[00m\n",
"├── \u001b[01;34mcode\u001b[00m\n",
"│ ├── mylib.py\n",
"│ └── \u001b[01;32mtest.py\u001b[00m\n",
"├── Dockerfile\n",
"├── Jenkinsfile\n",
"└── README\n",
"\n",
"1 directory, 5 files\n"
]
}
],
"source": [
"git checkout feature/arcanist-linter\n",
"tree"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[42m\u001b[1m CONFIGURED \u001b[m\u001b[49m \u001b[1mtext\u001b[m (Basic Text Linter)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mcsharp\u001b[m (C#)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mcpplint\u001b[m (C++ Google's Styleguide)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mcppcheck\u001b[m (C++ linter)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mlessc\u001b[m (CSS pre-processor)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mcsslint\u001b[m (CSSLint)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mchmod\u001b[m (Chmod)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mgjslint\u001b[m (Closure Linter)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mcoffeelint\u001b[m (CoffeeLint)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mcomposer\u001b[m (Composer Dependency Manager)\n",
"\u001b[42m\u001b[1m CONFIGURED \u001b[m\u001b[49m \u001b[1mfilename\u001b[m (Filename)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mgenerated\u001b[m (Generated Code)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mgolint\u001b[m (Golint)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mhlint\u001b[m (Haskell Linter)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mjsonlint\u001b[m (JSON Lint)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mjson\u001b[m (JSON Lint)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mjscs\u001b[m (JavaScript Code Style)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mjshint\u001b[m (JavaScript error checking)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mnolint\u001b[m (Lint Disabler)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mmerge-conflict\u001b[m (Merge Conflicts)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mphpcs\u001b[m (PHP_CodeSniffer)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mphutil-library\u001b[m (Phutil Library Linter)\n",
"\u001b[42m\u001b[1m CONFIGURED \u001b[m\u001b[49m \u001b[1mpylint\u001b[m (PyLint)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mflake8\u001b[m (Python Flake8 multi-linter)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mpep8\u001b[m (Python PEP 8)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mpyflakes\u001b[m (Python PyFlakes)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mruby\u001b[m (Ruby)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mrubocop\u001b[m (Ruby static code analyzer)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mscript-and-regex\u001b[m (Script and Regex)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mxml\u001b[m (SimpleXML Linter)\n",
"\u001b[42m\u001b[1m CONFIGURED \u001b[m\u001b[49m \u001b[1mspelling\u001b[m (Spellchecker)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mxhpast\u001b[m (XHPAST Lint)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mphp\u001b[m (php -l)\n",
"\u001b[43m\u001b[1m AVAILABLE \u001b[m\u001b[49m \u001b[1mpuppet-lint\u001b[m (puppet-lint)\n",
"(Run `arc linters --verbose` for more details.)\n"
]
}
],
"source": [
"arc linters"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{\n",
" \"linters\": {\n",
" \"spelling-linter\": {\n",
" \"type\": \"spelling\"\n",
" },\n",
" \"filename-linter\": {\n",
" \"type\": \"filename\"\n",
" },\n",
" \"text-linter\": {\n",
" \"type\": \"text\"\n",
" },\n",
" \"python-checks\": {\n",
" \"type\": \"pylint\",\n",
" \"include\": \"(\\\\.py$)\"\n",
" }\n",
" }\n",
"}\n"
]
}
],
"source": [
"cat .arclint"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Jenkinsfile:4:Auto-Fix: Trailing Whitespace\n",
"README:3:Auto-Fix: Trailing Whitespace at EOF\n",
"code/mylib.py:1:Advice: Missing Docstring\n",
"code/mylib.py:3:Advice: Missing Docstring\n",
"code/mylib.py:3:Advice: Old Style Class\n",
"code/mylib.py:8:Advice: Invalid Name\n",
"code/mylib.py:8:Advice: Missing Docstring\n",
"code/mylib.py:8:Advice: No Self Use\n",
"code/mylib.py:11:Advice: Invalid Name\n",
"code/mylib.py:11:Advice: Missing Docstring\n",
"code/mylib.py:11:Advice: No Self Use\n",
"code/test.py:1:Advice: Missing Docstring\n",
"code/test.py:5:Advice: Missing Docstring\n",
"code/test.py:9:Advice: Missing Docstring\n",
"code/test.py:12:Warning: Mixed Indentation\n",
"code/test.py:12:Error: Tab Literal\n",
"code/test.py:12:Advice: Missing Docstring\n",
"code/test.py:12:Warning: Unused Variable\n",
"code/test.py:13:Warning: Mixed Indentation\n"
]
},
{
"ename": "",
"evalue": "2",
"output_type": "error",
"traceback": []
}
],
"source": [
"arc lint --never-apply-patches --output summary --everything"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Unit-Test\n",
"### Jenkins and Arcanist"
]
}
],
"metadata": {
"celltoolbar": "Slideshow",
"kernelspec": {
"display_name": "Bash",
"language": "bash",
"name": "bash"
},
"language_info": {
"codemirror_mode": "shell",
"file_extension": ".sh",
"mimetype": "text/x-sh",
"name": "bash"
},
"livereveal": {
"height": 768,
"theme": "serif",
"transition": "none",
"width": 1024
}
},
"nbformat": 4,
"nbformat_minor": 2
}