.. default-domain:: bash ========= Functions ========= .. file:: just_functions.bsh A few functions are defined in a common. Sourced here for DRY .. envvar:: DRYRUN Print out some commands instead of executing. ${DRYRUN} Display key commands as they would be executed. This is conveniently set by using the ``-n``/``--dryrun`` argument. Default: ```` .. rubric:: Example .. code-block:: bash function Docker() { ${DRYRUN} docker "${@}" } .. note:: :envvar:`DRYRUN` doesn't just echo out the command, it uses the :func:`print_command print_command` function to print out a command that can be copied and pasted in. This handles spaces correctly unlike echo. .. seealso:: :func:`print_command print_command` .. envvar:: JUST_SEPARATOR Separator used for :func:`just_functions.bsh get_args` When passing an unknown number of arguments, the JUST_SEPARATOR can be used to begin and end a group of arguments. Default: -- .. seealso:: :func:`get_args`, :func:`get_additional_args` .. envvar:: JUST_SAFE_LOAD_DELIMITER Separator used for safe_load safe_load reads a key:value file, and this variable is used to separate the keys and values. Default: = .. seealso:: :func:`safe_load` just_plugins ------------ Plugin system for just .. rubric:: Usage Certain just targets can be used for many different projects, and rather then repeating this code, just_plugins give a way to expose the common code of these targets to many different projects. To add plugins to your project, simply add the plugin filenames to your :file:`.justplugins` file. When writing a new plugin, you need to follow a few rules: 1. A case statement in a defaultify function must be written. The case statement should have a unique name that is not likely to be used anywhere else. All plugin functions have to exist in the same namespace. 2. Add the case names to the array :envvar:`JUST_DEFAULTIFY_FUNCTIONS` to allow the commonly used functionality to be added as targets for just 3. Add the plugin filename to the array :envvar:`JUST_HELP_FILES` 4. Must return non-zero if the argument is not matched, and return 0 on target match .. rubric:: Example Source code of a just_example_functions.bsh, which looks something like: .. code-block:: bash JUST_DEFAULTIFY_FUNCTIONS+=(just_example_defaultify) JUST_HELP_FILES+=("${BASH_SOURCE[0]}") function just_example_defaultify() { arg="${1}" shift 1 case "${arg}" in foo|bar) #Foobar test echo "Foo or bar: ${1}" extra_args=1 ;; *) plugin_not_found=1 ;; esac return 0 } .. envvar:: JUST_PLUGIN_FILE Name of the just plugin file Defaults to :file:`.justplugins` in the same directory as the Justfile, but can be overridden to another filename/path by exporting this variable .. note:: Since plugins are loaded right before the project environment, you can not set the :envvar:`JUST_PLUGIN_FILE` in the project env file. If you are using a custom :envvar:`JUST_PLUGIN_FILE`, it will need to be set in the :file:`setup.env` .. seealso:: just safe_env .. envvar:: JUST_DEFAULTIFY_FUNCTIONS List of defaultify functions The values of this array are executed as a command. Each plugin should have a unique name for the function, and is responsible for adding that function name to :envvar:`JUST_DEFAULTIFY_FUNCTIONS` .. envvar:: JUST_HELP_FILES This should include just_functions.bsh, the Justfile, and any plugins. The plugin is responsible for adding itself to this array. .. rubric:: Usage Every project should have a :ref:`setup file `, typically called ``setup.env``. This script's location is stored in :envvar:`JUST_SETUP_SCRIPT` and is also where :envvar:`JUST_HELP_FILES` should be set. .. seealso:: :envvar:`JUST_SETUP_SCRIPT` .. envvar:: JUST_PROJECT_PREFIX Variable prefix for project Some just functions need to know the prefix used for a project's environment variables (e.g., in order to list all project env variables using compgen). This should be defined in your project's settings env file, and does not need to be overridable. .. envvar:: JUST_SETTINGS Absolute path to the project settings env file; set automatically when you source :file:`just_env` JUST_SETTINGS is set automatically by :file:`just_env`, which is used to setup the project's environment by sourcing the projects settings env file very early in the project's Justfile. .. rubric:: Example Typically, the Justfile will include a line like this very near the top: .. code-block:: bash source "${VSI_COMMON_DIR}/linux/just_files/just_env" "$(dirname "${BASH_SOURCE[0]}")"/'my_project.env' .. note:: :envvar:`JUST_SETTINGS` supports multiple files using the :envvar:`JUST_SETTINGS_SEPARATOR` (default: ``///``). The user and developer should not need to come up with this themselves, as it is automatically generated, unless they are manually overriding it. .. envvar:: JUST_SETTINGS_SEPARATOR Separator used between filenames of :envvar:`JUST_SETTINGS` .. function:: source_environment_files Convenience function for sourcing environments The just system works by sourcing environment files, aka settings files, and plugins 1. First, ${project_dir}/local.env. This file should never be added to version control. It should contain customizations for that particular install. 2. Then, the project's settings env file. This file should ideally contain all the default values necessary to run without any local.env settings. 3. Last, ${project_dir}/local_post.env. This file should never be added to version control. It is rarely used, except in situations where the value of a variable is based off of another variable. This is why it is loaded last. 4. Any plugins that are identified, are also sourced :Arguments: ``$1`` - The project settings filename (e.g., vsi_common.env) .. _source_environment_files: :Parameters: * [``JUST_LOCAL_SETTINGS``] - Path to the local settings file. Default: ${same_dir}/local.env * [``JUST_LOCAL_SETTINGS_POST``] - Path to the post local settings file. Default: ${same_dir}/local_post.env .. rubric:: Usage ``JUST_LOCAL_SETTINGS`` and ``JUST_LOCAL_SETTINGS_POST`` must not be set in any of the the project's settings files or plugins, as it will not have the desired effect. Instead, they should either be manually set in the environment or set in the setup script (see :envvar:`JUST_SETUP_SCRIPT`) .. note:: Certain exceptions to not storing values in the project settings file make sense such as credentials, encryption keys, etc... information that should not be hard-coded ever. Only require settings in ``local.env`` when it cannot be avoided. For example, it might be reasonable to use openssl to create ssl certs in a default location (that is ignored by version control). .. seealso:: :func:`just_common.bsh _just_get_plugins` :envvar:`JUST_SETUP_SCRIPT` .. function:: create_local_settings_file :Arguments: ``$1`` - Path to the settings file :Parameters: [``JUST_LOCAL_SETTINGS``] - The location to create the local settings file [``JUST_LOCAL_SETTINGS_POST``] - The location to create the post local local settings file :Return Value: * ``0`` - Settings file was created * ``1`` - Settings file was not created :Output: [``JUST_LOCAL_SETTINGS``] - Path to the local settings file. Default: ${same_dir}/local.env [``JUST_LOCAL_SETTINGS_POST``] - Path to the post local settings file. Default: ${same_dir}/local_post.env Try to create the local.env settings file Create the local.env settings file if a) it does not already exist and b) it will be owned by the correct user. .. function:: translate_just_settings Convert :envvar:`JUST_SETTINGS` to an array: ``JUST_SETTINGSS``, translating the paths to another filesystem :Arguments: - ``$1`` - Directory name to be translated from - ``$2`` - Directory name to translate to :Output: ``JUST_SETTINGSS`` - Array of the translated paths Useful for translating :envvar:`JUST_SETTINGS` from host to a container. .. seealso:: :func:`container_functions.bsh translate_path` .. function:: auto_path_escape Automatically add an escape character to paths that match a pattern TODO: Explain .. seealso:: :file:`just_env` .. function:: set_temp_array :Arguments: - ``$1`` - Name of array to check if it is already set - ``$2...`` - Default values of the array :Output: ``JUST_TEMP_ARRAY`` - Destination for all the values set DEPRECATED: Set array to default values if not already set. In bash, when setting a variable to a default value if it has not already been set, it is typical to follow the pattern: ``: ${MY_VAR=default}`` However, this syntax does not work for an array. Bash 3.2 does not give an equivalent one line version of this. Instead, using :func:`set_temp_array`, JUST_TEMP_ARRAY can be set to a default value if not already set (albeit, in two lines). It works by checking if an array is set. If it is, it copies all the values to the array JUST_TEMP_ARRAY. If it is not, it will copy the rest of the arguments (the default values) to ``JUST_TEMP_ARRAY``. When using this function with set -u turned on, it is best to reference ``JUST_TEMP_ARRAY`` by (${JUST_TEMP_ARRAY[@]+"${JUST_TEMP_ARRAY[@]}"}) in case the array is empty, as this triggers bash's unbound variable test. .. rubric:: Example .. code-block:: bash set_temp_array MY_ARRAY default1 default\ 2 "default 3" MY_ARRAY=(${JUST_TEMP_ARRAY[@]+"${JUST_TEMP_ARRAY[@]}"}) # And when done, clean up unset JUST_TEMP_ARRAY .. seealso:: :func:`set_array_default` .. function:: set_array_default :Arguments: ``$1`` - Name of array to check if it is already set ``$2...`` - Default values of the array Set array to default values if not already set. In bash, when setting a variable to a default value, if it has not already been set, it is typical to follow the pattern: ``: ${MY_VAR=default}`` However, this syntax does not work for an array. Bash 3.2 does not give an equivalent one line version of this. Instead, using :func:`set_array_default`, an array can be set to a default value if not already set (albeit, in two lines). It works by checking if an array is set. If it is set, nothing happens. If it is not set, it will copy the rest of the arguments (the default values) to the array named. .. note:: An empty array counts as set .. rubric:: Example .. code-block:: bash set_array_default MY_ARRAY default1 default\ 2 "default 3" .. function:: pretty_print_help :Arguments: *stdin* - Each line is an entry in the pretty printout, separated by " ${:envvar:`JUST_HELP_SEPARATOR`} " (without the quotes, but with the spaces) :Parameters: indent - How much the right side should be indented to make a uniform output Restructures text to indent properly on wrap around Each line consists of a command that will be printed on the left, and the indented description on the right. The description is wrapped around based on tput cols. .. seealso:: :envvar:`JUST_HELP_SEPARATOR` .. function:: print_help Prints the auto generated help info from Justfile .. function:: is_powershell Check if the current command window is powershell Using a Window's title trick, determine if the shell is running in powershell or some other shell (e.g., Windows command prompt, cygwin or git bash) .. function:: need_tty Check to see if a tty is missing. Normal tty checks do not work in mintty, as the pts appears to be a tty to native apps, but external apps will see a pipe. Checking to see if stdin has a name of "None" will tell us if we don't have a piped stdin. Calling this function is an expensive operation, and takes roughly 1-2 seconds (to call a new powershell). Setting JUST_IS_TTY to 1 will cause the function to return false instantly. :Parameters: [``JUST_IS_TTY``] - If set to `1``, need_tty automatically returns 1. If set to ``0``, then :func:`need_tty` will automatically returns 0 once. If ``JUST_IS_TTY`` is set in a ``local.env`` file, then it should be set conditionally: .. code-block:: bash : ${JUST_IS_TTY=0} This way it is triggered only once, instead of every time ``local.env`` is source, which can be quite frequently. One use case for this is "ssh from linux into windows, start git bash, . setup.env, and then run python". This will detect a fake tty name, because it is piped. However, you still need a tty because in python up and down will not work. I do not know why in this case... .. seealso:: https://tinyurl.com/yb5jjzqt .. function:: setup_tty Pops up a new powershell window and runs just Cygwin bash is pretty bad. The new git bash (MINGW64) does not have a tty. This is a problem for docker. Powershell does have a tty, but cannot run bash scrips natively. The solution is to run bash in powershell (which is not as straight forward as it sounds). This function is designed to re-execute in a new powershell on Windows if not in powershell :Parameters: [``JUST_PTY``] - PTY app to use create a psuedoterminal for windows apps. By default, uses ``winpty``. Set to ``powershell`` for the old behavior .. rubric:: Usage setup_tty ${@+"${@}"} .. function:: defaultify Default commands for just Handles a few default commands for just: - Calls plugins specified in :envvar:`JUST_DEFAULTIFY_FUNCTIONS` - --dryrun|-n - Sets DRYRUN to :func:`print_command print_command`. This way ${DRYRUN} can be put in front of any command and be printed instead of executed when in dryrun mode - --separator - Used to override the :envvar:`JUST_SEPARATOR` (default --) to anything else. This only affect commands that use get_args - -h|--help|help - Prints out help using print_help - _null - A target that does nothing. This is seldom needed. - * - For all other commands not captured yet, called unknownify (which can be overridden for other behavior) to print an error message. .. function:: unknownify :Parameters: ``JUST_FILE_NOT_FOUND`` - Prints out a different message if ``JUST_FILE_NOT_FOUND`` is set to ``1`` The function executed when an unknown target is specified The default behavior is to print an error and exit 1. However, a custom unknownify can be declared in Justfile to override this behavior .. var:: extra_args The number of additional arguments consumed in a caseify target When writing a caseify target, by default one argument is consumed. This is enough for any simple target. However some targets need additional arguments. There are really three situation when extra arguments are needed #. A fixed number of arguments. For example, if 3 arguments are needed, just use $1, $2, $3, and set ``extra_args=3`` #. The rest of the arguments are going to be consumed. In this case, use all the arguments (typically by $@) and set extra_args=${#} #. An unknown number of arguments need to be used, and maybe multiple groups of unknown arguments. This is the case where JUST_SEPARATOR is used. (See EXAMPLE) .. rubric:: Example .. code-block:: bash # Lets say we call: just test 11 22 33 -- aa bb cc dd -- _null # test is called with 3 arguments # aa is called with 3 arguments # _null is called .. rubric:: The deprecated way .. code-block:: bash # Lets say we call: just test -- 11 22 33 -- aa bb cc dd -- _null # And I have a target test that takes two sets of arguments of unknown length test) get_args ${@+"${@}"} first=${args[@]+"${args[@]}"} get_additional_args ${@+"${@}"} second=${args[@]+"${args[@]}"} ;; In this case, there is no need to add :var:`extra_args`, :func:`get_args` does this already .. function:: justify :Arguments: ``$1...`` - list of targets The main loop of just Handles determining what subcommands are and calling commands and subcommands. When one target is called from another target, justify should be used to do the calling, for example: justify target2 .. function:: callify Helper for consuming arguments and calling a function with them Takes: cmd -- $1 $2 $3 ... $n -- $m $m+1 ... and calls: cmd $1 $2 $3 ... $n and consumes n+2 args Trailing -- is optional .. rubric:: Example .. code-block:: bash target) callify python ${@+"${@}"} ;; just target -- -c "print 1+3" .. note:: Not sure this is worth keeping .. function:: get_args :Arguments: Inputs ``$@`` - All the unconsumed arguments :Output: ``args`` - Array of unknown number of arguments consumed ``get_args_args_used`` - Number of arguments used, including separator Get and consume an unknown collection of arguments Gets an unknown number of arguments. The ending of the collection of arguments is annotated with the :envvar:`JUST_SEPARATOR`. The trailing annotation is optional as long as there are no more just commands following .. rubric:: Bugs This will conveniently update ``extra_args`` inside of the Justfile caseify function. However, if you are calling from another function (i.e., not writing a caseify section), this will produce incorrect behavior; therefore, extra_args should be made a local variable in the calling function .. note:: ``get_args_args_used`` is a global variable. Unsetting it can cause unexpected behavior .. function:: get_additional_args Get and consume additional collections of arguments :func:`get_args` can only be called once, but :func:`get_additional_args` can be called continuously after the first call of :func:`get_args` .. rubric:: Example Must be called after :func:`get_args` with all arguments passed in. For example: .. code-block:: bash target) get_args ${@+"${@}"} args1=(${args[@]+"${args[@]}"}) get_additional_args ${@+"${@}"} args2=(${args[@]+"${args[@]}"}) get_additional_args ${@+"${@}"} args3=(${args[@]+"${args[@]}"}) ;; just target 11 22 33 -- aa bb cc -- '!!' '@@' '##' #args1=(11 22 33) #args2=(aa bb cc) #args3=(!! @@ ##) .. function:: safe_load A non-eval method for loading and exporting a text file of keys/values :Arguments: Inputs ``$1`` - Filename of file to be loaded :Parameters: [:envvar:`JUST_SAFE_LOAD_DELIMITER`] - By default, = is used between key and value. If = is needed, then this environment variable can override the delimiter. :Output: All keys are set as environment variables and exported .. rubric:: Example .. code-block:: bash key1=value_one key2=this is another value .. note:: Does not support comments, blank lines, or anything else other than key=value .. rubric:: Bugs As with anything in a language like bash, it's not guaranteed that an arbitrary command won't be executed with this, but as far as is known, there are no known issues with this function. .. function:: just_error Helper function to be combined with calls to print a useful error message on error. Errors captured by :func:`just_functions.bsh just_error` will not print out an error stack. :Arguments: Inputs ``$1...`` - Message to echo on error :Return Value: * Returns the same return value of the previously run command :Output: Echos message .. rubric:: Example .. code-block:: bash run_some_command || just_error "That command failed, here's some helpful advise" .. file:: .justplugins Plugin file The filename defaults to .justplugins and sits next to your Justfile, but can be overridden by exporting the environment variable :envvar:`JUST_PLUGIN_FILE`. Lists the various plugin files. Blank lines and # comment lines are allowed