diff --git a/.github/workflows/template.yml b/.github/workflows/template.yml new file mode 100644 index 0000000..3e91db6 --- /dev/null +++ b/.github/workflows/template.yml @@ -0,0 +1,182 @@ +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 }} + continue-on-error: ${{ matrix.experimental }} + strategy: + fail-fast: false + matrix: + lesson: [swcarpentry/shell-novice, datacarpentry/r-intro-geospatial, librarycarpentry/lc-git] + os: [ubuntu-20.04, macos-latest, windows-latest] + experimental: [false] + include: + - os: ubuntu-20.04 + os-name: Linux + - 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 + - lesson: datacarpentry/astronomy-python + lesson-name: (DC) Foundations of Astronomical Data Science + experimental: true + os: ubuntu-20.04 + os-name: Linux + - lesson: carpentries/lesson-example + lesson-name: (CP) Lesson Example + experimental: false + os: ubuntu-20.04 + os-name: Linux + defaults: + run: + shell: bash # forces 'Git for Windows' on Windows + env: + RSPM: 'https://packagemanager.rstudio.com/cran/__linux__/focal/latest' + steps: + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '2.7' + bundler-cache: true + + - 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 kramdown-parser-gfm + + - 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: Sync lesson with carpentries/styles + working-directory: lesson + run: | + echo "::group::Fetch Styles" + if [[ -n "${{ github.event.pull_request.number }}" ]] + then + ref="refs/pull/${{ github.event.pull_request.number }}/head" + else + ref="gh-pages" + fi + + 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 fetch styles $ref:styles-ref + echo "::endgroup::" + echo "::group::Synchronize Styles" + # Sync up only if necessary + if [[ $(git rev-list --count HEAD..styles-ref) != 0 ]] + then + + # The merge command below might fail for lessons that use remote theme + # https://github.com/carpentries/carpentries-theme + echo "Testing merge using recursive strategy, accepting upstream changes without committing" + if ! git merge -s recursive -Xtheirs --no-commit styles-ref + then + + # Remove "deleted by us, unmerged" files from the staging area. + # these are the files that were removed from the lesson + # but are still present in the carpentries/styles repo + echo "Removing previously deleted files" + git rm $(git diff --name-only --diff-filter=DU) + + # If there are still "unmerged" files, + # let's raise an error and look into this more closely + if [[ -n $(git diff --name-only --diff-filter=U) ]] + then + echo "There were unmerged files in ${{ matrix.lesson-name }}:" + echo "$(git diff --compact-summary --diff-filter=U)" + exit 1 + fi + fi + + echo "Committing changes" + git commit -m "Sync lesson with carpentries/styles" + fi + echo "::endgroup::" + + - 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: | + packages = setdiff(c('remotes', 'rprojroot', 'renv', 'desc', 'rmarkdown', 'knitr'), rownames(installed.packages())) + install.packages(packages, repo="https://cran.rstudio.com/") + 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 stringi from source + if: runner.os == 'Linux' && steps.check-rmd.outputs.count != 0 + run: install.packages('stringi', repos='https://cloud.r-project.org') + shell: Rscript {0} + + - 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", "20.04"), sep = "\n")') + + - run: make site + working-directory: lesson + + - run: make lesson-check-all + working-directory: lesson diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml new file mode 100644 index 0000000..246d4c7 --- /dev/null +++ b/.github/workflows/website.yml @@ -0,0 +1,121 @@ +name: Website +on: + push: + branches: + - gh-pages + - main + pull_request: [] +jobs: + build-website: + if: ${{ !endsWith(github.repository, '/styles') }} + runs-on: ubuntu-20.04 + env: + RSPM: "https://packagemanager.rstudio.com/cran/__linux__/focal/latest" + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + defaults: + run: + shell: bash + steps: + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '2.7' + bundler-cache: true + + - 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 kramdown-parser-gfm + + - 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: 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 needed packages + if: steps.check-rmd.outputs.count != 0 + run: | + packages = setdiff(c('remotes', 'rprojroot', 'renv', 'desc', 'rmarkdown', 'knitr'), rownames(installed.packages())) + install.packages(packages, repo="https://cran.rstudio.com/") + 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: 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", "20.04"), sep = "\n")') + + - name: Render the markdown and confirm that the site can be built + run: make site + + - name: Checkout github pages + if: ${{ github.event_name == 'push' && steps.check-rmd.outputs.count != 0 && github.ref != 'refs/heads/gh-pages'}} + uses: actions/checkout@master + with: + ref: gh-pages + path: gh-pages + + - name: Commit and Push + if: ${{ github.event_name == 'push' && steps.check-rmd.outputs.count != 0 && github.ref != 'refs/heads/gh-pages'}} + run: | + # clean up gh-pages + cd gh-pages + git rm -rf . # remove all previous files + git restore --staged . # remove things from the stage + cd .. + # copy everything into gh-pages site + cp -r `ls -A | grep -v 'gh-pages' | grep -v '.git' | grep -v '.bundle/' | grep -v '_site'` gh-pages + # move into gh-pages, add, commit, and push + cd gh-pages + # setup git + git config --local user.email "actions@github.com" + git config --local user.name "GitHub Actions" + git add -A . + git commit --allow-empty -m "[Github Actions] render website (via ${{ github.sha }})" + git push origin gh-pages + # return + cd .. + + - run: make lesson-check-all + if: always() diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index c3b9669..2cf1133 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -8,4 +8,5 @@ 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 %} +[coc]: https://docs.carpentries.org/topic_folders/policies/code-of-conduct.html +[coc-reporting]: https://docs.carpentries.org/topic_folders/policies/incident-reporting.html diff --git a/Gemfile b/Gemfile index 4fa94a7..13525ab 100644 --- a/Gemfile +++ b/Gemfile @@ -10,5 +10,5 @@ ruby '>=2.7.1' gem 'github-pages', group: :jekyll_plugins if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.0.0') - gem 'webrick', '>=1.6.1' -end \ No newline at end of file + gem 'webrick', '>= 1.6.1' +end diff --git a/Makefile b/Makefile index 116e3ae..93cad7d 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ export SHELL = /bin/bash # Settings MAKEFILES=Makefile $(wildcard *.mk) -JEKYLL=bundle config --local set path .vendor/bundle && bundle install && bundle update && bundle exec jekyll +JEKYLL=bundle config set --local path .vendor/bundle && bundle install && bundle update && bundle exec jekyll PARSER=bin/markdown_ast.rb DST=_site @@ -15,7 +15,7 @@ DST=_site PYTHON3_EXE := $(shell which python3 2>/dev/null) ifneq (, $(PYTHON3_EXE)) ifeq (,$(findstring Microsoft/WindowsApps/python3,$(subst \,/,$(PYTHON3_EXE)))) - PYTHON := python3 + PYTHON := $(PYTHON3_EXE) endif endif @@ -24,25 +24,25 @@ ifeq (,$(PYTHON)) 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.") + ifeq (3, ${PYTHON_VERSION_MAJOR}) + PYTHON := $(PYTHON_EXE) + else + PYTHON_NOTE = "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.") + PYTHON_NOTE = "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 ## ================================================= +.PHONY: site docker-serve repo-check clean clean-rmd + ## * serve : render website and run a local server serve : lesson-md ${JEKYLL} serve --livereload @@ -53,8 +53,8 @@ site : lesson-md ## * docker-serve : use Docker to serve the site docker-serve : - docker pull carpentries/lesson-docker:latest - docker run --rm -it \ + @docker pull carpentries/lesson-docker:latest + @docker run --rm -it \ -v $${PWD}:/home/rstudio \ -p 4000:4000 \ -p 8787:8787 \ @@ -63,7 +63,7 @@ docker-serve : carpentries/lesson-docker:latest ## * repo-check : check repository settings -repo-check : +repo-check : python @${PYTHON} bin/repo_check.py -s . ## * clean : clean up junk files @@ -71,6 +71,9 @@ clean : @rm -rf ${DST} @rm -rf .sass-cache @rm -rf bin/__pycache__ + @rm -rf .vendor + @rm -rf .bundle + @rm -f Gemfile.lock @find . -name .DS_Store -exec rm {} \; @find . -name '*~' -exec rm {} \; @find . -name '*.pyc' -exec rm {} \; @@ -88,7 +91,7 @@ clean-rmd : .PHONY : workshop-check ## * workshop-check : check workshop homepage -workshop-check : +workshop-check : python @${PYTHON} bin/workshop_check.py . @@ -125,19 +128,20 @@ HTML_DST = \ ## * lesson-md : convert Rmarkdown files to markdown lesson-md : ${RMD_DST} -_episodes/%.md: _episodes_rmd/%.Rmd - @bin/knit_lessons.sh $< $@ +_episodes/%.md: _episodes_rmd/%.Rmd install-rmd-deps + @mkdir -p _episodes + @$(SHELL) bin/knit_lessons.sh $< $@ -# * lesson-check : validate lesson Markdown -lesson-check : lesson-fixme +## * lesson-check : validate lesson Markdown +lesson-check : python 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 : +lesson-check-all : python @${PYTHON} bin/lesson_check.py -s . -p ${PARSER} -r _includes/links.md -l -w --permissive ## * unittest : run unit tests on checking tools -unittest : +unittest : python @${PYTHON} bin/test_lesson_check.py ## * lesson-files : show expected names of generated files for debugging @@ -155,6 +159,15 @@ lesson-fixme : ## IV. Auxililary (plumbing) commands ## ================================================= +.PHONY : commands python + ## * commands : show all commands. commands : - @sed -n -e '/^##/s|^##[[:space:]]*||p' $(MAKEFILE_LIST) \ No newline at end of file + @sed -n -e '/^##/s|^##[[:space:]]*||p' $(MAKEFILE_LIST) + +python : +ifeq (, $(PYTHON)) + $(error $(PYTHON_NOTE)) +else + @: +endif diff --git a/bin/boilerplate/CONTRIBUTING.md b/bin/boilerplate/CONTRIBUTING.md index f5158b0..a997b35 100644 --- a/bin/boilerplate/CONTRIBUTING.md +++ b/bin/boilerplate/CONTRIBUTING.md @@ -70,7 +70,7 @@ There are many ways to contribute, from writing new exercises and improving existing ones to updating or filling in the documentation and submitting [bug reports][issues] -about things that do not work, aren not clear, or are missing. +about things that do not work, are not clear, or are missing. If you are looking for ideas, please see the 'Issues' tab for a list of issues associated with this repository, or you may also look at the issues for [Data Carpentry][dc-issues], @@ -94,7 +94,7 @@ and (b) explain what you would take out to make room for it. The first encourages contributors to be honest about requirements; the second, to think hard about priorities. -We are also not looking for exercises or other material that only run on one platform. +We are also not looking for exercises or other material that will only run on one platform. Our workshops typically contain a mixture of Windows, macOS, and Linux users; in order to be usable, our lessons must run equally well on all three. @@ -104,7 +104,7 @@ our lessons must run equally well on all three. If you choose to contribute via GitHub, you may want to look at [How to Contribute to an Open Source Project on GitHub][how-contribute]. To manage changes, we follow [GitHub flow][github-flow]. -Each lesson has two maintainers who review issues and pull requests or encourage others to do so. +Each lesson has at least two maintainers who review issues and pull requests or encourage others to do so. The maintainers are community volunteers and have final say over what gets merged into the lesson. To use the web interface for contributing to a lesson: @@ -128,12 +128,12 @@ repository for reference while revising. ## Other Resources -General discussion of [Software Carpentry][swc-site] and [Data Carpentry][dc-site] +General discussion of [Software Carpentry][swc-site], [Data Carpentry][dc-site], and [Library Carpentry][lc-site] happens on the [discussion mailing list][discuss-list], which everyone is welcome to join. You can also [reach us by email][email]. -[email]: mailto:admin@software-carpentry.org +[email]: mailto:team@carpentries.org [dc-issues]: https://github.com/issues?q=user%3Adatacarpentry [dc-lessons]: http://datacarpentry.org/lessons/ [dc-site]: http://datacarpentry.org/ diff --git a/bin/dependencies.R b/bin/dependencies.R index 640b3e2..ee9d38a 100644 --- a/bin/dependencies.R +++ b/bin/dependencies.R @@ -5,10 +5,10 @@ install_required_packages <- function(lib = NULL, repos = getOption("repos", def } message("lib paths: ", paste(lib, collapse = ", ")) - missing_pkgs <- setdiff( - c("rprojroot", "desc", "remotes", "renv"), - rownames(installed.packages(lib.loc = lib)) - ) + required_pkgs <- c("rprojroot", "desc", "remotes", "renv") + installed_pkgs <- rownames(installed.packages(lib.loc = lib)) + missing_pkgs <- setdiff(required_pkgs, installed_pkgs) + # The default installation of R will have "@CRAN@" as the default repository, which directs contrib.url() to either # force the user to choose a mirror if interactive or fail if not. Since we are not interactve, we need to force the # mirror here. @@ -16,8 +16,9 @@ install_required_packages <- function(lib = NULL, repos = getOption("repos", def repos <- c(CRAN = "https://cran.rstudio.com/") } - install.packages(missing_pkgs, lib = lib, repos = repos) - + if (length(missing_pkgs) != 0) { + install.packages(missing_pkgs, lib = lib, repos = repos) + } } find_root <- function() { diff --git a/bin/lesson_check.py b/bin/lesson_check.py index 25388d3..8416fa8 100644 --- a/bin/lesson_check.py +++ b/bin/lesson_check.py @@ -56,6 +56,18 @@ # Pattern to match {% include ... %} statements P_INTERNAL_INCLUDE_LINK = re.compile(r'^{% include ([^ ]*) %}$') +# Pattern to match image-only and link-only lines +P_LINK_IMAGE_LINE = re.compile(r''' + [> #]* # any number of '>', '#', and spaces + !? # ! or nothing + \[[^]]+\] # [any text] + [([] # ( or [ + [^])]+ # 1+ characters that are neither ] nor ) + [])] # ] or ) + (?:{:[^}]+})? # {:any text} or nothing + [ ]* # any number of spaces + \\?$ # \ or nothing + end of line''', re.VERBOSE) + # What kinds of blockquotes are allowed? KNOWN_BLOCKQUOTES = { 'callout', @@ -113,7 +125,10 @@ def main(): if life_cycle == "pre-alpha": args.permissive = True check_source_rmd(args.reporter, args.source_dir, args.parser) - args.references = read_references(args.reporter, args.reference_path) + + args.references = {} + if not using_remote_theme(args.source_dir): + args.references = read_references(args.reporter, args.reference_path) docs = read_all_markdown(args.source_dir, args.parser) check_fileset(args.source_dir, args.reporter, list(docs.keys())) @@ -167,6 +182,10 @@ def parse_args(): return args +def using_remote_theme(source_dir): + config_file = os.path.join(source_dir, '_config.yml') + config = load_yaml(config_file) + return 'remote_theme' in config def check_config(reporter, source_dir): """Check configuration file.""" @@ -188,6 +207,8 @@ def check_config(reporter, source_dir): reporter.check(defaults in config.get('defaults', []), 'configuration', '"root" not set to "." in configuration') + if 'life_cycle' not in config: + config['life_cycle'] = None return config['life_cycle'] def check_source_rmd(reporter, source_dir, parser): @@ -218,7 +239,17 @@ def read_references(reporter, ref_path): with open(ref_path, 'r', encoding='utf-8') as reader: for (num, line) in enumerate(reader, 1): - if P_INTERNAL_INCLUDE_LINK.search(line): continue + # Skip empty lines + if len(line.strip()) == 0: + continue + + # Skip HTML comments + if line.strip().startswith(""): + continue + + # Skip Liquid's {% include ... %} lines + if P_INTERNAL_INCLUDE_LINK.search(line): + continue m = P_INTERNAL_LINK_DEF.search(line) @@ -357,12 +388,19 @@ def check_line_lengths(self): """Check the raw text of the lesson body.""" if self.args.line_lengths: - over = [i for (i, l, n) in self.lines if ( - n > MAX_LINE_LEN) and (not l.startswith('!'))] - self.reporter.check(not over, + over_limit = [] + + for (i, l, n) in self.lines: + # Report lines that are longer than the suggested + # line length limit only if they're not + # link-only or image-only lines. + if n > MAX_LINE_LEN and not P_LINK_IMAGE_LINE.match(l): + over_limit.append(i) + + self.reporter.check(not over_limit, self.filename, 'Line(s) too long: {0}', - ', '.join([str(i) for i in over])) + ', '.join([str(i) for i in over_limit])) def check_trailing_whitespace(self): """Check for whitespace at the ends of lines.""" @@ -390,7 +428,8 @@ def check_codeblock_classes(self): for node in self.find_all(self.doc, {'type': 'codeblock'}): cls = self.get_val(node, 'attr', 'class') - self.reporter.check(cls in KNOWN_CODEBLOCKS or cls.startswith('language-'), + self.reporter.check(cls is not None and (cls in KNOWN_CODEBLOCKS or + cls.startswith('language-')), (self.filename, self.get_loc(node)), 'Unknown or missing code block type {0}', cls) @@ -490,7 +529,8 @@ def check(self): """Run extra tests.""" super().check() - self.check_reference_inclusion() + if not using_remote_theme(args.source_dir): + self.check_reference_inclusion() def check_metadata(self): super().check_metadata() diff --git a/bin/markdown_ast.rb b/bin/markdown_ast.rb index 4fdb684..2ef3f77 100755 --- a/bin/markdown_ast.rb +++ b/bin/markdown_ast.rb @@ -4,9 +4,10 @@ # Use Kramdown parser to produce AST for Markdown document. require 'kramdown' +require 'kramdown-parser-gfm' require 'json' markdown = $stdin.read -doc = Kramdown::Document.new(markdown) +doc = Kramdown::Document.new(markdown, input: 'GFM', hard_wrap: false) tree = doc.to_hash_a_s_t puts JSON.pretty_generate(tree) diff --git a/bin/util.py b/bin/util.py index 0e16d86..ed76084 100644 --- a/bin/util.py +++ b/bin/util.py @@ -11,14 +11,6 @@ sys.exit(1) -# Things an image file's name can end with. -IMAGE_FILE_SUFFIX = { - '.gif', - '.jpg', - '.png', - '.svg' -} - # Files that shouldn't be present. UNWANTED_FILES = [ '.nojekyll' @@ -115,7 +107,7 @@ def read_markdown(parser, path): for (i, line) in enumerate(body.split('\n'))] # Parse Markdown. - cmd = 'ruby {0}'.format(parser) + cmd = 'bundle exec ruby {0}'.format(parser) p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, close_fds=True, universal_newlines=True, encoding='utf-8') stdout_data, stderr_data = p.communicate(body) diff --git a/bin/workshop_check.py b/bin/workshop_check.py index 15d954a..2c8dddf 100644 --- a/bin/workshop_check.py +++ b/bin/workshop_check.py @@ -17,7 +17,7 @@ # Defaults. CARPENTRIES = ("dc", "swc", "lc", "cp") -DEFAULT_CONTACT_EMAIL = 'admin@software-carpentry.org' +DEFAULT_CONTACT_EMAIL = 'team@carpentries.org' USAGE = 'Usage: "workshop_check.py path/to/root/directory"'