diff --git a/README.md b/README.md index 0c244f0..22dc352 100644 --- a/README.md +++ b/README.md @@ -34,22 +34,22 @@ See [`navapbc/platform`](https://github.com/navapbc/platform) for other template To get started using the template application on your project: -1. Run the [download and install script](./template-only-bin/download-and-install-template.sh) in your project's root directory. +1. Run the [download and install script](./template-only-bin/download-and-install-template) in your project's root directory. ```bash - curl https://raw.githubusercontent.com/navapbc/template-application-rails/main/template-only-bin/download-and-install-template.sh | bash -s + curl https://raw.githubusercontent.com/navapbc/template-application-rails/main/template-only-bin/download-and-install-template | bash -s ``` This script will: 1. Clone the template repository 2. Copy the template files into your project directory - 3. Remove any files specific to the template repository, like this README. + 3. Ignore any files specific to the template repository, like this README. You can optionally pass in a branch, commit hash, or release that you want to install. For example: ```bash - curl https://raw.githubusercontent.com/navapbc/template-application-rails/main/template-only-bin/download-and-install-template.sh | bash -s -- + curl https://raw.githubusercontent.com/navapbc/template-application-rails/main/template-only-bin/download-and-install-template | bash -s -- ``` 2. [Follow the steps in `app-rails/README.md`](./app-rails/README.md) to set up the application locally. 3. Optional, if using the Platform infrastructure template: [Follow the steps in the `template-infra` README](https://github.com/navapbc/template-infra#installation) to set up the various pieces of your infrastructure. @@ -58,16 +58,16 @@ To get started using the template application on your project: If you have previously installed this template and would like to update your project to use a newer version of this application: -1. Run the [download and install script](./template-only-bin/download-and-install-template.sh) in your project's root directory and pass in the branch, commit hash, or release that you want to update to, followed by the name of your application directory (e.g. `app-rails`). +1. Run the [update script](./template-only-bin/update-template) in your project's root directory and pass in the branch, commit hash, or release that you want to update to, followed by the name of your application directory (e.g. `app-rails`). ```bash - curl https://raw.githubusercontent.com/navapbc/template-application-rails/main/template-only-bin/download-and-install-template.sh | bash -s -- + curl https://raw.githubusercontent.com/navapbc/template-application-rails/main/template-only-bin/download-and-install-template | bash -s -- ``` This script will: 1. Clone the template repository 2. Copy the template files into your project directory - 3. Remove any files specific to the template repository, like this README. + 3. Ignore any files specific to the template repository, like this README. ⚠️ Warning! This will modify existing files. Review all changes carefully after executing the script by running `git diff`. diff --git a/template-only-bin/download-and-install-template.sh b/template-only-bin/download-and-install-template similarity index 87% rename from template-only-bin/download-and-install-template.sh rename to template-only-bin/download-and-install-template index 0e368cd..19be463 100755 --- a/template-only-bin/download-and-install-template.sh +++ b/template-only-bin/download-and-install-template @@ -21,10 +21,6 @@ template_short_name="app-${template_name##*-}" target_version=${1:-"main"} app_name=${2:-"${template_short_name}"} -echo "template_short_name: ${template_short_name}" -echo "app_name: ${app_name}" -echo "target_version: ${target_version}" - git clone "https://github.com/navapbc/${template_name}.git" cd "${template_name}" @@ -33,7 +29,7 @@ git checkout "$target_version" cd - &> /dev/null echo "Installing ${template_name}..." -"./${template_name}/template-only-bin/install-template.sh" "${template_name}" "${app_name}" +curl "https://raw.githubusercontent.com/navapbc/${template_name}/rocket/update-template-only-bin-scripts/template-only-bin/install-template" | bash -s -- "${template_name}" "${app_name}" echo "Storing template version in a file..." cd "${template_name}" diff --git a/template-only-bin/install-template.sh b/template-only-bin/install-template similarity index 50% rename from template-only-bin/install-template.sh rename to template-only-bin/install-template index 3ffe4d1..ecf201e 100755 --- a/template-only-bin/install-template.sh +++ b/template-only-bin/install-template @@ -1,7 +1,8 @@ #!/usr/bin/env bash # ----------------------------------------------------------------------------- # This script installs an application template to your project. -# Run this script using ./download-and-install-template.sh +# Run this script using ./download-and-install-template. Expected to be run +# from the project's root directory. # # Positional parameters: # template_name (required) – the name of the template to install @@ -16,34 +17,27 @@ template_name=$1 template_short_name="app-${template_name##*-}" app_name=$2 -echo "template_short_name: ${template_short_name}" -echo "app_name: ${app_name}" - curr_dir=$(pwd) -script_dir=$(dirname $0) -template_dir="${script_dir}/.." -cd $template_dir +cd "${template_name}" -if [ "$template_short_name" != "$app_name" ]; then +if [ "${template_short_name}" != "${app_name}" ]; then echo "Modifying template to use ${app_name} instead of ${template_short_name}..." - "./template-only-bin/rename-template-app.sh" "${app_name}" "${template_short_name}" + curl "https://raw.githubusercontent.com/navapbc/${template_name}/rocket/update-template-only-bin-scripts/template-only-bin/rename-template-app" | bash -s -- "${template_short_name}" "${app_name}" fi -echo "Copying files from $template_name..." -# Note: Keep this list of paths in sync with INCLUDE_PATHS in update-template.sh +# Note: Keep this list in sync with the files listed in update-template +# Copy only relevant files that should be included in the project repo. +echo "Copying files from ${template_name}..." +# Copy top level paths. cp -r \ - .github \ - .gitignore \ .grype.yml \ "${app_name}" \ docker-compose.yml \ docker-compose.mock-production.yml \ - docs \ - $curr_dir -cd - >& /dev/null + ${curr_dir} +# Copy nested paths. Make any missing directories. +mkdir -p "${curr_dir}/.github/workflows" && cp ".github/workflows/ci-${app_name}.yml" "${curr_dir}/.github/workflows" +mkdir -p "${curr_dir}/docs" && cp -r "docs/${app_name}" "${curr_dir}/docs" -echo "Removing files relevant only to template development..." -# Note: Keep this list of paths in sync with EXCLUDE_OPT in update-template.sh -rm -rf .github/workflows/template-only-* -rm -rf .github/ISSUE_TEMPLATE +cd - >& /dev/null \ No newline at end of file diff --git a/template-only-bin/rename-template-app b/template-only-bin/rename-template-app new file mode 100755 index 0000000..7bb2bba --- /dev/null +++ b/template-only-bin/rename-template-app @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +# ----------------------------------------------------------------------------- +# This script renames the template application in a project. +# Run this script in a project's root directory. +# +# The project name is the name of the folder in your project's root directory. Use +# lowercase letters and hyphens. Do not use spaces. Underscores may have unexpected side +# effects. Choose a unique string that will avoid collisions with commonly used words. +# By default, the application name is `app-rails`. +# +# Positional parameters: +# current_name (required) – the current name for the application +# new_name (required) - the new name for the application +# ----------------------------------------------------------------------------- +set -euo pipefail + +# Helper to get the correct sed -i behavior for both GNU sed and BSD sed (installed by default on macOS) +# Hat tip: https://stackoverflow.com/a/38595160 +sedi () { + sed --version >/dev/null 2>&1 && sed -i -- "$@" || sed -i "" "$@" +} +# Export the function so it can be used in the `find -exec` calls later on +export -f sedi + +current_name=$1 +new_name=$2 +default_name="app-rails" + +# Debug: +echo "---------------------------------------------------------------------" +echo "current_name: ${current_name}" +echo "new_name: ${new_name}" +echo + +if [[ "${current_name}" == "${new_name}" ]]; then + # Debug: + echo "No rename required: ${current_name} == ${new_name}" + exit 0 +fi + +# Note: Keep this list in sync with the files copied in install-template and update-template +declare -a include_paths +include_paths=(.github/workflows/ci-app-rails.yml) +include_paths+=(.grype.yml) +include_paths+=(app-rails) +include_paths+=(docker-compose.yml) +include_paths+=(docker-compose.mock-production.yml) +include_paths+=(docs/app-rails) + +# Loop through the paths to be included in this template. +for include_path in "${include_paths[@]}"; do + # If the application does not use the default name (i.e. it has already been renamed), + # change the include path to use the correct current_name. + if [[ "${current_name}" != "${default_name}" ]]; then + include_path=$(echo "${include_path}" | sed "s/${default_name}/${current_name}/g") + fi + + echo "Checking '${include_path}' to rename '${current_name}' to '${new_name}'..." + + # Skip if the path does not exist. + if [[ ! -d "${include_path}" ]] && [[ ! -f "${include_path}" ]]; then + echo "Skipping ahead. ${include_path} does not exist in this repo" + continue + fi + + # Construct the correct string substitution that respects word boundaries. + # Hat tip: https://unix.stackexchange.com/a/393968 + if sed --version >/dev/null 2>&1; then + word_boundary_replacement="s/\<${current_name}\>/${new_name}/g" + else + word_boundary_replacement="s/[[:<:]]${current_name}[[:>:]]/${new_name}/g" + fi + + # Replace occurrances of the current_name with the new_name in the path. + # If the path is a file, replace in the file. + # If the path is a directory, recursively replace in all files in the directory. + LC_ALL=C find "${include_path}" -type f -exec bash -c "sedi \"${word_boundary_replacement}\" \"{}\"" \; + + # Rename included paths that contain the current_name. + if [[ "${include_path}" =~ "${current_name}" ]]; then + new_include_path=$(echo "${include_path}" | sed "s/${current_name}/${new_name}/g") + echo "Renaming path from '${include_path}' to '${new_include_path}'..." + mv "${include_path}" "${new_include_path}" + fi +done diff --git a/template-only-bin/rename-template-app.sh b/template-only-bin/rename-template-app.sh deleted file mode 100755 index a012b93..0000000 --- a/template-only-bin/rename-template-app.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash -# ----------------------------------------------------------------------------- -# This script renames the application template in your project. -# Run this script in your project's root directory. -# -# Positional parameters: -# new_name (required) - the new name for the application, in either snake- or kebab-case -# old_name (required) – the old name for the application, in either snake- or kebab-case -# ----------------------------------------------------------------------------- -set -euo pipefail - -# Helper to get the correct sed -i behavior for both GNU sed and BSD sed (installed by default on macOS) -# Hat tip: https://stackoverflow.com/a/38595160 -sedi () { - sed --version >/dev/null 2>&1 && sed -i -- "$@" || sed -i "" "$@" -} -# Export the function so it can be used in the `find -exec` calls later on -export -f sedi - -new_name=$1 -old_name=$2 - -# Don't make assumptions about whether the arguments are snake- or kebab-case. Handle both. -# Get kebab-case names -old_name_kebab=$(echo $old_name | tr "_" "-") -new_name_kebab=$(echo $new_name | tr "_" "-") - -# Get snake-case names -old_name_snake=$(echo $old_name | tr "-" "_") -new_name_snake=$(echo $new_name | tr "-" "_") - -# Rename the app directory -if [ -d "$old_name" ]; then - echo "Renaming ${old_name} to ${new_name}..." - mv "${old_name}" "${new_name}" -fi - -# Rename all kebab-case instances -echo "Performing a find-and-replace for all instances of (kebab-case) '$old_name_kebab' with '$new_name_kebab'..." -LC_ALL=C find . -type f -not -path "./.git/*" -exec bash -c "sedi \"s/$old_name_kebab/$new_name_kebab/g\" \"{}\"" \; - -# Rename all snake-case instances -echo "Performing a find-and-replace for all instances of (snake-case) '$old_name_snake' with '$new_name_snake'..." -LC_ALL=C find . -type f -not -path "./.git/*" -exec bash -c "sedi \"s/$old_name_snake/$new_name_snake/g\" \"{}\"" \; - -echo "...Done." diff --git a/template-only-bin/update-template b/template-only-bin/update-template new file mode 100755 index 0000000..d3a0efc --- /dev/null +++ b/template-only-bin/update-template @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +# ----------------------------------------------------------------------------- +# This script updates an application template in your project. +# This script from your project's root directory. +# +# Positional parameters: +# target_version (optional) – the version of the template application to install. +# Defaults to main. Can be any target that can be checked out, including a branch, +# version tag, or commit hash. +# app_name (optional) – the name of the application, in either snake- or kebab-case +# Defaults to app-rails. +# ----------------------------------------------------------------------------- +set -euo pipefail + +# Helper to get the correct sed -i behavior for both GNU sed and BSD sed (installed by default on macOS) +# Hat tip: https://stackoverflow.com/a/38595160 +sedi () { + sed --version >/dev/null 2>&1 && sed -i -- "$@" || sed -i "" "$@" +} +# Export the function so it can be used in the `find -exec` calls later on +export -f sedi + +template_name="template-application-rails" +# Use shell parameter expansion to get the last word, where the delimiter between +# words is `-`. +# See https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Shell-Parameter-Expansion +template_short_name="app-${template_name##*-}" + +target_version=${1:-"main"} +app_name=${2:-"${template_short_name}"} +current_version=$(cat ".${template_name}-version") + +git clone "https://github.com/navapbc/${template_name}.git" + +echo "Checking out $target_version..." +cd "${template_name}" +git checkout "$target_version" +cd - &> /dev/null + +# Note: Keep this list in sync with the files copied in install-template +cd "${template_name}" +include_paths=" \ + .github/workflows/ci-${template_short_name}.yml + .grype.yml \ + ${template_short_name} \ + docker-compose.yml \ + docker-compose.mock-production.yml \ + docs/${template_short_name}" +git diff $current_version $target_version -- $include_paths > update.patch +cd - &> /dev/null + +if [ "$template_short_name" != "$app_name" ]; then + echo "Modifying patch to use ${app_name} instead of ${template_short_name}..." + # Construct the correct string substitution that respects word boundaries. + # Hat tip: https://unix.stackexchange.com/a/393968 + if sed --version >/dev/null 2>&1; then + word_boundary_replacement="s/\<${template_short_name}\>/${app_name}/g" + else + word_boundary_replacement="s/[[:<:]]${template_short_name}[[:>:]]/${app_name}/g" + fi + sedi "${word_boundary_replacement}" "${template_name}/update.patch" +fi + +echo "Applying patch..." +git apply --allow-empty "${template_name}/update.patch" + +echo "Storing template version in a file..." +cd "${template_name}" +git rev-parse HEAD >../".${template_name}-version" +cd - &> /dev/null + +echo "Cleaning up ${template_name} folder..." +rm -fr "${template_name}" + +echo "...Done." \ No newline at end of file