#!/usr/bin/env bash ### TODO: ensure files deleted in the maintainer repository are ### also deleted in the students repository ### WARN: this script will not work unless the remote name is `origin` (default). ### WARN: this script expects you to connect to github via ssh. ### ensure you registered your public key. # constant definitions # WARN: make sure you understand the implications before # you change the value (or name) of a constant! required_tools=( "cat" "dirname" "find" "git" "grep" "pandoc" "realpath" "sed" "tar" "tput" ) common_dir="../common" laboratory_dir="./laboratories" student_repo_name="student-repository" student_repo_url="git@github.zhaw.ch:CT/ct1-students.git" code_start="STUDENTS: To be programmed" code_end="END: To be programmed" make_start="solution[s]*=start" make_end="solution[s]*=end" md_solution_start="solution[s]*=start" md_solution_end="solution[s]*=end" md_student_start="student[s]*=start" md_student_end="student[s]*=end" keili_dir_name="keil" archive_name="release.tar" exclude_file_name=".release-ignore" # sanity checks for variables if [[ "${laboratory_dir}" =~ ".." ]]; then echo "'laboratory_dir' has to be a child directory of the one containing this script" fi if [[ "." != "$(dirname "${student_repo_name}")" ]]; then echo "'student_repo_name' may not contain directories!" exit 0 fi if [[ "." != "$(dirname "${keili_dir_name}")" ]]; then echo "'keili_dir_name' may not contain directories!" exit 0 fi if [[ "." != "$(dirname "${exclude_file_name}")" ]]; then echo "'exclude_file_name' may not contain directories!" exit 0 fi # helper function declarations ## print highlighted text ## ## used the same as `echo` function print_highlighted { if ! $(which tput &>/dev/null) || [[ -z "$(tput smso)" ]]; then echo "$*" return 0 fi tput smso echo "$*" tput sgr0 } ## print yellow text ## ## used the same as `echo` function print_warning { if ! $(which tput &>/dev/null); then echo "$*" return 0 fi tput setaf 3 echo "$*" tput sgr0 } ## print red text ## ## used the same as `echo` function print_error { if ! $(which tput &>/dev/null); then echo "$*" return 0 fi tput setaf 1 echo "$*" tput sgr0 } # main logic # ensure the required tools are available unset error_message for tool in "${required_tools}"; do if ! $(which "${tool}" &>/dev/null); then error_message+="${tool}" fi done if [[ -n "${error_message}" ]]; then print_error "missing the following tools: [ ${error_message} ]" exit 1 fi # ensure commands are executed in the directory where the # script is located root_dir="$(realpath "${BASH_SOURCE}" | dirname -)" cd "${root_dir}" # check that there are no paths with spaces illegal_paths="$(find . -type d -name ".git" -prune -o -name "*" -print | sort | grep ' ')" if [[ -n "${illegal_paths}" ]]; then print_error "no spaces in paths allowed!" print_error "the following paths are illegal:" echo "" echo "${illegal_paths}" exit 1 fi # ensure the working tree of the maintainer repository is not behind # no need to check the students repository, as it is derived from the # maintainer repository git fetch &>/dev/null if [[ -n "$(git diff origin/HEAD)" ]]; then print_warning "working tree of the maintainer repository differs from the remotes head." print_warning "pull and/or commit/push first to resolve the differences." exit 1 fi # remove trailing whitespace and convert to unix file format bash "${common_dir}/trailing-whitespace.sh" -u . # ensure the students repository is checked out if [[ ! -d "${student_repo_name}" ]]; then print_highlighted "cloning students repository" git clone "${student_repo_url}" "${student_repo_name}" fi # ensure the students repository is ignored if [[ -z "$(cat "./.gitignore" | grep "${student_repo_name}")" ]]; then echo "${student_repo_name}/" >>"./.gitignore" fi # remove solutions from source code code_files=($( find "${laboratory_dir}" \ -type f \ -name "*.[chs]" )) for file in "${code_files[@]}"; do sed -i " /${code_start}/,/${code_end}/ { /${code_start}/n /${code_end}/!d }" ${file} # add blank lines to show where code goes sed -i "/${code_start}/a\\\\" ${file} sed -i "/${code_start}/a\\\\" ${file} sed -i "/${code_start}/a\\\\" ${file} done echo "> removed solutions from source files." # removing solutions from makefiles make_files=($( find "${laboratory_dir}" \ -type f \ -name "Makefile" \ -or \ -name "*.mk" )) for file in "${make_files[@]}"; do sed -i "/${make_start}/,/${make_end}/d" ${file} done # remove solutions from lab instructions md_files=($( find "${laboratory_dir}" \ -type f \ -name "*.md" )) for file in "${md_files[@]}"; do sed -i "/${md_solution_start}/,/${md_solution_end}/d" ${file} sed -i "/${md_student_start}/d" ${file} sed -i "/${md_student_end}/d" ${file} done echo "> removed solutions from lab instructions." # convert readmes from `pandoc markdown` to `github flavoured markdown` and pdf echo "converting markdown files to pdfs" echo "(this may take a while)" template="$(realpath "${common_dir}/template.tex")" unset pdf_files for file in "${md_files[@]}"; do filename="${file##*/}" pushd "${file%/*}" &>/dev/null pandoc \ -f markdown \ -t pdf \ --pdf-engine pdflatex \ --listings \ --template "${template}" \ "${filename}" \ -o "${filename/%md/pdf}" pandoc \ -f markdown \ -t gfm \ "${filename}" \ -o "${filename}" popd &>/dev/null pdf_files+=("${file/%md/pdf}") done unset template echo "> converted readme files to github flavoured markdown." # generate zip archives of keili projects keili_dirs=($(find . -type d -name "${keili_dir_name}")) for item in "${keili_dirs[@]}"; do pushd "$(dirname "${item}")" &>/dev/null zip -r "${keili_dir_name}.zip" "${keili_dir_name}" &>/dev/null popd &>/dev/null done unset keili_dirs keili_archives=($(find "${laboratory_dir}" -name "${keili_dir_name}.zip")) # copy modified contents to students repository exclude_patterns=("${exclude_file_name}") exclude_patterns+=("${keili_dir_name}") exclude_patterns+=("*.pptx") exclude_patterns+=("*.docx") # collect exclude globs ignore_files=($(find . -type f -iname "${exclude_file_name}")) for file in "${ignore_files[@]}"; do temp_path="$(dirname ${file})/" temp_patterns=($(cat "${file}")) exclude_patterns+=(${temp_patterns[@]/#/${temp_path}}) done # remove contents of the student repository to avoid # orphaned files/directories find "${student_repo_name}" \ -mindepth 1 \ -maxdepth 1 \ -not -name ".git" \ -exec rm -rf {} \; # create release using `tar` tar -cf "${archive_name}" \ -h \ --exclude-vcs \ --exclude-vcs-ignore \ --exclude="${archive_name}" \ "${exclude_patterns[@]/#/--exclude=}" \ "${keili_archives[@]/#/--add-file=}" \ "${pdf_files[@]/#/--add-file=}" \ . tar -xf "${archive_name}" \ -C "${student_repo_name}/" # reset repository after release creation git reset --hard &>/dev/null git clean -fd &>/dev/null rm "${keili_archives[@]}" "${pdf_files[@]}" # print message to report success and a reminder to commit and # push the students repository relative_path="$(dirname "${BASH_SOURCE}")/${student_repo_name}" echo "" echo "successfully prepared the release for the students repository." echo "generated release is found at \`${relative_path}\`." echo "" print_highlighted "remember to commit and push the generated release!" echo ""