diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..1d7d564
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,25 @@
+root = true
+
+[*]
+charset = utf-8
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+indent_size = 2
+indent_style = space
+max_line_length = 100 # Please keep this in sync with bin/lesson_check.py!
+
+[*.r]
+max_line_length = 80
+
+[*.py]
+indent_size = 4
+indent_style = space
+max_line_length = 79
+
+[*.sh]
+end_of_line = lf
+
+[Makefile]
+indent_style = tab
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..2ee9d0e
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,2 @@
+github: [carpentries, swcarpentry, datacarpentry, librarycarpentry]
+custom: ["https://carpentries.wedid.it"]
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..077de4c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,21 @@
+
+Instructions
+
+Thanks for contributing! :heart:
+
+If this contribution is for instructor training, please email the link to this contribution to
+checkout@carpentries.org so we can record your progress. You've completed your contribution
+step for instructor checkout by submitting this contribution!
+
+If this issue is about a specific episode within a lesson, please provide its link or filename.
+
+Keep in mind that **lesson maintainers are volunteers** and it may take them some time to
+respond to your contribution. Although not all contributions can be incorporated into the lesson
+materials, we appreciate your time and effort to improve the curriculum. If you have any questions
+about the lesson maintenance process or would like to volunteer your time as a contribution
+reviewer, please contact The Carpentries Team at team@carpentries.org.
+
+You may delete these instructions from your comment.
+
+\- The Carpentries
+
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..07aadca
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,19 @@
+
+Instructions
+
+Thanks for contributing! :heart:
+
+If this contribution is for instructor training, please email the link to this contribution to
+checkout@carpentries.org so we can record your progress. You've completed your contribution
+step for instructor checkout by submitting this contribution!
+
+Keep in mind that **lesson maintainers are volunteers** and it may take them some time to
+respond to your contribution. Although not all contributions can be incorporated into the lesson
+materials, we appreciate your time and effort to improve the curriculum. If you have any questions
+about the lesson maintenance process or would like to volunteer your time as a contribution
+reviewer, please contact The Carpentries Team at team@carpentries.org.
+
+You may delete these instructions from your comment.
+
+\- The Carpentries
+
diff --git a/.github/workflows/template.yml b/.github/workflows/template.yml
new file mode 100644
index 0000000..80b49f6
--- /dev/null
+++ b/.github/workflows/template.yml
@@ -0,0 +1,131 @@
+name: Test template
+on:
+ push:
+ branches: gh-pages
+ pull_request:
+jobs:
+ check-template:
+ name: ${{ matrix.lesson-name }} (${{ matrix.os-name }})
+ if: github.repository == 'carpentries/styles'
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ lesson: [swcarpentry/shell-novice, datacarpentry/r-intro-geospatial, librarycarpentry/lc-git]
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ include:
+ - os: ubuntu-latest
+ os-name: Ubuntu
+ - os: macos-latest
+ os-name: macOS
+ - os: windows-latest
+ os-name: Windows
+ - lesson: swcarpentry/shell-novice
+ lesson-name: (SWC) Shell novice
+ - lesson: datacarpentry/r-intro-geospatial
+ lesson-name: (DC) R Intro Geospatial
+ - lesson: librarycarpentry/lc-git
+ lesson-name: (LC) Intro to Git
+ defaults:
+ run:
+ shell: bash # forces 'Git for Windows' on Windows
+ env:
+ RSPM: 'https://packagemanager.rstudio.com/cran/__linux__/bionic/latest'
+ steps:
+ - name: Set up Ruby
+ uses: actions/setup-ruby@main
+ with:
+ ruby-version: '2.7.1'
+
+ - name: Set up Python
+ uses: actions/setup-python@v2
+ with:
+ python-version: '3.x'
+
+ - name: Install GitHub Pages, Bundler, and kramdown gems
+ run: |
+ gem install github-pages bundler kramdown
+
+ - name: Install Python modules
+ run: |
+ if [[ $RUNNER_OS == macOS || $RUNNER_OS == Linux ]]; then
+ python3 -m pip install --upgrade pip setuptools wheel pyyaml==5.3.1 requests
+ elif [[ $RUNNER_OS == Windows ]]; then
+ python -m pip install --upgrade pip setuptools wheel pyyaml==5.3.1 requests
+ fi
+
+ - name: Checkout the ${{ matrix.lesson }} lesson
+ uses: actions/checkout@master
+ with:
+ repository: ${{ matrix.lesson }}
+ path: lesson
+ fetch-depth: 0
+
+ - name: Determine the proper reference to use
+ id: styles-ref
+ run: |
+ if [[ -n "${{ github.event.pull_request.number }}" ]]; then
+ echo "::set-output name=ref::refs/pull/${{ github.event.pull_request.number }}/head"
+ else
+ echo "::set-output name=ref::gh-pages"
+ fi
+
+ - name: Sync lesson with carpentries/styles
+ working-directory: lesson
+ run: |
+ git config --global user.email "team@carpentries.org"
+ git config --global user.name "The Carpentries Bot"
+ git remote add styles https://github.com/carpentries/styles.git
+ git config --local remote.styles.tagOpt --no-tags
+ git fetch styles ${{ steps.styles-ref.outputs.ref }}:styles-ref
+ git merge -s recursive -Xtheirs --no-commit styles-ref
+ git commit -m "Sync lesson with carpentries/styles"
+
+ - name: Look for R-markdown files
+ id: check-rmd
+ working-directory: lesson
+ run: |
+ echo "::set-output name=count::$(shopt -s nullglob; files=($(find . -iname '*.Rmd')); echo ${#files[@]})"
+
+ - name: Set up R
+ if: steps.check-rmd.outputs.count != 0
+ uses: r-lib/actions/setup-r@master
+ with:
+ r-version: 'release'
+
+ - name: Install needed packages
+ if: steps.check-rmd.outputs.count != 0
+ run: |
+ install.packages(c('remotes', 'rprojroot', 'renv', 'desc', 'rmarkdown', 'knitr'))
+ shell: Rscript {0}
+
+ - name: Query dependencies
+ if: steps.check-rmd.outputs.count != 0
+ working-directory: lesson
+ run: |
+ source('bin/dependencies.R')
+ deps <- identify_dependencies()
+ create_description(deps)
+ saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2)
+ writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version")
+ shell: Rscript {0}
+
+ - name: Cache R packages
+ if: runner.os != 'Windows' && steps.check-rmd.outputs.count != 0
+ uses: actions/cache@v1
+ with:
+ path: ${{ env.R_LIBS_USER }}
+ key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }}
+ restore-keys: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-
+
+ - name: Install system dependencies for R packages
+ if: runner.os == 'Linux' && steps.check-rmd.outputs.count != 0
+ working-directory: lesson
+ run: |
+ while read -r cmd
+ do
+ eval sudo $cmd
+ done < <(Rscript -e 'cat(remotes::system_requirements("ubuntu", "18.04"), sep = "\n")')
+
+ - run: make site
+ working-directory: lesson
diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml
new file mode 100644
index 0000000..dba10f2
--- /dev/null
+++ b/.github/workflows/website.yml
@@ -0,0 +1,85 @@
+name: Website
+on:
+ push:
+ branches: gh-pages
+ pull_request: []
+jobs:
+ build-website:
+ if: github.repository != 'carpentries/styles' && (github.repository_owner == 'swcarpentry' || github.repository_owner == 'datacarpentry' || github.repository_owner == 'librarycarpentry' || github.repository_owner == 'carpentries')
+ runs-on: ubuntu-latest
+ defaults:
+ run:
+ shell: bash
+ steps:
+ - name: Set up Ruby
+ uses: actions/setup-ruby@main
+ with:
+ ruby-version: '2.7.1'
+
+ - name: Set up Python
+ uses: actions/setup-python@v2
+ with:
+ python-version: '3.x'
+
+ - name: Install GitHub Pages, Bundler, and kramdown gems
+ run: |
+ gem install github-pages bundler kramdown
+
+ - name: Install Python modules
+ run: |
+ python3 -m pip install --upgrade pip setuptools wheel pyyaml==5.3.1 requests
+
+ - name: Checkout the lesson
+ uses: actions/checkout@master
+ with:
+ fetch-depth: 0
+ ref: ${{ github.event.pull_request.head.sha }}
+
+ - name: Look for R-markdown files
+ id: check-rmd
+ run: |
+ echo "::set-output name=count::$(shopt -s nullglob; files=($(find . -iname '*.Rmd')); echo ${#files[@]})"
+
+ - name: Set up R
+ if: steps.check-rmd.outputs.count != 0
+ uses: r-lib/actions/setup-r@master
+ with:
+ r-version: 'release'
+
+ - name: Install needed packages
+ if: steps.check-rmd.outputs.count != 0
+ run: |
+ install.packages(c('remotes', 'rprojroot', 'renv', 'desc', 'rmarkdown', 'knitr'))
+ shell: Rscript {0}
+
+ - name: Query dependencies
+ if: steps.check-rmd.outputs.count != 0
+ run: |
+ source('bin/dependencies.R')
+ deps <- identify_dependencies()
+ create_description(deps)
+ saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2)
+ writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version")
+ shell: Rscript {0}
+
+ - name: Cache R packages
+ if: steps.check-rmd.outputs.count != 0
+ uses: actions/cache@v1
+ with:
+ path: ${{ env.R_LIBS_USER }}
+ key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }}
+ restore-keys: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-
+
+ - name: Install system dependencies for R packages
+ if: steps.check-rmd.outputs.count != 0
+ run: |
+ while read -r cmd
+ do
+ eval sudo $cmd
+ done < <(Rscript -e 'cat(remotes::system_requirements("ubuntu", "18.04"), sep = "\n")')
+
+ - run: make site
+ - run: make lesson-check
+ if: always()
+ - run: make lesson-check-all
+ if: always()
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1a9eadf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,17 @@
+*.pyc
+*~
+.DS_Store
+.ipynb_checkpoints
+.sass-cache
+.jekyll-cache/
+__pycache__
+_site
+.Rproj.user
+.Rhistory
+.RData
+.bundle/
+.vendor/
+vendor/
+.docker-vendor/
+Gemfile.lock
+.*history
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..c3b9669
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,11 @@
+---
+layout: page
+title: "Contributor Code of Conduct"
+---
+As contributors and maintainers of this project,
+we pledge to follow the [Carpentry Code of Conduct][coc].
+
+Instances of abusive, harassing, or otherwise unacceptable behavior
+may be reported by following our [reporting guidelines][coc-reporting].
+
+{% include links.md %}
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..fb25ae4
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+source 'https://rubygems.org'
+
+git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
+
+# Synchronize with https://pages.github.com/versions
+ruby '>=2.5.8'
+
+gem 'github-pages', group: :jekyll_plugins
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..0e259d4
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+ HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display,
+ communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+ likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+ v. rights protecting the extraction, dissemination, use and reuse of data
+ in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation
+ thereof, including any amended or successor version of such
+ directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+ world based on applicable law or treaty, and any national
+ implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+ warranties of any kind concerning the Work, express, implied,
+ statutory or otherwise, including without limitation warranties of
+ title, merchantability, fitness for a particular purpose, non
+ infringement, or the absence of latent or other defects, accuracy, or
+ the present or absence of errors, whether or not discoverable, all to
+ the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without
+ limitation any person's Copyright and Related Rights in the Work.
+ Further, Affirmer disclaims responsibility for obtaining any necessary
+ consents, permissions or other rights required for any use of the
+ Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to
+ this CC0 or use of the Work.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..d35f08f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,162 @@
+## ========================================
+## Commands for both workshop and lesson websites.
+
+# Settings
+MAKEFILES=Makefile $(wildcard *.mk)
+JEKYLL=bundle config --local set path .vendor/bundle && bundle install && bundle update && bundle exec jekyll
+PARSER=bin/markdown_ast.rb
+DST=_site
+
+# Check Python 3 is installed and determine if it's called via python3 or python
+# (https://stackoverflow.com/a/4933395)
+PYTHON3_EXE := $(shell which python3 2>/dev/null)
+ifneq (, $(PYTHON3_EXE))
+ ifeq (,$(findstring Microsoft/WindowsApps/python3,$(subst \,/,$(PYTHON3_EXE))))
+ PYTHON := python3
+ endif
+endif
+
+ifeq (,$(PYTHON))
+ PYTHON_EXE := $(shell which python 2>/dev/null)
+ ifneq (, $(PYTHON_EXE))
+ PYTHON_VERSION_FULL := $(wordlist 2,4,$(subst ., ,$(shell python --version 2>&1)))
+ PYTHON_VERSION_MAJOR := $(word 1,${PYTHON_VERSION_FULL})
+ ifneq (3, ${PYTHON_VERSION_MAJOR})
+ $(error "Your system does not appear to have Python 3 installed.")
+ endif
+ PYTHON := python
+ else
+ $(error "Your system does not appear to have any Python installed.")
+ endif
+endif
+
+
+# Controls
+.PHONY : commands clean files
+
+# Default target
+.DEFAULT_GOAL := commands
+
+## I. Commands for both workshop and lesson websites
+## =================================================
+
+## * serve : render website and run a local server
+serve : lesson-md
+ ${JEKYLL} serve
+
+## * site : build website but do not run a server
+site : lesson-md
+ ${JEKYLL} build
+
+## * docker-serve : use Docker to serve the site
+docker-serve :
+ docker pull carpentries/lesson-docker:latest
+ docker run --rm -it \
+ -v $${PWD}:/home/rstudio \
+ -p 4000:4000 \
+ -p 8787:8787 \
+ -e USERID=$$(id -u) \
+ -e GROUPID=$$(id -g) \
+ carpentries/lesson-docker:latest
+
+## * repo-check : check repository settings
+repo-check :
+ @${PYTHON} bin/repo_check.py -s .
+
+## * clean : clean up junk files
+clean :
+ @rm -rf ${DST}
+ @rm -rf .sass-cache
+ @rm -rf bin/__pycache__
+ @find . -name .DS_Store -exec rm {} \;
+ @find . -name '*~' -exec rm {} \;
+ @find . -name '*.pyc' -exec rm {} \;
+
+## * clean-rmd : clean intermediate R files (that need to be committed to the repo)
+clean-rmd :
+ @rm -rf ${RMD_DST}
+ @rm -rf fig/rmd-*
+
+
+##
+## II. Commands specific to workshop websites
+## =================================================
+
+.PHONY : workshop-check
+
+## * workshop-check : check workshop homepage
+workshop-check :
+ @${PYTHON} bin/workshop_check.py .
+
+
+##
+## III. Commands specific to lesson websites
+## =================================================
+
+.PHONY : lesson-check lesson-md lesson-files lesson-fixme install-rmd-deps
+
+# RMarkdown files
+RMD_SRC = $(wildcard _episodes_rmd/??-*.Rmd)
+RMD_DST = $(patsubst _episodes_rmd/%.Rmd,_episodes/%.md,$(RMD_SRC))
+
+# Lesson source files in the order they appear in the navigation menu.
+MARKDOWN_SRC = \
+ index.md \
+ CODE_OF_CONDUCT.md \
+ setup.md \
+ $(sort $(wildcard _episodes/*.md)) \
+ reference.md \
+ $(sort $(wildcard _extras/*.md)) \
+ LICENSE.md
+
+# Generated lesson files in the order they appear in the navigation menu.
+HTML_DST = \
+ ${DST}/index.html \
+ ${DST}/conduct/index.html \
+ ${DST}/setup/index.html \
+ $(patsubst _episodes/%.md,${DST}/%/index.html,$(sort $(wildcard _episodes/*.md))) \
+ ${DST}/reference/index.html \
+ $(patsubst _extras/%.md,${DST}/%/index.html,$(sort $(wildcard _extras/*.md))) \
+ ${DST}/license/index.html
+
+## * install-rmd-deps : Install R packages dependencies to build the RMarkdown lesson
+install-rmd-deps:
+ @${SHELL} bin/install_r_deps.sh
+
+## * lesson-md : convert Rmarkdown files to markdown
+lesson-md : ${RMD_DST}
+
+_episodes/%.md: _episodes_rmd/%.Rmd install-rmd-deps
+ @mkdir -p _episodes
+ @bin/knit_lessons.sh $< $@
+
+## * lesson-check : validate lesson Markdown
+lesson-check : lesson-fixme
+ @${PYTHON} bin/lesson_check.py -s . -p ${PARSER} -r _includes/links.md
+
+## * lesson-check-all : validate lesson Markdown, checking line lengths and trailing whitespace
+lesson-check-all :
+ @${PYTHON} bin/lesson_check.py -s . -p ${PARSER} -r _includes/links.md -l -w --permissive
+
+## * unittest : run unit tests on checking tools
+unittest :
+ @${PYTHON} bin/test_lesson_check.py
+
+## * lesson-files : show expected names of generated files for debugging
+lesson-files :
+ @echo 'RMD_SRC:' ${RMD_SRC}
+ @echo 'RMD_DST:' ${RMD_DST}
+ @echo 'MARKDOWN_SRC:' ${MARKDOWN_SRC}
+ @echo 'HTML_DST:' ${HTML_DST}
+
+## * lesson-fixme : show FIXME markers embedded in source files
+lesson-fixme :
+ @grep --fixed-strings --word-regexp --line-number --no-messages FIXME ${MARKDOWN_SRC} || true
+
+##
+## IV. Auxililary (plumbing) commands
+## =================================================
+
+## * commands : show all commands.
+commands :
+ @sed -n -e '/^##/s|^##[[:space:]]*||p' $(MAKEFILE_LIST)
diff --git a/_config.yml b/_config.yml
new file mode 100644
index 0000000..5ba25f6
--- /dev/null
+++ b/_config.yml
@@ -0,0 +1,61 @@
+#------------------------------------------------------------
+# Values for this lesson.
+#------------------------------------------------------------
+
+title: "Lesson Template"
+
+#------------------------------------------------------------
+# Generic settings (should not need to change).
+#------------------------------------------------------------
+
+# What kind of thing is this ("workshop" or "lesson")?
+kind: "lesson"
+
+# Magic to make URLs resolve both locally and on GitHub.
+# See https://help.github.com/articles/repository-metadata-on-github-pages/.
+# Please don't change it: / is correct.
+repository: /
+
+# Email address, no mailto:
+# (Don't change this -- the contact address for your workshop will be set
+# in the index.md file)
+email: "team@carpentries.org"
+
+# Start time in minutes (0 to be clock-independent, 540 to show a start at 09:00 am).
+start_time: 0
+
+# Specify that things in the episodes collection should be output.
+collections:
+ episodes:
+ output: true
+ permalink: /:path/index.html
+ extras:
+ output: true
+ permalink: /:path/index.html
+
+# Set the default layout for things in the episodes collection.
+defaults:
+ - values:
+ root: .
+ layout: page
+ - scope:
+ path: ""
+ type: episodes
+ values:
+ root: ..
+ layout: episode
+ - scope:
+ path: ""
+ type: extras
+ values:
+ root: ..
+ layout: page
+
+# Files and directories that are not to be copied.
+exclude:
+ - Makefile
+ - bin/
+ - .Rproj.user/
+
+# Turn on built-in syntax highlighting.
+highlighter: green
diff --git a/_episodes/.gitkeep b/_episodes/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/_episodes_rmd/.gitkeep b/_episodes_rmd/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/_episodes_rmd/data/.gitkeep b/_episodes_rmd/data/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/_extras/.gitkeep b/_extras/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/_includes/aio-script.md b/_includes/aio-script.md
new file mode 100644
index 0000000..d90e6d6
--- /dev/null
+++ b/_includes/aio-script.md
@@ -0,0 +1,25 @@
+{% comment %}
+As a maintainer, you don't need to edit this file.
+If you notice that something doesn't work, please
+open an issue: https://github.com/carpentries/styles/issues/new
+{% endcomment %}
+
+{% include manual_episode_order.html %}
+
+{% for lesson_episode in lesson_episodes %}
+
+{% if site.episode_order %}
+ {% assign e = site.episodes | where: "slug", lesson_episode | first %}
+{% else %}
+ {% assign e = lesson_episode %}
+{% endif %}
+
+
{{ e.title }}
+
+{% include episode_overview.html teaching_time=e.teaching exercise_time=e.exercises episode_questions=e.questions episode_objectives=e.objectives %}
+
+{{ e.content }}
+
+{% include episode_keypoints.html episode_keypoints=e.keypoints %}
+
+{% endfor %}
diff --git a/_includes/all_keypoints.html b/_includes/all_keypoints.html
new file mode 100644
index 0000000..f0abd25
--- /dev/null
+++ b/_includes/all_keypoints.html
@@ -0,0 +1,31 @@
+{% comment %}
+ Display key points of all episodes for reference.
+{% endcomment %}
+
+{% include base_path.html %}
+{% include manual_episode_order.html %}
+
+
diff --git a/_includes/episode_navbar.html b/_includes/episode_navbar.html
new file mode 100644
index 0000000..5abf86f
--- /dev/null
+++ b/_includes/episode_navbar.html
@@ -0,0 +1,42 @@
+{% comment %}
+For some reason, the relative_root_path seems out of scope in this file, so we
+need to re-assign it here
+{% endcomment %}
+
+{% include base_path.html %}
+
+{% comment %}
+ Navigation bar for an episode.
+{% endcomment %}
+
+{% include manual_episode_order.html %}
+{% comment %}
+ 'previous_episode' and 'next_episodes' are defined in 'manual_episode_order.html'.
+ These replace 'page.previous' and 'page.next' objects, correspondingly.
+{% endcomment %}
+
+
diff --git a/_includes/episode_overview.html b/_includes/episode_overview.html
new file mode 100644
index 0000000..169ab26
--- /dev/null
+++ b/_includes/episode_overview.html
@@ -0,0 +1,76 @@
+{% comment %}
+ Display episode's timings and learning objectives.
+
+ Regarding the `if page.*** == nil` below:
+ all-in-one page combines all episodes into one.
+ It, therefore, does not define its own objectives, exercises,
+ and questions, which 'normal' episodes define in the front matter.
+
+ To display episodes' teaching and exercise times, as well as episode
+ questions and objectives, we pass them as parameters to the Liquid's
+ `include` statement when we generate the page:
+
+ include episode_overview.html teaching_time=e.teaching ...
+
+ Here we obtain the information we need either from the episode itself or
+ from the parameters passed in.
+{% endcomment %}
+
+{% if page.teaching == nil %}
+{% assign teaching_time = include.teaching_time %}
+{% else %}
+{% assign teaching_time = page.teaching %}
+{% endif %}
+
+{% if page.exercises == nil %}
+{% assign exercise_time = include.exercise_time %}
+{% else %}
+{% assign exercise_time = page.exercises %}
+{% endif %}
+
+{% if page.questions == nil %}
+{% assign episode_questions = include.episode_questions %}
+{% else %}
+{% assign episode_questions = page.questions %}
+{% endif %}
+
+{% if page.objectives == nil %}
+{% assign episode_objectives = include.episode_objectives %}
+{% else %}
+{% assign episode_objectives = page.objectives %}
+{% endif %}
+
+
+
+
Overview
+
+
+
+ Teaching: {{ teaching_time }} min
+
+ Exercises: {{ exercise_time }} min
+
+
+ Questions
+
+ {% for question in episode_questions %}
+
{{ question|markdownify }}
+ {% endfor %}
+
+
+
+
+
+
+
+
+ Objectives
+
+ {% for objective in episode_objectives %}
+
{{ objective|markdownify }}
+ {% endfor %}
+
+
+
+
+
diff --git a/_includes/episode_title.html b/_includes/episode_title.html
new file mode 100644
index 0000000..d0abc65
--- /dev/null
+++ b/_includes/episode_title.html
@@ -0,0 +1,9 @@
+
+
+
+
+
{{ page.title }}
+
+
+
+
diff --git a/_includes/favicons.html b/_includes/favicons.html
new file mode 100644
index 0000000..8a50b4d
--- /dev/null
+++ b/_includes/favicons.html
@@ -0,0 +1,33 @@
+{% assign favicon_url = relative_root_path | append: '/assets/favicons/' | append: site.carpentry %}
+
+{% if site.carpentry == 'swc' %}
+{% assign carpentry = 'Software Carpentry' %}
+{% elsif site.carpentry == 'dc' %}
+{% assign carpentry = 'Data Carpentry' %}
+{% elsif site.carpentry == 'lc' %}
+{% assign carpentry = 'Library Carpentry' %}
+{% elsif site.carpentry == 'cp' %}
+{% assign carpentry = 'The Carpentries' %}
+{% endif %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/_includes/gh_variables.html b/_includes/gh_variables.html
new file mode 100644
index 0000000..3fdae4a
--- /dev/null
+++ b/_includes/gh_variables.html
@@ -0,0 +1,45 @@
+{% comment %}
+When rendering websites locally, `site.github.url` doesn't get resolved properly
+unless a GitHub Personal Access Token is set up and available in the
+environment. This leads to warnings and errors when trying to serve the site
+locally. To work around this, we use the `jekyll.environment` variable which is
+set to `development` when rendering the site locally, and set to `production` on
+GitHub where `site.github.url` is defined.
+{% endcomment %}
+
+{% if jekyll.environment == "production" %}
+
+{% comment %}
+First, get the name of the repository
+{% endcomment %}
+{% assign repo_name = site.github.repository_name %}
+
+{% comment %}
+`site.github.public_repositories` contains comprehensive information for all public repositories for the organization. We use `where` to extract the part
+of the metadata that is relevant to the present repository.
+{% endcomment %}
+{% assign repo_info = site.github.public_repositories | where: "name", repo_name %}
+
+{% comment %}
+Now, we can extract the default branch for the repo
+{% endcomment %}
+{% assign default_branch = repo_info[0].default_branch %}
+
+{% comment %}
+Other variables requested by the template
+{% endcomment %}
+{% assign repo_url = site.github.repository_url %}
+{% assign search_domain_url = site.github.url %}
+{% assign project_title = site.github.project_title %}
+{% assign source_branch = site.github.source.branch %}
+
+{% elsif jekyll.environment == "development" %}
+
+{% assign repo_name = "" %}
+{% assign repo_url = "" %}
+{% assign default_branch = "" %}
+{% assign search_domain_url = "" %}
+{% assign project_title = "" %}
+{% assign source_branch = "" %}
+
+{% endif %}
diff --git a/_includes/javascript.html b/_includes/javascript.html
new file mode 100644
index 0000000..fcc74e6
--- /dev/null
+++ b/_includes/javascript.html
@@ -0,0 +1,14 @@
+{% comment %}
+ JavaScript used in lesson and workshop pages.
+{% endcomment %}
+
+
+
+
diff --git a/_includes/lesson_footer.html b/_includes/lesson_footer.html
new file mode 100644
index 0000000..006f68a
--- /dev/null
+++ b/_includes/lesson_footer.html
@@ -0,0 +1,24 @@
+{% comment %}
+ Footer for lesson pages.
+{% endcomment %}
+
+{% include gh_variables.html %}
+
+
diff --git a/_includes/life_cycle.html b/_includes/life_cycle.html
new file mode 100644
index 0000000..efc5b6d
--- /dev/null
+++ b/_includes/life_cycle.html
@@ -0,0 +1,34 @@
+
+{% if site.life_cycle == "pre-alpha" %}
+
+
+
+ This lesson is still being designed and assembled (Pre-Alpha version)
+
+
+
+
+{% elsif site.life_cycle == "alpha" %}
+
+
+
+ This lesson is in the early stages of development (Alpha version)
+
+
+{% endif %}
diff --git a/_includes/manual_episode_order.html b/_includes/manual_episode_order.html
new file mode 100644
index 0000000..7908627
--- /dev/null
+++ b/_includes/manual_episode_order.html
@@ -0,0 +1,116 @@
+{% comment %}
+ This file enables manual episode ordering until
+ GitHub Pages switches to Jekyll that supports it
+ without any major hackery. Note, some logic will
+ be required even when this transition happens
+ but it won't be as involved as what we have to do
+ in this file.
+
+ To order lesson episodes or extras manually
+ (instead of the default alpha-numerical order),
+ create array variables 'episode_order' and
+ 'extras_order' in `_config.yml` like so:
+
+ episode_order:
+ - episodeA
+ - episodeB
+
+ extras_order:
+ - extraA
+ - extraB
+
+ Note that "Reference" page is currently always
+ added to "Extras" as the first item.
+
+ The main outcomes of the code in this file are:
+ - 'lesson_episodes' variable that replaces
+ 'site.episodes' variable when manual episode
+ order is defined.
+ - 'lesson_extras' variable that replaces
+ 'site.extras' variable when manual ordering of
+ files in '_extras' is used
+ - 'previous_episode' and 'next_episode' objects
+ that replace 'page.previous' and 'page.next' variables,
+ correspondingly, and that have such properties
+ as 'url' and 'title' and that are used in
+ 'episode_navbar.html'.
+
+ When episode order is specified manually, the 'lesson_episodes'
+ variable contains a list of episode names ("slugs", to be precise;
+ "slug" is the episode name without '.md'). Therefore, when we
+ iterate over 'lesson_episodes' (in syllabus.html and navbar.html) ,
+ we have to check whether we use manual episode ordering and, if so,
+ find the corresponding episode object. This is what we do with the
+ following code in every loop over 'lesson_episodes':
+
+ {% if site.episode_order %}
+ {% assign episode = site.episodes | where: "slug", lesson_episode | first %}
+ {% else %}
+ {% assign episode = lesson_episode %}
+ {% endif %}
+{% endcomment %}
+
+{% comment %}
+Manual ordering of Episodes begins here
+{% endcomment %}
+
+{% if site.episode_order %}
+ {% assign lesson_episodes = site.episode_order %}
+{% else %}
+ {% assign lesson_episodes = site.episodes %}
+{% endif %}
+
+
+{% comment %}
+ If 'episode_order' is defined, we need to determine
+ - previous episode object ('previous_episode')
+ - and next episode object ('next_episode')
+{% endcomment %}
+
+
+{% if site.episode_order %}
+ {% for lesson_episode in lesson_episodes %}
+
+ {% comment %}
+ We iterate over the specified lesson episodes using
+ a 'for' loop because we can use
+ 'forloop.first', 'forloop.last', and 'forloop.index0'.
+ {% endcomment %}
+
+ {% unless lesson_episode == page.slug %} {% continue %} {% endunless %}
+
+ {% if forloop.first %}
+ {% assign previous_episode = nil %}
+ {% else %}
+ {% assign p_idx = forloop.index0 | minus: 1 %}
+ {% assign p_name = lesson_episodes[p_idx] %}
+ {% assign previous_episode = site.episodes | where: "slug", p_name | first %}
+ {% endif %}
+
+ {% if forloop.last == true %}
+ {% assign next_episode = nil %}
+ {% else %}
+ {% assign n_idx = forloop.index0 | plus: 1 %}
+ {% assign n_name = lesson_episodes[n_idx] %}
+ {% assign next_episode = site.episodes | where: "slug", n_name | first %}
+ {% endif %}
+ {% endfor %}
+{% else %}
+ {% assign previous_episode = page.previous %}
+ {% assign next_episode = page.next %}
+{% endif %}
+
+
+{% comment %}
+Manual ordering of Extras begins here
+{% endcomment %}
+
+{% if site.extras_order %}
+ {% assign lesson_extras = site.extras_order %}
+{% else %}
+ {% assign lesson_extras = site.extras %}
+{% endif %}
+
+{% comment %}
+ We do not need to determine "previous" or "next" extra.
+{% endcomment %}
diff --git a/_includes/navbar.html b/_includes/navbar.html
new file mode 100644
index 0000000..acff12b
--- /dev/null
+++ b/_includes/navbar.html
@@ -0,0 +1,82 @@
+{% comment %}
+ Lesson navigation bar.
+{% endcomment %}
+
+{% include gh_variables.html %}
+{% include manual_episode_order.html %}
+
+
diff --git a/_includes/syllabus.html b/_includes/syllabus.html
new file mode 100644
index 0000000..6f11346
--- /dev/null
+++ b/_includes/syllabus.html
@@ -0,0 +1,93 @@
+{% include base_path.html %}
+
+{% comment %}
+ Display syllabus in tabular form.
+ Days are displayed if at least one episode has 'start = true'.
+{% endcomment %}
+
+{% include manual_episode_order.html %}
+
+
{% if hours < 10 %}0{% endif %}{{ hours }}:{% if minutes < 10 %}0{% endif %}{{ minutes }}
+
Finish
+
+
+
+
+
+ The actual schedule may vary slightly depending on the topics and exercises chosen by the instructor.
+
+
+
diff --git a/_includes/workshop_ad.html b/_includes/workshop_ad.html
new file mode 100644
index 0000000..e8e72e3
--- /dev/null
+++ b/_includes/workshop_ad.html
@@ -0,0 +1,32 @@
+{% comment %}
+ Advertising box at the top of a workshop website home page.
+{% endcomment %}
+
+
+
+
{{page.venue}}
+
+
+
{{page.humandate}}
+
{% if page.humantime %}{{page.humantime}}{% endif %}
+
+
+
+ Instructors:
+ {% if page.instructor %}
+ {{page.instructor | join: ', ' %}}
+ {% else %}
+ to be announced.
+ {% endif %}
+
+ {% if page.helper %}
+
+ Helpers:
+ {{page.helper | join: ', ' %}}
+
+ {% endif %}
+
+
+
+
+
diff --git a/_includes/workshop_calendar.html b/_includes/workshop_calendar.html
new file mode 100644
index 0000000..f88327d
--- /dev/null
+++ b/_includes/workshop_calendar.html
@@ -0,0 +1 @@
+Add to your Google Calendar.
diff --git a/_includes/workshop_footer.html b/_includes/workshop_footer.html
new file mode 100644
index 0000000..5390ac1
--- /dev/null
+++ b/_includes/workshop_footer.html
@@ -0,0 +1,26 @@
+{% comment %}
+ Footer for a standard workshop.
+{% endcomment %}
+
diff --git a/_layouts/base.html b/_layouts/base.html
new file mode 100644
index 0000000..87fc39f
--- /dev/null
+++ b/_layouts/base.html
@@ -0,0 +1,57 @@
+---
+---
+{% include base_path.html %}
+{% include gh_variables.html %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% include favicons.html %}
+
+
+
+
+
+
+ {% if page.title %}{{ page.title }}{% endif %}{% if page.title and site.title %} – {% endif %}{% if site.title %}{{ site.title }}{% endif %}
+
+
+
+
+
+
+
+