├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── configs └── build.cnf ├── git_101_tutorial.py ├── images.sh └── run.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | #Ipython Notebook 62 | .ipynb_checkpoints -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian 2 | # Step 1 done 3 | RUN apt-get update && apt-get install -y git lsb-release vim bsdmainutils man-db manpages && mkdir -p /myproject && touch /root/.bash_history 4 | # Step 2 done 5 | WORKDIR /myproject 6 | # Step 3 done 7 | RUN git config --global user.email "you@example.com" && git config --global user.name "Your Name" && echo 'git config --global user.email "you@example.com" && git config --global user.name "Your Name"' >> /root/.bash_history 8 | # Step 4 done 9 | RUN git init && echo 'git init' >> /root/.bash_history 10 | # Step 5 done 11 | RUN echo "#!/usr/bin/env python" > mycode.py && echo 'echo "#!/usr/bin/env python" > mycode.py' >> /root/.bash_history 12 | # Step 6 done 13 | RUN git add mycode.py && echo 'git add mycode.py' >> /root/.bash_history 14 | # Step 7 done 15 | RUN git commit -m 'mycode.py added' && echo "git commit -m 'mycode.py' added" >> /root/.bash_history 16 | # Step 8 done 17 | RUN echo "import string" >> mycode.py && echo 'echo "import string" >> mycode.py' >> /root/.bash_history 18 | # Step 9 done 19 | RUN git commit -am 'import added' && echo "git commit -am 'import added" >> /root/.bash_history 20 | # Step 10 done 21 | CMD /bin/bash 22 | # Step 11 done 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Ian Miell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Git 101 Tutorial 2 | A ShutIt-based tutorial on the very basics of git. 3 | 4 | ``` 5 | #install python-pip and docker (or docker.io) 6 | sudo pip install shutit 7 | git clone https://github.com/ianmiell/git-101-tutorial && cd git-101-tutorial 8 | ./run.sh 9 | ``` 10 | 11 | See [here](http://ianmiell.github.io/git-101-tutorial/) 12 | -------------------------------------------------------------------------------- /configs/build.cnf: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # PLEASE NOTE: This file should be changed only by the maintainer. 3 | # PLEASE NOTE: This file is only sourced if the "shutit build" command is run 4 | # and this file is in the relative path: configs/build.cnf 5 | # This is to ensure it is only sourced if _this_ module is the 6 | # target. 7 | ############################################################################### 8 | # When this module is the one being built, which modules should be built along with it by default? 9 | # This feeds into automated testing of each module. 10 | [tk.shutit.git_101_tutorial] 11 | shutit.core.module.build:yes 12 | # Allowed images as a regexp, eg ["ubuntu:12.*"], or [".*"], or ["centos"]. 13 | # It's recommended this is locked down as far as possible. 14 | shutit.core.module.allowed_images:["imiell/git-101-tutorial.*"] 15 | 16 | # Aspects of build process 17 | [build] 18 | base_image:imiell/git-101-tutorial:step_2 19 | 20 | # Volume arguments wanted as part of the build 21 | [target] 22 | volumes: 23 | 24 | [repository] 25 | name:git_101_tutorial 26 | -------------------------------------------------------------------------------- /git_101_tutorial.py: -------------------------------------------------------------------------------- 1 | from shutit_module import ShutItModule 2 | 3 | class git_101_tutorial(ShutItModule): 4 | 5 | def build(self, shutit): 6 | shutit.send('cd /myproject') 7 | if shutit.send_and_get_output('git version') != 'git version 2.1.4': 8 | shutit.fail('unexpected git version') 9 | 10 | shutit.challenge( 11 | '''In this tutorial you will be asked to set up git on your machine, 12 | create a repository, and add and commit some code to it. 13 | 14 | You have a full bash shell, so can use vi, less, man etc.. 15 | 16 | CTRL-] (right angle bracket) to continue. 17 | ''', 18 | '1', 19 | challenge_type='golf', 20 | expect_type='exact', 21 | hints=['Hit CTRL-]'], 22 | congratulations='OK!', 23 | follow_on_context={ 24 | 'check_command':'echo 1', 25 | 'context':'docker', 26 | 'reset_container_name':'imiell/git-101-tutorial:step_3', 27 | 'ok_container_name':'imiell/git-101-tutorial:step_3' 28 | }, 29 | num_stages=8 30 | ) 31 | shutit.challenge('''Configure git to tell it who you are (user.name and user.email). Don't forget that CTRL-h will give you hints.''', 32 | '11', 33 | challenge_type='golf', 34 | expect_type='exact', 35 | hints=['man git config','git config --global'], 36 | congratulations='OK!', 37 | follow_on_context={ 38 | 'check_command':r"""cat -s <(git config -l | grep user.email | wc -l) <(git config -l | grep user.name | wc -l) | tr -d '\n'""", 39 | 'context':'docker', 40 | 'reset_container_name':'imiell/git-101-tutorial:step_3', 41 | 'ok_container_name':'imiell/git-101-tutorial:step_4' 42 | } 43 | ) 44 | shutit.challenge('''Initialize a git repo in the /myproject folder. Don't create a subfolder or give git a folder name.''', 45 | 'cfcd208495d565ef66e7dff9f98764da', 46 | challenge_type='golf', 47 | expect_type='md5sum', 48 | hints=[], 49 | congratulations='OK!', 50 | follow_on_context={ 51 | 'check_command':'cat <(git status -s) <(git branch | wc -l)', 52 | 'context':'docker', 53 | 'reset_container_name':'imiell/git-101-tutorial:step_4', 54 | 'ok_container_name':'imiell/git-101-tutorial:step_5' 55 | } 56 | ) 57 | shutit.challenge('''Create a file called 'mycode.py' and put the line: 58 | 59 | #!/usr/bin/env python 60 | 61 | in it. 62 | 63 | Then run git status to see what git thinks is going on in this repo.''', 64 | 'e2ac2db4ef94965e8f02095a27b65f2d', 65 | challenge_type='golf', 66 | expect_type='md5sum', 67 | hints=['echo "#!/usr/bin/env python" > mycode.py'], 68 | congratulations='OK!', 69 | follow_on_context={ 70 | 'check_command':r'''cat <(sed 's/^[[:blank:]]*//;s/[[:blank:]]*$//' mycode.py | sed -e :a -e '/./,$!d;/^\n*$/{$d;N;};/\n$/ba') <(git status -s) <(find *)''', 71 | 'context':'docker', 72 | 'reset_container_name':'imiell/git-101-tutorial:step_5', 73 | 'ok_container_name':'imiell/git-101-tutorial:step_6' 74 | } 75 | ) 76 | shutit.challenge('''Add this file to your git repo ready to commit. 77 | 78 | Then try running 'git status' again to see how git's view of this file has 79 | changed.''', 80 | '1a8779dd8b7a61f23de73bcc0702f920', 81 | challenge_type='golf', 82 | expect_type='md5sum', 83 | hints=['git add mycode.py'], 84 | congratulations='OK!', 85 | follow_on_context={ 86 | 'check_command':r'''cat <(sed 's/^[[:blank:]]*//;s/[[:blank:]]*$//' mycode.py | sed -e :a -e '/./,$!d;/^\n*$/{$d;N;};/\n$/ba') <(git status -s) <(find *)''', 87 | 'context':'docker', 88 | 'reset_container_name':'imiell/git-101-tutorial:step_6', 89 | 'ok_container_name':'imiell/git-101-tutorial:step_7' 90 | } 91 | ) 92 | shutit.challenge('''Commit the file to the git repo. 93 | 94 | Then run 'git log' to see the history of the repository has now started''', 95 | '912251e04d2205b833772a31ca95f330', 96 | challenge_type='golf', 97 | expect_type='md5sum', 98 | hints=['git commit'], 99 | congratulations='OK!', 100 | follow_on_context={ 101 | 'check_command':r'''cat <(sed 's/^[[:blank:]]*//;s/[[:blank:]]*$//' mycode.py | sed -e :a -e '/./,$!d;/^\n*$/{$d;N;};/\n$/ba') <(git status -s) <(find *)''', 102 | 'context':'docker', 103 | 'reset_container_name':'imiell/git-101-tutorial:step_7', 104 | 'ok_container_name':'imiell/git-101-tutorial:step_8' 105 | } 106 | ) 107 | # TODO hints 108 | shutit.challenge('''Add the line 'import string' to the mycode.py file. Do not commit it. Do not add any empty lines. 109 | 110 | Then run 'git status' and 'git log' to see the status of the file and the 111 | change from git's point of view.''', 112 | '8b13003240e0f264617cfc0892e194d0', 113 | challenge_type='golf', 114 | expect_type='md5sum', 115 | hints=[''], 116 | congratulations='OK!', 117 | follow_on_context={ 118 | 'check_command':r'''cat <(sed 's/^[[:blank:]]*//;s/[[:blank:]]*$//' mycode.py | sed -e :a -e '/./,$!d;/^\n*$/{$d;N;};/\n$/ba') <(git status -s) <(find *)''', 119 | 'context':'docker', 120 | 'reset_container_name':'imiell/git-101-tutorial:step_8', 121 | 'ok_container_name':'imiell/git-101-tutorial:step_9' 122 | } 123 | ) 124 | shutit.challenge('''Add and commit the file again. 125 | 126 | Again, you can run 'git status' and 'git log' to see what git thinks has happened.''', 127 | '814967123be30fe11960749d22564000', 128 | challenge_type='golf', 129 | expect_type='md5sum', 130 | hints=[''], 131 | congratulations='OK!', 132 | follow_on_context={ 133 | 'check_command':r'''cat <(sed 's/^[[:blank:]]*//;s/[[:blank:]]*$//' mycode.py | sed -e :a -e '/./,$!d;/^\n*$/{$d;N;};/\n$/ba') <(git status -s) <(find *)''', 134 | 'context':'docker', 135 | 'reset_container_name':'imiell/git-101-tutorial:step_9', 136 | 'ok_container_name':'imiell/git-101-tutorial:step_10' 137 | } 138 | ) 139 | return True 140 | 141 | 142 | def module(): 143 | return git_101_tutorial( 144 | 'tk.shutit.git_101_tutorial', 1845506479.0001, 145 | description='', 146 | maintainer='', 147 | delivery_methods=['docker'], 148 | depends=['shutit.tk.setup'] 149 | ) 150 | -------------------------------------------------------------------------------- /images.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | set -u 4 | set -e 5 | IMAGE_NAME="imiell/git-101-tutorial" 6 | #docker images | tac | grep "$IMAGE_NAME" | awk '{print $3}' | xargs docker rmi 7 | docker build -t $IMAGE_NAME . 8 | x=1 9 | docker history -q "${IMAGE_NAME}:latest" | tac 10 | for id in $(docker history -q "${IMAGE_NAME}:latest" | tac | grep -v missing) 11 | do 12 | docker tag "${id}" "${IMAGE_NAME}:step_$x" 13 | ((x++)) 14 | done 15 | docker push "${IMAGE_NAME}" 16 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | [[ -z "$SHUTIT" ]] && SHUTIT="$1/shutit" 4 | [[ ! -a "$SHUTIT" ]] || [[ -z "$SHUTIT" ]] && SHUTIT="$(which shutit)" 5 | if [[ ! -a "$SHUTIT" ]] 6 | then 7 | echo "Must have shutit on path, eg export PATH=$PATH:/path/to/shutit_dir" 8 | exit 1 9 | fi 10 | $SHUTIT build -d docker --exam -l debug -o out.$(date +%s).log "$@" 11 | if [[ $? != 0 ]] 12 | then 13 | exit 1 14 | fi 15 | --------------------------------------------------------------------------------