Skip to content
Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Each of these examples demonstrates one aspect or feature of bashly.
- [key-value-pairs](key-value-pairs#readme) - parsing key=value arguments and flags
- [command-examples-on-error](command-examples-on-error#readme) - showing examples on error
- [internal-run](internal-run#readme) - calling other commands internally
- [command-line-manipulation](command-line-manipulation#readme) - read or modify the raw command line

## Customization

Expand Down
1 change: 1 addition & 0 deletions examples/command-line-manipulation/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
download
102 changes: 102 additions & 0 deletions examples/command-line-manipulation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Command Line Manipulation

Demonstrates how to read or override the raw input command line.

Note that this is *not needed nor recommended* under most circumstances - it
is provided as an edge case utility.

This example was generated with:

```bash
$ bashly init --minimal
$ bashly add hooks
# ... now edit src/bashly.yml to match the example ...
# ... now edit src/initialize.sh to match the example ...
# ... now edit src/before.sh to match the example ...
$ bashly generate
```

<!-- include: src/initialize.sh src/before.sh -->

-----

## `bashly.yml`

````yaml
name: download
help: Sample minimal application without commands
version: 0.1.0

args:
- name: source
required: true
help: URL to download from
- name: target
help: "Target filename (default: same as source)"

flags:
- long: --force
short: -f
help: Overwrite existing files
````

## `src/initialize.sh`

````bash
echo "==[ Initialize Called ]=="

# Override the command line completely if the first argument is 'debug'
if [[ "${command_line_args[0]:-""}" = "debug" ]]; then
command_line_args=("modified" "args" "--force")
fi

````

## `src/before.sh`

````bash
echo "==[ Before Hook Called ]=="

echo "Read-only copy of the raw input array: ${input[*]}"
inspect_args

````


## Output

### `$ ./download `

````shell
==[ Initialize Called ]==
missing required argument: SOURCE
usage: download SOURCE [TARGET] [OPTIONS]


````

### `$ ./download debug`

````shell
==[ Initialize Called ]==
==[ Before Hook Called ]==
Read-only copy of the raw input array: modified args --force
args:
- ${args[--force]} = 1
- ${args[source]} = modified
- ${args[target]} = args
# This file is located at 'src/root_command.sh'.
# It contains the implementation for the 'download' command.
# The code you write here will be wrapped by a function named 'download_command()'.
# Feel free to edit this file; your changes will persist when regenerating.
args:
- ${args[--force]} = 1
- ${args[source]} = modified
- ${args[target]} = args
==[ After Hook Called ]==


````



7 changes: 7 additions & 0 deletions examples/command-line-manipulation/src/after.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## after hook
##
## Any code here will be placed inside an `after_hook()` function and called
## after running any command.
##
## You can safely delete this file if you do not need it.
echo "==[ After Hook Called ]=="
15 changes: 15 additions & 0 deletions examples/command-line-manipulation/src/bashly.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: download
help: Sample minimal application without commands
version: 0.1.0

args:
- name: source
required: true
help: URL to download from
- name: target
help: "Target filename (default: same as source)"

flags:
- long: --force
short: -f
help: Overwrite existing files
4 changes: 4 additions & 0 deletions examples/command-line-manipulation/src/before.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
echo "==[ Before Hook Called ]=="

echo "Read-only copy of the raw input array: ${input[*]}"
inspect_args
6 changes: 6 additions & 0 deletions examples/command-line-manipulation/src/initialize.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
echo "==[ Initialize Called ]=="

# Override the command line completely if the first argument is 'debug'
if [[ "${command_line_args[0]:-""}" = "debug" ]]; then
command_line_args=("modified" "args" "--force")
fi
5 changes: 5 additions & 0 deletions examples/command-line-manipulation/src/root_command.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
echo "# This file is located at 'src/root_command.sh'."
echo "# It contains the implementation for the 'download' command."
echo "# The code you write here will be wrapped by a function named 'download_command()'."
echo "# Feel free to edit this file; your changes will persist when regenerating."
inspect_args
10 changes: 10 additions & 0 deletions examples/command-line-manipulation/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env bash

set -x

bashly generate

### Try Me ###

./download
./download debug
10 changes: 0 additions & 10 deletions examples/dependencies-alt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,6 @@ commands:
### `$ ./cli download`

````shell
# This file is located at 'src/download_command.sh'.
# It contains the implementation for the 'cli download' command.
# The code you write here will be wrapped by a function named 'cli_download_command()'.
# Feel free to edit this file; your changes will persist when regenerating.
args: none

deps:
- ${deps[git]} = /usr/bin/git
- ${deps[http_client]} = /usr/bin/curl
- ${deps[ruby]} = /home/vagrant/.rbenv/versions/3.4.1/bin/ruby


````
Expand Down
9 changes: 6 additions & 3 deletions examples/hooks/src/before.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
## before hook
##
## Any code here will be placed inside a `before_hook()` function and called
## before running any command (but after processing its arguments).
## Any code here will be placed inside the `before_hook()` function and called
## before running any command (but after argument processing is complete).
##
## - The processed args are available to you here as `args` and `extra_args`
## - The raw input array is also available in read-only mode as `input`
##
## You can safely delete this file if you do not need it.
echo "==[ Before Hook Called ]=="
inspect_args
inspect_args
4 changes: 4 additions & 0 deletions examples/hooks/src/initialize.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@
## Any code here will be placed inside the `initialize()` function and called
## before running anything else.
##
## The original command line arguments are available in the `command_line_args`
## array. You can modify or override the input before it is processed further,
## though this is usually only needed for advanced use cases.
##
## You can safely delete this file if you do not need it.
2 changes: 1 addition & 1 deletion examples/render-mandoc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ ISSUE TRACKER
AUTHORS
Lana Lang.

Version 0.1.0 July 2025 download(1)
Version 0.1.0 August 2025 download(1)


````
Expand Down
4 changes: 2 additions & 2 deletions examples/stacktrace/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ Examples:

Stack trace:
from ./download:15 in `root_command`
from ./download:260 in `run`
from ./download:266 in `main`
from ./download:259 in `run`
from ./download:267 in `main`


````
Expand Down
9 changes: 6 additions & 3 deletions lib/bashly/libraries/hooks/before.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
## before hook
##
## Any code here will be placed inside a `before_hook()` function and called
## before running any command (but after processing its arguments).
## Any code here will be placed inside the `before_hook()` function and called
## before running any command (but after argument processing is complete).
##
## - The processed args are available to you here as `args` and `extra_args`
## - The raw input array is also available in read-only mode as `input`
##
## You can safely delete this file if you do not need it.
echo "==[ Before Hook Called ]=="
inspect_args
inspect_args
4 changes: 4 additions & 0 deletions lib/bashly/libraries/hooks/initialize.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@
## Any code here will be placed inside the `initialize()` function and called
## before running anything else.
##
## The original command line arguments are available in the `command_line_args`
## array. You can modify or override the input before it is processed further,
## though this is usually only needed for advanced use cases.
##
## You can safely delete this file if you do not need it.
6 changes: 2 additions & 4 deletions lib/bashly/views/command/master_script.gtx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@
>
if Settings.enabled? :sourcing
> if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
> {{ Settings.function_name :initialize }}
> {{ Settings.function_name :run }} "$@"
= render(:start).indent 2
> fi
else
> {{ Settings.function_name :initialize }}
> {{ Settings.function_name :run }} "$@"
= render :start
end
>
5 changes: 5 additions & 0 deletions lib/bashly/views/command/start.gtx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
= view_marker

> command_line_args=("$@")
> {{ Settings.function_name :initialize }}
> {{ Settings.function_name :run }} "${command_line_args[@]}"
2 changes: 1 addition & 1 deletion spec/approvals/cli/preview/no-args
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/usr/bin/env bash
...
run "$@"
fi
26 changes: 26 additions & 0 deletions spec/approvals/examples/command-line-manipulation
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
+ bashly generate
creating user files in src
skipped src/root_command.sh (exists)
created ./download
run ./download --help to test your bash script
+ ./download
==[ Initialize Called ]==
missing required argument: SOURCE
usage: download SOURCE [TARGET] [OPTIONS]
+ ./download debug
==[ Initialize Called ]==
==[ Before Hook Called ]==
Read-only copy of the raw input array: modified args --force
args:
- ${args[--force]} = 1
- ${args[source]} = modified
- ${args[target]} = args
# This file is located at 'src/root_command.sh'.
# It contains the implementation for the 'download' command.
# The code you write here will be wrapped by a function named 'download_command()'.
# Feel free to edit this file; your changes will persist when regenerating.
args:
- ${args[--force]} = 1
- ${args[source]} = modified
- ${args[target]} = args
==[ After Hook Called ]==
2 changes: 1 addition & 1 deletion spec/approvals/examples/render-mandoc
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,4 @@ ISSUE TRACKER
AUTHORS
Lana Lang.

Version 0.1.0 August 2025 download(1)
<footer>
8 changes: 4 additions & 4 deletions spec/approvals/examples/stacktrace
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ Examples:

+ ./download something
./download: line 15: no_such_command: command not found
./download:15 in `root_command`: no_such_command
./download:<line> in `root_command`: no_such_command

Stack trace:
from ./download:15 in `root_command`
from ./download:259 in `run`
from ./download:265 in `main`
from ./download:<line> in `root_command`
from ./download:<line> in `run`
from ./download:<line> in `main`
4 changes: 3 additions & 1 deletion spec/bashly/commands/preview_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@

it 'prints the generated cli script' do
expect { subject.execute %w[preview] }.to output_approval('cli/preview/no-args')
.except(/env bash\n.*\n\s*run "\$@"\n.*/m, "env bash\n...\nrun \"$@\"")
.except(/env bash\n.*\n\s*fi\n/m, "env bash\n...\nfi\b")
end
end
end


15 changes: 14 additions & 1 deletion spec/bashly/integration/examples_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@
# Allow up to a certain string distance from the approval text in CI
leeway = ENV['CI'] ? 40 : 0

# For certain examples, allow some exceptions (replacements) since they
# are too volatile (e.g. line number changes)
exceptions = {
'examples/stacktrace' => [/download:\d+/, 'download:<line>'],
'examples/render-mandoc' => [/Version 0.1.0.*download\(1\)/, '<footer>'],
}

test_cases.each do |example|
approval_name = example.gsub 'spec/fixtures/workspaces', 'fixtures'

Expand All @@ -41,7 +48,13 @@
# - The "+ ..." shell messages driven by `set -x` have no space
# - The order of our `inspect_args` sometimes differs
# - The result of the `deps` array sometimes differs
expect(output).to match_approval(approval_name).diff(leeway)
# In addition, for some examples we allow exceptions, so the below is
# just a more granular version of:
# expect(output).to match_approval(approval_name).diff(leeway).except(*except)
match_output = match_approval(approval_name).diff(leeway)
except = exceptions[example]
match_output = match_output.except(*except) if except
expect(output).to match_output
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion spec/bashly/script/wrapper_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
lines = subject.code.split "\n"
expect(lines[0..13].join("\n")).to match_approval('script/wrapper/code')
.except(/\d+\.\d+\.\d+(\.rc\d)?/)
expect(lines[-2]).to eq ' run "$@"'
expect(lines[-2]).to eq ' run "${command_line_args[@]}"'
end
end

Expand Down