├── .editorconfig ├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── ci.yml │ ├── deploy.yml │ └── publish-docker.yml ├── .gitignore ├── AUTHORS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Gemfile ├── LICENSE.md ├── Makefile ├── README.md ├── _config.yml ├── _episodes ├── 01-introduction.md ├── 02-pulling-images.md ├── 03-running-containers.md ├── 04-file-io.md ├── 05-dockerfiles.md ├── 06-removal.md ├── 07-coffee-break.md ├── 08-entry.md ├── 09-build-with-ci.md └── 10-challenge-examples.md ├── _episodes_rmd ├── .gitkeep └── data │ └── .gitkeep ├── _extras ├── discuss.md └── guide.md ├── _includes ├── aio-script.md ├── all_keypoints.html ├── base_path.html ├── carpentries.html ├── dc │ ├── intro.html │ ├── schedule.html │ ├── syllabus.html │ └── who.html ├── episode_break.html ├── episode_keypoints.html ├── episode_navbar.html ├── episode_overview.html ├── episode_title.html ├── favicons.html ├── gh_variables.html ├── javascript.html ├── lc │ ├── intro.html │ ├── schedule.html │ ├── syllabus.html │ └── who.html ├── lesson_footer.html ├── life_cycle.html ├── links.md ├── main_title.html ├── manual_episode_order.html ├── navbar.html ├── sc │ ├── intro.html │ ├── schedule.html │ ├── syllabus.html │ └── who.html ├── syllabus.html ├── workshop_ad.html ├── workshop_calendar.html └── workshop_footer.html ├── _layouts ├── base.html ├── break.html ├── episode.html ├── lesson.html ├── page.html ├── reference.html └── workshop.html ├── aio.md ├── assets ├── css │ ├── bootstrap-theme.css │ ├── bootstrap-theme.css.map │ ├── bootstrap.css │ ├── bootstrap.css.map │ ├── lesson.scss │ └── syntax.css ├── favicons │ ├── cp │ │ ├── apple-touch-icon-114x114.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-144x144.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-57x57.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-72x72.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── favicon-128.png │ │ ├── favicon-16x16.png │ │ ├── favicon-196x196.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── favicon.ico │ │ ├── mstile-144x144.png │ │ ├── mstile-150x150.png │ │ ├── mstile-310x150.png │ │ ├── mstile-310x310.png │ │ └── mstile-70x70.png │ ├── dc │ │ ├── apple-touch-icon-114x114.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-144x144.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-57x57.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-72x72.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── favicon-128.png │ │ ├── favicon-16x16.png │ │ ├── favicon-196x196.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── favicon.ico │ │ ├── mstile-144x144.png │ │ ├── mstile-150x150.png │ │ ├── mstile-310x150.png │ │ ├── mstile-310x310.png │ │ └── mstile-70x70.png │ ├── lc │ │ ├── apple-touch-icon-114x114.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-144x144.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-57x57.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-72x72.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── favicon-128.png │ │ ├── favicon-16x16.png │ │ ├── favicon-196x196.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── favicon.ico │ │ ├── mstile-144x144.png │ │ ├── mstile-150x150.png │ │ ├── mstile-310x150.png │ │ ├── mstile-310x310.png │ │ └── mstile-70x70.png │ └── swc │ │ ├── apple-touch-icon-114x114.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-144x144.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-57x57.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-72x72.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── favicon-128.png │ │ ├── favicon-16x16.png │ │ ├── favicon-196x196.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── favicon.ico │ │ ├── mstile-144x144.png │ │ ├── mstile-150x150.png │ │ ├── mstile-310x150.png │ │ ├── mstile-310x310.png │ │ └── mstile-70x70.png ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── img │ ├── cp-logo-blue.svg │ ├── dc-icon-black.svg │ ├── dc-logo-black.svg │ ├── lc-icon-black.png │ ├── lc-icon-black.svg │ ├── lc-logo-black.png │ ├── lc-logo-black.svg │ ├── swc-icon-blue.svg │ ├── swc-logo-blue.png │ ├── swc-logo-blue.svg │ ├── swc-logo-white.png │ └── swc-logo-white.svg └── js │ ├── bootstrap.min.js │ ├── jquery.min.js │ ├── jquery.min.map │ └── lesson.js ├── bin ├── boilerplate │ ├── .travis.yml │ ├── AUTHORS │ ├── CITATION │ ├── CONTRIBUTING.md │ ├── README.md │ ├── _config.yml │ ├── _episodes │ │ └── 01-introduction.md │ ├── _extras │ │ ├── about.md │ │ ├── discuss.md │ │ ├── figures.md │ │ └── guide.md │ ├── index.md │ ├── reference.md │ └── setup.md ├── chunk-options.R ├── dependencies.R ├── generate_md_episodes.R ├── install_r_deps.sh ├── knit_lessons.sh ├── lesson_check.py ├── lesson_initialize.py ├── markdown_ast.rb ├── repo_check.py ├── run-make-docker-serve.sh ├── test_lesson_check.py ├── util.py └── workshop_check.py ├── code └── .gitkeep ├── data └── .gitkeep ├── docker └── Dockerfile ├── fig └── .gitkeep ├── files └── .gitkeep ├── index.md ├── reference.md └── setup.md /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | insert_final_newline = true 6 | trim_trailing_whitespace = true 7 | 8 | [*.md] 9 | indent_size = 2 10 | indent_style = space 11 | max_line_length = 100 # Please keep this in sync with bin/lesson_check.py! 12 | 13 | [*.r] 14 | max_line_length = 80 15 | 16 | [*.py] 17 | indent_size = 4 18 | indent_style = space 19 | max_line_length = 79 20 | 21 | [*.sh] 22 | end_of_line = lf 23 | 24 | [Makefile] 25 | indent_style = tab 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 |
2 | Instructions 3 | 4 | Thanks for contributing! :heart: 5 | 6 | If this contribution is for instructor training, please email the link to this contribution to 7 | checkout@carpentries.org so we can record your progress. You've completed your contribution 8 | step for instructor checkout by submitting this contribution! 9 | 10 | If this issue is about a specific episode within a lesson, please provide its link or filename. 11 | 12 | Keep in mind that **lesson maintainers are volunteers** and it may take them some time to 13 | respond to your contribution. Although not all contributions can be incorporated into the lesson 14 | materials, we appreciate your time and effort to improve the curriculum. If you have any questions 15 | about the lesson maintenance process or would like to volunteer your time as a contribution 16 | reviewer, please contact The Carpentries Team at team@carpentries.org. 17 | 18 | You may delete these instructions from your comment. 19 | 20 | \- The Carpentries 21 |
22 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 |
2 | Instructions 3 | 4 | Thanks for contributing! :heart: 5 | 6 | If this contribution is for instructor training, please email the link to this contribution to 7 | checkout@carpentries.org so we can record your progress. You've completed your contribution 8 | step for instructor checkout by submitting this contribution! 9 | 10 | Keep in mind that **lesson maintainers are volunteers** and it may take them some time to 11 | respond to your contribution. Although not all contributions can be incorporated into the lesson 12 | materials, we appreciate your time and effort to improve the curriculum. If you have any questions 13 | about the lesson maintenance process or would like to volunteer your time as a contribution 14 | reviewer, please contact The Carpentries Team at team@carpentries.org. 15 | 16 | You may delete these instructions from your comment. 17 | 18 | \- The Carpentries 19 |
20 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | 6 | jobs: 7 | 8 | deploy: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v2 13 | 14 | - name: Set up Ruby 15 | uses: ruby/setup-ruby@v1 16 | with: 17 | ruby-version: '2.7.1' 18 | 19 | - name: Install Ruby dependencies 20 | run: | 21 | gem install github-pages bundler kramdown 22 | 23 | - name: Check lesson 24 | run: | 25 | make lesson-check 26 | 27 | - name: Build site 28 | run: | 29 | make site 30 | ls -lhtra _site/ 31 | 32 | docker: 33 | 34 | runs-on: ubuntu-latest 35 | 36 | steps: 37 | - uses: actions/checkout@v2 38 | - name: Build Docker image 39 | if: "!(startsWith(github.ref, 'refs/tags/'))" 40 | uses: docker/build-push-action@v1 41 | with: 42 | repository: matthewfeickert/intro-to-docker 43 | dockerfile: docker/Dockerfile 44 | tags: test 45 | tag_with_sha: true 46 | tag_with_ref: true 47 | push: false 48 | - name: List Built Images 49 | run: docker images 50 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy website 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - v* 9 | 10 | jobs: 11 | 12 | deploy: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | 18 | - name: Set up Ruby 19 | uses: ruby/setup-ruby@v1 20 | with: 21 | ruby-version: '2.7.1' 22 | 23 | - name: Install Ruby dependencies 24 | run: | 25 | gem install github-pages bundler kramdown 26 | 27 | - name: Build site 28 | run: | 29 | make site 30 | rm _site/README.md 31 | rm _site/CONTRIBUTING.md 32 | ls -lhtra _site/ 33 | 34 | - name: Deploy site to GitHub pages 35 | if: success() && github.event_name == 'push' && github.ref == 'refs/heads/main' 36 | uses: peaceiris/actions-gh-pages@v3 37 | with: 38 | github_token: ${{ secrets.GITHUB_TOKEN }} 39 | publish_dir: _site 40 | publish_branch: gh-pages 41 | force_orphan: true 42 | user_name: 'github-actions[bot]' 43 | user_email: 'github-actions[bot]@users.noreply.github.com' 44 | commit_message: Deploy to GitHub pages 45 | -------------------------------------------------------------------------------- /.github/workflows/publish-docker.yml: -------------------------------------------------------------------------------- 1 | name: Publish Docker Images 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - v* 9 | 10 | jobs: 11 | build-and-publish: 12 | name: Build and publish Docker images to Docker Hub 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Build and Publish to Registry 18 | if: "!(startsWith(github.ref, 'refs/tags/'))" 19 | uses: docker/build-push-action@v1 20 | with: 21 | username: ${{ secrets.DOCKER_USERNAME }} 22 | password: ${{ secrets.DOCKER_PASSWORD }} 23 | repository: matthewfeickert/intro-to-docker 24 | dockerfile: docker/Dockerfile 25 | tags: latest,bootcamp-2020 26 | - name: Build and Publish to Registry with Release Tag 27 | if: startsWith(github.ref, 'refs/tags/') 28 | uses: docker/build-push-action@v1 29 | with: 30 | username: ${{ secrets.DOCKER_USERNAME }} 31 | password: ${{ secrets.DOCKER_PASSWORD }} 32 | repository: matthewfeickert/intro-to-docker 33 | dockerfile: docker/Dockerfile 34 | tags: latest,latest-stable 35 | tag_with_ref: true 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *~ 3 | .DS_Store 4 | .ipynb_checkpoints 5 | .sass-cache 6 | .jekyll-cache/ 7 | __pycache__ 8 | _site 9 | .Rproj.user 10 | .Rhistory 11 | .RData 12 | .bundle/ 13 | .vendor/ 14 | vendor/ 15 | .docker-vendor/ 16 | Gemfile.lock 17 | .*history 18 | .ruby-version 19 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Matthew Feickert 2 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: "Contributor Code of Conduct" 4 | --- 5 | As contributors and maintainers of this project, 6 | we pledge to follow the [Carpentry Code of Conduct][coc]. 7 | 8 | Instances of abusive, harassing, or otherwise unacceptable behavior 9 | may be reported by following our [reporting guidelines][coc-reporting]. 10 | 11 | {% include links.md %} 12 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | [The Carpentries][c-site] ([Software Carpentry][swc-site], [Data Carpentry][dc-site], and [Library Carpentry][lc-site]) are open source projects, 4 | and we welcome contributions of all kinds: 5 | new lessons, 6 | fixes to existing material, 7 | bug reports, 8 | and reviews of proposed changes are all welcome. 9 | 10 | ## Contributor Agreement 11 | 12 | By contributing, 13 | you agree that we may redistribute your work under [our license](LICENSE.md). 14 | In exchange, 15 | we will address your issues and/or assess your change proposal as promptly as we can, 16 | and help you become a member of our community. 17 | Everyone involved in [The Carpentries][c-site] 18 | agrees to abide by our [code of conduct](CODE_OF_CONDUCT.md). 19 | 20 | ## How to Contribute 21 | 22 | The easiest way to get started is to file an issue 23 | to tell us about a spelling mistake, 24 | some awkward wording, 25 | or a factual error. 26 | This is a good way to introduce yourself 27 | and to meet some of our community members. 28 | 29 | 1. If you do not have a [GitHub][github] account, 30 | you can [send us comments by email][email]. 31 | However, 32 | we will be able to respond more quickly if you use one of the other methods described below. 33 | 34 | 2. If you have a [GitHub][github] account, 35 | or are willing to [create one][github-join], 36 | but do not know how to use Git, 37 | you can report problems or suggest improvements by [creating an issue][issues]. 38 | This allows us to assign the item to someone 39 | and to respond to it in a threaded discussion. 40 | 41 | 3. If you are comfortable with Git, 42 | and would like to add or change material, 43 | you can submit a pull request (PR). 44 | Instructions for doing this are [included below](#using-github). 45 | 46 | ## Where to Contribute 47 | 48 | 1. If you wish to change this lesson, 49 | please work in , 50 | which can be viewed at . 51 | 52 | 2. If you wish to change the example lesson, 53 | please work in , 54 | which documents the format of our lessons 55 | and can be viewed at . 56 | 57 | 3. If you wish to change the template used for workshop websites, 58 | please work in . 59 | The home page of that repository explains how to set up workshop websites, 60 | while the extra pages in 61 | provide more background on our design choices. 62 | 63 | 4. If you wish to change CSS style files, tools, 64 | or HTML boilerplate for lessons or workshops stored in `_includes` or `_layouts`, 65 | please work in . 66 | 67 | ## What to Contribute 68 | 69 | There are many ways to contribute, 70 | from writing new exercises and improving existing ones 71 | to updating or filling in the documentation 72 | and submitting [bug reports][issues] 73 | about things that don't work, aren't clear, or are missing. 74 | If you are looking for ideas, please see the 'Issues' tab for 75 | a list of issues associated with this repository, 76 | or you may also look at the issues for [Data Carpentry][dc-issues], 77 | [Software Carpentry][swc-issues], and [Library Carpentry][lc-issues] projects. 78 | 79 | Comments on issues and reviews of pull requests are just as welcome: 80 | we are smarter together than we are on our own. 81 | Reviews from novices and newcomers are particularly valuable: 82 | it's easy for people who have been using these lessons for a while 83 | to forget how impenetrable some of this material can be, 84 | so fresh eyes are always welcome. 85 | 86 | ## What *Not* to Contribute 87 | 88 | Our lessons already contain more material than we can cover in a typical workshop, 89 | so we are usually *not* looking for more concepts or tools to add to them. 90 | As a rule, 91 | if you want to introduce a new idea, 92 | you must (a) estimate how long it will take to teach 93 | and (b) explain what you would take out to make room for it. 94 | The first encourages contributors to be honest about requirements; 95 | the second, to think hard about priorities. 96 | 97 | We are also not looking for exercises or other material that only run on one platform. 98 | Our workshops typically contain a mixture of Windows, macOS, and Linux users; 99 | in order to be usable, 100 | our lessons must run equally well on all three. 101 | 102 | ## Using GitHub 103 | 104 | If you choose to contribute via GitHub, you may want to look at 105 | [How to Contribute to an Open Source Project on GitHub][how-contribute]. 106 | To manage changes, we follow [GitHub flow][github-flow]. 107 | Each lesson has two maintainers who review issues and pull requests or encourage others to do so. 108 | The maintainers are community volunteers and have final say over what gets merged into the lesson. 109 | To use the web interface for contributing to a lesson: 110 | 111 | 1. Fork the originating repository to your GitHub profile. 112 | 2. Within your version of the forked repository, move to the `gh-pages` branch and 113 | create a new branch for each significant change being made. 114 | 3. Navigate to the file(s) you wish to change within the new branches and make revisions as required. 115 | 4. Commit all changed files within the appropriate branches. 116 | 5. Create individual pull requests from each of your changed branches 117 | to the `gh-pages` branch within the originating repository. 118 | 6. If you receive feedback, make changes using your issue-specific branches of the forked 119 | repository and the pull requests will update automatically. 120 | 7. Repeat as needed until all feedback has been addressed. 121 | 122 | When starting work, please make sure your clone of the originating `gh-pages` branch is up-to-date 123 | before creating your own revision-specific branch(es) from there. 124 | Additionally, please only work from your newly-created branch(es) and *not* 125 | your clone of the originating `gh-pages` branch. 126 | Lastly, published copies of all the lessons are available in the `gh-pages` branch of the originating 127 | repository for reference while revising. 128 | 129 | ## Other Resources 130 | 131 | General discussion of [Software Carpentry][swc-site] and [Data Carpentry][dc-site] 132 | happens on the [discussion mailing list][discuss-list], 133 | which everyone is welcome to join. 134 | You can also [reach us by email][email]. 135 | 136 | [email]: mailto:admin@software-carpentry.org 137 | [dc-issues]: https://github.com/issues?q=user%3Adatacarpentry 138 | [dc-lessons]: http://datacarpentry.org/lessons/ 139 | [dc-site]: http://datacarpentry.org/ 140 | [discuss-list]: http://lists.software-carpentry.org/listinfo/discuss 141 | [github]: https://github.com 142 | [github-flow]: https://guides.github.com/introduction/flow/ 143 | [github-join]: https://github.com/join 144 | [how-contribute]: https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github 145 | [issues]: https://guides.github.com/features/issues/ 146 | [swc-issues]: https://github.com/issues?q=user%3Aswcarpentry 147 | [swc-lessons]: https://software-carpentry.org/lessons/ 148 | [swc-site]: https://software-carpentry.org/ 149 | [c-site]: https://carpentries.org/ 150 | [lc-site]: https://librarycarpentry.org/ 151 | [lc-issues]: https://github.com/issues?q=user%3Alibrarycarpentry 152 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 6 | 7 | # Synchronize with https://pages.github.com/versions 8 | ruby '>=2.5.8' 9 | 10 | gem 'github-pages', group: :jekyll_plugins 11 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: "Licenses" 4 | root: . 5 | --- 6 | ## Instructional Material 7 | 8 | All Software Carpentry, Data Carpentry, and Library Carpentry instructional material is 9 | made available under the [Creative Commons Attribution 10 | license][cc-by-human]. The following is a human-readable summary of 11 | (and not a substitute for) the [full legal text of the CC BY 4.0 12 | license][cc-by-legal]. 13 | 14 | You are free: 15 | 16 | * to **Share**---copy and redistribute the material in any medium or format 17 | * to **Adapt**---remix, transform, and build upon the material 18 | 19 | for any purpose, even commercially. 20 | 21 | The licensor cannot revoke these freedoms as long as you follow the 22 | license terms. 23 | 24 | Under the following terms: 25 | 26 | * **Attribution**---You must give appropriate credit (mentioning that 27 | your work is derived from work that is Copyright © Software 28 | Carpentry and, where practical, linking to 29 | http://software-carpentry.org/), provide a [link to the 30 | license][cc-by-human], and indicate if changes were made. You may do 31 | so in any reasonable manner, but not in any way that suggests the 32 | licensor endorses you or your use. 33 | 34 | **No additional restrictions**---You may not apply legal terms or 35 | technological measures that legally restrict others from doing 36 | anything the license permits. With the understanding that: 37 | 38 | Notices: 39 | 40 | * You do not have to comply with the license for elements of the 41 | material in the public domain or where your use is permitted by an 42 | applicable exception or limitation. 43 | * No warranties are given. The license may not give you all of the 44 | permissions necessary for your intended use. For example, other 45 | rights such as publicity, privacy, or moral rights may limit how you 46 | use the material. 47 | 48 | ## Software 49 | 50 | Except where otherwise noted, the example programs and other software 51 | provided by Software Carpentry and Data Carpentry are made available under the 52 | [OSI][osi]-approved 53 | [MIT license][mit-license]. 54 | 55 | Permission is hereby granted, free of charge, to any person obtaining 56 | a copy of this software and associated documentation files (the 57 | "Software"), to deal in the Software without restriction, including 58 | without limitation the rights to use, copy, modify, merge, publish, 59 | distribute, sublicense, and/or sell copies of the Software, and to 60 | permit persons to whom the Software is furnished to do so, subject to 61 | the following conditions: 62 | 63 | The above copyright notice and this permission notice shall be 64 | included in all copies or substantial portions of the Software. 65 | 66 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 67 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 68 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 69 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 70 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 71 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 72 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 73 | 74 | ## Trademark 75 | 76 | "Software Carpentry" and "Data Carpentry" and their respective logos 77 | are registered trademarks of [Community Initiatives][CI]. 78 | 79 | [cc-by-human]: https://creativecommons.org/licenses/by/4.0/ 80 | [cc-by-legal]: https://creativecommons.org/licenses/by/4.0/legalcode 81 | [mit-license]: https://opensource.org/licenses/mit-license.html 82 | [ci]: http://communityin.org/ 83 | [osi]: https://opensource.org 84 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ## ======================================== 2 | ## Commands for both workshop and lesson websites. 3 | 4 | # Settings 5 | MAKEFILES=Makefile $(wildcard *.mk) 6 | JEKYLL=bundle config --local set path .vendor/bundle && bundle install && bundle update && bundle exec jekyll 7 | PARSER=bin/markdown_ast.rb 8 | DST=_site 9 | 10 | # Check Python 3 is installed and determine if it's called via python3 or python 11 | # (https://stackoverflow.com/a/4933395) 12 | PYTHON3_EXE := $(shell which python3 2>/dev/null) 13 | ifneq (, $(PYTHON3_EXE)) 14 | ifeq (,$(findstring Microsoft/WindowsApps/python3,$(subst \,/,$(PYTHON3_EXE)))) 15 | PYTHON := python3 16 | endif 17 | endif 18 | 19 | ifeq (,$(PYTHON)) 20 | PYTHON_EXE := $(shell which python 2>/dev/null) 21 | ifneq (, $(PYTHON_EXE)) 22 | PYTHON_VERSION_FULL := $(wordlist 2,4,$(subst ., ,$(shell python --version 2>&1))) 23 | PYTHON_VERSION_MAJOR := $(word 1,${PYTHON_VERSION_FULL}) 24 | ifneq (3, ${PYTHON_VERSION_MAJOR}) 25 | $(error "Your system does not appear to have Python 3 installed.") 26 | endif 27 | PYTHON := python 28 | else 29 | $(error "Your system does not appear to have any Python installed.") 30 | endif 31 | endif 32 | 33 | 34 | # Controls 35 | .PHONY : commands clean files 36 | 37 | # Default target 38 | .DEFAULT_GOAL := commands 39 | 40 | ## I. Commands for both workshop and lesson websites 41 | ## ================================================= 42 | 43 | ## * serve : render website and run a local server 44 | serve : lesson-md 45 | ${JEKYLL} serve 46 | 47 | ## * site : build website but do not run a server 48 | site : lesson-md 49 | ${JEKYLL} build 50 | 51 | ## * docker-serve : use Docker to serve the site 52 | docker-serve : 53 | docker pull carpentries/lesson-docker:latest 54 | docker run --rm -it \ 55 | -v $${PWD}:/home/rstudio \ 56 | -p 4000:4000 \ 57 | -p 8787:8787 \ 58 | -e USERID=$$(id -u) \ 59 | -e GROUPID=$$(id -g) \ 60 | carpentries/lesson-docker:latest 61 | 62 | ## * repo-check : check repository settings 63 | repo-check : 64 | @${PYTHON} bin/repo_check.py -s . 65 | 66 | ## * clean : clean up junk files 67 | clean : 68 | @rm -rf ${DST} 69 | @rm -rf .sass-cache 70 | @rm -rf bin/__pycache__ 71 | @find . -name .DS_Store -exec rm {} \; 72 | @find . -name '*~' -exec rm {} \; 73 | @find . -name '*.pyc' -exec rm {} \; 74 | 75 | ## * clean-rmd : clean intermediate R files (that need to be committed to the repo) 76 | clean-rmd : 77 | @rm -rf ${RMD_DST} 78 | @rm -rf fig/rmd-* 79 | 80 | 81 | ## 82 | ## II. Commands specific to workshop websites 83 | ## ================================================= 84 | 85 | .PHONY : workshop-check 86 | 87 | ## * workshop-check : check workshop homepage 88 | workshop-check : 89 | @${PYTHON} bin/workshop_check.py . 90 | 91 | 92 | ## 93 | ## III. Commands specific to lesson websites 94 | ## ================================================= 95 | 96 | .PHONY : lesson-check lesson-md lesson-files lesson-fixme install-rmd-deps 97 | 98 | # RMarkdown files 99 | RMD_SRC = $(wildcard _episodes_rmd/??-*.Rmd) 100 | RMD_DST = $(patsubst _episodes_rmd/%.Rmd,_episodes/%.md,$(RMD_SRC)) 101 | 102 | # Lesson source files in the order they appear in the navigation menu. 103 | MARKDOWN_SRC = \ 104 | index.md \ 105 | CODE_OF_CONDUCT.md \ 106 | setup.md \ 107 | $(sort $(wildcard _episodes/*.md)) \ 108 | reference.md \ 109 | $(sort $(wildcard _extras/*.md)) \ 110 | LICENSE.md 111 | 112 | # Generated lesson files in the order they appear in the navigation menu. 113 | HTML_DST = \ 114 | ${DST}/index.html \ 115 | ${DST}/conduct/index.html \ 116 | ${DST}/setup/index.html \ 117 | $(patsubst _episodes/%.md,${DST}/%/index.html,$(sort $(wildcard _episodes/*.md))) \ 118 | ${DST}/reference/index.html \ 119 | $(patsubst _extras/%.md,${DST}/%/index.html,$(sort $(wildcard _extras/*.md))) \ 120 | ${DST}/license/index.html 121 | 122 | ## * install-rmd-deps : Install R packages dependencies to build the RMarkdown lesson 123 | install-rmd-deps: 124 | @${SHELL} bin/install_r_deps.sh 125 | 126 | ## * lesson-md : convert Rmarkdown files to markdown 127 | lesson-md : ${RMD_DST} 128 | 129 | _episodes/%.md: _episodes_rmd/%.Rmd install-rmd-deps 130 | @mkdir -p _episodes 131 | @bin/knit_lessons.sh $< $@ 132 | 133 | ## * lesson-check : validate lesson Markdown 134 | lesson-check : lesson-fixme 135 | @${PYTHON} bin/lesson_check.py -s . -p ${PARSER} -r _includes/links.md 136 | 137 | ## * lesson-check-all : validate lesson Markdown, checking line lengths and trailing whitespace 138 | lesson-check-all : 139 | @${PYTHON} bin/lesson_check.py -s . -p ${PARSER} -r _includes/links.md -l -w --permissive 140 | 141 | ## * unittest : run unit tests on checking tools 142 | unittest : 143 | @${PYTHON} bin/test_lesson_check.py 144 | 145 | ## * lesson-files : show expected names of generated files for debugging 146 | lesson-files : 147 | @echo 'RMD_SRC:' ${RMD_SRC} 148 | @echo 'RMD_DST:' ${RMD_DST} 149 | @echo 'MARKDOWN_SRC:' ${MARKDOWN_SRC} 150 | @echo 'HTML_DST:' ${HTML_DST} 151 | 152 | ## * lesson-fixme : show FIXME markers embedded in source files 153 | lesson-fixme : 154 | @grep --fixed-strings --word-regexp --line-number --no-messages FIXME ${MARKDOWN_SRC} || true 155 | 156 | ## 157 | ## IV. Auxililary (plumbing) commands 158 | ## ================================================= 159 | 160 | ## * commands : show all commands. 161 | commands : 162 | @sed -n -e '/^##/s|^##[[:space:]]*||p' $(MAKEFILE_LIST) 163 | 164 | image: 165 | docker build . \ 166 | -f docker/Dockerfile \ 167 | -t matthewfeickert/intro-to-docker:debug-local 168 | 169 | docker_debug: 170 | docker run \ 171 | --rm \ 172 | -it \ 173 | -p 8888:8888 \ 174 | matthewfeickert/intro-to-docker:debug-local 175 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction to Docker 2 | 3 | This repository generates the corresponding lesson website from [The Carpentries](https://carpentries.org/) repertoire of lessons. 4 | 5 | ## Contributing 6 | 7 | We welcome all contributions to improve the lesson! Maintainers will do their best to help you if you have any 8 | questions, concerns, or experience any difficulties along the way. 9 | 10 | We'd like to ask you to familiarize yourself with our [Contribution Guide](CONTRIBUTING.md) and have a look at 11 | the [more detailed guidelines][lesson-example] on proper formatting, ways to render the lesson locally, and even 12 | how to write new episodes. 13 | 14 | Please see the current list of [issues][github-issues] for ideas for contributing to this 15 | repository. For making your contribution, we use the GitHub flow, which is 16 | nicely explained in the chapter [Contributing to a Project](http://git-scm.com/book/en/v2/GitHub-Contributing-to-a-Project) in Pro Git 17 | by Scott Chacon. 18 | Look for the tag ![good_first_issue](https://img.shields.io/badge/-good%20first%20issue-gold.svg). This indicates that the mantainers will welcome a pull request fixing this issue. 19 | 20 | ## Maintainer(s) 21 | 22 | Current maintainers of this lesson are 23 | 24 | * [Matthew Feickert](http://www.matthewfeickert.com/) 25 | 26 | ## Authors 27 | 28 | A list of contributors to the lesson can be found in [AUTHORS](AUTHORS) 29 | 30 | ## Citation 31 | 32 | To cite this lesson, please consult with [CITATION](CITATION) 33 | 34 | [lesson-example]: https://carpentries.github.io/lesson-example 35 | [github-issues]: https://github.com/matthewfeickert/intro-to-docker/issues 36 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------ 2 | # Values for this lesson. 3 | #------------------------------------------------------------ 4 | 5 | # Which carpentry is this ("swc", "dc", "lc", or "cp")? 6 | # swc: Software Carpentry 7 | # dc: Data Carpentry 8 | # lc: Library Carpentry 9 | # cp: Carpentries (to use for instructor traning for instance) 10 | carpentry: "swc" 11 | 12 | # Overall title for pages. 13 | title: "Introduction to Docker" 14 | 15 | # Life cycle stage of the lesson 16 | # possible values: "pre-alpha", "alpha", "beta", "stable" 17 | life_cycle: "beta" 18 | 19 | #------------------------------------------------------------ 20 | # Generic settings (should not need to change). 21 | #------------------------------------------------------------ 22 | 23 | # What kind of thing is this ("workshop" or "lesson")? 24 | kind: "lesson" 25 | 26 | # Magic to make URLs resolve both locally and on GitHub. 27 | # See https://help.github.com/articles/repository-metadata-on-github-pages/. 28 | # Please don't change it: / is correct. 29 | repository: matthewfeickert/intro-to-docker 30 | 31 | # Email address, no mailto: 32 | email: "matthew.feickert@cern.ch" 33 | 34 | # Sites. 35 | amy_site: "https://amy.software-carpentry.org/workshops" 36 | carpentries_github: "https://github.com/carpentries" 37 | carpentries_pages: "https://carpentries.github.io" 38 | carpentries_site: "https://carpentries.org/" 39 | dc_site: "http://datacarpentry.org" 40 | example_repo: "https://github.com/carpentries/lesson-example" 41 | example_site: "https://carpentries.github.io/lesson-example" 42 | lc_site: "https://librarycarpentry.org/" 43 | swc_github: "https://github.com/swcarpentry" 44 | swc_pages: "https://swcarpentry.github.io" 45 | swc_site: "https://software-carpentry.org" 46 | template_repo: "https://github.com/carpentries/styles" 47 | training_site: "https://carpentries.github.io/instructor-training" 48 | workshop_repo: "https://github.com/carpentries/workshop-template" 49 | workshop_site: "https://carpentries.github.io/workshop-template" 50 | cc_by_human: "https://creativecommons.org/licenses/by/4.0/" 51 | 52 | # Surveys. 53 | swc_pre_survey: "https://www.surveymonkey.com/r/swc_pre_workshop_v1?workshop_id=" 54 | swc_post_survey: "https://www.surveymonkey.com/r/swc_post_workshop_v1?workshop_id=" 55 | training_post_survey: "https://www.surveymonkey.com/r/post-instructor-training" 56 | dc_pre_survey: "https://www.surveymonkey.com/r/dcpreworkshopassessment?workshop_id=" 57 | dc_post_survey: "https://www.surveymonkey.com/r/dcpostworkshopassessment?workshop_id=" 58 | lc_pre_survey: "https://www.surveymonkey.com/r/lcpreworkshopsurvey?workshop_id=" 59 | lc_post_survey: "https://www.surveymonkey.com/r/lcpostworkshopsurvey?workshop_id=" 60 | instructor_pre_survey: "https://www.surveymonkey.com/r/instructor_training_pre_survey?workshop_id=" 61 | instructor_post_survey: "https://www.surveymonkey.com/r/instructor_training_post_survey?workshop_id=" 62 | 63 | 64 | # Start time in minutes (0 to be clock-independent, 540 to show a start at 09:00 am). 65 | start_time: 0 66 | 67 | # Specify that things in the episodes collection should be output. 68 | collections: 69 | episodes: 70 | output: true 71 | permalink: /:path/index.html 72 | extras: 73 | output: true 74 | permalink: /:path/index.html 75 | 76 | # Set the default layout for things in the episodes collection. 77 | defaults: 78 | - values: 79 | root: . 80 | layout: page 81 | - scope: 82 | path: "" 83 | type: episodes 84 | values: 85 | root: .. 86 | layout: episode 87 | - scope: 88 | path: "" 89 | type: extras 90 | values: 91 | root: .. 92 | layout: page 93 | 94 | # Files and directories that are not to be copied. 95 | exclude: 96 | - Makefile 97 | - bin/ 98 | - .Rproj.user/ 99 | 100 | # Turn on built-in syntax highlighting. 101 | highlighter: rouge 102 | -------------------------------------------------------------------------------- /_episodes/01-introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Introduction" 3 | teaching: 5 4 | exercises: 5 5 | questions: 6 | - "What are containers?" 7 | objectives: 8 | - "Understand the basics of images and containers." 9 | keypoints: 10 | - "Images are series of zip files that act as templates for containers." 11 | - "Containers are runtime instantiation of images — images with state and are native processes." 12 | --- 13 | 14 | # Documentation 15 | 16 | The [official Docker documentation and tutorial][docker-tutorial] can be found on the 17 | Docker website. 18 | It is quite thorough and useful. 19 | It is an excellent guide that should be routinely visited, but the emphasis of this 20 | introduction is on using Docker, not how Docker itself works. 21 | 22 | A note up front, Docker has very similar syntax to Git and Linux, so if you are familiar 23 | with the command line tools for them then most of Docker should seem somewhat natural 24 | (though you should still read the docs!). 25 | 26 | [![Docker logo](https://www.docker.com/sites/default/files/social/docker_twitter_share_new.png)](https://www.docker.com/) 27 | 28 | # Docker Images and Containers 29 | 30 | It is still important to know what Docker _is_ and what the components of it _are_. 31 | Docker images are executables that bundle together all necessary components for an 32 | application or an environment. 33 | [Docker containers][docker-containers] are the runtime instances of images — they 34 | are images with a state and act as native Linux processes. 35 | 36 | Importantly, containers share the host machine's OS system kernel and so don't require an 37 | OS per application. 38 | As discrete processes containers take up only as much memory as necessary, making them 39 | very lightweight and fast to spin up to run. 40 | 41 | It is also worth noting that as images are executables that produce containers, the same image 42 | can create multiple container instances that are running simultaneously as different processes. 43 | If you think about other executables that can be run in multiple processes on your machine this 44 | is perhaps not too surprising. 45 | 46 | [![Docker structure](https://www.docker.com/sites/default/files/styles/large/public/container-what-is-container.png)](https://www.docker.com/resources/what-container) 47 | 48 | [docker-tutorial]: https://docs.docker.com/get-started 49 | [docker-containers]: https://www.docker.com/resources/what-container 50 | 51 | {% include links.md %} 52 | -------------------------------------------------------------------------------- /_episodes/02-pulling-images.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Pulling Images" 3 | teaching: 10 4 | exercises: 5 5 | questions: 6 | - "How are images downloaded?" 7 | - "How are images distinguished?" 8 | objectives: 9 | - "Pull images from Docker Hub image registry" 10 | - "List local images" 11 | - "Introduce image tags" 12 | keypoints: 13 | - "Pull images with `docker pull`" 14 | - "List images with `docker images`" 15 | - "Image tags distinguish releases or version and are appended to the image name with a colon" 16 | --- 17 | 18 | # Docker Hub 19 | 20 | Much like GitHub allows for web hosting and searching for code, the [Docker Hub][docker-hub] 21 | image registry allows the same for Docker images. 22 | Hosting and building of images is [free for public repositories][docker-hub-billing] and 23 | allows for downloading images as they are needed. 24 | Additionally, through integrations with GitHub and Bitbucket, Docker Hub repositories can 25 | be linked against Git repositories so that 26 | [automated builds of Dockerfiles on Docker Hub][docker-hub-builds] will be triggered by 27 | pushes to repositories. 28 | 29 | # Pulling Images 30 | 31 | To begin with we're going to [pull][docker-docs-pull] down the Docker image we're going 32 | to be working in for the tutorial 33 | 34 | ~~~ 35 | docker pull matthewfeickert/intro-to-docker 36 | ~~~ 37 | {: .source} 38 | 39 | and then [list the images][docker-docs-images] that we have available to us locally 40 | 41 | ~~~ 42 | docker images 43 | ~~~ 44 | {: .source} 45 | 46 | If you have many images and want to get information on a particular one you can apply a 47 | filter, such as the repository name 48 | 49 | ~~~ 50 | docker images matthewfeickert/intro-to-docker 51 | ~~~ 52 | {: .source} 53 | 54 | ~~~ 55 | REPOSITORY TAG IMAGE ID CREATED SIZE 56 | matthewfeickert/intro-to-docker latest f4279e4a8ab0 26 hours ago 1.45GB 57 | ~~~ 58 | {: .output} 59 | 60 | or more explicitly 61 | 62 | ~~~ 63 | docker images --filter=reference="matthewfeickert/intro-to-docker" 64 | ~~~ 65 | {: .source} 66 | 67 | ~~~ 68 | REPOSITORY TAG IMAGE ID CREATED SIZE 69 | matthewfeickert/intro-to-docker latest f4279e4a8ab0 26 hours ago 1.45GB 70 | ~~~ 71 | {: .output} 72 | 73 | You can see here that there is the `TAG` field associated with the 74 | `matthewfeickert/intro-to-docker` image. 75 | Tags are way of further specifying different versions of the same image. 76 | As an example, let's pull the buster release tag of the 77 | [Debian image](https://hub.docker.com/_/debian). 78 | 79 | ~~~ 80 | docker pull debian:buster 81 | docker images debian 82 | ~~~ 83 | {: .source} 84 | 85 | ~~~ 86 | buster: Pulling from library/debian 87 | : Pull complete 88 | Digest: sha256: 89 | Status: Downloaded newer image for debian:buster 90 | docker.io/library/debian:buster 91 | 92 | REPOSITORY TAG IMAGE ID CREATED SIZE 93 | debian buster ee11c54e6bb7 3 weeks ago 114MB 94 | ~~~ 95 | {: .output} 96 | 97 | Additionally, there might be times where the _same_ image has different tags. 98 | For example, we can pull the `bootcamp-2020` tag of the `matthewfeickert/intro-to-docker` 99 | image, but when we inspect it we wee that it is the **same** image as the one we already pulled. 100 | 101 | ~~~ 102 | docker pull matthewfeickert/intro-to-docker:bootcamp-2020 103 | docker images matthewfeickert/intro-to-docker 104 | ~~~ 105 | {: .source} 106 | 107 | ~~~ 108 | REPOSITORY TAG IMAGE ID CREATED SIZE 109 | matthewfeickert/intro-to-docker bootcamp-2020 f4279e4a8ab0 26 hours ago 1.45GB 110 | matthewfeickert/intro-to-docker latest f4279e4a8ab0 26 hours ago 1.45GB 111 | ~~~ 112 | {: .output} 113 | 114 | > ## Pulling Python 115 | > 116 | > Pull the image for Python 3.8 and then list all `python` images along with 117 | > the `matthewfeickert/intro-to-docker` image 118 | > 119 | > > ## Solution 120 | > > 121 | > > ~~~ 122 | > > docker pull python:3.8 123 | > > docker images --filter=reference="matthewfeickert/intro-to-docker" --filter=reference="python" 124 | > > ~~~ 125 | > > {: .source} 126 | > > 127 | > > ~~~ 128 | > > REPOSITORY TAG IMAGE ID CREATED SIZE 129 | > > python 3.8 79cc46abd78d 2 weeks ago 882MB 130 | > > matthewfeickert/intro-to-docker bootcamp-2020 f4279e4a8ab0 26 hours ago 1.45GB 131 | > > matthewfeickert/intro-to-docker latest f4279e4a8ab0 26 hours ago 1.45GB 132 | > > ~~~ 133 | > > {: .output} 134 | > {: .solution} 135 | {: .challenge} 136 | 137 | [docker-hub]: https://hub.docker.com/ 138 | [docker-hub-billing]: https://hub.docker.com/billing-plans/ 139 | [docker-hub-builds]: https://docs.docker.com/docker-hub/builds/ 140 | [docker-docs-pull]: https://docs.docker.com/engine/reference/commandline/pull/ 141 | [docker-docs-images]: https://docs.docker.com/engine/reference/commandline/images/ 142 | 143 | {% include links.md %} 144 | -------------------------------------------------------------------------------- /_episodes/03-running-containers.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Running Containers" 3 | teaching: 15 4 | exercises: 5 5 | questions: 6 | - "How are containers run?" 7 | - "How do you monitor containers?" 8 | - "How are containers exited?" 9 | - "How are containers restarted?" 10 | objectives: 11 | - "Run containers" 12 | - "Understand container state" 13 | - "Stop and restart containers" 14 | keypoints: 15 | - "Run containers with `docker run`" 16 | - "Monitor containers with `docker ps`" 17 | - "Exit interactive sessions just as you would a shell" 18 | - "Restart stopped containers with `docker start`" 19 | --- 20 | 21 | To use a Docker image as a particular instance on a host machine you [run][docker-docs-run] 22 | it as a container. 23 | You can run in either a [detached or foreground][docker-docs-run-detached] (interactive) mode. 24 | 25 | Run the image we pulled as an interactive container 26 | 27 | ~~~ 28 | docker run -it matthewfeickert/intro-to-docker:latest /bin/bash 29 | ~~~ 30 | {: .source} 31 | 32 | You are now inside the container in an interactive bash session. Check the file directory 33 | 34 | ~~~ 35 | pwd 36 | ~~~ 37 | {: .source} 38 | 39 | ~~~ 40 | /home/docker/data 41 | ~~~ 42 | {: .output} 43 | 44 | and check the host to see that you are not in your local host system 45 | 46 | ~~~ 47 | hostname 48 | ~~~ 49 | {: .source} 50 | 51 | ~~~ 52 | 53 | ~~~ 54 | {: .output} 55 | 56 | Further, check the `os-release` to see that you are actually inside a release of Debian 57 | (given the [Docker Library's Python image][docker-hub-python] Dockerfile choices) 58 | 59 | ~~~ 60 | cat /etc/os-release 61 | ~~~ 62 | {: .source} 63 | 64 | ~~~ 65 | PRETTY_NAME="Debian GNU/Linux 10 (buster)" 66 | NAME="Debian GNU/Linux" 67 | VERSION_ID="10" 68 | VERSION="10 (buster)" 69 | VERSION_CODENAME=buster 70 | ID=debian 71 | HOME_URL="https://www.debian.org/" 72 | SUPPORT_URL="https://www.debian.org/support" 73 | BUG_REPORT_URL="https://bugs.debian.org/ 74 | ~~~ 75 | {: .output} 76 | 77 | ## Monitoring Containers 78 | 79 | Open up a new terminal tab on the host machine and 80 | [list the containers that are currently running][docker-docs-ps] 81 | 82 | ~~~ 83 | docker ps 84 | ~~~ 85 | {: .source} 86 | 87 | ~~~ 88 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 89 | "/bin/bash" n minutes ago Up n minutes 90 | ~~~ 91 | {: .output} 92 | 93 | Notice that the name of your container is some randomly generated name. 94 | To make the name more helpful, [rename][docker-docs-rename] the running container 95 | 96 | ~~~ 97 | docker rename my-example 98 | ~~~ 99 | {: .source} 100 | 101 | and then verify it has been renamed 102 | 103 | ~~~ 104 | docker ps 105 | ~~~ 106 | {: .source} 107 | 108 | ~~~ 109 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 110 | "/bin/bash" n minutes ago Up n minutes my-example 111 | ~~~ 112 | {: .output} 113 | 114 | > ## Renaming by name 115 | > 116 | >You can also identify containers to rename by their current name 117 | > 118 | >~~~ 119 | >docker rename my-example 120 | >~~~ 121 | >{: .source} 122 | {: .callout} 123 | 124 | # Exiting and restarting containers 125 | 126 | As a test, create a file in your container by printing the current datetime into a file 127 | 128 | ~~~ 129 | date > test.txt 130 | ~~~ 131 | {: .source} 132 | 133 | In the container exit at the command line 134 | 135 | ~~~ 136 | exit 137 | ~~~ 138 | {: .source} 139 | 140 | You are returned to your shell. 141 | If you list the containers you will notice that none are running 142 | 143 | ~~~ 144 | docker ps 145 | ~~~ 146 | {: .source} 147 | 148 | ~~~ 149 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 150 | ~~~ 151 | {: .output} 152 | 153 | but you can see all containers that have been run and not removed with 154 | 155 | ~~~ 156 | docker ps -a 157 | ~~~ 158 | {: .source} 159 | 160 | ~~~ 161 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 162 | "/bin/bash" n minutes ago Exited (0) t seconds ago my-example 163 | ~~~ 164 | {: .output} 165 | 166 | To restart your exited Docker container [start][docker-docs-start] it again and then 167 | [attach][docker-docs-attach] it to your shell 168 | 169 | ~~~ 170 | docker start 171 | docker attach 172 | ~~~ 173 | {: .source} 174 | 175 | > ## Starting and attaching by name 176 | > 177 | >You can also start and attach containers by their name 178 | > 179 | >~~~ 180 | >docker start 181 | >docker attach 182 | >~~~ 183 | >{: .source} 184 | {: .callout} 185 | 186 | 187 | Notice that your entry point is still `/home/docker/data` and then check that your 188 | `test.txt` still exists with the datetime you printed into it 189 | 190 | ~~~ 191 | ls 192 | ~~~ 193 | {: .source} 194 | 195 | ~~~ 196 | test.txt 197 | ~~~ 198 | {: .output} 199 | 200 | ~~~ 201 | cat test.txt 202 | ~~~ 203 | {: .source} 204 | 205 | ~~~ 206 | Wed Aug 26 08:03:53 UTC 2020 207 | ~~~ 208 | {: .output} 209 | 210 | So this shows us that we can exit Docker containers for arbitrary lengths of time and then 211 | return to our working environment inside of them as desired. 212 | 213 | >## Clean up a container 214 | > 215 | >If you want a container to be [cleaned up][docker-docs-run-clean-up] — that is 216 | >deleted — after you exit it then run with the `--rm` option flag 217 | > 218 | >~~~ 219 | >docker run --rm -it /bin/bash 220 | >~~~ 221 | >{: .source} 222 | {: .callout} 223 | 224 | [docker-docs-run]: https://docs.docker.com/engine/reference/run/ 225 | [docker-docs-run-detached]: https://docs.docker.com/engine/reference/run/#detached-vs-foreground 226 | [docker-docs-run-clean-up]: https://docs.docker.com/engine/reference/run/#clean-up---rm 227 | [docker-hub-python]: https://github.com/docker-library/python 228 | [docker-docs-ps]: https://docs.docker.com/engine/reference/commandline/ps/ 229 | [docker-docs-rename]: https://docs.docker.com/engine/reference/commandline/rename/ 230 | [docker-docs-start]: https://docs.docker.com/engine/reference/commandline/start/ 231 | [docker-docs-attach]: https://docs.docker.com/engine/reference/commandline/attach/ 232 | 233 | {% include links.md %} 234 | -------------------------------------------------------------------------------- /_episodes/04-file-io.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "File I/O with Containers" 3 | teaching: 15 4 | exercises: 5 5 | questions: 6 | - "How do containers interact with my local file system?" 7 | objectives: 8 | - "Better understand I/O with containers" 9 | keypoints: 10 | - "Learn how `docker cp` works" 11 | - "Learn about volume mounts" 12 | - "Show port forwarding of applications" 13 | --- 14 | 15 | # Copying 16 | 17 | [Copying][docker-docs-cp] files between the local host and Docker containers is possible. 18 | On your local host find a file that you want to transfer to the container and then 19 | 20 | ~~~ 21 | touch io_example.txt 22 | # If on Mac may need to do: chmod a+x io_example.txt 23 | echo "This was written on local host" > io_example.txt 24 | docker cp io_example.txt :/home/docker/data/ 25 | ~~~ 26 | {: .source} 27 | 28 | and then from the container check and modify it in some way 29 | 30 | ~~~ 31 | pwd 32 | ls 33 | cat io_example.txt 34 | echo "This was written inside Docker" >> io_example.txt 35 | ~~~ 36 | {: .source} 37 | 38 | ~~~ 39 | /home/docker/data 40 | io_example.txt 41 | This was written on local host 42 | ~~~ 43 | {: .output} 44 | 45 | and then on the local host copy the file out of the container 46 | 47 | ~~~ 48 | docker cp :/home/docker/data/io_example.txt . 49 | ~~~ 50 | {: .source} 51 | 52 | and verify if you want that the file has been modified as you wanted 53 | 54 | ~~~ 55 | cat io_example.txt 56 | ~~~ 57 | {: .source} 58 | 59 | ~~~ 60 | This was written on local host 61 | This was written inside Docker 62 | ~~~ 63 | {: .output} 64 | 65 | # Volume mounting 66 | 67 | What is more common and arguably **more** useful is to [mount volumes][docker-docs-volumes] to 68 | containers with the `-v` flag. 69 | This allows for direct access to the host file system inside of the container and for 70 | container processes to write directly to the host file system. 71 | 72 | ~~~ 73 | docker run -v : 74 | ~~~ 75 | {: .source} 76 | 77 | For example, to mount your current working directory on your local machine to the `data` 78 | directory in the example container 79 | 80 | ~~~ 81 | docker run --rm -it -v $PWD:/home/docker/data matthewfeickert/intro-to-docker:latest 82 | ~~~ 83 | {: .source} 84 | 85 | From inside the container you can `ls` to see the contents of your directory on your local 86 | machine 87 | 88 | ~~~ 89 | ls 90 | ~~~ 91 | {: .source} 92 | 93 | and yet you are still inside the container 94 | 95 | ~~~ 96 | pwd 97 | ~~~ 98 | {: .source} 99 | 100 | ~~~ 101 | /home/docker/data 102 | ~~~ 103 | {: .output} 104 | 105 | You can also see that any files created in this path in the container persist upon exit 106 | 107 | ~~~ 108 | touch created_inside.txt 109 | exit 110 | ls *.txt 111 | ~~~ 112 | {: .source} 113 | 114 | ~~~ 115 | created_inside.txt 116 | ~~~ 117 | {: .output} 118 | 119 | This I/O allows for Docker images to be used for specific tasks that may be difficult to 120 | do with the tools or software installed on only the local host machine. 121 | For example, debugging problems with software that arise on cross-platform software, or 122 | even just having a specific version of software perform a task (e.g., using Python 2 when 123 | you don't want it on your machine, or using a specific release of 124 | [TeX Live][Tex-Live-image] when you aren't ready to update your system release). 125 | 126 | > ## Flag choices 127 | > 128 | > What will be the result of running the following command? 129 | > > ~~~ 130 | > > docker run --rm -v $PWD:/home/docker/data matthewfeickert/intro-to-docker:latest 131 | > > ~~~ 132 | > > {: .source} 133 | > 134 | > > ## Solution 135 | > > 136 | > > Outwardly it would appear that there is no affect! 137 | > > You are returned to your starting terminal. 138 | > > However, something _did_ happen. 139 | > > Look again at the flags: `--rm -v` ...but no `-it` for interactive. 140 | > > So the container got spun up by `docker run`, wasn't given any command and 141 | > > and so executed a Bash shell with `/bin/bash`, wasn't put into an interactive 142 | > > state and finished, and then cleaned itself up with `--rm`. 143 | > {: .solution} 144 | {: .challenge} 145 | 146 | 147 | # Running Jupyter from a Docker Container 148 | 149 | You can run a Jupyter server from inside of your Docker container. 150 | First run a container while [exposing][docker-docs-run-expose-ports] the container's 151 | internal port `8888` with the `-p` flag 152 | 153 | ~~~ 154 | docker run --rm -it -p 8888:8888 matthewfeickert/intro-to-docker:latest /bin/bash 155 | ~~~ 156 | {: .source} 157 | 158 | Then [start a Jupyter server][jupyter-docs-server] with the server listening on all IPs 159 | 160 | ~~~ 161 | jupyter lab --allow-root --no-browser --ip 0.0.0.0 162 | ~~~ 163 | {: .source} 164 | 165 | though for your convince the example container has been configured with these default 166 | settings so you can just run 167 | 168 | ~~~ 169 | jupyter lab 170 | ~~~ 171 | {: .source} 172 | 173 | Finally, copy and paste the following with the generated token from the server as 174 | `` into your web browser on your local host machine 175 | 176 | ~~~ 177 | http://localhost:8888/?token= 178 | ~~~ 179 | {: .source} 180 | 181 | You now have access to Jupyter running on your Docker container. 182 | 183 | [docker-docs-cp]: https://docs.docker.com/engine/reference/commandline/cp/ 184 | [docker-docs-volumes]: https://docs.docker.com/storage/volumes/ 185 | [Tex-Live-image]: https://hub.docker.com/r/matthewfeickert/latex-docker/ 186 | [docker-docs-run-expose-ports]: https://docs.docker.com/engine/reference/run/#expose-incoming-ports 187 | [jupyter-docs-server]: https://jupyter.readthedocs.io/en/latest/running.html#starting-the-notebook-server 188 | 189 | {% include links.md %} 190 | -------------------------------------------------------------------------------- /_episodes/06-removal.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Removal of Containers and Images" 3 | teaching: 5 4 | exercises: 5 5 | questions: 6 | - "How do you cleanup old containers?" 7 | - "How do you delete images?" 8 | objectives: 9 | - "Learn how to cleanup after Docker" 10 | keypoints: 11 | - "Remove containers with `docker rm`" 12 | - "Remove images with `docker rmi`" 13 | - "Perform faster cleanup with `docker container prune`, `docker image prune`, and `docker system prune`" 14 | --- 15 | 16 | You can cleanup/remove a container [`docker rm`][docker-docs-rm] 17 | ~~~ 18 | docker rm 19 | ~~~ 20 | {: .source} 21 | 22 | > ## Remove old containers 23 | > 24 | > Start an instance of the tutorial container, exit it, and then remove it with 25 | > `docker rm` 26 | > 27 | > > ## Solution 28 | > > 29 | > > ~~~ 30 | > > docker run matthewfeickert/intro-to-docker:latest 31 | > > docker ps -a 32 | > > docker rm 33 | > > docker ps -a 34 | > > ~~~ 35 | > > {: .source} 36 | > > 37 | > > ~~~ 38 | > >CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 39 | > > "/bin/bash" n seconds ago Exited (0) t seconds ago 40 | > > 41 | > > 42 | > > 43 | > >CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 44 | > > ~~~ 45 | > > {: .output} 46 | > {: .solution} 47 | {: .challenge} 48 | 49 | You can remove an image from your computer entirely with [`docker rmi`][docker-docs-rmi] 50 | ~~~ 51 | docker rmi 52 | ~~~ 53 | {: .source} 54 | 55 | > ## Remove an image 56 | > 57 | > Pull down the Python 2.7 image from Docker Hub and then delete it. 58 | > 59 | > > ## Solution 60 | > > 61 | > > ~~~ 62 | > > docker pull python:2.7 63 | > > docker images python 64 | > > docker rmi 65 | > > docker images python 66 | > > ~~~ 67 | > > {: .source} 68 | > > 69 | > > ~~~ 70 | > >2.7: Pulling from library/python 71 | > >: Pull complete 72 | > >: Pull complete 73 | > >: Pull complete 74 | > >: Pull complete 75 | > >: Pull complete 76 | > >: Pull complete 77 | > >: Pull complete 78 | > >: Pull complete 79 | > >Digest: sha256: 80 | > >Status: Downloaded newer image for python:2.7 81 | > >docker.io/library/python:2.7 82 | > > 83 | > >REPOSITORY TAG IMAGE ID CREATED SIZE 84 | > >python 3.7 5c1cd4638fb7 8 days ago 876MB 85 | > >python 3.8 79cc46abd78d 2 weeks ago 882MB 86 | > >python 2.7 68e7be49c28c 4 months ago 902MB 87 | > > 88 | > >Untagged: python@sha256: 89 | > >Deleted: sha256: 90 | > >Deleted: sha256: 91 | > >Deleted: sha256: 92 | > >Deleted: sha256: 93 | > >Deleted: sha256: 94 | > >Deleted: sha256: 95 | > >Deleted: sha256: 96 | > >Deleted: sha256: 97 | > >Deleted: sha256: 98 | > >Deleted: sha256: 99 | > > 100 | > >REPOSITORY TAG IMAGE ID CREATED SIZE 101 | > >python 3.7 5c1cd4638fb7 8 days ago 876MB 102 | > >python 3.8 79cc46abd78d 2 weeks ago 882MB 103 | > > ~~~ 104 | > > {: .output} 105 | > {: .solution} 106 | {: .challenge} 107 | 108 | > ## Helpful cleanup commands 109 | > What is helpful is to have Docker detect and remove unwanted images and containers for you. 110 | > This can be done with `prune`, which depending on the context will remove different things. 111 | > - [`docker container prune`](https://docs.docker.com/engine/reference/commandline/container_prune/) removes all stopped containers, which is helpful to clean up forgotten stopped containers. 112 | > - [`docker image prune`](https://docs.docker.com/engine/reference/commandline/image_prune/) removes all unused or dangling images (images that do not have a tag). This is helpful for cleaning up after builds. 113 | > - [`docker system prune`](https://docs.docker.com/engine/reference/commandline/system_prune/) removes all stopped containers, dangling images, and dangling build caches. This is very helpful for cleaning up everything all at once. 114 | {: .callout} 115 | 116 | > ## Worth cleaning up to save disk 117 | > Docker images are just lots of compressed archives, and they can take up lots of disk pretty fast. 118 | > You can monitor the total disk being used by Docker with [`df`][docker-docs-df] 119 | > > ~~~ 120 | > >docker system df 121 | > > ~~~ 122 | > > {: .source} 123 | > > 124 | > > ~~~ 125 | > >TYPE TOTAL ACTIVE SIZE RECLAIMABLE 126 | > >Images n 0 X.YGB X.YGB (100%) 127 | > >Containers 0 0 0B 0B 128 | > >Local Volumes m 0 A.BkB A.BkB (100%) 129 | > >Build Cache 0 0 0B 0B 130 | > > ~~~ 131 | > > {: .output} 132 | > > and for lots of detailed information you can use the `-v` verbose flag 133 | > > 134 | > > ~~~ 135 | > >docker system df -v 136 | > > ~~~ 137 | > > {: .source} 138 | {: .callout} 139 | 140 | [docker-docs-rm]: https://docs.docker.com/engine/reference/commandline/rm/ 141 | [docker-docs-rmi]: https://docs.docker.com/engine/reference/commandline/rmi/ 142 | [docker-docs-df]: https://docs.docker.com/engine/reference/commandline/system_df/ 143 | 144 | {% include links.md %} 145 | -------------------------------------------------------------------------------- /_episodes/07-coffee-break.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Coffee break" 3 | teaching: 0 4 | exercises: 15 5 | questions: 6 | - "Coffee or tea?" 7 | objectives: 8 | - "Refresh your mental faculties with coffee and conversation" 9 | keypoints: 10 | - "Breaks are helpful in the service of learning" 11 | --- 12 | 13 | [![coffee](https://i.giphy.com/media/2jd7CRuYayGpW/giphy.webp)]() 14 | [![nyan-whale](https://raw.githubusercontent.com/matthewfeickert/talk-IML-workshop-2019/master/figures/Docker_nyan_whale.gif)](https://github.com/matthewfeickert/talk-IML-workshop-2019/blob/master/figures/Docker_nyan_whale.gif) 15 | 16 | {% include links.md %} 17 | -------------------------------------------------------------------------------- /_episodes/08-entry.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Using CMD and ENTRYPOINT in Dockerfiles" 3 | teaching: 20 4 | exercises: 10 5 | questions: 6 | - "How are default commands set in Dockerfiles?" 7 | objectives: 8 | - "Learn how and when to use `CMD`" 9 | - "Learn how and when to use `ENTRYPOINT`" 10 | keypoints: 11 | - "`CMD` provide defaults for an executing container" 12 | - "`CMD` can provide options for `ENTRYPOINT`" 13 | - "`ENTRYPOINT` allows you to configure commands that will always run for an executing container" 14 | --- 15 | 16 | So far everytime we've run the Docker containers we've typed 17 | 18 | ~~~ 19 | docker run --rm -it : 20 | ~~~ 21 | {: .source} 22 | 23 | like 24 | 25 | ~~~ 26 | docker run --rm -it python:3.7 /bin/bash 27 | ~~~ 28 | {: .source} 29 | 30 | Running this dumps us into a Bash session 31 | 32 | ~~~ 33 | printenv | grep SHELL 34 | ~~~ 35 | {: .source} 36 | 37 | ~~~ 38 | SHELL=/bin/bash 39 | ~~~ 40 | {: .output} 41 | 42 | However, if no `/bin/bash` is given then you are placed inside the Python 3.7 REPL. 43 | 44 | ~~~ 45 | docker run --rm -it python:3.7 46 | ~~~ 47 | {: .source} 48 | 49 | ~~~ 50 | Python 3.7.4 (default, Jul 13 2019, 14:04:11) 51 | [GCC 8.3.0] on linux 52 | Type "help", "copyright", "credits" or "license" for more information. 53 | >>> 54 | ~~~ 55 | {: .output} 56 | 57 | These are very different behaviors, so let's understand what is happening. 58 | 59 | The Python 3.7 Docker image has a default command that runs when the container is executed, 60 | which is specified in the Dockerfile with [`CMD`][docker-docs-CMD]. 61 | 62 | ~~~ 63 | # Dockerfile.defaults 64 | # Make the base image configurable 65 | ARG BASE_IMAGE=python:3.7 66 | FROM ${BASE_IMAGE} 67 | USER root 68 | RUN apt-get -qq -y update && \ 69 | apt-get -qq -y upgrade && \ 70 | apt-get -y autoclean && \ 71 | apt-get -y autoremove && \ 72 | rm -rf /var/lib/apt/lists/* 73 | # Create user "docker" 74 | RUN useradd -m docker && \ 75 | cp /root/.bashrc /home/docker/ && \ 76 | mkdir /home/docker/data && \ 77 | chown -R --from=root docker /home/docker 78 | ENV HOME /home/docker 79 | WORKDIR ${HOME}/data 80 | USER docker 81 | 82 | CMD ["/bin/bash"] 83 | ~~~ 84 | {: .source} 85 | 86 | ~~~ 87 | docker build -f Dockerfile.defaults -t defaults-example:latest --compress . 88 | ~~~ 89 | {: .source} 90 | 91 | Now running 92 | 93 | ~~~ 94 | docker run --rm -it defaults-example:latest 95 | ~~~ 96 | {: .source} 97 | 98 | again drops you into a Bash shell as specified by `CMD`. 99 | As has already been seen, `CMD` can be overridden by giving a command after the image 100 | 101 | ~~~ 102 | docker run --rm -it defaults-example:latest python3 103 | ~~~ 104 | {: .source} 105 | 106 | The [`ENTRYPOINT`][docker-docs-ENTRYPOINT] builder command allows to define a command or 107 | commands that are **always** run at the "entry" to the Docker container. 108 | If an `ENTRYPOINT` has been defined then `CMD` provides optional inputs to the `ENTRYPOINT`. 109 | 110 | ~~~ 111 | # entrypoint.sh 112 | #!/usr/bin/env bash 113 | 114 | set -e 115 | 116 | function main() { 117 | if [[ $# -eq 0 ]]; then 118 | printf "\nHello, World!\n" 119 | else 120 | printf "\nHello, %s!\n" "${1}" 121 | fi 122 | } 123 | 124 | main "$@" 125 | 126 | /bin/bash 127 | ~~~ 128 | {: .language-bash} 129 | 130 | ~~~ 131 | # Dockerfile.defaults 132 | # Make the base image configurable 133 | ARG BASE_IMAGE=python:3.7 134 | FROM ${BASE_IMAGE} 135 | USER root 136 | RUN apt-get -qq -y update && \ 137 | apt-get -qq -y upgrade && \ 138 | apt-get -y autoclean && \ 139 | apt-get -y autoremove && \ 140 | rm -rf /var/lib/apt/lists/* 141 | # Create user "docker" 142 | RUN useradd -m docker && \ 143 | cp /root/.bashrc /home/docker/ && \ 144 | mkdir /home/docker/data && \ 145 | chown -R --from=root docker /home/docker 146 | ENV HOME /home/docker 147 | WORKDIR ${HOME}/data 148 | USER docker 149 | 150 | COPY entrypoint.sh $HOME/entrypoint.sh 151 | ENTRYPOINT ["/bin/bash", "/home/docker/entrypoint.sh"] 152 | CMD ["Docker"] 153 | ~~~ 154 | {: .source} 155 | 156 | ~~~ 157 | docker build -f Dockerfile.defaults -t defaults-example:latest --compress . 158 | ~~~ 159 | {: .source} 160 | 161 | So now 162 | 163 | ~~~ 164 | docker run --rm -it defaults-example:latest 165 | ~~~ 166 | {: .source} 167 | 168 | ~~~ 169 | 170 | Hello, Docker! 171 | docker@2a99ffabb512:~/data$ 172 | ~~~ 173 | {: .output} 174 | 175 | > ## Applied `ENTRYPOINT` and `CMD` 176 | > 177 | > What will be the output of 178 | >~~~ 179 | >docker run --rm -it defaults-example:latest $USER 180 | >~~~ 181 | >{: .source} 182 | > and why? 183 | > 184 | > > ## Solution 185 | > > 186 | > >~~~ 187 | > > 188 | > >Hello, ! 189 | > >docker@2a99ffabb512:~/data$ 190 | > >~~~ 191 | > >{: .output} 192 | > `$USER` is evaluated and then overrides the default `CMD` to be passed to `entrypoint.sh` 193 | > {: .solution} 194 | {: .challenge} 195 | 196 | [docker-docs-CMD]: https://docs.docker.com/engine/reference/builder/#cmd 197 | [docker-docs-ENTRYPOINT]: https://docs.docker.com/engine/reference/builder/#entrypoint 198 | 199 | {% include links.md %} 200 | -------------------------------------------------------------------------------- /_episodes/10-challenge-examples.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Challenge Examples" 3 | teaching: 0 4 | exercises: 20 5 | questions: 6 | - "How to do a few more things?" 7 | objectives: 8 | - "How to run with SSH and not use `cp`" 9 | keypoints: 10 | - "Containers are extensive" 11 | --- 12 | 13 | > ## Get SSH credentials in a container without `cp` 14 | > 15 | > Get SSH credentials inside a container without using `cp` 16 | > 17 | > > ## Solution 18 | > > 19 | > > Mount multiple volumes 20 | > >~~~ 21 | > >docker run --rm -it \ 22 | > > -w /home/atlas/Bootcamp \ 23 | > > -v $PWD:/home/atlas/Bootcamp \ 24 | > > -v $HOME/.ssh:/home/atlas/.ssh \ 25 | > > -v $HOME/.gitconfig:/home/atlas/.gitconfig \ 26 | > > atlas/analysisbase:21.2.85-centos7 27 | > >~~~ 28 | > >{: .source} 29 | > {: .solution} 30 | {: .challenge} 31 | 32 | {% include links.md %} 33 | -------------------------------------------------------------------------------- /_episodes_rmd/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/_episodes_rmd/.gitkeep -------------------------------------------------------------------------------- /_episodes_rmd/data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/_episodes_rmd/data/.gitkeep -------------------------------------------------------------------------------- /_extras/discuss.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Discussion 3 | --- 4 | 5 | Readers may be interested in: 6 | 7 | * The [official Docker docs](https://docs.docker.com/). 8 | * Pretty good YouTube videos on the basic concepts of containers: [Docker in 100 Seconds](https://youtu.be/Gjnup-PuquQ) and [Learn Docker in 7 Easy Steps - Full Beginner's Tutorial](https://youtu.be/gAkwW2tuIqE) by Fireship. 9 | 10 | {% include links.md %} 11 | -------------------------------------------------------------------------------- /_extras/guide.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Instructor Notes" 3 | --- 4 | 5 | The instructor notes should provide additional discussion useful to instructors, 6 | but not appropriate for inclusion in the main lessons. The following structure 7 | provides a consistent way for instructors to both prepare for a workshop and 8 | quickly find necessary information during a workshop. 9 | 10 | Please remember not to overload on details, and to keep the comments here positive! 11 | 12 | ## Lesson motivation and learning objectives 13 | 14 | These concepts should be highlighted in the main lesson material, but ideas for 15 | explaining these concepts further can be placed here. 16 | 17 | ## Lesson design 18 | 19 | Most lessons contain more material than can be taught in a single workshop. 20 | Describe a general narrative (with time estimates) for teaching either a half day 21 | or full day with this lesson material. You may also choose to include multiple 22 | options for lesson design, or what material can be skipped while teaching. 23 | This section may also include recommendations for how this lesson fits into 24 | the overall workshop. 25 | 26 | ## Technical tips and tricks 27 | 28 | Provide information on setting up your environment for learners to view your 29 | live coding (increasing text size, changing text color, etc), as well as 30 | general recommendations for working with coding tools to best suit the 31 | learning environment. 32 | 33 | ## Common problems 34 | 35 | This can include answers to common learner questions, as well as links to 36 | resources (blog posts, stack overflow answers, etc) that may solve problems that 37 | may occur during a workshop. 38 | -------------------------------------------------------------------------------- /_includes/aio-script.md: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | As a maintainer, you don't need to edit this file. 3 | If you notice that something doesn't work, please 4 | open an issue: https://github.com/carpentries/styles/issues/new 5 | {% endcomment %} 6 | 7 | {% include manual_episode_order.html %} 8 | 9 | {% for lesson_episode in lesson_episodes %} 10 | 11 | {% if site.episode_order %} 12 | {% assign e = site.episodes | where: "slug", lesson_episode | first %} 13 | {% else %} 14 | {% assign e = lesson_episode %} 15 | {% endif %} 16 | 17 |

{{ e.title }}

18 | 19 | {% include episode_overview.html teaching_time=e.teaching exercise_time=e.exercises episode_questions=e.questions episode_objectives=e.objectives %} 20 | 21 | {{ e.content }} 22 | 23 | {% include episode_keypoints.html episode_keypoints=e.keypoints %} 24 |
25 | {% endfor %} 26 | -------------------------------------------------------------------------------- /_includes/all_keypoints.html: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Display key points of all episodes for reference. 3 | {% endcomment %} 4 | 5 | {% include base_path.html %} 6 | {% include manual_episode_order.html %} 7 | 8 |

Key Points

9 | 10 | {% for lesson_episode in lesson_episodes %} 11 | {% if site.episode_order %} 12 | {% assign episode = site.episodes | where: "slug", lesson_episode | first %} 13 | {% else %} 14 | {% assign episode = lesson_episode %} 15 | {% endif %} 16 | {% unless episode.break %} 17 | 18 | 21 | 28 | 29 | {% endunless %} 30 | {% endfor %} 31 |
19 | {{ episode.title }} 20 | 22 |
    23 | {% for keypoint in episode.keypoints %} 24 |
  • {{ keypoint|markdownify }}
  • 25 | {% endfor %} 26 |
27 |
32 | -------------------------------------------------------------------------------- /_includes/base_path.html: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | This is adapted from: https://ricostacruz.com/til/relative-paths-in-jekyll 3 | 4 | `page.url` gives the URL of the current page with a leading /: 5 | 6 | - when the URL ends with the extension (e.g., /foo/bar.html) then we can get 7 | the depth by counting the number of / and remove - 1 8 | - when the URL ends with a / (e.g. /foo/bar/) then the number / gives the depth 9 | directly 10 | {% endcomment %} 11 | 12 | {% assign relative_root_path = '' %} 13 | 14 | {% assign last_char = page.url | slice: -1 %} 15 | 16 | {% if last_char == "/"} 17 | {% assign offset = 0 %} 18 | {% else %} 19 | {% assign offset = 1 %} 20 | {% endif %} 21 | 22 | {% assign depth = page.url | split: '/' | size | minus: offset %} 23 | {% if depth <= 1 %}{% assign relative_root_path = '.' %} 24 | {% elsif depth == 2 %}{% assign relative_root_path = '..' %} 25 | {% elsif depth == 3 %}{% assign relative_root_path = '../..' %} 26 | {% elsif depth == 4 %}{% assign relative_root_path = '../../..' %} 27 | {% endif %} 28 | -------------------------------------------------------------------------------- /_includes/carpentries.html: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | General description of Software, Data, and Library Carpentry. 3 | {% endcomment %} 4 | 5 | {% include base_path.html %} 6 | 7 |
8 |
9 | The Carpentries logo 10 |
11 |
12 |

The Carpentries comprises 13 | Software Carpentry, Data Carpentry, and Library Carpentry communities of Instructors, Trainers, 14 | Maintainers, helpers, and supporters who share a mission to teach 15 | foundational coding and data science skills to researchers and people 16 | working in library- and information-related roles. In January, 17 | 2018, The Carpentries was formed by the merger of Software Carpentry and 18 | Data Carpentry. Library Carpentry became an official Carpentries Lesson Program in November 2018.

19 | 20 |

While individual lessons and workshops continue to be run under each 21 | lesson project, The Carpentries provide overall staffing and governance, as 22 | well as support for assessment, instructor training and mentoring. 23 | Memberships are joint, and the Carpentries project maintains a shared Code 24 | of Conduct. The Carpentries is a fiscally sponsored project of Community 25 | Initiatives, a registered 501(c)3 non-profit based in California, USA.

26 |
27 |
28 |
29 |
30 | Software Carpentry logo 31 |
32 |
33 |

Since 1998, Software Carpentry has 34 | been teaching researchers across all disciplines the foundational coding 35 | skills they need to get more done in less time and with less pain. Its 36 | volunteer instructors have run hundreds of events for thousands of learners 37 | around the world. Now that all research involves some degree of 38 | computational work, whether with big data, cloud computing, or simple task 39 | automation, these skills are needed more than ever.

40 |
41 |
42 |
43 |
44 |
45 | Data Carpentry logo 46 |
47 |
48 |

Data Carpentry develops and teaches 49 | workshops on the fundamental data skills needed to conduct research. Its 50 | target audience is researchers who have little to no prior computational 51 | experience, and its lessons are domain specific, building on learners' 52 | existing knowledge to enable them to quickly apply skills learned to their 53 | own research. Data Carpentry workshops take researchers through the entire 54 | data life cycle.

55 |
56 |
57 |
58 |
59 |
60 | Library Carpentry logo 61 |
62 |
63 |

Library Carpentry develops lessons and 64 | teaches workshops for and with people working in library- and 65 | information-related roles. Its goal is to create an on-ramp to empower this 66 | community to use software and data in their own work, as well as be 67 | advocates for and train others in efficient, effective and reproducible data 68 | and software practices.

69 |
70 |
71 | -------------------------------------------------------------------------------- /_includes/dc/intro.html: -------------------------------------------------------------------------------- 1 |

2 | Data Carpentry 3 | aims to help researchers get their work done 4 | in less time and with less pain 5 | by teaching them basic research computing skills. 6 | This hands-on workshop will cover basic concepts and tools, 7 | including program design, version control, data management, 8 | and task automation. 9 | Participants will be encouraged to help one another 10 | and to apply what they have learned to their own research problems. 11 |

12 |

13 | 14 | For more information on what we teach and why, 15 | please see our paper 16 | "Best Practices for Scientific Computing". 17 | 18 |

19 | -------------------------------------------------------------------------------- /_includes/dc/schedule.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Day 1

4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
09:00 Automating tasks with the Unix shell
10:30 Coffee
12:00 Lunch break
13:00 Building programs with Python
14:30 Coffee
16:00 Wrap-up
12 |
13 |
14 |

Day 2

15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
09:00 Version control with Git
10:30 Coffee
12:00 Lunch break
13:00 Managing data with SQL
14:30 Coffee
16:00 Wrap-up
23 |
24 |
25 | -------------------------------------------------------------------------------- /_includes/dc/syllabus.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

The Unix Shell

4 |
    5 |
  • Files and directories
  • 6 |
  • History and tab completion
  • 7 |
  • Pipes and redirection
  • 8 |
  • Looping over files
  • 9 |
  • Creating and running shell scripts
  • 10 |
  • Finding things
  • 11 |
  • Reference...
  • 12 |
13 |
14 |
15 |

Programming in Python

16 |
    17 |
  • Using libraries
  • 18 |
  • Working with arrays
  • 19 |
  • Reading and plotting data
  • 20 |
  • Creating and using functions
  • 21 |
  • Loops and conditionals
  • 22 |
  • Defensive programming
  • 23 |
  • Using Python from the command line
  • 24 |
  • Reference...
  • 25 |
26 |
27 | 40 | 53 |
54 | 55 |
56 |
57 |

Version Control with Git

58 |
    59 |
  • Creating a repository
  • 60 |
  • Recording changes to files: add, commit, ...
  • 61 |
  • Viewing changes: status, diff, ...
  • 62 |
  • Ignoring files
  • 63 |
  • Working on the web: clone, pull, push, ...
  • 64 |
  • Resolving conflicts
  • 65 |
  • Open licenses
  • 66 |
  • Where to host work, and why
  • 67 |
  • Reference...
  • 68 |
69 |
70 | 86 |
87 |

Open Refine

88 |
    89 |
  • Introduction to OpenRefine
  • 90 |
  • Importing data
  • 91 |
  • Basic functions
  • 92 |
  • Advanced Functions
  • 93 |
  • Reference...
  • 94 |
95 |
96 |
97 | -------------------------------------------------------------------------------- /_includes/dc/who.html: -------------------------------------------------------------------------------- 1 |

2 | Who: 3 | The course is aimed at graduate students and other researchers. 4 | 5 | You don't need to have any previous knowledge of the tools 6 | that will be presented at the workshop. 7 | 8 |

9 | -------------------------------------------------------------------------------- /_includes/episode_break.html: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Display a break's timings in a box similar to a learning episode's. 3 | {% endcomment %} 4 |
5 |

Overview

6 | 7 |
8 |
9 | Break: {{ page.break }} min 10 |
11 |
12 |
13 |
14 | 15 |
16 | -------------------------------------------------------------------------------- /_includes/episode_keypoints.html: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Display key points for an episode. 3 | {% endcomment %} 4 | 5 | {% if page.keypoints == nil %} 6 | {% assign episode_keypoints = include.episode_keypoints %} 7 | {% else %} 8 | {% assign episode_keypoints = page.keypoints %} 9 | {% endif %} 10 | 11 |
12 |

Key Points

13 |
    14 | {% for keypoint in episode_keypoints %} 15 |
  • {{ keypoint|markdownify }}
  • 16 | {% endfor %} 17 |
18 |
19 | -------------------------------------------------------------------------------- /_includes/episode_navbar.html: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | For some reason, the relative_root_path seems out of scope in this file, so we 3 | need to re-assign it here 4 | {% endcomment %} 5 | 6 | {% include base_path.html %} 7 | 8 | {% comment %} 9 | Navigation bar for an episode. 10 | {% endcomment %} 11 | 12 | {% include manual_episode_order.html %} 13 | {% comment %} 14 | 'previous_episode' and 'next_episodes' are defined in 'manual_episode_order.html'. 15 | These replace 'page.previous' and 'page.next' objects, correspondingly. 16 | {% endcomment %} 17 | 18 |
19 |
20 |

21 | {% if previous_episode %} 22 | previous episode 23 | {% else %} 24 | lesson home 25 | {% endif %} 26 |

27 |
28 |
29 | {% if include.episode_navbar_title %} 30 |

{{ site.title }}

31 | {% endif %} 32 |
33 |
34 |

35 | {% if next_episode %} 36 | next episode 37 | {% else %} 38 | lesson home 39 | {% endif %} 40 |

41 |
42 |
43 | -------------------------------------------------------------------------------- /_includes/episode_overview.html: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Display episode's timings and learning objectives. 3 | 4 | Regarding the `if page.*** == nil` below: 5 | all-in-one page combines all episodes into one. 6 | It, therefore, does not define its own objectives, exercises, 7 | and questions, which 'normal' episodes define in the front matter. 8 | 9 | To display episodes' teaching and exercise times, as well as episode 10 | questions and objectives, we pass them as parameters to the Liquid's 11 | `include` statement when we generate the page: 12 | 13 | include episode_overview.html teaching_time=e.teaching ... 14 | 15 | Here we obtain the information we need either from the episode itself or 16 | from the parameters passed in. 17 | {% endcomment %} 18 | 19 | {% if page.teaching == nil %} 20 | {% assign teaching_time = include.teaching_time %} 21 | {% else %} 22 | {% assign teaching_time = page.teaching %} 23 | {% endif %} 24 | 25 | {% if page.exercises == nil %} 26 | {% assign exercise_time = include.exercise_time %} 27 | {% else %} 28 | {% assign exercise_time = page.exercises %} 29 | {% endif %} 30 | 31 | {% if page.questions == nil %} 32 | {% assign episode_questions = include.episode_questions %} 33 | {% else %} 34 | {% assign episode_questions = page.questions %} 35 | {% endif %} 36 | 37 | {% if page.objectives == nil %} 38 | {% assign episode_objectives = include.episode_objectives %} 39 | {% else %} 40 | {% assign episode_objectives = page.objectives %} 41 | {% endif %} 42 | 43 | 44 |
45 |

Overview

46 | 47 |
48 |
49 | Teaching: {{ teaching_time }} min 50 |
51 | Exercises: {{ exercise_time }} min 52 |
53 |
54 | Questions 55 |
    56 | {% for question in episode_questions %} 57 |
  • {{ question|markdownify }}
  • 58 | {% endfor %} 59 |
60 |
61 |
62 | 63 |
64 |
65 |
66 |
67 | Objectives 68 |
    69 | {% for objective in episode_objectives %} 70 |
  • {{ objective|markdownify }}
  • 71 | {% endfor %} 72 |
73 |
74 |
75 | 76 |
77 | -------------------------------------------------------------------------------- /_includes/episode_title.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

{{ page.title }}

6 |
7 |
8 |
9 |
10 | -------------------------------------------------------------------------------- /_includes/favicons.html: -------------------------------------------------------------------------------- 1 | {% assign favicon_url = relative_root_path | append: '/assets/favicons/' | append: site.carpentry %} 2 | 3 | {% if site.carpentry == 'swc' %} 4 | {% assign carpentry = 'Software Carpentry' %} 5 | {% elsif site.carpentry == 'dc' %} 6 | {% assign carpentry = 'Data Carpentry' %} 7 | {% elsif site.carpentry == 'lc' %} 8 | {% assign carpentry = 'Library Carpentry' %} 9 | {% elsif site.carpentry == 'cp' %} 10 | {% assign carpentry = 'The Carpentries' %} 11 | {% endif %} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /_includes/gh_variables.html: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | When rendering websites locally, `site.github.url` doesn't get resolved properly 3 | unless a GitHub Personal Access Token is set up and available in the 4 | environment. This leads to warnings and errors when trying to serve the site 5 | locally. To work around this, we use the `jekyll.environment` variable which is 6 | set to `development` when rendering the site locally, and set to `production` on 7 | GitHub where `site.github.url` is defined. 8 | {% endcomment %} 9 | 10 | {% if jekyll.environment == "production" %} 11 | 12 | {% comment %} 13 | First, get the name of the repository 14 | {% endcomment %} 15 | {% assign repo_name = site.github.repository_name %} 16 | 17 | {% comment %} 18 | `site.github.public_repositories` contains comprehensive information for all public repositories for the organization. We use `where` to extract the part 19 | of the metadata that is relevant to the present repository. 20 | {% endcomment %} 21 | {% assign repo_info = site.github.public_repositories | where: "name", repo_name %} 22 | 23 | {% comment %} 24 | Now, we can extract the default branch for the repo 25 | {% endcomment %} 26 | {% assign default_branch = repo_info[0].default_branch %} 27 | 28 | {% comment %} 29 | Other variables requested by the template 30 | {% endcomment %} 31 | {% assign repo_url = site.github.repository_url %} 32 | {% assign search_domain_url = site.github.url %} 33 | {% assign project_title = site.github.project_title %} 34 | {% assign source_branch = site.github.source.branch %} 35 | 36 | {% elsif jekyll.environment == "development" %} 37 | 38 | {% assign repo_name = "" %} 39 | {% assign repo_url = "" %} 40 | {% assign default_branch = "" %} 41 | {% assign search_domain_url = "" %} 42 | {% assign project_title = "" %} 43 | {% assign source_branch = "" %} 44 | 45 | {% endif %} 46 | -------------------------------------------------------------------------------- /_includes/javascript.html: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | JavaScript used in lesson and workshop pages. 3 | {% endcomment %} 4 | 5 | 6 | 7 | 15 | -------------------------------------------------------------------------------- /_includes/lc/intro.html: -------------------------------------------------------------------------------- 1 |

2 | Library Carpentry 3 | is made by people working in library- and information-related roles to help you: 4 |

5 |
    6 |
  • automate repetitive, boring, error-prone tasks
  • 7 |
  • create, maintain and analyze sustainable and reusable data
  • 8 |
  • work effectively with IT and systems colleagues
  • 9 |
  • better understand the use of software in research
  • 10 |
  • and much more...
  • 11 |
12 |

13 | 14 | Library Carpentry introduces you to the fundamentals of computing 15 | and provides you with a platform for further self-directed learning. 16 | For more information on what we teach and why, please see our paper 17 | "Library Carpentry: software skills training for library professionals". 18 | 19 |

20 | -------------------------------------------------------------------------------- /_includes/lc/schedule.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Day 1

4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
09:00 Data Intro for Librarians
10:30 Coffee
12:00 Lunch break
13:00 Shell Lessons for Libraries
14:30 Coffee
16:00 Wrap-up
12 |
13 |
14 |

Day 2

15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
09:00 Git Intro for Librarians
10:30 Coffee
12:00 Lunch break
13:00 OpenRefine for Librarians
14:30 Coffee
16:00 Wrap-up
23 |
24 |
25 | -------------------------------------------------------------------------------- /_includes/lc/syllabus.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Data Intro

4 |
    5 |
  • Intro to data
  • 6 |
  • Jargon busting
  • 7 |
  • Keyboard shortcuts
  • 8 |
  • Plain text formats
  • 9 |
  • Naming files
  • 10 |
  • Regular expressions
  • 11 |
  • Reference...
  • 12 |
13 |
14 |
15 |

The Unix Shell

16 |
    17 |
  • Files and directories
  • 18 |
  • History and tab completion
  • 19 |
  • Counting and sorting contents in files
  • 20 |
  • Pipes and redirection
  • 21 |
  • Mining or searching in files
  • 22 |
  • Reference...
  • 23 |
24 |
25 | 26 |
27 | 28 |
29 |
30 |

Version Control with Git

31 |
    32 |
  • Creating a repository
  • 33 |
  • Configuring git
  • 34 |
  • Recording changes to files: add, commit, ...
  • 35 |
  • Viewing state changes with status
  • 36 |
  • Working on the web: clone, pull, push, ...
  • 37 |
  • Where to host work, and why
  • 38 |
  • Reference...
  • 39 |
40 |
41 |
42 |
43 |

Open Refine

44 |
    45 |
  • Introduction to OpenRefine
  • 46 |
  • Importing data
  • 47 |
  • Basic functions
  • 48 |
  • Advanced Functions
  • 49 |
  • Reference...
  • 50 |
51 |
52 |
53 |
54 | 70 | -------------------------------------------------------------------------------- /_includes/lc/who.html: -------------------------------------------------------------------------------- 1 |

2 | Who: 3 | The course is for people working in library- and information-related roles. 4 | 5 | You don't need to have any previous knowledge of the tools that 6 | will be presented at the workshop. 7 | 8 |

9 | -------------------------------------------------------------------------------- /_includes/lesson_footer.html: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Footer for lesson pages. 3 | {% endcomment %} 4 | 5 | {% include gh_variables.html %} 6 | 7 | 55 | -------------------------------------------------------------------------------- /_includes/life_cycle.html: -------------------------------------------------------------------------------- 1 | 2 | {% if site.life_cycle == "pre-alpha" %} 3 | 4 |
5 |
6 | This lesson is still being designed and assembled (Pre-Alpha version) 7 |
8 |
9 | 10 | 11 | {% elsif site.life_cycle == "alpha" %} 12 | 13 |
14 |
15 | This lesson is in the early stages of development (Alpha version) 16 |
17 |
18 | 19 | 20 | {% elsif site.life_cycle == "beta" %} 21 | 22 |
23 |
24 | This lesson is being piloted (Beta version) 25 |
26 |
27 | 28 | {% elsif site.life_cycle == "stable" %} 29 | 30 | {% comment %} 31 | We don't do anything special for now 32 | {% endcomment %} 33 | 34 | {% endif %} 35 | -------------------------------------------------------------------------------- /_includes/links.md: -------------------------------------------------------------------------------- 1 | {% include base_path.html %} 2 | [cc-by-human]: https://creativecommons.org/licenses/by/4.0/ 3 | [cc-by-legal]: https://creativecommons.org/licenses/by/4.0/legalcode 4 | [ci]: http://communityin.org/ 5 | [coc-reporting]: https://docs.carpentries.org/topic_folders/policies/incident-reporting.html 6 | [coc]: https://docs.carpentries.org/topic_folders/policies/code-of-conduct.html 7 | [concept-maps]: https://carpentries.github.io/instructor-training/05-memory/ 8 | [contrib-covenant]: https://contributor-covenant.org/ 9 | [contributing]: {{ repo_url }}/blob/{{ source_branch }}/CONTRIBUTING.md 10 | [cran-checkpoint]: https://cran.r-project.org/package=checkpoint 11 | [cran-knitr]: https://cran.r-project.org/package=knitr 12 | [cran-stringr]: https://cran.r-project.org/package=stringr 13 | [dc-lessons]: http://www.datacarpentry.org/lessons/ 14 | [email]: mailto:team@carpentries.org 15 | [github-importer]: https://import.github.com/ 16 | [importer]: https://github.com/new/import 17 | [jekyll-collection]: https://jekyllrb.com/docs/collections/ 18 | [jekyll-install]: https://jekyllrb.com/docs/installation/ 19 | [jekyll-windows]: http://jekyll-windows.juthilo.com/ 20 | [jekyll]: https://jekyllrb.com/ 21 | [jupyter]: https://jupyter.org/ 22 | [kramdown]: https://kramdown.gettalong.org/ 23 | [lc-lessons]: https://librarycarpentry.org/lessons/ 24 | [lesson-aio]: {{ relative_root_path }}{% link aio.md %} 25 | [lesson-coc]: {{ relative_root_path }}{% link CODE_OF_CONDUCT.md %} 26 | [lesson-example]: https://carpentries.github.io/lesson-example/ 27 | [lesson-license]: {{ relative_root_path }}{% link LICENSE.md %} 28 | [lesson-mainpage]: {{ relative_root_path }}{% link index.md %} 29 | [lesson-reference]: {{ relative_root_path }}{% link reference.md %} 30 | [lesson-setup]: {{ relative_root_path }}{% link setup.md %} 31 | [mit-license]: https://opensource.org/licenses/mit-license.html 32 | [morea]: https://morea-framework.github.io/ 33 | [numfocus]: https://numfocus.org/ 34 | [osi]: https://opensource.org 35 | [pandoc]: https://pandoc.org/ 36 | [paper-now]: https://github.com/PeerJ/paper-now 37 | [python-gapminder]: https://swcarpentry.github.io/python-novice-gapminder/ 38 | [pyyaml]: https://pypi.org/project/PyYAML/ 39 | [r-markdown]: https://rmarkdown.rstudio.com/ 40 | [rstudio]: https://www.rstudio.com/ 41 | [ruby-install-guide]: https://www.ruby-lang.org/en/downloads/ 42 | [ruby-installer]: https://rubyinstaller.org/ 43 | [rubygems]: https://rubygems.org/pages/download/ 44 | [styles]: https://github.com/carpentries/styles/ 45 | [swc-lessons]: https://software-carpentry.org/lessons/ 46 | [swc-releases]: https://github.com/swcarpentry/swc-releases 47 | [training]: https://carpentries.github.io/instructor-training/ 48 | [workshop-repo]: {{ site.workshop_repo }} 49 | [yaml]: http://yaml.org/ 50 | -------------------------------------------------------------------------------- /_includes/main_title.html: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Main title for lesson pages. 3 | {% endcomment %} 4 | 5 | {% include base_path.html %} 6 | 7 | {% if site.kind == "lesson" %} 8 |

{{ site.title }}{% if page.title %}: {{ page.title }}{% endif %}

9 | 10 | {% else %} 11 | 12 |

{{ page.title }}

13 | 14 | {% endif %} 15 | -------------------------------------------------------------------------------- /_includes/manual_episode_order.html: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | This file enables manual episode ordering until 3 | GitHub Pages switches to Jekyll that supports it 4 | without any major hackery. Note, some logic will 5 | be required even when this transition happens 6 | but it won't be as involved as what we have to do 7 | in this file. 8 | 9 | To order lesson episodes or extras manually 10 | (instead of the default alpha-numerical order), 11 | create array variables 'episode_order' and 12 | 'extras_order' in `_config.yml` like so: 13 | 14 | episode_order: 15 | - episodeA 16 | - episodeB 17 | 18 | extras_order: 19 | - extraA 20 | - extraB 21 | 22 | Note that "Reference" page is currently always 23 | added to "Extras" as the first item. 24 | 25 | The main outcomes of the code in this file are: 26 | - 'lesson_episodes' variable that replaces 27 | 'site.episodes' variable when manual episode 28 | order is defined. 29 | - 'lesson_extras' variable that replaces 30 | 'site.extras' variable when manual ordering of 31 | files in '_extras' is used 32 | - 'previous_episode' and 'next_episode' objects 33 | that replace 'page.previous' and 'page.next' variables, 34 | correspondingly, and that have such properties 35 | as 'url' and 'title' and that are used in 36 | 'episode_navbar.html'. 37 | 38 | When episode order is specified manually, the 'lesson_episodes' 39 | variable contains a list of episode names ("slugs", to be precise; 40 | "slug" is the episode name without '.md'). Therefore, when we 41 | iterate over 'lesson_episodes' (in syllabus.html and navbar.html) , 42 | we have to check whether we use manual episode ordering and, if so, 43 | find the corresponding episode object. This is what we do with the 44 | following code in every loop over 'lesson_episodes': 45 | 46 | {% if site.episode_order %} 47 | {% assign episode = site.episodes | where: "slug", lesson_episode | first %} 48 | {% else %} 49 | {% assign episode = lesson_episode %} 50 | {% endif %} 51 | {% endcomment %} 52 | 53 | {% comment %} 54 | Manual ordering of Episodes begins here 55 | {% endcomment %} 56 | 57 | {% if site.episode_order %} 58 | {% assign lesson_episodes = site.episode_order %} 59 | {% else %} 60 | {% assign lesson_episodes = site.episodes %} 61 | {% endif %} 62 | 63 | 64 | {% comment %} 65 | If 'episode_order' is defined, we need to determine 66 | - previous episode object ('previous_episode') 67 | - and next episode object ('next_episode') 68 | {% endcomment %} 69 | 70 | 71 | {% if site.episode_order %} 72 | {% for lesson_episode in lesson_episodes %} 73 | 74 | {% comment %} 75 | We iterate over the specified lesson episodes using 76 | a 'for' loop because we can use 77 | 'forloop.first', 'forloop.last', and 'forloop.index0'. 78 | {% endcomment %} 79 | 80 | {% unless lesson_episode == page.slug %} {% continue %} {% endunless %} 81 | 82 | {% if forloop.first %} 83 | {% assign previous_episode = nil %} 84 | {% else %} 85 | {% assign p_idx = forloop.index0 | minus: 1 %} 86 | {% assign p_name = lesson_episodes[p_idx] %} 87 | {% assign previous_episode = site.episodes | where: "slug", p_name | first %} 88 | {% endif %} 89 | 90 | {% if forloop.last == true %} 91 | {% assign next_episode = nil %} 92 | {% else %} 93 | {% assign n_idx = forloop.index0 | plus: 1 %} 94 | {% assign n_name = lesson_episodes[n_idx] %} 95 | {% assign next_episode = site.episodes | where: "slug", n_name | first %} 96 | {% endif %} 97 | {% endfor %} 98 | {% else %} 99 | {% assign previous_episode = page.previous %} 100 | {% assign next_episode = page.next %} 101 | {% endif %} 102 | 103 | 104 | {% comment %} 105 | Manual ordering of Extras begins here 106 | {% endcomment %} 107 | 108 | {% if site.extras_order %} 109 | {% assign lesson_extras = site.extras_order %} 110 | {% else %} 111 | {% assign lesson_extras = site.extras %} 112 | {% endif %} 113 | 114 | {% comment %} 115 | We do not need to determine "previous" or "next" extra. 116 | {% endcomment %} 117 | -------------------------------------------------------------------------------- /_includes/navbar.html: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Lesson navigation bar. 3 | {% endcomment %} 4 | 5 | {% include gh_variables.html %} 6 | {% include manual_episode_order.html %} 7 | 8 | 107 | -------------------------------------------------------------------------------- /_includes/sc/intro.html: -------------------------------------------------------------------------------- 1 |

2 | Software Carpentry 3 | aims to help researchers get their work done 4 | in less time and with less pain 5 | by teaching them basic research computing skills. 6 | This hands-on workshop will cover basic concepts and tools, 7 | including program design, version control, data management, 8 | and task automation. 9 | Participants will be encouraged to help one another 10 | and to apply what they have learned to their own research problems. 11 |

12 |

13 | 14 | For more information on what we teach and why, 15 | please see our paper 16 | "Best Practices for Scientific Computing". 17 | 18 |

19 | -------------------------------------------------------------------------------- /_includes/sc/schedule.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Day 1

4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
09:00 Automating tasks with the Unix shell
10:30 Coffee
12:00 Lunch break
13:00 Building programs with Python
14:30 Coffee
16:00 Wrap-up
12 |
13 |
14 |

Day 2

15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
09:00 Version control with Git
10:30 Coffee
12:00 Lunch break
13:00 Managing data with SQL
14:30 Coffee
16:00 Wrap-up
23 |
24 |
25 | -------------------------------------------------------------------------------- /_includes/sc/syllabus.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

The Unix Shell

4 |
    5 |
  • Files and directories
  • 6 |
  • History and tab completion
  • 7 |
  • Pipes and redirection
  • 8 |
  • Looping over files
  • 9 |
  • Creating and running shell scripts
  • 10 |
  • Finding things
  • 11 |
  • Reference...
  • 12 |
13 |
14 |
15 |

Programming in Python

16 |
    17 |
  • Using libraries
  • 18 |
  • Working with arrays
  • 19 |
  • Reading and plotting data
  • 20 |
  • Creating and using functions
  • 21 |
  • Loops and conditionals
  • 22 |
  • Defensive programming
  • 23 |
  • Using Python from the command line
  • 24 |
  • Reference...
  • 25 |
26 |
27 | 40 | 53 |
54 | 55 |
56 |
57 |

Version Control with Git

58 |
    59 |
  • Creating a repository
  • 60 |
  • Recording changes to files: add, commit, ...
  • 61 |
  • Viewing changes: status, diff, ...
  • 62 |
  • Ignoring files
  • 63 |
  • Working on the web: clone, pull, push, ...
  • 64 |
  • Resolving conflicts
  • 65 |
  • Open licenses
  • 66 |
  • Where to host work, and why
  • 67 |
  • Reference...
  • 68 |
69 |
70 | 86 |
87 |

Open Refine

88 |
    89 |
  • Introduction to OpenRefine
  • 90 |
  • Importing data
  • 91 |
  • Basic functions
  • 92 |
  • Advanced Functions
  • 93 |
  • Reference...
  • 94 |
95 |
96 |
97 | -------------------------------------------------------------------------------- /_includes/sc/who.html: -------------------------------------------------------------------------------- 1 |

2 | Who: 3 | The course is aimed at graduate students and other researchers. 4 | 5 | You don't need to have any previous knowledge of the tools 6 | that will be presented at the workshop. 7 | 8 |

9 | -------------------------------------------------------------------------------- /_includes/syllabus.html: -------------------------------------------------------------------------------- 1 | {% include base_path.html %} 2 | 3 | {% comment %} 4 | Display syllabus in tabular form. 5 | Days are displayed if at least one episode has 'start = true'. 6 | {% endcomment %} 7 | 8 | {% include manual_episode_order.html %} 9 | 10 |
11 |

Schedule

12 | 13 | {% assign lesson_number = 0 %} 14 | {% assign day = 0 %} 15 | {% assign multiday = false %} 16 | {% for lesson_episode in lesson_episodes %} 17 | {% if site.episode_order %} 18 | {% assign episode = site.episodes | where: "slug", lesson_episode | first %} 19 | {% else %} 20 | {% assign episode = lesson_episode %} 21 | {% endif %} 22 | {% if episode.start %}{% assign multiday = true %}{% break %}{% endif %} 23 | {% endfor %} 24 | {% assign current = site.start_time %} 25 | 26 | 27 | 28 | {% if multiday %}{% endif %} 29 | 30 | 31 | 32 | 33 | {% for lesson_episode in lesson_episodes %} 34 | {% if site.episode_order %} 35 | {% assign episode = site.episodes | where: "slug", lesson_episode | first %} 36 | {% else %} 37 | {% assign episode = lesson_episode %} 38 | {% endif %} 39 | {% if episode.start %} {% comment %} Starting a new day? {% endcomment %} 40 | {% assign day = day | plus: 1 %} 41 | {% if day > 1 %} {% comment %} If about to start day 2 or later, show finishing time for previous day {% endcomment %} 42 | {% assign hours = current | divided_by: 60 %} 43 | {% assign minutes = current | modulo: 60 %} 44 | 45 | {% if multiday %}{% endif %} 46 | 47 | 48 | 49 | 50 | {% endif %} 51 | {% assign current = site.start_time %} {% comment %}Re-set start time of this episode to general daily start time {% endcomment %} 52 | {% endif %} 53 | {% assign hours = current | divided_by: 60 %} 54 | {% assign minutes = current | modulo: 60 %} 55 | 56 | {% if multiday %}{% endif %} 57 | 58 | 62 | 76 | 77 | {% assign current = current | plus: episode.teaching | plus: episode.exercises | plus: episode.break %} 78 | {% endfor %} 79 | {% assign hours = current | divided_by: 60 %} 80 | {% assign minutes = current | modulo: 60 %} 81 | 82 | {% if multiday %}{% endif %} 83 | 84 | 85 | 86 | 87 |
SetupDownload files required for the lesson
{% if hours < 10 %}0{% endif %}{{ hours }}:{% if minutes < 10 %}0{% endif %}{{ minutes }}Finish
{% if episode.start %}Day {{ day }}{% endif %}{% if hours < 10 %}0{% endif %}{{ hours }}:{% if minutes < 10 %}0{% endif %}{{ minutes }} 59 | {% assign lesson_number = lesson_number | plus: 1 %} 60 | {{ lesson_number }}. {{ episode.title }} 61 | 63 | {% if episode.break %} 64 | Break 65 | {% else %} 66 | {% if episode.questions %} 67 | {% for question in episode.questions %} 68 | {{question|markdownify|strip_html}} 69 | {% unless forloop.last %} 70 |
71 | {% endunless %} 72 | {% endfor %} 73 | {% endif %} 74 | {% endif %} 75 |
{% if hours < 10 %}0{% endif %}{{ hours }}:{% if minutes < 10 %}0{% endif %}{{ minutes }}Finish
88 | 89 |

90 | The actual schedule may vary slightly depending on the topics and exercises chosen by the instructor. 91 |

92 | 93 |
94 | -------------------------------------------------------------------------------- /_includes/workshop_ad.html: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Advertising box at the top of a workshop website home page. 3 | {% endcomment %} 4 |
5 |
6 |
7 |

{{page.venue}}

8 |
9 |
10 |

{{page.humandate}}

11 |

{% if page.humantime %}{{page.humantime}}{% endif %}

12 |
13 |
14 |

15 | Instructors: 16 | {% if page.instructor %} 17 | {{page.instructor | join: ', ' %}} 18 | {% else %} 19 | to be announced. 20 | {% endif %} 21 |

22 | {% if page.helper %} 23 |

24 | Helpers: 25 | {{page.helper | join: ', ' %}} 26 |

27 | {% endif %} 28 |
29 |
30 |
31 |
32 |
33 | -------------------------------------------------------------------------------- /_includes/workshop_calendar.html: -------------------------------------------------------------------------------- 1 | Add to your Google Calendar. 2 | -------------------------------------------------------------------------------- /_includes/workshop_footer.html: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Footer for a standard workshop. 3 | {% endcomment %} 4 | 27 | -------------------------------------------------------------------------------- /_layouts/base.html: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | {% include base_path.html %} 4 | {% include gh_variables.html %} 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | {% include favicons.html %} 22 | 23 | 24 | 25 | 29 | 30 | 31 | {% if page.title %}{{ page.title }}{% endif %}{% if page.title and site.title %} – {% endif %}{% if site.title %}{{ site.title }}{% endif %} 32 | 33 | 34 | 35 | 36 | 37 | {% include life_cycle.html %} 38 | 39 |
40 | {% include navbar.html %} 41 | {{ content }} 42 | {% if site.kind == "workshop" %} 43 | {% include workshop_footer.html %} 44 | {% else %} 45 | {% include lesson_footer.html %} 46 | {% endif %} 47 |
48 | {% include javascript.html %} 49 | 50 | 51 | -------------------------------------------------------------------------------- /_layouts/break.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: base 3 | --- 4 | {% include episode_navbar.html episode_navbar_title=true %} 5 |
6 | {% include episode_title.html %} 7 | {% include episode_break.html %} 8 | {{content}} 9 |
10 | {% include episode_navbar.html episode_navbar_title=false %} 11 | -------------------------------------------------------------------------------- /_layouts/episode.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: base 3 | --- 4 | {% include episode_navbar.html episode_navbar_title=true %} 5 |
6 | {% include episode_title.html %} 7 | {% include episode_overview.html %} 8 | {{content}} 9 | {% include episode_keypoints.html %} 10 |
11 | {% include episode_navbar.html episode_navbar_title=false %} 12 | -------------------------------------------------------------------------------- /_layouts/lesson.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: base 3 | --- 4 | {% include main_title.html %} 5 |
6 | {{ content }} 7 |
8 | {% include syllabus.html %} 9 | -------------------------------------------------------------------------------- /_layouts/page.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: base 3 | --- 4 | {% include main_title.html %} 5 |
6 | {{content}} 7 |
8 | -------------------------------------------------------------------------------- /_layouts/reference.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: "Reference" 4 | --- 5 | {% include all_keypoints.html %} 6 | {{content}} 7 | -------------------------------------------------------------------------------- /_layouts/workshop.html: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | {% include base_path.html %} 4 | {% include gh_variables.html %} 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | {% endif %} 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | {% include favicons.html %} 38 | 39 | 40 | 41 | 45 | {{ page.venue }}: {{ page.humandate }} 46 | 47 | 48 |
49 | {% include navbar.html %} 50 | {% include workshop_ad.html %} 51 | {{ content }} 52 | {% include workshop_footer.html %} 53 |
54 | {% include javascript.html %} 55 | 56 | 57 | -------------------------------------------------------------------------------- /aio.md: -------------------------------------------------------------------------------- 1 | --- 2 | permalink: /aio/index.html 3 | --- 4 | 5 | {% comment %} 6 | As a maintainer, you don't need to edit this file. 7 | If you notice that something doesn't work, please 8 | open an issue: https://github.com/carpentries/styles/issues/new 9 | {% endcomment %} 10 | 11 | {% include base_path.html %} 12 | 13 | {% include aio-script.md %} 14 | -------------------------------------------------------------------------------- /assets/css/syntax.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight { background: #f8f8f8; } 3 | .highlight .c { color: #387d7d; font-style: italic } /* Comment */ 4 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 5 | .highlight .k { color: #008000; font-weight: bold } /* Keyword */ 6 | .highlight .o { color: #666666 } /* Operator */ 7 | .highlight .ch { color: #387d7d; font-style: italic } /* Comment.Hashbang */ 8 | .highlight .cm { color: #387d7d; font-style: italic } /* Comment.Multiline */ 9 | .highlight .cp { color: #BC7A00 } /* Comment.Preproc */ 10 | .highlight .cpf { color: #387d7d; font-style: italic } /* Comment.PreprocFile */ 11 | .highlight .c1 { color: #387d7d; font-style: italic } /* Comment.Single */ 12 | .highlight .cs { color: #387d7d; font-style: italic } /* Comment.Special */ 13 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 14 | .highlight .ge { font-style: italic } /* Generic.Emph */ 15 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 16 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 17 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 18 | .highlight .go { color: #888888 } /* Generic.Output */ 19 | .highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 20 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 21 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 22 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 23 | .highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ 24 | .highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ 25 | .highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ 26 | .highlight .kp { color: #008000 } /* Keyword.Pseudo */ 27 | .highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ 28 | .highlight .kt { color: #B00040 } /* Keyword.Type */ 29 | .highlight .m { color: #666666 } /* Literal.Number */ 30 | .highlight .s { color: #BA2121 } /* Literal.String */ 31 | .highlight .na { color: #7D9029 } /* Name.Attribute */ 32 | .highlight .nb { color: #008000 } /* Name.Builtin */ 33 | .highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ 34 | .highlight .no { color: #880000 } /* Name.Constant */ 35 | .highlight .nd { color: #AA22FF } /* Name.Decorator */ 36 | .highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ 37 | .highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ 38 | .highlight .nf { color: #0000FF } /* Name.Function */ 39 | .highlight .nl { color: #A0A000 } /* Name.Label */ 40 | .highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 41 | .highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ 42 | .highlight .nv { color: #19177C } /* Name.Variable */ 43 | .highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 44 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 45 | .highlight .mb { color: #666666 } /* Literal.Number.Bin */ 46 | .highlight .mf { color: #666666 } /* Literal.Number.Float */ 47 | .highlight .mh { color: #666666 } /* Literal.Number.Hex */ 48 | .highlight .mi { color: #666666 } /* Literal.Number.Integer */ 49 | .highlight .mo { color: #666666 } /* Literal.Number.Oct */ 50 | .highlight .sa { color: #BA2121 } /* Literal.String.Affix */ 51 | .highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ 52 | .highlight .sc { color: #BA2121 } /* Literal.String.Char */ 53 | .highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ 54 | .highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ 55 | .highlight .s2 { color: #BA2121 } /* Literal.String.Double */ 56 | .highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ 57 | .highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ 58 | .highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ 59 | .highlight .sx { color: #008000 } /* Literal.String.Other */ 60 | .highlight .sr { color: #BB6688 } /* Literal.String.Regex */ 61 | .highlight .s1 { color: #BA2121 } /* Literal.String.Single */ 62 | .highlight .ss { color: #19177C } /* Literal.String.Symbol */ 63 | .highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ 64 | .highlight .fm { color: #0000FF } /* Name.Function.Magic */ 65 | .highlight .vc { color: #19177C } /* Name.Variable.Class */ 66 | .highlight .vg { color: #19177C } /* Name.Variable.Global */ 67 | .highlight .vi { color: #19177C } /* Name.Variable.Instance */ 68 | .highlight .vm { color: #19177C } /* Name.Variable.Magic */ 69 | .highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ 70 | -------------------------------------------------------------------------------- /assets/favicons/cp/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/cp/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /assets/favicons/cp/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/cp/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /assets/favicons/cp/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/cp/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /assets/favicons/cp/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/cp/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /assets/favicons/cp/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/cp/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /assets/favicons/cp/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/cp/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /assets/favicons/cp/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/cp/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /assets/favicons/cp/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/cp/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /assets/favicons/cp/favicon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/cp/favicon-128.png -------------------------------------------------------------------------------- /assets/favicons/cp/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/cp/favicon-16x16.png -------------------------------------------------------------------------------- /assets/favicons/cp/favicon-196x196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/cp/favicon-196x196.png -------------------------------------------------------------------------------- /assets/favicons/cp/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/cp/favicon-32x32.png -------------------------------------------------------------------------------- /assets/favicons/cp/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/cp/favicon-96x96.png -------------------------------------------------------------------------------- /assets/favicons/cp/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/cp/favicon.ico -------------------------------------------------------------------------------- /assets/favicons/cp/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/cp/mstile-144x144.png -------------------------------------------------------------------------------- /assets/favicons/cp/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/cp/mstile-150x150.png -------------------------------------------------------------------------------- /assets/favicons/cp/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/cp/mstile-310x150.png -------------------------------------------------------------------------------- /assets/favicons/cp/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/cp/mstile-310x310.png -------------------------------------------------------------------------------- /assets/favicons/cp/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/cp/mstile-70x70.png -------------------------------------------------------------------------------- /assets/favicons/dc/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/dc/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /assets/favicons/dc/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/dc/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /assets/favicons/dc/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/dc/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /assets/favicons/dc/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/dc/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /assets/favicons/dc/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/dc/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /assets/favicons/dc/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/dc/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /assets/favicons/dc/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/dc/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /assets/favicons/dc/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/dc/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /assets/favicons/dc/favicon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/dc/favicon-128.png -------------------------------------------------------------------------------- /assets/favicons/dc/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/dc/favicon-16x16.png -------------------------------------------------------------------------------- /assets/favicons/dc/favicon-196x196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/dc/favicon-196x196.png -------------------------------------------------------------------------------- /assets/favicons/dc/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/dc/favicon-32x32.png -------------------------------------------------------------------------------- /assets/favicons/dc/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/dc/favicon-96x96.png -------------------------------------------------------------------------------- /assets/favicons/dc/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/dc/favicon.ico -------------------------------------------------------------------------------- /assets/favicons/dc/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/dc/mstile-144x144.png -------------------------------------------------------------------------------- /assets/favicons/dc/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/dc/mstile-150x150.png -------------------------------------------------------------------------------- /assets/favicons/dc/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/dc/mstile-310x150.png -------------------------------------------------------------------------------- /assets/favicons/dc/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/dc/mstile-310x310.png -------------------------------------------------------------------------------- /assets/favicons/dc/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/dc/mstile-70x70.png -------------------------------------------------------------------------------- /assets/favicons/lc/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/lc/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /assets/favicons/lc/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/lc/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /assets/favicons/lc/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/lc/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /assets/favicons/lc/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/lc/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /assets/favicons/lc/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/lc/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /assets/favicons/lc/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/lc/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /assets/favicons/lc/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/lc/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /assets/favicons/lc/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/lc/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /assets/favicons/lc/favicon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/lc/favicon-128.png -------------------------------------------------------------------------------- /assets/favicons/lc/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/lc/favicon-16x16.png -------------------------------------------------------------------------------- /assets/favicons/lc/favicon-196x196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/lc/favicon-196x196.png -------------------------------------------------------------------------------- /assets/favicons/lc/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/lc/favicon-32x32.png -------------------------------------------------------------------------------- /assets/favicons/lc/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/lc/favicon-96x96.png -------------------------------------------------------------------------------- /assets/favicons/lc/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/lc/favicon.ico -------------------------------------------------------------------------------- /assets/favicons/lc/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/lc/mstile-144x144.png -------------------------------------------------------------------------------- /assets/favicons/lc/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/lc/mstile-150x150.png -------------------------------------------------------------------------------- /assets/favicons/lc/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/lc/mstile-310x150.png -------------------------------------------------------------------------------- /assets/favicons/lc/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/lc/mstile-310x310.png -------------------------------------------------------------------------------- /assets/favicons/lc/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/lc/mstile-70x70.png -------------------------------------------------------------------------------- /assets/favicons/swc/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/swc/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /assets/favicons/swc/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/swc/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /assets/favicons/swc/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/swc/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /assets/favicons/swc/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/swc/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /assets/favicons/swc/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/swc/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /assets/favicons/swc/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/swc/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /assets/favicons/swc/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/swc/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /assets/favicons/swc/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/swc/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /assets/favicons/swc/favicon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/swc/favicon-128.png -------------------------------------------------------------------------------- /assets/favicons/swc/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/swc/favicon-16x16.png -------------------------------------------------------------------------------- /assets/favicons/swc/favicon-196x196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/swc/favicon-196x196.png -------------------------------------------------------------------------------- /assets/favicons/swc/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/swc/favicon-32x32.png -------------------------------------------------------------------------------- /assets/favicons/swc/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/swc/favicon-96x96.png -------------------------------------------------------------------------------- /assets/favicons/swc/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/swc/favicon.ico -------------------------------------------------------------------------------- /assets/favicons/swc/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/swc/mstile-144x144.png -------------------------------------------------------------------------------- /assets/favicons/swc/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/swc/mstile-150x150.png -------------------------------------------------------------------------------- /assets/favicons/swc/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/swc/mstile-310x150.png -------------------------------------------------------------------------------- /assets/favicons/swc/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/swc/mstile-310x310.png -------------------------------------------------------------------------------- /assets/favicons/swc/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/favicons/swc/mstile-70x70.png -------------------------------------------------------------------------------- /assets/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /assets/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /assets/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /assets/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /assets/img/cp-logo-blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/img/dc-icon-black.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 30 | 54 | 59 | 64 | 69 | 74 | 75 | -------------------------------------------------------------------------------- /assets/img/dc-logo-black.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 30 | 54 | 59 | 64 | 69 | 74 | 75 | -------------------------------------------------------------------------------- /assets/img/lc-icon-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/img/lc-icon-black.png -------------------------------------------------------------------------------- /assets/img/lc-logo-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/img/lc-logo-black.png -------------------------------------------------------------------------------- /assets/img/swc-icon-blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /assets/img/swc-logo-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/img/swc-logo-blue.png -------------------------------------------------------------------------------- /assets/img/swc-logo-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/assets/img/swc-logo-white.png -------------------------------------------------------------------------------- /assets/js/lesson.js: -------------------------------------------------------------------------------- 1 | // Make all tables striped by default. 2 | $("table").addClass("table table-striped"); 3 | 4 | 5 | // Handle foldable challenges and solutions (on click and at start). 6 | $(".solution").click(function(event) { 7 | var trigger = $(event.target).has(".fold-unfold").length > 0 8 | || $(event.target).filter(".fold-unfold").length > 0; 9 | if (trigger) { 10 | $(">*:not(h2)", this).toggle(400); 11 | $(">h2>span.fold-unfold", this).toggleClass("glyphicon-collapse-down glyphicon-collapse-up"); 12 | event.stopPropagation(); 13 | } 14 | }); 15 | $(".solution").each(function() { 16 | $(">*:not(h2)", this).toggle(); 17 | var h2 = $("h2:first", this); 18 | h2.append(""); 19 | }); 20 | 21 | 22 | // Handle searches. 23 | // Relies on document having 'meta' element with name 'search-domain'. 24 | function google_search() { 25 | var query = document.getElementById("google-search").value; 26 | var domain = $("meta[name=search-domain]").attr("value"); 27 | window.open("https://www.google.com/search?q=" + query + "+site:" + domain); 28 | } 29 | 30 | // function to shrink the life cycle bar when scrolling 31 | $(function(){ 32 | $('#life-cycle').data('size','big'); 33 | }); 34 | 35 | $(window).scroll(function(){ 36 | if($(document).scrollTop() > 0) 37 | { 38 | if($('#life-cycle').data('size') == 'big') 39 | { 40 | $('#life-cycle').data('size','small'); 41 | $('#life-cycle').stop().animate({ 42 | padding: '5px' 43 | },100); 44 | } 45 | } 46 | else 47 | { 48 | if($('#life-cycle').data('size') == 'small') 49 | { 50 | $('#life-cycle').data('size','big'); 51 | $('#life-cycle').stop().animate({ 52 | padding: '15px' 53 | },100); 54 | } 55 | } 56 | }); 57 | -------------------------------------------------------------------------------- /bin/boilerplate/.travis.yml: -------------------------------------------------------------------------------- 1 | # Travis CI is only used to check the lesson and is not involved in its deployment 2 | dist: bionic 3 | language: ruby 4 | rvm: 5 | - 2.7.1 6 | 7 | branches: 8 | only: 9 | - gh-pages 10 | - /.*/ 11 | 12 | cache: 13 | apt: true 14 | bundler: true 15 | directories: 16 | - /home/travis/.rvm/ 17 | - $R_LIBS_USER 18 | - $HOME/.cache/pip 19 | 20 | env: 21 | global: 22 | - NOKOGIRI_USE_SYSTEM_LIBRARIES=true # speeds up installation of html-proofer 23 | - R_LIBS_USER=~/R/Library 24 | - R_LIBS_SITE=/usr/local/lib/R/site-library:/usr/lib/R/site-library 25 | - R_VERSION=4.0.2 26 | 27 | before_install: 28 | ## Install R + pandoc + dependencies 29 | - sudo add-apt-repository -y "ppa:marutter/rrutter4.0" 30 | - sudo add-apt-repository -y "ppa:c2d4u.team/c2d4u4.0+" 31 | - sudo add-apt-repository -y "ppa:ubuntugis/ppa" 32 | - sudo add-apt-repository -y "ppa:cran/travis" 33 | - travis_apt_get_update 34 | - sudo apt-get install -y --no-install-recommends build-essential gcc g++ libblas-dev liblapack-dev libncurses5-dev libreadline-dev libjpeg-dev libpcre3-dev libpng-dev zlib1g-dev libbz2-dev liblzma-dev libicu-dev cdbs qpdf texinfo libssh2-1-dev gfortran jq python3.5 python3-pip r-base 35 | - export PATH=${TRAVIS_HOME}/R-bin/bin:$PATH 36 | - export LD_LIBRARY_PATH=${TRAVIS_HOME}/R-bin/lib:$LD_LIBRARY_PATH 37 | - sudo mkdir -p /usr/local/lib/R/site-library $R_LIBS_USER 38 | - sudo chmod 2777 /usr/local/lib/R /usr/local/lib/R/site-library $R_LIBS_USER 39 | - echo 'options(repos = c(CRAN = "https://packagemanager.rstudio.com/all/__linux__/bionic/latest"))' > ~/.Rprofile.site 40 | - export R_PROFILE=~/.Rprofile.site 41 | - curl -fLo /tmp/texlive.tar.gz https://github.com/jimhester/ubuntu-bin/releases/download/latest/texlive.tar.gz 42 | - tar xzf /tmp/texlive.tar.gz -C ~ 43 | - export PATH=${TRAVIS_HOME}/texlive/bin/x86_64-linux:$PATH 44 | - tlmgr update --self 45 | - curl -fLo /tmp/pandoc-2.2-1-amd64.deb https://github.com/jgm/pandoc/releases/download/2.2/pandoc-2.2-1-amd64.deb 46 | - sudo dpkg -i /tmp/pandoc-2.2-1-amd64.deb 47 | - sudo apt-get install -f 48 | - rm /tmp/pandoc-2.2-1-amd64.deb 49 | - Rscript -e "install.packages(setdiff(c('renv', 'rprojroot'), installed.packages()), loc = Sys.getenv('R_LIBS_USER')); update.packages(lib.loc = Sys.getenv('R_LIBS_USER'), ask = FALSE, checkBuilt = TRUE)" 50 | - Rscript -e 'sessionInfo()' 51 | ## Install python and dependencies 52 | - python3 -m pip install --upgrade pip setuptools wheel 53 | - python3 -m pip install pyyaml 54 | 55 | script: 56 | - make lesson-check-all 57 | - make --always-make site 58 | -------------------------------------------------------------------------------- /bin/boilerplate/AUTHORS: -------------------------------------------------------------------------------- 1 | FIXME: list authors' names and email addresses. -------------------------------------------------------------------------------- /bin/boilerplate/CITATION: -------------------------------------------------------------------------------- 1 | FIXME: describe how to cite this lesson. -------------------------------------------------------------------------------- /bin/boilerplate/README.md: -------------------------------------------------------------------------------- 1 | # FIXME Lesson title 2 | 3 | [![Create a Slack Account with us](https://img.shields.io/badge/Create_Slack_Account-The_Carpentries-071159.svg)](https://swc-slack-invite.herokuapp.com/) 4 | 5 | This repository generates the corresponding lesson website from [The Carpentries](https://carpentries.org/) repertoire of lessons. 6 | 7 | ## Contributing 8 | 9 | We welcome all contributions to improve the lesson! Maintainers will do their best to help you if you have any 10 | questions, concerns, or experience any difficulties along the way. 11 | 12 | We'd like to ask you to familiarize yourself with our [Contribution Guide](CONTRIBUTING.md) and have a look at 13 | the [more detailed guidelines][lesson-example] on proper formatting, ways to render the lesson locally, and even 14 | how to write new episodes. 15 | 16 | Please see the current list of [issues][FIXME] for ideas for contributing to this 17 | repository. For making your contribution, we use the GitHub flow, which is 18 | nicely explained in the chapter [Contributing to a Project](http://git-scm.com/book/en/v2/GitHub-Contributing-to-a-Project) in Pro Git 19 | by Scott Chacon. 20 | Look for the tag ![good_first_issue](https://img.shields.io/badge/-good%20first%20issue-gold.svg). This indicates that the maintainers will welcome a pull request fixing this issue. 21 | 22 | 23 | ## Maintainer(s) 24 | 25 | Current maintainers of this lesson are 26 | 27 | * FIXME 28 | * FIXME 29 | * FIXME 30 | 31 | 32 | ## Authors 33 | 34 | A list of contributors to the lesson can be found in [AUTHORS](AUTHORS) 35 | 36 | ## Citation 37 | 38 | To cite this lesson, please consult with [CITATION](CITATION) 39 | 40 | [lesson-example]: https://carpentries.github.io/lesson-example 41 | -------------------------------------------------------------------------------- /bin/boilerplate/_config.yml: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------ 2 | # Values for this lesson. 3 | #------------------------------------------------------------ 4 | 5 | # Which carpentry is this ("swc", "dc", "lc", or "cp")? 6 | # swc: Software Carpentry 7 | # dc: Data Carpentry 8 | # lc: Library Carpentry 9 | # cp: Carpentries (to use for instructor traning for instance) 10 | carpentry: "swc" 11 | 12 | # Overall title for pages. 13 | title: "Lesson Title" 14 | 15 | # Life cycle stage of the lesson 16 | # See this page for more details: https://cdh.carpentries.org/the-lesson-life-cycle.html 17 | # Possible values: "pre-alpha", "alpha", "beta", "stable" 18 | life_cycle: "pre-alpha" 19 | 20 | #------------------------------------------------------------ 21 | # Generic settings (should not need to change). 22 | #------------------------------------------------------------ 23 | 24 | # What kind of thing is this ("workshop" or "lesson")? 25 | kind: "lesson" 26 | 27 | # Magic to make URLs resolve both locally and on GitHub. 28 | # See https://help.github.com/articles/repository-metadata-on-github-pages/. 29 | # Please don't change it: / is correct. 30 | repository: / 31 | 32 | # Email address, no mailto: 33 | email: "team@carpentries.org" 34 | 35 | # Sites. 36 | amy_site: "https://amy.carpentries.org/" 37 | carpentries_github: "https://github.com/carpentries" 38 | carpentries_pages: "https://carpentries.github.io" 39 | carpentries_site: "https://carpentries.org/" 40 | dc_site: "https://datacarpentry.org" 41 | example_repo: "https://github.com/carpentries/lesson-example" 42 | example_site: "https://carpentries.github.io/lesson-example" 43 | lc_site: "https://librarycarpentry.org/" 44 | swc_github: "https://github.com/swcarpentry" 45 | swc_pages: "https://swcarpentry.github.io" 46 | swc_site: "https://software-carpentry.org" 47 | template_repo: "https://github.com/carpentries/styles" 48 | training_site: "https://carpentries.github.io/instructor-training" 49 | workshop_repo: "https://github.com/carpentries/workshop-template" 50 | workshop_site: "https://carpentries.github.io/workshop-template" 51 | cc_by_human: "https://creativecommons.org/licenses/by/4.0/" 52 | 53 | # Surveys. 54 | pre_survey: "https://carpentries.typeform.com/to/wi32rS?slug=" 55 | post_survey: "https://carpentries.typeform.com/to/UgVdRQ?slug=" 56 | instructor_pre_survey: "https://www.surveymonkey.com/r/instructor_training_pre_survey?workshop_id=" 57 | instructor_post_survey: "https://www.surveymonkey.com/r/instructor_training_post_survey?workshop_id=" 58 | 59 | 60 | # Start time in minutes (0 to be clock-independent, 540 to show a start at 09:00 am). 61 | start_time: 0 62 | 63 | # Specify that things in the episodes collection should be output. 64 | collections: 65 | episodes: 66 | output: true 67 | permalink: /:path/index.html 68 | extras: 69 | output: true 70 | permalink: /:path/index.html 71 | 72 | # Set the default layout for things in the episodes collection. 73 | defaults: 74 | - values: 75 | root: . 76 | layout: page 77 | - scope: 78 | path: "" 79 | type: episodes 80 | values: 81 | root: .. 82 | layout: episode 83 | - scope: 84 | path: "" 85 | type: extras 86 | values: 87 | root: .. 88 | layout: page 89 | 90 | # Files and directories that are not to be copied. 91 | exclude: 92 | - Makefile 93 | - bin/ 94 | - .Rproj.user/ 95 | - .vendor/ 96 | - vendor/ 97 | - .docker-vendor/ 98 | 99 | # Turn on built-in syntax highlighting. 100 | highlighter: rouge 101 | -------------------------------------------------------------------------------- /bin/boilerplate/_episodes/01-introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Introduction" 3 | teaching: 0 4 | exercises: 0 5 | questions: 6 | - "Key question (FIXME)" 7 | objectives: 8 | - "First learning objective. (FIXME)" 9 | keypoints: 10 | - "First key point. Brief Answer to questions. (FIXME)" 11 | --- 12 | FIXME 13 | 14 | {% include links.md %} 15 | 16 | -------------------------------------------------------------------------------- /bin/boilerplate/_extras/about.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: About 3 | --- 4 | {% include carpentries.html %} 5 | {% include links.md %} 6 | -------------------------------------------------------------------------------- /bin/boilerplate/_extras/discuss.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Discussion 3 | --- 4 | FIXME 5 | 6 | {% include links.md %} 7 | -------------------------------------------------------------------------------- /bin/boilerplate/_extras/figures.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Figures 3 | --- 4 | 5 | {% include base_path.html %} 6 | {% include manual_episode_order.html %} 7 | 8 | 67 | 68 | {% comment %} Create anchor for each one of the episodes. {% endcomment %} 69 | 70 | {% for lesson_episode in lesson_episodes %} 71 | {% if site.episode_order %} 72 | {% assign episode = site.episodes | where: "slug", lesson_episode | first %} 73 | {% else %} 74 | {% assign episode = lesson_episode %} 75 | {% endif %} 76 |
77 | {% endfor %} 78 | 79 | {% include links.md %} 80 | -------------------------------------------------------------------------------- /bin/boilerplate/_extras/guide.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Instructor Notes" 3 | --- 4 | FIXME 5 | 6 | {% include links.md %} 7 | -------------------------------------------------------------------------------- /bin/boilerplate/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: lesson 3 | root: . # Is the only page that doesn't follow the pattern /:path/index.html 4 | permalink: index.html # Is the only page that doesn't follow the pattern /:path/index.html 5 | --- 6 | FIXME: home page introduction 7 | 8 | 9 | 10 | {% comment %} This is a comment in Liquid {% endcomment %} 11 | 12 | > ## Prerequisites 13 | > 14 | > FIXME 15 | {: .prereq} 16 | 17 | {% include links.md %} 18 | -------------------------------------------------------------------------------- /bin/boilerplate/reference.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: reference 3 | --- 4 | 5 | ## Glossary 6 | 7 | FIXME 8 | 9 | {% include links.md %} 10 | -------------------------------------------------------------------------------- /bin/boilerplate/setup.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Setup 3 | --- 4 | FIXME 5 | 6 | 7 | {% include links.md %} 8 | -------------------------------------------------------------------------------- /bin/chunk-options.R: -------------------------------------------------------------------------------- 1 | # These settings control the behavior of all chunks in the novice R materials. 2 | # For example, to generate the lessons with all the output hidden, simply change 3 | # `results` from "markup" to "hide". 4 | # For more information on available chunk options, see 5 | # http://yihui.name/knitr/options#chunk_options 6 | 7 | library("knitr") 8 | 9 | fix_fig_path <- function(pth) file.path("..", pth) 10 | 11 | 12 | ## We set the path for the figures globally below, so if we want to 13 | ## customize it for individual episodes, we can append a prefix to the 14 | ## global path. For instance, if we call knitr_fig_path("01-") in the 15 | ## first episode of the lesson, it will generate the figures in 16 | ## `fig/rmd-01-` 17 | knitr_fig_path <- function(prefix) { 18 | new_path <- paste0(opts_chunk$get("fig.path"), 19 | prefix) 20 | opts_chunk$set(fig.path = new_path) 21 | } 22 | 23 | ## We use the rmd- prefix for the figures generated by the lessons so 24 | ## they can be easily identified and deleted by `make clean-rmd`. The 25 | ## working directory when the lessons are generated is the root so the 26 | ## figures need to be saved in fig/, but when the site is generated, 27 | ## the episodes will be one level down. We fix the path using the 28 | ## `fig.process` option. 29 | 30 | opts_chunk$set(tidy = FALSE, results = "markup", comment = NA, 31 | fig.align = "center", fig.path = "fig/rmd-", 32 | fig.process = fix_fig_path, 33 | fig.width = 8.5, fig.height = 8.5, 34 | fig.retina = 2) 35 | 36 | # The hooks below add html tags to the code chunks and their output so that they 37 | # are properly formatted when the site is built. 38 | 39 | hook_in <- function(x, options) { 40 | lg <- tolower(options$engine) 41 | style <- paste0(".language-", lg) 42 | 43 | stringr::str_c("\n\n~~~\n", 44 | paste0(x, collapse="\n"), 45 | "\n~~~\n{: ", style, "}\n\n") 46 | } 47 | 48 | hook_out <- function(x, options) { 49 | x <- gsub("\n$", "", x) 50 | stringr::str_c("\n\n~~~\n", 51 | paste0(x, collapse="\n"), 52 | "\n~~~\n{: .output}\n\n") 53 | } 54 | 55 | hook_error <- function(x, options) { 56 | x <- gsub("\n$", "", x) 57 | stringr::str_c("\n\n~~~\n", 58 | paste0(x, collapse="\n"), 59 | "\n~~~\n{: .error}\n\n") 60 | } 61 | 62 | hook_warning <- function(x, options) { 63 | x <- gsub("\n$", "", x) 64 | stringr::str_c("\n\n~~~\n", 65 | paste0(x, collapse = "\n"), 66 | "\n~~~\n{: .warning}\n\n") 67 | } 68 | 69 | knit_hooks$set(source = hook_in, output = hook_out, warning = hook_warning, 70 | error = hook_error, message = hook_out) 71 | -------------------------------------------------------------------------------- /bin/dependencies.R: -------------------------------------------------------------------------------- 1 | install_required_packages <- function(lib = NULL, repos = getOption("repos")) { 2 | 3 | if (is.null(lib)) { 4 | lib <- .libPaths() 5 | } 6 | 7 | message("lib paths: ", paste(lib, collapse = ", ")) 8 | missing_pkgs <- setdiff( 9 | c("rprojroot", "desc", "remotes", "renv"), 10 | rownames(installed.packages(lib.loc = lib)) 11 | ) 12 | 13 | install.packages(missing_pkgs, lib = lib, repos = repos) 14 | 15 | } 16 | 17 | find_root <- function() { 18 | 19 | cfg <- rprojroot::has_file_pattern("^_config.y*ml$") 20 | root <- rprojroot::find_root(cfg) 21 | 22 | root 23 | } 24 | 25 | identify_dependencies <- function() { 26 | 27 | root <- find_root() 28 | 29 | required_pkgs <- unique(c( 30 | ## Packages for episodes 31 | renv::dependencies(file.path(root, "_episodes_rmd"), progress = FALSE, error = "ignore")$Package, 32 | ## Packages for tools 33 | renv::dependencies(file.path(root, "bin"), progress = FALSE, error = "ignore")$Package 34 | )) 35 | 36 | required_pkgs 37 | } 38 | 39 | create_description <- function(required_pkgs) { 40 | d <- desc::description$new("!new") 41 | lapply(required_pkgs, function(x) d$set_dep(x)) 42 | d$write("DESCRIPTION") 43 | } 44 | 45 | install_dependencies <- function(required_pkgs, ...) { 46 | 47 | create_description(required_pkgs) 48 | on.exit(file.remove("DESCRIPTION")) 49 | remotes::install_deps(dependencies = TRUE, ...) 50 | 51 | if (require("knitr") && packageVersion("knitr") < '1.9.19') { 52 | stop("knitr must be version 1.9.20 or higher") 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /bin/generate_md_episodes.R: -------------------------------------------------------------------------------- 1 | generate_md_episodes <- function() { 2 | 3 | ## get the Rmd file to process from the command line, and generate the path 4 | ## for their respective outputs 5 | args <- commandArgs(trailingOnly = TRUE) 6 | if (!identical(length(args), 2L)) { 7 | stop("input and output file must be passed to the script") 8 | } 9 | 10 | src_rmd <- args[1] 11 | dest_md <- args[2] 12 | 13 | ## knit the Rmd into markdown 14 | knitr::knit(src_rmd, output = dest_md) 15 | 16 | # Read the generated md files and add comments advising not to edit them 17 | add_no_edit_comment <- function(y) { 18 | con <- file(y) 19 | mdfile <- readLines(con) 20 | if (mdfile[1] != "---") 21 | stop("Input file does not have a valid header") 22 | mdfile <- append( 23 | mdfile, 24 | "# Please do not edit this file directly; it is auto generated.", 25 | after = 1 26 | ) 27 | mdfile <- append( 28 | mdfile, 29 | paste("# Instead, please edit", basename(y), "in _episodes_rmd/"), 30 | after = 2 31 | ) 32 | writeLines(mdfile, con) 33 | close(con) 34 | return(paste("Warning added to YAML header of", y)) 35 | } 36 | 37 | vapply(dest_md, add_no_edit_comment, character(1)) 38 | } 39 | 40 | generate_md_episodes() 41 | -------------------------------------------------------------------------------- /bin/install_r_deps.sh: -------------------------------------------------------------------------------- 1 | Rscript -e "source(file.path('bin', 'dependencies.R')); install_required_packages(); install_dependencies(identify_dependencies())" 2 | -------------------------------------------------------------------------------- /bin/knit_lessons.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Only try running R to translate files if there are some files present. 4 | # The Makefile passes in the names of files. 5 | 6 | if [ $# -eq 2 ] ; then 7 | Rscript -e "source('bin/generate_md_episodes.R')" "$@" 8 | fi 9 | -------------------------------------------------------------------------------- /bin/lesson_initialize.py: -------------------------------------------------------------------------------- 1 | """Initialize a newly-created repository.""" 2 | 3 | 4 | import sys 5 | import os 6 | import shutil 7 | 8 | BOILERPLATE = ( 9 | '.travis.yml', 10 | 'AUTHORS', 11 | 'CITATION', 12 | 'CONTRIBUTING.md', 13 | 'README.md', 14 | '_config.yml', 15 | os.path.join('_episodes', '01-introduction.md'), 16 | os.path.join('_extras', 'about.md'), 17 | os.path.join('_extras', 'discuss.md'), 18 | os.path.join('_extras', 'figures.md'), 19 | os.path.join('_extras', 'guide.md'), 20 | 'index.md', 21 | 'reference.md', 22 | 'setup.md', 23 | ) 24 | 25 | 26 | def main(): 27 | """Check for collisions, then create.""" 28 | 29 | # Check. 30 | errors = False 31 | for path in BOILERPLATE: 32 | if os.path.exists(path): 33 | print('Warning: {0} already exists.'.format(path), file=sys.stderr) 34 | errors = True 35 | if errors: 36 | print('**Exiting without creating files.**', file=sys.stderr) 37 | sys.exit(1) 38 | 39 | # Create. 40 | for path in BOILERPLATE: 41 | shutil.copyfile( 42 | os.path.join('bin', 'boilerplate', path), 43 | path 44 | ) 45 | 46 | 47 | if __name__ == '__main__': 48 | main() 49 | -------------------------------------------------------------------------------- /bin/markdown_ast.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # Use Kramdown parser to produce AST for Markdown document. 4 | 5 | require "kramdown" 6 | require "json" 7 | 8 | markdown = STDIN.read() 9 | doc = Kramdown::Document.new(markdown) 10 | tree = doc.to_hash_a_s_t 11 | puts JSON.pretty_generate(tree) 12 | -------------------------------------------------------------------------------- /bin/repo_check.py: -------------------------------------------------------------------------------- 1 | """ 2 | Check repository settings. 3 | """ 4 | 5 | 6 | import sys 7 | import os 8 | from subprocess import Popen, PIPE 9 | import re 10 | from argparse import ArgumentParser 11 | 12 | from util import Reporter, require 13 | 14 | # Import this way to produce a more useful error message. 15 | try: 16 | import requests 17 | except ImportError: 18 | print('Unable to import requests module: please install requests', file=sys.stderr) 19 | sys.exit(1) 20 | 21 | 22 | # Pattern to match Git command-line output for remotes => (user name, project name). 23 | P_GIT_REMOTE = re.compile(r'upstream\s+(?:https://|git@)github.com[:/]([^/]+)/([^.]+)(\.git)?\s+\(fetch\)') 24 | 25 | # Repository URL format string. 26 | F_REPO_URL = 'https://github.com/{0}/{1}/' 27 | 28 | # Pattern to match repository URLs => (user name, project name) 29 | P_REPO_URL = re.compile(r'https?://github\.com/([^.]+)/([^/]+)/?') 30 | 31 | # API URL format string. 32 | F_API_URL = 'https://api.github.com/repos/{0}/{1}/labels' 33 | 34 | # Expected labels and colors. 35 | EXPECTED = { 36 | 'help wanted': 'dcecc7', 37 | 'status:in progress': '9bcc65', 38 | 'status:changes requested': '679f38', 39 | 'status:wait': 'fff2df', 40 | 'status:refer to cac': 'ffdfb2', 41 | 'status:need more info': 'ee6c00', 42 | 'status:blocked': 'e55100', 43 | 'status:out of scope': 'eeeeee', 44 | 'status:duplicate': 'bdbdbd', 45 | 'type:typo text': 'f8bad0', 46 | 'type:bug': 'eb3f79', 47 | 'type:formatting': 'ac1357', 48 | 'type:template and tools': '7985cb', 49 | 'type:instructor guide': '00887a', 50 | 'type:discussion': 'b2e5fc', 51 | 'type:enhancement': '7fdeea', 52 | 'type:clarification': '00acc0', 53 | 'type:teaching example': 'ced8dc', 54 | 'good first issue': 'ffeb3a', 55 | 'high priority': 'd22e2e' 56 | } 57 | 58 | 59 | def main(): 60 | """ 61 | Main driver. 62 | """ 63 | 64 | args = parse_args() 65 | reporter = Reporter() 66 | repo_url = get_repo_url(args.repo_url) 67 | check_labels(reporter, repo_url) 68 | reporter.report() 69 | 70 | 71 | def parse_args(): 72 | """ 73 | Parse command-line arguments. 74 | """ 75 | 76 | parser = ArgumentParser(description="""Check repository settings.""") 77 | parser.add_argument('-r', '--repo', 78 | default=None, 79 | dest='repo_url', 80 | help='repository URL') 81 | parser.add_argument('-s', '--source', 82 | default=os.curdir, 83 | dest='source_dir', 84 | help='source directory') 85 | 86 | args, extras = parser.parse_known_args() 87 | require(not extras, 88 | 'Unexpected trailing command-line arguments "{0}"'.format(extras)) 89 | 90 | return args 91 | 92 | 93 | def get_repo_url(repo_url): 94 | """ 95 | Figure out which repository to query. 96 | """ 97 | 98 | # Explicitly specified. 99 | if repo_url is not None: 100 | return repo_url 101 | 102 | # Guess. 103 | cmd = 'git remote -v' 104 | p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, 105 | close_fds=True, universal_newlines=True, encoding='utf-8') 106 | stdout_data, stderr_data = p.communicate() 107 | stdout_data = stdout_data.split('\n') 108 | matches = [P_GIT_REMOTE.match(line) for line in stdout_data] 109 | matches = [m for m in matches if m is not None] 110 | require(len(matches) == 1, 111 | 'Unexpected output from git remote command: "{0}"'.format(matches)) 112 | 113 | username = matches[0].group(1) 114 | require( 115 | username, 'empty username in git remote output {0}'.format(matches[0])) 116 | 117 | project_name = matches[0].group(2) 118 | require( 119 | username, 'empty project name in git remote output {0}'.format(matches[0])) 120 | 121 | url = F_REPO_URL.format(username, project_name) 122 | return url 123 | 124 | 125 | def check_labels(reporter, repo_url): 126 | """ 127 | Check labels in repository. 128 | """ 129 | 130 | actual = get_labels(repo_url) 131 | extra = set(actual.keys()) - set(EXPECTED.keys()) 132 | 133 | reporter.check(not extra, 134 | None, 135 | 'Extra label(s) in repository {0}: {1}', 136 | repo_url, ', '.join(sorted(extra))) 137 | 138 | missing = set(EXPECTED.keys()) - set(actual.keys()) 139 | reporter.check(not missing, 140 | None, 141 | 'Missing label(s) in repository {0}: {1}', 142 | repo_url, ', '.join(sorted(missing))) 143 | 144 | overlap = set(EXPECTED.keys()).intersection(set(actual.keys())) 145 | for name in sorted(overlap): 146 | reporter.check(EXPECTED[name].lower() == actual[name].lower(), 147 | None, 148 | 'Color mis-match for label {0} in {1}: expected {2}, found {3}', 149 | name, repo_url, EXPECTED[name], actual[name]) 150 | 151 | 152 | def get_labels(repo_url): 153 | """ 154 | Get actual labels from repository. 155 | """ 156 | 157 | m = P_REPO_URL.match(repo_url) 158 | require( 159 | m, 'repository URL {0} does not match expected pattern'.format(repo_url)) 160 | 161 | username = m.group(1) 162 | require(username, 'empty username in repository URL {0}'.format(repo_url)) 163 | 164 | project_name = m.group(2) 165 | require( 166 | username, 'empty project name in repository URL {0}'.format(repo_url)) 167 | 168 | url = F_API_URL.format(username, project_name) 169 | r = requests.get(url) 170 | require(r.status_code == 200, 171 | 'Request for {0} failed with {1}'.format(url, r.status_code)) 172 | 173 | result = {} 174 | for entry in r.json(): 175 | result[entry['name']] = entry['color'] 176 | return result 177 | 178 | 179 | if __name__ == '__main__': 180 | main() 181 | -------------------------------------------------------------------------------- /bin/run-make-docker-serve.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | 8 | bundle install 9 | bundle update 10 | exec bundle exec jekyll serve --host 0.0.0.0 11 | -------------------------------------------------------------------------------- /bin/test_lesson_check.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import lesson_check 4 | import util 5 | 6 | 7 | class TestFileList(unittest.TestCase): 8 | def setUp(self): 9 | self.reporter = util.Reporter() # TODO: refactor reporter class. 10 | 11 | def test_file_list_has_expected_entries(self): 12 | # For first pass, simply assume that all required files are present 13 | 14 | lesson_check.check_fileset('', self.reporter, lesson_check.REQUIRED_FILES) 15 | self.assertEqual(len(self.reporter.messages), 0) 16 | 17 | 18 | if __name__ == "__main__": 19 | unittest.main() 20 | -------------------------------------------------------------------------------- /bin/util.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import json 4 | from subprocess import Popen, PIPE 5 | 6 | # Import this way to produce a more useful error message. 7 | try: 8 | import yaml 9 | except ImportError: 10 | print('Unable to import YAML module: please install PyYAML', file=sys.stderr) 11 | sys.exit(1) 12 | 13 | 14 | # Things an image file's name can end with. 15 | IMAGE_FILE_SUFFIX = { 16 | '.gif', 17 | '.jpg', 18 | '.png', 19 | '.svg' 20 | } 21 | 22 | # Files that shouldn't be present. 23 | UNWANTED_FILES = [ 24 | '.nojekyll' 25 | ] 26 | 27 | # Marker to show that an expected value hasn't been provided. 28 | # (Can't use 'None' because that might be a legitimate value.) 29 | REPORTER_NOT_SET = [] 30 | 31 | 32 | class Reporter: 33 | """Collect and report errors.""" 34 | 35 | def __init__(self): 36 | """Constructor.""" 37 | self.messages = [] 38 | 39 | def check_field(self, filename, name, values, key, expected=REPORTER_NOT_SET): 40 | """Check that a dictionary has an expected value.""" 41 | 42 | if key not in values: 43 | self.add(filename, '{0} does not contain {1}', name, key) 44 | elif expected is REPORTER_NOT_SET: 45 | pass 46 | elif type(expected) in (tuple, set, list): 47 | if values[key] not in expected: 48 | self.add( 49 | filename, '{0} {1} value {2} is not in {3}', name, key, values[key], expected) 50 | elif values[key] != expected: 51 | self.add(filename, '{0} {1} is {2} not {3}', 52 | name, key, values[key], expected) 53 | 54 | def check(self, condition, location, fmt, *args): 55 | """Append error if condition not met.""" 56 | 57 | if not condition: 58 | self.add(location, fmt, *args) 59 | 60 | def add(self, location, fmt, *args): 61 | """Append error unilaterally.""" 62 | 63 | self.messages.append((location, fmt.format(*args))) 64 | 65 | @staticmethod 66 | def pretty(item): 67 | location, message = item 68 | if isinstance(location, type(None)): 69 | return message 70 | elif isinstance(location, str): 71 | return location + ': ' + message 72 | elif isinstance(location, tuple): 73 | return '{0}:{1}: '.format(*location) + message 74 | 75 | print('Unknown item "{0}"'.format(item), file=sys.stderr) 76 | return NotImplemented 77 | 78 | @staticmethod 79 | def key(item): 80 | location, message = item 81 | if isinstance(location, type(None)): 82 | return ('', -1, message) 83 | elif isinstance(location, str): 84 | return (location, -1, message) 85 | elif isinstance(location, tuple): 86 | return (location[0], location[1], message) 87 | 88 | print('Unknown item "{0}"'.format(item), file=sys.stderr) 89 | return NotImplemented 90 | 91 | def report(self, stream=sys.stdout): 92 | """Report all messages in order.""" 93 | 94 | if not self.messages: 95 | return 96 | 97 | for m in sorted(self.messages, key=self.key): 98 | print(self.pretty(m), file=stream) 99 | 100 | 101 | def read_markdown(parser, path): 102 | """ 103 | Get YAML and AST for Markdown file, returning 104 | {'metadata':yaml, 'metadata_len':N, 'text':text, 'lines':[(i, line, len)], 'doc':doc}. 105 | """ 106 | 107 | # Split and extract YAML (if present). 108 | with open(path, 'r', encoding='utf-8') as reader: 109 | body = reader.read() 110 | metadata_raw, metadata_yaml, body = split_metadata(path, body) 111 | 112 | # Split into lines. 113 | metadata_len = 0 if metadata_raw is None else metadata_raw.count('\n') 114 | lines = [(metadata_len+i+1, line, len(line)) 115 | for (i, line) in enumerate(body.split('\n'))] 116 | 117 | # Parse Markdown. 118 | cmd = 'ruby {0}'.format(parser) 119 | p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, 120 | close_fds=True, universal_newlines=True, encoding='utf-8') 121 | stdout_data, stderr_data = p.communicate(body) 122 | doc = json.loads(stdout_data) 123 | 124 | return { 125 | 'metadata': metadata_yaml, 126 | 'metadata_len': metadata_len, 127 | 'text': body, 128 | 'lines': lines, 129 | 'doc': doc 130 | } 131 | 132 | 133 | def split_metadata(path, text): 134 | """ 135 | Get raw (text) metadata, metadata as YAML, and rest of body. 136 | If no metadata, return (None, None, body). 137 | """ 138 | 139 | metadata_raw = None 140 | metadata_yaml = None 141 | 142 | pieces = text.split('---', 2) 143 | if len(pieces) == 3: 144 | metadata_raw = pieces[1] 145 | text = pieces[2] 146 | try: 147 | metadata_yaml = yaml.load(metadata_raw, Loader=yaml.SafeLoader) 148 | except yaml.YAMLError as e: 149 | print('Unable to parse YAML header in {0}:\n{1}'.format( 150 | path, e), file=sys.stderr) 151 | sys.exit(1) 152 | 153 | return metadata_raw, metadata_yaml, text 154 | 155 | 156 | def load_yaml(filename): 157 | """ 158 | Wrapper around YAML loading so that 'import yaml' is only needed 159 | in one file. 160 | """ 161 | 162 | try: 163 | with open(filename, 'r', encoding='utf-8') as reader: 164 | return yaml.load(reader, Loader=yaml.SafeLoader) 165 | except (yaml.YAMLError, IOError) as e: 166 | print('Unable to load YAML file {0}:\n{1}'.format( 167 | filename, e), file=sys.stderr) 168 | sys.exit(1) 169 | 170 | 171 | def check_unwanted_files(dir_path, reporter): 172 | """ 173 | Check that unwanted files are not present. 174 | """ 175 | 176 | for filename in UNWANTED_FILES: 177 | path = os.path.join(dir_path, filename) 178 | reporter.check(not os.path.exists(path), 179 | path, 180 | "Unwanted file found") 181 | 182 | 183 | def require(condition, message): 184 | """Fail if condition not met.""" 185 | 186 | if not condition: 187 | print(message, file=sys.stderr) 188 | sys.exit(1) 189 | -------------------------------------------------------------------------------- /code/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/code/.gitkeep -------------------------------------------------------------------------------- /data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/data/.gitkeep -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8 2 | 3 | WORKDIR /root 4 | 5 | SHELL [ "/bin/bash", "-c" ] 6 | 7 | # Install general dependencies 8 | RUN apt-get -qq -y update && \ 9 | apt-get -qq -y install apt-utils && \ 10 | apt-get -qq -y update && \ 11 | apt-get -qq -y install --no-install-recommends \ 12 | sudo \ 13 | curl \ 14 | wget \ 15 | vim \ 16 | emacs && \ 17 | apt-get -y autoclean && \ 18 | apt-get -y autoremove && \ 19 | rm -rf /var/lib/apt/lists/* 20 | 21 | RUN pip install --upgrade --no-cache-dir pip setuptools wheel && \ 22 | pip install --no-cache-dir -q \ 23 | scipy \ 24 | matplotlib \ 25 | jupyter \ 26 | jupyterlab 27 | 28 | # Have Jupyter notebooks launch without additional command line options 29 | RUN jupyter notebook --generate-config && \ 30 | sed -i -e "/allow_root/ a c.NotebookApp.allow_root = True" ~/.jupyter/jupyter_notebook_config.py && \ 31 | sed -i -e "/custom_display_url/ a c.NotebookApp.custom_display_url = \'http://localhost:8888\'" ~/.jupyter/jupyter_notebook_config.py && \ 32 | sed -i -e "/c.NotebookApp.ip/ a c.NotebookApp.ip = '0.0.0.0'" ~/.jupyter/jupyter_notebook_config.py && \ 33 | sed -i -e "/open_browser/ a c.NotebookApp.open_browser = False" ~/.jupyter/jupyter_notebook_config.py 34 | 35 | # Enable tab completion by uncommenting it from /etc/bash.bashrc 36 | # The relevant lines are those below the phrase "enable bash completion in interactive shells" 37 | RUN export SED_RANGE="$(($(sed -n '\|enable bash completion in interactive shells|=' /etc/bash.bashrc)+1)),$(($(sed -n '\|enable bash completion in interactive shells|=' /etc/bash.bashrc)+7))" && \ 38 | sed -i -e "${SED_RANGE}"' s/^#//' /etc/bash.bashrc && \ 39 | unset SED_RANGE 40 | 41 | # Create user "docker" with sudo powers 42 | RUN useradd -m docker && \ 43 | usermod -aG sudo docker && \ 44 | echo '%sudo ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers && \ 45 | cp /root/.bashrc /home/docker/ && \ 46 | cp -r /root/.jupyter /home/docker/ && \ 47 | mkdir /home/docker/data && \ 48 | chown -R --from=root docker /home/docker 49 | 50 | # Use C.UTF-8 locale to avoid issues with ASCII encoding 51 | ENV LC_ALL=C.UTF-8 52 | ENV LANG=C.UTF-8 53 | 54 | WORKDIR /home/docker/data 55 | ENV HOME /home/docker 56 | ENV USER docker 57 | USER docker 58 | ENV PATH /home/docker/.local/bin:$PATH 59 | # Avoid first use of sudo warning. c.f. https://askubuntu.com/a/22614/781671 60 | RUN touch $HOME/.sudo_as_admin_successful 61 | 62 | CMD [ "/bin/bash" ] 63 | -------------------------------------------------------------------------------- /fig/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/fig/.gitkeep -------------------------------------------------------------------------------- /files/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewfeickert/intro-to-docker/34f8f6436860e83f6f9e09da7c4a763520c46947/files/.gitkeep -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: lesson 3 | root: . # Is the only page that doesn't follow the pattern /:path/index.html 4 | permalink: index.html # Is the only page that doesn't follow the pattern /:path/index.html 5 | --- 6 | An opinionated introduction to using [Docker](https://www.docker.com/) as a software 7 | development tool. 8 | 9 |

10 | 11 | 12 | 13 |

14 | 15 | 16 | 17 | 18 | {% comment %} This is a comment in Liquid {% endcomment %} 19 | 20 | > ## Prerequisites 21 | > 22 | > A computer with Docker installed on it 23 | {: .prereq} 24 | 25 | {% include links.md %} 26 | -------------------------------------------------------------------------------- /reference.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: reference 3 | --- 4 | 5 | ## Glossary 6 | 7 | {% include links.md %} 8 | -------------------------------------------------------------------------------- /setup.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Setup 3 | --- 4 | To install Docker Community Edition on your Linux, Mac, or Windows machine follow the [instructions in the Docker docs](https://docs.docker.com/install/). 5 | 6 | 7 | {% include links.md %} 8 | --------------------------------------------------------------------------------