git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] improve one-shot variable detection with shell function
@ 2024-07-22  6:59 Eric Sunshine
  2024-07-22  6:59 ` [PATCH 1/4] t3430: modernize one-shot "VAR=val shell-func" invocation Eric Sunshine
                   ` (5 more replies)
  0 siblings, 6 replies; 37+ messages in thread
From: Eric Sunshine @ 2024-07-22  6:59 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Rubén Justo, Eric Sunshine

From: Eric Sunshine <sunshine@sunshineco.com>

This series addresses a blind-spot of check-non-portable-shell's
detection of one-shot environment variable assignment with shell
functions. In particular, although it correctly detects:

    VAR=val shell-func

it will miss invocations such as:

    echo X | VAR=val shell-func

References:
https://lore.kernel.org/git/CAPig+cRyj8J7MZEufu34NUzwOL2n=w35nT1Ug7FGRwMC0=Qpwg@mail.gmail.com/
https://lore.kernel.org/git/bc1b9cce-d04d-4a79-8fab-55ec3c8bae30@gmail.com/

Eric Sunshine (4):
  t3430: modernize one-shot "VAR=val shell-func" invocation
  t4034: fix use of one-shot variable assignment with shell function
  check-non-portable-shell: improve `VAR=val shell-func` detection
  check-non-portable-shell: suggest alternative for `VAR=val shell-func`

 t/check-non-portable-shell.pl | 4 ++--
 t/t3430-rebase-merges.sh      | 4 ++--
 t/t4034-diff-words.sh         | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)

-- 
2.45.2


^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH 1/4] t3430: modernize one-shot "VAR=val shell-func" invocation
  2024-07-22  6:59 [PATCH 0/4] improve one-shot variable detection with shell function Eric Sunshine
@ 2024-07-22  6:59 ` Eric Sunshine
  2024-07-22 15:09   ` Phillip Wood
  2024-07-22 18:24   ` Junio C Hamano
  2024-07-22  6:59 ` [PATCH 2/4] t4034: fix use of one-shot variable assignment with shell function Eric Sunshine
                   ` (4 subsequent siblings)
  5 siblings, 2 replies; 37+ messages in thread
From: Eric Sunshine @ 2024-07-22  6:59 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Rubén Justo, Eric Sunshine

From: Eric Sunshine <sunshine@sunshineco.com>

Unlike "VAR=val cmd" one-shot environment variable assignments which
exist only for the invocation of 'cmd', those assigned by "VAR=val
shell-func" exist within the running shell and continue to do so until
the process exits (or are explicitly unset). check-non-portable-shell.pl
warns when it detects such usage since, more often than not, the author
who writes such an invocation is unaware of the undesirable behavior.

A common way to work around the problem is to wrap a subshell around the
variable assignments and function call, thus ensuring that the
assignments are short-lived. However, these days, a more ergonomic
approach is to employ test_env() which is tailor-made for this specific
use-case.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t3430-rebase-merges.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index 36ca126bcd..e851ede4f9 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -392,8 +392,8 @@ test_expect_success 'refuse to merge ancestors of HEAD' '
 
 test_expect_success 'root commits' '
 	git checkout --orphan unrelated &&
-	(GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \
-	 test_commit second-root) &&
+	test_env GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \
+		test_commit second-root &&
 	test_commit third-root &&
 	cat >script-from-scratch <<-\EOF &&
 	pick third-root
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH 2/4] t4034: fix use of one-shot variable assignment with shell function
  2024-07-22  6:59 [PATCH 0/4] improve one-shot variable detection with shell function Eric Sunshine
  2024-07-22  6:59 ` [PATCH 1/4] t3430: modernize one-shot "VAR=val shell-func" invocation Eric Sunshine
@ 2024-07-22  6:59 ` Eric Sunshine
  2024-07-22  6:59 ` [PATCH 3/4] check-non-portable-shell: improve `VAR=val shell-func` detection Eric Sunshine
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 37+ messages in thread
From: Eric Sunshine @ 2024-07-22  6:59 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Rubén Justo, Eric Sunshine

From: Eric Sunshine <sunshine@sunshineco.com>

Unlike "VAR=val cmd" one-shot environment variable assignments which
exist only for the invocation of 'cmd', those assigned by "VAR=val
shell-func" exist within the running shell and continue to do so until
the process exits (or are explicitly unset). In most cases, it is
unlikely that this behavior was intended by the test author, and, even
if those leaked assignments do not impact other tests today, they can
negatively impact tests added later by authors unaware that the variable
assignments are still hanging around. Address this shortcoming by
ensuring that the assignments are short-lived.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t4034-diff-words.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh
index 74586f3813..4dcd7e9925 100755
--- a/t/t4034-diff-words.sh
+++ b/t/t4034-diff-words.sh
@@ -70,7 +70,7 @@ test_language_driver () {
 		word_diff --color-words
 	'
 	test_expect_success "diff driver '$lang' in Islandic" '
-		LANG=is_IS.UTF-8 LANGUAGE=is LC_ALL="$is_IS_locale" \
+		test_env LANG=is_IS.UTF-8 LANGUAGE=is LC_ALL="$is_IS_locale" \
 		word_diff --color-words
 	'
 }
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH 3/4] check-non-portable-shell: improve `VAR=val shell-func` detection
  2024-07-22  6:59 [PATCH 0/4] improve one-shot variable detection with shell function Eric Sunshine
  2024-07-22  6:59 ` [PATCH 1/4] t3430: modernize one-shot "VAR=val shell-func" invocation Eric Sunshine
  2024-07-22  6:59 ` [PATCH 2/4] t4034: fix use of one-shot variable assignment with shell function Eric Sunshine
@ 2024-07-22  6:59 ` Eric Sunshine
  2024-07-22 14:46   ` Rubén Justo
  2024-07-22 17:26   ` Kyle Lippincott
  2024-07-22  6:59 ` [PATCH 4/4] check-non-portable-shell: suggest alternative for `VAR=val shell-func` Eric Sunshine
                   ` (2 subsequent siblings)
  5 siblings, 2 replies; 37+ messages in thread
From: Eric Sunshine @ 2024-07-22  6:59 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Rubén Justo, Eric Sunshine

From: Eric Sunshine <sunshine@sunshineco.com>

Unlike "VAR=val cmd" one-shot environment variable assignments which
exist only for the invocation of 'cmd', those assigned by "VAR=val
shell-func" exist within the running shell and continue to do so until
the process exits. check-non-portable-shell.pl warns when it detects
such usage since, more often than not, the author who writes such an
invocation is unaware of the undesirable behavior.

However, a limitation of the check is that it only detects such
invocations when variable assignment (i.e. `VAR=val`) is the first
thing on the line. Thus, it can easily be fooled by an invocation such
as:

    echo X | VAR=val shell-func

Address this shortcoming by loosening the check so that the variable
assignment can be recognized even when not at the beginning of the line.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/check-non-portable-shell.pl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl
index b2b28c2ced..44b23d6ddd 100755
--- a/t/check-non-portable-shell.pl
+++ b/t/check-non-portable-shell.pl
@@ -49,7 +49,7 @@ sub err {
 	/\bexport\s+[A-Za-z0-9_]*=/ and err '"export FOO=bar" is not portable (use FOO=bar && export FOO)';
 	/\blocal\s+[A-Za-z0-9_]*=\$([A-Za-z0-9_{]|[(][^(])/ and
 		err q(quote "$val" in 'local var=$val');
-	/^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
+	/\b([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and !/test_env.+=/ and exists($func{$4}) and
 		err '"FOO=bar shell_func" assignment extends beyond "shell_func"';
 	$line = '';
 	# this resets our $. for each file
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH 4/4] check-non-portable-shell: suggest alternative for `VAR=val shell-func`
  2024-07-22  6:59 [PATCH 0/4] improve one-shot variable detection with shell function Eric Sunshine
                   ` (2 preceding siblings ...)
  2024-07-22  6:59 ` [PATCH 3/4] check-non-portable-shell: improve `VAR=val shell-func` detection Eric Sunshine
@ 2024-07-22  6:59 ` Eric Sunshine
  2024-07-22 14:47   ` Rubén Justo
  2024-07-22 14:50 ` [PATCH 0/4] improve one-shot variable detection with shell function Rubén Justo
  2024-07-26  8:15 ` [PATCH v2 0/5] " Eric Sunshine
  5 siblings, 1 reply; 37+ messages in thread
From: Eric Sunshine @ 2024-07-22  6:59 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Rubén Justo, Eric Sunshine

From: Eric Sunshine <sunshine@sunshineco.com>

Most problems reported by check-non-portable-shell are accompanied by
advice suggesting how the test author can repair the problem. For
instance:

    error: egrep/fgrep obsolescent (use grep -E/-F)

However, when one-shot variable assignment is detected when calling a
shell function (i.e. `VAR=val shell-func`), the problem is reported, but
no advice is given. The lack of advice is particularly egregious since
neither the problem nor the workaround are likely well-known by
newcomers to the project writing tests for the first time. Address this
shortcoming by recommending the use of test_env() which is tailor made
for this specific use-case.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/check-non-portable-shell.pl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl
index 44b23d6ddd..56db7cc6ed 100755
--- a/t/check-non-portable-shell.pl
+++ b/t/check-non-portable-shell.pl
@@ -50,7 +50,7 @@ sub err {
 	/\blocal\s+[A-Za-z0-9_]*=\$([A-Za-z0-9_{]|[(][^(])/ and
 		err q(quote "$val" in 'local var=$val');
 	/\b([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and !/test_env.+=/ and exists($func{$4}) and
-		err '"FOO=bar shell_func" assignment extends beyond "shell_func"';
+		err '"FOO=bar shell_func" assignment extends beyond "shell_func" (use test_env FOO=bar shell_func)';
 	$line = '';
 	# this resets our $. for each file
 	close ARGV if eof;
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [PATCH 3/4] check-non-portable-shell: improve `VAR=val shell-func` detection
  2024-07-22  6:59 ` [PATCH 3/4] check-non-portable-shell: improve `VAR=val shell-func` detection Eric Sunshine
@ 2024-07-22 14:46   ` Rubén Justo
  2024-07-26  6:45     ` Eric Sunshine
  2024-07-22 17:26   ` Kyle Lippincott
  1 sibling, 1 reply; 37+ messages in thread
From: Rubén Justo @ 2024-07-22 14:46 UTC (permalink / raw)
  To: Eric Sunshine, git; +Cc: Junio C Hamano, Eric Sunshine

On Mon, Jul 22, 2024 at 02:59:13AM -0400, Eric Sunshine wrote:
> From: Eric Sunshine <sunshine@sunshineco.com>
> 
> Unlike "VAR=val cmd" one-shot environment variable assignments which
> exist only for the invocation of 'cmd', those assigned by "VAR=val
> shell-func" exist within the running shell and continue to do so until
> the process exits. check-non-portable-shell.pl warns when it detects
> such usage since, more often than not, the author who writes such an
> invocation is unaware of the undesirable behavior.
> 
> However, a limitation of the check is that it only detects such
> invocations when variable assignment (i.e. `VAR=val`) is the first
> thing on the line. Thus, it can easily be fooled by an invocation such
> as:
> 
>     echo X | VAR=val shell-func
> 
> Address this shortcoming by loosening the check so that the variable
> assignment can be recognized even when not at the beginning of the line.
> 
> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
> ---
>  t/check-non-portable-shell.pl | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl
> index b2b28c2ced..44b23d6ddd 100755
> --- a/t/check-non-portable-shell.pl
> +++ b/t/check-non-portable-shell.pl
> @@ -49,7 +49,7 @@ sub err {
>  	/\bexport\s+[A-Za-z0-9_]*=/ and err '"export FOO=bar" is not portable (use FOO=bar && export FOO)';
>  	/\blocal\s+[A-Za-z0-9_]*=\$([A-Za-z0-9_{]|[(][^(])/ and
>  		err q(quote "$val" in 'local var=$val');
> -	/^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
> +	/\b([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and !/test_env.+=/ and exists($func{$4}) and

Losing "^\s*" means we'll cause false positives, such as:

    # VAR=VAL shell-func
    echo VAR=VAL shell-func

Regardless of that, the regex will continue to pose problems with:

  VAR=$OTHER_VALUE shell-func
  VAR=$(cmd) shell-func
  VAR=VAL\ UE shell-func
  VAR="\"val\" shell-func UE" non-shell-func 

Which, of course, should be cases that should be written in a more
orthodox way. 

But we will start to detect errors like the ones mentioned in the
message, which are more likely to happen.

I think this change is a good step forward, and I'm happy with it as it
is.

Thanks

>  		err '"FOO=bar shell_func" assignment extends beyond "shell_func"';
>  	$line = '';
>  	# this resets our $. for each file
> -- 
> 2.45.2
>  

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH 4/4] check-non-portable-shell: suggest alternative for `VAR=val shell-func`
  2024-07-22  6:59 ` [PATCH 4/4] check-non-portable-shell: suggest alternative for `VAR=val shell-func` Eric Sunshine
@ 2024-07-22 14:47   ` Rubén Justo
  0 siblings, 0 replies; 37+ messages in thread
From: Rubén Justo @ 2024-07-22 14:47 UTC (permalink / raw)
  To: Eric Sunshine, git; +Cc: Junio C Hamano, Eric Sunshine

On 7/22/24 8:59 AM, Eric Sunshine wrote:
> From: Eric Sunshine <sunshine@sunshineco.com>
> 
> Most problems reported by check-non-portable-shell are accompanied by
> advice suggesting how the test author can repair the problem. For
> instance:
> 
>     error: egrep/fgrep obsolescent (use grep -E/-F)
> 
> However, when one-shot variable assignment is detected when calling a
> shell function (i.e. `VAR=val shell-func`), the problem is reported, but
> no advice is given. The lack of advice is particularly egregious since
> neither the problem nor the workaround are likely well-known by
> newcomers to the project writing tests for the first time. Address this
> shortcoming by recommending the use of test_env() which is tailor made
> for this specific use-case.
> 
> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
> ---
>  t/check-non-portable-shell.pl | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl
> index 44b23d6ddd..56db7cc6ed 100755
> --- a/t/check-non-portable-shell.pl
> +++ b/t/check-non-portable-shell.pl
> @@ -50,7 +50,7 @@ sub err {
>  	/\blocal\s+[A-Za-z0-9_]*=\$([A-Za-z0-9_{]|[(][^(])/ and
>  		err q(quote "$val" in 'local var=$val');
>  	/\b([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and !/test_env.+=/ and exists($func{$4}) and
> -		err '"FOO=bar shell_func" assignment extends beyond "shell_func"';
> +		err '"FOO=bar shell_func" assignment extends beyond "shell_func" (use test_env FOO=bar shell_func)';

This is a nice improvement.

>  	$line = '';
>  	# this resets our $. for each file
>  	close ARGV if eof;

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH 0/4] improve one-shot variable detection with shell function
  2024-07-22  6:59 [PATCH 0/4] improve one-shot variable detection with shell function Eric Sunshine
                   ` (3 preceding siblings ...)
  2024-07-22  6:59 ` [PATCH 4/4] check-non-portable-shell: suggest alternative for `VAR=val shell-func` Eric Sunshine
@ 2024-07-22 14:50 ` Rubén Justo
  2024-07-26  8:15 ` [PATCH v2 0/5] " Eric Sunshine
  5 siblings, 0 replies; 37+ messages in thread
From: Rubén Justo @ 2024-07-22 14:50 UTC (permalink / raw)
  To: Eric Sunshine, git; +Cc: Junio C Hamano, Eric Sunshine

On Mon, Jul 22, 2024 at 02:59:10AM -0400, Eric Sunshine wrote:
> From: Eric Sunshine <sunshine@sunshineco.com>
> 
> This series addresses a blind-spot of check-non-portable-shell's
> detection of one-shot environment variable assignment with shell
> functions. In particular, although it correctly detects:
> 
>     VAR=val shell-func
> 
> it will miss invocations such as:
> 
>     echo X | VAR=val shell-func
> 
> References:
> https://lore.kernel.org/git/CAPig+cRyj8J7MZEufu34NUzwOL2n=w35nT1Ug7FGRwMC0=Qpwg@mail.gmail.com/
> https://lore.kernel.org/git/bc1b9cce-d04d-4a79-8fab-55ec3c8bae30@gmail.com/
> 
> Eric Sunshine (4):
>   t3430: modernize one-shot "VAR=val shell-func" invocation
>   t4034: fix use of one-shot variable assignment with shell function
>   check-non-portable-shell: improve `VAR=val shell-func` detection
>   check-non-portable-shell: suggest alternative for `VAR=val shell-func`

All these changes look good to me.

Thanks.

> 
>  t/check-non-portable-shell.pl | 4 ++--
>  t/t3430-rebase-merges.sh      | 4 ++--
>  t/t4034-diff-words.sh         | 2 +-
>  3 files changed, 5 insertions(+), 5 deletions(-)
> 
> -- 
> 2.45.2
> 

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH 1/4] t3430: modernize one-shot "VAR=val shell-func" invocation
  2024-07-22  6:59 ` [PATCH 1/4] t3430: modernize one-shot "VAR=val shell-func" invocation Eric Sunshine
@ 2024-07-22 15:09   ` Phillip Wood
  2024-07-23  9:26     ` Phillip Wood
  2024-07-26  6:15     ` Eric Sunshine
  2024-07-22 18:24   ` Junio C Hamano
  1 sibling, 2 replies; 37+ messages in thread
From: Phillip Wood @ 2024-07-22 15:09 UTC (permalink / raw)
  To: Eric Sunshine, git; +Cc: Junio C Hamano, Rubén Justo, Eric Sunshine

Hi Eric

On 22/07/2024 07:59, Eric Sunshine wrote:
> From: Eric Sunshine <sunshine@sunshineco.com>
> 
> Unlike "VAR=val cmd" one-shot environment variable assignments which
> exist only for the invocation of 'cmd', those assigned by "VAR=val
> shell-func" exist within the running shell and continue to do so until
> the process exits (or are explicitly unset).

I'm not sure I follow. If I run

sh -c 'f() {
     echo "f: HELLO=$HELLO"
     env | grep HELLO
}
HELLO=x f; echo "HELLO=$HELLO"'

Then I see

f: HELLO=x
HELLO=x
HELLO=

which seems to contradict the commit message as $HELLO is unset when the 
function returns. I see the same result if I replace "sh" (which is bash 
on my system) with an explicit "bash", "dash" or "zsh".

I'm also confused as to why this caused a problem for Rubén's test as 
$HELLO is set in the environment so I'm don't understand why git wasn't 
picking up the right pager.

> check-non-portable-shell.pl
> warns when it detects such usage since, more often than not, the author
> who writes such an invocation is unaware of the undesirable behavior.
> 
> A common way to work around the problem is to wrap a subshell around the
> variable assignments and function call, thus ensuring that the
> assignments are short-lived. However, these days, a more ergonomic
> approach is to employ test_env() which is tailor-made for this specific
> use-case.

Oh, that sounds useful, I didn't know it existed.

Best Wishes

Phillip

> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
> ---
>   t/t3430-rebase-merges.sh | 4 ++--
>   1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
> index 36ca126bcd..e851ede4f9 100755
> --- a/t/t3430-rebase-merges.sh
> +++ b/t/t3430-rebase-merges.sh
> @@ -392,8 +392,8 @@ test_expect_success 'refuse to merge ancestors of HEAD' '
>   
>   test_expect_success 'root commits' '
>   	git checkout --orphan unrelated &&
> -	(GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \
> -	 test_commit second-root) &&
> +	test_env GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \
> +		test_commit second-root &&
>   	test_commit third-root &&
>   	cat >script-from-scratch <<-\EOF &&
>   	pick third-root

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH 3/4] check-non-portable-shell: improve `VAR=val shell-func` detection
  2024-07-22  6:59 ` [PATCH 3/4] check-non-portable-shell: improve `VAR=val shell-func` detection Eric Sunshine
  2024-07-22 14:46   ` Rubén Justo
@ 2024-07-22 17:26   ` Kyle Lippincott
  2024-07-22 18:10     ` Junio C Hamano
  1 sibling, 1 reply; 37+ messages in thread
From: Kyle Lippincott @ 2024-07-22 17:26 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: git, Junio C Hamano, Rubén Justo, Eric Sunshine

On Mon, Jul 22, 2024 at 12:01 AM Eric Sunshine <ericsunshine@charter.net> wrote:
>
> From: Eric Sunshine <sunshine@sunshineco.com>
>
> Unlike "VAR=val cmd" one-shot environment variable assignments which
> exist only for the invocation of 'cmd', those assigned by "VAR=val
> shell-func" exist within the running shell and continue to do so until
> the process exits. check-non-portable-shell.pl warns when it detects
> such usage since, more often than not, the author who writes such an
> invocation is unaware of the undesirable behavior.
>
> However, a limitation of the check is that it only detects such
> invocations when variable assignment (i.e. `VAR=val`) is the first
> thing on the line. Thus, it can easily be fooled by an invocation such
> as:
>
>     echo X | VAR=val shell-func
>
> Address this shortcoming by loosening the check so that the variable
> assignment can be recognized even when not at the beginning of the line.
>
> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
> ---
>  t/check-non-portable-shell.pl | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl
> index b2b28c2ced..44b23d6ddd 100755
> --- a/t/check-non-portable-shell.pl
> +++ b/t/check-non-portable-shell.pl
> @@ -49,7 +49,7 @@ sub err {
>         /\bexport\s+[A-Za-z0-9_]*=/ and err '"export FOO=bar" is not portable (use FOO=bar && export FOO)';
>         /\blocal\s+[A-Za-z0-9_]*=\$([A-Za-z0-9_{]|[(][^(])/ and
>                 err q(quote "$val" in 'local var=$val');
> -       /^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
> +       /\b([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and !/test_env.+=/ and exists($func{$4}) and
>                 err '"FOO=bar shell_func" assignment extends beyond "shell_func"';

Is there an example of a shell on Linux that has this behavior that I
can observe, and/or reproduction steps? `bash --posix` does not do
this, as far as I can tell. I tried:

```
bash --posix
echo_hi() { echo hi; }
FOO=BAR echo_hi
echo $FOO
<no output>
```

and the simpler:

```
bash --posix
FOO=BAR echo hi
echo $FOO
```

Both attempts were done interactively, not via a script.

I'm asking mostly because of the recent "platform support document"
patch series - this is a very surprising behavior (which is presumably
why it was added to this test), and it's possible this was just a bug
in a shell that isn't really used anymore. Having documentation on how
to reproduce the issue lets us know when we can remove these kinds of
restrictions on our codebase that we took in the name of
compatibility, possibly over a decade ago.

>         $line = '';
>         # this resets our $. for each file
> --
> 2.45.2
>
>

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH 3/4] check-non-portable-shell: improve `VAR=val shell-func` detection
  2024-07-22 17:26   ` Kyle Lippincott
@ 2024-07-22 18:10     ` Junio C Hamano
  2024-07-22 21:35       ` Kyle Lippincott
  0 siblings, 1 reply; 37+ messages in thread
From: Junio C Hamano @ 2024-07-22 18:10 UTC (permalink / raw)
  To: Kyle Lippincott; +Cc: Eric Sunshine, git, Rubén Justo, Eric Sunshine

Kyle Lippincott <spectral@google.com> writes:

> Is there an example of a shell on Linux that has this behavior that I
> can observe, and/or reproduction steps?

Every once in a while this comes up and we fix, e.g.

https://lore.kernel.org/git/528CE716.8060307@ramsay1.demon.co.uk/
https://lore.kernel.org/git/c6efda03848abc00cf8bf8d84fc34ef0d652b64c.1264151435.git.mhagger@alum.mit.edu/
https://lore.kernel.org/git/Koa4iojOlOQ_YENPwWXKt7G8Aa1x6UaBnFFtliKdZmpcrrqOBhY7NQ@cipher.nrlssc.navy.mil/
https://lore.kernel.org/git/20180713055205.32351-2-sunshine@sunshineco.com/
https://lore.kernel.org/git/574E27A4.6040804@ramsayjones.plus.com/

which is from a query

    https://lore.kernel.org/git/?q=one-shot+export+shell+function

but unfortunately we do not document which exact shell the observed
breakage happened with.

The closest article I found that is suitable as a discussion
reignitor talks about what POSIX requires, which may be more
relevant:

  https://lore.kernel.org/git/4B5027B8.2090507@viscovery.net/


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH 1/4] t3430: modernize one-shot "VAR=val shell-func" invocation
  2024-07-22  6:59 ` [PATCH 1/4] t3430: modernize one-shot "VAR=val shell-func" invocation Eric Sunshine
  2024-07-22 15:09   ` Phillip Wood
@ 2024-07-22 18:24   ` Junio C Hamano
  2024-07-26  6:30     ` Eric Sunshine
  1 sibling, 1 reply; 37+ messages in thread
From: Junio C Hamano @ 2024-07-22 18:24 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: git, Rubén Justo, Eric Sunshine

Eric Sunshine <ericsunshine@charter.net> writes:

> A common way to work around the problem is to wrap a subshell around the
> variable assignments and function call, thus ensuring that the
> assignments are short-lived. However, these days, a more ergonomic
> approach is to employ test_env() which is tailor-made for this specific
> use-case.

OK.  I am not sure if that is "ergonomic", though.  An explict
subshell has a good documentation value that even though we call
test_commit there, we do not care about the committer timestamps
subsequent commits would record, and we do not mind losing the
effect of test_tick from this invocation of test_commit.  Hiding
all of that behind test_env loses the documentation value.

We could resurrect it by explicitly passing "--no-tick" to
test_commit, though ;-).

> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
> ---
>  t/t3430-rebase-merges.sh | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
> index 36ca126bcd..e851ede4f9 100755
> --- a/t/t3430-rebase-merges.sh
> +++ b/t/t3430-rebase-merges.sh
> @@ -392,8 +392,8 @@ test_expect_success 'refuse to merge ancestors of HEAD' '
>  
>  test_expect_success 'root commits' '
>  	git checkout --orphan unrelated &&
> -	(GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \
> -	 test_commit second-root) &&
> +	test_env GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \
> +		test_commit second-root &&
>  	test_commit third-root &&
>  	cat >script-from-scratch <<-\EOF &&
>  	pick third-root

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH 3/4] check-non-portable-shell: improve `VAR=val shell-func` detection
  2024-07-22 18:10     ` Junio C Hamano
@ 2024-07-22 21:35       ` Kyle Lippincott
  2024-07-22 21:57         ` Junio C Hamano
  0 siblings, 1 reply; 37+ messages in thread
From: Kyle Lippincott @ 2024-07-22 21:35 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Eric Sunshine, git, Rubén Justo, Eric Sunshine

On Mon, Jul 22, 2024 at 11:10 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Kyle Lippincott <spectral@google.com> writes:
>
> > Is there an example of a shell on Linux that has this behavior that I
> > can observe, and/or reproduction steps?
>
> Every once in a while this comes up and we fix, e.g.
>
> https://lore.kernel.org/git/528CE716.8060307@ramsay1.demon.co.uk/
> https://lore.kernel.org/git/c6efda03848abc00cf8bf8d84fc34ef0d652b64c.1264151435.git.mhagger@alum.mit.edu/
> https://lore.kernel.org/git/Koa4iojOlOQ_YENPwWXKt7G8Aa1x6UaBnFFtliKdZmpcrrqOBhY7NQ@cipher.nrlssc.navy.mil/
> https://lore.kernel.org/git/20180713055205.32351-2-sunshine@sunshineco.com/

Thanks, this one leads to
https://lore.kernel.org/git/20180713055205.32351-1-sunshine@sunshineco.com/,
which references
https://public-inbox.org/git/xmqqefg8w73c.fsf@gitster-ct.c.googlers.com/T/,
which claims that `dash` has this behavior 6 years ago. The version of
`dash` I have on my machine right now doesn't seem to have this issue,
but I can believe some older version does.

> https://lore.kernel.org/git/574E27A4.6040804@ramsayjones.plus.com/
>
> which is from a query
>
>     https://lore.kernel.org/git/?q=one-shot+export+shell+function
>
> but unfortunately we do not document which exact shell the observed
> breakage happened with.
>
> The closest article I found that is suitable as a discussion
> reignitor talks about what POSIX requires, which may be more
> relevant:
>
>   https://lore.kernel.org/git/4B5027B8.2090507@viscovery.net/

This claims that `ksh` "gets it right", and I can confirm that ksh
does behave this way on my Linux machine.

Having just looked at the POSIX standard (I don't think I'm allowed to
copy from this document), the POSIX standard (POSIX.1-2024, at least)
explicitly leaves it unspecified whether the variable assignments
remain in effect after function execution.

Thanks for indulging my curiosity; should we include a statement in
the linter along the lines of `# POSIX.1-2024 explicitly does not
specify if variable assignment persists after executing a shell
function; some shells, such as ksh, have these variables remain.`?

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH 3/4] check-non-portable-shell: improve `VAR=val shell-func` detection
  2024-07-22 21:35       ` Kyle Lippincott
@ 2024-07-22 21:57         ` Junio C Hamano
  0 siblings, 0 replies; 37+ messages in thread
From: Junio C Hamano @ 2024-07-22 21:57 UTC (permalink / raw)
  To: Kyle Lippincott; +Cc: Eric Sunshine, git, Rubén Justo, Eric Sunshine

Kyle Lippincott <spectral@google.com> writes:

> Having just looked at the POSIX standard (I don't think I'm allowed to
> copy from this document), the POSIX standard (POSIX.1-2024, at least)
> explicitly leaves it unspecified whether the variable assignments
> remain in effect after function execution.

True.

https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_09_01_02

also says that it is unspecified if the variable gets exported, and
older version of dash that comes on Ubuntu 20.04 chooses *not* to
export, which was the test breakage that triggered this whole
discussion.  The thread can be seen here:

https://lore.kernel.org/git/xmqqbk2p9lwi.fsf_-_@gitster.g/

> Thanks for indulging my curiosity; should we include a statement in
> the linter along the lines of `# POSIX.1-2024 explicitly does not
> specify if variable assignment persists after executing a shell
> function; some shells, such as ksh, have these variables remain.`?

Giving a review (either positive or negative is fine, as long as it
is constructive) on the update to CodingGuidelines

  https://lore.kernel.org/git/xmqqbk2p9lwi.fsf_-_@gitster.g/

may be a good place to start.

Thanks.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH 1/4] t3430: modernize one-shot "VAR=val shell-func" invocation
  2024-07-22 15:09   ` Phillip Wood
@ 2024-07-23  9:26     ` Phillip Wood
  2024-07-26  6:33       ` Eric Sunshine
  2024-07-26  6:15     ` Eric Sunshine
  1 sibling, 1 reply; 37+ messages in thread
From: Phillip Wood @ 2024-07-23  9:26 UTC (permalink / raw)
  To: Eric Sunshine, git; +Cc: Junio C Hamano, Rubén Justo, Eric Sunshine

On 22/07/2024 16:09, Phillip Wood wrote:
> Hi Eric
> 
> On 22/07/2024 07:59, Eric Sunshine wrote:
>> From: Eric Sunshine <sunshine@sunshineco.com>
>>
>> Unlike "VAR=val cmd" one-shot environment variable assignments which
>> exist only for the invocation of 'cmd', those assigned by "VAR=val
>> shell-func" exist within the running shell and continue to do so until
>> the process exits (or are explicitly unset).

Having seen the parallel discussion about the behavior of hash this 
construct is non-portable because the behavior differs between shells so 
perhaps the commit message could say something like

Unlike "VAR=val cmd" one-shot environment variable assignments which 
only exist for the invocation of external command "cmd", the behavior of 
"VAR=val func" where "func" is a shell function or builtin command 
varies between shells and so we should not use it in our test suite.

Best Wishes

Phillip

> I'm not sure I follow. If I run
> 
> sh -c 'f() {
>      echo "f: HELLO=$HELLO"
>      env | grep HELLO
> }
> HELLO=x f; echo "HELLO=$HELLO"'
> 
> Then I see
> 
> f: HELLO=x
> HELLO=x
> HELLO=
> 
> which seems to contradict the commit message as $HELLO is unset when the 
> function returns. I see the same result if I replace "sh" (which is bash 
> on my system) with an explicit "bash", "dash" or "zsh".
> 
> I'm also confused as to why this caused a problem for Rubén's test as 
> $HELLO is set in the environment so I'm don't understand why git wasn't 
> picking up the right pager.
> 
>> check-non-portable-shell.pl
>> warns when it detects such usage since, more often than not, the author
>> who writes such an invocation is unaware of the undesirable behavior.
>>
>> A common way to work around the problem is to wrap a subshell around the
>> variable assignments and function call, thus ensuring that the
>> assignments are short-lived. However, these days, a more ergonomic
>> approach is to employ test_env() which is tailor-made for this specific
>> use-case.
> 
> Oh, that sounds useful, I didn't know it existed.
> 
> Best Wishes
> 
> Phillip
> 
>> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
>> ---
>>   t/t3430-rebase-merges.sh | 4 ++--
>>   1 file changed, 2 insertions(+), 2 deletions(-)
>>
>> diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
>> index 36ca126bcd..e851ede4f9 100755
>> --- a/t/t3430-rebase-merges.sh
>> +++ b/t/t3430-rebase-merges.sh
>> @@ -392,8 +392,8 @@ test_expect_success 'refuse to merge ancestors of 
>> HEAD' '
>>   test_expect_success 'root commits' '
>>       git checkout --orphan unrelated &&
>> -    (GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \
>> -     test_commit second-root) &&
>> +    test_env GIT_AUTHOR_NAME="Parsnip" 
>> GIT_AUTHOR_EMAIL="root@example.com" \
>> +        test_commit second-root &&
>>       test_commit third-root &&
>>       cat >script-from-scratch <<-\EOF &&
>>       pick third-root


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH 1/4] t3430: modernize one-shot "VAR=val shell-func" invocation
  2024-07-22 15:09   ` Phillip Wood
  2024-07-23  9:26     ` Phillip Wood
@ 2024-07-26  6:15     ` Eric Sunshine
  1 sibling, 0 replies; 37+ messages in thread
From: Eric Sunshine @ 2024-07-26  6:15 UTC (permalink / raw)
  To: phillip.wood; +Cc: Eric Sunshine, git, Junio C Hamano, Rubén Justo

On Mon, Jul 22, 2024 at 11:10 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> On 22/07/2024 07:59, Eric Sunshine wrote:
> > Unlike "VAR=val cmd" one-shot environment variable assignments which
> > exist only for the invocation of 'cmd', those assigned by "VAR=val
> > shell-func" exist within the running shell and continue to do so until
> > the process exits (or are explicitly unset).
>
> I'm not sure I follow. If I run
>
> sh -c 'f() {
>      echo "f: HELLO=$HELLO"
>      env | grep HELLO
> }
> HELLO=x f; echo "HELLO=$HELLO"'
>
> Then I see
>
> f: HELLO=x
> HELLO=x
> HELLO=
>
> which seems to contradict the commit message as $HELLO is unset when the
> function returns. I see the same result if I replace "sh" (which is bash
> on my system) with an explicit "bash", "dash" or "zsh".

I believe downstream discussion[1][2] established that the behavior is
inconsistent between various shells and versions of shells, and is
considered undefined by POSIX.

[1]: https://lore.kernel.org/git/xmqq34o1cn6b.fsf@gitster.g/
[2]: https://lore.kernel.org/git/xmqqbk2p9lwi.fsf_-_@gitster.g/

> I'm also confused as to why this caused a problem for Rubén's test as
> $HELLO is set in the environment so I'm don't understand why git wasn't
> picking up the right pager.

Junio summarized the problem and explanation[3].

[3]: https://lore.kernel.org/git/xmqq7cdd9l0m.fsf@gitster.g/

> > A common way to work around the problem is to wrap a subshell around the
> > variable assignments and function call, thus ensuring that the
> > assignments are short-lived. However, these days, a more ergonomic
> > approach is to employ test_env() which is tailor-made for this specific
> > use-case.
>
> Oh, that sounds useful, I didn't know it existed.

I didn't know about it either, and only discovered it upon my initial
attempt at making check-non-portable-shell.pl recognize the case Rubén
identified, at which point it started showing false-positives on
`test_env` invocations. Actually, considering that I was involved[4]
in the conversation which led to the introduction[5] of `test_env` by
Peff, it may be that I did know about it but forgot.

[4]: https://lore.kernel.org/git/CAPig+cR989yU4+JNTFREaeXqY61nusUOhufeBGGVCi29tR1P5w@mail.gmail.com/
[5]: https://lore.kernel.org/git/20160601070425.GA13648@sigill.intra.peff.net/

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH 1/4] t3430: modernize one-shot "VAR=val shell-func" invocation
  2024-07-22 18:24   ` Junio C Hamano
@ 2024-07-26  6:30     ` Eric Sunshine
  0 siblings, 0 replies; 37+ messages in thread
From: Eric Sunshine @ 2024-07-26  6:30 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Eric Sunshine, git, Rubén Justo

On Mon, Jul 22, 2024 at 2:24 PM Junio C Hamano <gitster@pobox.com> wrote:
> Eric Sunshine <ericsunshine@charter.net> writes:
> > A common way to work around the problem is to wrap a subshell around the
> > variable assignments and function call, thus ensuring that the
> > assignments are short-lived. However, these days, a more ergonomic
> > approach is to employ test_env() which is tailor-made for this specific
> > use-case.
>
> OK.  I am not sure if that is "ergonomic", though.  An explict
> subshell has a good documentation value that even though we call
> test_commit there, we do not care about the committer timestamps
> subsequent commits would record, and we do not mind losing the
> effect of test_tick from this invocation of test_commit.  Hiding
> all of that behind test_env loses the documentation value.
>
> We could resurrect it by explicitly passing "--no-tick" to
> test_commit, though ;-).

Your mention of `test_commit` reminded me that, these days, it accepts
an --author switch which seems tailor-made for what this chunk of code
in this test is actually checking: namely, that the root commit of the
orphan branch has "Parsnip <root@example.com>" as its author. So an
even simpler approach is to change it to:

    test_commit --author "Parsnip <root@example.com>" second-root &&

That conveys precisely that the test is interested in overriding the
author for that one commit.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH 1/4] t3430: modernize one-shot "VAR=val shell-func" invocation
  2024-07-23  9:26     ` Phillip Wood
@ 2024-07-26  6:33       ` Eric Sunshine
  0 siblings, 0 replies; 37+ messages in thread
From: Eric Sunshine @ 2024-07-26  6:33 UTC (permalink / raw)
  To: phillip.wood; +Cc: Eric Sunshine, git, Junio C Hamano, Rubén Justo

On Tue, Jul 23, 2024 at 5:26 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> On 22/07/2024 16:09, Phillip Wood wrote:
> > On 22/07/2024 07:59, Eric Sunshine wrote:
> >> Unlike "VAR=val cmd" one-shot environment variable assignments which
> >> exist only for the invocation of 'cmd', those assigned by "VAR=val
> >> shell-func" exist within the running shell and continue to do so until
> >> the process exits (or are explicitly unset).
>
> Having seen the parallel discussion about the behavior of hash this
> construct is non-portable because the behavior differs between shells so
> perhaps the commit message could say something like
>
> Unlike "VAR=val cmd" one-shot environment variable assignments which
> only exist for the invocation of external command "cmd", the behavior of
> "VAR=val func" where "func" is a shell function or builtin command
> varies between shells and so we should not use it in our test suite.

Indeed, given all the subsequent discussion, it is now apparent that
multiple undesirable behaviors have been experienced, not just the one
mentioned by this commit message, and that POSIX states that the
behavior is undefined.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH 3/4] check-non-portable-shell: improve `VAR=val shell-func` detection
  2024-07-22 14:46   ` Rubén Justo
@ 2024-07-26  6:45     ` Eric Sunshine
  2024-07-26 13:15       ` Rubén Justo
  0 siblings, 1 reply; 37+ messages in thread
From: Eric Sunshine @ 2024-07-26  6:45 UTC (permalink / raw)
  To: Rubén Justo; +Cc: Eric Sunshine, git, Junio C Hamano

On Mon, Jul 22, 2024 at 10:46 AM Rubén Justo <rjusto@gmail.com> wrote:
> On Mon, Jul 22, 2024 at 02:59:13AM -0400, Eric Sunshine wrote:
> > -     /^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
> > +     /\b([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and !/test_env.+=/ and exists($func{$4}) and
>
> Losing "^\s*" means we'll cause false positives, such as:
>
>     # VAR=VAL shell-func
>     echo VAR=VAL shell-func

True, though, considering that "shell-func" in these examples must
match the name of a function actually defined in one of the input
files, one would expect (or at least hope) that this sort of
false-positive will be exceedingly rare. Indeed, there are no such
false-positives in the existing test scripts. Of course, we can always
tighten the regex later if it proves to be problematic.

> Regardless of that, the regex will continue to pose problems with:
>
>   VAR=$OTHER_VALUE shell-func
>   VAR=$(cmd) shell-func
>   VAR=VAL\ UE shell-func
>   VAR="\"val\" shell-func UE" non-shell-func
>
> Which, of course, should be cases that should be written in a more
> orthodox way.

Yes, it can be difficult to be thorough when "linting" a programming
language merely via regular-expressions, and this particular
expression is already almost unreadable. The effort involved in trying
to make it perfect may very well outweigh the potential gain in
coverage.

> But we will start to detect errors like the ones mentioned in the
> message, which are more likely to happen.

Indeed.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH v2 0/5] improve one-shot variable detection with shell function
  2024-07-22  6:59 [PATCH 0/4] improve one-shot variable detection with shell function Eric Sunshine
                   ` (4 preceding siblings ...)
  2024-07-22 14:50 ` [PATCH 0/4] improve one-shot variable detection with shell function Rubén Justo
@ 2024-07-26  8:15 ` Eric Sunshine
  2024-07-26  8:15   ` [PATCH v2 1/5] t3430: drop unnecessary one-shot "VAR=val shell-func" invocation Eric Sunshine
                     ` (6 more replies)
  5 siblings, 7 replies; 37+ messages in thread
From: Eric Sunshine @ 2024-07-26  8:15 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Rubén Justo, Phillip Wood, Kyle Lippincott,
	Eric Sunshine

From: Eric Sunshine <sunshine@sunshineco.com>

This is a reroll of [1] which improves check-non-portable-shell's
detection of one-shot environment variable assignment with shell
functions. Changes since v1:

* commit messages now state the behavior is undefined according to POSIX
  rather than focusing only on the original reason given (that the
  assignments could outlive the shell function invocation)

* t3430 simplified by dropping the subshell altogether in favor of
  `test_commit --author`

* new commit to improve the error message when one-shot assignment with
  shell function is detected

[1]: https://lore.kernel.org/git/20240722065915.80760-1-ericsunshine@charter.net/

Eric Sunshine (5):
  t3430: drop unnecessary one-shot "VAR=val shell-func" invocation
  t4034: fix use of one-shot variable assignment with shell function
  check-non-portable-shell: loosen one-shot assignment error message
  check-non-portable-shell: suggest alternative for `VAR=val shell-func`
  check-non-portable-shell: improve `VAR=val shell-func` detection

 t/check-non-portable-shell.pl | 4 ++--
 t/t3430-rebase-merges.sh      | 3 +--
 t/t4034-diff-words.sh         | 2 +-
 3 files changed, 4 insertions(+), 5 deletions(-)

Range-diff against v1:
1:  5bb6811f68 ! 1:  0d3c0593c9 t3430: modernize one-shot "VAR=val shell-func" invocation
    @@ Metadata
     Author: Eric Sunshine <sunshine@sunshineco.com>
     
      ## Commit message ##
    -    t3430: modernize one-shot "VAR=val shell-func" invocation
    +    t3430: drop unnecessary one-shot "VAR=val shell-func" invocation
     
    -    Unlike "VAR=val cmd" one-shot environment variable assignments which
    -    exist only for the invocation of 'cmd', those assigned by "VAR=val
    -    shell-func" exist within the running shell and continue to do so until
    -    the process exits (or are explicitly unset). check-non-portable-shell.pl
    -    warns when it detects such usage since, more often than not, the author
    -    who writes such an invocation is unaware of the undesirable behavior.
    +    The behavior of a one-shot environment variable assignment of the form
    +    "VAR=val cmd" is undefined according to POSIX when "cmd" is a shell
    +    function. Indeed the behavior differs between shell implementations and
    +    even different versions of the same shell. One such ill-defined behavior
    +    is that, with some shells, the assignment will outlive the invocation of
    +    the function, thus may potentially impact subsequent commands in the
    +    test, as well as subsequent tests. A common way to work around the
    +    problem is to wrap a subshell around the one-shot assignment, thus
    +    ensuring that the assignment is short-lived.
     
    -    A common way to work around the problem is to wrap a subshell around the
    -    variable assignments and function call, thus ensuring that the
    -    assignments are short-lived. However, these days, a more ergonomic
    -    approach is to employ test_env() which is tailor-made for this specific
    -    use-case.
    +    In this test, the subshell is employed precisely for this purpose; other
    +    side-effects of the subshell, such as losing the effect of `test_tick`
    +    which is invoked by `test_commit`, are immaterial.
    +
    +    These days, we can take advantage of `test_commit --author` to more
    +    clearly convey that the test is interested only in overriding the author
    +    of the commit.
     
         Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
     
    @@ t/t3430-rebase-merges.sh: test_expect_success 'refuse to merge ancestors of HEAD
      	git checkout --orphan unrelated &&
     -	(GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \
     -	 test_commit second-root) &&
    -+	test_env GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \
    -+		test_commit second-root &&
    ++	test_commit --author "Parsnip <root@example.com>" second-root &&
      	test_commit third-root &&
      	cat >script-from-scratch <<-\EOF &&
      	pick third-root
2:  1f35449847 ! 2:  19ee8295ef t4034: fix use of one-shot variable assignment with shell function
    @@ Metadata
      ## Commit message ##
         t4034: fix use of one-shot variable assignment with shell function
     
    -    Unlike "VAR=val cmd" one-shot environment variable assignments which
    -    exist only for the invocation of 'cmd', those assigned by "VAR=val
    -    shell-func" exist within the running shell and continue to do so until
    -    the process exits (or are explicitly unset). In most cases, it is
    -    unlikely that this behavior was intended by the test author, and, even
    -    if those leaked assignments do not impact other tests today, they can
    -    negatively impact tests added later by authors unaware that the variable
    -    assignments are still hanging around. Address this shortcoming by
    -    ensuring that the assignments are short-lived.
    +    The behavior of a one-shot environment variable assignment of the form
    +    "VAR=val cmd" is undefined according to POSIX when "cmd" is a shell
    +    function. Indeed the behavior differs between shell implementations and
    +    even different versions of the same shell, thus should be avoided.
     
         Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
     
-:  ---------- > 3:  220ca26d4f check-non-portable-shell: loosen one-shot assignment error message
-:  ---------- > 4:  4910756aab check-non-portable-shell: suggest alternative for `VAR=val shell-func`
3:  89621f72a2 ! 5:  7a15553a5a check-non-portable-shell: improve `VAR=val shell-func` detection
    @@ Metadata
      ## Commit message ##
         check-non-portable-shell: improve `VAR=val shell-func` detection
     
    -    Unlike "VAR=val cmd" one-shot environment variable assignments which
    -    exist only for the invocation of 'cmd', those assigned by "VAR=val
    -    shell-func" exist within the running shell and continue to do so until
    -    the process exits. check-non-portable-shell.pl warns when it detects
    -    such usage since, more often than not, the author who writes such an
    -    invocation is unaware of the undesirable behavior.
    +    The behavior of a one-shot environment variable assignment of the form
    +    "VAR=val cmd" is undefined according to POSIX when "cmd" is a shell
    +    function. Indeed the behavior differs between shell implementations and
    +    even different versions of the same shell, thus should be avoided.
     
    +    As such, check-non-portable-shell.pl warns when it detects such usage.
         However, a limitation of the check is that it only detects such
    -    invocations when variable assignment (i.e. `VAR=val`) is the first
    -    thing on the line. Thus, it can easily be fooled by an invocation such
    -    as:
    +    invocations when variable assignment (i.e. `VAR=val`) is the first thing
    +    on the line. Thus, it can easily be fooled by an invocation such as:
     
             echo X | VAR=val shell-func
     
    @@ t/check-non-portable-shell.pl: sub err {
      		err q(quote "$val" in 'local var=$val');
     -	/^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
     +	/\b([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and !/test_env.+=/ and exists($func{$4}) and
    - 		err '"FOO=bar shell_func" assignment extends beyond "shell_func"';
    + 		err '"FOO=bar shell_func" is not portable (use test_env FOO=bar shell_func)';
      	$line = '';
      	# this resets our $. for each file
4:  7b2e1dd895 < -:  ---------- check-non-portable-shell: suggest alternative for `VAR=val shell-func`
-- 
2.45.2


^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH v2 1/5] t3430: drop unnecessary one-shot "VAR=val shell-func" invocation
  2024-07-26  8:15 ` [PATCH v2 0/5] " Eric Sunshine
@ 2024-07-26  8:15   ` Eric Sunshine
  2024-07-26 18:50     ` Junio C Hamano
  2024-07-26  8:15   ` [PATCH v2 2/5] t4034: fix use of one-shot variable assignment with shell function Eric Sunshine
                     ` (5 subsequent siblings)
  6 siblings, 1 reply; 37+ messages in thread
From: Eric Sunshine @ 2024-07-26  8:15 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Rubén Justo, Phillip Wood, Kyle Lippincott,
	Eric Sunshine

From: Eric Sunshine <sunshine@sunshineco.com>

The behavior of a one-shot environment variable assignment of the form
"VAR=val cmd" is undefined according to POSIX when "cmd" is a shell
function. Indeed the behavior differs between shell implementations and
even different versions of the same shell. One such ill-defined behavior
is that, with some shells, the assignment will outlive the invocation of
the function, thus may potentially impact subsequent commands in the
test, as well as subsequent tests. A common way to work around the
problem is to wrap a subshell around the one-shot assignment, thus
ensuring that the assignment is short-lived.

In this test, the subshell is employed precisely for this purpose; other
side-effects of the subshell, such as losing the effect of `test_tick`
which is invoked by `test_commit`, are immaterial.

These days, we can take advantage of `test_commit --author` to more
clearly convey that the test is interested only in overriding the author
of the commit.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t3430-rebase-merges.sh | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index 36ca126bcd..2aa8593f77 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -392,8 +392,7 @@ test_expect_success 'refuse to merge ancestors of HEAD' '
 
 test_expect_success 'root commits' '
 	git checkout --orphan unrelated &&
-	(GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \
-	 test_commit second-root) &&
+	test_commit --author "Parsnip <root@example.com>" second-root &&
 	test_commit third-root &&
 	cat >script-from-scratch <<-\EOF &&
 	pick third-root
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v2 2/5] t4034: fix use of one-shot variable assignment with shell function
  2024-07-26  8:15 ` [PATCH v2 0/5] " Eric Sunshine
  2024-07-26  8:15   ` [PATCH v2 1/5] t3430: drop unnecessary one-shot "VAR=val shell-func" invocation Eric Sunshine
@ 2024-07-26  8:15   ` Eric Sunshine
  2024-07-26  8:15   ` [PATCH v2 3/5] check-non-portable-shell: loosen one-shot assignment error message Eric Sunshine
                     ` (4 subsequent siblings)
  6 siblings, 0 replies; 37+ messages in thread
From: Eric Sunshine @ 2024-07-26  8:15 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Rubén Justo, Phillip Wood, Kyle Lippincott,
	Eric Sunshine

From: Eric Sunshine <sunshine@sunshineco.com>

The behavior of a one-shot environment variable assignment of the form
"VAR=val cmd" is undefined according to POSIX when "cmd" is a shell
function. Indeed the behavior differs between shell implementations and
even different versions of the same shell, thus should be avoided.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t4034-diff-words.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh
index 74586f3813..4dcd7e9925 100755
--- a/t/t4034-diff-words.sh
+++ b/t/t4034-diff-words.sh
@@ -70,7 +70,7 @@ test_language_driver () {
 		word_diff --color-words
 	'
 	test_expect_success "diff driver '$lang' in Islandic" '
-		LANG=is_IS.UTF-8 LANGUAGE=is LC_ALL="$is_IS_locale" \
+		test_env LANG=is_IS.UTF-8 LANGUAGE=is LC_ALL="$is_IS_locale" \
 		word_diff --color-words
 	'
 }
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v2 3/5] check-non-portable-shell: loosen one-shot assignment error message
  2024-07-26  8:15 ` [PATCH v2 0/5] " Eric Sunshine
  2024-07-26  8:15   ` [PATCH v2 1/5] t3430: drop unnecessary one-shot "VAR=val shell-func" invocation Eric Sunshine
  2024-07-26  8:15   ` [PATCH v2 2/5] t4034: fix use of one-shot variable assignment with shell function Eric Sunshine
@ 2024-07-26  8:15   ` Eric Sunshine
  2024-07-26  8:15   ` [PATCH v2 4/5] check-non-portable-shell: suggest alternative for `VAR=val shell-func` Eric Sunshine
                     ` (3 subsequent siblings)
  6 siblings, 0 replies; 37+ messages in thread
From: Eric Sunshine @ 2024-07-26  8:15 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Rubén Justo, Phillip Wood, Kyle Lippincott,
	Eric Sunshine

From: Eric Sunshine <sunshine@sunshineco.com>

When a0a630192d (t/check-non-portable-shell: detect "FOO=bar
shell_func", 2018-07-13) added the check for one-shot environment
variable assignment for shell functions, the primary reason given for
avoiding them was that, under some shells, the assignment outlives the
invocation of the shell function, thus could potentially negatively
impact subsequent commands in the same test, as well as subsequent
tests.

However, it has recently become apparent that this is not the only
potential problem with one-shot assignments and shell functions. Another
problem is that some shells do not actually export the variable to
commands which the function invokes[1]. More significantly, however, the
behavior of one-shot assignments with shell functions is considered
undefined by POSIX[2].

Given this new understanding, the presented error message ("assignment
extends beyond 'shell_func'") is too specific and potentially
misleading. Address this by emitting a less specific error message.

(Note that the wording "is not portable" is chosen over the more
specific "has undefined behavior according to POSIX" for consistency
with almost all other error message issued by this "lint" script.)

[1]: https://lore.kernel.org/git/xmqqbk2p9lwi.fsf_-_@gitster.g/
[2]: https://lore.kernel.org/git/xmqq34o19jj1.fsf@gitster.g/

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/check-non-portable-shell.pl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl
index b2b28c2ced..179efaa39d 100755
--- a/t/check-non-portable-shell.pl
+++ b/t/check-non-portable-shell.pl
@@ -50,7 +50,7 @@ sub err {
 	/\blocal\s+[A-Za-z0-9_]*=\$([A-Za-z0-9_{]|[(][^(])/ and
 		err q(quote "$val" in 'local var=$val');
 	/^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
-		err '"FOO=bar shell_func" assignment extends beyond "shell_func"';
+		err '"FOO=bar shell_func" is not portable';
 	$line = '';
 	# this resets our $. for each file
 	close ARGV if eof;
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v2 4/5] check-non-portable-shell: suggest alternative for `VAR=val shell-func`
  2024-07-26  8:15 ` [PATCH v2 0/5] " Eric Sunshine
                     ` (2 preceding siblings ...)
  2024-07-26  8:15   ` [PATCH v2 3/5] check-non-portable-shell: loosen one-shot assignment error message Eric Sunshine
@ 2024-07-26  8:15   ` Eric Sunshine
  2024-07-26 13:11     ` Rubén Justo
  2024-07-26  8:15   ` [PATCH v2 5/5] check-non-portable-shell: improve `VAR=val shell-func` detection Eric Sunshine
                     ` (2 subsequent siblings)
  6 siblings, 1 reply; 37+ messages in thread
From: Eric Sunshine @ 2024-07-26  8:15 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Rubén Justo, Phillip Wood, Kyle Lippincott,
	Eric Sunshine

From: Eric Sunshine <sunshine@sunshineco.com>

Most problems reported by check-non-portable-shell are accompanied by
advice suggesting how the test author can repair the problem. For
instance:

    error: egrep/fgrep obsolescent (use grep -E/-F)

However, when one-shot variable assignment is detected when calling a
shell function (i.e. `VAR=val shell-func`), the problem is reported, but
no advice is given. The lack of advice is particularly egregious since
neither the problem nor the workaround are likely well-known by
newcomers to the project writing tests for the first time. Address this
shortcoming by recommending the use of `test_env` which is tailor made
for this specific use-case.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/check-non-portable-shell.pl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl
index 179efaa39d..903af14294 100755
--- a/t/check-non-portable-shell.pl
+++ b/t/check-non-portable-shell.pl
@@ -50,7 +50,7 @@ sub err {
 	/\blocal\s+[A-Za-z0-9_]*=\$([A-Za-z0-9_{]|[(][^(])/ and
 		err q(quote "$val" in 'local var=$val');
 	/^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
-		err '"FOO=bar shell_func" is not portable';
+		err '"FOO=bar shell_func" is not portable (use test_env FOO=bar shell_func)';
 	$line = '';
 	# this resets our $. for each file
 	close ARGV if eof;
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v2 5/5] check-non-portable-shell: improve `VAR=val shell-func` detection
  2024-07-26  8:15 ` [PATCH v2 0/5] " Eric Sunshine
                     ` (3 preceding siblings ...)
  2024-07-26  8:15   ` [PATCH v2 4/5] check-non-portable-shell: suggest alternative for `VAR=val shell-func` Eric Sunshine
@ 2024-07-26  8:15   ` Eric Sunshine
  2024-07-26 18:38   ` [PATCH v2 0/5] improve one-shot variable detection with shell function Junio C Hamano
  2024-07-27  5:35   ` [PATCH v3 " Eric Sunshine
  6 siblings, 0 replies; 37+ messages in thread
From: Eric Sunshine @ 2024-07-26  8:15 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Rubén Justo, Phillip Wood, Kyle Lippincott,
	Eric Sunshine

From: Eric Sunshine <sunshine@sunshineco.com>

The behavior of a one-shot environment variable assignment of the form
"VAR=val cmd" is undefined according to POSIX when "cmd" is a shell
function. Indeed the behavior differs between shell implementations and
even different versions of the same shell, thus should be avoided.

As such, check-non-portable-shell.pl warns when it detects such usage.
However, a limitation of the check is that it only detects such
invocations when variable assignment (i.e. `VAR=val`) is the first thing
on the line. Thus, it can easily be fooled by an invocation such as:

    echo X | VAR=val shell-func

Address this shortcoming by loosening the check so that the variable
assignment can be recognized even when not at the beginning of the line.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/check-non-portable-shell.pl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl
index 903af14294..6ee7700eb4 100755
--- a/t/check-non-portable-shell.pl
+++ b/t/check-non-portable-shell.pl
@@ -49,7 +49,7 @@ sub err {
 	/\bexport\s+[A-Za-z0-9_]*=/ and err '"export FOO=bar" is not portable (use FOO=bar && export FOO)';
 	/\blocal\s+[A-Za-z0-9_]*=\$([A-Za-z0-9_{]|[(][^(])/ and
 		err q(quote "$val" in 'local var=$val');
-	/^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
+	/\b([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and !/test_env.+=/ and exists($func{$4}) and
 		err '"FOO=bar shell_func" is not portable (use test_env FOO=bar shell_func)';
 	$line = '';
 	# this resets our $. for each file
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [PATCH v2 4/5] check-non-portable-shell: suggest alternative for `VAR=val shell-func`
  2024-07-26  8:15   ` [PATCH v2 4/5] check-non-portable-shell: suggest alternative for `VAR=val shell-func` Eric Sunshine
@ 2024-07-26 13:11     ` Rubén Justo
  2024-07-26 19:31       ` Eric Sunshine
  0 siblings, 1 reply; 37+ messages in thread
From: Rubén Justo @ 2024-07-26 13:11 UTC (permalink / raw)
  To: Eric Sunshine, git
  Cc: Junio C Hamano, Phillip Wood, Kyle Lippincott, Eric Sunshine

On Fri, Jul 26, 2024 at 04:15:21AM -0400, Eric Sunshine wrote:
> From: Eric Sunshine <sunshine@sunshineco.com>
> 
> Most problems reported by check-non-portable-shell are accompanied by
> advice suggesting how the test author can repair the problem. For
> instance:
> 
>     error: egrep/fgrep obsolescent (use grep -E/-F)
> 
> However, when one-shot variable assignment is detected when calling a
> shell function (i.e. `VAR=val shell-func`), the problem is reported, but
> no advice is given. The lack of advice is particularly egregious since
> neither the problem nor the workaround are likely well-known by
> newcomers to the project writing tests for the first time. Address this
> shortcoming by recommending the use of `test_env` which is tailor made
> for this specific use-case.
> 
> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
> ---
>  t/check-non-portable-shell.pl | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl
> index 179efaa39d..903af14294 100755
> --- a/t/check-non-portable-shell.pl
> +++ b/t/check-non-portable-shell.pl
> @@ -50,7 +50,7 @@ sub err {
>  	/\blocal\s+[A-Za-z0-9_]*=\$([A-Za-z0-9_{]|[(][^(])/ and
>  		err q(quote "$val" in 'local var=$val');
>  	/^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
> -		err '"FOO=bar shell_func" is not portable';
> +		err '"FOO=bar shell_func" is not portable (use test_env FOO=bar shell_func)';

When someone blames this line in the future, the message of this commit
will appear and be informative.  However, I think the message of the
previous patch [3/5], which also touches this line, would also be
relevant for this context.  And it won't be so obvious to get to that
message.  Therefore, it might be worth combining this commit with the
previous one.  But I'm not sure the change is worth it to have a new
iteration of this series.

Anyway, the change in the err() is a convenient improvement.

>  	$line = '';
>  	# this resets our $. for each file
>  	close ARGV if eof;
> -- 
> 2.45.2
> 

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH 3/4] check-non-portable-shell: improve `VAR=val shell-func` detection
  2024-07-26  6:45     ` Eric Sunshine
@ 2024-07-26 13:15       ` Rubén Justo
  0 siblings, 0 replies; 37+ messages in thread
From: Rubén Justo @ 2024-07-26 13:15 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Eric Sunshine, git, Junio C Hamano

On Fri, Jul 26, 2024 at 02:45:59AM -0400, Eric Sunshine wrote:
> On Mon, Jul 22, 2024 at 10:46 AM Rubén Justo <rjusto@gmail.com> wrote:
> > On Mon, Jul 22, 2024 at 02:59:13AM -0400, Eric Sunshine wrote:
> > > -     /^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
> > > +     /\b([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and !/test_env.+=/ and exists($func{$4}) and
> >
> > Losing "^\s*" means we'll cause false positives, such as:
> >
> >     # VAR=VAL shell-func
> >     echo VAR=VAL shell-func
> 
> True, though, considering that "shell-func" in these examples must
> match the name of a function actually defined in one of the input
> files, one would expect (or at least hope) that this sort of
> false-positive will be exceedingly rare. Indeed, there are no such
> false-positives in the existing test scripts. Of course, we can always
> tighten the regex later if it proves to be problematic.
> 
> > Regardless of that, the regex will continue to pose problems with:
> >
> >   VAR=$OTHER_VALUE shell-func
> >   VAR=$(cmd) shell-func
> >   VAR=VAL\ UE shell-func
> >   VAR="\"val\" shell-func UE" non-shell-func
> >
> > Which, of course, should be cases that should be written in a more
> > orthodox way.
> 
> Yes, it can be difficult to be thorough when "linting" a programming
> language merely via regular-expressions, and this particular
> expression is already almost unreadable. The effort involved in trying
> to make it perfect may very well outweigh the potential gain in
> coverage.

I tried to be exhaustive in the analysis of the change and explicit in
the conclusions so that it is clear, and documented in the list, that we
acknowledge the magnitude of the change.

I agree.  I don't think it's worth refining the regex any further.  It
might even be counterproductive.  It covers the cases it was already
covering and the new ones that have occurred.

A simple 'Acked-by: Rubén Justo <rjusto@gmail.com>' didn't seem
sufficient to me :), but perhaps it would have been clearer.

I have the same positive opinion of your new iteration:
20240722065915.80760-5-ericsunshine@charter.net

> 
> > But we will start to detect errors like the ones mentioned in the
> > message, which are more likely to happen.
> 
> Indeed.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2 0/5] improve one-shot variable detection with shell function
  2024-07-26  8:15 ` [PATCH v2 0/5] " Eric Sunshine
                     ` (4 preceding siblings ...)
  2024-07-26  8:15   ` [PATCH v2 5/5] check-non-portable-shell: improve `VAR=val shell-func` detection Eric Sunshine
@ 2024-07-26 18:38   ` Junio C Hamano
  2024-07-27  5:35   ` [PATCH v3 " Eric Sunshine
  6 siblings, 0 replies; 37+ messages in thread
From: Junio C Hamano @ 2024-07-26 18:38 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: git, Rubén Justo, Phillip Wood, Kyle Lippincott,
	Eric Sunshine

Eric Sunshine <ericsunshine@charter.net> writes:

>     @@ t/t3430-rebase-merges.sh: test_expect_success 'refuse to merge ancestors of HEAD
>       	git checkout --orphan unrelated &&
>      -	(GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \
>      -	 test_commit second-root) &&
>     -+	test_env GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \
>     -+		test_commit second-root &&
>     ++	test_commit --author "Parsnip <root@example.com>" second-root &&

Very pleasing to the eyes.

>     @@ t/check-non-portable-shell.pl: sub err {
>       		err q(quote "$val" in 'local var=$val');
>      -	/^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
>      +	/\b([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and !/test_env.+=/ and exists($func{$4}) and
>     - 		err '"FOO=bar shell_func" assignment extends beyond "shell_func"';
>     + 		err '"FOO=bar shell_func" is not portable (use test_env FOO=bar shell_func)';

OK.

Thanks.  Will queue.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2 1/5] t3430: drop unnecessary one-shot "VAR=val shell-func" invocation
  2024-07-26  8:15   ` [PATCH v2 1/5] t3430: drop unnecessary one-shot "VAR=val shell-func" invocation Eric Sunshine
@ 2024-07-26 18:50     ` Junio C Hamano
  2024-07-26 19:32       ` Eric Sunshine
  0 siblings, 1 reply; 37+ messages in thread
From: Junio C Hamano @ 2024-07-26 18:50 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: git, Rubén Justo, Phillip Wood, Kyle Lippincott,
	Eric Sunshine

Eric Sunshine <ericsunshine@charter.net> writes:

> From: Eric Sunshine <sunshine@sunshineco.com>
>
> The behavior of a one-shot environment variable assignment of the form
> "VAR=val cmd" is undefined according to POSIX when "cmd" is a shell

Please use the right word to describe what the standard says.

Throughout the topic's discussion, you seem to be repeating
"undefined", but the word POSIX uses for this particular unportable
behaviour is "unspecified".  The differences are subtle, and for
programs that want to be conformant, there is no practical
difference (in other words, we should not rely on the existence or
validity of the value or behaviour if we wanted to be portable).

The former is what results from use of an invalid construct or
feeding an invalid data input.  The implementation can do whatever
it wants to do once you trigger an undefined behaviour.  The latter
is what results from use of a valid construct or valid data input,
but outcome may differ across implementations.  An "unspecified"
behaviour often are still consistent and sensible within a single
conformant implementation.


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2 4/5] check-non-portable-shell: suggest alternative for `VAR=val shell-func`
  2024-07-26 13:11     ` Rubén Justo
@ 2024-07-26 19:31       ` Eric Sunshine
  0 siblings, 0 replies; 37+ messages in thread
From: Eric Sunshine @ 2024-07-26 19:31 UTC (permalink / raw)
  To: Rubén Justo
  Cc: Eric Sunshine, git, Junio C Hamano, Phillip Wood, Kyle Lippincott

On Fri, Jul 26, 2024 at 9:11 AM Rubén Justo <rjusto@gmail.com> wrote:
> On Fri, Jul 26, 2024 at 04:15:21AM -0400, Eric Sunshine wrote:
> > -             err '"FOO=bar shell_func" is not portable';
> > +             err '"FOO=bar shell_func" is not portable (use test_env FOO=bar shell_func)';
>
> When someone blames this line in the future, the message of this commit
> will appear and be informative.  However, I think the message of the
> previous patch [3/5], which also touches this line, would also be
> relevant for this context.  And it won't be so obvious to get to that
> message.  Therefore, it might be worth combining this commit with the
> previous one.  But I'm not sure the change is worth it to have a new
> iteration of this series.

I did consider combining the two patches but decided against it.
Despite the fact that both patches touch the same line/message, they
really are two distinct "fixes" as evidenced by the fact that the
explanation provided by each commit message is entirely orthogonal to
the other.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2 1/5] t3430: drop unnecessary one-shot "VAR=val shell-func" invocation
  2024-07-26 18:50     ` Junio C Hamano
@ 2024-07-26 19:32       ` Eric Sunshine
  0 siblings, 0 replies; 37+ messages in thread
From: Eric Sunshine @ 2024-07-26 19:32 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Eric Sunshine, git, Rubén Justo, Phillip Wood,
	Kyle Lippincott

On Fri, Jul 26, 2024 at 2:50 PM Junio C Hamano <gitster@pobox.com> wrote:
> Eric Sunshine <ericsunshine@charter.net> writes:
> > The behavior of a one-shot environment variable assignment of the form
> > "VAR=val cmd" is undefined according to POSIX when "cmd" is a shell
>
> Please use the right word to describe what the standard says.
>
> Throughout the topic's discussion, you seem to be repeating
> "undefined", but the word POSIX uses for this particular unportable
> behaviour is "unspecified".  The differences are subtle, and for
> programs that want to be conformant, there is no practical
> difference (in other words, we should not rely on the existence or
> validity of the value or behaviour if we wanted to be portable).
>
> The former is what results from use of an invalid construct or
> feeding an invalid data input.  The implementation can do whatever
> it wants to do once you trigger an undefined behaviour.  The latter
> is what results from use of a valid construct or valid data input,
> but outcome may differ across implementations.  An "unspecified"
> behaviour often are still consistent and sensible within a single
> conformant implementation.

Makes sense. Will adjust the commit messages.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH v3 0/5] improve one-shot variable detection with shell function
  2024-07-26  8:15 ` [PATCH v2 0/5] " Eric Sunshine
                     ` (5 preceding siblings ...)
  2024-07-26 18:38   ` [PATCH v2 0/5] improve one-shot variable detection with shell function Junio C Hamano
@ 2024-07-27  5:35   ` Eric Sunshine
  2024-07-27  5:35     ` [PATCH v3 1/5] t3430: drop unnecessary one-shot "VAR=val shell-func" invocation Eric Sunshine
                       ` (4 more replies)
  6 siblings, 5 replies; 37+ messages in thread
From: Eric Sunshine @ 2024-07-27  5:35 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Rubén Justo, Phillip Wood, Kyle Lippincott,
	Eric Sunshine

From: Eric Sunshine <sunshine@sunshineco.com>

This is a reroll of [1] which improves check-non-portable-shell's
detection of one-shot environment variable assignment with shell
functions.

The only difference from v2 is that the commit messages have been
adjusted to use more accurate terminology[2]. In particular, they now
say that the behavior of one-shot variable assignment with a
shell-function is _unspecified_, not _undefined_.

[1]: https://lore.kernel.org/git/20240726081522.28015-1-ericsunshine@charter.net/
[2]: https://lore.kernel.org/git/xmqqplr0t2bo.fsf@gitster.g/

Eric Sunshine (5):
  t3430: drop unnecessary one-shot "VAR=val shell-func" invocation
  t4034: fix use of one-shot variable assignment with shell function
  check-non-portable-shell: loosen one-shot assignment error message
  check-non-portable-shell: suggest alternative for `VAR=val shell-func`
  check-non-portable-shell: improve `VAR=val shell-func` detection

 t/check-non-portable-shell.pl | 4 ++--
 t/t3430-rebase-merges.sh      | 3 +--
 t/t4034-diff-words.sh         | 2 +-
 3 files changed, 4 insertions(+), 5 deletions(-)

Range-diff against v2:
1:  0d3c0593c9 ! 1:  3bf12762a5 t3430: drop unnecessary one-shot "VAR=val shell-func" invocation
    @@ Commit message
         t3430: drop unnecessary one-shot "VAR=val shell-func" invocation
     
         The behavior of a one-shot environment variable assignment of the form
    -    "VAR=val cmd" is undefined according to POSIX when "cmd" is a shell
    +    "VAR=val cmd" is unspecified according to POSIX when "cmd" is a shell
         function. Indeed the behavior differs between shell implementations and
    -    even different versions of the same shell. One such ill-defined behavior
    +    even different versions of the same shell. One such problematic behavior
         is that, with some shells, the assignment will outlive the invocation of
         the function, thus may potentially impact subsequent commands in the
         test, as well as subsequent tests. A common way to work around the
2:  19ee8295ef ! 2:  cb77c3dc66 t4034: fix use of one-shot variable assignment with shell function
    @@ Commit message
         t4034: fix use of one-shot variable assignment with shell function
     
         The behavior of a one-shot environment variable assignment of the form
    -    "VAR=val cmd" is undefined according to POSIX when "cmd" is a shell
    +    "VAR=val cmd" is unspecified according to POSIX when "cmd" is a shell
         function. Indeed the behavior differs between shell implementations and
         even different versions of the same shell, thus should be avoided.
     
3:  220ca26d4f ! 3:  0b3716cfb3 check-non-portable-shell: loosen one-shot assignment error message
    @@ Commit message
         potential problem with one-shot assignments and shell functions. Another
         problem is that some shells do not actually export the variable to
         commands which the function invokes[1]. More significantly, however, the
    -    behavior of one-shot assignments with shell functions is considered
    -    undefined by POSIX[2].
    +    behavior of one-shot assignments with shell functions is not specified
    +    by POSIX[2].
     
         Given this new understanding, the presented error message ("assignment
         extends beyond 'shell_func'") is too specific and potentially
         misleading. Address this by emitting a less specific error message.
     
         (Note that the wording "is not portable" is chosen over the more
    -    specific "has undefined behavior according to POSIX" for consistency
    -    with almost all other error message issued by this "lint" script.)
    +    specific "behavior not specified by POSIX" for consistency with almost
    +    all other error message issued by this "lint" script.)
     
         [1]: https://lore.kernel.org/git/xmqqbk2p9lwi.fsf_-_@gitster.g/
         [2]: https://lore.kernel.org/git/xmqq34o19jj1.fsf@gitster.g/
4:  4910756aab = 4:  24ae9be947 check-non-portable-shell: suggest alternative for `VAR=val shell-func`
5:  7a15553a5a ! 5:  38cd3556c5 check-non-portable-shell: improve `VAR=val shell-func` detection
    @@ Commit message
         check-non-portable-shell: improve `VAR=val shell-func` detection
     
         The behavior of a one-shot environment variable assignment of the form
    -    "VAR=val cmd" is undefined according to POSIX when "cmd" is a shell
    +    "VAR=val cmd" is unspecified according to POSIX when "cmd" is a shell
         function. Indeed the behavior differs between shell implementations and
         even different versions of the same shell, thus should be avoided.
     
-- 
2.45.2


^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH v3 1/5] t3430: drop unnecessary one-shot "VAR=val shell-func" invocation
  2024-07-27  5:35   ` [PATCH v3 " Eric Sunshine
@ 2024-07-27  5:35     ` Eric Sunshine
  2024-07-27  5:35     ` [PATCH v3 2/5] t4034: fix use of one-shot variable assignment with shell function Eric Sunshine
                       ` (3 subsequent siblings)
  4 siblings, 0 replies; 37+ messages in thread
From: Eric Sunshine @ 2024-07-27  5:35 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Rubén Justo, Phillip Wood, Kyle Lippincott,
	Eric Sunshine

From: Eric Sunshine <sunshine@sunshineco.com>

The behavior of a one-shot environment variable assignment of the form
"VAR=val cmd" is unspecified according to POSIX when "cmd" is a shell
function. Indeed the behavior differs between shell implementations and
even different versions of the same shell. One such problematic behavior
is that, with some shells, the assignment will outlive the invocation of
the function, thus may potentially impact subsequent commands in the
test, as well as subsequent tests. A common way to work around the
problem is to wrap a subshell around the one-shot assignment, thus
ensuring that the assignment is short-lived.

In this test, the subshell is employed precisely for this purpose; other
side-effects of the subshell, such as losing the effect of `test_tick`
which is invoked by `test_commit`, are immaterial.

These days, we can take advantage of `test_commit --author` to more
clearly convey that the test is interested only in overriding the author
of the commit.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t3430-rebase-merges.sh | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index 36ca126bcd..2aa8593f77 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -392,8 +392,7 @@ test_expect_success 'refuse to merge ancestors of HEAD' '
 
 test_expect_success 'root commits' '
 	git checkout --orphan unrelated &&
-	(GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \
-	 test_commit second-root) &&
+	test_commit --author "Parsnip <root@example.com>" second-root &&
 	test_commit third-root &&
 	cat >script-from-scratch <<-\EOF &&
 	pick third-root
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v3 2/5] t4034: fix use of one-shot variable assignment with shell function
  2024-07-27  5:35   ` [PATCH v3 " Eric Sunshine
  2024-07-27  5:35     ` [PATCH v3 1/5] t3430: drop unnecessary one-shot "VAR=val shell-func" invocation Eric Sunshine
@ 2024-07-27  5:35     ` Eric Sunshine
  2024-07-27  5:35     ` [PATCH v3 3/5] check-non-portable-shell: loosen one-shot assignment error message Eric Sunshine
                       ` (2 subsequent siblings)
  4 siblings, 0 replies; 37+ messages in thread
From: Eric Sunshine @ 2024-07-27  5:35 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Rubén Justo, Phillip Wood, Kyle Lippincott,
	Eric Sunshine

From: Eric Sunshine <sunshine@sunshineco.com>

The behavior of a one-shot environment variable assignment of the form
"VAR=val cmd" is unspecified according to POSIX when "cmd" is a shell
function. Indeed the behavior differs between shell implementations and
even different versions of the same shell, thus should be avoided.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/t4034-diff-words.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh
index 74586f3813..4dcd7e9925 100755
--- a/t/t4034-diff-words.sh
+++ b/t/t4034-diff-words.sh
@@ -70,7 +70,7 @@ test_language_driver () {
 		word_diff --color-words
 	'
 	test_expect_success "diff driver '$lang' in Islandic" '
-		LANG=is_IS.UTF-8 LANGUAGE=is LC_ALL="$is_IS_locale" \
+		test_env LANG=is_IS.UTF-8 LANGUAGE=is LC_ALL="$is_IS_locale" \
 		word_diff --color-words
 	'
 }
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v3 3/5] check-non-portable-shell: loosen one-shot assignment error message
  2024-07-27  5:35   ` [PATCH v3 " Eric Sunshine
  2024-07-27  5:35     ` [PATCH v3 1/5] t3430: drop unnecessary one-shot "VAR=val shell-func" invocation Eric Sunshine
  2024-07-27  5:35     ` [PATCH v3 2/5] t4034: fix use of one-shot variable assignment with shell function Eric Sunshine
@ 2024-07-27  5:35     ` Eric Sunshine
  2024-07-27  5:35     ` [PATCH v3 4/5] check-non-portable-shell: suggest alternative for `VAR=val shell-func` Eric Sunshine
  2024-07-27  5:35     ` [PATCH v3 5/5] check-non-portable-shell: improve `VAR=val shell-func` detection Eric Sunshine
  4 siblings, 0 replies; 37+ messages in thread
From: Eric Sunshine @ 2024-07-27  5:35 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Rubén Justo, Phillip Wood, Kyle Lippincott,
	Eric Sunshine

From: Eric Sunshine <sunshine@sunshineco.com>

When a0a630192d (t/check-non-portable-shell: detect "FOO=bar
shell_func", 2018-07-13) added the check for one-shot environment
variable assignment for shell functions, the primary reason given for
avoiding them was that, under some shells, the assignment outlives the
invocation of the shell function, thus could potentially negatively
impact subsequent commands in the same test, as well as subsequent
tests.

However, it has recently become apparent that this is not the only
potential problem with one-shot assignments and shell functions. Another
problem is that some shells do not actually export the variable to
commands which the function invokes[1]. More significantly, however, the
behavior of one-shot assignments with shell functions is not specified
by POSIX[2].

Given this new understanding, the presented error message ("assignment
extends beyond 'shell_func'") is too specific and potentially
misleading. Address this by emitting a less specific error message.

(Note that the wording "is not portable" is chosen over the more
specific "behavior not specified by POSIX" for consistency with almost
all other error message issued by this "lint" script.)

[1]: https://lore.kernel.org/git/xmqqbk2p9lwi.fsf_-_@gitster.g/
[2]: https://lore.kernel.org/git/xmqq34o19jj1.fsf@gitster.g/

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/check-non-portable-shell.pl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl
index b2b28c2ced..179efaa39d 100755
--- a/t/check-non-portable-shell.pl
+++ b/t/check-non-portable-shell.pl
@@ -50,7 +50,7 @@ sub err {
 	/\blocal\s+[A-Za-z0-9_]*=\$([A-Za-z0-9_{]|[(][^(])/ and
 		err q(quote "$val" in 'local var=$val');
 	/^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
-		err '"FOO=bar shell_func" assignment extends beyond "shell_func"';
+		err '"FOO=bar shell_func" is not portable';
 	$line = '';
 	# this resets our $. for each file
 	close ARGV if eof;
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v3 4/5] check-non-portable-shell: suggest alternative for `VAR=val shell-func`
  2024-07-27  5:35   ` [PATCH v3 " Eric Sunshine
                       ` (2 preceding siblings ...)
  2024-07-27  5:35     ` [PATCH v3 3/5] check-non-portable-shell: loosen one-shot assignment error message Eric Sunshine
@ 2024-07-27  5:35     ` Eric Sunshine
  2024-07-27  5:35     ` [PATCH v3 5/5] check-non-portable-shell: improve `VAR=val shell-func` detection Eric Sunshine
  4 siblings, 0 replies; 37+ messages in thread
From: Eric Sunshine @ 2024-07-27  5:35 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Rubén Justo, Phillip Wood, Kyle Lippincott,
	Eric Sunshine

From: Eric Sunshine <sunshine@sunshineco.com>

Most problems reported by check-non-portable-shell are accompanied by
advice suggesting how the test author can repair the problem. For
instance:

    error: egrep/fgrep obsolescent (use grep -E/-F)

However, when one-shot variable assignment is detected when calling a
shell function (i.e. `VAR=val shell-func`), the problem is reported, but
no advice is given. The lack of advice is particularly egregious since
neither the problem nor the workaround are likely well-known by
newcomers to the project writing tests for the first time. Address this
shortcoming by recommending the use of `test_env` which is tailor made
for this specific use-case.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/check-non-portable-shell.pl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl
index 179efaa39d..903af14294 100755
--- a/t/check-non-portable-shell.pl
+++ b/t/check-non-portable-shell.pl
@@ -50,7 +50,7 @@ sub err {
 	/\blocal\s+[A-Za-z0-9_]*=\$([A-Za-z0-9_{]|[(][^(])/ and
 		err q(quote "$val" in 'local var=$val');
 	/^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
-		err '"FOO=bar shell_func" is not portable';
+		err '"FOO=bar shell_func" is not portable (use test_env FOO=bar shell_func)';
 	$line = '';
 	# this resets our $. for each file
 	close ARGV if eof;
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v3 5/5] check-non-portable-shell: improve `VAR=val shell-func` detection
  2024-07-27  5:35   ` [PATCH v3 " Eric Sunshine
                       ` (3 preceding siblings ...)
  2024-07-27  5:35     ` [PATCH v3 4/5] check-non-portable-shell: suggest alternative for `VAR=val shell-func` Eric Sunshine
@ 2024-07-27  5:35     ` Eric Sunshine
  4 siblings, 0 replies; 37+ messages in thread
From: Eric Sunshine @ 2024-07-27  5:35 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Rubén Justo, Phillip Wood, Kyle Lippincott,
	Eric Sunshine

From: Eric Sunshine <sunshine@sunshineco.com>

The behavior of a one-shot environment variable assignment of the form
"VAR=val cmd" is unspecified according to POSIX when "cmd" is a shell
function. Indeed the behavior differs between shell implementations and
even different versions of the same shell, thus should be avoided.

As such, check-non-portable-shell.pl warns when it detects such usage.
However, a limitation of the check is that it only detects such
invocations when variable assignment (i.e. `VAR=val`) is the first thing
on the line. Thus, it can easily be fooled by an invocation such as:

    echo X | VAR=val shell-func

Address this shortcoming by loosening the check so that the variable
assignment can be recognized even when not at the beginning of the line.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 t/check-non-portable-shell.pl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl
index 903af14294..6ee7700eb4 100755
--- a/t/check-non-portable-shell.pl
+++ b/t/check-non-portable-shell.pl
@@ -49,7 +49,7 @@ sub err {
 	/\bexport\s+[A-Za-z0-9_]*=/ and err '"export FOO=bar" is not portable (use FOO=bar && export FOO)';
 	/\blocal\s+[A-Za-z0-9_]*=\$([A-Za-z0-9_{]|[(][^(])/ and
 		err q(quote "$val" in 'local var=$val');
-	/^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
+	/\b([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and !/test_env.+=/ and exists($func{$4}) and
 		err '"FOO=bar shell_func" is not portable (use test_env FOO=bar shell_func)';
 	$line = '';
 	# this resets our $. for each file
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 37+ messages in thread

end of thread, other threads:[~2024-07-27  5:37 UTC | newest]

Thread overview: 37+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-07-22  6:59 [PATCH 0/4] improve one-shot variable detection with shell function Eric Sunshine
2024-07-22  6:59 ` [PATCH 1/4] t3430: modernize one-shot "VAR=val shell-func" invocation Eric Sunshine
2024-07-22 15:09   ` Phillip Wood
2024-07-23  9:26     ` Phillip Wood
2024-07-26  6:33       ` Eric Sunshine
2024-07-26  6:15     ` Eric Sunshine
2024-07-22 18:24   ` Junio C Hamano
2024-07-26  6:30     ` Eric Sunshine
2024-07-22  6:59 ` [PATCH 2/4] t4034: fix use of one-shot variable assignment with shell function Eric Sunshine
2024-07-22  6:59 ` [PATCH 3/4] check-non-portable-shell: improve `VAR=val shell-func` detection Eric Sunshine
2024-07-22 14:46   ` Rubén Justo
2024-07-26  6:45     ` Eric Sunshine
2024-07-26 13:15       ` Rubén Justo
2024-07-22 17:26   ` Kyle Lippincott
2024-07-22 18:10     ` Junio C Hamano
2024-07-22 21:35       ` Kyle Lippincott
2024-07-22 21:57         ` Junio C Hamano
2024-07-22  6:59 ` [PATCH 4/4] check-non-portable-shell: suggest alternative for `VAR=val shell-func` Eric Sunshine
2024-07-22 14:47   ` Rubén Justo
2024-07-22 14:50 ` [PATCH 0/4] improve one-shot variable detection with shell function Rubén Justo
2024-07-26  8:15 ` [PATCH v2 0/5] " Eric Sunshine
2024-07-26  8:15   ` [PATCH v2 1/5] t3430: drop unnecessary one-shot "VAR=val shell-func" invocation Eric Sunshine
2024-07-26 18:50     ` Junio C Hamano
2024-07-26 19:32       ` Eric Sunshine
2024-07-26  8:15   ` [PATCH v2 2/5] t4034: fix use of one-shot variable assignment with shell function Eric Sunshine
2024-07-26  8:15   ` [PATCH v2 3/5] check-non-portable-shell: loosen one-shot assignment error message Eric Sunshine
2024-07-26  8:15   ` [PATCH v2 4/5] check-non-portable-shell: suggest alternative for `VAR=val shell-func` Eric Sunshine
2024-07-26 13:11     ` Rubén Justo
2024-07-26 19:31       ` Eric Sunshine
2024-07-26  8:15   ` [PATCH v2 5/5] check-non-portable-shell: improve `VAR=val shell-func` detection Eric Sunshine
2024-07-26 18:38   ` [PATCH v2 0/5] improve one-shot variable detection with shell function Junio C Hamano
2024-07-27  5:35   ` [PATCH v3 " Eric Sunshine
2024-07-27  5:35     ` [PATCH v3 1/5] t3430: drop unnecessary one-shot "VAR=val shell-func" invocation Eric Sunshine
2024-07-27  5:35     ` [PATCH v3 2/5] t4034: fix use of one-shot variable assignment with shell function Eric Sunshine
2024-07-27  5:35     ` [PATCH v3 3/5] check-non-portable-shell: loosen one-shot assignment error message Eric Sunshine
2024-07-27  5:35     ` [PATCH v3 4/5] check-non-portable-shell: suggest alternative for `VAR=val shell-func` Eric Sunshine
2024-07-27  5:35     ` [PATCH v3 5/5] check-non-portable-shell: improve `VAR=val shell-func` detection Eric Sunshine

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).