├── .codecov.yaml
├── .funky
├── .gitignore
├── .gitmodules
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── cookie
├── img
├── demo.auto
├── demo.gif
└── logo.png
├── scripts
├── install-deps
└── zsh
│ └── _cookie
├── setup.cfg
└── tests
├── README.md
├── runtests
├── test_cookie.sh
└── test_install.sh
/.codecov.yaml:
--------------------------------------------------------------------------------
1 | coverage:
2 | range: "70...100"
3 |
4 | ignore:
5 | - "scripts/*"
6 | - "tests/*"
7 | - "shunit2"
8 |
--------------------------------------------------------------------------------
/.funky:
--------------------------------------------------------------------------------
1 | {"C": "echo \"Makefile .travis.yml .codecov.yaml\"", "D": "echo \"README.md CHANGELOG.md\" \"$@\"", "i": "sudo make install \"$@\"", "SS": "echo \"./scripts/install-deps\" \"$@\"", "u": "sudo make uninstall \"$@\"", "T": "echo \"tests/runtests tests/test_cookie.sh tests/test_install.sh\" \"$@\"", "V": "echo \"cookie\" \"$@\"", "t": "make check \"$@\""}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | marketing.txt
2 | bashlibs/*
3 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "lib/bashlibs"]
2 | path = lib/bashlibs
3 | url = https://github.com/bbugyi200/bashlibs.git
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: bash
2 |
3 | sudo: required
4 |
5 | install: ./scripts/install-deps
6 |
7 | script: ./tests/runtests
8 |
9 | os: linux
10 |
11 | addons:
12 | apt:
13 | packages:
14 | - libcurl4-openssl-dev
15 | - libelf-dev
16 | - libdw-dev
17 | - cmake
18 |
19 | after_success: |
20 | wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz &&
21 | tar xzf master.tar.gz &&
22 | cd kcov-master &&
23 | mkdir build &&
24 | cd build &&
25 | cmake .. &&
26 | make &&
27 | sudo make install &&
28 | cd ../.. &&
29 | rm -rf kcov-master &&
30 | mkdir -p coverage &&
31 | kcov coverage ./tests/runtests &&
32 | bash <(curl -s https://codecov.io/bash)
33 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file. This project adheres to
4 | [Semantic Versioning](https://semver.org/), though minor breaking changes can happen in minor
5 | releases.
6 |
7 | ### Unreleased
8 |
9 | Added:
10 |
11 | * Will now automatically copy a full directory if one is given as a target.
12 |
13 | Changed:
14 |
15 | * TEMPLATE command-line option is now positional.
16 |
17 |
18 | ### v0.2.0 (2019-01-22)
19 |
20 | Added:
21 |
22 | * The `--remove` option.
23 | * Upgraded the startline template statements so now vim will identify the column number of the statement as well as the line number. In effect, cookie is now able to start vim with the cursor positioned at the exact spot where the startline statement was.
24 | * A ZSH completion script to the GitHub repository and into the standard `install` rule for the project's Makefile.
25 | * The `--mode` option for setting file mode bits.
26 |
27 | Changed:
28 |
29 | * The syntax for startline template statements (cookie now uses `{% INSERT %}` and `{% NORMAL %}`).
30 | * The `-T` option to `-t`.
31 |
32 | Removed:
33 |
34 | * The `--executable` option
35 |
36 | Fixed:
37 |
38 | * Only require the `-f` flag when the target is going to be executable.
39 |
40 | ### v0.1.1 (2018-11-18)
41 |
42 | Fixed:
43 |
44 | * When user is in root dir, default subdir should be used.
45 | * Spaces in template variables should be optional.
46 | * With variables with repeated occurrences in the template, cookie was
47 | forgetting the variables value and thus prompting the user repeatedly
48 | for the same variable.
49 | * `EXEC_HOOK_CMD` was not evaluating `${TARGET}`.
50 | * Can now use absolute path with `TARGET` argument.
51 |
52 | ### v0.1.0 (2018-11-13)
53 |
54 | * First Release
55 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018, Bryan M Bugyi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .DEFAULT_GOAL := help
2 |
3 | PREFIX ?= /usr
4 | BINDIR ?= $(PREFIX)/bin
5 |
6 | bindir=$(DESTDIR)/$(BINDIR)
7 | runtests=tests/runtests
8 | bashlibs=lib/bashlibs
9 | project=cookie
10 |
11 | define update-bashlibs
12 | git submodule update --init
13 | git submodule update --remote $(bashlibs)
14 | endef
15 |
16 |
17 | .PHONY: help
18 | help: ## Print this message.
19 | @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) | sort
20 |
21 | .PHONY: install
22 | install: install-bashlibs install-zsh $(bindir) $(project) ## Install cookie.
23 | cp $(project) $(bindir)/$(project)
24 | chmod +x $(bindir)/$(project)
25 |
26 | .PHONY: install-bashlibs
27 | install-bashlibs: ## Install the bashlibs library.
28 | ifeq (,$(wildcard /usr/bin/gutils.sh))
29 | $(call update-bashlibs)
30 | $(MAKE) -C $(bashlibs) install
31 | endif
32 |
33 | .PHONY: install-zsh
34 | install-zsh: ## Install ZSH completion function.
35 | @mkdir -p $(DESTDIR)/$(PREFIX)/share/zsh/site-functions/
36 | cp ./scripts/zsh/_cookie $(DESTDIR)/$(PREFIX)/share/zsh/site-functions/
37 |
38 | $(bindir):
39 | @mkdir -p $(bindir)
40 |
41 | .PHONY: uninstall
42 | uninstall: ## Uninstall cookie.
43 | @rm -f $(bindir)/$(project)
44 |
45 | .PHONY: uninstall-all
46 | uninstall-all: uninstall ## Uninstall cookie and all of its dependencies.
47 | $(call update-bashlibs)
48 | $(MAKE) -C $(bashlibs) uninstall
49 |
50 | .PHONY: test check
51 | test: check
52 | check: $(runtests) ## Run all tests.
53 | $(call update-bashlibs)
54 | ./$(runtests)
55 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # cookie [](https://twitter.com/intent/tweet?text=Stop%20repeating%20yourself!%20Cookie%20templates%20make%20writing%20scripts,%20LaTeX%20documents,%20Makefiles,%20and%20other%20one-off%20files%20easier%20than%20ever!&url=https://github.com/bbugyi200/funky&via=bryan_bugyi&hashtags=Linux,commandlineftw,developers)
2 |
3 | **Stop repeating yourself! Cookie templates make writing scripts, LaTeX documents, Makefiles, and other one-off files easier than ever!**
4 |
5 | [](https://travis-ci.org/bbugyi200/cookie) [](https://codecov.io/gh/bbugyi200/cookie) [](https://opensource.org/licenses/MIT)
6 |
7 | ![demo]
8 |
9 | ## Usage
10 | ```
11 | Usage: cookie [-d] [-D TARGET_DIR] [-f] [-m MODE] [-q] [-v] [-x] TEMPLATE [TARGET]
12 | cookie -c
13 | cookie -e TEMPLATE
14 | cookie -h
15 | cookie -l [TEMPLATE]
16 | cookie -r TEMPLATE
17 |
18 | Initializes a new file (TARGET) using a predefined template (TEMPLATE).
19 | The target file can be a new script, configuration file, markup file, etc....
20 | After the target file has been initialized, it is opened for editing using the
21 | system's default editor.
22 |
23 | Positional Arguments:
24 | TARGET The name of the file to initialize.
25 |
26 | Optional Arguments:
27 | -d | --debug
28 | Enable debug mode.
29 |
30 | -c | --config
31 | Edit the configuration file.
32 |
33 | -D DIR | --bin-subdir DIR
34 | Initialize TARGET into DIR, which should be a subdirectory of the
35 | default bin directory (see the configuration file).
36 |
37 | -e TEMPLATE | --edit TEMPLATE
38 | Add / edit cookie template.
39 |
40 | -f | --force
41 | Force TARGET initialization to be relative to the current
42 | directory. This option essentially overrides the ROOT_DIR
43 | configuration setting. Enabled by default for non-executable
44 | targets.
45 |
46 | -h | --help
47 | View this help message.
48 |
49 | -l [TEMPLATE] | --list [TEMPLATE]
50 | If TEMPLATE is provided, output template contents to STDOUT.
51 | Otherwise, list available templates.
52 |
53 | -m MODE | --mode MODE
54 | Sets file mode bits. Accepts any form for MODE that is recognized
55 | by the 'chmod' command.
56 |
57 | -r TEMPLATE | --remove TEMPLATE
58 | Delete cookie template.
59 |
60 | -q | --quiet
61 | Just initialize the new script without opening it up in an editor.
62 |
63 | -v | --verbose
64 | Enable verbose output.
65 |
66 | -x
67 | Make TARGET executable. Equivalent to '-m +x'.
68 | ```
69 |
70 | ## Templates
71 |
72 | Templates are stored in the directory specified by `$COOKIE_DIR` which, if not specified in the configuration file (see the [Configuration](#config) section), defaults to `~/.cookiecutters` (the same directory used by `cookiecutter` to store templates).
73 |
74 | See my personal [templates] for examples on how you can use templates.
75 |
76 | ### Template Variables and Statements
77 | While not as full-featured as the [jinja] template engine that [cookiecutter] uses, there are a few special variables and statements available. The syntax for these will be familiar if you have used [jinja] in the past.
78 |
79 | #### Variable Substitution
80 | cookie also recognizes template variables of the form:
81 | ```
82 | {{ foobar }}
83 | ```
84 | This string will be replaced by the value of the environment variable `foobar` if it exists. Otherwise, the user will be prompted to provide a value for `foobar` on the command-line.
85 |
86 | To ensure compatibility with files in cookiecutter templates, you may also preface the variable with `cookiecutter` followed by a period. Hence, the following statement is equivalent to the one discussed above:
87 | ```
88 | {{ cookiecutter.foobar }}
89 | ```
90 |
91 | #### Mark Start Point for Editing (only works when vim is set as the default system editor)
92 | If the following statement is found in the template, vim will start with the cursor positioned on the line and column of the first curly brace (after removing the statement) and will start in INSERT mode:
93 | ```
94 | {% INSERT %}
95 | ```
96 |
97 | The following statement does the same thing but will start vim in NORMAL mode (vim's default behavior):
98 | ```
99 | {% NORMAL %}
100 | ```
101 |
102 | ## Configuration
103 |
104 | The configuration file can be found at `$XDG_CONFIG_HOME/cookie/config`. The following options are available:
105 |
106 | ``` ini
107 | # The target file will be initialized in a location relative to this directory
108 | # unless you specify the `-f` option. In which case the target file will be
109 | # initialized relative to the current directory.
110 | #
111 | # Defaults to "./" (the current directory).
112 | ROOT_DIR=
113 |
114 | # The target file is initialized in $ROOT_DIR/$DEFAULT_TARGET_DIR
115 | # unless the `-D {DIR}` option is used. In which case the target file will
116 | # be initialized to $ROOT_DIR/{DIR}.
117 | DEFAULT_TARGET_DIR=
118 |
119 | # If specified, this command is evaluated after (and if) the target file
120 | # has its executable bit set. This can be used to create symlinks to
121 | # the target file (using `stow`, for example).
122 | #
123 | # The $TARGET variable, which contains the full path of the target file,
124 | # will be injected into the environment of this command.
125 | EXEC_HOOK_CMD=
126 |
127 | # The directory used to store cookie templates.
128 | #
129 | # Defaults to "~/.cookiecutters".
130 | COOKIE_DIR=
131 | ```
132 |
133 | ## Using Shell Aliases / Functions
134 |
135 | You can of course run `cookie` directly, but I have not found that to
136 | be very convenient. Instead, I have created a variety of shell aliases and
137 | functions which serve as custom initialization commands that are specific to a
138 | single goal and filetype. Here are a few examples:
139 |
140 | ``` bash
141 | alias ainit='cookie -t template.awk -D awk -x'
142 | alias binit='cookie -t minimal.sh -x'
143 | alias Binit='cookie -t full.sh -x'
144 | hw() { ASSIGNMENT_NUMBER="$1" cookie -t hw.tex "${@:2}" HW"$1"/hw"$1".tex; }
145 | alias minit='cookie -t c.make Makefile'
146 | alias mtinit='cookie -t gtest.make Makefile'
147 | alias pyinit='cookie -t template.py -x'
148 | pytinit() { cookie -t pytest.py test_"$1".py; }
149 | alias texinit='cookie -t template.tex'
150 | ```
151 |
152 | ## Examples
153 |
154 | Let us now take a look at a few examples of how cookie might be useful.
155 |
156 | For reference, my personal cookie templates can be found [here][templates] and these are the configuration settings that I use:
157 | ``` bash
158 | PARENT_BIN_DIR="/home/bryan/Dropbox/bin"
159 | DEFAULT_BIN_SUBDIR="main"
160 | EXEC_HOOK_CMD=/usr/local/bin/clinks
161 | ```
162 | where `/home/bryan/Dropbox/bin` is a home for [this][scripts] GitHub repository. (The [clinks] script wraps a bunch of [stow] commands which makes creating symlinks to a system `bin` folder a walk in the park while still keeping my scripts organized the way I like in my filesystem.)
163 |
164 | To initialize a minimal bash script named `foo` into the `/home/bryan/Dropbox/main` directory (where I keep most of my scripts), I could run the following command:
165 | ```
166 | binit foo
167 | ```
168 | Suppose instead that I wanted to initialize a new productivity script named `bar` into the `/home/bryan/Dropbox/GTD` directory. Furthermore, suppose that I know that `bar` might get complicated (and thus needs to scale well). I could then choose to run
169 | ```
170 | Binit -D GTD bar
171 | ```
172 | to initialize a full featured bash script (bells and whistles included) into the `/home/bryan/Dropbox/GTD` directory.
173 |
174 | ## Advanced Usage
175 |
176 | I wrote a short [blog post][blog] describing a few of cookie's more advanced features.
177 |
178 | ## Similar Projects
179 |
180 | * [cookiecutter] - A command-line utility that creates projects from cookiecutters (project templates).
181 | * [j2cli] - Jinja2 Command-Line Tool.
182 | * [plop] - Micro-generator framework that makes it easy for an entire team to create files with a level of uniformity.
183 |
184 | ## Installation
185 |
186 | #### Root Installation
187 | If you have root permissions on your machine, installation is as simple as cloning the repository with `git clone https://github.com/bbugyi200/cookie`, traveling into the project directory (`cd cookie`), and then running `sudo make install`.
188 |
189 | #### User Installation
190 | If you do not have root permissions on your machine, you can still install cookie by using an alternate bin directory. This can be accomplished, for example, by cloning the repository and using `cd` to travel to the repository directory (as described in the previous section) and then running the following command:
191 | ```
192 | make DESTDIR=/home//.local PREFIX= install
193 | ```
194 | where `` should be replaced with your username. Keep in mind that, for this to work, the `/home//.local/bin` directory must be added to your system's path.
195 |
196 | #### macOS
197 |
198 | MacOS uses a different version of getopt than Linux does. You can install a GNU version of getopt and set it as the default getopt for your system using brew:
199 |
200 | ```
201 | brew install gnu-getopt
202 | brew link --force gnu-getopt
203 | ```
204 | See this [Stack Overflow answer](https://stackoverflow.com/questions/12152077/how-can-i-make-bash-deal-with-long-param-using-getopt-command-in-mac) before modifying your `gnu-getopt`.
205 |
206 | #### ZSH Completion
207 |
208 | The appropriate completion function should be installed automatically. If necessary, however, you can enable ZSH command-line completion manually by copying the [\_cookie][zsh-completion] file to a directory listed by your system's `$fpath` variable (normally the `/usr/share/zsh/site-functions` directory works).
209 |
210 | [blog]: https://bryanbugyi.com/blog/tips-and-tricks-for-using-cookie/
211 | [logo]: https://raw.githubusercontent.com/bbugyi200/cookie/master/img/logo.png
212 | [demo]: https://raw.githubusercontent.com/bbugyi200/cookie/master/img/demo.gif "Cookie Demonstration GIF"
213 | [jinja]: https://github.com/pallets/jinja
214 | [cookiecutter]: https://github.com/audreyr/cookiecutter
215 | [scripts]: https://github.com/bbugyi200/scripts
216 | [clinks]: https://github.com/bbugyi200/scripts/blob/master/main/clinks
217 | [templates]: https://github.com/bbugyi200/dotfiles/tree/master/.cookiecutters
218 | [stow]: https://www.gnu.org/software/stow/manual/stow.html
219 | [travis]: https://travis-ci.org/bbugyi200/cookie.svg?branch=master
220 | [codecov]: https://codecov.io/gh/bbugyi200/cookie/branch/master/graph/badge.svg
221 | [j2cli]: https://github.com/kolypto/j2cli
222 | [zsh-completion]: https://github.com/bbugyi200/cookie/blob/master/scripts/zsh/_cookie
223 | [plop]: https://github.com/amwmedia/plop
224 |
--------------------------------------------------------------------------------
/cookie:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # shellcheck disable=SC1090
4 |
5 | read -r -d '' doc <<-EOM
6 | Initializes a new file (TARGET) using a predefined template (TEMPLATE).
7 | The target file can be a new script, configuration file, markup file, etc....
8 | After the target file has been initialized, it is opened for editing using the
9 | system's default editor.
10 | EOM
11 |
12 | # ---------- Source Libraries / Configs ----------
13 | # ===== Global Utilities =====
14 | source bugyi.sh
15 |
16 | [ -d "${MY_XDG_CONFIG}" ] || mkdir -p "${MY_XDG_CONFIG}"
17 |
18 | # ===== Configuration File =====
19 | read -r -d '' default_config << "EOM"
20 | # The target file will be initialized in a location relative to this directory
21 | # unless you specify the `-f` option. In which case the target file will be
22 | # initialized relative to the current directory.
23 | #
24 | # Defaults to "./" (the current directory).
25 | ROOT_DIR=
26 |
27 | # The target file is initialized in $ROOT_DIR/$DEFAULT_TARGET_DIR
28 | # unless the `-D {DIR}` option is used. In which case the target file will
29 | # be initialized to $ROOT_DIR/{DIR}.
30 | DEFAULT_TARGET_DIR=
31 |
32 | # If specified, this command is evaluated after (and if) the target file
33 | # has its executable bit set. This can be used to create symlinks to
34 | # the target file (using `stow`, for example).
35 | #
36 | # The $TARGET variable, which contains the full path of the target file,
37 | # will be injected into the environment of this command.
38 | EXEC_HOOK_CMD=
39 |
40 | # The directory used to store cookie templates.
41 | #
42 | # Defaults to "~/.cookiecutters".
43 | COOKIE_DIR=
44 | EOM
45 |
46 | # LCOV_EXCL_START
47 | config_file="${MY_XDG_CONFIG}"/config
48 | if [[ -f "${config_file}" ]]; then
49 | source "${config_file}"
50 | else
51 | imsg "Configruation file has been initialized."
52 | echo "${default_config}" > "${config_file}"
53 | fi
54 | # LCOV_EXCL_STOP
55 |
56 | # Set default bin directory if not specified in config file.
57 | if [[ -z "${ROOT_DIR}" ]]; then
58 | ROOT_DIR=./
59 | fi
60 |
61 | if [[ -z "${COOKIE_DIR}" ]]; then
62 | COOKIE_DIR="${HOME}"/.cookiecutters
63 | fi
64 |
65 | # ---------- Traps ----------
66 | function exit_handler() {
67 | EC="$1"; shift
68 | created_by_me="$1"; shift
69 | dest_dir="$1"; shift
70 |
71 | if [[ "${created_by_me}" = true && "${EC}" -ne 0 ]]; then
72 | dmsg "Removing directory: ${dest_dir}"
73 | rm -rf "${dest_dir}"
74 | fi
75 | }
76 |
77 | trap 'exit_handler $? ${parent_dir_created} ${dest_dir}' EXIT
78 |
79 | # ---------- Function Definitions ----------
80 | function main() {
81 | parse_args "$@"
82 |
83 | get_dest_dir "${target}" "${force}"
84 |
85 | full_target="${dest_dir}"/"${target}"
86 | full_template="${COOKIE_DIR}"/"${template}"
87 | if ! [[ -f "${full_template}" ]]; then
88 | if [[ -d "${full_template}" ]]; then
89 | cp -r "${full_template}" "${full_target}"
90 | imsg "Initializing the '${full_target}' directory."
91 | exit 0
92 | fi
93 | die "Template does not exist: ${full_template}"
94 | fi
95 |
96 | # ===== Initialize New Script =====
97 | # >>> Copy Template Contents to New Script
98 | if ! [[ -f "${full_target}" ]]; then
99 | imsg "Initializing the '${full_target}' script."
100 | cp "${full_template}" "${full_target}"
101 | else
102 | imsg "The '${full_target}' script already exists."
103 | maybe_edit "$(editor_cmd "" "" "NORMAL" "${full_target}")"
104 | exit 0
105 | fi
106 |
107 | # >>> Set File Mode
108 | if [[ -n "${mode}" ]]; then
109 | sudo chmod "${mode}" "${full_target}"
110 |
111 | if [[ "${executable}" = true ]]; then
112 | if [[ -n "${EXEC_HOOK_CMD}" ]]; then
113 | TARGET="${full_target}" eval "imsg \"Running execute hook: ${EXEC_HOOK_CMD}\""
114 | TARGET="${full_target}" eval "${EXEC_HOOK_CMD}"
115 | fi
116 | fi
117 | fi
118 |
119 | contents="$(cat "${full_target}")"
120 | template_engine "${contents}"
121 | echo "${new_contents}" > "${full_target}"
122 |
123 | maybe_edit "$(editor_cmd "${start_line}" "${start_col}" "${mode}" "${full_target}")"
124 | }
125 |
126 | function maybe_edit() {
127 | if [[ "${quiet}" != true ]]; then
128 | eval "$@"
129 | fi
130 | }
131 |
132 | function parse_args() {
133 | eval set -- "$(getopt -o "d,c,D:,e:,f,F:,h,l,m:,r:,q,v,x" -l "config,debug,docs:,bin-subdir:,edit:,help,list,mode:,remove:,quiet,verbose,use-extension:" -- "$@")"
134 |
135 | # LCOV_EXCL_START
136 | export USAGE_GRAMMAR=(
137 | "[-d] [-D TARGET_DIR] [-f] [-m MODE] [-q] [-v] [-x] TEMPLATE [TARGET]"
138 | "-c"
139 | "-e TEMPLATE"
140 | "-h"
141 | "-l [TEMPLATE]"
142 | "-r TEMPLATE"
143 | )
144 | # LCOV_EXCL_STOP
145 |
146 | # shellcheck disable=SC2153
147 | if [[ -n "${EDITOR}" ]]; then
148 | EDITOR="${EDITOR}"
149 | else
150 | EDITOR="vim"
151 | fi
152 |
153 | read -r -d '' help <<-EOM
154 | $(usage)
155 |
156 | ${doc}
157 |
158 | Positional Arguments:
159 | TARGET The name of the file to initialize.
160 |
161 | Optional Arguments:
162 | -d | --debug
163 | Enable debug mode.
164 |
165 | -c | --config
166 | Edit the configuration file.
167 |
168 | -D DIR | --bin-subdir DIR
169 | Initialize TARGET into DIR, which should be a subdirectory of the
170 | default bin directory (see the configuration file).
171 |
172 | -e TEMPLATE | --edit TEMPLATE
173 | Add / edit cookie template.
174 |
175 | -f | --force
176 | Force TARGET initialization to be relative to the current
177 | directory. This option essentially overrides the ROOT_DIR
178 | configuration setting. Enabled by default for non-executable
179 | targets.
180 |
181 | -h | --help
182 | View this help message.
183 |
184 | -l [TEMPLATE] | --list [TEMPLATE]
185 | If TEMPLATE is provided, output template contents to STDOUT.
186 | Otherwise, list available templates.
187 |
188 | -m MODE | --mode MODE
189 | Sets file mode bits. Accepts any form for MODE that is recognized
190 | by the 'chmod' command.
191 |
192 | -r TEMPLATE | --remove TEMPLATE
193 | Delete cookie template.
194 |
195 | -q | --quiet
196 | Just initialize the new script without opening it up in an editor.
197 |
198 | -v | --verbose
199 | Enable verbose output.
200 |
201 | -x
202 | Make TARGET executable. Equivalent to '-m +x'.
203 | EOM
204 |
205 | force=false
206 | template=
207 | target=
208 | debug=false
209 | verbose=false
210 |
211 | while [[ -n "$1" ]]; do
212 | case $1 in
213 | -c|--config )
214 | "${EDITOR}" "${config_file}"
215 | exit 0
216 | ;;
217 | -d|--debug )
218 | debug=true # LCOV_EXCL_LINE
219 | ;;
220 | -e|--edit )
221 | shift
222 | "${EDITOR}" "${COOKIE_DIR}"/"$1"
223 | exit 0
224 | ;;
225 | -h|--help )
226 | # LCOV_EXCL_START
227 | echo "${help}"
228 | exit 0
229 | # LCOV_EXCL_STOP
230 | ;;
231 | -l|--list )
232 | list=true
233 | ;;
234 | -D|--bin-subdir )
235 | shift
236 | target_dir="$1"
237 | ;;
238 | --docs )
239 | # LCOV_EXCL_START
240 | shift
241 | eval "printf -- \"\${$1}\n\""
242 | exit 0
243 | # LCOV_EXCL_STOP
244 | ;;
245 | -f )
246 | force=true
247 | ;;
248 | -m|--mode )
249 | shift
250 | mode="$1"
251 | ;;
252 | -r|--remove )
253 | shift
254 | template="$1"
255 | remove=true
256 | ;;
257 | -q|--quiet )
258 | quiet=true
259 | ;;
260 | -v|--verbose )
261 | verbose=true # LCOV_EXCL_LINE
262 | ;;
263 | -x )
264 | mode="+x"
265 | ;;
266 | -- )
267 | shift
268 | break
269 | ;;
270 | esac
271 | shift
272 | done
273 |
274 | if [[ "${list}" = true ]]; then
275 | if [[ -n "$1" ]]; then
276 | template="$1"; shift
277 | full_template="${COOKIE_DIR}"/"${template}"
278 | [[ -f "${full_template}" ]] || die "Template does not exist: ${full_template}"
279 | cat "${full_template}"
280 | else
281 | for T in "${COOKIE_DIR}"/*; do
282 | [[ -f "${T}" ]] && basename "${T}"
283 | done
284 | fi
285 |
286 | exit 0
287 | fi
288 |
289 | if [[ "${remove}" = true ]]; then
290 | full_template="${COOKIE_DIR}"/"${template}"
291 | [[ -f "${full_template}" ]] || die "Template does not exist: ${full_template}"
292 | read -n1 -p "Are you sure you want to delete the ${template} template? [y/n]: " choice
293 | if [[ "${choice}" == "y" ]]; then
294 | rm "${full_template}"
295 | printf "\n"
296 | imsg "The ${template} template has been deleted."
297 | fi
298 | exit 0
299 | fi
300 |
301 | if [[ "${mode}" == *"7"* || "${mode}" == *"+"*"x"* ]]; then
302 | executable=true
303 | else
304 | executable=false
305 | fi
306 |
307 | if [[ "${debug}" = true && "${verbose}" = true ]]; then
308 | # LCOV_EXCL_START
309 | PS4='$LINENO: '
310 | set -x
311 | # LCOV_EXCL_STOP
312 | fi
313 |
314 | if [[ -z "$1" ]]; then
315 | die "$(usage)" 2
316 | fi
317 |
318 | template="$1"; shift
319 |
320 | if [[ -n "$1" ]]; then
321 | target="$1"; shift
322 | else
323 | target="$(basename "${template}")"
324 | fi
325 | }
326 |
327 | function get_dest_dir() {
328 | target="$1"; shift
329 | force="$1"; shift
330 |
331 | if [[ "${target}" == "/"* ]]; then
332 | dest_dir="$(dirname "${target}")"
333 | target="$(basename "${target}")"
334 | return 0
335 | fi
336 |
337 | # ===== Calculate Filesystem Paths =====
338 | if [[ "$PWD" == "${ROOT_DIR}"/* && -z "${target_dir}" ]] || [[ "${force}" = true ]]; then
339 | dest_dir="$PWD"
340 | elif [[ -n "${target_dir}" ]]; then
341 | dest_dir="${ROOT_DIR}"/"${target_dir}"
342 | elif [[ -n "${DEFAULT_TARGET_DIR}" ]]; then
343 | dest_dir="${ROOT_DIR}"/"${DEFAULT_TARGET_DIR}"
344 | else
345 | dest_dir="${ROOT_DIR}"
346 | fi
347 |
348 | target_dir="$(dirname "${target}")"
349 |
350 | if [[ "${target_dir}" != "." ]]; then
351 | dest_dir="${dest_dir}"/"${target_dir}"
352 | target="$(basename "${target}")"
353 | fi
354 |
355 | if [[ "${dest_dir}" != "./"* ]]; then
356 | if ! [[ -d "${dest_dir}" ]]; then
357 | dmsg "Creating directory: ${dest_dir}"
358 | mkdir -p "${dest_dir}"
359 | parent_dir_created=true
360 | fi
361 | fi
362 | }
363 |
364 | function template_engine() {
365 | old_contents="$1"; shift
366 | new_contents="${old_contents}"
367 |
368 | # ===== Template Statements and Substitutions =====
369 | # >>> START HERE
370 | istart_mark="{% INSERT %}"
371 | read -r iline icol <<< "$(get_start_line "${istart_mark}" "${old_contents}")"
372 |
373 | nstart_mark="{% NORMAL %}"
374 | read -r nline ncol <<< "$(get_start_line "${nstart_mark}" "${old_contents}")"
375 |
376 | new_contents="${new_contents//\{% INSERT %\}}"
377 | new_contents="${new_contents//\{% NORMAL %\}}"
378 |
379 | if [[ -n "${iline}" ]] && [[ "${iline}" -gt 0 ]]; then
380 | mode="INSERT"
381 | start_line="${iline}"
382 | start_col="${icol}"
383 | elif [[ -n "${nline}" ]] && [[ "${nline}" -gt 0 ]]; then
384 | mode="NORMAL"
385 | start_line="${nline}"
386 | start_col="${ncol}"
387 | fi
388 |
389 | # >>> Environment Variable Replacements
390 | grep_epttrn="{{[ ]*(.*?)[ ]*}}"
391 | sed_epttrn="{{[ ]*\([^ ]*\)[ ]*}}"
392 |
393 | exec 5>&0 # save STDIN
394 | while read evar; do
395 | evalue="$(eval "echo \"\$${evar}\"")"
396 | if [[ -z "${evalue}" ]]; then
397 | read -p "${evar}: " evalue <&5
398 | eval "${evar}=${evalue}"
399 | fi
400 |
401 | new_contents="$(echo "${new_contents}" | sed "s/{{[ ]*${evar}[ ]*}}/${evalue}/g")"
402 | new_contents="$(echo "${new_contents}" | sed "s/{{[ ]*cookiecutter.${evar}[ ]*}}/${evalue}/g")"
403 | done < <(echo "${new_contents}" | grep -P -o "${grep_epttrn}" | sed "s/${sed_epttrn}/\1/" | sed 's/cookiecutter\.//') # LCOV_EXCL_LINE
404 | }
405 |
406 | function get_start_line() {
407 | mark="$1"; shift
408 | contents="$1"; shift
409 |
410 | line="$(echo "${contents}" | grep -n "${mark}" | awk -F':' '{print $1}')"
411 |
412 | line_contents="$(echo "${contents}" | grep "${mark}")"
413 | col="$(awk -v line_contents="${line_contents}" 'BEGIN{print index(line_contents, "{")}')"
414 |
415 | printf "${line} ${col}\n"
416 | }
417 |
418 | function editor_cmd() {
419 | start_line="$1"; shift
420 | start_col="$1"; shift
421 | mode="$1"; shift
422 | full_target="$1"; shift
423 |
424 | if [[ "${EDITOR}" == *"vim"* ]]; then
425 | Vim_Opts=()
426 | if [[ -n "${start_line}" ]] && [[ -n "${start_col}" ]]; then
427 | Vim_Opts+=( "-c" "\"call cursor(${start_line},${start_col})\"" )
428 | fi
429 |
430 | if [[ "${mode}" == "INSERT" ]]; then
431 | Vim_Opts+=( +startinsert )
432 | fi
433 |
434 | echo "${EDITOR} ${Vim_Opts[*]} ${full_target}"
435 | else
436 | echo "${EDITOR} ${full_target}"
437 | fi
438 | }
439 |
440 | if [[ "${SCRIPTNAME}" == "cookie" ]]; then
441 | main "$@" # LCOV_EXCL_LINE
442 | fi
443 |
--------------------------------------------------------------------------------
/img/demo.auto:
--------------------------------------------------------------------------------
1 | ################################################################################
2 | # Autodemo specification for 'cookie' project demonstration. #
3 | ################################################################################
4 | ((50))# This is a demo for cookie.<<1>>[CLEAR]((100))# We use the '-l' option to list all templates.<<1>>[CLEAR]cookie -l
5 | ((100))# Let's take a closer look at the 'full.sh' template.<<1>>[CLEAR]cookie -e full.sh
6 | :4
7 | <<0.5>>v$h<<1.5>>[ESC]:q
8 | ((100))# We'll now create a new script using that template.<<1>>[CLEAR]# We use the '-x' option to make the script executable.<<1>>[CLEAR]cookie -t full.sh -x demo
9 | <<2>>((50))This is a test bash script created for the 'cookie' demo.[ESC]<<1>>:wq
10 | demo -h
11 | ((100))# Did you notice how we used 'demo' instead of './demo'?<<1>>[CLEAR]# We were able to do that because the 'demo' script was initialized in a directory on our system's PATH.<<1>>[CLEAR]# We could have instead used the '-f' option to force cookie to use the current directory.<<1>>[CLEAR]((25))clear && rm ~/Dropbox/bin/main/demo
12 | ((100))# Next, we'll explore the 'hw.tex' template.<<1>>[CLEAR]cookie -e hw.tex
13 | :3
14 | <<0.5>>((50))^12lv68l<<2>>[ESC]:q
15 | ((100))# Did you notice the template variables in the 'hw.tex' template?<<1>>[CLEAR]# Let's see how they work.<<1>>[CLEAR]cookie -t hw.tex -f hw5.tex
16 | 5
17 | CS
18 | 314
19 | 03
20 | [ESC]:3
21 | <<0.5>>^12lv12l<<2>>[ESC]:wq
22 | ((100))# We can also use environment variables to set those values.<<1>>[CLEAR]((25))clear && rm hw5.tex
23 |
24 |
--------------------------------------------------------------------------------
/img/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bbugyi200/cookie/e4315c40cb10488041ac571a32ad145d1abbd6ab/img/demo.gif
--------------------------------------------------------------------------------
/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bbugyi200/cookie/e4315c40cb10488041ac571a32ad145d1abbd6ab/img/logo.png
--------------------------------------------------------------------------------
/scripts/install-deps:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | mkdir lib > /dev/null 2>&1
4 |
5 | make update-bashlibs
6 | cp lib/bashlibs/gutils.sh ./
7 |
8 | git clone https://github.com/kward/shunit2 lib/shunit2
9 | cp lib/shunit2/shunit2 ./
10 |
--------------------------------------------------------------------------------
/scripts/zsh/_cookie:
--------------------------------------------------------------------------------
1 | #compdef cookie
2 |
3 | args=(
4 | "-c[edit config file]"
5 | "-d[debug output]"
6 | "-D[bin-subdir]" ":SUBDIR"
7 | "-e[edit template]:filename:_files -W ${HOME}/.cookiecutters"
8 | "-f[force relative directory]"
9 | "-h[help menu]"
10 | "-l[list templates]::filename:_files -W ${HOME}/.cookiecutters"
11 | "-m[set file mode]:mode:_file_modes"
12 | "-r[remove template]:filename:_files -W ${HOME}/.cookiecutters"
13 | "-x[make executable]"
14 | "-v[verbose output]"
15 | )
16 |
17 | _arguments -C -s "${args[@]}"
18 | _alternative "files:filename:_files -W ${HOME}/.cookiecutters"
19 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [bumpversion]
2 | current_version = 0.2.0
3 | commit = True
4 | tag = True
5 |
--------------------------------------------------------------------------------
/tests/README.md:
--------------------------------------------------------------------------------
1 | # Unit Tests
2 | These tests are written with the help of the [shunit2] test framework.
3 |
4 | The following command will run all of the tests:
5 | ``` bash
6 | make check
7 | ```
8 |
9 | [shunit2]: https://github.com/kward/shunit2
10 |
--------------------------------------------------------------------------------
/tests/runtests:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | EC=0
4 |
5 | printf "%s\n" "------- test_cookie.sh --------"
6 | if ! ./tests/test_cookie.sh; then
7 | EC=1
8 | fi
9 |
10 | printf "\n%s\n" "------- test_install.sh --------"
11 | if ! sudo ./tests/test_install.sh; then
12 | EC=1
13 | fi
14 |
15 | exit "${EC}"
16 |
--------------------------------------------------------------------------------
/tests/test_cookie.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # shellcheck disable=SC2154
4 |
5 | source ./cookie
6 |
7 | my_target="myTarget"
8 | my_template="myTemplate"
9 |
10 | tmpdir=/tmp/foodir
11 | tmpdir2=/tmp/bardir
12 | foobar=/tmp/foobar
13 | foobaz=/tmp/foobaz
14 | fake_temp=cookie.tmp
15 |
16 | setUp() {
17 | echo "Fake Template" > /tmp/"${fake_temp}"
18 | mkdir -p "${tmpdir}" &> /dev/null
19 | mkdir -p "${tmpdir2}" &> /dev/null
20 | }
21 |
22 | tearDown() {
23 | export ROOT_DIR=
24 | export DEFAULT_TARGET_DIR=
25 | export target_dir=
26 |
27 | rm -rf "${tmpdir}"
28 | rm -rf "${tmpdir2}"
29 | rm -rf "${foobar}"
30 | rm -rf "${foobaz}"
31 | rm -rf /tmp/"${fake_temp}"
32 | }
33 |
34 | ###############################################################################
35 | # Test parse_args #
36 | ###############################################################################
37 | test_parse_args__NOX_NOF() {
38 | parse_args "${my_template}" "${my_target}" &> /dev/null
39 | assertEquals 0 "$?"
40 |
41 | assertEquals "${template}" "${my_template}"
42 | assertEquals "${my_target}" "${target}"
43 | assertEquals false "${force}"
44 | }
45 |
46 | test_parse_args__X_NOF() {
47 | parse_args "${my_template}" "-x" "${my_target}" &> /dev/null
48 | assertEquals 0 "$?"
49 |
50 | assertEquals "${template}" "${my_template}"
51 | assertEquals "${my_target}" "${target}"
52 | assertEquals false "${force}"
53 | }
54 |
55 | test_parse_args__X_F() {
56 | parse_args "${my_template}" "-x" "-f" "${my_target}" &> /dev/null
57 | assertEquals 0 "$?"
58 |
59 | assertEquals "+x" "${mode}"
60 | assertEquals true "${force}"
61 | }
62 |
63 | test_parse_args__CONFIG() {
64 | export EDITOR="echo"
65 | output="$(parse_args -c)"
66 | assertEquals "${output##*/}" "config"
67 | }
68 |
69 | test_parse_args__EDIT_NO_EXISTS() {
70 | export EDITOR="echo"
71 | export COOKIE_DIR=/tmp
72 |
73 | assertEquals "/tmp/my_template" "$(parse_args -e my_template)"
74 | }
75 |
76 | test_parse_args__EDIT_EXISTS() {
77 | export EDITOR="echo"
78 | export COOKIE_DIR=/tmp
79 |
80 | assertEquals "${foobar}" "$(parse_args -e foobar)"
81 | }
82 |
83 | test_parse_args__REMOVE() {
84 | export COOKIE_DIR=/tmp
85 |
86 | assertTrue "${fake_temp} NEVER existed to begin with" "[[ -f /tmp/${fake_temp} ]]"
87 | (parse_args "-r" "${fake_temp}" < <(printf "y") &> /dev/null)
88 | assertFalse "${fake_temp} STILL exists" "[[ -f /tmp/${fake_temp} ]]"
89 | }
90 |
91 | ###############################################################################
92 | # Test get_dest_dir #
93 | ###############################################################################
94 | test_get_dest_dir__NO_CONFIG() {
95 | export ROOT_DIR=./
96 | get_dest_dir "${my_target}"
97 | assertEquals "./" "${dest_dir}"
98 | }
99 |
100 | test_get_dest_dir__ROOT() {
101 | export ROOT_DIR=/tmp
102 | get_dest_dir "${my_target}"
103 | assertEquals "/tmp" "${dest_dir}"
104 | }
105 |
106 | test_get_dest_dir__ROOT_AND_TARGET() {
107 | export ROOT_DIR=/tmp
108 | export DEFAULT_TARGET_DIR=foobar
109 |
110 | get_dest_dir "${my_target}"
111 | assertEquals "${foobar}" "${dest_dir}"
112 | rm -rf "${foobar}"
113 | }
114 |
115 | test_get_dest_dir__ROOT_AND_TARGET_IN_ROOT() {
116 | export ROOT_DIR=/tmp
117 | export DEFAULT_TARGET_DIR=foobar
118 |
119 | cd /tmp || return 1
120 |
121 | get_dest_dir "${my_target}"
122 | assertEquals "${foobar}" "${dest_dir}"
123 | rm -rf "${foobar}"
124 | }
125 |
126 | test_get_dest_dir__ROOT_AND_TARGET_IN_ROOT_SUBDIR() {
127 | export ROOT_DIR=/tmp
128 | export DEFAULT_TARGET_DIR=foodir
129 |
130 | OLD_PWD=$PWD
131 | cd "${tmpdir2}" || return 1
132 |
133 | get_dest_dir "${my_target}"
134 | assertEquals "${tmpdir2}" "${dest_dir}"
135 |
136 | cd "$OLD_PWD" || return 1
137 | }
138 |
139 | test_get_dest_dir__ABSOLUTE_PATH() {
140 | export ROOT_DIR=/tmp
141 | get_dest_dir "/home/$HOME/my_target"
142 |
143 | assertEquals "/home/$HOME" "${dest_dir}"
144 | }
145 |
146 | ###############################################################################
147 | # Test editor_cmd #
148 | ###############################################################################
149 | test_editor_cmd__VIM() {
150 | export EDITOR="vim"
151 | read editor_cmd < <(editor_cmd "" "" "" "${my_target}")
152 | assertEquals "vim ${my_target}" "${editor_cmd}"
153 | }
154 |
155 | test_editor_cmd__VIM_NSTART() {
156 | export EDITOR="vim"
157 | read editor_cmd < <(editor_cmd "5" "15" "" "${my_target}")
158 | assertEquals "vim -c \"call cursor(5,15)\" ${my_target}" "${editor_cmd}"
159 | }
160 |
161 | test_editor_cmd__VIM_ISTART() {
162 | export EDITOR="vim"
163 | read editor_cmd < <(editor_cmd "5" "15" "INSERT" "${my_target}")
164 | assertEquals "vim -c \"call cursor(5,15)\" +startinsert ${my_target}" "${editor_cmd}"
165 | }
166 |
167 | test_editor_cmd__BAD_ARGS() {
168 | export EDITOR="vim"
169 | read editor_cmd < <(editor_cmd "0" "" "INSERT" "${my_target}")
170 | assertEquals "vim +startinsert ${my_target}" "${editor_cmd}"
171 | }
172 |
173 | test_editor_cmd__NOVIM() {
174 | export EDITOR="nano"
175 | read editor_cmd < <(editor_cmd "5" "15" "INSERT" "${my_target}")
176 | assertEquals "nano ${my_target}" "${editor_cmd}"
177 | }
178 |
179 | ###############################################################################
180 | # Test template_engine #
181 | ###############################################################################
182 | test_template_engine__START() {
183 | read -r -d '' old_contents <<-EOM
184 | FOO
185 | {% INSERT %}
186 | EOM
187 |
188 | template_engine "${old_contents}"
189 |
190 | IFS= read -r -d '' expected <<-EOM
191 | FOO
192 | EOM
193 |
194 | assertEquals "${expected}" "${new_contents}"
195 |
196 | read -r -d '' old_contents <<-EOM
197 | FOO
198 | {% NORMAL %}
199 | EOM
200 |
201 | template_engine "${old_contents}"
202 | assertEquals "${expected}" "${new_contents}"
203 | }
204 |
205 | test_template_engine__START_LINE_AND_COL_CHECK() {
206 | read -r -d '' contents <<-EOM
207 | FOO
208 | {% INSERT %}
209 | EOM
210 |
211 | template_engine "${contents}"
212 | assertEquals "2" "${start_line}"
213 | assertEquals "5" "${start_col}"
214 |
215 | read -r -d '' contents <<-EOM
216 | FOO
217 |
218 | BAR {% NORMAL %} { OTHER STUFF }
219 | EOM
220 |
221 | template_engine "${contents}"
222 | assertEquals "3" "${start_line}"
223 | assertEquals "7" "${start_col}"
224 |
225 | read -r -d '' contents <<-EOM
226 | FOO
227 | {% INSERT %}
228 | EOM
229 |
230 | template_engine "${contents}"
231 | assertEquals "2" "${start_line}"
232 | assertEquals "1" "${start_col}"
233 | }
234 |
235 | test_template_engine__ENVVAR() {
236 | read -r -d '' old_contents <<-EOM
237 | FOO
238 | {{ my_variable }}
239 | EOM
240 | export my_variable="BAR"
241 | template_engine "${old_contents}"
242 | read -r -d '' expected <<-EOM
243 | FOO
244 | BAR
245 | EOM
246 |
247 | assertEquals "${expected}" "${new_contents}"
248 | }
249 |
250 | test_template_engine__CC_ENVVAR() {
251 | read -r -d '' old_contents <<-EOM
252 | FOO
253 | {{ cookiecutter.my_variable }}
254 | EOM
255 | export my_variable="BAR"
256 | template_engine "${old_contents}"
257 | read -r -d '' expected <<-EOM
258 | FOO
259 | BAR
260 | EOM
261 |
262 | assertEquals "${expected}" "${new_contents}"
263 | }
264 |
265 | test_template_engine__ENVVAR_NOT_DEFINED() {
266 | export my_variable=
267 | read -r -d '' old_contents <<-EOM
268 | FOO
269 | {{ my_variable }}
270 | EOM
271 |
272 | read -r -d '' expected <<-EOM
273 | FOO
274 | BAR
275 | EOM
276 |
277 | template_engine "${old_contents}" < <(echo "BAR")
278 |
279 | assertEquals "${expected}" "${new_contents}"
280 | }
281 |
282 | test_template_engine__NO_SPACES() {
283 | read -r -d '' old_contents <<-EOM
284 | FOO
285 | {{my_variable}}
286 | EOM
287 | export my_variable="BAR"
288 | template_engine "${old_contents}"
289 | read -r -d '' expected <<-EOM
290 | FOO
291 | BAR
292 | EOM
293 |
294 | assertEquals "${expected}" "${new_contents}"
295 | }
296 |
297 | ###############################################################################
298 | # Test Exit Handler #
299 | ###############################################################################
300 | test_exit_handler__CREATED_ZERO_EC() {
301 | exit_handler 0 true "${tmpdir}"
302 | assertTrue "${tmpdir} is NOT a directory" "[[ -d ${tmpdir} ]]"
303 | }
304 |
305 | test_exit_handler__NOT_CREATED_NONZERO_EC() {
306 | exit_handler 1 false "${tmpdir}"
307 | assertTrue "${tmpdir} is NOT a directory" "[[ -d ${tmpdir} ]]"
308 | }
309 |
310 | test_exit_handler__CREATED_ZERO_EC() {
311 | exit_handler 1 true "${tmpdir}"
312 | assertFalse "${tmpdir} still exists" "[[ -d ${tmpdir} ]]"
313 | }
314 |
315 | ###############################################################################
316 | # Test main #
317 | ###############################################################################
318 | test_main() {
319 | export EDITOR=":"
320 | export PARENT_DIR=/tmp
321 | export DEFAULT_TARGET_DIR=
322 | export COOKIE_DIR=/tmp
323 |
324 | main "${fake_temp}" "-x" "foobar" &> /dev/null
325 | assertTrue "foobar is NOT a file." "[ -f foobar ]"
326 | assertTrue "foobar is NOT executable." "[ -x foobar ]"
327 | }
328 |
329 | test_main__LIST() {
330 | export COOKIE_DIR="${tmpdir2}"
331 |
332 | mkdir -p "${COOKIE_DIR}"
333 |
334 | template1="${COOKIE_DIR}"/footemp
335 | template2="${COOKIE_DIR}"/bartemp
336 |
337 | echo "FOOBAR" > "${template1}"
338 | touch "${template2}"
339 |
340 | read -r -d '' expected <<-EOM
341 | bartemp
342 | footemp
343 | EOM
344 |
345 | assertEquals "${expected}" "$(main "-l")"
346 | assertEquals "FOOBAR" "$(main "-l" "footemp")"
347 | }
348 |
349 | test_main__TEMPLATE_NOT_EXIST() {
350 | (main fake_template.sh foobar &> /dev/null); EC=$?
351 | assertTrue "cookie fails to die when the template doesn't exist" "[[ ${EC} -ne 0 ]]"
352 | }
353 |
354 | test_main__USAGE_ERROR() {
355 | (main &> /dev/null); EC=$?
356 | assertTrue "cookie failed to die with a usage message" "[[ ${EC} -eq 2 ]]"
357 | }
358 |
359 | test_main__TARGET_ALREADY_EXISTS() {
360 | export COOKIE_DIR=/tmp
361 |
362 | echo ":)" > "${foobar}"
363 |
364 | (main "${fake_temp}" "${foobar}" &> /dev/null); EC=$?
365 | assertTrue "cookie fails when the target already exists" "[[ ${EC} -eq 0 ]]"
366 | assertEquals ":)" "$(cat ${foobar})"
367 | }
368 |
369 | test_main__EXEC_HOOK_CMD_X() {
370 | export EXEC_HOOK_CMD="echo \"Hook Output: \${TARGET}\" > ${foobaz}"
371 | (main "${fake_temp}" -x "${foobar}" &> /dev/null)
372 |
373 | assertEquals "Hook Output: ${foobar}" "$(cat "${foobaz}")"
374 | }
375 |
376 | test_main__EXEC_HOOK_CMD_MASK() {
377 | export EXEC_HOOK_CMD="echo \"Hook Output: \${TARGET}\" > ${foobaz}"
378 | (main "${fake_temp}" -m 755 "${foobar}" &> /dev/null)
379 |
380 | assertEquals "Hook Output: ${foobar}" "$(cat "${foobaz}")"
381 | }
382 |
383 | test_main__BIN_SUBDIR() {
384 | export ROOT_DIR=/tmp
385 |
386 | (main "${fake_temp}" "-D" "foodir" "foobar" &> /dev/null); EC=$?
387 | assertTrue "cookie fails when using -D option" "[[ ${EC} -eq 0 ]]"
388 | assertTrue "cookie does not respect the -D option" "[[ -f /tmp/foodir/foobar ]]"
389 | }
390 |
391 | test_main__DEEP_TARGET() {
392 | export ROOT_DIR=/tmp
393 |
394 | (main "${fake_temp}" "foo/bar" &> /dev/null); EC=$?
395 | assertTrue "cookie fails when using deep target" "[[ ${EC} -eq 0 ]]"
396 | assertTrue "cookie fails to initialize deep target" "[[ -f /tmp/foo/bar ]]"
397 |
398 | rm -rf "/tmp/foo/bar"
399 | }
400 |
401 | test_main__MODE() {
402 | export ROOT_DIR=/tmp
403 |
404 | (main "${fake_temp}" "-m" "666" "${foobar}"); EC=$?
405 | assertTrue "cookie fails when using --mode option" "[[ ${EC} -eq 0 ]]"
406 | assertEquals "666" "$(stat -c "%a" "${foobar}")"
407 | }
408 |
409 | test_main__TEMPLATE_IS_DIRECTORY() {
410 | export ROOT_DIR=/tmp
411 |
412 | (main "$(basename "${tmpdir}")" "${foobar}"); EC=$?
413 |
414 | assertTrue "cookie fails to copy template directory" "[[ -d ${foobar} ]]"
415 | }
416 |
417 | test_main_NO_TARGET() {
418 | export ROOT_DIR=/var/tmp
419 |
420 | (main "${fake_temp}"); EC=$?
421 | assertTrue "cookie fails when target is not given" "[[ -f /var/tmp/$(basename ${fake_temp}) ]]"
422 | }
423 |
424 | source shunit2
425 |
--------------------------------------------------------------------------------
/tests/test_install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | EC=0
4 | capout=/tmp/cookie.capout
5 |
6 | ###########
7 | # Setup #
8 | ###########
9 | make uninstall-all &> /dev/null
10 |
11 | ##################
12 | # make install #
13 | ##################
14 | make install &> "${capout}"
15 |
16 | error_msg=
17 |
18 | if ! [[ -f /usr/bin/cookie && -f /usr/bin/gutils.sh ]]; then
19 | error_msg="/usr/bin/cookie does not exist"
20 | elif ! [[ -x /usr/bin/cookie ]]; then
21 | error_msg="/usr/bin/cookie is not executable"
22 | fi
23 |
24 | if [[ -n "${error_msg}" ]]; then
25 | echo "[FAIL] make install: ${error_msg}"
26 | EC=1
27 | else
28 | echo "[PASS] make install"
29 | fi
30 |
31 | ########################
32 | # make uninstall-all #
33 | ########################
34 | make uninstall-all &> "${capout}"
35 |
36 | error_msg=
37 |
38 | if [[ -f /usr/bin/cookie ]]; then
39 | error_msg="/usr/bin/cookie still exists"
40 | fi
41 |
42 | if [[ -f /usr/bin/gutils.sh ]]; then
43 | error_msg="/usr/bin/gutils.sh still exists"
44 | fi
45 |
46 | if [[ -n "${error_msg}" ]]; then
47 | echo "[FAIL] make uninstall-all: ${error_msg}"
48 | EC=1
49 | else
50 | echo "[PASS] make uninstall-all"
51 | fi
52 |
53 | ##############
54 | # Teardown #
55 | ##############
56 | if [[ "${EC}" -ne 0 ]]; then
57 | printf "\n------- Captured Output -------\n"
58 | cat "${capout}"
59 | fi
60 |
61 | make install &> /dev/null # >>> TEARDOWN
62 |
63 | rm "${capout}"
64 | exit "${EC}"
65 |
--------------------------------------------------------------------------------