summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMica White <botahamec@outlook.com>2026-10-04 19:24:09 -0400
committerMica White <botahamec@outlook.com>2026-10-04 19:24:09 -0400
commit6dfd539d4a1647cb99a19f9fc14f2e0af231cb1d (patch)
tree8e1b3879bb1d560c4129b87bc8624bf4117928ff
First commitHEADmain
-rwxr-xr-xLICENSE121
-rw-r--r--README.md78
-rwxr-xr-xshake168
3 files changed, 367 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100755
index 0000000..0e259d4
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+ HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display,
+ communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+ likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+ v. rights protecting the extraction, dissemination, use and reuse of data
+ in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation
+ thereof, including any amended or successor version of such
+ directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+ world based on applicable law or treaty, and any national
+ implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+ warranties of any kind concerning the Work, express, implied,
+ statutory or otherwise, including without limitation warranties of
+ title, merchantability, fitness for a particular purpose, non
+ infringement, or the absence of latent or other defects, accuracy, or
+ the present or absence of errors, whether or not discoverable, all to
+ the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without
+ limitation any person's Copyright and Related Rights in the Work.
+ Further, Affirmer disclaims responsibility for obtaining any necessary
+ consents, permissions or other rights required for any use of the
+ Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to
+ this CC0 or use of the Work.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..896808e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,78 @@
+# shake - a simple shell-based command runner
+
+`shake` is a simpler `just`.
+
+Commands are stored in a file called `shakefile` using POSIX shell syntax:
+
+```sh
+HOST=$(uname -a)
+
+shake_doc build
+build() {
+ shake_echo_command cc *.c -o main
+}
+
+shake_doc test
+test() {
+ build
+ shake_echo_command ./test --all
+ echo "Yay, all your tests passed!"
+}
+
+shake_doc b
+alias b=build
+```
+
+```
+$ shake -l
+build
+test
+b
+$ shake test
+cc *.c -o main
+./test --all
+Yay, all your tests passed!
+```
+
+`shake` has many of `just`'s features, but not all. The following are not
+supported:
+ - Windows, without installing something like Git Bash
+ - Recipes cannot accept command-line arguments
+ - `shake` does not have a custom syntax. It uses POSIX shell.
+ - It cannot support arbitrary languages. Just POSIX shell.
+
+## Installation
+
+Copy the `shake` file from this repo into your `~/.local/bin`
+
+## Quick Start
+
+Once `shake` is installed, create a file named `shakefile` in the root of your
+project with the following contents:
+
+```sh
+default() {
+ echo "This is a recipe"
+}
+
+another-recipe() {
+ echo "This is another recipe"
+}
+```
+
+By default, running `shake` with no arguments runs the `default` recipe. One or
+more arguments specify the recipe(s) to run:
+
+```
+$ shake
+This is a recipe
+$ shake another-recipe
+This is another recipe
+```
+
+## Contributing
+
+`shake` is considered feature complete, but if you have a cool idea, feel free
+to create an issue for it. If you do submit a patch, note that `shake` is
+released under the [CC0](https://creativecommons.org/publicdomain/zero/1.0/legalcode.txt)
+public license and thus your changes must also be under this license.
diff --git a/shake b/shake
new file mode 100755
index 0000000..d0f4110
--- /dev/null
+++ b/shake
@@ -0,0 +1,168 @@
+#!/usr/bin/env sh
+#shellcheck disable=SC1090,SC2034
+
+SHAKE_VERSION="0.1"
+TEMPDIR="/tmp"
+__SHAKE_COMMAND_LIST=""
+
+# Predefined constants
+HEX="0123456789abcdef"
+HEXLOWER="0123456789abcdef"
+HEXUPPER="0123456789ABCDEF"
+PATH_SEP="/"
+PATH_VAR_SEP=":"
+CLEAR="\ec"
+NORMAL="\e[0m"
+BOLD="\e[1m"
+UNDERLINE="\e[4m"
+INVERT="\e[7m"
+HIDE="\e[8m"
+STRIKETHROUGH="\e[9m"
+BLACK="\e[30m"
+RED="\e[31m"
+GREEN="\e[32m"
+YELLOW="\e[33m"
+BLUE="\e[34m"
+MAGENTA="\e[35m"
+CYAN="\e[36m"
+WHITE="\e[37m"
+BG_BLACK="\e[40m"
+BG_RED="\e[41m"
+BG_GREEN="\e[42m"
+BG_YELLOW="\e[43m"
+BG_BLUE="\e[44m"
+BG_MAGENTA="\e[45m"
+BG_CYAN="\e[46m"
+BG_WHITE="\e[47m"
+
+shake_echo_command() {
+ echo "$@"
+ "$@"
+}
+
+shake_source_command() {
+ __SHAKE_TMPFILE="$TEMPDIR/.shake-tmp-$(od -N4 -tu /dev/random | awk 'NR==1 {print $2} {}')"
+ "$@" > "$__SHAKE_TMPFILE"
+ . "$__SHAKE_TMPFILE"
+}
+
+shake_echoerr() {
+ echo "$@" 1>&2;
+}
+
+shake_no_cd() {
+ cd "$__ORIGINAL_DIR" || return
+}
+
+shake_doc() {
+ __SHAKE_COMMAND_LIST="$__SHAKE_COMMAND_LIST $1"
+}
+
+shake_minimum_version() {
+ if [ -z "$1" ]; then
+ shake_echoerr "shake_minimum_version must be called with a version number, such as $SHAKE_VERSION"
+ exit 1
+ fi
+ if [ "$(echo "$1>$SHAKE_VERSION" | bc -l)" = "1" ]; then
+ shake_echoerr "This project's Shakefile requires version $1. The installed version is $SHAKE_VERSION."
+ exit 1
+ fi
+}
+
+__SHAKE_WANTS_LIST=$(echo "$1" | grep -E "^(--list|-l)$" - > /dev/null && echo "$?")
+__SHAKE_WANTS_HELP=$(echo "$1" | grep -E "^(--help|-h)$" - > /dev/null && echo "$?")
+__SHAKE_WANTS_FILE=$(echo "$1" | grep -E "^(--shakefile)$" - > /dev/null && echo "$?")
+__SHAKE_WANTS_GLOBAL=$(echo "$1" | grep -E "^(--global-shakefile|-g)$" - > /dev/null && echo "$?")
+__COMMANDS=""
+for opt in "$@"; do
+ if echo "$opt" | grep "^--shakefile$" - > /dev/null; then
+ __SHAKE_SKIP_NEXT="1"
+ continue
+ fi
+ if echo "$opt" | grep "^[^-]" - > /dev/null; then
+ if [ -z "$__SHAKE_SKIP_NEXT" ]; then
+ __COMMANDS="$__COMMANDS $opt"
+ else
+ __SHAKE_SKIP_NEXT=
+ fi
+ fi
+done
+if [ "$#" -lt 1 ]; then
+ __COMMANDS="default"
+fi
+
+__ORIGINAL_DIR="$PWD"
+
+__detect_shakefile() {
+ for file in ./*; do
+ if basename "$file" | grep --ignore-case -E "^\.?shakefile(\..*)?$" - > /dev/null; then
+ __SHAKEFILE="$file"
+ return 1
+ fi
+ done
+ return 0
+}
+
+__exit_if_at_root() {
+ if [ "$PWD" = "/" ]; then
+ shake_echoerr "No Shakefile found"
+ cd "$__ORIGINAL_DIR" || exit 1
+ exit 1
+ fi
+}
+
+__exit_if_at_home() {
+ if [ "$PWD" = "$HOME" ]; then
+ shake_echoerr "No Shakefile found"
+ cd "$__ORIGINAL_DIR" || exit 1
+ exit 1
+ fi
+}
+
+if [ "$__SHAKE_WANTS_FILE" = "0" ]; then
+ __SHAKEFILE="$2"
+ cd "$(dirname "$2")" || return
+elif [ "$__SHAKE_WANTS_GLOBAL" = "0" ]; then
+ if [ -z "$XDG_CONFIG_HOME" ]; then
+ XDG_CONFIG_HOME="$HOME/.config"
+ fi
+
+ cd "$XDG_CONFIG_HOME/shake" || return
+ while __detect_shakefile; do
+ __exit_if_at_home
+ cd ..
+ done
+else
+ while __detect_shakefile; do
+ __exit_if_at_root
+ cd ..
+ done
+fi
+
+. "$__SHAKEFILE"
+if [ "$__SHAKE_WANTS_LIST" = "0" ]; then
+ for command in $__SHAKE_COMMAND_LIST; do
+ echo "$command"
+ done
+elif [ "$__SHAKE_WANTS_HELP" = "0" ]; then
+ echo "A simple shell-based command runner"
+ echo ""
+ echo "Usage: shake [RECIPES]..."
+ echo ""
+ echo "Arguments:"
+ echo " [RECIPES]...: Overrides the recipes to run, defaulting"
+ echo " to the \`default\` recipe."
+ echo ""
+ echo "Arguments:"
+ echo " -l, --list: List the available and documented recipes"
+ echo " -h, --help: View this help message"
+ echo " -g, --global-shakefile: Use a global shakefile from the config home"
+ echo " (e.g. \$XDG_CONFIG_HOME/shake/shakefile)"
+ echo " --shakefile <FILE>: Use a specified shakefile"
+else
+ for command in $__COMMANDS; do
+ "$command"
+ done
+fi
+
+cd "$__ORIGINAL_DIR" || exit 1