From a34a5ab63fcfc2d313394c3fa146669e426d449f Mon Sep 17 00:00:00 2001 From: Edwin Kofler Date: Tue, 15 Jun 2021 16:34:04 -0700 Subject: [PATCH] refactor: Complete overhaul - remove useless '|| exit' in second bootstrap line - Add a synthetic stack, showing the trace on error --- .editorconfig | 3 + README.md | 4 + actions/do-go-check.sh | 15 -- actions/do-nim-build.sh | 17 -- actions/do-node-app-build.sh | 13 -- .../{do-go-build.sh => effect-deb-package.sh} | 7 +- actions/effect-git-tag.sh | 2 +- actions/effect-github-release.sh | 2 +- ...an-package.sh => effect-pacman-package.sh} | 14 +- actions/effect-rpm-package.sh | 14 ++ actions/result-deb-package.sh | 5 - actions/result-rpm-package.sh | 11 -- actions/tool-bats.sh | 11 +- actions/tool-checkmake.sh | 15 +- actions/tool-conventional-changelog.sh | 10 +- actions/tool-eslint-init.sh | 8 - actions/tool-go-build.sh | 12 ++ actions/tool-gofmt.sh | 11 +- actions/tool-golangci-lint.sh | 13 +- actions/tool-govet.sh | 9 +- actions/tool-mkdocs.sh | 58 +++++++ actions/tool-nim-build.sh | 20 +++ actions/tool-nimpretty.sh | 15 +- actions/tool-node-app-build.sh | 16 ++ actions/tool-prettier-init.sh | 9 - actions/tool-shdoc.sh | 32 ++-- actions/tool-shellcheck.sh | 2 +- actions/tool-shellharden.sh | 15 +- actions/util-Bash-generate-bins.sh | 12 +- actions/util-Bash-version-bump.sh | 2 +- .../run.sh | 0 .../toast.yml | 0 .../dev/PKGBUILD | 0 .../release/PKGBUILD | 0 .../toast.yml | 0 .../pkg.spec | 0 .../toast.yml | 0 configs/standard-version/.versionrc.json | 8 - configs/standard-version/glue-auto-updater.js | 27 --- .../.golangci.yaml | 0 .../.vscode/settings.json | 0 .../.goreleaser.yml | 0 configs/tool-mkdocs/Dockerfile | 6 + configs/tool-mkdocs/mkdocs.yml | 88 ++++++++++ configs/tool-mkdocs/pyproject.toml | 22 +++ configs/tool-mkdocs/toast.yml | 27 +++ docs/task-steps.md | 2 + docs/tips.md | 49 ++++++ tasks/Bash.build.sh | 4 +- tasks/Bash.docs.sh | 14 +- tasks/Bash.lint.sh | 2 +- tasks/Bash.release.sh | 4 +- tasks/Bash.run.sh | 2 +- tasks/Bash.test.sh | 2 +- tasks/Go_Binary.build.sh | 10 +- tasks/Go_Binary.release.sh | 51 +----- tasks/Nim_Binary.build.sh | 14 +- tasks/NodeJS_Server.ci.sh | 2 +- tasks/NodeJS_Server.deploy.sh | 2 +- tasks/NodeJS_Server.lint.sh | 2 +- tasks/NodeJS_Server.release.sh | 2 +- tasks/Python.build.sh | 2 +- util/action.sh | 30 ---- util/bootstrap.sh | 163 ++++++++++++++++-- util/ensure.sh | 89 ++++++++++ util/generated.sh | 42 ----- util/is.sh | 15 ++ util/semver.sh | 5 + util/task.sh | 25 --- util/toml.sh | 11 +- util/util.sh | 123 ++++++++++--- 71 files changed, 837 insertions(+), 385 deletions(-) delete mode 100755 actions/do-go-check.sh delete mode 100755 actions/do-nim-build.sh delete mode 100755 actions/do-node-app-build.sh rename actions/{do-go-build.sh => effect-deb-package.sh} (60%) rename actions/{result-pacman-package.sh => effect-pacman-package.sh} (87%) create mode 100755 actions/effect-rpm-package.sh delete mode 100755 actions/result-deb-package.sh delete mode 100755 actions/result-rpm-package.sh delete mode 100755 actions/tool-eslint-init.sh create mode 100755 actions/tool-go-build.sh create mode 100644 actions/tool-mkdocs.sh create mode 100755 actions/tool-nim-build.sh create mode 100755 actions/tool-node-app-build.sh delete mode 100755 actions/tool-prettier-init.sh rename configs/{result-deb-package => effect-deb-package}/run.sh (100%) rename configs/{result-deb-package => effect-deb-package}/toast.yml (100%) rename configs/{result-pacman-package => effect-pacman-package}/dev/PKGBUILD (100%) rename configs/{result-pacman-package => effect-pacman-package}/release/PKGBUILD (100%) rename configs/{result-pacman-package => effect-pacman-package}/toast.yml (100%) rename configs/{result-rpm-package => effect-rpm-package}/pkg.spec (100%) rename configs/{result-rpm-package => effect-rpm-package}/toast.yml (100%) delete mode 100644 configs/standard-version/.versionrc.json delete mode 100644 configs/standard-version/glue-auto-updater.js rename configs/{golangci-lint => tool-golangci-lint}/.golangci.yaml (100%) rename configs/{golangci-lint => tool-golangci-lint}/.vscode/settings.json (100%) rename configs/{goreleaser => tool-goreleaser}/.goreleaser.yml (100%) create mode 100644 configs/tool-mkdocs/Dockerfile create mode 100644 configs/tool-mkdocs/mkdocs.yml create mode 100644 configs/tool-mkdocs/pyproject.toml create mode 100644 configs/tool-mkdocs/toast.yml create mode 100644 docs/tips.md delete mode 100644 util/action.sh delete mode 100644 util/generated.sh delete mode 100644 util/task.sh diff --git a/.editorconfig b/.editorconfig index a54d21c..b7e7b85 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,3 +1,6 @@ [*] indent_style = tab indent_size = 4 + +[*.{yaml,yml}] +indent_style = space diff --git a/README.md b/README.md index b51b864..fc096c7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ # glue-store My personal store for [glue](https://github.com/eankeen/glue); visit that repository for more information about Glue + +TODO + +- linter for bootstrapping files and functions diff --git a/actions/do-go-check.sh b/actions/do-go-check.sh deleted file mode 100755 index f764b5e..0000000 --- a/actions/do-go-check.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash -eval "$GLUE_BOOTSTRAP" -bootstrap || exit - -ensure.cmd 'aligncheck' -ensure.cmd 'structcheck' -ensure.cmd 'varcheck' - -aligncheck ./... - -structcheck -t ./... - -varcheck ./... - -unbootstrap diff --git a/actions/do-nim-build.sh b/actions/do-nim-build.sh deleted file mode 100755 index 82d22db..0000000 --- a/actions/do-nim-build.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash -eval "$GLUE_BOOTSTRAP" -bootstrap || exit - -ensure.cmd 'nimble' - -projectName= -for nimbleFile in *.nimble; do - projectName=${nimbleFile%%.*} - break -done - -ensure.nonZero 'projectName' "$projectName" - -nimble build "$projectName" - -unbootstrap diff --git a/actions/do-node-app-build.sh b/actions/do-node-app-build.sh deleted file mode 100755 index 0810d49..0000000 --- a/actions/do-node-app-build.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash -eval "$GLUE_BOOTSTRAP" -bootstrap || exit - -if [ -f "$GLUE_WD/package-lock.json" ]; then - npm build -elif [ -f "$GLUE_WD/yarn.lock" ]; then - yarn build -elif [ -f "$GLUE_WD/pnpm-lock.yaml" ]; then - pnpm build -fi - -unbootstrap diff --git a/actions/do-go-build.sh b/actions/effect-deb-package.sh similarity index 60% rename from actions/do-go-build.sh rename to actions/effect-deb-package.sh index fb7ffb7..85ee929 100755 --- a/actions/do-go-build.sh +++ b/actions/effect-deb-package.sh @@ -1,7 +1,10 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap -ensure.cmd 'go' +action() { + : +} +action "$@" unbootstrap diff --git a/actions/effect-git-tag.sh b/actions/effect-git-tag.sh index 0666888..2c1deb3 100644 --- a/actions/effect-git-tag.sh +++ b/actions/effect-git-tag.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap action() { local version="$1" diff --git a/actions/effect-github-release.sh b/actions/effect-github-release.sh index b83530e..5e8af9f 100644 --- a/actions/effect-github-release.sh +++ b/actions/effect-github-release.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap action() { local version="$1" diff --git a/actions/result-pacman-package.sh b/actions/effect-pacman-package.sh similarity index 87% rename from actions/result-pacman-package.sh rename to actions/effect-pacman-package.sh index b2a058a..543788c 100755 --- a/actions/result-pacman-package.sh +++ b/actions/effect-pacman-package.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap # makerepropkg *.zst # repro -f *.zst @@ -34,11 +34,11 @@ action() { ensure.nonZero 'myEmail' "$myEmail" ensure.nonZero 'myVer' "$myVer" - # glue useConfig(result-pacman-package) - util.get_config "result-pacman-package/dev/PKGBUILD" - pkgbuildFile="$REPLY" + # glue useConfig(effect-pacman-package) + util.get_config "effect-pacman-package/dev/PKGBUILD" + cfgPkgbuild="$REPLY" - generated.in 'result-pacman-package' + bootstrap.generated 'effect-pacman-package' ( cd "$GENERATED_DIR" || error.cd_failed mkdir dev @@ -47,7 +47,7 @@ action() { tar --create --directory "$GLUE_WD" --file "$myProject-$myVer.tar.gz" --exclude './.git' \ --exclude "$myProject-$myVer.tar.gz" --transform "s/^\./$myProject-$myVer/" ./ - cp "$pkgbuildFile" . + cp "$cfgPkgbuild" . sed -i -e "s/# Maintainer:.*/# Maintainer: $myName <$myEmail>/g" PKGBUILD sed -i -e "s/pkgname=.*\$/pkgname='$myProject'/g" PKGBUILD sed -i -e "s/pkgver=.*\$/pkgver='$myVer'/g" PKGBUILD @@ -62,7 +62,7 @@ action() { makepkg -Cfsrc namcap ./*.zst ) || exit - generated.out + unbootstrap.generated } action "$@" diff --git a/actions/effect-rpm-package.sh b/actions/effect-rpm-package.sh new file mode 100755 index 0000000..25a09d8 --- /dev/null +++ b/actions/effect-rpm-package.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +eval "$GLUE_BOOTSTRAP" +bootstrap + +action() { + # wget https://github.com/eankeen/bash-args/archive/refs/tags/v0.5.0.tar.gz + # mv v0.5.0.tar.gz bash-args_0.5.0.orig.tar.gz + # tar xf bash-args_0.5.0.orig.tar.gz + + fedpkg --release f34 local +} + +action "$@" +unbootstrap diff --git a/actions/result-deb-package.sh b/actions/result-deb-package.sh deleted file mode 100755 index 57e1baa..0000000 --- a/actions/result-deb-package.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash -eval "$GLUE_BOOTSTRAP" -bootstrap || exit - -unbootstrap diff --git a/actions/result-rpm-package.sh b/actions/result-rpm-package.sh deleted file mode 100755 index 8eb1060..0000000 --- a/actions/result-rpm-package.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -eval "$GLUE_BOOTSTRAP" -bootstrap || exit - -# wget https://github.com/eankeen/bash-args/archive/refs/tags/v0.5.0.tar.gz -# mv v0.5.0.tar.gz bash-args_0.5.0.orig.tar.gz -# tar xf bash-args_0.5.0.orig.tar.gz - -fedpkg --release f34 local - -unbootstrap diff --git a/actions/tool-bats.sh b/actions/tool-bats.sh index b8d2bf7..5691943 100755 --- a/actions/tool-bats.sh +++ b/actions/tool-bats.sh @@ -1,20 +1,19 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit - -ensure.cmd 'bats' +bootstrap action() { local -a dirs=() local exitCode=0 + ensure.cmd 'bats' + ( local exitCode=0 if [ -d pkg ]; then - if ! cd pkg; then - error.cd_failed - fi + ensure.cd 'pkg' + dirs=(../test ../tests) else dirs=(test tests) diff --git a/actions/tool-checkmake.sh b/actions/tool-checkmake.sh index aca3a90..6fcfcce 100755 --- a/actions/tool-checkmake.sh +++ b/actions/tool-checkmake.sh @@ -1,12 +1,15 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap -ensure.cmd 'checkmake' +action() { + ensure.cmd 'checkmake' -for file in **/{Makefile,GNUMakefile,*.mk}; do - checkmake "$file" -done -unset -v file + for file in **/{Makefile,GNUMakefile,*.mk}; do + checkmake "$file" + done + unset -v file +} +action "$@" unbootstrap diff --git a/actions/tool-conventional-changelog.sh b/actions/tool-conventional-changelog.sh index fc13d0e..ec0f5de 100755 --- a/actions/tool-conventional-changelog.sh +++ b/actions/tool-conventional-changelog.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap ensure.cmd 'conventional-changelog' @@ -15,16 +15,16 @@ action() { # glue useConfig(tool-conventional-changelog) util.get_config 'tool-conventional-changelog/context.json' - local configContextJson="$REPLY" + local cfgContextJson="$REPLY" toml.get_key 'gitRepoName' 'glue.toml' local gitRepoName="$REPLY" ensure.nonZero 'gitRepoName' "$gitRepoName" - generated.in 'tool-conventional-changelog'; ( + bootstrap.generated 'tool-conventional-changelog'; ( cd "$GENERATED_DIR" || error.cd_failed - cp "$configContextJson" . + cp "$cfgContextJson" . sed -i \ -e "s/TEMPLATE_CONTEXT_VERSION/$version/g" \ -e "s/TEMPLATE_CONTEXT_REPOSITORY/$gitRepoName/g" \ @@ -46,7 +46,7 @@ action() { --release-count 1 \ --context "context.json" \ --commit-path "$GLUE_WD" - ); generated.out + ); unbootstrap.generated REPLY="$GENERATED_DIR/CHANGELOG-CURRENT.md" } diff --git a/actions/tool-eslint-init.sh b/actions/tool-eslint-init.sh deleted file mode 100755 index 8ae7642..0000000 --- a/actions/tool-eslint-init.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -eval "$GLUE_BOOTSTRAP" -bootstrap || exit - -# glue useConfig(.eslintrc.js) -util.ln_config ".eslintrc.js" - -unbootstrap diff --git a/actions/tool-go-build.sh b/actions/tool-go-build.sh new file mode 100755 index 0000000..f8d5054 --- /dev/null +++ b/actions/tool-go-build.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +eval "$GLUE_BOOTSTRAP" +bootstrap + +action() { + ensure.cmd 'go' + + go build ./... +} + +action "$@" +unbootstrap diff --git a/actions/tool-gofmt.sh b/actions/tool-gofmt.sh index 8feec12..31a4d9b 100755 --- a/actions/tool-gofmt.sh +++ b/actions/tool-gofmt.sh @@ -1,10 +1,13 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap -ensure.cmd 'gofmt' +action() { + ensure.cmd 'gofmt' -go list -f '{{.Dir}}' ./... \ - | xargs gofmt -s -l -w + go list -f '{{.Dir}}' ./... \ + | xargs gofmt -s -l -w +} +action "$@" unbootstrap diff --git a/actions/tool-golangci-lint.sh b/actions/tool-golangci-lint.sh index e75e281..688daf6 100755 --- a/actions/tool-golangci-lint.sh +++ b/actions/tool-golangci-lint.sh @@ -1,12 +1,15 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap -# glue useConfig(golangci-lint) -util.ln_config "golangci-lint/.golangci.yaml" ".golangci.yaml" +action() { + # glue useConfig(golangci-lint) + util.ln_config "golangci-lint/.golangci.yaml" ".golangci.yaml" -ensure.cmd 'golangci-lint' + ensure.cmd 'golangci-lint' -golangci-lint run --enable-all ./... + golangci-lint run --enable-all ./... +} +action "$@" unbootstrap diff --git a/actions/tool-govet.sh b/actions/tool-govet.sh index cc254a2..94309df 100755 --- a/actions/tool-govet.sh +++ b/actions/tool-govet.sh @@ -1,9 +1,12 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap -ensure.cmd 'go' +action() { + ensure.cmd 'go' -go get ./... + go get ./... +} +action "$@" unbootstrap diff --git a/actions/tool-mkdocs.sh b/actions/tool-mkdocs.sh new file mode 100644 index 0000000..219fe8f --- /dev/null +++ b/actions/tool-mkdocs.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +eval "$GLUE_BOOTSTRAP" +bootstrap + +action() { + ensure.cmd 'toast' + + util.shopt -s globstar + + local exitCode=0 + + toml.get_key 'gitRemoteUser' glue.toml + local gitRemoteUser="$REPLY" + + toml.get_key 'gitRemoteRepo' glue.toml + local gitRemoteRepo="$REPLY" + + bootstrap.generated 'tool-mkdocs' + ensure.cd "$GENERATED_DIR" + + # TODO: only import docs + git clone --depth 1 file://"$GLUE_WD" . + + # glue useConfig(tool-mkdocs) + util.get_config 'tool-mkdocs/mkdocs.yml' + local cfgMkdocsYml="$REPLY" + + util.get_config 'tool-mkdocs/pyproject.toml' + local cfgPyprojectToml="$REPLY" + + util.get_config 'tool-mkdocs/toast.yml' + local cfgToastYml="$REPLY" + + cp "$cfgMkdocsYml" "$cfgPyprojectToml" "$cfgToastYml" . + + ensure.file "$GLUE_WD/README.md" + cp "$GLUE_WD/README.md" 'docs/index.md' + + # Copy specialized files to 'docs' before build + util.run_hook 'hook.tool-mkdocs.copy_docs' + + toast mkdocs + cp -r "$GENERATED_DIR/site" "site" + + ensure.cd site + git init --initial-branch=main + git add -A + # TODO: commit message + git commit -m 'Update site' + # TODO: rebase or configure merge strategy + git push -f "https://github.com/$gitRemoteUser/$gitRemoteRepo.git" main:gh-pages + unbootstrap.generated + + REPLY="$exitCode" +} + +action "$@" +unbootstrap diff --git a/actions/tool-nim-build.sh b/actions/tool-nim-build.sh new file mode 100755 index 0000000..973a632 --- /dev/null +++ b/actions/tool-nim-build.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +eval "$GLUE_BOOTSTRAP" +bootstrap + +action() { + ensure.cmd 'nimble' + + projectName= + for nimbleFile in *.nimble; do + projectName=${nimbleFile%%.*} + break + done + + ensure.nonZero 'projectName' "$projectName" + + nimble build "$projectName" +} + +action "$@" +unbootstrap diff --git a/actions/tool-nimpretty.sh b/actions/tool-nimpretty.sh index ae0fd00..ef907ec 100755 --- a/actions/tool-nimpretty.sh +++ b/actions/tool-nimpretty.sh @@ -1,13 +1,16 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap -ensure.cmd 'nimpretty' +action() { + ensure.cmd 'nimpretty' -util.shopt -s dotglob -util.shopt -s globstar -util.shopt -u nullglob + util.shopt -s dotglob + util.shopt -s globstar + util.shopt -u nullglob -nimpretty --maxLinelen:100 ./**/*.nim + nimpretty --maxLinelen:100 ./**/*.nim +} +action "$@" unbootstrap diff --git a/actions/tool-node-app-build.sh b/actions/tool-node-app-build.sh new file mode 100755 index 0000000..36a31e4 --- /dev/null +++ b/actions/tool-node-app-build.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +eval "$GLUE_BOOTSTRAP" +bootstrap + +action() { + if [ -f "$GLUE_WD/package-lock.json" ]; then + npm build + elif [ -f "$GLUE_WD/yarn.lock" ]; then + yarn build + elif [ -f "$GLUE_WD/pnpm-lock.yaml" ]; then + pnpm build + fi +} + +action "$@" +unbootstrap diff --git a/actions/tool-prettier-init.sh b/actions/tool-prettier-init.sh deleted file mode 100755 index 3f11133..0000000 --- a/actions/tool-prettier-init.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash -eval "$GLUE_BOOTSTRAP" -bootstrap || exit - -# glue useConfig(.prettierrc.js) - -util.get_config '.prettierrc.js' - -unbootstrap diff --git a/actions/tool-shdoc.sh b/actions/tool-shdoc.sh index 093804f..68999fc 100755 --- a/actions/tool-shdoc.sh +++ b/actions/tool-shdoc.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap action() { ensure.cmd 'shdoc' @@ -11,31 +11,33 @@ action() { local exitCode=0 - generated.in 'tool-shdoc'; ( - if [ ! -d pkg ]; then - error.non_conforming "'./pkg' directory does not exist" - fi - - if ! cd pkg; then - error.cd_failed - fi + bootstrap.generated 'tool-shdoc'; ( + ensure.cd 'pkg' local exitCode=0 for file in ./**/*.{sh,bash}; do - local output="$GENERATED_DIR/$file" - mkdir -p "${output%/*}" - output="${output%.*}" - output="$output.md" - if shdoc < "$file" > "$output"; then : else + local outputFile="$GENERATED_DIR/$file" + mkdir -p "${outputFile%/*}" + outputFile="${outputFile%.*}" + outputFile="$outputFile.md" + if shdoc < "$file" > "$outputFile"; then : else + # TODO: set exitCode on all if is.wet_release; then exitCode=$? fi fi + + if [ "$(stat -c "%s" "$outputFile")" -le 5 ]; then + rm "$outputFile" + rmdir -p --ignore-fail-on-non-empty "$GENERATED_DIR" + fi + + mkdir -p "$GENERATED_DIR" done return "$exitCode" - ); exitCode=$?; generated.out + ); exitCode=$?; unbootstrap.generated REPLY="$exitCode" } diff --git a/actions/tool-shellcheck.sh b/actions/tool-shellcheck.sh index 37f2058..2a2a989 100755 --- a/actions/tool-shellcheck.sh +++ b/actions/tool-shellcheck.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap action() { ensure.cmd 'shellcheck' diff --git a/actions/tool-shellharden.sh b/actions/tool-shellharden.sh index afe761f..a16603a 100755 --- a/actions/tool-shellharden.sh +++ b/actions/tool-shellharden.sh @@ -1,13 +1,16 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap -ensure.cmd 'shellharden' +action() { + ensure.cmd 'shellharden' -util.shopt -s globstar -util.shopt -s nullglob + util.shopt -s globstar + util.shopt -s nullglob -# shellharden --suggest -- ./**/*.{sh,bash} -# shellharden --check -- ./**/*.{sh,bash} + shellharden --suggest -- ./**/*.{sh,bash} + shellharden --check -- ./**/*.{sh,bash} +} +action "$@" unbootstrap diff --git a/actions/util-Bash-generate-bins.sh b/actions/util-Bash-generate-bins.sh index eb241f4..d5b9f4d 100755 --- a/actions/util-Bash-generate-bins.sh +++ b/actions/util-Bash-generate-bins.sh @@ -1,6 +1,6 @@ # shellcheck shell=bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap action() { toml.get_key 'distroPackageName' glue.toml @@ -70,10 +70,12 @@ realpath.absolute() { # distribution's package manager, the 'lib' files are in an extra subfolder # compared to an installation through Git (ex. Basher) realpath.location "${BASH_SOURCE[0]}" -if [ -d "$REPLY"/../lib/TEMPLATE_PACKAGE_DIR ]; then - PROGRAM_LIB_DIR="$REPLY/../lib/TEMPLATE_PACKAGE_DIR" -elif [ -d "$REPLY"/../lib ]; then - PROGRAM_LIB_DIR="$REPLY/../lib" +REPLY="${REPLY/%\/}" +REPLY="${REPLY%/*}" +if [ -d "$REPLY/lib/TEMPLATE_PACKAGE_DIR" ]; then + PROGRAM_LIB_DIR="$REPLY/lib/TEMPLATE_PACKAGE_DIR" +elif [ -d "$REPLY/lib" ]; then + PROGRAM_LIB_DIR="$REPLY/lib" else echo "Error: Could not determine \$PROGRAM_LIB_DIR" exit 1 diff --git a/actions/util-Bash-version-bump.sh b/actions/util-Bash-version-bump.sh index 954bb02..d5bfec7 100644 --- a/actions/util-Bash-version-bump.sh +++ b/actions/util-Bash-version-bump.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap action() { local newVersion="$1" diff --git a/configs/result-deb-package/run.sh b/configs/effect-deb-package/run.sh similarity index 100% rename from configs/result-deb-package/run.sh rename to configs/effect-deb-package/run.sh diff --git a/configs/result-deb-package/toast.yml b/configs/effect-deb-package/toast.yml similarity index 100% rename from configs/result-deb-package/toast.yml rename to configs/effect-deb-package/toast.yml diff --git a/configs/result-pacman-package/dev/PKGBUILD b/configs/effect-pacman-package/dev/PKGBUILD similarity index 100% rename from configs/result-pacman-package/dev/PKGBUILD rename to configs/effect-pacman-package/dev/PKGBUILD diff --git a/configs/result-pacman-package/release/PKGBUILD b/configs/effect-pacman-package/release/PKGBUILD similarity index 100% rename from configs/result-pacman-package/release/PKGBUILD rename to configs/effect-pacman-package/release/PKGBUILD diff --git a/configs/result-pacman-package/toast.yml b/configs/effect-pacman-package/toast.yml similarity index 100% rename from configs/result-pacman-package/toast.yml rename to configs/effect-pacman-package/toast.yml diff --git a/configs/result-rpm-package/pkg.spec b/configs/effect-rpm-package/pkg.spec similarity index 100% rename from configs/result-rpm-package/pkg.spec rename to configs/effect-rpm-package/pkg.spec diff --git a/configs/result-rpm-package/toast.yml b/configs/effect-rpm-package/toast.yml similarity index 100% rename from configs/result-rpm-package/toast.yml rename to configs/effect-rpm-package/toast.yml diff --git a/configs/standard-version/.versionrc.json b/configs/standard-version/.versionrc.json deleted file mode 100644 index 08e9f10..0000000 --- a/configs/standard-version/.versionrc.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "bumpFiles": [ - { - "filename": "glue-auto.toml", - "updater": ".glue/configs/auto/glue-auto-updater.js" - } - ] -} diff --git a/configs/standard-version/glue-auto-updater.js b/configs/standard-version/glue-auto-updater.js deleted file mode 100644 index 7ed5ad4..0000000 --- a/configs/standard-version/glue-auto-updater.js +++ /dev/null @@ -1,27 +0,0 @@ -module.exports.readVersion = function(/** @type string */ content) { - const contentArray = content.split("\n") - - for (let i = 0; i < contentArray.length; i++) { - const line = contentArray[i]; - - if (line.match(/[ \t]*version[ \t]*=/u)) { - const start = line.indexOf('"'); - const last = line.lastIndexOf('"'); - return line.slice(start + 1, last); - } - } - - return '' -} - -module.exports.writeVersion = function(/** @type string */ content, /** @type string */ version) { - const contentArray = content.split("\n") - - for (let i = 0; i < contentArray.length; i++) { - if (contentArray[i].match(/[ \t]*version[ \t]*=/u)) { - contentArray[i] = `version = "${version}"` - } - } - - return contentArray.join('\n') -} diff --git a/configs/golangci-lint/.golangci.yaml b/configs/tool-golangci-lint/.golangci.yaml similarity index 100% rename from configs/golangci-lint/.golangci.yaml rename to configs/tool-golangci-lint/.golangci.yaml diff --git a/configs/golangci-lint/.vscode/settings.json b/configs/tool-golangci-lint/.vscode/settings.json similarity index 100% rename from configs/golangci-lint/.vscode/settings.json rename to configs/tool-golangci-lint/.vscode/settings.json diff --git a/configs/goreleaser/.goreleaser.yml b/configs/tool-goreleaser/.goreleaser.yml similarity index 100% rename from configs/goreleaser/.goreleaser.yml rename to configs/tool-goreleaser/.goreleaser.yml diff --git a/configs/tool-mkdocs/Dockerfile b/configs/tool-mkdocs/Dockerfile new file mode 100644 index 0000000..2600789 --- /dev/null +++ b/configs/tool-mkdocs/Dockerfile @@ -0,0 +1,6 @@ +FROM archlinux + +RUN pacman -Sy --noconfirm python poetry git + +RUN poetry init --name 'packageName' --description 'Description' --author 'author' -l MIT && \ + && poetry add mkdocs mkdocs-material mkdocs-git-revision-date-localized-plugin mike mkdocs-mermaid2-plugin mkdocs-macros-plugin diff --git a/configs/tool-mkdocs/mkdocs.yml b/configs/tool-mkdocs/mkdocs.yml new file mode 100644 index 0000000..40166bf --- /dev/null +++ b/configs/tool-mkdocs/mkdocs.yml @@ -0,0 +1,88 @@ +site_name: Glue +# TODO: fix +site_url: https://github.com/eankeen/glue +repo_url: https://github.com/eankeen/glue +repo_name: eankeen/glue +site_description: Glue is the manifestation of a generalized task runner with respect to language agnosticity +site_author: Edwin Kofler +copyright: © 2021 +google_analytics: null +remote_branch: gh-pages +remote_name: origin +edit_uri: edit/main/docs +theme: + name: material + palette: + scheme: slate # 'default' for light theme + primary: deep orange + accent: deep orange + language: en + # logo: assets/logo.png + favicon: images/favicon.png + features: + # - navigation.instant + icon: + repo: fontawesome/brands/github + extra: + version: + provider: mike + social: + - name: Edwin's DockerHub + icon: fontawesome/brands/docker + link: https://hub.docker.com/u/eankeen + - name: Edwin's Medium + icon: fontawesome/brands/medium + link: https://medium.com/@eankeen + - name: Edwin's Product Hunt + icon: fontawesome/brands/product-hunt + link: https://www.producthunt.com/@eankeen + - name: Edwin's Twitter + icon: fontawesome/brands/twitter + link: https://twitter.com/eankeen + copyright: Copyright © 2021 Edwin Kofler +docs_dir: docs +site_dir: site +extra: + version: 1.0 +use_directory_urls: true +strict: true +dev_addr: 127.0.0.1:8000 +markdown_extensions: + - toc: + permalink: ⚓︎ + - admonition: + - pymdownx.details + # - pymdownx.superfences: + # # make exceptions to highlighting of code: + # custom_fences: + # - name: mermaid + # class: mermaid + # format: !!python/name:mermaid2.fence_mermaid + - attr_list + - footnotes + - pymdownx.critic + - pymdownx.betterem: + smart_enable: all + - pymdownx.caret + - pymdownx.mark + - pymdownx.tilde + - pymdownx.smartsymbols + - def_list + - meta +plugins: + - search: + separator: '[\s\-\.\_]+' + min_search_length: 2 + lang: ['en'] + prebuild_index: false + - git-revision-date-localized: + type: timeago + fallback_to_build_date: false + enable_creation_date: true + - mike: + version_selector: true + canonical_version: null + - mermaid2: + arguments: + theme: dark + - macros diff --git a/configs/tool-mkdocs/pyproject.toml b/configs/tool-mkdocs/pyproject.toml new file mode 100644 index 0000000..23b4850 --- /dev/null +++ b/configs/tool-mkdocs/pyproject.toml @@ -0,0 +1,22 @@ +[tool.poetry] +name = "glue" +version = "0.1.0" +description = "" +authors = ["Edwin Kofler "] + +[tool.poetry.dependencies] +python = "^3.9" +mkdocs = "^1.1.2" +mkdocs-bootstrap386 = "^0.0.2" +mkdocs-bootswatch = "^1.1" +mkdocs-material = "^7.1.5" +mkdocs-git-revision-date-localized-plugin = "^0.9.2" +mike = "^1.0.0" +mkdocs-mermaid2-plugin = "^0.5.1" +mkdocs-macros-plugin = "^0.5.5" + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/configs/tool-mkdocs/toast.yml b/configs/tool-mkdocs/toast.yml new file mode 100644 index 0000000..09ef77f --- /dev/null +++ b/configs/tool-mkdocs/toast.yml @@ -0,0 +1,27 @@ +image: archlinux +tasks: + # TODO: not as root + init: + command: | + pacman -Sy --noconfirm python poetry git + # TODO: + # poetry init --name 'packageName' --description 'Description' --author 'author' -l MIT + poetry init -n + poetry add mkdocs mkdocs-material mkdocs-git-revision-date-localized-plugin mike mkdocs-mermaid2-plugin mkdocs-macros-plugin + + # init2: + # input_paths: + # - ./ + # command: | + # poetry install + + mkdocs: + dependencies: + - init + # - init2 + input_paths: + - ./ + output_paths: + - site + command: | + poetry run mkdocs build diff --git a/docs/task-steps.md b/docs/task-steps.md index c867a6a..84650b9 100644 --- a/docs/task-steps.md +++ b/docs/task-steps.md @@ -1,5 +1,7 @@ # General Steps for Tasks +TODO add `--help` as described + Common task identifiers like 'build', 'docs', 'lint' are common across languages. This document lists them with some explanations. Depending on the project type, some tasks may accept flags. For example, use `glue cmd docs -- --help` to show the help, if applicable ## docs diff --git a/docs/tips.md b/docs/tips.md new file mode 100644 index 0000000..2eaa9f1 --- /dev/null +++ b/docs/tips.md @@ -0,0 +1,49 @@ +# Tips + +Besides the general tips when writing Bash, consider the following when writing scripts + +## Avoid explicit subshells + +Consider this `action`: + +```bash +#!/usr/bin/env bash +eval "$GLUE_BOOTSTRAP" +bootstrap + +action() { + ensure.cmd 'toast' + + local exitCode=0 + + bootstrap.generated 'tool-mkdocs'; ( + ensure.cd "$GENERATED_DIR" + + # Intermediary commands hidden for brevity + + toast mkdocs + ); exitCode=$?; unbootstrap.generated + + REPLY="$exitCode" +} + +action "$@" +unbootstrap +``` + +If `toast mkdocs` errors, then the callstack is printed like so: + +```txt +[INFO] Running task mkdocs… +[ERROR] Error appending data to tar archive. Reason: paths in archives must not have `..` when setting path for scratch +CALLSTACK +=> tool-mkdocs.sh:action() + => tool-shdoc.sh:action() + => Bash.docs.sh:task() +CALLSTACK +=> tool-mkdocs.sh:action() + => tool-shdoc.sh:action() + => Bash.docs.sh:task() +``` + +Without `set -E`, the callstack is not printed at all! So don't use subshells explicitly if you don't have to. The EXIT trap and `unbootstrap.generated` already automatically change directory back to the original directory (`$GLOBAL_ORIGINAL_WD`) diff --git a/tasks/Bash.build.sh b/tasks/Bash.build.sh index fe1dfac..e7bac8a 100755 --- a/tasks/Bash.build.sh +++ b/tasks/Bash.build.sh @@ -1,12 +1,12 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap task() { util.extract_version_string local version="$REPLY" - custom.bump_version_hook() { + hook.util.update_version_strings.bump_version() { local version="$1" # glue useAction(util-Bash-version-bump.sh) diff --git a/tasks/Bash.docs.sh b/tasks/Bash.docs.sh index 3b9c2b2..6b971fd 100755 --- a/tasks/Bash.docs.sh +++ b/tasks/Bash.docs.sh @@ -1,14 +1,24 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap task() { # glue useAction(tool-shdoc.sh) util.get_action 'tool-shdoc.sh' source "$REPLY" + local exitOne="$REPLY" + + hook.tool-mkdocs.copy_docs() { + mkdir -p docs + cp -r "$GLUE_WD/.glue/generated/tool-shdoc" ./docs/shdoc + } + # glue useAction(tool-mkdocs.sh) + util.get_action 'tool-mkdocs.sh' + source "$REPLY" + local exitTwo="$REPLY" # shellcheck disable=SC2269 - REPLY="$REPLY" + REPLY="$((exitOne | exitTwo))" } task "$@" diff --git a/tasks/Bash.lint.sh b/tasks/Bash.lint.sh index 3d5f69b..a9afb2e 100755 --- a/tasks/Bash.lint.sh +++ b/tasks/Bash.lint.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap task() { # glue useAction(tool-shellcheck.sh) diff --git a/tasks/Bash.release.sh b/tasks/Bash.release.sh index e371cf9..95af4d5 100755 --- a/tasks/Bash.release.sh +++ b/tasks/Bash.release.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap task() { declare -g RELEASE_STATUS=dry @@ -50,7 +50,7 @@ task() { util.prompt_new_version_string local version="$REPLY" - custom.bump_version_hook() { + hook.util.update_version_strings.bump_version() { local version="$1" # glue useAction(util-Bash-version-bump.sh) diff --git a/tasks/Bash.run.sh b/tasks/Bash.run.sh index 8f28e8c..fe64fda 100755 --- a/tasks/Bash.run.sh +++ b/tasks/Bash.run.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap task() { local cmdName="$1" diff --git a/tasks/Bash.test.sh b/tasks/Bash.test.sh index 09cfa19..e3dd4ce 100755 --- a/tasks/Bash.test.sh +++ b/tasks/Bash.test.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap task() { # glue useAction(tool-bats.sh) diff --git a/tasks/Go_Binary.build.sh b/tasks/Go_Binary.build.sh index b5edbf2..d50869c 100755 --- a/tasks/Go_Binary.build.sh +++ b/tasks/Go_Binary.build.sh @@ -1,5 +1,11 @@ #!/usr/bin/env bash +eval "$GLUE_BOOTSTRAP" +bootstrap -# glue useAction(tool-golangci-lint.sh) +task() { + go build -ldflags "-X main.version=1.0.1" main.go +} -go build -ldflags "-X main.version=1.0.1" main.go + +task "$@" +unbootstrap diff --git a/tasks/Go_Binary.release.sh b/tasks/Go_Binary.release.sh index 505164e..939757b 100755 --- a/tasks/Go_Binary.release.sh +++ b/tasks/Go_Binary.release.sh @@ -1,52 +1,11 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap -ensure.cmd 'git' -ensure.cmd 'gh' +task() { + : +} -# Ensure working tree not dirty -if [ -n "$(git status --porcelain)" ]; then - die 'Working tree still dirty. Please commit all changes before making a release' -fi - -# Get current version -toml.get_key version glue-auto.toml -declare -r currentVersion="$REPLY" - -# Get new version -echo "Current Version: $currentVersion" -read -rp 'New Version? ' -ei "$currentVersion" -declare -r newVersion="$REPLY" - -# Ensure new version is valid (does not already exist) -if [ -n "$(git tag -l "v$newVersion")" ]; then - # TODO: ensure there are no tag sthat exists that are greater than it - die 'Version already exists in a Git tag' -fi - -# Embed version string in application and working tree -sed -i -e "s|\(version=\"\).*\(\"\)|\1${currentVersion}\2|g" glue.toml -sed -i -e "s|\(PROGRAM_VERSION=\"\).*\(\"\)|\1${currentVersion}\2|g" glue.sh - -# Build - -# Local Release -git tag -a "v$newVersion" -m "Release $newVersion" HEAD -git push --follow-tags origin HEAD - -declare -a args=() -if [ -f CHANGELOG.md ]; then - args+=("--notes-file" "CHANGELOG.md") -elif [ -f changelog.md ]; then - args+=("--notes-file" "changelog.md") -else - log.warn 'CHANGELOG.md file not found. Not creating a notes file for release' -fi - -# Remote Release -toml.get_key project glue.toml -projectName="${REPLY:-Release}" -gh release create "v$newVersion" --target main --title "$projectName v$newVersion" "${args[@]}" +task "$@" unbootstrap diff --git a/tasks/Nim_Binary.build.sh b/tasks/Nim_Binary.build.sh index a68a30e..a28d1b9 100755 --- a/tasks/Nim_Binary.build.sh +++ b/tasks/Nim_Binary.build.sh @@ -1,15 +1,15 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap -# glue useAction(do-nim-format.sh) -util.get_action "do-nim-format.sh" +# glue useAction(tool-nim-format.sh) +util.get_action "tool-nim-format.sh" source "$REPLY" -# glue useAction(do-nim-lint.sh) -util.get_action "do-nim-lint.sh" +# glue useAction(tool-nim-lint.sh) +util.get_action "tool-nim-lint.sh" source "$REPLY" -# glue useAction(do-nim-build.sh) -util.get_action "do-nim-build.sh" +# glue useAction(tool-nim-build.sh) +util.get_action "tool-nim-build.sh" source "$REPLY" diff --git a/tasks/NodeJS_Server.ci.sh b/tasks/NodeJS_Server.ci.sh index 57e1baa..6a4b947 100755 --- a/tasks/NodeJS_Server.ci.sh +++ b/tasks/NodeJS_Server.ci.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap unbootstrap diff --git a/tasks/NodeJS_Server.deploy.sh b/tasks/NodeJS_Server.deploy.sh index 4d6595f..5973055 100755 --- a/tasks/NodeJS_Server.deploy.sh +++ b/tasks/NodeJS_Server.deploy.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap # docker # node-prune diff --git a/tasks/NodeJS_Server.lint.sh b/tasks/NodeJS_Server.lint.sh index 4428ff5..f7f2842 100755 --- a/tasks/NodeJS_Server.lint.sh +++ b/tasks/NodeJS_Server.lint.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap # glue useAction(do-tool-prettier-init.sh) # glue useAction(do-tool-eslint-init.sh) diff --git a/tasks/NodeJS_Server.release.sh b/tasks/NodeJS_Server.release.sh index 57e1baa..6a4b947 100755 --- a/tasks/NodeJS_Server.release.sh +++ b/tasks/NodeJS_Server.release.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap unbootstrap diff --git a/tasks/Python.build.sh b/tasks/Python.build.sh index 31c11bf..90fe14f 100755 --- a/tasks/Python.build.sh +++ b/tasks/Python.build.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash eval "$GLUE_BOOTSTRAP" -bootstrap || exit +bootstrap diff --git a/util/action.sh b/util/action.sh deleted file mode 100644 index a32c921..0000000 --- a/util/action.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash - -# @description Print the currently running action -# @noargs -# @see task.log -action.log() { - # ${BASH_SOURCE[0]}: Ex. ~/.../.glue/actions/auto/util/action.sh - # ${BASH_SOURCE[1]}: Ex. ~/.../.glue/actions/auto/util/bootstrap.sh - # ${BASH_SOURCE[2]}: Ex. ~/.../.glue/actions/auto/do-tool-prettier-init.sh - - # Path to the currently actually executing 'action' script - # This works on the assumption that 'source's are all absolute paths - local currentAction="${BASH_SOURCE[2]}" - local currentActionDirname="${currentAction%/*}" - - if [ "${currentActionDirname##*/}" = auto ]; then - if [[ "${LANG,,?}" == *utf?(-)8 ]]; then - echo "■■■■ 🢂 START ACTION: 'auto/${currentAction##*/}'" - else - echo ":::: => START ACTION: 'auto/${currentAction##*/}'" - fi - else - if [[ "${LANG,,?}" == *utf?(-)8 ]]; then - echo "■■■■ 🢂 START ACTION: '${currentAction##*/}'" - else - echo ":::: => START ACTION: '${currentAction##*/}'" - fi - - fi -} diff --git a/util/bootstrap.sh b/util/bootstrap.sh index 99760c5..86aec30 100644 --- a/util/bootstrap.sh +++ b/util/bootstrap.sh @@ -8,23 +8,44 @@ bootstrap() { unset REPLY task action - trap 'bootstrap.int' INT - bootstrap.int() { + if [[ ! -v 'GLOBAL_CALLSTACK' ]]; then + declare -g GLOBAL_CALLSTACK= + fi + + # TODO: account for 'auto' + local fileName="${BASH_SOURCE[1]}" + dirName="${fileName%/*}"; dirName="${dirName/%\/auto/}"; + dirName="${dirName##*/}"; dirName="${dirName/%?/}" + fileName="${fileName##*/}" + bootstrap.fn "$fileName:$dirName()" + + trap 'bootstrap.trap.int' INT + bootstrap.trap.int() { die 'Received SIGINT' } - local _original_wd="$PWD" - trap 'bootstrap.exit' EXIT - bootstrap.exit() { + # 'cd' builtins might be used to change directory. This ensures + # that we will end up back where we started + local GLOBAL_ORIGINAL_WD="$PWD" + trap 'bootstrap.trap.exit' EXIT + bootstrap.trap.exit() { # shellcheck disable=SC2164 - cd "$_original_wd" + cd "$GLOBAL_ORIGINAL_WD" + } + + trap 'bootstrap.trap.err' ERR + bootstrap.trap.err() { + util.callstack_print } # source files in 'util' local dir="util" - shopt -q nullglob - local shoptExitStatus="$?" + if shopt -q nullglob; then + local shoptExitStatus="$?" + else + local shoptExitStatus="$?" + fi shopt -s nullglob local -a filesToSource=() @@ -80,12 +101,51 @@ bootstrap() { dir="${dir##*/}" # Print + # ${BASH_SOURCE[0]}: Ex. ~/.../.glue/actions/auto/util/action.sh + # ${BASH_SOURCE[1]}: Ex. ~/.../.glue/actions/auto/util/bootstrap.sh + # ${BASH_SOURCE[2]}: Ex. ~/.../.glue/actions/auto/do-tool-prettier-init.sh + case "$dir" in actions) - action.log + # Path to the currently actually executing 'action' script + # This works on the assumption that 'source's are all absolute paths + local currentAction="${BASH_SOURCE[2]}" + local currentActionDirname="${currentAction%/*}" + + if [ "${currentActionDirname##*/}" = auto ]; then + if [[ "${LANG,,?}" == *utf?(-)8 ]]; then + echo "■■■■ 🢂 START ACTION: 'auto/${currentAction##*/}'" + else + echo ":::: => START ACTION: 'auto/${currentAction##*/}'" + fi + else + if [[ "${LANG,,?}" == *utf?(-)8 ]]; then + echo "■■■■ 🢂 START ACTION: '${currentAction##*/}'" + else + echo ":::: => START ACTION: '${currentAction##*/}'" + fi + + fi ;; tasks) - task.log + # Path to the currently actually executing 'action' script + # This works on the assumption that 'source's are all absolute paths + local currentTask="${BASH_SOURCE[2]}" + local currentTaskDirname="${currentTask%/*}" + + if [ "${currentTaskDirname##*/}" = auto ]; then + if [[ "${LANG,,?}" == *utf?(-)8 ]]; then + echo "■■ 🢂 START TASK: 'auto/${currentTask##*/}'" + else + echo ":: => START TASK: 'auto/${currentTask##*/}'" + fi + else + if [[ "${LANG,,?}" == *utf?(-)8 ]]; then + echo "■■ 🢂 START TASK: '${currentTask##*/}'" + else + echo ":: => START TASK: '${currentTask##*/}'" + fi + fi ;; *) die "boostrap: Directory '$dir' not supported" @@ -117,6 +177,7 @@ unbootstrap() { if [ "$GLUE_IS_AUTO" ]; then dir="${dir%/*}" fi + dir="${dir##*/}" # Print @@ -137,6 +198,86 @@ unbootstrap() { fi ;; *) - die "boostrap: Directory '$dir' not supported" + echo "Context: '$0'" >&2 + echo "Context \${BASH_SOURCE[*]}: ${BASH_SOURCE[*]}" >&2 + log.error "boostrap: Directory '$dir' not supported" + exit 1 esac } + +# @description Bootstraps a particular function. This function is called +# within the context of 'bootstrap', so we cannot assume that any helper +# functions exist. Additionally, this function is called by other utility +# functions, so using any of those will cause a core dump due to +# infinite recursion +# @arg $1 string Name of funtion currently being bootstrapped +bootstrap.fn() { + REPLY= + + local fnName="$1" + + if [ -z "$fnName" ]; then + die "bootstrap.fn: Variable '$fnName' must be non zero" + fi + + GLOBAL_CALLSTACK="$fnName${GLOBAL_CALLSTACK:+";$GLOBAL_CALLSTACK"}" +} + +# @description Unbootstraps a particular function +# @arg $2 string Name of function currently being unbootstrapped +unbootstrap.fn() { + GLOBAL_CALLSTACK="${GLOBAL_CALLSTACK#*;}" +} + +# @description Prepare a directory to store generated contents. It removes +# everything from the directory if it exists, and prints info about it. +# Typically, immediately after using this function, a subshell is spawned to +# better 'isolate' any activity or execution. The following variables are set +# - `GENERATED_DIR`: Full path to the generated directory +# - `GENERATED_DIR_PRETTY`: The basename `GENERATED_DIR` +# @arrg $1 string Name of directory to generate. It _should_ have the same name of the file containing the callsite to this function +bootstrap.generated() { + local fn='bootstrap.generated' + bootstrap.fn "$fn" + + local dir="$1" + + ensure.nonZero 'dir' "$dir" + + # shellcheck disable=SC2034 + declare -g GENERATED_DIR="$GLUE_WD/.glue/generated/$dir" + declare -g GENERATED_DIR_PRETTY="$dir" + + if [ -d "$GLUE_WD/.glue/generated/$dir" ]; then + rm -rf "$GLUE_WD/.glue/generated/$dir" + fi + mkdir -p "$GLUE_WD/.glue/generated/$dir" + + if [[ "${LANG,,?}" == *utf?(-)8 ]]; then + echo "■■■■■■ 🢂 IN GENERATED: '$GENERATED_DIR_PRETTY'" + else + echo "=> IN GENERATED: '$GENERATED_DIR_PRETTY'" + fi + + unbootstrap.fn +} + +# @description Prints info that the user exited a generated directory +# @noargs +unbootstrap.generated() { + local fn='unbootstrap.generated' + bootstrap.fn "$fn" + + cd "$GLOBAL_ORIGINAL_WD" + + if [[ "${LANG,,?}" == *utf?(-)8 ]]; then + echo "■■■■■■ 🢀 OUT GENERATED: '$GENERATED_DIR_PRETTY'" + else + echo "<= OUT GENERATED: '$GENERATED_DIR_PRETTY'" + fi + + unset GENERATED_DIR + unset GENERATED_DIR_PRETTY + + unbootstrap.fn +} diff --git a/util/ensure.sh b/util/ensure.sh index 4033cbd..f2b97ac 100644 --- a/util/ensure.sh +++ b/util/ensure.sh @@ -4,6 +4,9 @@ # the program if it cannot be found # @arg string Command to check in the PATH ensure.cmd() { + local fn='ensure.cmd' + bootstrap.fn "$fn" + local cmd="$1" ensure.nonZero 'cmd' "$cmd" @@ -11,6 +14,8 @@ ensure.cmd() { if ! command -v "$cmd" &>/dev/null; then die "Command '$cmd' not found" fi + + unbootstrap.fn } # @description Checks a function's arguments, terminating the program @@ -19,6 +24,9 @@ ensure.cmd() { # @arg $2 string Space separated list of numbers to check # @arg $3 array Arguments to check ensure.args() { + local fn='ensure.args' + bootstrap.fn "$fn" + local fnName="$1" local argNums="$2" shift; shift; @@ -36,6 +44,8 @@ ensure.args() { exit 1 fi done + + unbootstrap.fn } # @description Ensures a particular argument is not empty, @@ -43,6 +53,9 @@ ensure.args() { # @arg $1 string Name of the variable # @arg $2 string Value of the variable ensure.nonZero() { + local fn='ensure.nonZero' + bootstrap.fn "$fn" + local varName="$1" local varValue="$2" @@ -53,12 +66,17 @@ ensure.nonZero() { if [ -z "$varValue" ]; then die "ensure.nonZero: Variable '$varName' must be non zero" fi + + unbootstrap.fn } # @description Ensures that a file exists. If it does not, # the program is terminated # @arg $1 string Path of the file to check. Recommend passing an absolute path ensure.file() { + local fn='ensure.file' + bootstrap.fn "$fn" + local fileName="$1" ensure.nonZero 'fileName' "$fileName" @@ -66,12 +84,17 @@ ensure.file() { if [ ! -f "$fileName" ]; then die "ensure.file: File '$fileName' does not exist. It *must* exist" fi + + unbootstrap.fn } # @description Ensures that a directory exists. If it does not, the # program is terminated # @arg $1 string Path of the directory to check. Recommend passing an absolute path ensure.dir() { + local fn='ensure.dir' + bootstrap.fn "$fn" + local dirName="$1" ensure.nonZero 'dirName' "$dirName" @@ -79,6 +102,8 @@ ensure.dir() { if [ ! -f "$dirName" ]; then die "ensure.dir: Directory '$dirName' does not exist. It *must* exist" fi + + unbootstrap.fn } # @description Ensures the Git working tree is dirty. If it is not, @@ -86,6 +111,9 @@ ensure.dir() { # @noargs # @see ensure.git_working_tree_clean ensure.git_working_tree_dirty() { + local fn='ensure.git_working_tree_dirty' + bootstrap.fn "$fn" + log.ensure 'git_working_tree_clean' if ! is.git_working_tree_dirty; then @@ -98,6 +126,8 @@ ensure.git_working_tree_dirty() { "$cmd" 'ensure.git_working_tree_dirty: Git working directory is clean. At this point, it *must* be dirty' fi + + unbootstrap.fn } # @description Ensures the Git working tree is clean. If it is not, @@ -105,6 +135,9 @@ ensure.git_working_tree_dirty() { # @noargs # @see ensure.git_working_tree_clean ensure.git_working_tree_clean() { + local fn='ensure.git_working_tree_clean' + bootstrap.fn "$fn" + log.info 'ensure: git_working_tree_clean' if is.git_working_tree_dirty; then @@ -117,6 +150,8 @@ ensure.git_working_tree_clean() { "$cmd" 'ensure.git_working_tree_clean: Git working directory is dirty. At this point, it *must* be clean' fi + + unbootstrap.fn } # @description Ensures the current local Git branch shares the same history @@ -125,6 +160,9 @@ ensure.git_working_tree_clean() { # it cannot, and the release mode is 'wet', the program terminates # @noargs ensure.git_common_history() { + local fn='ensure.git_common_history' + bootstrap.fn "$fn" + log.info 'ensure: git_common_history' local remote="${1-origin}" @@ -143,6 +181,7 @@ ensure.git_common_history() { "$cmd" "ensure.git_common_history: Detected that your 'main' branch and it's remote have diverged. At this point, both Git branch histories *must* be shared" fi + unbootstrap.fn } # TODO: ensure there are no tags that exists that are greater than it @@ -151,6 +190,9 @@ ensure.git_common_history() { # program terminates # @noargs ensure.git_version_tag_validity() { + local fn='ensure.git_version_tag_validity' + bootstrap.fn "$fn" + log.info 'ensure: git_version_tag_validity' local version="$1" @@ -162,12 +204,17 @@ ensure.git_version_tag_validity() { if [ -n "$(git tag -l "v$version")" ]; then die "ensure.git_version_tag_validity: Specified version '$version' is invalid. At this point, it *must not* be an already-existing Git tag" fi + + unbootstrap.fn } # @description Check if the current directory has a properly initialized # Git repository. If it does not, then the program terminates # @noargs ensure.git_repository_initialized() { + local fn='ensure.git_repository_initialized' + bootstrap.fn "$fn" + log.info 'ensure: git_repository_initialized' if [ ! -d .git ] || [ ! -f .git/HEAD ]; then @@ -177,6 +224,8 @@ ensure.git_repository_initialized() { if ! git log -1 &>/dev/null; then die 'ensure.git_repository_initialized: At least one commit must be stored in the Git repository' fi + + unbootstrap.fn } # @description Check if _only_ version changes are in the Git working tree. If @@ -184,6 +233,9 @@ ensure.git_repository_initialized() { # is 'wet', then terminate the program # @noargs ensure.git_only_version_changes() { + local fn='ensure.git_only_version_changes' + bootstrap.fn "$fn" + log.info 'ensure: git_only_version_changes' # We filter 'diff' until only changed lines remains. We then @@ -212,6 +264,8 @@ ensure.git_only_version_changes() { "$cmd" "ensure.git_only_version_changes: Changes other than version increments exist in the working tree. No changes *must* exist in the working tree with the exception of version increments" fi + + unbootstrap.fn } # TODO: rename to version_excludes_build_string @@ -221,6 +275,9 @@ ensure.git_only_version_changes() { # identifier, terminate the program # @arg $1 string Versrion string to check ensure.version_is_only_major_minor_patch() { + local fn='ensure.version_is_only_major_minor_patch' + bootstrap.fn "$fn" + log.info 'ensure: version_is_only_major_minor_patch' local version="$1" @@ -230,26 +287,39 @@ ensure.version_is_only_major_minor_patch() { if [[ $version == *+* ]]; then die 'ensure.version_is_only_major_minor_patch: Version string contains more than just major, minor, and patch numbers' fi + + unbootstrap.fn } # @description Ensure the user really wants to perform a 'wet' release. # If not, then the program terminates # @noargs ensure.confirm_wet_release() { + local fn='ensure.confirm_wet_release' + bootstrap.fn "$fn" + read -rei 'Do wet release? ' if [[ "$REPLY" != *y* ]]; then die fi + + unbootstrap.fn } # @description Ensure a particular exit code was successfull # If not, and release mode is 'wet', the program terminates # @arg $1 number Exit code ensure.exit_code_success() { + local fn='ensure.exit_code_success' + bootstrap.fn "$fn" + local exitCode="$1" ensure.nonZero 'exitCode' "$exitCode" + # Note that the "$exitCode" here may not be the exact exitCode of + # the previous running application, since in some places, + # exit codes are Bitwise OR'ed together if [ "$exitCode" -ne 0 ]; then local cmd if is.wet_release; then @@ -260,4 +330,23 @@ ensure.exit_code_success() { "$cmd" 'ensure.exit_code_success: A previous step did not exit successfully' fi + + unbootstrap.fn +} + +# @description Ensure that a change directory was successfull +# @arg $1 string Directory to change to +ensure.cd() { + local fn='ensure.cd' + bootstrap.fn "$fn" + + local dir="$1" + + ensure.nonZero 'dir' "$dir" + + if ! cd "$dir"; then + error.cd_failed + fi + + unbootstrap.fn } diff --git a/util/generated.sh b/util/generated.sh deleted file mode 100644 index 9af697d..0000000 --- a/util/generated.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash - -# @name generated.sh -# @brief File contaning functions that are only for `.glue/generated` - -# @description Prepare a directory to store generated contents. It removes -# everything from the directory if it exists, and prints info about it. -# Typically, immediately after using this function, a subshell is spawned to -# better 'isolate' any activity or execution. The following variables are set -# - `GENERATED_DIR`: Full path to the generated directory -# - `GENERATED_DIR_PRETTY`: The basename `GENERATED_DIR` -# @arrg $1 string Name of directory to generate. It _should_ have the same name of the file containing the callsite to this function -generated.in() { - local dir="$1" - - ensure.nonZero 'dir' "$dir" - - # shellcheck disable=SC2034 - declare -g GENERATED_DIR="$GLUE_WD/.glue/generated/$dir" - declare -g GENERATED_DIR_PRETTY="$dir" - - if [ -d "$GLUE_WD/.glue/generated/$dir" ]; then - rm -rf "$GLUE_WD/.glue/generated/$dir" - fi - mkdir -p "$GLUE_WD/.glue/generated/$dir" - - if [[ "${LANG,,?}" == *utf?(-)8 ]]; then - echo "■■■■■■ 🢂 IN GENERATED: '$GENERATED_DIR_PRETTY'" - else - echo "=> IN GENERATED: '$GENERATED_DIR_PRETTY'" - fi -} - -# @description Prints info that the user exited a generated directory -# @noargs -generated.out() { - if [[ "${LANG,,?}" == *utf?(-)8 ]]; then - echo "■■■■■■ 🢀 OUT GENERATED: '$GENERATED_DIR_PRETTY'" - else - echo "<= OUT GENERATED: '$GENERATED_DIR_PRETTY'" - fi -} diff --git a/util/is.sh b/util/is.sh index adc269c..43a61b8 100644 --- a/util/is.sh +++ b/util/is.sh @@ -7,31 +7,46 @@ # @exitcode 0 If dirty # @exitcode 1 If clean is.git_working_tree_dirty() { + local fn='is.git_working_tree_dirty' + bootstrap.fn "$fn" + if [ -n "$(git status --porcelain)" ]; then return 0 else return 1 fi + + unbootstrap.fn } # @description Checks if release mode is 'dry' # @exitcode 0 if dry # @exitcode 1 if wet is.dry_release() { + local fn='is.dry_release' + bootstrap.fn "$fn" + if [ "$RELEASE_STATUS" != 'wet' ]; then return 0 else return 1 fi + + unbootstrap.fn } # @description Checks if the release mode is 'wet' # @exitcode 0 if wet # @exitcode 1 if dry is.wet_release() { + local fn='is.wet_release' + bootstrap.fn "$fn" + if [ "$RELEASE_STATUS" = 'wet' ]; then return 0 else return 1 fi + + unbootstrap.fn } diff --git a/util/semver.sh b/util/semver.sh index 3674575..1256acf 100644 --- a/util/semver.sh +++ b/util/semver.sh @@ -15,6 +15,9 @@ debug() { # # @arg $1 semver version blurb semver.parse() { + local fn='semver.parse' + bootstrap.fn "$fn" + declare -g major= minor= patch= preRelease= build= local mode=major char= while IFS= read -rn1 char; do @@ -56,4 +59,6 @@ semver.parse() { ;; esac done <<< "$1" + + unbootstrap.fn } diff --git a/util/task.sh b/util/task.sh deleted file mode 100644 index 1a7e3d0..0000000 --- a/util/task.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash - -# @description Log the currently running task -# @noargs -# @see action.log -task.log() { - # Path to the currently actually executing 'action' script - # This works on the assumption that 'source's are all absolute paths - local currentTask="${BASH_SOURCE[2]}" - local currentTaskDirname="${currentTask%/*}" - - if [ "${currentTaskDirname##*/}" = auto ]; then - if [[ "${LANG,,?}" == *utf?(-)8 ]]; then - echo "■■ 🢂 START TASK: 'auto/${currentTask##*/}'" - else - echo ":: => START TASK: 'auto/${currentTask##*/}'" - fi - else - if [[ "${LANG,,?}" == *utf?(-)8 ]]; then - echo "■■ 🢂 START TASK: '${currentTask##*/}'" - else - echo ":: => START TASK: '${currentTask##*/}'" - fi - fi -} diff --git a/util/toml.sh b/util/toml.sh index bf62791..998cb56 100644 --- a/util/toml.sh +++ b/util/toml.sh @@ -7,10 +7,17 @@ # @arg $1 value of key to store in $REPLY # @arg $2 file to parse toml.get_key() { + local fn='toml.get_key' + bootstrap.fn "$fn" + local theirKey="$1" local file="$2" - REPLY= + ensure.nonZero 'theirKey' "$theirKey" + ensure.nonZero 'file' "$file" + + ensure.file "$file" + while IFS= read -r line; do if [ "${line::1}" = '#' ]; then continue @@ -44,4 +51,6 @@ toml.get_key() { fi done < "$file" + + unbootstrap.fn } diff --git a/util/util.sh b/util/util.sh index ce9ec96..5ae9f0c 100644 --- a/util/util.sh +++ b/util/util.sh @@ -7,6 +7,9 @@ # the current project. Do not specify function with '()' because # we assume consumer invokes function in subshell to capture output util.get_working_dir() { + local fn='util.get_working_dir' + bootstrap.fn "$fn" + while [ ! -f "glue.sh" ] && [ "$PWD" != / ]; do cd .. done @@ -16,11 +19,16 @@ util.get_working_dir() { fi printf "%s\n" "$PWD" + + unbootstrap.fn } # Get any particular file, parameterized by any # top level folder contained within "$GLUE_WD/.glue" util.get_file() { + local fn='util.get_file' + bootstrap.fn "$fn" + local dir="$1" local file="$2" @@ -35,45 +43,65 @@ util.get_file() { else error.file_not_found_in_dot_glue_dir "$file" "$dir" fi + + unbootstrap.fn } # Get any particular file in the 'actions' directory -# Pass '-q' as the first arg to set the result to -# '$REPLY' rather than outputing to standard output +# Pass '-p' to the first arg to standard output rather +# than setting '$REPLY' util.get_action() { + local fn='util.get_action' + bootstrap.fn "$fn" + if [ "$1" = "-p" ]; then - util.get_file "actions" "$2" + util.get_file 'actions' "$2" printf "%s\n" "$REPLY" else - util.get_file "actions" "$1" + util.get_file 'actions' "$1" fi + + unbootstrap.fn } # Get any particular file in the 'tasks' directory -# Pass '-q' as the first arg to set the result to -# '$REPLY' rather than outputing to standard output +# Pass '-p' to the first arg to standard output rather +# than setting '$REPLY' util.get_task() { + local fn='util.get_task' + bootstrap.fn "$fn" + if [ "$1" = "-p" ]; then - util.get_file "tasks" "$2" + util.get_file 'tasks' "$2" printf "%s\n" "$REPLY" else - util.get_file "tasks" "$1" + util.get_file 'tasks' "$1" fi + + unbootstrap.fn } # Get any particular file in the 'configs' directory -# Pass '-q' as the first arg to set the result to -# '$REPLY' rather than outputing to standard output +# Pass '-p' to the first arg to standard output rather +# than setting '$REPLY' util.get_config() { + local fn='util.get_config' + bootstrap.fn "$fn" + if [ "$1" = "-p" ]; then - util.get_file "configs" "$2" + util.get_file 'config' "$2" printf "%s\n" "$REPLY" else - util.get_file "configs" "$1" + util.get_file 'configs' "$1" fi + + unbootstrap.fn } util.ln_config() { + local fn='util.ln_config' + bootstrap.fn "$fn" + ensure.args 'util.ln_config' '1 2' "$@" if [ -f "$GLUE_WD/.glue/configs/$1" ]; then @@ -83,19 +111,27 @@ util.ln_config() { else error.file_not_found_in_dot_glue_dir "$1" 'configs' fi + + unbootstrap.fn } # Set or unset a shopt parameter, which will be reversed # during the bootstrap.deinit phase util.shopt() { + local fn='util.shopt' + bootstrap.fn "$fn" + ensure.args 'util.shopt' '1 2' "$@" shopt "$1" "$2" _util_shopt_data+="$1.$2 " + + unbootstrap.fn } util.prompt_new_version_string() { - REPLY= + local fn='util.prompt_new_version_string' + bootstrap.fn "$fn" local newVersion= if is.wet_release; then @@ -125,11 +161,17 @@ util.prompt_new_version_string() { fi REPLY="$newVersion" + + unbootstrap.fn } # @description Writes the new version string to 'glue-auto.toml' # and any relevant source files +# @noargs util.update_version_strings() { + local fn='util.update_version_strings' + bootstrap.fn "$fn" + local version="$1" ensure.nonZero 'version' "$version" @@ -144,16 +186,19 @@ util.update_version_strings() { fi # Write version (project type specific) - if command -v custom.bump_version_hook &>/dev/null; then - if ! custom.bump_version_hook "$version"; then - die "Hook 'custom.bump_version_hook' did not complete successfully" - fi - fi - unset custom.bump_version_hook + util.run_hook 'hook.util.update_version_strings.bump_version' + + unbootstrap.fn } +# @description Extracts the most recent version string. This version +# string complies with Semantic Versioning, and may include the +# current short commit hash, and a qualifier specifying the state of the Git +# working tree ('-DIRTY' or '') +# @noargs util.extract_version_string() { - REPLY= + local fn='util.extract_version_string' + bootstrap.fn "$fn" ensure.git_repository_initialized @@ -176,4 +221,42 @@ util.extract_version_string() { id="$(git rev-parse --short HEAD)" version+="+$id${dirty:+-DIRTY}" REPLY="$version" + + unbootstrap.fn +} + +# @description Runs a particular hook, then unsets the hook name +# @arg $1 string Name of the hook +util.run_hook() { + local fn='util.run_hook' + bootstrap.fn "$fn" + + local hookName="$1" + + ensure.nonZero 'hookName' "$hookName" + + if command -v "$hookName"; then + if ! "$hookName"; then + die "Hook '$hookName' did not complete successfully" + fi + fi + unset "$hookName" + + unbootstrap.fn +} + +# @description Prints the current callstack +# @noarg +util.callstack_print() { + local ff= + local -a callSites + + echo 'CALLSTACK' + while IFS=\; read -ra callSites; do + for callSite in "${callSites[@]}"; do + printf "%s\n" "$ff=> $callSite" + + ff+=" " + done + done <<< "$GLOBAL_CALLSTACK" }