From fe64411461f52f354ddea30c0d6d2a41e8020183 Mon Sep 17 00:00:00 2001 From: joao-paulo-parity Date: Tue, 15 Nov 2022 11:02:08 -0300 Subject: [PATCH 01/11] check all crates individually It's relevant to check workspace crates individually because otherwise their compilation problems due to feature misconfigurations won't be caught, as exemplified by https://github.com/paritytech/substrate/issues/12705 --- .gitlab-ci.yml | 21 ++++++++-- frame/contracts/proc-macro/Cargo.toml | 2 +- scripts/ci/gitlab/check-each-crate.sh | 33 +++++++++++++++ scripts/ci/gitlab/pipeline/build.yml | 3 -- scripts/ci/gitlab/pipeline/test.yml | 60 ++++++++++++++------------- 5 files changed, 84 insertions(+), 35 deletions(-) create mode 100755 scripts/ci/gitlab/check-each-crate.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 75a1d54776eb6..5d94fce852ed2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -136,7 +136,7 @@ default: # exclude cargo-check-benches from such runs .test-refs-check-benches: rules: - - if: $CI_COMMIT_REF_NAME == "master" && $CI_PIPELINE_SOURCE == "parent_pipeline" && $CI_IMAGE =~ /staging$/ + - if: $CI_COMMIT_REF_NAME == "master" && $CI_PIPELINE_SOURCE == "parent_pipeline" && $CI_IMAGE =~ /staging$/ when: never - if: $CI_PIPELINE_SOURCE == "web" - if: $CI_PIPELINE_SOURCE == "schedule" @@ -313,10 +313,25 @@ cancel-pipeline-test-linux-stable-int: needs: - job: test-linux-stable-int -cancel-pipeline-cargo-check-subkey: +cancel-pipeline-cargo-check-each-crate-1: extends: .cancel-pipeline-template needs: - - job: cargo-check-subkey + - job: "cargo-check-each-crate 1/2" + +cancel-pipeline-cargo-check-each-crate-2: + extends: .cancel-pipeline-template + needs: + - job: "cargo-check-each-crate 2/2" + +cancel-pipeline-cargo-check-each-crate-macos-1: + extends: .cancel-pipeline-template + needs: + - job: "cargo-check-each-crate-macos 1/2" + +cancel-pipeline-cargo-check-each-crate-macos-2: + extends: .cancel-pipeline-template + needs: + - job: "cargo-check-each-crate-macos 2/2" cancel-pipeline-check-tracing: extends: .cancel-pipeline-template diff --git a/frame/contracts/proc-macro/Cargo.toml b/frame/contracts/proc-macro/Cargo.toml index aca1d86ac24a1..08dafcc6a8fca 100644 --- a/frame/contracts/proc-macro/Cargo.toml +++ b/frame/contracts/proc-macro/Cargo.toml @@ -17,7 +17,7 @@ proc-macro = true [dependencies] proc-macro2 = "1" quote = "1" -syn = "1.0.98" +syn = { version = "1.0.98", features = ["full"] } [dev-dependencies] diff --git a/scripts/ci/gitlab/check-each-crate.sh b/scripts/ci/gitlab/check-each-crate.sh new file mode 100755 index 0000000000000..51c4ae7bb931f --- /dev/null +++ b/scripts/ci/gitlab/check-each-crate.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +## A script that checks each workspace crate individually. +## It's relevant to check workspace crates individually because otherwise their compilation problems +## due to feature misconfigurations won't be caught, as exemplified by +## https://github.com/paritytech/substrate/issues/12705 + +set -Eeu -o pipefail +shopt -s inherit_errexit + +set -x + +target_group="$1" +groups_total="$2" + +readarray -t workspace_crates < <(\ + cargo tree --workspace --depth 0 | \ + awk '{ if (length($1) == 0 || substr($1, 1, 1) == "[") { skip } else { print $1 } }' +) + +crates_total=${#workspace_crates[*]} +crates_per_group=$(( (crates_total / groups_total) + (crates_total % groups_total > 0) )) + +group=1 +for ((i=0; i < crates_total; i += crates_per_group)); do + if [ $group -eq "$target_group" ]; then + for crate in "${workspace_crates[@]:$i:$crates_per_group}"; do + cargo check --locked --release -p "$crate" + done + break + fi + group=$(( group + 1 )) +done diff --git a/scripts/ci/gitlab/pipeline/build.yml b/scripts/ci/gitlab/pipeline/build.yml index 2e403c10d4520..906c1bcbe242e 100644 --- a/scripts/ci/gitlab/pipeline/build.yml +++ b/scripts/ci/gitlab/pipeline/build.yml @@ -90,9 +90,6 @@ build-linux-substrate: variables: # this variable gets overriden by "rusty-cachier environment inject", use the value as default CARGO_TARGET_DIR: "$CI_PROJECT_DIR/target" - needs: - - job: cargo-check-subkey - artifacts: false before_script: - mkdir -p ./artifacts/subkey - !reference [.rusty-cachier, before_script] diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index 710fbe6227f46..06ff483b46649 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -133,24 +133,8 @@ node-bench-regression-guard: --compare-with artifacts/benches/$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA' after_script: [""] -cargo-check-subkey: - stage: test - extends: - - .docker-env - - .test-refs - - .pipeline-stopper-artifacts - script: - - rusty-cachier snapshot create - - cd ./bin/utils/subkey - - SKIP_WASM_BUILD=1 time cargo check --locked --release - - rusty-cachier cache upload - cargo-check-try-runtime: stage: test - # this is an artificial job dependency, for pipeline optimization using GitLab's DAGs - needs: - - job: cargo-check-subkey - artifacts: false extends: - .docker-env - .test-refs @@ -393,6 +377,9 @@ test-full-crypto-feature: test-wasmer-sandbox: stage: test + needs: + - job: cargo-check-wasmer-sandbox + artifacts: false extends: - .docker-env - .test-refs-wasmer-sandbox @@ -409,18 +396,6 @@ test-wasmer-sandbox: - time cargo nextest run --locked --release --features runtime-benchmarks,wasmer-sandbox,disable-ui-tests --partition count:${CI_NODE_INDEX}/${CI_NODE_TOTAL} - if [ ${CI_NODE_INDEX} == 1 ]; then rusty-cachier cache upload; fi -cargo-check-macos: - stage: test - extends: .test-refs-no-trigger - before_script: - - !reference [.rust-info-script, script] - variables: - SKIP_WASM_BUILD: 1 - script: - - time cargo check --locked --release - tags: - - osx - check-rustdoc: stage: test variables: @@ -435,3 +410,32 @@ check-rustdoc: - rusty-cachier snapshot create - time cargo +nightly doc --locked --workspace --all-features --verbose --no-deps - rusty-cachier cache upload + +.cargo-check-each-crate_template: &cargo-check-each-crate_template + stage: test + extends: + - .docker-env + - .test-refs + parallel: 2 + +cargo-check-each-crate: + <<: *cargo-check-each-crate_template + variables: + # $CI_JOB_NAME is set manually so that rusty-cachier can share the cache for all + # "cargo-check-each-crate I/N" jobs + CI_JOB_NAME: cargo-check-each-crate + before_script: + - !reference [.rust-info-script, script] + - !reference [.rusty-cachier, before_script] + script: + - rusty-cachier snapshot create + - ./scripts/ci/gitlab/check-each-crate.sh "$CI_NODE_INDEX" "$CI_NODE_TOTAL" + # need to update cache only from one job + - if [ "$CI_NODE_INDEX" == 1 ]; then rusty-cachier cache upload; fi + +cargo-check-each-crate-macos: + <<: *cargo-check-each-crate_template + script: + - ./scripts/ci/gitlab/check-each-crate.sh "$CI_NODE_INDEX" "$CI_NODE_TOTAL" + tags: + - osx From 5b99f73988cdca479e65e4b56bb558ecb488d411 Mon Sep 17 00:00:00 2001 From: joao-paulo-parity Date: Tue, 15 Nov 2022 11:48:20 -0300 Subject: [PATCH 02/11] adapt to lack of multiple macos runners https://github.com/paritytech/substrate/pull/12709#discussion_r1022868752 --- scripts/ci/gitlab/pipeline/test.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index 06ff483b46649..c5d26b7cd94fd 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -416,7 +416,6 @@ check-rustdoc: extends: - .docker-env - .test-refs - parallel: 2 cargo-check-each-crate: <<: *cargo-check-each-crate_template @@ -432,10 +431,13 @@ cargo-check-each-crate: - ./scripts/ci/gitlab/check-each-crate.sh "$CI_NODE_INDEX" "$CI_NODE_TOTAL" # need to update cache only from one job - if [ "$CI_NODE_INDEX" == 1 ]; then rusty-cachier cache upload; fi + parallel: 2 cargo-check-each-crate-macos: <<: *cargo-check-each-crate_template script: - - ./scripts/ci/gitlab/check-each-crate.sh "$CI_NODE_INDEX" "$CI_NODE_TOTAL" + # TODO: enable rusty-cachier once it supports Mac + # TODO: use parallel jobs, as per cargo-check-each-crate, once more Mac runners are available + - ./scripts/ci/gitlab/check-each-crate.sh 1 1 tags: - osx From c2096d4c8aad5421847f656a3673176d408b4bca Mon Sep 17 00:00:00 2001 From: joao-paulo-parity Date: Tue, 15 Nov 2022 11:49:22 -0300 Subject: [PATCH 03/11] fix cancel-pipeline-cargo-check-each-crate-macos --- .gitlab-ci.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5d94fce852ed2..c14708bfcb888 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -323,15 +323,10 @@ cancel-pipeline-cargo-check-each-crate-2: needs: - job: "cargo-check-each-crate 2/2" -cancel-pipeline-cargo-check-each-crate-macos-1: +cancel-pipeline-cargo-check-each-crate-macos: extends: .cancel-pipeline-template needs: - - job: "cargo-check-each-crate-macos 1/2" - -cancel-pipeline-cargo-check-each-crate-macos-2: - extends: .cancel-pipeline-template - needs: - - job: "cargo-check-each-crate-macos 2/2" + - job: cargo-check-each-crate-macos cancel-pipeline-check-tracing: extends: .cancel-pipeline-template From 17e932a1da9d938450e5a41ab50eeece456e27e5 Mon Sep 17 00:00:00 2001 From: joao-paulo-parity Date: Tue, 15 Nov 2022 12:56:15 -0300 Subject: [PATCH 04/11] fix cargo-check-each-crate-macos again --- scripts/ci/gitlab/pipeline/test.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index c5d26b7cd94fd..0ed30be7cca1b 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -411,14 +411,11 @@ check-rustdoc: - time cargo +nightly doc --locked --workspace --all-features --verbose --no-deps - rusty-cachier cache upload -.cargo-check-each-crate_template: &cargo-check-each-crate_template +cargo-check-each-crate: stage: test extends: - .docker-env - .test-refs - -cargo-check-each-crate: - <<: *cargo-check-each-crate_template variables: # $CI_JOB_NAME is set manually so that rusty-cachier can share the cache for all # "cargo-check-each-crate I/N" jobs @@ -434,7 +431,12 @@ cargo-check-each-crate: parallel: 2 cargo-check-each-crate-macos: - <<: *cargo-check-each-crate_template + stage: test + extends: .test-refs + before_script: + - !reference [.rust-info-script, script] + variables: + SKIP_WASM_BUILD: 1 script: # TODO: enable rusty-cachier once it supports Mac # TODO: use parallel jobs, as per cargo-check-each-crate, once more Mac runners are available From bc535e4a4aaec730673f92d6f116b83aa8fcd6ef Mon Sep 17 00:00:00 2001 From: joao-paulo-parity Date: Tue, 15 Nov 2022 12:56:45 -0300 Subject: [PATCH 05/11] time command execution --- scripts/ci/gitlab/pipeline/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index 0ed30be7cca1b..c19e4b0c00bfc 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -425,7 +425,7 @@ cargo-check-each-crate: - !reference [.rusty-cachier, before_script] script: - rusty-cachier snapshot create - - ./scripts/ci/gitlab/check-each-crate.sh "$CI_NODE_INDEX" "$CI_NODE_TOTAL" + - time ./scripts/ci/gitlab/check-each-crate.sh "$CI_NODE_INDEX" "$CI_NODE_TOTAL" # need to update cache only from one job - if [ "$CI_NODE_INDEX" == 1 ]; then rusty-cachier cache upload; fi parallel: 2 @@ -440,6 +440,6 @@ cargo-check-each-crate-macos: script: # TODO: enable rusty-cachier once it supports Mac # TODO: use parallel jobs, as per cargo-check-each-crate, once more Mac runners are available - - ./scripts/ci/gitlab/check-each-crate.sh 1 1 + - time ./scripts/ci/gitlab/check-each-crate.sh 1 1 tags: - osx From 9f6a5640b513615b5f52a9a04e4ba5cb02b16b4b Mon Sep 17 00:00:00 2001 From: joao-paulo-parity Date: Tue, 15 Nov 2022 12:58:33 -0300 Subject: [PATCH 06/11] fix YAML anchors --- scripts/ci/gitlab/pipeline/test.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index c19e4b0c00bfc..5876f9ad5dcee 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -420,9 +420,6 @@ cargo-check-each-crate: # $CI_JOB_NAME is set manually so that rusty-cachier can share the cache for all # "cargo-check-each-crate I/N" jobs CI_JOB_NAME: cargo-check-each-crate - before_script: - - !reference [.rust-info-script, script] - - !reference [.rusty-cachier, before_script] script: - rusty-cachier snapshot create - time ./scripts/ci/gitlab/check-each-crate.sh "$CI_NODE_INDEX" "$CI_NODE_TOTAL" @@ -435,6 +432,7 @@ cargo-check-each-crate-macos: extends: .test-refs before_script: - !reference [.rust-info-script, script] + - !reference [.pipeline-stopper-vars, script] variables: SKIP_WASM_BUILD: 1 script: From 53f28d59170dbe6f185389bfb0bb64dc0f13866a Mon Sep 17 00:00:00 2001 From: joao-paulo-parity Date: Wed, 16 Nov 2022 08:31:20 -0300 Subject: [PATCH 07/11] add explanation for rounding division --- scripts/ci/gitlab/check-each-crate.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/ci/gitlab/check-each-crate.sh b/scripts/ci/gitlab/check-each-crate.sh index 51c4ae7bb931f..1838fd95d24d9 100755 --- a/scripts/ci/gitlab/check-each-crate.sh +++ b/scripts/ci/gitlab/check-each-crate.sh @@ -19,6 +19,14 @@ readarray -t workspace_crates < <(\ ) crates_total=${#workspace_crates[*]} + +# We add `crates_total % groups_total > 0` (which evaluates to 1 in case there's a remainder for +# `crates_total % groups_total`) to round UP `crates_total / groups_total` 's potentially-fractional +# result to the nearest integer. Doing that ensures that we'll not miss any crate in case +# `crates_total / groups_total` would normally result in a fractional number, since in those cases +# Bash would round DOWN the result to the nearest integer. For example, if `crates_total = 5` and +# `groups_total = 2`, then `crates_total / groups_total` would round down to 2; since the loop below +# would then step by 2, we'd miss the 5th crate. crates_per_group=$(( (crates_total / groups_total) + (crates_total % groups_total > 0) )) group=1 From 066103e8232c8751a0af09f5600f9afcce8ac891 Mon Sep 17 00:00:00 2001 From: joao-paulo-parity Date: Wed, 16 Nov 2022 08:42:17 -0300 Subject: [PATCH 08/11] ensure the minimum of one crate per group --- scripts/ci/gitlab/check-each-crate.sh | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/scripts/ci/gitlab/check-each-crate.sh b/scripts/ci/gitlab/check-each-crate.sh index 1838fd95d24d9..a5f2e48caa2d1 100755 --- a/scripts/ci/gitlab/check-each-crate.sh +++ b/scripts/ci/gitlab/check-each-crate.sh @@ -20,14 +20,18 @@ readarray -t workspace_crates < <(\ crates_total=${#workspace_crates[*]} -# We add `crates_total % groups_total > 0` (which evaluates to 1 in case there's a remainder for -# `crates_total % groups_total`) to round UP `crates_total / groups_total` 's potentially-fractional -# result to the nearest integer. Doing that ensures that we'll not miss any crate in case -# `crates_total / groups_total` would normally result in a fractional number, since in those cases -# Bash would round DOWN the result to the nearest integer. For example, if `crates_total = 5` and -# `groups_total = 2`, then `crates_total / groups_total` would round down to 2; since the loop below -# would then step by 2, we'd miss the 5th crate. -crates_per_group=$(( (crates_total / groups_total) + (crates_total % groups_total > 0) )) +if [ "$crates_total" -le "$groups_total" ]; then + crates_per_group=1 +else + # We add `crates_total % groups_total > 0` (which evaluates to 1 in case there's a remainder for + # `crates_total % groups_total`) to round UP `crates_total / groups_total` 's + # potentially-fractional result to the nearest integer. Doing that ensures that we'll not miss any + # crate in case `crates_total / groups_total` would normally result in a fractional number, since + # in those cases Bash would round DOWN the result to the nearest integer. For example, if + # `crates_total = 5` and `groups_total = 2`, then `crates_total / groups_total` would round down + # to 2; since the loop below would then step by 2, we'd miss the 5th crate. + crates_per_group=$(( (crates_total / groups_total) + (crates_total % groups_total > 0) )) +fi group=1 for ((i=0; i < crates_total; i += crates_per_group)); do From 3452d1628b45e5b647fe847deb47121faea1ffa4 Mon Sep 17 00:00:00 2001 From: joao-paulo-parity Date: Wed, 16 Nov 2022 09:16:16 -0300 Subject: [PATCH 09/11] collect artifacts for pipeline stopper --- scripts/ci/gitlab/pipeline/test.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index 5876f9ad5dcee..c38eac45d7ba4 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -416,6 +416,8 @@ cargo-check-each-crate: extends: - .docker-env - .test-refs + - .collect-artifacts + - .pipeline-stopper-artifacts variables: # $CI_JOB_NAME is set manually so that rusty-cachier can share the cache for all # "cargo-check-each-crate I/N" jobs @@ -429,7 +431,10 @@ cargo-check-each-crate: cargo-check-each-crate-macos: stage: test - extends: .test-refs + extends: + - .test-refs + - .collect-artifacts + - .pipeline-stopper-artifacts before_script: - !reference [.rust-info-script, script] - !reference [.pipeline-stopper-vars, script] From 56d29482fd55f515a9e6e8c656dcd32ee9961f2c Mon Sep 17 00:00:00 2001 From: joao-paulo-parity Date: Thu, 17 Nov 2022 07:23:05 -0300 Subject: [PATCH 10/11] revert hardcoded crates_per_group --- scripts/ci/gitlab/check-each-crate.sh | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/scripts/ci/gitlab/check-each-crate.sh b/scripts/ci/gitlab/check-each-crate.sh index a5f2e48caa2d1..1838fd95d24d9 100755 --- a/scripts/ci/gitlab/check-each-crate.sh +++ b/scripts/ci/gitlab/check-each-crate.sh @@ -20,18 +20,14 @@ readarray -t workspace_crates < <(\ crates_total=${#workspace_crates[*]} -if [ "$crates_total" -le "$groups_total" ]; then - crates_per_group=1 -else - # We add `crates_total % groups_total > 0` (which evaluates to 1 in case there's a remainder for - # `crates_total % groups_total`) to round UP `crates_total / groups_total` 's - # potentially-fractional result to the nearest integer. Doing that ensures that we'll not miss any - # crate in case `crates_total / groups_total` would normally result in a fractional number, since - # in those cases Bash would round DOWN the result to the nearest integer. For example, if - # `crates_total = 5` and `groups_total = 2`, then `crates_total / groups_total` would round down - # to 2; since the loop below would then step by 2, we'd miss the 5th crate. - crates_per_group=$(( (crates_total / groups_total) + (crates_total % groups_total > 0) )) -fi +# We add `crates_total % groups_total > 0` (which evaluates to 1 in case there's a remainder for +# `crates_total % groups_total`) to round UP `crates_total / groups_total` 's potentially-fractional +# result to the nearest integer. Doing that ensures that we'll not miss any crate in case +# `crates_total / groups_total` would normally result in a fractional number, since in those cases +# Bash would round DOWN the result to the nearest integer. For example, if `crates_total = 5` and +# `groups_total = 2`, then `crates_total / groups_total` would round down to 2; since the loop below +# would then step by 2, we'd miss the 5th crate. +crates_per_group=$(( (crates_total / groups_total) + (crates_total % groups_total > 0) )) group=1 for ((i=0; i < crates_total; i += crates_per_group)); do From 478c87add89be86e0b62720e93d8c5b755034647 Mon Sep 17 00:00:00 2001 From: joao-paulo-parity Date: Thu, 17 Nov 2022 08:51:42 -0300 Subject: [PATCH 11/11] re-add crates_per_group=1 --- scripts/ci/gitlab/check-each-crate.sh | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/scripts/ci/gitlab/check-each-crate.sh b/scripts/ci/gitlab/check-each-crate.sh index 1838fd95d24d9..27e2cf947787a 100755 --- a/scripts/ci/gitlab/check-each-crate.sh +++ b/scripts/ci/gitlab/check-each-crate.sh @@ -20,14 +20,19 @@ readarray -t workspace_crates < <(\ crates_total=${#workspace_crates[*]} -# We add `crates_total % groups_total > 0` (which evaluates to 1 in case there's a remainder for -# `crates_total % groups_total`) to round UP `crates_total / groups_total` 's potentially-fractional -# result to the nearest integer. Doing that ensures that we'll not miss any crate in case -# `crates_total / groups_total` would normally result in a fractional number, since in those cases -# Bash would round DOWN the result to the nearest integer. For example, if `crates_total = 5` and -# `groups_total = 2`, then `crates_total / groups_total` would round down to 2; since the loop below -# would then step by 2, we'd miss the 5th crate. -crates_per_group=$(( (crates_total / groups_total) + (crates_total % groups_total > 0) )) +if [ "$crates_total" -lt "$groups_total" ]; then + # `crates_total / groups_total` would result in 0, so round it up to 1 + crates_per_group=1 +else + # We add `crates_total % groups_total > 0` (which evaluates to 1 in case there's a remainder for + # `crates_total % groups_total`) to round UP `crates_total / groups_total` 's + # potentially-fractional result to the nearest integer. Doing that ensures that we'll not miss any + # crate in case `crates_total / groups_total` would normally result in a fractional number, since + # in those cases Bash would round DOWN the result to the nearest integer. For example, if + # `crates_total = 5` and `groups_total = 2`, then `crates_total / groups_total` would round down + # to 2; since the loop below would then step by 2, we'd miss the 5th crate. + crates_per_group=$(( (crates_total / groups_total) + (crates_total % groups_total > 0) )) +fi group=1 for ((i=0; i < crates_total; i += crates_per_group)); do