From f348e154c7e59ab8d2dee491bd17d8fbfd46e161 Mon Sep 17 00:00:00 2001 From: Jonathan Daigle Date: Tue, 19 May 2020 22:27:14 -0400 Subject: [PATCH 1/8] Add support for ESH --- CONTRIBUTORS | 1 + Dockerfile | 3 + Makefile | 2 +- docker-compose.yml | 2 +- test/test_alt.py | 2 +- test/test_unit_template_esh.py | 114 +++++++++++++++++++++++++++++++++ yadm | 23 +++++++ yadm.1 | 15 ++++- yadm.md | 18 +++++- 9 files changed, 175 insertions(+), 5 deletions(-) create mode 100644 test/test_unit_template_esh.py diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 319b463..e9f3392 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -27,3 +27,4 @@ Patrick Hof Russ Allbery Satoshi Ohki Sheng Yang +Jonathan Daigle diff --git a/Dockerfile b/Dockerfile index 469fe54..4544bff 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,6 +36,9 @@ RUN pip3 install \ yamllint==1.17.0 \ ; +RUN curl https://raw.githubusercontent.com/jirutka/esh/v0.3.0/esh > /usr/local/bin/esh; \ + chmod +x /usr/local/bin/esh + # Create a flag to identify when running inside the yadm testbed RUN touch /.yadmtestbed diff --git a/Makefile b/Makefile index c9509a1..60210b8 100644 --- a/Makefile +++ b/Makefile @@ -90,7 +90,7 @@ $(PYTESTS): test: @if [ -f /.yadmtestbed ]; then \ cd /yadm && \ - py.test -v $(testargs); \ + py.test -vv $(testargs); \ else \ if command -v "docker-compose" &> /dev/null; then \ docker-compose run --rm testbed make test testargs="$(testargs)"; \ diff --git a/docker-compose.yml b/docker-compose.yml index 831a062..0fad313 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,4 +4,4 @@ services: testbed: volumes: - .:/yadm:ro - image: yadm/testbed:2020-01-20 + build: . diff --git a/test/test_alt.py b/test/test_alt.py index 359f32d..96c3129 100644 --- a/test/test_alt.py +++ b/test/test_alt.py @@ -126,7 +126,7 @@ def test_alt_conditions( @pytest.mark.usefixtures('ds1_copy') @pytest.mark.parametrize( - 'kind', ['default', '', None, 'envtpl', 'j2cli', 'j2']) + 'kind', ['default', '', None, 'envtpl', 'j2cli', 'j2', 'esh']) @pytest.mark.parametrize('label', ['t', 'template', 'yadm', ]) def test_alt_templates( runner, paths, kind, label): diff --git a/test/test_unit_template_esh.py b/test/test_unit_template_esh.py new file mode 100644 index 0000000..c290b5f --- /dev/null +++ b/test/test_unit_template_esh.py @@ -0,0 +1,114 @@ +"""Unit tests: template_esh""" + +LOCAL_CLASS = "esh_Test+@-!^Class" +LOCAL_SYSTEM = "esh_Test+@-!^System" +LOCAL_HOST = "esh_Test+@-!^Host" +LOCAL_USER = "esh_Test+@-!^User" +LOCAL_DISTRO = "esh_Test+@-!^Distro" +TEMPLATE = f''' +start of template +esh class = ><%=$YADM_CLASS%>< +esh os = ><%=$YADM_OS%>< +esh host = ><%=$YADM_HOSTNAME%>< +esh user = ><%=$YADM_USER%>< +esh distro = ><%=$YADM_DISTRO%>< +<% if [ "$YADM_CLASS" = "wrongclass1" ]; then -%> +wrong class 1 +<% fi -%> +<% if [ "$YADM_CLASS" = "{LOCAL_CLASS}" ]; then -%> +Included section for class = <%=$YADM_CLASS%> (<%=$YADM_CLASS%> repeated) +<% fi -%> +<% if [ "$YADM_CLASS" = "wrongclass2" ]; then -%> +wrong class 2 +<% fi -%> +<% if [ "$YADM_OS" = "wrongos1" ]; then -%> +wrong os 1 +<% fi -%> +<% if [ "$YADM_OS" = "{LOCAL_SYSTEM}" ]; then -%> +Included section for os = <%=$YADM_OS%> (<%=$YADM_OS%> repeated) +<% fi -%> +<% if [ "$YADM_OS" = "wrongos2" ]; then -%> +wrong os 2 +<% fi -%> +<% if [ "$YADM_HOSTNAME" = "wronghost1" ]; then -%> +wrong host 1 +<% fi -%> +<% if [ "$YADM_HOSTNAME" = "{LOCAL_HOST}" ]; then -%> +Included section for host = <%=$YADM_HOSTNAME%> (<%=$YADM_HOSTNAME%> again) +<% fi -%> +<% if [ "$YADM_HOSTNAME" = "wronghost2" ]; then -%> +wrong host 2 +<% fi -%> +<% if [ "$YADM_USER" = "wronguser1" ]; then -%> +wrong user 1 +<% fi -%> +<% if [ "$YADM_USER" = "{LOCAL_USER}" ]; then -%> +Included section for user = <%=$YADM_USER%> (<%=$YADM_USER%> repeated) +<% fi -%> +<% if [ "$YADM_USER" = "wronguser2" ]; then -%> +wrong user 2 +<% fi -%> +<% if [ "$YADM_DISTRO" = "wrongdistro1" ]; then -%> +wrong distro 1 +<% fi -%> +<% if [ "$YADM_DISTRO" = "{LOCAL_DISTRO}" ]; then -%> +Included section for distro = <%=$YADM_DISTRO%> (<%=$YADM_DISTRO%> again) +<% fi -%> +<% if [ "$YADM_DISTRO" = "wrongdistro2" ]; then -%> +wrong distro 2 +<% fi -%> +end of template +''' +EXPECTED = f''' +start of template +esh class = >{LOCAL_CLASS}< +esh os = >{LOCAL_SYSTEM}< +esh host = >{LOCAL_HOST}< +esh user = >{LOCAL_USER}< +esh distro = >{LOCAL_DISTRO}< +Included section for class = {LOCAL_CLASS} ({LOCAL_CLASS} repeated) +Included section for os = {LOCAL_SYSTEM} ({LOCAL_SYSTEM} repeated) +Included section for host = {LOCAL_HOST} ({LOCAL_HOST} again) +Included section for user = {LOCAL_USER} ({LOCAL_USER} repeated) +Included section for distro = {LOCAL_DISTRO} ({LOCAL_DISTRO} again) +end of template +''' + + +def test_template_esh(runner, yadm, tmpdir): + """Test processing by esh""" + + input_file = tmpdir.join('input') + input_file.write(TEMPLATE, ensure=True) + output_file = tmpdir.join('output') + + script = f""" + YADM_TEST=1 source {yadm} + local_class="{LOCAL_CLASS}" + local_system="{LOCAL_SYSTEM}" + local_host="{LOCAL_HOST}" + local_user="{LOCAL_USER}" + local_distro="{LOCAL_DISTRO}" + template_esh "{input_file}" "{output_file}" + """ + run = runner(command=['bash'], inp=script) + assert run.success + assert run.err == '' + assert output_file.read().strip() == str(EXPECTED).strip() + + +def test_source(runner, yadm, tmpdir): + """Test YADM_SOURCE""" + + input_file = tmpdir.join('input') + input_file.write('<%= $YADM_SOURCE %>', ensure=True) + output_file = tmpdir.join('output') + + script = f""" + YADM_TEST=1 source {yadm} + template_esh "{input_file}" "{output_file}" + """ + run = runner(command=['bash'], inp=script) + assert run.success + assert run.err == '' + assert output_file.read().strip() == str(input_file) diff --git a/yadm b/yadm index f3a2343..cad4b8e 100755 --- a/yadm +++ b/yadm @@ -45,6 +45,7 @@ GIT_CRYPT_PROGRAM="git-crypt" TRANSCRYPT_PROGRAM="transcrypt" J2CLI_PROGRAM="j2" ENVTPL_PROGRAM="envtpl" +ESH_PROGRAM="esh" LSB_RELEASE_PROGRAM="lsb_release" OS_RELEASE="/etc/os-release" @@ -299,6 +300,8 @@ function choose_template_cmd() { if [ "$kind" = "default" ] || [ "$kind" = "" ] && awk_available; then echo "template_default" + elif [ "$kind" = "esh" ] && esh_available; then + echo "template_esh" elif [ "$kind" = "j2cli" ] || [ "$kind" = "j2" ] && j2cli_available; then echo "template_j2cli" elif [ "$kind" = "envtpl" ] || [ "$kind" = "j2" ] && envtpl_available; then @@ -407,6 +410,22 @@ function template_envtpl() { [ -f "$temp_file" ] && mv -f "$temp_file" "$output" } +function template_esh() { + input="$1" + output="$2" + temp_file="${output}.$$.$RANDOM" + + "$ESH_PROGRAM" -o "$temp_file" "$input" \ + YADM_CLASS="$local_class" \ + YADM_OS="$local_system" \ + YADM_HOSTNAME="$local_host" \ + YADM_USER="$local_user" \ + YADM_DISTRO="$local_distro" \ + YADM_SOURCE="$input" + + [ -f "$temp_file" ] && mv -f "$temp_file" "$output" +} + # ****** yadm Commands ****** function alt() { @@ -1936,6 +1955,10 @@ function envtpl_available() { command -v "$ENVTPL_PROGRAM" &> /dev/null && return return 1 } +function esh_available() { + command -v "$ESH_PROGRAM" &> /dev/null && return + return 1 +} function readlink_available() { command -v "readlink" &> /dev/null && return return 1 diff --git a/yadm.1 b/yadm.1 index 49b200c..2fddcc3 100644 --- a/yadm.1 +++ b/yadm.1 @@ -637,6 +637,9 @@ upon which is available on most *nix systems. To use this processor, specify the value of "default" or just leave the value off (e.g. "##template"). .TP +.B esh +To use the esh template processor, specify the value of "esh" +.TP .B j2cli To use the j2cli Jinja template processor, specify the value of "j2" or "j2cli". @@ -654,7 +657,7 @@ to create or overwrite files. During processing, the following variables are available in the template: - Default Jinja Description + Default Jinja & Esh Description ------------- ------------- -------------------------- yadm.class YADM_CLASS Locally defined yadm class yadm.distro YADM_DISTRO lsb_release -si @@ -702,6 +705,16 @@ would look like: config=dev-whatever {% endif -%} +And an equivalent esh named +.I whatever##template.esh +would look like: + + <% if [ "$YADM_USER" = "harvey" ]; then -%> + config=<%= $YADM_CLASS %>-<%= $YADM_OS %> + <% else -%> + config=dev-whatever + <% fi -%> + .SH ENCRYPTION It can be useful to manage confidential files, like SSH or GPG keys, across diff --git a/yadm.md b/yadm.md index 425a226..01efaa7 100644 --- a/yadm.md +++ b/yadm.md @@ -495,6 +495,15 @@ most *nix systems. To use this processor, specify the value of "default" or just leave the value off (e.g. "##template"). + esh + ESH is a super light template processor written in POSIX + compliant shell. With no other dependencies than awk. It should + therefore run on any system. It can be "installed" by simply + downloading the file. Also one of the great features of esh is + that you can execute shell commands from inside templates. This + can be verry usefull to use your own configuration variables + from inside templates, like this `<% yadm config my-config %>` + j2cli To use the j2cli Jinja template processor, specify the value of "j2" or "j2cli". @@ -511,7 +520,7 @@ During processing, the following variables are available in the tem- plate: - Default Jinja Description + Default Jinja & Esh Description ------------- ------------- -------------------------- yadm.class YADM_CLASS Locally defined yadm class yadm.distro YADM_DISTRO lsb_release -si @@ -554,6 +563,13 @@ config=dev-whatever {% endif -%} + And an equivalent esh namedwhatever##template.esh would look like: + + <% if [ "$YADM_USER" = "harvey" ]; then -%> + config=<%= $YADM_CLASS %>-<%= $YADM_OS %> + <% else -%> + config=dev-whatever + <% fi -%> ## ENCRYPTION It can be useful to manage confidential files, like SSH or GPG keys, From bea6e5506ab3091d1198edff5e9b61d68ee7e4b7 Mon Sep 17 00:00:00 2001 From: Jonathan Daigle Date: Thu, 21 May 2020 00:40:54 -0400 Subject: [PATCH 2/8] Process config alt before any other. This allows to version the configuration, using alt and to still be able to use those configuration values in esh template (using <% yamd config some-config %> ) --- yadm | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/yadm b/yadm index cad4b8e..58a7418 100755 --- a/yadm +++ b/yadm @@ -251,11 +251,19 @@ function record_score() { done # if we don't find an existing index, create one by appending to the array if [ "$index" -eq -1 ]; then - alt_targets+=("$tgt") - # set index to the last index (newly created one) - for index in "${!alt_targets[@]}"; do :; done - # and set its initial score to zero - alt_scores[$index]=0 + # The configuration must be the first alt of the list. + # This ensures that if any templates uses "yadm config some-confid", the config will be available. + if [ "$tgt" = "$YADM_DIR/.config/yadm/config" ]; then + alt_targets=("$tgt" "${alt_targets[@]}") + alt_scores=(0 "${alt_scores[@]}") + index=0 + else + alt_targets+=("$tgt") + # set index to the last index (newly created one) + for index in "${!alt_targets[@]}"; do :; done + # and set its initial score to zero + alt_scores[$index]=0 + fi fi # record nothing if a template command is registered for this file From 51752233e9f006bf8662d4f2f07854c754b4739c Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Wed, 8 Jul 2020 01:48:37 -0500 Subject: [PATCH 3/8] Revert yadm.md & CONTRIBUTORS yadm.md is generated from yadm.1 during release. CONTRIBUTORS is generated from Git history during release. --- CONTRIBUTORS | 1 - yadm.md | 18 +----------------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index e9f3392..319b463 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -27,4 +27,3 @@ Patrick Hof Russ Allbery Satoshi Ohki Sheng Yang -Jonathan Daigle diff --git a/yadm.md b/yadm.md index 01efaa7..425a226 100644 --- a/yadm.md +++ b/yadm.md @@ -495,15 +495,6 @@ most *nix systems. To use this processor, specify the value of "default" or just leave the value off (e.g. "##template"). - esh - ESH is a super light template processor written in POSIX - compliant shell. With no other dependencies than awk. It should - therefore run on any system. It can be "installed" by simply - downloading the file. Also one of the great features of esh is - that you can execute shell commands from inside templates. This - can be verry usefull to use your own configuration variables - from inside templates, like this `<% yadm config my-config %>` - j2cli To use the j2cli Jinja template processor, specify the value of "j2" or "j2cli". @@ -520,7 +511,7 @@ During processing, the following variables are available in the tem- plate: - Default Jinja & Esh Description + Default Jinja Description ------------- ------------- -------------------------- yadm.class YADM_CLASS Locally defined yadm class yadm.distro YADM_DISTRO lsb_release -si @@ -563,13 +554,6 @@ config=dev-whatever {% endif -%} - And an equivalent esh namedwhatever##template.esh would look like: - - <% if [ "$YADM_USER" = "harvey" ]; then -%> - config=<%= $YADM_CLASS %>-<%= $YADM_OS %> - <% else -%> - config=dev-whatever - <% fi -%> ## ENCRYPTION It can be useful to manage confidential files, like SSH or GPG keys, From 71ecf272917a414713dfdd9e059f1ad07fd0e44c Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Wed, 8 Jul 2020 00:58:54 -0500 Subject: [PATCH 4/8] Update testbed docker image --- .travis.yml | 4 ++-- Dockerfile | 5 +++-- Makefile | 7 ++++--- docker-compose.yml | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index e340cd2..5683e96 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,6 @@ language: minimal services: - docker before_install: - - docker pull yadm/testbed:2020-01-20 + - docker pull yadm/testbed:2020-07-08 script: - - docker run -t --rm -v "$PWD:/yadm:ro" yadm/testbed:2020-01-20 + - docker run -t --rm -v "$PWD:/yadm:ro" yadm/testbed:2020-07-08 diff --git a/Dockerfile b/Dockerfile index 4544bff..f686108 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,8 +36,9 @@ RUN pip3 install \ yamllint==1.17.0 \ ; -RUN curl https://raw.githubusercontent.com/jirutka/esh/v0.3.0/esh > /usr/local/bin/esh; \ - chmod +x /usr/local/bin/esh +RUN \ + curl https://raw.githubusercontent.com/jirutka/esh/v0.3.0/esh > /usr/local/bin/esh && \ + chmod +x /usr/local/bin/esh # Create a flag to identify when running inside the yadm testbed RUN touch /.yadmtestbed diff --git a/Makefile b/Makefile index 60210b8..e7895be 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ PYTESTS = $(wildcard test/test_*.py) +IMAGE = yadm/testbed:2020-07-08 .PHONY: all all: @@ -90,7 +91,7 @@ $(PYTESTS): test: @if [ -f /.yadmtestbed ]; then \ cd /yadm && \ - py.test -vv $(testargs); \ + py.test -v $(testargs); \ else \ if command -v "docker-compose" &> /dev/null; then \ docker-compose run --rm testbed make test testargs="$(testargs)"; \ @@ -112,7 +113,7 @@ testhost: require-docker --hostname testhost \ --rm -it \ -v "/tmp/testhost:/bin/yadm:ro" \ - yadm/testbed:2020-01-20 \ + $(IMAGE) \ bash -l .PHONY: scripthost @@ -129,7 +130,7 @@ scripthost: require-docker --rm -it \ -v "$$PWD/script.gz:/script.gz:rw" \ -v "/tmp/testhost:/bin/yadm:ro" \ - yadm/testbed:2020-01-20 \ + $(IMAGE) \ bash -c "script /tmp/script -q -c 'bash -l'; gzip < /tmp/script > /script.gz" @echo @echo "Script saved to $$PWD/script.gz" diff --git a/docker-compose.yml b/docker-compose.yml index 0fad313..60458d5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,4 +4,4 @@ services: testbed: volumes: - .:/yadm:ro - build: . + image: yadm/testbed:2020-07-08 From 7f4894293467203dbe5d15088cc034612c626bab Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Wed, 8 Jul 2020 01:45:33 -0500 Subject: [PATCH 5/8] Update man page for ESH --- yadm.1 | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/yadm.1 b/yadm.1 index 1c18ef8..37dfacc 100644 --- a/yadm.1 +++ b/yadm.1 @@ -637,8 +637,14 @@ upon which is available on most *nix systems. To use this processor, specify the value of "default" or just leave the value off (e.g. "##template"). .TP -.B esh -To use the esh template processor, specify the value of "esh" +.B ESH +ESH is a template processor written in POSIX compliant shell. It allows +executing shell commands within templates. This can be used to reference your +own configurations within templates, for example: + + <% yadm config mysection.myconfig %> + +To use the ESH template processor, specify the value of "esh" .TP .B j2cli To use the j2cli Jinja template processor, specify the value of "j2" or @@ -657,7 +663,7 @@ to create or overwrite files. During processing, the following variables are available in the template: - Default Jinja & Esh Description + Default Jinja or ESH Description ------------- ------------- -------------------------- yadm.class YADM_CLASS Locally defined yadm class yadm.distro YADM_DISTRO lsb_release -si @@ -705,7 +711,7 @@ would look like: config=dev-whatever {% endif -%} -And an equivalent esh named +An equivalent ESH templated named .I whatever##template.esh would look like: From b056051603654d5ea6a6791e2778b57a0c1d1683 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Wed, 8 Jul 2020 02:03:52 -0500 Subject: [PATCH 6/8] Identify yadm's configuration using $YADM_CONFIG --- yadm | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/yadm b/yadm index 58a7418..57219c8 100755 --- a/yadm +++ b/yadm @@ -251,9 +251,8 @@ function record_score() { done # if we don't find an existing index, create one by appending to the array if [ "$index" -eq -1 ]; then - # The configuration must be the first alt of the list. - # This ensures that if any templates uses "yadm config some-confid", the config will be available. - if [ "$tgt" = "$YADM_DIR/.config/yadm/config" ]; then + # $YADM_CONFIG must be processed first, in case other templates lookup yadm configurations + if [ "$tgt" = "$YADM_CONFIG" ]; then alt_targets=("$tgt" "${alt_targets[@]}") alt_scores=(0 "${alt_scores[@]}") index=0 From 87f81143b217e444eca7c00a0c23a0eae0f6f863 Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Wed, 8 Jul 2020 02:40:13 -0500 Subject: [PATCH 7/8] Add unit test for alternate order Ensure that any alternates for YADM_CONFIG are processed prior to templates. --- test/test_unit_record_score.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/test/test_unit_record_score.py b/test/test_unit_record_score.py index 525c967..78596e1 100644 --- a/test/test_unit_record_score.py +++ b/test/test_unit_record_score.py @@ -112,3 +112,30 @@ def test_existing_template(runner, yadm): assert 'SCORES:1\n' in run.out assert 'TARGETS:testtgt\n' in run.out assert 'SOURCES:\n' in run.out + + +def test_config_first(runner, yadm): + """Verify YADM_CONFIG is always processed first""" + + config = 'yadm_config_file' + script = f""" + YADM_TEST=1 source {yadm} + {INIT_VARS} + YADM_CONFIG={config} + record_score "1" "tgt_before" "src_before" + record_template "tgt_tmp" "cmd_tmp" "src_tmp" + record_score "2" "{config}" "src_config" + record_score "3" "tgt_after" "src_after" + {REPORT_RESULTS} + echo "CMD_VALUE:${{alt_template_cmds[@]}}" + echo "CMD_INDEX:${{!alt_template_cmds[@]}}" + """ + run = runner(command=['bash'], inp=script) + assert run.success + assert run.err == '' + assert 'SIZE:3\n' in run.out + assert 'SCORES:2 1 3\n' in run.out + assert f'TARGETS:{config} tgt_before tgt_tmp tgt_after\n' in run.out + assert 'SOURCES:src_config src_before src_tmp src_after\n' in run.out + assert 'CMD_VALUE:cmd_tmp\n' in run.out + assert 'CMD_INDEX:2\n' in run.out From ed4a4a5fbdaaa022ed8606f650bd01038d0aa6fe Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Wed, 8 Jul 2020 02:41:49 -0500 Subject: [PATCH 8/8] Fix bug with out-of-sync sources The indexes of targets, scores, sources, etc. must be kept in sync. --- yadm | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/yadm b/yadm index 57219c8..af5a3bd 100755 --- a/yadm +++ b/yadm @@ -254,8 +254,18 @@ function record_score() { # $YADM_CONFIG must be processed first, in case other templates lookup yadm configurations if [ "$tgt" = "$YADM_CONFIG" ]; then alt_targets=("$tgt" "${alt_targets[@]}") + alt_sources=("$src" "${alt_sources[@]}") alt_scores=(0 "${alt_scores[@]}") index=0 + # increase the index of any existing alt_template_cmds + new_cmds=() + for cmd_index in "${!alt_template_cmds[@]}"; do + new_cmds[$((cmd_index+1))]="${alt_template_cmds[$cmd_index]}" + done + alt_template_cmds=() + for cmd_index in "${!new_cmds[@]}"; do + alt_template_cmds[$cmd_index]="${new_cmds[$cmd_index]}" + done else alt_targets+=("$tgt") # set index to the last index (newly created one)