Skip to content

Commit

Permalink
feat: changes to obfus - ignore vars; fix import within argsh env; an…
Browse files Browse the repository at this point in the history
…d tests (#18)

- feature obfus: to ignore vars as flag
- fix import: was ignoring `ARGSH_SOURCE`
- tests: added minify test
- fix args: didn't handle rest positional params correctly (array)
- moved test fixtures to tests itself
  • Loading branch information
fentas authored Apr 5, 2024
1 parent ee1e715 commit c75d182
Show file tree
Hide file tree
Showing 35 changed files with 142 additions and 46 deletions.
10 changes: 6 additions & 4 deletions .bin/argsh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# shellcheck disable=SC1091 disable=SC2034 disable=SC2046
# shellcheck disable=SC1091 disable=SC2034 disable=SC2046 disable=SC2120
set -euo pipefail

: "${PATH_BASE:="$(git rev-parse --show-toplevel)"}"
Expand Down Expand Up @@ -59,7 +59,7 @@ minify::argsh() {

coverage::argsh() {
:args "Generate coverage report for argsh" "${@}"
argsh::main coverage libraries coverage --min "${MIN_COVERAGE}"
argsh::main coverage libraries -o coverage --min "${MIN_COVERAGE}"
}

test::argsh() {
Expand All @@ -72,7 +72,7 @@ test::argsh() {
BATS_LOAD="argsh.min.sh"
export BATS_LOAD
}
argsh::main test libraries
argsh::main test libraries .docker/test
}

lint::argsh() {
Expand All @@ -89,7 +89,7 @@ lint::argsh() {
}

argsh::docker() {
local tag="${1:-latest}"
local tag="latest"
local -a args=(
'tag|t' "Docker image tag"
)
Expand All @@ -102,6 +102,8 @@ argsh::docker() {
###
argsh::main() {
local tty=""
argsh::docker &>/dev/null

[[ ! -t 1 ]] || tty="-it"
# shellcheck disable=SC2046
docker run --rm ${tty} $(docker::user) -w /workspace \
Expand Down
34 changes: 29 additions & 5 deletions .bin/obfus
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ sub print_usage() {
say "\t\t-C\tis an option to clean out full line comments and blank lines.";
say "\t\t-F\tis an option to flatten out the code (remove indentations)";
say "\t\t-A\tis an option to aggressive obfuscation (implies using -F and -C)(tries to put more content on same line when possible)";
say "\t\t-I\tis an option to ignore specific variables in the obfuscation process, separated by commas (default: usage,args)";
exit 0;
}

Expand All @@ -33,6 +34,7 @@ sub parse_cmd_args {
my $flatten="";
my $new_variable_prefix="a";
my $aggressive="";
my $ignore_vars="usage,args";
for my $argnum (0 .. $#ARGV) {
if ($ARGV[$argnum] eq "-i") {
$input_file=$ARGV[$argnum+1];
Expand All @@ -53,17 +55,21 @@ sub parse_cmd_args {
$aggressive=1;
$flatten=1;
$delete_blanks=1;
} elsif ($ARGV[$argnum] eq "-I") {
$ignore_vars=$ARGV[$argnum+1];
$argnum++;
}
}
if ($input_file eq "" || $output_file eq "") {
say "Input or output file not specified!!";
&print_usage();
}
return ($input_file,$output_file,$new_variable_prefix,$delete_blanks,$flatten,$aggressive);
return ($input_file,$output_file,$new_variable_prefix,$delete_blanks,$flatten,$aggressive,$ignore_vars);
}

sub parse_vars_from_file {
my $file_name=shift;
my $ignore_vars=shift;
open(my $file_handle, "<", $file_name) || die "Couldn't open '".$file_name."' for reading because: ".$!;
my %vars=();
my $skip_next_line=0;
Expand Down Expand Up @@ -124,6 +130,12 @@ sub parse_vars_from_file {
}
}
}

# go through $ignore_vars split by ,
for my $name (split /,/, $ignore_vars) {
delete $vars{$name};
}

close $file_handle;
return keys %vars;
}
Expand Down Expand Up @@ -391,8 +403,20 @@ sub newline_process {
close $handle;
}

my ($input_file,$output_file,$new_variable_prefix,$delete_blanks,$flatten,$aggressive)=&parse_cmd_args();
my @parsed_vars=&parse_vars_from_file($input_file);
my (
$input_file,
$output_file,
$new_variable_prefix,
$delete_blanks,
$flatten,
$aggressive,
$ignore_vars
) = &parse_cmd_args();

my @parsed_vars = &parse_vars_from_file($input_file, $ignore_vars);
my @sorted_vars = sort { length($b) <=> length($a) } @parsed_vars;
&obfuscate($input_file,$output_file,$new_variable_prefix,$delete_blanks,$flatten,$aggressive,@sorted_vars);
&newlines($output_file);

&obfuscate($input_file, $output_file, $new_variable_prefix, $delete_blanks, $flatten, $aggressive, @sorted_vars);
if ($aggressive) {
&newlines($output_file);
}
30 changes: 17 additions & 13 deletions .docker/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ export ARGSH_SOURCE
argsh::minify() {
local template out="/dev/stdout"
# shellcheck disable=SC2034
local -a files args=(
'files' "Files to minify, can be a glob pattern"
'template|t:~file' "Path to a template file to use for the minified file"
local -a files ignore_variable args=(
'files' "Files to minify, can be a glob pattern"
'template|t:~file' "Path to a template file to use for the minified file"
'out|o' "Path to the output file"
'ignore-variable|i' "Ignores specific variable names from obfuscation"
)
:args "Minify Bash files" "${@}"
! is::uninitialized files || {
Expand Down Expand Up @@ -42,8 +43,12 @@ argsh::minify() {
} >>"${content}"
done
done

obfus -i "${content}" -o "${tout}" -A
iVars=""
if (( ${#ignore_variable[@]} )); then
iVars="-I $(array::join "," "${ignore_variable[@]}")"
fi
# shellcheck disable=SC2086
obfus -i "${content}" -o "${tout}" -A ${iVars}
local -r data="$(cat "${tout}")"
if [[ -z "${template:-}" ]]; then
echo -n "${data}" >"${out}"
Expand Down Expand Up @@ -87,36 +92,35 @@ argsh::lint() {
}

argsh::test() {
local tests="."
# shellcheck disable=SC2034
local -a args=(
local -a tests=(".") args=(
'tests' "Path to the bats test files"
)
:args "Run tests" "${@}"
[[ -z "${BATS_LOAD:-}" ]] || {
echo "Running tests for ${BATS_LOAD}" >&2
}
bats "${tests}"
bats "${tests[@]}"
}

argsh::coverage() {
local out tests="." min=75
local out="./coverage" min=75
# shellcheck disable=SC2034
local -a args=(
local -a tests=(".") args=(
'tests' "Path to the bats test files"
'out' "Path to the output directory"
'out|o' "Path to the output directory"
'min|:~int' "Minimum coverage required"
)
:args "Generate coverage report for your Bash scripts" "${@}"

echo "Generating coverage report" >&2
echo "Generating coverage report for: ${tests[*]}" >&2
kcov \
--clean \
--bash-dont-parse-binary-dir \
--include-pattern=.sh \
--exclude-pattern=tests \
--include-path=. \
"${out}" bats "${tests}" >/dev/null 2>&1 || {
"${out}" bats "${tests[@]}" >/dev/null 2>&1 || {
echo "Failed to generate coverage report"
echo "Run tests with 'argsh test' to see what went wrong"
exit 1
Expand Down
9 changes: 9 additions & 0 deletions .docker/test/fixtures/minify/ignore_vars.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash

main() {
local usage="test"
local args="test"
local obfuscate="test"
echo "${usage} ${args} ${obfuscate}"
}
[[ "${0}" != "${BASH_SOURCE[0]}" && -z "${ARGSH_SOURCE}" ]] || main "${@}"
19 changes: 19 additions & 0 deletions .docker/test/minify.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env bats
# shellcheck shell=bash disable=SC2154
# vim: filetype=bash
# This test file has to be run from the docker container itself
set -euo pipefail

load "/workspace/test/helper"
load_source

@test "ignore variables" {
(
docker-entrypoint.sh minify "${PATH_FIXTURES}/ignore_vars.sh"
) >"${stdout}" 2>"${stderr}" || status="${?}"

is_empty stderr
grep -q 'local usage' "${stdout}"
grep -q 'local args' "${stdout}"
grep -vq obfuscate "${stdout}"
}
20 changes: 10 additions & 10 deletions coverage/coverage.json
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
{
"files": [
{"file": "/workspace/test/fixtures/args/attrs.sh", "percent_covered": "52.50", "covered_lines": "21", "total_lines": "40"},
{"file": "/workspace/libraries/fixtures/import/print_out.sh", "percent_covered": "100.00", "covered_lines": "2", "total_lines": "2"},
{"file": "/workspace/libraries/fixtures/args/usage.sh", "percent_covered": "67.57", "covered_lines": "25", "total_lines": "37"},
{"file": "/workspace/libraries/fixtures/args/attrs.sh", "percent_covered": "52.50", "covered_lines": "21", "total_lines": "40"},
{"file": "/workspace/libraries/array.sh", "percent_covered": "35.71", "covered_lines": "5", "total_lines": "14"},
{"file": "/workspace/test/fixtures/import/print_out.sh", "percent_covered": "100.00", "covered_lines": "2", "total_lines": "2"},
{"file": "/workspace/libraries/error.sh", "percent_covered": "27.78", "covered_lines": "5", "total_lines": "18"},
{"file": "/workspace/test/fixtures/args/fmt.sh", "percent_covered": "33.33", "covered_lines": "13", "total_lines": "39"},
{"file": "/workspace/libraries/to.sh", "percent_covered": "41.67", "covered_lines": "10", "total_lines": "24"},
{"file": "/workspace/libraries/is.sh", "percent_covered": "75.00", "covered_lines": "6", "total_lines": "8"},
{"file": "/workspace/libraries/fmt.sh", "percent_covered": "62.50", "covered_lines": "5", "total_lines": "8"},
{"file": "/workspace/libraries/import.sh", "percent_covered": "100.00", "covered_lines": "19", "total_lines": "19"},
{"file": "/workspace/libraries/import.sh", "percent_covered": "100.00", "covered_lines": "20", "total_lines": "20"},
{"file": "/workspace/libraries/string.sh", "percent_covered": "100.00", "covered_lines": "41", "total_lines": "41"},
{"file": "/workspace/test/fixtures/args/usage.sh", "percent_covered": "67.57", "covered_lines": "25", "total_lines": "37"},
{"file": "/workspace/libraries/args.sh", "percent_covered": "89.80", "covered_lines": "273", "total_lines": "304"}
{"file": "/workspace/libraries/fixtures/args/fmt.sh", "percent_covered": "33.33", "covered_lines": "13", "total_lines": "39"},
{"file": "/workspace/libraries/args.sh", "percent_covered": "90.03", "covered_lines": "280", "total_lines": "311"}
],
"percent_covered": "76.71",
"covered_lines": 425,
"total_lines": 554,
"percent_covered": "77.05",
"covered_lines": 433,
"total_lines": 562,
"percent_low": 25,
"percent_high": 75,
"command": "bats",
"date": "2024-03-25 12:26:58"
"date": "2024-04-05 10:46:08"
}
19 changes: 16 additions & 3 deletions libraries/args.sh
Original file line number Diff line number Diff line change
Expand Up @@ -159,20 +159,33 @@ import array
exit 0
fi

local field="" i positional_index=1
local first=0 field="" i positional_index=1
local -A match=()
local -a cli=("${@}")

while (( ${#cli[@]} )); do
# positional
if [[ ${cli[0]:0:1} != "-" ]]; then
local name value
i="$(:args::field_positional "${positional_index}")" ||
:args::error_usage "too many arguments: ${cli[0]}"

field="${args[i]}"
name="$(args::field_name "${field}")"
value="$(:args::field_value "${cli[0]}")" || exit "${?}"

# shellcheck disable=SC2155
local -n ref="$(args::field_name "${field}")"
ref="$(:args::field_value "${cli[0]}")" || exit "${?}"
local -n ref="${name}"
if is::array "${name}"; then
(( first )) || {
ref=()
first=1
}
ref+=("${value}")
else
# shellcheck disable=SC2178
ref="${value}"
fi
cli=("${cli[@]:1}")
(( ++positional_index ))
continue
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions libraries/import.bats
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ load_source

@test "can import library" {
(
unset ARGSH_SOURCE
import "string"
string::random
) >"${stdout}" 2>"${stderr}" || status="${?}"
Expand Down
3 changes: 2 additions & 1 deletion libraries/import.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ declare -gA import_cache=()
local _s="${ARGSH_SOURCE:-${BASH_SOURCE[-1]}}"
src="${_s%/*}/${src:1}"
else
src="${BASH_SOURCE[0]%/*}/${src}"
local _s="${ARGSH_SOURCE:-${BASH_SOURCE[0]}}"
src="${_s%/*}/${src}"
fi
import::source "${src}" || exit 1
}
Expand Down
14 changes: 6 additions & 8 deletions test/helper.bash
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,14 @@ load_source() {
else
file="${BATS_TEST_FILENAME/%.bats/.sh}"
fi
PATH_FIXTURES="$(
realpath "$(dirname "${BATS_TEST_FILENAME}")/../test/fixtures/$(basename "${BATS_TEST_FILENAME%.*}")"
)"
PATH_SNAPSHOTS="${PATH_FIXTURES}/snapshots"
: "${PATH_FIXTURES:="$(
realpath "$(dirname "${BATS_TEST_FILENAME}")/fixtures/$(basename "${BATS_TEST_FILENAME%.*}")"
)"}"
: "${PATH_SNAPSHOTS="${PATH_FIXTURES}/snapshots"}"
mkdir -p "${PATH_SNAPSHOTS}"

[[ -f "${file}" ]] || {
echo "■■ Source file ${file} not found"
return 1
}
[[ -f "${file}" ]] ||
return 0

# shellcheck disable=SC1090
source "${file}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ There are different tools for measuring code coverage in Bash scripts. The most
Argsh provides a wrapper for kcov that you can use in your projects. If you [installed argsh](../../getting-started#project-based) as executable, you can use it like this:

```bash
argsh coverage ./scripts ./coverage-output
argsh coverage ./scripts -o ./coverage-output
```

Use `argsh coverage --help` to get more information about the available options.
Expand Down
20 changes: 19 additions & 1 deletion www/apps/docs/content/development/fundamentals/minify.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,22 @@ The template file should contain the parts that you want to add back. You can us
# shellcheck disable=SC2178 disable=SC2120 disable=SC1090 disable=SC2046 disable=SC2155
COMMIT_SHA="${commit_sha}"; VERSION="${version}"
${data}
```
```

### Ignore line

If you need a variable or line unchanged you can write a comment above it.

```bash
# obfus ignore variable
```

### Ignore variables globally

You can ignore specific variable names for obfuscation.

```bash
argsh minify --ignore-variable var_name
# or short
argsh minify -i var_name -i foo -i bar
```
7 changes: 7 additions & 0 deletions www/apps/docs/content/getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ This gives you the ability to use the following options to help you to write nic
- [coverage](./development/fundamentals/coverage) - Generate a coverage report
- [minify](./development/fundamentals/minify) - Minify your script (experimental)

:::note
If you use `argsh` as executer (shebang) you have to include `${ARGSH_SOURCE}` in the call check.
```bash
[[ "${0}" != "${BASH_SOURCE[0]}" && -z "${ARGSH_SOURCE}" ]] || main "${@}"
```
:::

### Globally

You can install argsh globally by running the following command:
Expand Down

0 comments on commit c75d182

Please sign in to comment.