Skip to content

add profile subcommand #1865

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Jan 3, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -17,3 +17,8 @@ bats
enabled/*
/enabled
tmp/

# Do not save profiles
profiles/*
# apart from the default one
!profiles/default.bash_it
41 changes: 40 additions & 1 deletion completion/available/bash-it.completion.bash
Original file line number Diff line number Diff line change
@@ -57,6 +57,18 @@ _bash-it-comp-list-available()
COMPREPLY=( $(compgen -W "${enabled_things}" -- ${cur}) )
}

_bash-it-comp-list-profiles()
{
local profiles

profiles=$(for f in `compgen -G "${BASH_IT}/profiles/*.bash_it" | sort -d`;
do
basename $f | sed -e 's/.bash_it//g'
done)

COMPREPLY=( $(compgen -W "${profiles}" -- ${cur}) )
}

_bash-it-comp()
{
local cur prev opts
@@ -65,7 +77,7 @@ _bash-it-comp()
prev="${COMP_WORDS[COMP_CWORD-1]}"
chose_opt="${COMP_WORDS[1]}"
file_type="${COMP_WORDS[2]}"
opts="disable enable help migrate reload restart doctor search show update version"
opts="disable enable help migrate reload restart profile doctor search show update version"
case "${chose_opt}" in
show)
local show_args="aliases completions plugins"
@@ -82,6 +94,33 @@ _bash-it-comp()
return 0
fi
;;
profile)
case "${file_type}" in
load)
if [[ "load" == "$prev" ]]; then
_bash-it-comp-list-profiles
fi
return 0
;;
rm)
if [[ "rm" == "$prev" ]]; then
_bash-it-comp-list-profiles
fi
return 0
;;
save)
return 0
;;
list)
return 0
;;
*)
local profile_args="load save list rm"
COMPREPLY=( $(compgen -W "${profile_args}" -- ${cur}) )
return 0
;;
esac
;;
doctor)
local doctor_args="errors warnings all"
COMPREPLY=( $(compgen -W "${doctor_args}" -- ${cur}) )
1 change: 1 addition & 0 deletions docs/commands/index.rst
Original file line number Diff line number Diff line change
@@ -13,3 +13,4 @@ You should be familiar with them in order to fully utilize Bash-it.
search
reload
doctor
profile
31 changes: 31 additions & 0 deletions docs/commands/profile.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.. _profile:

Bash-it Profile
---------------

Have you ever wanted to port your *Bash-it* configuration into another machine?

If you did, then ``bash-it profile`` is for you!

This command can save and load custom *"profile"* files, that can be later
used to load and recreate your configuration, in any machine you would like |:smile:|

When porting your configuration into a new machine, you just need to save your current profile, copy the resulting *"profile"* file, and load it in the other machine.

Example
^^^^^^^

.. code-block:: bash
# Saves your current profile
bash-it profile save my_profile
# Load the default profile, which is the one used in the default installation.
bash-it profile load default
# Do whatever you want:
# Disable stuff
bash-it disable ...
# Enable stuff
bash-it enable ...
# If you want to get back into your original configuration, you can do it easily
bash-it profile load my_profile
9 changes: 3 additions & 6 deletions install.sh
Original file line number Diff line number Diff line change
@@ -208,6 +208,8 @@ export BASH_IT_AUTOMATIC_RELOAD_AFTER_CONFIG_CHANGE=''
source "${BASH_IT}"/vendor/github.com/erichs/composure/composure.sh
# shellcheck source=./lib/utilities.bash
source "$BASH_IT/lib/utilities.bash"
# shellcheck source=./lib/log.bash
source "${BASH_IT}/lib/log.bash"
cite _about _param _example _group _author _version
# shellcheck source=./lib/helpers.bash
source "$BASH_IT/lib/helpers.bash"
@@ -219,12 +221,7 @@ if [[ -n $interactive && -z "${silent}" ]]; then
done
else
echo ""
echo -e "\033[0;32mEnabling reasonable defaults\033[0m"
_enable-completion bash-it
_enable-completion system
_enable-plugin base
_enable-plugin alias-completion
_enable-alias general
_bash-it-profile-load "default"
fi

echo ""
241 changes: 229 additions & 12 deletions lib/helpers.bash
Original file line number Diff line number Diff line change
@@ -108,6 +108,7 @@ bash-it ()
example '$ bash-it version'
example '$ bash-it reload'
example '$ bash-it restart'
example '$ bash-it profile list|save|load|rm [profile_name]'
example '$ bash-it doctor errors|warnings|all'
typeset verb=${1:-}
shift
@@ -126,6 +127,8 @@ bash-it ()
func=_help-$component;;
doctor)
func=_bash-it-doctor-$component;;
profile)
func=_bash-it-profile-$component;;
search)
_bash-it-search $component "$@"
return;;
@@ -457,6 +460,172 @@ _bash-it-doctor-() {
_bash-it-doctor-all
}

_bash-it-profile-save() {
_about 'saves the current configuration to the "profile" directory'
_group 'lib'

local name=$1
while [ -z "$1" ]; do
read -r -e -p "Please enter the name of the profile to save: " name
case $name in
"")
echo -e "\033[91mPlease choose a name.\033[m"
;;
*)
break
;;
esac
done

local profile_path="${BASH_IT}/profiles/${name}.bash_it"
if [ -f "$profile_path" ]; then
echo -e "\033[0;33mProfile \"$name\" already exists.\033[m"
while true; do
read -r -e -n 1 -p "Would you like to overwrite existing profile? [y/N] " RESP
case $RESP in
[yY])
echo -e "\033[0;32mOverwriting profile \"$name\"...\033[m"
rm "$profile_path"
break
;;
[nN] | "")
echo -e "\033[91mAborting profile save...\033[m"
return 1
;;
*)
echo -e "\033[91mPlease choose y or n.\033[m"
;;
esac
done
fi

local something_exists
echo "# This file is auto generated by Bash-it. Do not edit manually!" > "$profile_path"
for subdirectory in "plugins" "completion" "aliases"; do
local component_exists=""
echo "Saving $subdirectory configuration..."
for f in "${BASH_IT}/$subdirectory/available/"*.bash; do
_bash-it-determine-component-status-from-path "$f"
if [ "$enabled" == "x" ]; then
if [ -z "$component_exists" ]; then
# This is the first component of this type, print the header
component_exists="yes"
something_exists="yes"
echo "" >> "$profile_path"
echo "# $subdirectory" >> "$profile_path"
fi
echo "$subdirectory $enabled_file_clean" >> "$profile_path"
fi
done
done
if [ -z "$something_exists" ]; then
echo "It seems like no configuration was enabled.."
echo "Make sure to double check that this is the wanted behavior."
fi

echo "All done!"
echo ""
echo "Profile location: $profile_path"
echo "Load the profile by invoking \"bash-it profile load $name\""
}

_bash-it-profile-load-parse-profile() {
_about 'Internal function used to parse the profile file'
_param '1: path to the profile file'
_param '2: dry run- only check integrity of the profile file'
_example '$ _bash-it-profile-load-parse-profile "profile.bash_it" "dry"'

local num=0
while read -r -a line; do
num=$((num + 1))
# Ignore comments and empty lines
[[ -z "${line[*]}" || "${line[*]}" =~ ^#.* ]] && continue
local enable_func="_enable-${line[0]}"
local subdirectory=${line[0]}
local component=${line[1]}

typeset to_enable=$(command ls "${BASH_IT}/$subdirectory/available/$component".*bash 2>/dev/null | head -1)
# Ignore botched lines
if [[ -z "$to_enable" ]]; then
echo -e "\033[91mBad line(#$num) in profile, aborting load...\033[m"
local bad="bad line"
break
fi
# Do not actually modify config on dry run
[[ -z $2 ]] || continue
# Actually enable the component
$enable_func "$component"
done < "$1"

# Make sure to propagate the error
[[ -z $bad ]]
}

_bash-it-profile-list() {
about 'lists all profiles from the "profiles" directory'
_group 'lib'

echo "Available profiles:"
for profile in "${BASH_IT}/profiles"/*.bash_it; do
profile="${profile##*/}"
echo "${profile/.bash_it/}"
done
}

_bash-it-profile-rm() {
about 'Removes a profile from the "profiles" directory'
_group 'lib'
local name="$1"
if [[ -z $name ]]; then
echo -e "\033[91mPlease specify profile name to remove...\033[m"
return 1
fi

# Users should not be allowed to delete the default profile
if [[ $name == "default" ]]; then
echo -e "\033[91mCan not remove the default profile...\033[m"
return 1
fi

local profile_path="${BASH_IT}/profiles/$name.bash_it"
if [[ ! -f "$profile_path" ]]; then
echo -e "\033[91mCould not find profile \"$name\"...\033[m"
return 1
fi

command rm "$profile_path"
echo "Removed profile \"$name\" successfully!"
}

_bash-it-profile-load() {
_about 'loads a configuration from the "profiles" directory'
_group 'lib'

local name="$1"
if [[ -z $name ]]; then
echo -e "\033[91mPlease specify profile name to load, not changing configuration...\033[m"
return 1
fi

local profile_path="${BASH_IT}/profiles/$name.bash_it"
if [[ ! -f "$profile_path" ]]; then
echo -e "\033[91mCould not find profile \"$name\", not changing configuration...\033[m"
return 1
fi

echo "Trying to parse profile \"$name\"..."
if _bash-it-profile-load-parse-profile "$profile_path" "dry"; then
echo "Profile \"$name\" parsed successfully!"
echo "Disabling current configuration..."
_disable-all
echo ""
echo "Enabling configuration based on profile..."
_bash-it-profile-load-parse-profile "$profile_path"
echo ""
echo "Profile \"$name\" enabled!"
fi
}

_bash-it-restart() {
_about 'restarts the shell in order to fully reload it'
_group 'lib'
@@ -492,6 +661,25 @@ _bash-it-reload() {
popd &> /dev/null || return
}

_bash-it-determine-component-status-from-path ()
{
_about 'internal function used to process component name and check if its enabled'
_param '1: full path to available component file'
_example '$ _bash-it-determine-component-status-from-path "${BASH_IT}/plugins/available/git.plugin.bash'

# Check for both the old format without the load priority, and the extended format with the priority
declare enabled_files enabled_file
enabled_file="${f##*/}"
enabled_file_clean=$(echo "$enabled_file" | sed -e 's/\(.*\)\..*\.bash/\1/g')
enabled_files=$(sort <(compgen -G "${BASH_IT}/enabled/*$BASH_IT_LOAD_PRIORITY_SEPARATOR${enabled_file}") <(compgen -G "${BASH_IT}/$subdirectory/enabled/${enabled_file}") <(compgen -G "${BASH_IT}/$subdirectory/enabled/*$BASH_IT_LOAD_PRIORITY_SEPARATOR${enabled_file}") | wc -l)

if [ "$enabled_files" -gt 0 ]; then
enabled='x'
else
enabled=' '
fi
}

_bash-it-describe ()
{
_about 'summarizes available bash_it components'
@@ -511,17 +699,8 @@ _bash-it-describe ()
printf "%-20s%-10s%s\n" "$column_header" 'Enabled?' 'Description'
for f in "${BASH_IT}/$subdirectory/available/"*.bash
do
# Check for both the old format without the load priority, and the extended format with the priority
declare enabled_files enabled_file
enabled_file="${f##*/}"
enabled_files=$(sort <(compgen -G "${BASH_IT}/enabled/*$BASH_IT_LOAD_PRIORITY_SEPARATOR${enabled_file}") <(compgen -G "${BASH_IT}/$subdirectory/enabled/${enabled_file}") <(compgen -G "${BASH_IT}/$subdirectory/enabled/*$BASH_IT_LOAD_PRIORITY_SEPARATOR${enabled_file}") | wc -l)

if [ $enabled_files -gt 0 ]; then
enabled='x'
else
enabled=' '
fi
printf "%-20s%-10s%s\n" "$(basename $f | sed -e 's/\(.*\)\..*\.bash/\1/g')" " [$enabled]" "$(cat $f | metafor about-$file_type)"
_bash-it-determine-component-status-from-path "$f"
printf "%-20s%-10s%s\n" "$enabled_file_clean" " [$enabled]" "$(cat $f | metafor about-$file_type)"
done
printf '\n%s\n' "to enable $preposition $file_type, do:"
printf '%s\n' "$ bash-it enable $file_type <$file_type name> [$file_type name]... -or- $ bash-it enable $file_type all"
@@ -540,6 +719,17 @@ _on-disable-callback()
_command_exists $callback && $callback
}

_disable-all ()
{
_about 'disables all bash_it components'
_example '$ _disable-all'
_group 'lib'

_disable-plugin "all"
_disable-alias "all"
_disable-completion "all"
}

_disable-plugin ()
{
_about 'disables bash_it plugin'
@@ -623,7 +813,11 @@ _disable-thing ()

_bash-it-clean-component-cache "${file_type}"

printf '%s\n' "$file_entity disabled."
if [ "$file_entity" = "all" ]; then
printf '%s\n' "$file_entity $(_bash-it-pluralize-component "$file_type") disabled."
else
printf '%s\n' "$file_entity disabled."
fi
}

_enable-plugin ()
@@ -636,6 +830,12 @@ _enable-plugin ()
_enable-thing "plugins" "plugin" $1 $BASH_IT_LOAD_PRIORITY_DEFAULT_PLUGIN
}

_enable-plugins ()
{
_about 'alias of _enable-plugin'
_enable-plugin "$@"
}

_enable-alias ()
{
_about 'enables bash_it alias'
@@ -646,6 +846,12 @@ _enable-alias ()
_enable-thing "aliases" "alias" $1 $BASH_IT_LOAD_PRIORITY_DEFAULT_ALIAS
}

_enable-aliases ()
{
_about 'alias of _enable-alias'
_enable-alias "$@"
}

_enable-completion ()
{
_about 'enables bash_it completion'
@@ -802,6 +1008,17 @@ _help-plugins()
rm $grouplist 2> /dev/null
}

_help-profile () {
_about 'help message for profile command'
_group 'lib'

echo "Manages profiles of bash it."
echo "Use 'bash-it profile list' to see all available profiles."
echo "Use 'bash-it profile save foo' to save the current configuration into a profile named 'foo'."
echo "Use 'bash-it profile load foo' to load an existing profile named 'foo'."
echo "Use 'bash-it profile rm foo' to remove an existing profile named 'foo'."
}

_help-update () {
_about 'help message for update command'
_group 'lib'
12 changes: 12 additions & 0 deletions profiles/default.bash_it
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# This is the default profile of Bash-it

# plugins
plugins alias-completion
plugins base

# completion
completion bash-it
completion system

# aliases
aliases general
22 changes: 16 additions & 6 deletions test/completion/bash-it.completion.bats
Original file line number Diff line number Diff line change
@@ -80,32 +80,42 @@ function __check_completion () {

@test "completion bash-it: show options" {
run __check_completion 'bash-it '
assert_line -n 0 "disable enable help migrate reload restart doctor search show update version"
assert_line -n 0 "disable enable help migrate reload restart profile doctor search show update version"
}

@test "completion bash-it: bash-ti - show options" {
run __check_completion 'bash-ti '
assert_line -n 0 "disable enable help migrate reload restart doctor search show update version"
assert_line -n 0 "disable enable help migrate reload restart profile doctor search show update version"
}

@test "completion bash-it: shit - show options" {
run __check_completion 'shit '
assert_line -n 0 "disable enable help migrate reload restart doctor search show update version"
assert_line -n 0 "disable enable help migrate reload restart profile doctor search show update version"
}

@test "completion bash-it: bashit - show options" {
run __check_completion 'bashit '
assert_line -n 0 "disable enable help migrate reload restart doctor search show update version"
assert_line -n 0 "disable enable help migrate reload restart profile doctor search show update version"
}

@test "completion bash-it: batshit - show options" {
run __check_completion 'batshit '
assert_line -n 0 "disable enable help migrate reload restart doctor search show update version"
assert_line -n 0 "disable enable help migrate reload restart profile doctor search show update version"
}

@test "completion bash-it: bash_it - show options" {
run __check_completion 'bash_it '
assert_line -n 0 "disable enable help migrate reload restart doctor search show update version"
assert_line -n 0 "disable enable help migrate reload restart profile doctor search show update version"
}

@test "completion bash-it: profile - show options" {
run __check_completion 'bash-it profile '
assert_line -n 0 "load save list rm"
}

@test "completion bash-it: profile load - show options" {
run __check_completion 'bash-it profile load '
assert_line -n 0 "default"
}

@test "completion bash-it: show - show options" {
12 changes: 12 additions & 0 deletions test/fixtures/bash_it/profiles/test-bad-component.bash_it
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# plugins
plugins alias-completion
plugins base

# completion
completion bash-it
completion system

# aliases
aliases general
# Bad component
aliases bla
12 changes: 12 additions & 0 deletions test/fixtures/bash_it/profiles/test-bad-type.bash_it
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# plugins
plugins alias-completion
plugins base
# Bad type
pluugins alias-completion

# completion
completion bash-it
completion system

# aliases
aliases general
156 changes: 156 additions & 0 deletions test/lib/helpers.bats
Original file line number Diff line number Diff line change
@@ -13,11 +13,24 @@ load ../../plugins/available/base.plugin

function local_setup {
setup_test_fixture

# Copy the test fixture to the Bash-it folder
if command -v rsync &> /dev/null; then
rsync -a "$BASH_IT/test/fixtures/bash_it/" "$BASH_IT/"
else
find "$BASH_IT/test/fixtures/bash_it" \
-mindepth 1 -maxdepth 1 \
-exec cp -r {} "$BASH_IT/" \;
fi
}

# TODO Create global __is_enabled function
# TODO Create global __get_base_name function
# TODO Create global __get_enabled_name function
@test "bash-it: verify that the test fixture is available" {
assert_file_exist "$BASH_IT/profiles/test-bad-component.bash_it"
assert_file_exist "$BASH_IT/profiles/test-bad-type.bash_it"
}

@test "helpers: _command_exists function exists" {
run type -a _command_exists &> /dev/null
@@ -283,6 +296,149 @@ function local_setup {
assert_link_exist "$BASH_IT/enabled/225---nvm.plugin.bash"
}

@test "helper: profile load command sanity" {
run _bash-it-profile-load "default"

assert_link_exist "$BASH_IT/enabled/150---general.aliases.bash"
assert_link_exist "$BASH_IT/enabled/250---base.plugin.bash"
assert_link_exist "$BASH_IT/enabled/365---alias-completion.plugin.bash"
assert_link_exist "$BASH_IT/enabled/350---bash-it.completion.bash"
assert_link_exist "$BASH_IT/enabled/350---system.completion.bash"
}

@test "helper: profile save command sanity" {
run _enable-plugin "nvm"

run _bash-it-profile-save "test"
assert_line -n 0 "Saving plugins configuration..."
assert_line -n 1 "Saving completion configuration..."
assert_line -n 2 "Saving aliases configuration..."
assert_line -n 3 "All done!"
assert_file_exist "$BASH_IT/profiles/test.bash_it"
}

@test "helper: profile save creates valid file with only plugin enabled" {
run _enable-plugin "nvm"

run _bash-it-profile-save "test"
run cat "$BASH_IT/profiles/test.bash_it"
assert_line -n 0 "# This file is auto generated by Bash-it. Do not edit manually!"
assert_line -n 1 "# plugins"
assert_line -n 2 "plugins nvm"
}

@test "helper: profile save creates valid file with only completion enabled" {
run _enable-completion "bash-it"

run _bash-it-profile-save "test"
run cat "$BASH_IT/profiles/test.bash_it"
assert_line -n 0 "# This file is auto generated by Bash-it. Do not edit manually!"
assert_line -n 1 "# completion"
assert_line -n 2 "completion bash-it"
}

@test "helper: profile save creates valid file with only aliases enabled" {
run _enable-alias "general"

run _bash-it-profile-save "test"
run cat "$BASH_IT/profiles/test.bash_it"
assert_line -n 0 "# This file is auto generated by Bash-it. Do not edit manually!"
assert_line -n 1 "# aliases"
assert_line -n 2 "aliases general"
}

@test "helper: profile edge case, empty configuration" {
run _bash-it-profile-save "test"
assert_line -n 3 "It seems like no configuration was enabled.."
assert_line -n 4 "Make sure to double check that this is the wanted behavior."

run _enable-alias "general"
run _enable-plugin "base"
run _enable-plugin "alias-completion"
run _enable-completion "bash-it"
run _enable-completion "system"

run _bash-it-profile-load "test"
assert_link_not_exist "$BASH_IT/enabled/150---general.aliases.bash"
assert_link_not_exist "$BASH_IT/enabled/250---base.plugin.bash"
assert_link_not_exist "$BASH_IT/enabled/365---alias-completion.plugin.bash"
assert_link_not_exist "$BASH_IT/enabled/350---bash-it.completion.bash"
assert_link_not_exist "$BASH_IT/enabled/350---system.completion.bash"
}

@test "helper: profile save and load" {
run _enable-alias "general"
run _enable-plugin "base"
run _enable-plugin "alias-completion"
run _enable-completion "bash-it"
run _enable-completion "system"

run _bash-it-profile-save "test"
assert_success

run _disable-alias "general"
assert_link_not_exist "$BASH_IT/enabled/150---general.aliases.bash"
run _bash-it-profile-load "test"
assert_link_exist "$BASH_IT/enabled/150---general.aliases.bash"
}

@test "helper: profile load corrupted profile file: bad component" {
run _bash-it-profile-load "test-bad-component"
assert_line -n 1 -p "Bad line(#12) in profile, aborting load..."
}

@test "helper: profile load corrupted profile file: bad subdirectory" {
run _bash-it-profile-load "test-bad-type"
assert_line -n 1 -p "Bad line(#5) in profile, aborting load..."
}

@test "helper: profile rm sanity" {
run _bash-it-profile-save "test"
assert_file_exist "$BASH_IT/profiles/test.bash_it"
run _bash-it-profile-rm "test"
assert_line -n 0 "Removed profile \"test\" successfully!"
assert_file_not_exist "$BASH_IT/profiles/test.bash_it"
}

@test "helper: profile rm no params" {
run _bash-it-profile-rm ""
assert_line -n 0 -p "Please specify profile name to remove..."
}

@test "helper: profile load no params" {
run _bash-it-profile-load ""
assert_line -n 0 -p "Please specify profile name to load, not changing configuration..."
}

@test "helper: profile rm default" {
run _bash-it-profile-rm "default"
assert_line -n 0 -p "Can not remove the default profile..."
assert_file_exist "$BASH_IT/profiles/default.bash_it"
}

@test "helper: profile rm bad profile name" {
run _bash-it-profile-rm "notexisting"
assert_line -n 0 -p "Could not find profile \"notexisting\"..."
}

@test "helper: profile list sanity" {
run _bash-it-profile-list
assert_line -n 0 "Available profiles:"
assert_line -n 1 "default"
}

@test "helper: profile list more profiles" {
run _bash-it-profile-save "cactus"
run _bash-it-profile-save "another"
run _bash-it-profile-save "brother"
run _bash-it-profile-list
assert_line -n 0 "Available profiles:"
assert_line -n 4 "default"
assert_line -n 3 "cactus"
assert_line -n 1 "another"
assert_line -n 2 "brother"
}

@test "helpers: migrate plugins and completions that share the same name" {
ln -s $BASH_IT/completion/available/dirs.completion.bash $BASH_IT/completion/enabled/350---dirs.completion.bash
assert_link_exist "$BASH_IT/completion/enabled/350---dirs.completion.bash"