diff --git a/README.md b/README.md index 74f8e1a..d5ceac6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,79 @@ -The Tools branch of OPE contains the main ope tool along with its friends. +# OPE Tool +This tool is designed to facilitate the management of projects within the Open Publishing Environment (OPE). It includes a variety of functions for handling repositories, books, containers, and course creation. + +## Installation + +1. Clone this repository to your local machine. +2. Ensure you have Git installed, as it is required for several functions. +3. **Command**: In the directory, run `./install.sh` + +## Usage + +Each function in the script is intended for specific tasks. Below are the usages and descriptions for each function. + +### General + +- **Usage**: Run the tool with a specific command and relevant arguments. +- **Command**: `ope [arguments]` + +### `new_project` + +- **Description**: Creates a new OPE project from templates. +- **Usage**: `ope new_project [repo_url]` +- **Arguments**: + - `project_name`: Name of the new project. + - `repo_url` (optional): URL of the Git repository to associate with the project. + +### `new_book` + +- **Description**: Creates a new book within the project. +- **Usage**: `ope new_book ` +- **Arguments**: + - `book_name`: Name of the new book. + +### `new_container` + +- **Description**: Adds source for building a new container for the project. +- **Usage**: `ope new_container ` +- **Arguments**: + - `container_name`: Name of the new container. + +### `new_course` + +- **Description**: Creates a new course with a standard set of books and a container. +- **Usage**: `ope new_course [repo_url]` +- **Arguments**: + - `course_name`: Name of the new course. + - `repo_url` (optional): URL of the Git repository for the course. + +### `update` + +- **Description**: Updates and rebases changes from the OPE framework. +- **Usage**: `ope update` +- No arguments required. + +### Helper Functions + +- **`repo_add`**: Adds a repository to a project. +- **`findprojectdir`**: Finds the project directory. +- **`projectdir`**: Outputs the project directory. +- **`new_part`**: Creates a new part in a book or course. +- **`print_func_page`**: Prints the help page for functions. +- **`Usage`**: Prints general usage information. + +### Logging and Configuration + +- The script includes logging functionality. Check `/tmp/ope..log` for logs. +- Color configuration is included for better readability of output. + + +## License + +This project is licensed under the MIT License. See the `LICENSE` file for details. + +--- + +## OPE USER GUIDE + +A collection of OPE books and associated container images are organized into a project. diff --git a/bin/ope b/bin/ope index 9ab695a..a0051b1 100755 --- a/bin/ope +++ b/bin/ope @@ -1,6 +1,7 @@ #!/bin/bash #set -x # Copyright (C) 2022 by Jonathan Appavoo, Boston University +# Revised by Yuxi Ge, Boston University 2023 # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -23,29 +24,30 @@ # Services information # -------------------- +RMDIR="rm -rf" + # REPO Services REPO_CMD="git" INIT="init" REMOTE_ADD="remote add" CHECK_REMOTES="remote -v" CLONE="clone" +CLONE_TEMPLATE="$CLONE --depth 1 --branch" PULL="pull" PUSH="push" -PUSH_FIRST="push -f -u" +PUSH_FIRST="push -u" SWITCH_BRANCH="checkout" CREATE_BRANCH="checkout -b" IGNORE_FILE=".gitignore" -RENAME_BRANCH="branch -m" +RENAME_BRANCH="branch -M" FETCH="fetch" ADD="add" COMMIT="commit -m" CURRENT_BRANCH="branch --show-current" REBASE="rebase" -REPO_URL="git@github.com:\${ORG_NAME}/\${REPO_NAME}.git" -REPO_SERVICE_DIR=".git" IF_INSIDE_REPO="rev-parse --is-inside-work-tree" SHOW_TOP_LEVEL="rev-parse --show-toplevel" - +DEFAULT_REMOTE_NAME=origin # email services EMAIL_SERVICE="gmail" @@ -53,16 +55,32 @@ EMAIL_SERVICE="gmail" # OPE INFORMATION # -------------------- OPE_ORG_NAME="OPEFFORT" -OPE_REPO_NAME="ope" -OPE_CONFIG_DIR="opeconfig" -UPSTREAM_BRANCHES=("main" "coursecontenttemplate" "Documentation") +OPE_GITURL="git@github.com:${OPE_ORG_NAME}" +OPE_CONFIG_DIR=".ope" +OPE_CONFIG_NAME="${OPE_CONFIG_DIR}/name" +OPE_PROJECT_TEMPLATE="project-template" +OPE_CONTAINER_TEMPLATE="container-template" +OPE_CONTAINERS_DIR=containers +OPE_BOOK_TEMPLATE="book-template" +OPE_BOOKS_DIR=books +OPE_BOOK_CONTENT_SOURCE=../../content +OPE_CONTENT_TEMPLATE="content-template" +OPE_CONTENT_DIR=content +OPE_CONTENT_EXAMPLES="content-examples" +OPE_CONTENT_EXAMPLES_DIR="$OPE_CONTENT_DIR/examples" +OPE_EXAMPLE_PART="$OPE_CONTENT_DIR/examples/part" +OPE_EXAMPLE_BOOK_PART=dummy_part +OPE_BOOK_CONTENT_SEEDS="${OPE_CONTENT_EXAMPLES_DIR}/book/intro.md ${OPE_CONTENT_EXAMPLES_DIR}/book/config.yml" +OPE_CONTENT_SEEDS="${OPE_CONTENT_EXAMPLES_DIR}/css ${OPE_CONTENT_EXAMPLES_DIR}/python ${OPE_CONTENT_EXAMPLES_DIR}/src ${OPE_CONTENT_EXAMPLES_DIR}/images ${OPE_CONTENT_EXAMPLES_DIR}/images/logo.png" + +UPSTREAM_BRANCHES=("main" "coursecontenttemplate" "Documentation") # --------- Script begins here ------------- # Script Configuration HEADER="[OPE] " VERBOSE=0 -LOG_FILE="/tmp/ope.log" +LOG_FILE="/tmp/ope.$$.log" # Color Configuration Color_Off='\033[0m' # Text Reset @@ -81,13 +99,13 @@ BGreen='\033[1;32m' # Helper functions fc_header_logging (){ - current_time=$(date +%T) - cwd=$(pwd) + local current_time=$(date +%T) + local cwd=$(pwd) echo "[${current_time}]: ${0##*/} $1 $cwd" >> $LOG_FILE } rolling_output () { - input=$(cat <&0 | tr '\r' '\n') + local input=$(cat <&0 | tr '\r' '\n') if [[ $VERBOSE -eq 0 ]]; then echo $input | while read line; do echo -ne "\033[2K\r"; printf " ${Grey}$line${Color_Off}\r"; done else @@ -97,238 +115,349 @@ rolling_output () { verify_repo (){ + local name=$1 # Check if we are inside a repository if ! ${REPO_CMD} ${IF_INSIDE_REPO} &>/dev/null; then - echo -e "$HEADER${Red}ERROR: Not a valid repository${Color_Off}" + echo -e "$HEADER${Red}ERROR: No repository for project${Color_Off}" return -1 fi - + return 0 # Check if we are inside an OPE repository - output=$(${REPO_CMD} ${CHECK_REMOTES}) - if ! echo "$output" | grep -q "ope-upstream"; then - echo -e "$HEADER${Red}ERROR: Not a valid OPE repository${Color_Off}" - return -1 - fi + #output=$(${REPO_CMD} ${CHECK_REMOTES}) + #if [[ -n $name ]] && ! echo "$output" | grep -q "$name"; then + # echo -e "$HEADER${Red}ERROR: Not a valid OPE repository${Color_Off}" + # return -1 + #fi # Check if we are inside the top level of the OPE repository - if [ "$(pwd)" != "$(${REPO_CMD} ${SHOW_TOP_LEVEL})" ]; then - cd "$(${REPO_CMD} ${SHOW_TOP_LEVEL})" - fi - + #if [ "$(pwd)" != "$(${REPO_CMD} ${SHOW_TOP_LEVEL})" ]; then + # cd "$(${REPO_CMD} ${SHOW_TOP_LEVEL})" + #fi } get_help_from_makefile(){ - option=$1 + local option=$1 cat Makefile | grep -E '^[a-zA-Z0-9_%/-]+:.*?## .*$$' | grep -E "^${option}" | tr -d '#' |column -t -s : | tr '-' ' ' } -function clone -#DESCRIPTION: clone the OPE repository for development -{ - url=$1 - - # Help message - if [[ -z $url || $url = '-h' ]]; then - echo "Usage: ${0##*/} clone " - echo " will clone the OPE repository and setup the branches for development" - echo " See the ope gettings started doc for more info." - return -1 +clone_template() { + local repo=$1 + local name=$2 + local branch=$3 + [[ -z $repo ]] && return 0 + [[ -z $name ]] && return 0 + [[ -z $branch ]] && branch=main + + if ! $REPO_CMD $CLONE_TEMPLATE $branch $OPE_GITURL/${repo}.git $name >/dev/null 2>&1; then + echo "ERROR: ${FUNCNAME[0]}: failed to clone $repo:$branch as $name" > /dev/stderr + exit -1 + else + $RMDIR $name/.git fi + return 0 +} - basename=$(basename $url) - repo=${basename%.*} +function repo_add +#DESCRIPTION: add a repository to a project +{ + local repourl=$1 + local name=$2 + local autopush=$3 + + # Logging information + local func_name=${FUNCNAME[0]} + local func_args=$@ + fc_header_logging "$func_name $func_args" - # Check if the organization already exists locally - if [[ -d $repo ]]; then - echo -e "$HEADER${Red}ERROR: Repository $repo already exists${Color_Off}" + local pdir=$(projectdir) + [[ -z $pdir ]] && return -1 + + # Help message + if [[ $name == '-h' ]]; then + echo "Usage: ${0##*/} ${func_name} [git remote url] [name] [autopush]" + echo " Add a git repository to the project to store and tack" + echo " all the project content. With now arguments will " + echo " Indicate if there is a repo for the project and what it is" + echo " Eg. ope repo_add git@github.com:myuser/opeproject.git" + echo " - If specified name will be used for the remote default is $DEFAULT_REMOTE_NAME" + echo " - If autopush is not empty then an initial push of all project content will be done" return -1 fi - ORG_NAME="${OPE_ORG_NAME}" - REPO_NAME="${OPE_REPO_NAME}" - eval "OPE_REPO_URL=$REPO_URL" - echo $OPE_REPO_URL - - echo -e "$HEADER${Cyan}Creating OPE repository locally${Color_Off}" - mkdir -p ${repo} - cd ${repo} - echo -e "$HEADER${Cyan}Linking ope-upstream branches${Color_Off}" - - ${REPO_CMD} ${INIT} - # Adding the remotes - ${REPO_CMD} ${REMOTE_ADD} ope-upstream ${OPE_REPO_URL} # | rolling_output #0<&0 - ${REPO_CMD} ${REMOTE_ADD} origin ${url} # | rolling_output #0<&0 - - for branch in "${UPSTREAM_BRANCHES[@]}"; do - ${REPO_CMD} ${FETCH} ope-upstream ${branch} 2>&1 # | rolling_output #0<&0 #| while read line; do echo -ne "\033[2K\r"; printf " ${Grey}$line${Color_Off}\r"; done - ${REPO_CMD} ${FETCH} origin ${branch} 2>&1 # | rolling_output #0<&0 #| while read line; do echo -ne "\033[2K\r"; printf " ${Grey}$line${Color_Off}\r"; done - ${REPO_CMD} ${CREATE_BRANCH} ${branch} origin/${branch} 2>&1 # | rolling_output #0<&0 #| while read line; do echo -ne "\033[2K\r"; printf " ${Grey}$line${Color_Off}\r"; done - ${REPO_CMD} ${CREATE_BRANCH} ${branch}-source ope-upstream/${branch} 2>&1 # | rolling_output #0<&0 #| while read line; do echo -ne "\033[2K\r"; printf " ${Grey}$line${Color_Off}\r"; done - done - - ${REPO_CMD} ${SWITCH_BRANCH} ${UPSTREAM_BRANCHES[0]} 2>&1 - + [[ -z $name ]] && name=$DEFAULT_REMOTE_NAME + ( + cd $pdir + if ! verify_repo ; then + if [[ -n $repourl ]]; then + echo "setting up repo" + $REPO_CMD $INIT + $REPO_CMD $ADD $OPE_CONFIG_DIR + $REPO_CMD $COMMIT "starting OPE Project $(cat $OPE_CONFIG_NAME)" + $REPO_CMD $RENAME_BRANCH main + $REPO_CMD $REMOTE_ADD $name $repourl + if [[ -n $autopush ]]; then + $REPO_CMD $ADD *; $REPO_CMD $COMMIT 'starting materials' + $REPO_CMD $PUSH_FIRST $name main + else + echo "You should now add the rest of your content to the repo and push it to the remote" + echo "Eg. $REPO_CMD $ADD *; $REPO_CMD $COMMIT 'starting materials'" + echo "and be sure to push your content to your remote" + echo "$REPO_CMD $PUSH_FIRST $name main" + fi + fi + fi + ) 2>&1 | tee >(sed "s/$(printf '\033')\[[0-9;]*[a-zA-Z]//g" >> $LOG_FILE) } -function create -#DESCRIPTION: create a new book organization from the ope template -{ - name=$1 +function new_project +#DESCRIPTION: create a new ope project from the ope templates +{ + local name=$1 + local repo=$2 # Logging information - func_name=${FUNCNAME[0]} - func_args=$@ + local func_name=${FUNCNAME[0]} + local func_args=$@ fc_header_logging "$func_name $func_args" # Help message if [[ -z $name || $name == '-h' ]]; then - echo "Usage: ${0##*/} create " + echo "Usage: ${0##*/} ${func_name} [repo url]" echo " create an organization using ope template of the name specified" - echo " eg. ope create MyBook" + echo " eg. ope create MyProject" echo " will create a new local repository seeded from the ope " - echo " template. See the ope gettings started doc for more info." + echo " templates. See the ope gettings started doc for more info." + echo " Within a project you can will typically add books and containers." + echo " See ope new_book and ope new_container" + echo " If you supply a repository url the project will be configured with it" return -1 fi { - # Check if the organization already exists locally + # Check if the project already exists locally if [[ -d $name ]]; then - echo -e "$HEADER${Red}ERROR: Organization/Directory already exists${Color_Off}" + echo -e "$HEADER${Red}ERROR: Project Directory $name already exists${Color_Off}" return -1 fi - # Create the organization - mkdir -p $name - # Creating configuration - echo -e "$HEADER${Cyan}Creating OPE configurations${Color_Off}" - #touch $IGNORE_FILE - #echo $OPE_CONFIG_DIR >> $IGNORE_FILE - #mkdir -p $OPE_CONFIG_DIR - - cd $name - - read -r -p " Enter the email of the ${name} orginzation: " user_input - if [ -z "$user_input" ]; then - #echo "${name}@${EMAIL_SERVICE}.com" > ../$OPE_CONFIG_DIR/email - echo -e " The email was set to default value: ${BBlack}${name}@${EMAIL_SERVICE}.com${Color_Off}" - else - #echo $user_input > ../$OPE_CONFIG_DIR/email - echo -e " The email was set to: ${BBlack}${user_input}${Color_Off}" - fi - + # Create the project from ope template + clone_template $OPE_PROJECT_TEMPLATE $name - read -r -p " Enter the source URL of the ${name} orginzation: " user_input - if [ -z "$user_input" ]; then - ORG_NAME="${name}" - REPO_NAME="${name}" - eval "NEW_REPO_URL=$REPO_URL" - else - NEW_REPO_URL=$user_input + # seed project config + if [[ -d $name/$OPE_CONFIG_DIR ]]; then + echo $name > $name/$OPE_CONFIG_NAME + else + echo -e "$HEADER${Red}ERROR: FATAL could not locate ope project config dir${Color_Off}" + return -1 fi - #echo $NEW_REPO_URL > ../$OPE_CONFIG_DIR/source_url - echo -e " The ${name} source URL was set to: ${BBlack}${NEW_REPO_URL}${Color_Off}" - - # Creating the organization - while true; do - read -r -p " Are you building on top of an existing OPE book? [Y/n]: " user_input - if [ -z "$user_input" ]; then - user_input="n" - break; - elif [ "$user_input" == "n" ]; then - break; - elif [ "$user_input" == "Y" ]; then - break; - else - echo -e " ${Red}ERROR: Invalid input. Please enter Y/n ${Color_Off}" - fi - done - if [ "$user_input" == "n" ]; then - echo -e "$HEADER${Cyan}Seeding from OPE content template${Color_Off}" - ${REPO_CMD} ${INIT} - ORG_NAME="${OPE_ORG_NAME}" - REPO_NAME="${OPE_REPO_NAME}" - eval "OPE_REPO_URL=$REPO_URL" - else - echo -e "$HEADER${Cyan}Seeding from existing book content template${Color_Off}" - while true; do - read -r -p " Enter the OPE source URL of the existing book: " user_input - if [ -z "$user_input" ]; then - echo -e " ${Red}ERROR: Invalid input. Please try again${Color_Off}" - else - break; - fi - done - # | rolling_output - OPE_REPO_URL=$user_input - fi + # seed project content structure + clone_template $OPE_CONTENT_TEMPLATE $name/$OPE_CONTENT_DIR + + # get a copy of the example content + clone_template $OPE_CONTENT_EXAMPLES $name/$OPE_CONTENT_EXAMPLES_DIR + + # seed local copies of typical content from examples + for seed in $OPE_CONTENT_SEEDS; do + local sb=$(basename $seed) + if [[ ! -a $name/$OPE_CONTENT_DIR/$sb ]]; then + cp -r $name/$seed $name/$OPE_CONTENT_DIR + fi + done + + if [[ -n $repo ]]; then + ( + cd $name + repo_add $repo $DEFAULT_REMOTE_NAME yes + ) + fi + } 2>&1 | tee >(sed "s/$(printf '\033')\[[0-9;]*[a-zA-Z]//g" >> $LOG_FILE) +} - ${REPO_CMD} ${INIT} +findprojectdir() +{ + if [[ -r ${OPE_CONFIG_NAME} ]]; then + pwd -P + elif [[ $(pwd -P) != / ]]; then + cd .. + findprojectdir + fi +} - # Adding the remotes - ${REPO_CMD} ${REMOTE_ADD} ope-upstream ${OPE_REPO_URL} # | rolling_output #0<&0 - ${REPO_CMD} ${REMOTE_ADD} origin ${NEW_REPO_URL} # | rolling_output #0<&0 +projectdir() +{ + pd=$(findprojectdir) + if [[ -n $pd ]]; then + echo $pd + return 0 + else + echo -e "$HEADER${Red}ERROR: You don't seem to be in an ope project${Color_Off}" > /dev/stderr + return -1 + fi +} - for branch in "${UPSTREAM_BRANCHES[@]}"; do - ${REPO_CMD} ${FETCH} ope-upstream ${branch} 2>&1 # | rolling_output #0<&0 #| while read line; do echo -ne "\033[2K\r"; printf " ${Grey}$line${Color_Off}\r"; done - ${REPO_CMD} ${SWITCH_BRANCH} ${branch} 2>&1 # | rolling_output #0<&0 #| while read line; do echo -ne "\033[2K\r"; printf " ${Grey}$line${Color_Off}\r"; done - ${REPO_CMD} ${RENAME_BRANCH} ${branch}-source 2>&1 # | rolling_output #0<&0 #| while read line; do echo -ne "\033[2K\r"; printf " ${Grey}$line${Color_Off}\r"; done - ${REPO_CMD} ${CREATE_BRANCH} ${branch} 2>&1 # | rolling_output #0<&0 #| while read line; do echo -ne "\033[2K\r"; printf " ${Grey}$line${Color_Off}\r"; done - ${REPO_CMD} ${PUSH_FIRST} origin ${branch} 2>&1 # | rolling_output #0<&0 - done +new_part() +{ + local name=$1 + if [[ -z $name ]]; then + return -1; + fi + if [[ ! -a $name ]]; then + cp -r $OPE_EXAMPLE_PART $name + fi +} - ${REPO_CMD} ${SWITCH_BRANCH} ${UPSTREAM_BRANCHES[0]} 2>&1 # # | rolling_output - echo -ne "\033[2K\r" - #printf "$HEADER${Cyan}Initilal Commit to ${name} repository.${Color_Off}\n" - #mv ../$IGNORE_FILE . - #mv ../$OPE_CONFIG_DIR . - #${REPO_CMD} ${ADD} . #2>&1 # | rolling_output - #${REPO_CMD} ${COMMIT} "Initial commit from the ope command line tool" 2>&1 ## | rolling_output - #${REPO_CMD} ${PUSH} 2>&1 ## | rolling_output - echo -e "$HEADER${Green}OPE book organization has been successfully created ${Color_Off}" +function new_book +#DESCRIPTION: create a new publishable book for your content +{ + local name=$1 + + # Logging information + local func_name=${FUNCNAME[0]} + local func_args=$@ + fc_header_logging "$func_name $func_args" - } 2>&1 | tee >(sed "s/$(printf '\033')\[[0-9;]*[a-zA-Z]//g" >> $LOG_FILE) + local pdir=$(projectdir) + [[ -z $pdir ]] && return -1 + + # Help message + if [[ -z $name || $name == '-h' ]]; then + echo "Usage: ${0##*/} ${func_name} " + echo " create a new book of the name specified" + echo " eg. ope ${func_name} textbook" + return -1 + fi + ( + cd $pdir + # Check if the book already exists + if [[ -d ${OPE_BOOKS_DIR}/$name ]]; then + echo -e "$HEADER${Red}ERROR: book directory $name already exists${Color_Off}" + return -1 + fi + + # Create the book from ope book template + clone_template $OPE_BOOK_TEMPLATE ${OPE_BOOKS_DIR}/$name + + # link book content to project content + ( + cd ${OPE_BOOKS_DIR}/$name + if [[ ! -d ${OPE_BOOK_CONTENT_SOURCE} ]]; then + echo "ERROR can't find ope project content directory" + exit -1 + fi + ln -s ${OPE_BOOK_CONTENT_SOURCE} content + ) + # Create the minimum necessary book specific content files + for file in $OPE_BOOK_CONTENT_SEEDS; do + local fb=$(basename $file) + if [[ ! -a ${OPE_CONTENT_DIR}/${name}_${fb} ]]; then + if ! cp $file ${OPE_CONTENT_DIR}/${name}_${fb}; then + echo "ERROR creating ${OPE_CONTENT_DIR}/${name}_$fb" > /dev/stderr + exit -1 + fi + fi + done + + cat > ${OPE_CONTENT_DIR}/${name}_toc.yml <&1 | tee >(sed "s/$(printf '\033')\[[0-9;]*[a-zA-Z]//g" >> $LOG_FILE) } -function update -#DESCRIPTION: update and rebase changes from the OPE framework +function new_container +#DESCRIPTION: add source for building a new container for your project { - option=$1 - verify_repo - - if [[ ! -z "$option" && $option = '-h' ]]; then - echo "Usage: ${0##*/} update [options]" - echo " will update and rebase based on the current branch" - echo " options:" - echo " -u: update the OPE framework only and do not perform a rebase" - echo " See the ope gettings started doc for more info." + local name=$1 + + # Logging information + local func_name=${FUNCNAME[0]} + local func_args=$@ + fc_header_logging "$func_name $func_args" + + local pdir=$(projectdir) + [[ -z $pdir ]] && return -1 + + # Help message + if [[ -z $name || $name == '-h' ]]; then + echo "Usage: ${0##*/} ${func_name} " + echo " create source for a new container of the name specified" + echo " eg. ope ${func_name} standard" return -1 fi + ( + cd $pdir + # Check if the container already exists + if [[ -d ${OPE_CONTAINER_DIR}/$name ]]; then + echo -e "$HEADER${Red}ERROR: container directory $name already exists${Color_Off}" + return -1 + fi + # Create the container from ope book template + clone_template $OPE_CONTAINER_TEMPLATE ${OPE_CONTAINERS_DIR}/$name + ) 2>&1 | tee >(sed "s/$(printf '\033')\[[0-9;]*[a-zA-Z]//g" >> $LOG_FILE) +} - current_branch=$(${REPO_CMD} ${CURRENT_BRANCH}) - echo -e "$HEADER${Cyan}Updating the book repository on ${Color_Off}${BCyan}${current_branch}${Color_Off}" - ${REPO_CMD} ${SWITCH_BRANCH} ${current_branch}-source - ${REPO_CMD} ${PULL} ope-upstream ${current_branch} - ${REPO_CMD} ${SWITCH_BRANCH} ${current_branch} - - if [[ $option = '-u' ]]; then - echo -e "$HEADER${Cyan}Updating the OPE framework only${Color_Off}" - echo -e "$HEADER${BGreen}OPE upstream ${current_branch}${Color_Off}${Green} has been successfully updated ${Color_Off}" - return - fi +function update +#DESCRIPTION: Rebase current project with the latest changes from OPEFFORT templates +{ + # Logging information + local func_name=${FUNCNAME[0]} + fc_header_logging "$func_name" + + local pdir=$(projectdir) + [[ -z $pdir ]] && return -1 + + # Helper function to rebase from a specific OPEFFORT template + rebase_from_template() { + local template_name=$1 + local template_branch=$2 + local template_repo="$OPE_GITURL/${template_name}.git" + + echo -e "$HEADER${Cyan}Updating ${template_name} from ${template_repo}${Color_Off}" + + if ! $REPO_CMD $FETCH $template_repo $template_branch; then + echo -e "$HEADER${Red}ERROR: Failed to fetch updates from ${template_repo}${Color_Off}" + return -1 + fi + + if ! $REPO_CMD $REBASE FETCH_HEAD; then + echo -e "$HEADER${Red}ERROR: Rebase failed. Please resolve conflicts and complete the rebase manually.${Color_Off}" + return -1 + fi + + echo -e "$HEADER${Green}Rebase completed successfully for ${template_name}${Color_Off}" + } - echo -e "$HEADER${Cyan}Rebasing the book repository on ${Color_Off}${BCyan}${current_branch}${Color_Off}" - ${REPO_CMD} ${REBASE} ${current_branch}-source - echo -e "$HEADER${BGreen}${current_branch}${Color_Off}${Green} has been successfully updated ${Color_Off}" + ( + cd $pdir + # Rebasing from container-template, book-template, and content-example + for template in container-template book-template content-example; do + rebase_from_template $template "main" + if [[ $? -ne 0 ]]; then + echo -e "$HEADER${Red}ERROR: Failed to update ${template}${Color_Off}" + return -1 + fi + done + echo -e "$HEADER${Green}Project successfully updated with the latest OPEFFORT templates.${Color_Off}" + ) 2>&1 | tee >(sed "s/$(printf '\033')\[[0-9;]*[a-zA-Z]//g" >> $LOG_FILE) } + function build #DESCRIPTION: build the book { - option=$1 - verify_repo + local option=$1 + if [[ $option = '-h' ]]; then echo "Usage: ${0##*/} build [options]" @@ -362,8 +491,8 @@ function build function pub #DESCRIPTION: publish the book { - option=$1 - verify_repo + local option=$1 + if [[ $option = '-h' ]]; then echo "Usage: ${0##*/} pub [options]" @@ -386,9 +515,62 @@ function pub echo -e "$HEADER${Green}${current_branch} has been successfully published ${Color_Off}" } +function test +#DESCRIPTION: run .ipynb tests to verify environment is properly set up +{ + ipython $(git $SHOW_TOP_LEVEL)/tests/All-tests.ipynb +} + +function new_course +#DESCRIPTION: Creates a new ope project seeded with 3 books (textbook, lecture notes, and labmanual) and one container +{ + local project_name=$1 + local repo_url=$2 + + # Logging information + local func_name=${FUNCNAME[0]} + local func_args=$@ + fc_header_logging "$func_name $func_args" + + if [[ -z $project_name || $project_name == '-h' ]]; then + echo "Usage: ${0##*/} ${func_name} [repo url]" + echo " Create a new ope project with a standard set of books and a container." + return -1 + fi + + # Create a new project + new_project "$project_name" "$repo_url" + if [[ $? -ne 0 ]]; then + echo -e "$HEADER${Red}ERROR: Failed to create project $project_name${Color_Off}" + return -1 + fi + + # Navigate to the project directory + cd "$project_name" + + # Create standard set of books + for book in textbook lecture_notes lab_manual; do + new_book "$book" + if [[ $? -ne 0 ]]; then + echo -e "$HEADER${Red}ERROR: Failed to create book $book${Color_Off}" + return -1 + fi + done + + # Create a standard container + new_container "standard_container" + if [[ $? -ne 0 ]]; then + echo -e "$HEADER${Red}ERROR: Failed to create container standard_container${Color_Off}" + return -1 + fi + + echo -e "$HEADER${Green}New course $project_name created successfully with standard books and container.${Color_Off}" + return 0 +} + print_func_page() { - func_names=($(grep '^function' $0 | awk '{print $2}')) + local func_names=($(grep '^function' $0 | awk '{print $2}')) grep '^#DESCRIPTION' $0 | awk -F: '{print $2}' | while read line ; do echo -e " ${Blue}${func_names[$i]}${Color_Off}, $line\n"; i=$((i+1)); done | column -t -s, }