From 441b8ca50334618daa09224d9d94c8689f497243 Mon Sep 17 00:00:00 2001 From: Charlie <5764343+charlielye@users.noreply.github.com> Date: Thu, 22 Oct 2026 14:51:17 +0000 Subject: [PATCH 1/9] refactor(ci3): consume ci3 as a submodule ci3 now lives in aztec-labs-eng/ci3, shared with aztec-node, so the two repos stop drifting: this repo's copy was months behind on the logic both share. The tree here is replaced by the submodule. Scripts specific to this repo (the merge-train and canary notifiers, the public release assertion, the GCP artifact login and the npm release publisher) move to ci3-local/ rather than into the shared repo, carrying their latest changes, with call sites updated. ci.sh and bootstrap.sh check the submodule is initialised before sourcing it. ci.sh and the two barretenberg benchmark scripts stop re-sourcing source_redis, source_refname and source_cache, which ci3/source already includes. Co-Authored-By: Claude Opus 5.5 --- .claude/skills/merge-train-infra/SKILL.md | 6 +- .gitmodules | 5 + barretenberg/cpp/bootstrap.sh | 2 +- .../cpp/scripts/ci_benchmark_ivc_flows.sh | 2 - .../ci_benchmark_ultrahonk_circuits.sh | 2 - barretenberg/rust/bootstrap.sh | 2 +- bootstrap.sh | 10 +- ci.sh | 7 +- ci3 | 1 + {ci3 => ci3-local}/assert_public_release | 0 {ci3 => ci3-local}/gcp_artifact_login | 0 .../merge_train_failure_slack_notify | 0 {ci3 => ci3-local}/merge_train_stale_check | 0 {ci3 => ci3-local}/npm/.npmrc | 0 {ci3 => ci3-local}/publish_npm_release | 0 .../release_canary_slack_notify | 0 ci3/.gitignore | 1 - ci3/README.md | 90 - ci3/add_log_time_delta | 7 - ci3/add_timestamps | 6 - ci3/arch | 14 - ci3/atomic_append | 14 - ci3/aws/ami_id_amd64 | 1 - ci3/aws/ami_id_arm64 | 1 - ci3/aws/ami_update.sh | 73 - ci3/aws/build_instance_ssh_config | 10 - ci3/aws_instance_name | 37 - ci3/aws_request_instance | 93 - ci3/aws_request_instance_type | 251 - ci3/aws_terminate_instance | 26 - ci3/bench_compare | 180 - ci3/bench_engine | 57 - ci3/bin/parallel | 15360 ---------------- ci3/bin/redis-cli | Bin 2253984 -> 0 bytes ci3/bootstrap.sh | 25 - ci3/bootstrap_ec2 | 479 - ci3/cache_content_hash | 88 - ci3/cache_delete | 19 - ci3/cache_download | 90 - ci3/cache_exists | 41 - ci3/cache_load_image | 15 - ci3/cache_local.test.sh | 230 - ci3/cache_log | 79 - ci3/cache_ls | 22 - ci3/cache_upload | 68 - ci3/check_port | 19 - ci3/clean_remote_tags | 36 - ci3/color_prefix | 76 - ci3/cpuset_count | 15 - ci3/dashboard/Dockerfile | 27 - ci3/dashboard/chonk-breakdowns/README.md | 60 - .../chonk-breakdowns/breakdown-viewer.html | 855 - ci3/dashboard/chonk-breakdowns/run-local.sh | 58 - ci3/dashboard/ci-metrics/Dockerfile | 11 - ci3/dashboard/ci-metrics/app.py | 1149 -- ci3/dashboard/ci-metrics/billing/__init__.py | 14 - ci3/dashboard/ci-metrics/billing/aws.py | 382 - .../ci-metrics/billing/billing-dashboard.html | 415 - ci3/dashboard/ci-metrics/billing/explore.py | 619 - .../ci-metrics/billing/fetch_billing.py | 262 - ci3/dashboard/ci-metrics/billing/gcp.py | 289 - ci3/dashboard/ci-metrics/ci-run-seed.json.gz | Bin 357714 -> 0 bytes ci3/dashboard/ci-metrics/db.py | 272 - ci3/dashboard/ci-metrics/ec2_pricing.py | 251 - ci3/dashboard/ci-metrics/github_data.py | 1011 - .../ci-metrics/merge-queue-backfill.json | 2564 --- ci3/dashboard/ci-metrics/metrics.py | 1222 -- ci3/dashboard/ci-metrics/requirements.txt | 9 - ci3/dashboard/ci-metrics/rk.py | 1 - ci3/dashboard/ci-metrics/sync_to_sqlite.py | 60 - ci3/dashboard/ci-metrics/test_cache.py | 154 - ci3/dashboard/ci-metrics/test_cloudtrail.py | 302 - .../ci-metrics/views/ci-health-report.html | 999 - .../ci-metrics/views/ci-insights.html | 1221 -- ci3/dashboard/ci-metrics/views/commits.html | 345 - .../ci-metrics/views/cost-overview.html | 649 - ci3/dashboard/ci-metrics/views/flake-prs.html | 155 - .../ci-metrics/views/test-timings.html | 7 - ci3/dashboard/debug.sh | 3 - ci3/dashboard/deploy-test.sh | 143 - ci3/dashboard/deploy.sh | 13 - ci3/dashboard/requirements.txt | 9 - ci3/dashboard/rk.py | 675 - ci3/dashboard/rk_cli.py | 24 - ci3/dashboard/rk_core.py | 126 - ci3/dashboard/set-filter.lua | 60 - ci3/denoise | 168 - ci3/deploy_npm | 91 - ci3/dist_tag | 20 - ci3/do_or_dryrun | 6 - ci3/docker_has_image | 3 - ci3/docker_isolate | 90 - ci3/dump_fail | 46 - ci3/echo_header | 5 - ci3/exec_test | 86 - ci3/fd_monitor | 23 - ci3/filter_cached_test_cmd | 39 - ci3/filter_test_cmds | 17 - ci3/get_test_entry | 27 - ci3/get_test_owner | 6 - ci3/grind_test | 79 - ci3/internal/augment_prompt | 2 - ci3/log_ci_run | 109 - ci3/lua/set-filter.lua | 56 - ci3/memsuspend_limit | 22 - ci3/memusage | 64 - ci3/npm_install_deps | 50 - ci3/os | 7 - ci3/parallelize | 62 - ci3/parallelize_strict | 104 - ci3/post_github_status | 49 - ci3/redact | 107 - ci3/release_prep_package_json | 41 - ci3/retry | 52 - ci3/run_compose_test | 56 - ci3/run_test_cmd | 428 - ci3/sem_sched | 135 - ci3/semc.c | 231 - ci3/semver | 365 - ci3/slack_notify | 24 - ci3/source | 13 - ci3/source_base | 53 - ci3/source_bootstrap | 47 - ci3/source_cache | 89 - ci3/source_color | 23 - ci3/source_options | 9 - ci3/source_redis | 120 - ci3/source_refname | 55 - ci3/source_stdlib | 76 - ci3/source_test | 47 - ci3/source_test_params | 30 - ci3/spinner | 77 - ci3/squash_args | 5 - ci3/ssm_send_command | 151 - ci3/start_interactive | 8 - ci3/test_engine | 17 - ci3/tests/color_prefix_test | 232 - ci3/tests/denoise_test | 153 - ci3/tests/redact_test | 138 - ci3/tests/signal_test | 123 - ci3/total_mem_gb | 13 - ci3/upload_benchmarks | 138 - ci3/upload_benchmarks_test | 135 - ci3/watch_ci | 98 - ipc-runtime/bootstrap.sh | 2 +- 145 files changed, 25 insertions(+), 36473 deletions(-) create mode 160000 ci3 rename {ci3 => ci3-local}/assert_public_release (100%) rename {ci3 => ci3-local}/gcp_artifact_login (100%) rename {ci3 => ci3-local}/merge_train_failure_slack_notify (100%) rename {ci3 => ci3-local}/merge_train_stale_check (100%) rename {ci3 => ci3-local}/npm/.npmrc (100%) rename {ci3 => ci3-local}/publish_npm_release (100%) rename {ci3 => ci3-local}/release_canary_slack_notify (100%) delete mode 100644 ci3/.gitignore delete mode 100644 ci3/README.md delete mode 100755 ci3/add_log_time_delta delete mode 100755 ci3/add_timestamps delete mode 100755 ci3/arch delete mode 100755 ci3/atomic_append delete mode 100644 ci3/aws/ami_id_amd64 delete mode 100644 ci3/aws/ami_id_arm64 delete mode 100755 ci3/aws/ami_update.sh delete mode 100644 ci3/aws/build_instance_ssh_config delete mode 100755 ci3/aws_instance_name delete mode 100755 ci3/aws_request_instance delete mode 100755 ci3/aws_request_instance_type delete mode 100755 ci3/aws_terminate_instance delete mode 100755 ci3/bench_compare delete mode 100755 ci3/bench_engine delete mode 100755 ci3/bin/parallel delete mode 100755 ci3/bin/redis-cli delete mode 100755 ci3/bootstrap.sh delete mode 100755 ci3/bootstrap_ec2 delete mode 100755 ci3/cache_content_hash delete mode 100755 ci3/cache_delete delete mode 100755 ci3/cache_download delete mode 100755 ci3/cache_exists delete mode 100755 ci3/cache_load_image delete mode 100755 ci3/cache_local.test.sh delete mode 100755 ci3/cache_log delete mode 100755 ci3/cache_ls delete mode 100755 ci3/cache_upload delete mode 100755 ci3/check_port delete mode 100755 ci3/clean_remote_tags delete mode 100755 ci3/color_prefix delete mode 100755 ci3/cpuset_count delete mode 100644 ci3/dashboard/Dockerfile delete mode 100644 ci3/dashboard/chonk-breakdowns/README.md delete mode 100644 ci3/dashboard/chonk-breakdowns/breakdown-viewer.html delete mode 100755 ci3/dashboard/chonk-breakdowns/run-local.sh delete mode 100644 ci3/dashboard/ci-metrics/Dockerfile delete mode 100644 ci3/dashboard/ci-metrics/app.py delete mode 100644 ci3/dashboard/ci-metrics/billing/__init__.py delete mode 100644 ci3/dashboard/ci-metrics/billing/aws.py delete mode 100644 ci3/dashboard/ci-metrics/billing/billing-dashboard.html delete mode 100644 ci3/dashboard/ci-metrics/billing/explore.py delete mode 100644 ci3/dashboard/ci-metrics/billing/fetch_billing.py delete mode 100644 ci3/dashboard/ci-metrics/billing/gcp.py delete mode 100644 ci3/dashboard/ci-metrics/ci-run-seed.json.gz delete mode 100644 ci3/dashboard/ci-metrics/db.py delete mode 100644 ci3/dashboard/ci-metrics/ec2_pricing.py delete mode 100644 ci3/dashboard/ci-metrics/github_data.py delete mode 100644 ci3/dashboard/ci-metrics/merge-queue-backfill.json delete mode 100644 ci3/dashboard/ci-metrics/metrics.py delete mode 100644 ci3/dashboard/ci-metrics/requirements.txt delete mode 100644 ci3/dashboard/ci-metrics/rk.py delete mode 100755 ci3/dashboard/ci-metrics/sync_to_sqlite.py delete mode 100644 ci3/dashboard/ci-metrics/test_cache.py delete mode 100644 ci3/dashboard/ci-metrics/test_cloudtrail.py delete mode 100644 ci3/dashboard/ci-metrics/views/ci-health-report.html delete mode 100644 ci3/dashboard/ci-metrics/views/ci-insights.html delete mode 100644 ci3/dashboard/ci-metrics/views/commits.html delete mode 100644 ci3/dashboard/ci-metrics/views/cost-overview.html delete mode 100644 ci3/dashboard/ci-metrics/views/flake-prs.html delete mode 100644 ci3/dashboard/ci-metrics/views/test-timings.html delete mode 100755 ci3/dashboard/debug.sh delete mode 100755 ci3/dashboard/deploy-test.sh delete mode 100755 ci3/dashboard/deploy.sh delete mode 100644 ci3/dashboard/requirements.txt delete mode 100644 ci3/dashboard/rk.py delete mode 100644 ci3/dashboard/rk_cli.py delete mode 100644 ci3/dashboard/rk_core.py delete mode 100644 ci3/dashboard/set-filter.lua delete mode 100755 ci3/denoise delete mode 100755 ci3/deploy_npm delete mode 100755 ci3/dist_tag delete mode 100755 ci3/do_or_dryrun delete mode 100755 ci3/docker_has_image delete mode 100755 ci3/docker_isolate delete mode 100755 ci3/dump_fail delete mode 100755 ci3/echo_header delete mode 100755 ci3/exec_test delete mode 100755 ci3/fd_monitor delete mode 100755 ci3/filter_cached_test_cmd delete mode 100755 ci3/filter_test_cmds delete mode 100755 ci3/get_test_entry delete mode 100755 ci3/get_test_owner delete mode 100755 ci3/grind_test delete mode 100755 ci3/internal/augment_prompt delete mode 100755 ci3/log_ci_run delete mode 100644 ci3/lua/set-filter.lua delete mode 100755 ci3/memsuspend_limit delete mode 100755 ci3/memusage delete mode 100755 ci3/npm_install_deps delete mode 100755 ci3/os delete mode 100755 ci3/parallelize delete mode 100755 ci3/parallelize_strict delete mode 100755 ci3/post_github_status delete mode 100755 ci3/redact delete mode 100755 ci3/release_prep_package_json delete mode 100755 ci3/retry delete mode 100755 ci3/run_compose_test delete mode 100755 ci3/run_test_cmd delete mode 100755 ci3/sem_sched delete mode 100644 ci3/semc.c delete mode 100755 ci3/semver delete mode 100755 ci3/slack_notify delete mode 100644 ci3/source delete mode 100644 ci3/source_base delete mode 100644 ci3/source_bootstrap delete mode 100644 ci3/source_cache delete mode 100644 ci3/source_color delete mode 100644 ci3/source_options delete mode 100644 ci3/source_redis delete mode 100644 ci3/source_refname delete mode 100644 ci3/source_stdlib delete mode 100644 ci3/source_test delete mode 100644 ci3/source_test_params delete mode 100755 ci3/spinner delete mode 100755 ci3/squash_args delete mode 100755 ci3/ssm_send_command delete mode 100755 ci3/start_interactive delete mode 100755 ci3/test_engine delete mode 100755 ci3/tests/color_prefix_test delete mode 100755 ci3/tests/denoise_test delete mode 100755 ci3/tests/redact_test delete mode 100755 ci3/tests/signal_test delete mode 100755 ci3/total_mem_gb delete mode 100755 ci3/upload_benchmarks delete mode 100755 ci3/upload_benchmarks_test delete mode 100755 ci3/watch_ci diff --git a/.claude/skills/merge-train-infra/SKILL.md b/.claude/skills/merge-train-infra/SKILL.md index 86a89072429..e046a1e218b 100644 --- a/.claude/skills/merge-train-infra/SKILL.md +++ b/.claude/skills/merge-train-infra/SKILL.md @@ -21,7 +21,7 @@ The merge-train system is fully automated via GitHub Actions in `.github/workflo 5. **Recreation & Wakeup** (`merge-train-recreate.yml`): Triggered when a PR is closed (merged). If the merged PR's head branch starts with `merge-train/`, recreates the branch from the base branch (usually `next`). Then runs `scripts/merge-train/wakeup-prs.sh` to add the `ci-wakeup-pr-after-merge` label to all open PRs targeting the branch that have passed CI and have automerge enabled. This triggers a CI re-run (typically a no-op via tree-hash cache) so those PRs can proceed through the merge queue. The label is immediately removed by a step in `ci3.yml` so it can be re-applied on subsequent merges. -6. **Failure Notification** (`merge-queue-dequeue-notify.yml`): Triggered when a PR is dequeued from the merge queue. If the PR's head branch starts with `merge-train/` and the PR was NOT merged, sends a Slack notification via `ci3/merge_train_failure_slack_notify`. +6. **Failure Notification** (`merge-queue-dequeue-notify.yml`): Triggered when a PR is dequeued from the merge queue. If the PR's head branch starts with `merge-train/` and the PR was NOT merged, sends a Slack notification via `ci3-local/merge_train_failure_slack_notify`. ## Release-Line Ports @@ -78,7 +78,7 @@ When a CI run fails on an EC2 instance, it calls `merge_train_failure_slack_noti 1. Create a branch from the desired base (`next` for most trains; a release line like `v5-next` for a release-specific train) with naming pattern `merge-train/{team}`. For a v5-release train use the `-v5` suffix (`merge-train/{team}-v5`): `merge-train-create-pr.yml` routes any `*-v5` branch to a `v5-next` base automatically and adds the `private-port-next` label. 2. Add the branch to the loop in `.github/workflows/merge-train-next-to-branches.yml`. If it tracks a base branch other than `next`, also add that base to the workflow's `push` trigger and pass it as the second argument to `merge-next.sh` (see the `v5-next` → `-v5` trains wiring) 3. For a base other than `next` that the `*-v5` convention does not already cover, set the PR base in `.github/workflows/merge-train-create-pr.yml`. Either way, add a stale-check job that passes `BASE_BRANCH` in `.github/workflows/merge-train-stale-check.yml` -4. Add the branch-to-Slack-channel mapping in `ci3/merge_train_failure_slack_notify` +4. Add the branch-to-Slack-channel mapping in `ci3-local/merge_train_failure_slack_notify` 5. Optionally add CI mode overrides in `.github/ci3_labels_to_env.sh` and `bootstrap.sh` 6. Push code to the branch -- automation handles PR creation from there @@ -113,7 +113,7 @@ When a CI run fails on an EC2 instance, it calls `merge_train_failure_slack_noti |---|---| | `.github/ci3_labels_to_env.sh` | CI mode selection based on labels and target branches | | `.github/ci3.sh` | Instance postfix for merge-train parallelism | -| `ci3/merge_train_failure_slack_notify` | Slack failure notification with branch-to-channel mapping | +| `ci3-local/merge_train_failure_slack_notify` | Slack failure notification with branch-to-channel mapping | | `ci3/run_test_cmd` | Test history tracking for merge-train branches | | `ci3/bootstrap_ec2` | EC2 failure notification trigger | | `bootstrap.sh` | CI mode definitions (`ci-barretenberg`, `ci-full`, etc.) | diff --git a/.gitmodules b/.gitmodules index 43ed5d858f1..cc79cebc42c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -30,3 +30,8 @@ branch = main shallow = true update = none +[submodule "ci3"] + path = ci3 + url = https://github.com/aztec-labs-eng/ci3.git + branch = main + shallow = true diff --git a/barretenberg/cpp/bootstrap.sh b/barretenberg/cpp/bootstrap.sh index 8e3deb35a40..880c2a32e29 100755 --- a/barretenberg/cpp/bootstrap.sh +++ b/barretenberg/cpp/bootstrap.sh @@ -494,7 +494,7 @@ function release { # A private release publishes only to the internal Artifact Registry; refuse a GitHub publish # here regardless of call path (this function is not on the private dispatch path, but the guard # makes a leak impossible rather than merely unreached). - "$root/ci3/assert_public_release" + "$root/ci3-local/assert_public_release" # The GitHub release assets are the legacy channel: the same binaries ship as npm packages # (@aztec-foundation/bb, @aztec-foundation/bb-avm and their platform packages) and bbup # installs from npm first. Kept while older bbup versions and the static libraries need it. diff --git a/barretenberg/cpp/scripts/ci_benchmark_ivc_flows.sh b/barretenberg/cpp/scripts/ci_benchmark_ivc_flows.sh index e35dd983cca..32bf7b53507 100755 --- a/barretenberg/cpp/scripts/ci_benchmark_ivc_flows.sh +++ b/barretenberg/cpp/scripts/ci_benchmark_ivc_flows.sh @@ -3,8 +3,6 @@ # Downloads the pinned inputs itself when the requested flow is missing or stale. REPO_ROOT=$(git rev-parse --show-toplevel) NO_CD=1 source "$REPO_ROOT/ci3/source" -source "$REPO_ROOT/ci3/source_redis" -source "$REPO_ROOT/ci3/source_cache" source "$REPO_ROOT/barretenberg/cpp/scripts/pinned_chonk_inputs.sh" default_chonk_flow="ecdsar1+transfer_0_recursions+sponsored_fpc" diff --git a/barretenberg/cpp/scripts/ci_benchmark_ultrahonk_circuits.sh b/barretenberg/cpp/scripts/ci_benchmark_ultrahonk_circuits.sh index 7c652017fa0..a3bc4fd8372 100755 --- a/barretenberg/cpp/scripts/ci_benchmark_ultrahonk_circuits.sh +++ b/barretenberg/cpp/scripts/ci_benchmark_ultrahonk_circuits.sh @@ -11,8 +11,6 @@ own_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" NO_CD=1 source "$(git rev-parse --show-toplevel)/ci3/source" -source "$root/ci3/source_redis" -source "$root/ci3/source_cache" if [[ $# -ne 3 ]]; then echo "Usage: $0 " diff --git a/barretenberg/rust/bootstrap.sh b/barretenberg/rust/bootstrap.sh index 06142f6e825..cd962c086d3 100755 --- a/barretenberg/rust/bootstrap.sh +++ b/barretenberg/rust/bootstrap.sh @@ -70,7 +70,7 @@ function release { echo_header "barretenberg-rs release" # crates.io is a public channel: a private release must never reach it, regardless of call path. - "$root/ci3/assert_public_release" + "$root/ci3-local/assert_public_release" local version=${REF_NAME#v} diff --git a/bootstrap.sh b/bootstrap.sh index 859a329df52..dc00f4f78b2 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -159,6 +159,12 @@ function install_deps { fi } +# ci3 is a submodule: without it nothing below can be sourced. +if [ ! -f "$(git rev-parse --show-toplevel)/ci3/source" ]; then + echo "ci3 submodule is not initialised. Run: git submodule update --init ci3" >&2 + exit 1 +fi + # Special case for installing dependencies (can run on older bash). if [ "${1:-}" = "install_deps" ]; then set -euo pipefail @@ -570,7 +576,7 @@ function release_bb_github { # Create a GitHub release in AztecProtocol/barretenberg for bb artifacts. # Users can manually create releases in aztec-packages via the GitHub UI if needed. # A private release must never reach a GitHub publish: refuse here regardless of call path. - "$root/ci3/assert_public_release" + "$root/ci3-local/assert_public_release" local bb_repo="AztecProtocol/barretenberg" if gh release view "$REF_NAME" --repo "$bb_repo" &>/dev/null; then return @@ -673,7 +679,7 @@ function private_release { # Activate the CI service account (gcp_artifact_login activates the SA globally) and mint a # short-lived access token for npm auth against the AR npm repo. - ci3/gcp_artifact_login + ci3-local/gcp_artifact_login set +x # Never echo the access token. export NPM_TOKEN=$(gcloud auth print-access-token) # Route our scopes to the internal npm registry; public deps still resolve from the default registry diff --git a/ci.sh b/ci.sh index 222c90d7aec..be78c85cf76 100755 --- a/ci.sh +++ b/ci.sh @@ -1,7 +1,10 @@ #!/usr/bin/env bash +# ci3 is a submodule: without it nothing below can be sourced. +if [ ! -f "$(git rev-parse --show-toplevel)/ci3/source" ]; then + echo "ci3 submodule is not initialised. Run: git submodule update --init ci3" >&2 + exit 1 +fi source $(git rev-parse --show-toplevel)/ci3/source -source $ci3/source_redis -source $ci3/source_refname cmd=${1:-} arch=${ARCH:-$(arch)} diff --git a/ci3 b/ci3 new file mode 160000 index 00000000000..b505e87c42c --- /dev/null +++ b/ci3 @@ -0,0 +1 @@ +Subproject commit b505e87c42cb979a20ced790d8be40e3003ab41f diff --git a/ci3/assert_public_release b/ci3-local/assert_public_release similarity index 100% rename from ci3/assert_public_release rename to ci3-local/assert_public_release diff --git a/ci3/gcp_artifact_login b/ci3-local/gcp_artifact_login similarity index 100% rename from ci3/gcp_artifact_login rename to ci3-local/gcp_artifact_login diff --git a/ci3/merge_train_failure_slack_notify b/ci3-local/merge_train_failure_slack_notify similarity index 100% rename from ci3/merge_train_failure_slack_notify rename to ci3-local/merge_train_failure_slack_notify diff --git a/ci3/merge_train_stale_check b/ci3-local/merge_train_stale_check similarity index 100% rename from ci3/merge_train_stale_check rename to ci3-local/merge_train_stale_check diff --git a/ci3/npm/.npmrc b/ci3-local/npm/.npmrc similarity index 100% rename from ci3/npm/.npmrc rename to ci3-local/npm/.npmrc diff --git a/ci3/publish_npm_release b/ci3-local/publish_npm_release similarity index 100% rename from ci3/publish_npm_release rename to ci3-local/publish_npm_release diff --git a/ci3/release_canary_slack_notify b/ci3-local/release_canary_slack_notify similarity index 100% rename from ci3/release_canary_slack_notify rename to ci3-local/release_canary_slack_notify diff --git a/ci3/.gitignore b/ci3/.gitignore deleted file mode 100644 index fe35732c790..00000000000 --- a/ci3/.gitignore +++ /dev/null @@ -1 +0,0 @@ -semc diff --git a/ci3/README.md b/ci3/README.md deleted file mode 100644 index 802002cca50..00000000000 --- a/ci3/README.md +++ /dev/null @@ -1,90 +0,0 @@ -# Build System - -This repository uses a custom build system that is **agnostic to CI platforms** and **leverages a remote cache** (S3 or MinIO) for caching build artifacts. Our approach enables efficient incremental builds, distributed parallel testing, and consistent deployment to ephemeral infrastructure (e.g., AWS Spot Instances). - -We avoid heavy CI vendor lock-in by using shell scripts with a uniform framework (ci3 folder). Each project defines its own bootstrap or build script but relies on the shared `ci3` folder for advanced features like: - -- Generating **content hashes** to skip unchanged builds -- Uploading/downloading build artifacts -- Parallelizing & caching test results -- Logging ephemeral output -- Provisioning ephemeral AWS instances for maximum compute at minimal cost - -## Requirements & Rationale - -1. **Monorepo-friendly** - Multiple projects within one repository can have separate build steps that only rebuild if their subset of files changes. - -2. **Remote Caching** - A stateless approach using S3-compatible storage (AWS S3 or MinIO). This replaces older Docker-image-based caches, streamlines artifact reuse, and easily shares builds across different runners or machines. - -3. **Content-based Rebuilds** - We compare content-hashes of relevant files. If no changes, no rebuild. This encourages fine-grained patterns (e.g., ignoring docs changes, but not ignoring new code). - -4. **Ephemeral Infrastructure** - We frequently run on EC2 spot instances to take advantage of cost savings. On eviction, the system gracefully shuts down and requeues tasks if needed. - -5. **Avoiding CI Lock-In** - We do *not* rely on special vendor features. Our build is run via shell scripts that can be triggered on any platform. - -## Important Concepts - -### Content Hashes -Instead of tagging artifacts by commit or branch, we compute a hash of the actual files that matter (via `.rebuild_patterns`). If code, dependencies, or scripts in that set change, we produce a new content hash and thus trigger a fresh build. - -### CI-Agnostic -All logic is in shell scripts, grouped under the **ci3** folder. A minimal layer of environment detection handles toggling debug modes, caches, or ephemeral logs. This means the same build system can run on local machines, ephemeral Docker containers, or GitHub Actions. - -### Rebuild Patterns -Each project folder often contains a `.rebuild_patterns` file. This file has patterns that, if matched by changed files, triggers a new build. Typical patterns might include: `^barretenberg/cpp/src/.*.cpp` and other patterns from root. - -When `cache_content_hash` sees new changes in these paths, it regenerates a new hash which prevents a cache download until an S3 artifact with that hash is uploaded. - -### Tools - -Tools are provided for the following themes. - -1. **Caching** - - **`cache_content_hash`**: Takes file patterns (or `.rebuild_patterns`) to compute a stable content hash. - - **`cache_upload`, `cache_download`**: Upload/download `.tar.gz` artifacts from a remote S3-like cache. - - **`cache_upload_flag`, `cache_download_flag`**: Mark or detect a particular test's success state. Avoids re-running long tests. - -2. **Test Parallelization & Caching** - - **`parallelize`**: Reads test commands from STDIN, executes in parallel, aggregates logs. - - **`run_test_cmd`**: Single test runner that can skip tests cached as “already passed.” - - **`filter_cached_test_cmd`**: Filters out test commands known to have succeeded (based on flags in redis). - -3. **Ephemeral Logging** - - **`denoise`**: Minimizes output spam; prints dots for each line, reveals full logs only if a command fails. - - **`cache_log`**, **`dump_fail`**: Captures output for ephemeral storage and prints or reveals logs when needed. - -4. **AWS Provisioning** - - **`aws_request_instance`** & **`aws_terminate_instance`**: Provision ephemeral spot or on-demand instances. - - **`aws_handle_evict`**: Detects spot-instance eviction signals, handles graceful shutdown or requeue. - -5. **Docker Isolation** - - **`docker_isolate`**: Executes a script in a new Docker container with ephemeral volumes, isolating host environment from side effects. - -6. **General Utilities** - - **`source_bootstrap`, `source_refname`, `source_color`**: Shared environment, color-coded logging, or version detection. - - **`echo_header`**: Prints a heading for better log readability. - - **ci3/source** is the central include-script for Aztec’s build system, automatically configuring Bash strict mode, setting up your `$PATH` to include all ci3 utilities (e.g. caching, parallel test commands), and providing helper functions and color-coded logging. Simply add source `$(git rev-parse --show-toplevel)/ci3/source` at the top of any project script to inherit a robust, preconfigured environment. - -### Usage Flow - -1. **In each project**: A `bootstrap.sh` might: - - Call `cache_content_hash` to detect changes. - - If changed, do the relevant compile step, then `cache_upload`. - - If not changed, do `cache_download` to restore a previously built artifact. - -2. **In test scripts**: - - We gather test commands (`test_cmds`) in a form easily read by `parallelize`. - - `parallelize` calls `run_test_cmd` on each line, skipping or running tests as needed. - -3. **In local or ephemeral servers**: - - Either manually run `./bootstrap.sh fast` or let the CI invoke it on a fresh machine. - - The system pulls dependencies, attempts to restore from remote caches, rebuilds only if necessary, then parallelizes tests. - -4. **On success**: - - Artifacts can be re-uploaded or flagged as “passed,” letting future runs skip unchanged steps. - diff --git a/ci3/add_log_time_delta b/ci3/add_log_time_delta deleted file mode 100755 index dc19d477d68..00000000000 --- a/ci3/add_log_time_delta +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -start_time=$(date +%s%3N) -while IFS= read -r line; do - current_time=$(date +%s%3N) - elapsed=$((current_time - start_time)) - printf '%d.%03d %s\n' $((elapsed / 1000)) $((elapsed % 1000)) "$line" -done diff --git a/ci3/add_timestamps b/ci3/add_timestamps deleted file mode 100755 index 9abf85c8fe5..00000000000 --- a/ci3/add_timestamps +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash -# Close stderr to prevent holding inherited pipeline FDs open (see cache_log). -exec 2>/dev/null -while IFS= read -r line; do - printf '%(%H:%M:%S)T %s\n' -1 "$line" -done diff --git a/ci3/arch b/ci3/arch deleted file mode 100755 index ac2d92d00f1..00000000000 --- a/ci3/arch +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -# Returns the standard artifact prefixes for each arch. -case $(uname -m) in - aarch64|arm64) - echo arm64 - ;; - amd64|x86_64) - echo amd64 - ;; - *) - echo "Error: unknown architecture $(uname -m)" >&2 - exit 1 - ;; -esac diff --git a/ci3/atomic_append b/ci3/atomic_append deleted file mode 100755 index ebe6b52aaef..00000000000 --- a/ci3/atomic_append +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -# Atomically appends stdin to a file using flock. -# Usage: echo "data" | atomic_append /path/to/file -set -euo pipefail - -file="$1" - -# Read all of stdin first, then acquire lock and write -data=$(cat) - -( - flock -x 200 - printf '%s\n' "$data" >&200 -) 200>>"$file" diff --git a/ci3/aws/ami_id_amd64 b/ci3/aws/ami_id_amd64 deleted file mode 100644 index df270515a2a..00000000000 --- a/ci3/aws/ami_id_amd64 +++ /dev/null @@ -1 +0,0 @@ -ami-005af01ba84151744 diff --git a/ci3/aws/ami_id_arm64 b/ci3/aws/ami_id_arm64 deleted file mode 100644 index 218856fd58c..00000000000 --- a/ci3/aws/ami_id_arm64 +++ /dev/null @@ -1 +0,0 @@ -ami-0a6b922f8414b98e8 diff --git a/ci3/aws/ami_update.sh b/ci3/aws/ami_update.sh deleted file mode 100755 index aed2a0555a9..00000000000 --- a/ci3/aws/ami_update.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env bash -source $(git rev-parse --show-toplevel)/ci3/source - -arch=${ARCH:-$(arch)} - -[ -t 1 ] && ssh_args="-t" || ssh_args="" - -# Trap function to terminate our running instance when the script exits. -function on_exit { - [ "${NO_TERMINATE:-0}" -eq 0 ] && aws_terminate_instance $state_dir -} - -if ! aws sts get-caller-identity > /dev/null 2>&1; then - echo "AWS CLI credentials for the CI account are required (aws sts get-caller-identity failed)." - exit 1 -fi - -case "$arch" in - "amd64") - ami="ami-04f167a56786e4b09" - ;; - "arm64") - ami="ami-0ae6f07ad3a8ef182" - ;; - *) - echo "Unknown arch: $ARCH" - exit 1 -esac - -# Request new instance (ami: ubuntu 24.04 LTS). SSH mode: the setup below is driven over ssh. -state_dir=$(mktemp -d /tmp/aws_request_instance.XXXXXX) -trap on_exit EXIT -AMI=$ami KEY_NAME=${KEY_NAME:-build-instance} aws_request_instance ami_update_$arch 4 $arch $state_dir -ip=$(cat $state_dir/ip) -iid=$(cat $state_dir/iid | tr -d '\n\r' | xargs) - -echo "Instance ip: $ip" - -# Initial setup. -ssh $ssh_args -F build_instance_ssh_config ubuntu@$ip ' - set -e - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --batch --yes --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg - echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null - sudo apt update - sudo apt install -y apt-transport-https ca-certificates curl software-properties-common docker-ce - sudo usermod -aG docker ${USER} - mkdir .aws -' - -ssh -F build_instance_ssh_config -O exit ubuntu@$ip 2>/dev/null || true - -# Download uncompressed crs onto machine for fast cold starts (deletes compressed if present). -ssh $ssh_args -F build_instance_ssh_config ubuntu@$ip 'export CRS_FORMAT=uncompressed; bash -s' < ../../barretenberg/crs/bootstrap.sh - -# Pull devbox onto host, and build into docker-in-docker volume. -ssh $ssh_args -F build_instance_ssh_config ubuntu@$ip " - docker run --privileged --rm -v bootstrap_ci_local_docker:/var/lib/docker aztecprotocol/devbox:3.2 bash -c \" - docker pull aztecprotocol/build:3.2 - \" -" - -if [ "${NO_AMI:-0}" -eq 0 ]; then - export AWS_DEFAULT_REGION=us-east-2 - ami_id=$(aws ec2 create-image \ - --instance-id "$iid" \ - --name "build-instance-$arch-$(date +'%d%m%y%H%M')" \ - --query "ImageId" \ - --output text) - echo "Waiting for AMI to be created: $ami_id" - while ! aws ec2 wait image-available --image-ids "$ami_id"; do true; done - echo "$ami_id" > ami_id_$arch - echo "Done." -fi diff --git a/ci3/aws/build_instance_ssh_config b/ci3/aws/build_instance_ssh_config deleted file mode 100644 index 6748b0ebe6d..00000000000 --- a/ci3/aws/build_instance_ssh_config +++ /dev/null @@ -1,10 +0,0 @@ -IdentityFile ~/.ssh/build_instance_key -StrictHostKeyChecking no -User ubuntu -ServerAliveInterval 60 -ServerAliveCountMax 5 - -# SSH multiplexing for persistent connections -ControlMaster auto -ControlPath /tmp/ssh_mux_%h_%p_%r -ControlPersist 10m \ No newline at end of file diff --git a/ci3/aws_instance_name b/ci3/aws_instance_name deleted file mode 100755 index 6ea76db1455..00000000000 --- a/ci3/aws_instance_name +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash -# Derive the canonical build-instance EC2 "Name" tag. Single source of truth for -# the launch path (bootstrap_ec2) and the connect/kill helpers (ci.sh), so the two -# can never drift. -# -# Usage: aws_instance_name [ref] [arch] [postfix] -# ref default $REF_NAME, else the current git branch. -# arch default $ARCH, else amd64. Normalised: x86_64->amd64, aarch64|arm64->arm64. -# postfix default $INSTANCE_POSTFIX (the per-job suffix, e.g. x-fast, bench); optional. -# -# The repo prefix is $GITHUB_REPOSITORY's basename (default aztec-packages) so that -# aztec-packages and aztec-packages-private don't collide on names. -set -eu - -ref=${1:-${REF_NAME:-$(git rev-parse --abbrev-ref HEAD 2>/dev/null || true)}} -arch=${2:-${ARCH:-amd64}} -postfix=${3:-${INSTANCE_POSTFIX:-}} - -# Normalise to the CI arch convention (amd64/arm64), not the kernel's (x86_64/aarch64). -case "$arch" in - x86_64 | amd64) arch=amd64 ;; - aarch64 | arm64) arch=arm64 ;; -esac - -repo=${GITHUB_REPOSITORY:-aztec-packages} -repo=${repo##*/} - -# Merge-queue refs look like gh-readonly-queue//pr--; key on pr- -# so every attempt for a PR shares one name. Otherwise sanitise the ref (cap to 50). -if [[ "$ref" =~ ^gh-readonly-queue/.*(pr-[0-9]+) ]]; then - name="${repo}_${BASH_REMATCH[1]}_${arch}" -else - name="${repo}_$(printf '%s' "$ref" | head -c 50 | tr -c 'a-zA-Z0-9-' '_')_${arch}" -fi -[ -n "$postfix" ] && name+="_${postfix}" - -printf '%s\n' "$name" diff --git a/ci3/aws_request_instance b/ci3/aws_request_instance deleted file mode 100755 index 2382873370c..00000000000 --- a/ci3/aws_request_instance +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/env bash -[ -n "${BUILD_SYSTEM_DEBUG:-}" ] && set -x # conditionally trace -set -eu - -name=$1 -cpus=$2 -arch=$3 -state_dir=$4 - -cd $(dirname $0) - -# Map a cpu count to its instance size suffix. -declare -A cpu_map -cpu_map=( - [2]="large" - [4]="xlarge" - [8]="2xlarge" - [16]="4xlarge" - [32]="8xlarge" - [48]="12xlarge" - [64]="16xlarge" - [96]="24xlarge" - [128]="32xlarge" - [192]="48xlarge" - [0192]="metal" -) - -# Spot pool preference by cpu count (lower number = tried first). 128 is the -# known-good build size, then 192, then 64 as a last resort before on-demand. -declare -A cpu_priority=([128]=1 [192]=2 [64]=3) - -# Capacity-diverse spot pools per cpu count. Restricted to families with -# >=4 GB/vCPU (m/r) so the build's measured ~1.9 GB/vCPU peak keeps ~2x -# headroom; c-series (2 GB/vCPU) is opt-in via CI_SPOT_INCLUDE_C. All entries -# are confirmed offered as spot across every us-east-2 AZ. -function spot_pools_for { - local cpu=$1 - case "$arch" in - x86_64|amd64) - case "$cpu" in - 128) echo "m6a.32xlarge m7a.32xlarge m6i.32xlarge r6a.32xlarge r7a.32xlarge" ;; - 192) echo "m6a.48xlarge m7a.48xlarge m7i.48xlarge r6a.48xlarge r7a.48xlarge" ;; - 64) echo "m6a.16xlarge m7a.16xlarge m7i.16xlarge m6i.16xlarge m5.16xlarge r6a.16xlarge" ;; - *) local s=${cpu_map[$cpu]:?Unknown cpu count: $cpu}; echo "m6a.$s m7a.$s m6i.$s r6a.$s" ;; - esac ;; - arm64) - case "$cpu" in - 64) echo "r7g.16xlarge r8g.16xlarge m7g.16xlarge m8g.16xlarge" ;; - *) local s=${cpu_map[$cpu]:?Unknown cpu count: $cpu}; echo "r7g.$s r8g.$s m7g.$s m8g.$s" ;; - esac ;; - esac -} - -# Optional c-series pools (2 GB/vCPU). Gated behind CI_SPOT_INCLUDE_C until -# build-phase parallelism is throttled to fit the lower memory headroom. -function spot_pools_c_for { - local cpu=$1 - case "$arch" in - x86_64|amd64) - case "$cpu" in - 128) echo "c6a.32xlarge c6i.32xlarge" ;; - 192) echo "c6a.48xlarge c7a.48xlarge" ;; - 64) echo "c6a.16xlarge c6i.16xlarge" ;; - esac ;; - esac -} - -if [ "$arch" == "x86_64" ] || [ "$arch" == "amd64" ]; then - ami=${AMI:-$(cat ./aws/ami_id_amd64)} -elif [ "$arch" == "arm64" ]; then - ami=${AMI:-$(cat ./aws/ami_id_arm64)} -else - echo "Unsupported arch: $arch" >&2 - exit 1 -fi - -# Build the fleet override set as space-separated "instanceType:priority" tokens. -# AWS_INSTANCE pins a single pool (e.g. for debugging); otherwise diversify -# across every requested size. -overrides="" -if [ -n "${AWS_INSTANCE:-}" ]; then - overrides="$AWS_INSTANCE:1" -else - IFS=',' read -ra cpu_list <<< "$cpus" - for cpu in "${cpu_list[@]}"; do - prio=${cpu_priority[$cpu]:-9} - types="$(spot_pools_for "$cpu")" - [ "${CI_SPOT_INCLUDE_C:-0}" -eq 1 ] && types+=" $(spot_pools_c_for "$cpu")" - for t in $types; do overrides+=" $t:$prio"; done - done -fi - -FLEET_OVERRIDES="${overrides# }" aws_request_instance_type "$name" "$ami" "$state_dir" diff --git a/ci3/aws_request_instance_type b/ci3/aws_request_instance_type deleted file mode 100755 index a8e6d5631d4..00000000000 --- a/ci3/aws_request_instance_type +++ /dev/null @@ -1,251 +0,0 @@ -#!/usr/bin/env bash -source $(git rev-parse --show-toplevel)/ci3/source - -name=$1 -ami=$2 -state_dir=$3 - -# Space-separated "instanceType:priority" tokens describing the spot pools to -# diversify across (set by aws_request_instance). -overrides_spec=${FLEET_OVERRIDES:?FLEET_OVERRIDES must be set} - -spec_path=$state_dir/lt_data.json -iid_path=$state_dir/iid -ip_path=$state_dir/ip -lt_path=$state_dir/launch_template_id - -export AWS_DEFAULT_REGION=us-east-2 - -# Delete the ephemeral launch template. Safe once instances are launched (they -# no longer reference it) and idempotent, so we run it on every exit. -function delete_lt { - [ -f "$lt_path" ] || return 0 - aws ec2 delete-launch-template --launch-template-id "$(cat "$lt_path")" >/dev/null 2>&1 || true - rm -f "$lt_path" -} - -# On interrupt, tear down whatever we acquired. On normal exit we only drop the -# launch template (the instance must survive for the caller). -function cleanup { - delete_lt - aws_terminate_instance "$state_dir" -} -trap 'cleanup' SIGINT SIGTERM -trap 'delete_lt' EXIT - -# KeyName is optional: omit when KEY_NAME is empty (SSM-only mode). -key_name_json="" -if [ -n "${KEY_NAME:-}" ]; then - key_name_json="\"KeyName\": \"$KEY_NAME\"," -fi - -# Always attach the instance profile so containers can use instance-profile credentials. -CI3_INSTANCE_PROFILE_NAME=${CI3_INSTANCE_PROFILE_NAME:-ci3-build-instance-profile} - -# In SSM mode use the CI3 security group; in SSH mode use the original SG (allows port 22). -if [ -z "${KEY_NAME:-}" ]; then - sg_id="${CI3_SECURITY_GROUP_ID:-sg-01fe61a1c1aaeb393}" -else - sg_id="sg-0ccd4e5df0dcca0c9" -fi - -# create-fleet spreads across AZs via subnet overrides, which must live in the -# same VPC as the security group. Pick one subnet per AZ. -vpc_id=$(aws ec2 describe-security-groups --group-ids "$sg_id" \ - --query 'SecurityGroups[0].VpcId' --output text) -mapfile -t subnets < <(aws ec2 describe-subnets \ - --filters "Name=vpc-id,Values=$vpc_id" \ - --query 'Subnets[].[AvailabilityZone,SubnetId]' --output text | sort -k1,1 -u | awk '{print $2}') -if [ "${#subnets[@]}" -eq 0 ]; then - echo_stderr "No subnets found in VPC $vpc_id for security group $sg_id." - exit 1 -fi - -# Instance tags, applied at launch via the launch template (propagate to fleet instances). -tags_json="{\"Key\":\"Name\",\"Value\":\"$name\"},{\"Key\":\"Group\",\"Value\":\"build-instance\"}" -[ -n "${GITHUB_ACTOR:-}" ] && tags_json+=",{\"Key\":\"GithubActor\",\"Value\":\"${GITHUB_ACTOR//[\[\]]/}\"}" -[ -n "${CI_MODE:-}" ] && tags_json+=",{\"Key\":\"CICommand\",\"Value\":\"$CI_MODE\"}" -[ -n "${CI_DASHBOARD:-}" ] && tags_json+=",{\"Key\":\"Dashboard\",\"Value\":\"$CI_DASHBOARD\"}" -if [ "${UNSAFE_AWS_KEEP_ALIVE:-0}" -eq 1 ]; then - echo_stderr "You have set UNSAFE_AWS_KEEP_ALIVE=1, so the instance will not be terminated after 1.5 hours by the reaper script. Make sure you shut the machine down when done." - tags_json+=",{\"Key\":\"Keep-Alive\",\"Value\":\"true\"}" -fi - -# Common launch config shared by every pool. Sizes/AZs vary per fleet override. -lt_data=$(cat < "$spec_path" - -lt_name="ci3-${name}-$$-$(date +%s)" -lt_id=$(aws ec2 create-launch-template \ - --launch-template-name "$lt_name" \ - --launch-template-data "$lt_data" \ - --query 'LaunchTemplate.LaunchTemplateId' --output text) -echo "$lt_id" > "$lt_path" - -# Cross every pool with every AZ. Priority biases capacity-optimized toward the -# preferred sizes (see aws_request_instance) while still optimizing for depth. -overrides_json="" -for tok in $overrides_spec; do - itype=${tok%%:*} - prio=${tok##*:} - for sn in "${subnets[@]}"; do - overrides_json+="{\"InstanceType\":\"$itype\",\"SubnetId\":\"$sn\",\"Priority\":$prio}," - done -done -ltc="[{\"LaunchTemplateSpecification\":{\"LaunchTemplateId\":\"$lt_id\",\"Version\":\"\$Latest\"},\"Overrides\":[${overrides_json%,}]}]" - -info="(name: $name) (pools: ${overrides_spec// /,}) (ami: $ami)" - -iid="" -instance_type="" -lifecycle="" - -# Try spot first, retrying the whole diversified fleet until the time budget -# expires (preserves the historical "give spot ~1 min, then on-demand" pattern). -if [ "${NO_SPOT:-0}" -ne 1 ]; then - spot_timeout=${CI_SPOT_TIMEOUT:-60} - spot_poll=${CI_SPOT_POLL:-10} - SECONDS=0 - while true; do - echo "Requesting spot fleet $info (capacity-optimized-prioritized, ${spot_timeout}s budget)..." - resp=$(aws ec2 create-fleet \ - --type instant \ - --target-capacity-specification "TotalTargetCapacity=1,DefaultTargetCapacityType=spot" \ - --spot-options "AllocationStrategy=capacity-optimized-prioritized" \ - --launch-template-configs "$ltc" \ - --output json 2>"$state_dir/fleet_err") || true - iid=$(echo "$resp" | jq -r '.Instances[0].InstanceIds[0] // empty' 2>/dev/null || true) - if [ -n "$iid" ]; then - instance_type=$(echo "$resp" | jq -r '.Instances[0].InstanceType // empty') - lifecycle=spot - break - fi - if (( SECONDS + spot_poll >= spot_timeout )); then - echo "Spot fleet unfulfilled after ${spot_timeout}s; falling back to on-demand." - break - fi - sleep "$spot_poll" - done -fi - -# On-demand fallback (cheapest pool) only if spot couldn't be had. Some pinned pools (e.g. -# m6a.metal for benchmarks) can hit InsufficientInstanceCapacity even on-demand, so retry -# the whole request until a time budget expires rather than giving up on the first miss. -if [ -z "$iid" ]; then - ondemand_timeout=${CI_ONDEMAND_TIMEOUT:-300} - ondemand_poll=${CI_ONDEMAND_POLL:-20} - SECONDS=0 - while true; do - echo "Requesting on-demand fleet $info..." - resp=$(aws ec2 create-fleet \ - --type instant \ - --target-capacity-specification "TotalTargetCapacity=1,DefaultTargetCapacityType=on-demand" \ - --on-demand-options "AllocationStrategy=lowest-price" \ - --launch-template-configs "$ltc" \ - --output json 2>"$state_dir/fleet_err") || true - iid=$(echo "$resp" | jq -r '.Instances[0].InstanceIds[0] // empty' 2>/dev/null || true) - if [ -n "$iid" ]; then - instance_type=$(echo "$resp" | jq -r '.Instances[0].InstanceType // empty') - lifecycle=ondemand - break - fi - if (( SECONDS + ondemand_poll >= ondemand_timeout )); then - echo "On-demand fleet unfulfilled after ${ondemand_timeout}s; giving up." - break - fi - echo "On-demand capacity unavailable$(echo "$resp" | jq -r '.Errors[0].ErrorCode // "" | if . == "" then "" else " (" + . + ")" end' 2>/dev/null); retrying in ${ondemand_poll}s (elapsed ${SECONDS}s)..." - sleep "$ondemand_poll" - done -fi - -if [ -z "$iid" ]; then - echo_stderr "Failed to acquire any instance (spot or on-demand)." - echo "$resp" | jq -r '.Errors[]? | " \(.ErrorCode): \(.ErrorMessage)"' >&2 2>/dev/null || true - cat "$state_dir/fleet_err" >&2 2>/dev/null || true - exit 1 -fi - -echo "Instance id: $iid ($instance_type, $lifecycle)" -echo "$iid" > "$iid_path" - -# Instances are launched; the launch template is no longer needed. -delete_lt - -# Record the instance type so callers can pass it downstream (e.g. into Docker). -echo "$instance_type" > "$state_dir/instance_type" -# Record whether this is spot or on-demand. -echo "$lifecycle" > "$state_dir/spot" - -ip="" -while [ -z "${ip:-}" ]; do - sleep 1 - ip=$(aws ec2 describe-instances \ - --filter "Name=instance-id,Values=$iid" \ - --query "Reservations[*].Instances[*].PublicIpAddress" \ - --output=text) - echo $ip > $ip_path -done - -# When no key pair (SSM-only), wait for SSM registration and set the shutdown timer. -if [ -z "${KEY_NAME:-}" ]; then - >&2 echo "Waiting for SSM registration for instance $iid..." - SECONDS=0 - while (( SECONDS < 300 )); do - ssm_status=$(aws ssm describe-instance-information \ - --region us-east-2 \ - --filters "Key=InstanceIds,Values=$iid" \ - --query "InstanceInformationList[0].PingStatus" \ - --output text 2>/dev/null) || true - >&2 echo "SSM status: ${ssm_status:-Inactive}" - [ "$ssm_status" = "Online" ] && break - sleep 5 - done - - if [ "$ssm_status" != "Online" ]; then - >&2 echo "Timed out waiting for SSM to see instance $iid" - exit 1 - fi - - # Set the shutdown timer via SSM, mirroring the SSH path. Fire-and-forget. - # Non-fatal: a failed timer shouldn't abort provisioning (reaper catches orphans). - if [ "${NO_TERMINATE:-0}" -ne 1 ]; then - >&2 echo "Setting shutdown timer (+${AWS_SHUTDOWN_TIME:-60} min) via SSM..." - SSM_NO_WAIT=1 ssm_send_command "$iid" "sudo shutdown -h +${AWS_SHUTDOWN_TIME:-60}" || \ - >&2 echo "Warning: failed to set shutdown timer via SSM (non-fatal)." - fi -else - # Wait till ssh port is open. - >&2 echo "Waiting for SSH at $ip..." - SECONDS=0 - SSH_CONFIG_PATH=${SSH_CONFIG_PATH:-aws/build_instance_ssh_config} - [ "${NO_TERMINATE:-0}" -eq 1 ] && LIVE_CMD=true || LIVE_CMD="sudo shutdown -h +${AWS_SHUTDOWN_TIME:-60}" - while ! ssh -F $SSH_CONFIG_PATH -o ConnectTimeout=1 $ip $LIVE_CMD > /dev/null 2>&1; do - if (( SECONDS >= 60 )); then - >&2 echo "Timeout: SSH could not login to $ip within 60 seconds." - exit 1 - fi - sleep 1 - done -fi diff --git a/ci3/aws_terminate_instance b/ci3/aws_terminate_instance deleted file mode 100755 index 0fe44d8a1b5..00000000000 --- a/ci3/aws_terminate_instance +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash -source $(git rev-parse --show-toplevel)/ci3/source - -state_path=$1 - -if [ ! -d "$state_path" ]; then - echo_stderr "State path $state_path does not exist." - exit -fi - -iid=$(cat $state_path/iid 2>/dev/null || true) -sir=$(cat $state_path/sir 2>/dev/null || true) - -export AWS_DEFAULT_REGION=us-east-2 - -if [ -n "$iid" ]; then - echo "Terminating instance: $iid..." - dump_fail "aws ec2 terminate-instances --instance-ids $iid" > /dev/null -fi - -if [ -n "$sir" ]; then - echo "Terminating spot request: $sir..." - dump_fail "aws ec2 cancel-spot-instance-requests --spot-instance-request-ids $sir" > /dev/null -fi - -rm -rf $state_path diff --git a/ci3/bench_compare b/ci3/bench_compare deleted file mode 100755 index 1c716a2c915..00000000000 --- a/ci3/bench_compare +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/env bash -# Compare a PR commit's benchmark run against its merge-base on a baseline branch (default: next), -# optionally averaging a window of baseline runs, and emit the relative perf diffs as JSON. -# -# Per-run benchmark data lives in the CI build-cache keyed by the commit's *git tree hash* -# (`bench-.tar.gz` -> `bench-out/bench.json`, a flat array of {name, unit, value}). This -# pulls only those ~40 KB per-run blobs; it never touches the multi-MB per-branch `data.js` history. -# -# It deliberately does NOT decide which benchmarks matter — it emits every comparable bench, plus a -# stddev/z-score when a window is used. The caller (the `bench-regression` skill) filters to the -# benches relevant to what the PR changes and drops the noise. -# -# BASELINE BRANCH — read this. The cache is content-addressed by git *tree hash*, so branches with -# different perf (next vs v5-next vs merge-train/*) already live under different keys and never cross- -# contaminate. But you MUST merge-base against the PR's *actual* base branch: comparing a v5-next PR -# against `next` would surface the whole v5-next-vs-next delta as bogus regressions. If --baseline is -# not given, this auto-detects the base of the current branch's PR via `gh` and falls back to -# origin/next with a loud warning. The chosen baseline is always printed to stderr — check it. -# Caveat: the cache key carries no branch/repo/hardware provenance, so a tree benched under a different -# environment (e.g. the private fork) yields whichever result was uploaded first; comparisons are only -# trustworthy within one bench infrastructure. -# -# Usage: ci3/bench_compare [PR_REF] [--baseline REF] [--window N] [--out FILE] -# PR_REF commit whose bench run to inspect (default: HEAD) -# --baseline baseline branch/ref to merge-base against (default: auto-detect, else origin/next) -# --window N average the merge-base + N-1 preceding baseline runs (default: 1) -# --out FILE write JSON here (default: stdout); the human summary always goes to stderr -# -# JSON (stdout): { meta:{pr_commit,merge_base,baseline_branch,baseline_runs,counts}, benches:[ -# { name, unit, pr, baseline, n, status, pct, stddev, z } ] } sorted by pct desc. -set -uo pipefail # not -e: failures are handled explicitly, and `cond && break` under -e would exit. -[ "${BUILD_SYSTEM_DEBUG:-}" = 1 ] && set -x - -BASELINE="${BASELINE:-}" # empty => auto-detect the PR's base branch below -WINDOW=1 -PR_REF="HEAD" -OUT="" -SCAN_LIMIT=300 -# Same endpoints cache_download uses: the in-CI S3 http endpoint, else the public cache CDN. -if [ "${CI:-0}" -eq 1 ]; then - CACHE_ENDPOINT="http://aztec-ci-artifacts.s3.amazonaws.com/build-cache" -else - CACHE_ENDPOINT="https://build-cache.aztec-labs.com" -fi - -die() { echo "bench_compare: error: $*" >&2; exit 1; } - -while [ $# -gt 0 ]; do - case "$1" in - --baseline) BASELINE="$2"; shift 2 ;; - --window) WINDOW="$2"; shift 2 ;; - --out) OUT="$2"; shift 2 ;; - -h|--help) sed -n '2,22p' "$0"; exit 0 ;; - --*) die "unknown flag $1" ;; - *) PR_REF="$1"; shift ;; - esac -done - -command -v jq >/dev/null || die "jq not found" -command -v python3 >/dev/null || die "python3 not found" - -# Resolve the baseline branch. Explicit --baseline (or $BASELINE) wins; otherwise auto-detect the base -# of the current branch's PR via gh, and only then fall back to origin/next. Always announced — a wrong -# baseline silently invalidates every number, so never let it be a silent default. -if [ -z "$BASELINE" ]; then - det="" - command -v gh >/dev/null && det="$(gh pr view --json baseRefName -q .baseRefName 2>/dev/null)" - if [ -n "$det" ]; then - BASELINE="origin/$det" - echo "bench_compare: auto-detected PR base branch -> $BASELINE" >&2 - else - BASELINE="origin/next" - echo "bench_compare: WARNING baseline defaulted to origin/next (no PR base detected). Pass --baseline for v5-next / merge-train PRs." >&2 - fi -fi -echo "bench_compare: baseline = $BASELINE" >&2 - -# Fetch a per-run bench.json for a tree hash. Tries the http cache (no creds), then aws s3 (creds). -fetch_bench() { # -> 0 if found - local tree="$1" dest="$2" tmp - tmp="$(mktemp -d)" - if curl -fsSL "$CACHE_ENDPOINT/bench-$tree.tar.gz" -o "$tmp/b.tgz" 2>/dev/null \ - || aws s3 cp "s3://aztec-ci-artifacts/build-cache/bench-$tree.tar.gz" "$tmp/b.tgz" >/dev/null 2>&1; then - if tar -xzf "$tmp/b.tgz" -C "$tmp" 2>/dev/null && [ -f "$tmp/bench-out/bench.json" ]; then - cp "$tmp/bench-out/bench.json" "$dest"; rm -rf "$tmp"; return 0 - fi - fi - rm -rf "$tmp"; return 1 -} - -pr_commit="$(git rev-parse --verify "$PR_REF^{commit}" 2>/dev/null)" || die "bad PR ref '$PR_REF'" -git rev-parse --verify "$BASELINE" >/dev/null 2>&1 || die "baseline '$BASELINE' not found (git fetch?)" -base_commit="$(git merge-base "$pr_commit" "$BASELINE")" || die "no merge-base between PR and $BASELINE" -pr_tree="$(git rev-parse "$pr_commit^{tree}")" - -work="$(mktemp -d)"; trap 'rm -rf "$work"' EXIT - -fetch_bench "$pr_tree" "$work/pr.json" \ - || die "no bench data for PR commit $pr_commit (tree $pr_tree). Trigger a run (label 'ci-full-no-test-cache') or pass a benched commit." - -# Baseline set: merge-base, then first-parent predecessors; take the first $WINDOW that have data. -baseline_files=(); n=0 -for c in $(git rev-list --first-parent "$base_commit" -n "$SCAN_LIMIT"); do - if [ "$n" -ge "$WINDOW" ]; then break; fi - t="$(git rev-parse "$c^{tree}")" - if fetch_bench "$t" "$work/base_$n.json"; then - baseline_files+=("$work/base_$n.json"); n=$((n + 1)) - fi -done -[ "${#baseline_files[@]}" -gt 0 ] || die "no bench data for baseline (merge-base $base_commit or predecessors)" - -META_JSON="$(jq -n \ - --arg pr "$pr_commit" --arg base "$base_commit" --arg branch "$BASELINE" \ - --argjson n "${#baseline_files[@]}" \ - '{pr_commit:$pr, merge_base:$base, baseline_branch:$branch, baseline_runs:$n}')" - -python3 - "$META_JSON" "$work/pr.json" "${baseline_files[@]}" <<'PY' > "${OUT:-/dev/stdout}" -import json, sys, math - -meta = json.loads(sys.argv[1]) -pr = sys.argv[2] -bases = sys.argv[3:] - -def load(path): - with open(path) as f: - arr = json.load(f) - out = {} - for e in arr: - try: - out[e["name"]] = (float(e["value"]), e.get("unit", "")) - except (ValueError, TypeError, KeyError): - pass # drop non-numeric / malformed - return out - -pr = load(pr) -bases = [load(p) for p in bases] - -names = set(pr).union(*bases) if bases else set(pr) -rows = [] -for name in names: - vals = [b[name][0] for b in bases if name in b] - unit = next((b[name][1] for b in bases if name in b), pr.get(name, (0, ""))[1]) - prv = pr[name][0] if name in pr else None - mean = sum(vals) / len(vals) if vals else None - std = (math.sqrt(sum((v - mean) ** 2 for v in vals) / len(vals)) if len(vals) > 1 else 0.0) if vals else None - row = {"name": name, "unit": unit, "pr": prv, "baseline": mean, "n": len(vals)} - if prv is None: - row["status"] = "only_in_baseline" - elif mean is None: - row["status"] = "only_in_pr" - else: - row["status"] = "compared" - row["pct"] = ((prv - mean) / abs(mean) * 100) if mean != 0 else None - row["stddev"] = std - row["z"] = ((prv - mean) / std) if std and std > 0 else None - rows.append(row) - -cmp = [r for r in rows if r["status"] == "compared" and r.get("pct") is not None] -cmp.sort(key=lambda r: r["pct"], reverse=True) -only_pr = [r for r in rows if r["status"] == "only_in_pr"] -only_base = [r for r in rows if r["status"] == "only_in_baseline"] -meta["counts"] = {"compared": len(cmp), "only_in_pr": len(only_pr), "only_in_baseline": len(only_base)} -print(json.dumps({"meta": meta, "benches": cmp + only_pr + only_base})) - -def fmt(r): - z = f", z={r['z']:.1f}" if r.get("z") is not None else "" - return f" {r['pct']:+7.1f}% {r['baseline']:.4g} -> {r['pr']:.4g} {r['unit']}{z} {r['name']}" -print(f"\nbaseline: {meta['baseline_branch']} @ {meta['merge_base'][:10]} " - f"(window {meta['baseline_runs']} run(s)) vs PR {meta['pr_commit'][:10]}", file=sys.stderr) -print(f"compared {len(cmp)} | only-in-PR {len(only_pr)} | only-in-baseline {len(only_base)}", file=sys.stderr) -print("\ntop 15 regressions (PR larger; customSmallerIsBetter):", file=sys.stderr) -for r in cmp[:15]: - print(fmt(r), file=sys.stderr) -print("\ntop 5 improvements:", file=sys.stderr) -for r in cmp[-5:][::-1]: - print(fmt(r), file=sys.stderr) -PY - -[ -n "$OUT" ] && echo "bench_compare: wrote $OUT" >&2 -exit 0 diff --git a/ci3/bench_engine b/ci3/bench_engine deleted file mode 100755 index 6e0968a2878..00000000000 --- a/ci3/bench_engine +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env bash -# Uses strict scheduling to run benchmarks in parallel on their own cpus. -# For benchmarks that can't be parallelized, runs them one at a time to avoid resource contention. -NO_CD=1 source $(git rev-parse --show-toplevel)/ci3/source - -bench_cmds_file=$1 - -# Turn hyperthreading off and schedule benches over the bottom half of the logical CPUs -# (one thread per physical core). Timing-sensitive benches (e.g. the p2p client benches) -# degrade nonlinearly when a sibling thread on the same physical core runs other work, so -# we offline the siblings outright. The strict scheduler then pools exactly these physical -# cores via BENCH_CPU_COUNT. -function isolate_bench_cpus { - [ "$CI" -eq 0 ] && return - - # CPU layout assumption: physical cores are 0..N/2-1, hyperthreads are N/2..N-1. - local total_cpus=$(nproc) - local physical=$((total_cpus / 2)) - - for cpu in $(seq $physical $((total_cpus - 1))); do - sudo sh -c "echo 0 > /sys/devices/system/cpu/cpu$cpu/online" 2>/dev/null || true - done - - export BENCH_CPU_COUNT=$physical - echo "Benchmark CPU isolation: CPUs 0-$((physical - 1)) ($physical cores, hyperthreads off) for benchmarks." -} - -function unisolate_bench_cpus { - [ "$CI" -eq 0 ] && return - - echo "Re-enabling all CPUs..." - local total_cpus=$(nproc --all) - for cpu in $(seq 1 $((total_cpus - 1))); do - sudo sh -c "echo 1 > /sys/devices/system/cpu/cpu$cpu/online" 2>/dev/null || true - done - echo "All CPUs re-enabled. Online CPUs: $(nproc)" -} - -isolate_bench_cpus - -# Clean up old benchmark outputs to avoid confusion with new results. -find . -type d -iname bench-out | xargs rm -rf - -# Run parallelizable benchmarks. -cat $bench_cmds_file | grep -v ':PARALLEL=0' | STRICT_SCHEDULING=1 parallelize - -# Run serial benchmarks one at a time (for memory bandwidth / cache isolation). -serial_cmds=$(cat $bench_cmds_file | grep ':PARALLEL=0' || true) -if [ -n "$serial_cmds" ]; then - echo "Running serial benchmarks..." - while IFS= read -r cmd; do - [ -z "$cmd" ] && continue - run_test_cmd "$cmd" - done <<< "$serial_cmds" -fi - -unisolate_bench_cpus diff --git a/ci3/bin/parallel b/ci3/bin/parallel deleted file mode 100755 index d9d66e19922..00000000000 --- a/ci3/bin/parallel +++ /dev/null @@ -1,15360 +0,0 @@ -#!/usr/bin/perl - -# Copyright (C) 2007-2023 Ole Tange, http://ole.tange.dk and Free -# Software Foundation, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, see -# or write to the Free Software Foundation, Inc., 51 Franklin St, -# Fifth Floor, Boston, MA 02110-1301 USA -# -# SPDX-FileCopyrightText: 2007-2023 Ole Tange, http://ole.tange.dk and Free Software and Foundation, Inc. -# SPDX-License-Identifier: GPL-3.0-or-later - -# open3 used in Job::start -use IPC::Open3; -use POSIX; -# gensym used in Job::start -use Symbol qw(gensym); -# tempfile used in Job::start -use File::Temp qw(tempfile tempdir); -# mkpath used in openresultsfile -use File::Path; -# GetOptions used in get_options_from_array -use Getopt::Long; -# Used to ensure code quality -use strict; -use File::Basename; - -sub set_input_source_header($$) { - my ($command_ref,$input_source_fh_ref) = @_; - if(defined $opt::header and not $opt::pipe) { - # split with colsep or \t - # $header force $colsep = \t if undef? - my $delimiter = defined $opt::colsep ? $opt::colsep : "\t"; - # regexp for {= - my $left = "\Q$Global::parensleft\E"; - my $l = $Global::parensleft; - # regexp for =} - my $right = "\Q$Global::parensright\E"; - my $r = $Global::parensright; - if($opt::header ne "0") { - my $id = 1; - for my $fh (@$input_source_fh_ref) { - my $line = <$fh>; - chomp($line); - $line =~ s/\r$//; - ::debug("init", "Delimiter: '$delimiter'"); - for my $s (split /$delimiter/o, $line) { - ::debug("init", "Colname: '$s'"); - # Replace {colname} with {2} - for(@$command_ref, @Global::ret_files, - @Global::transfer_files, $opt::tagstring, - $opt::workdir, $opt::results, $opt::retries, - @Global::template_contents, @Global::template_names, - @opt::filter) { - # Skip if undefined - $_ or next; - s:\{$s(|/|//|\.|/\.)\}:\{$id$1\}:g; - # {=header1 ... =} => {=1 ... =} - s:$left $s (.*?) $right:$l$id$1$r:gx; - } - $Global::input_source_header{$id} = $s; - $id++; - } - } - } - # Make it possible to do: - # parallel --header 0 echo {file2} {file1} :::: file1 file2 - my $id = 1; - for my $s (@opt::a) { - # ::: are put into files and given a filehandle - # ignore these and only keep the filenames. - fileno $s and next; - for(@$command_ref, @Global::ret_files, - @Global::transfer_files, $opt::tagstring, - $opt::workdir, $opt::results, $opt::retries, - @Global::template_contents, @Global::template_names, - @opt::filter) { - # Skip if undefined - $_ or next; - s:\{\Q$s\E(|/|//|\.|/\.)\}:\{$id$1\}:g; - # {=header1 ... =} => {=1 ... =} - s:$left \Q$s\E (.*?) $right:$l$id$1$r:gx; - } - $Global::input_source_header{$id} = $s; - $id++; - } - } else { - my $id = 1; - for my $fh (@$input_source_fh_ref) { - $Global::input_source_header{$id} = $id; - $id++; - } - } -} - -sub max_jobs_running() { - # Compute $Global::max_jobs_running as the max number of jobs - # running on each sshlogin. - # Returns: - # $Global::max_jobs_running - if(not $Global::max_jobs_running) { - for my $sshlogin (values %Global::host) { - $sshlogin->max_jobs_running(); - } - } - if(not $Global::max_jobs_running) { - ::error("Cannot run any jobs."); - wait_and_exit(255); - } - return $Global::max_jobs_running; -} - -sub halt() { - # Compute exit value, - # wait for children to complete - # and exit - if($opt::halt and $Global::halt_when ne "never") { - if(not defined $Global::halt_exitstatus) { - if($Global::halt_pct) { - $Global::halt_exitstatus = - ::ceil($Global::total_failed / - ($Global::total_started || 1) * 100); - } elsif($Global::halt_count) { - $Global::halt_exitstatus = - ::min(undef_as_zero($Global::total_failed),101); - } - } - wait_and_exit($Global::halt_exitstatus); - } else { - if($Global::semaphore) { - # --semaphore runs a single job: - # Use exit value of that - wait_and_exit($Global::halt_exitstatus); - } else { - # 0 = all jobs succeeded - # 1-100 = n jobs failed - # 101 = >100 jobs failed - wait_and_exit(min(undef_as_zero($Global::exitstatus),101)); - } - } -} - - -sub __PIPE_MODE__() {} - - -sub pipepart_setup() { - # Compute the blocksize - # Generate the commands to extract the blocks - # Push the commands on queue - # Changes: - # @Global::cat_prepends - # $Global::JobQueue - if($opt::tee) { - # Prepend each command with - # < file - my $cat_string = "< ".Q($opt::a[0]); - for(1..$Global::JobQueue->total_jobs()) { - push @Global::cat_appends, $cat_string; - push @Global::cat_prepends, ""; - } - } else { - if(not $opt::blocksize) { - # --blocksize with 10 jobs per jobslot - $opt::blocksize = -10; - } - if($opt::roundrobin) { - # --blocksize with 1 job per jobslot - $opt::blocksize = -1; - } - if($opt::blocksize < 0) { - my $size = 0; - # Compute size of -a - for(@opt::a) { - if(-f $_) { - $size += -s $_; - } elsif(-b $_) { - $size += size_of_block_dev($_); - } elsif(-e $_) { - ::error("$_ is neither a file nor a block device"); - wait_and_exit(255); - } else { - ::error("File not found: $_"); - wait_and_exit(255); - } - } - # Run in total $job_slots*(- $blocksize) jobs - # Set --blocksize = size / no of proc / (- $blocksize) - $Global::dummy_jobs = 1; - $Global::blocksize = 1 + - int($size / max_jobs_running() / - -multiply_binary_prefix($opt::blocksize)); - } - @Global::cat_prepends = (map { pipe_part_files($_) } - # ::: are put into files and given a filehandle - # ignore these and only keep the filenames. - grep { ! fileno $_ } @opt::a); - # Unget the empty arg as many times as there are parts - $Global::JobQueue->{'commandlinequeue'}{'arg_queue'}->unget( - map { [Arg->new("\0noarg")] } @Global::cat_prepends - ); - } -} - -sub pipe_tee_setup() { - # Create temporary fifos - # Run 'tee fifo1 fifo2 fifo3 ... fifoN' in the background - # This will spread the input to fifos - # Generate commands that reads from fifo1..N: - # cat fifo | user_command - # Changes: - # @Global::cat_prepends - my @fifos; - for(1..$Global::JobQueue->total_jobs()) { - push @fifos, tmpfifo(); - } - # cat foo | tee fifo1 fifo2 fifo3 fifo4 fifo5 > /dev/null - if(not fork()){ - # Test if tee supports --output-error=warn-nopipe - `echo | tee --output-error=warn-nopipe /dev/null >/dev/null 2>/dev/null`; - my $opt = $? ? "" : "--output-error=warn-nopipe"; - ::debug("init","tee $opt"); - if($opt::dryrun) { - # This is not exactly what is run, but it gives the basic idea - print "mkfifo @fifos\n"; - print "tee $opt @fifos >/dev/null &\n"; - } else { - # Let tee inherit our stdin - # and redirect stdout to null - open STDOUT, ">","/dev/null"; - if($opt) { - exec "tee", $opt, @fifos; - } else { - exec "tee", @fifos; - } - } - exit(0); - } - # For each fifo - # (rm fifo1; grep 1) < fifo1 - # (rm fifo2; grep 2) < fifo2 - # (rm fifo3; grep 3) < fifo3 - # Remove the tmpfifo as soon as it is open - @Global::cat_prepends = map { "(rm $_;" } shell_quote(@fifos); - @Global::cat_appends = map { ") < $_" } shell_quote(@fifos); -} - - -sub parcat_script() { - # TODO if script fails: Use parallel -j0 --plain --lb cat ::: fifos - my $script = q'{ - use POSIX qw(:errno_h); - use IO::Select; - use strict; - use threads; - use Thread::Queue; - use Fcntl qw(:DEFAULT :flock); - - my $opened :shared; - my $q = Thread::Queue->new(); - my $okq = Thread::Queue->new(); - my @producers; - - if(not @ARGV) { - if(-t *STDIN) { - print "Usage:\n"; - print " parcat file(s)\n"; - print " cat argfile | parcat\n"; - } else { - # Read arguments from stdin - chomp(@ARGV = ); - } - } - my $files_to_open = 0; - # Default: fd = stdout - my $fd = 1; - for (@ARGV) { - # --rm = remove file when opened - /^--rm$/ and do { $opt::rm = 1; next; }; - # -1 = output to fd 1, -2 = output to fd 2 - /^-(\d+)$/ and do { $fd = $1; next; }; - push @producers, threads->create("producer", $_, $fd); - $files_to_open++; - } - - sub producer { - # Open a file/fifo, set non blocking, enqueue fileno of the file handle - my $file = shift; - my $output_fd = shift; - open(my $fh, "<", $file) || do { - print STDERR "parcat: Cannot open $file\n"; - exit(1); - }; - # Remove file when it has been opened - if($opt::rm) { - unlink $file; - } - set_fh_non_blocking($fh); - $opened++; - # Pass the fileno to parent - $q->enqueue(fileno($fh),$output_fd); - # Get an OK that the $fh is opened and we can release the $fh - while(1) { - my $ok = $okq->dequeue(); - if($ok == fileno($fh)) { last; } - # Not ours - very unlikely to happen - $okq->enqueue($ok); - } - return; - } - - my $s = IO::Select->new(); - my %buffer; - - sub add_file { - my $infd = shift; - my $outfd = shift; - open(my $infh, "<&=", $infd) || die; - open(my $outfh, ">&=", $outfd) || die; - $s->add($infh); - # Tell the producer now opened here and can be released - $okq->enqueue($infd); - # Initialize the buffer - @{$buffer{$infh}{$outfd}} = (); - $Global::fh{$outfd} = $outfh; - } - - sub add_files { - # Non-blocking dequeue - my ($infd,$outfd); - do { - ($infd,$outfd) = $q->dequeue_nb(2); - if(defined($outfd)) { - add_file($infd,$outfd); - } - } while(defined($outfd)); - } - - sub add_files_block { - # Blocking dequeue - my ($infd,$outfd) = $q->dequeue(2); - add_file($infd,$outfd); - } - - - my $fd; - my (@ready,$infh,$rv,$buf); - do { - # Wait until at least one file is opened - add_files_block(); - while($q->pending or keys %buffer) { - add_files(); - while(keys %buffer) { - @ready = $s->can_read(0.01); - if(not @ready) { - add_files(); - } - for $infh (@ready) { - # There is only one key, namely the output file descriptor - for my $outfd (keys %{$buffer{$infh}}) { - # TODO test if 60800 is optimal (2^17 is used elsewhere) - $rv = sysread($infh, $buf, 60800); - if (!$rv) { - if($! == EAGAIN) { - # Would block: Nothing read - next; - } else { - # Nothing read, but would not block: - # This file is done - $s->remove($infh); - for(@{$buffer{$infh}{$outfd}}) { - syswrite($Global::fh{$outfd},$_); - } - delete $buffer{$infh}; - # Closing the $infh causes it to block - # close $infh; - add_files(); - next; - } - } - # Something read. - # Find \n or \r for full line - my $i = (rindex($buf,"\n")+1); - if($i) { - # Print full line - for(@{$buffer{$infh}{$outfd}}, substr($buf,0,$i)) { - syswrite($Global::fh{$outfd},$_); - } - # @buffer = remaining half line - $buffer{$infh}{$outfd} = [substr($buf,$i,$rv-$i)]; - } else { - # Something read, but not a full line - push @{$buffer{$infh}{$outfd}}, $buf; - } - redo; - } - } - } - } - } while($opened < $files_to_open); - - for (@producers) { - $_->join(); - } - - sub set_fh_non_blocking { - # Set filehandle as non-blocking - # Inputs: - # $fh = filehandle to be blocking - # Returns: - # N/A - my $fh = shift; - my $flags; - fcntl($fh, &F_GETFL, $flags) || die $!; # Get the current flags on the filehandle - $flags |= &O_NONBLOCK; # Add non-blocking to the flags - fcntl($fh, &F_SETFL, $flags) || die $!; # Set the flags on the filehandle - } - }'; - return ::spacefree(3, $script); -} - -sub sharder_script() { - my $script = q{ - use B; - # Column separator - my $sep = shift; - # Which columns to shard on (count from 1) - my $col = shift; - # Which columns to shard on (count from 0) - my $col0 = $col - 1; - # Perl expression - my $perlexpr = shift; - my $bins = @ARGV; - # Open fifos for writing, fh{0..$bins} - my $t = 0; - my %fh; - for(@ARGV) { - open $fh{$t++}, ">", $_; - # open blocks until it is opened by reader - # so unlink only happens when it is ready - unlink $_; - } - if($perlexpr) { - my $subref = eval("sub { no strict; no warnings; $perlexpr }"); - while() { - # Split into $col columns (no need to split into more) - @F = split $sep, $_, $col+1; - { - local $_ = $F[$col0]; - &$subref(); - $fh = $fh{ hex(B::hash($_))%$bins }; - } - print $fh $_; - } - } else { - while() { - # Split into $col columns (no need to split into more) - @F = split $sep, $_, $col+1; - $fh = $fh{ hex(B::hash($F[$col0]))%$bins }; - print $fh $_; - } - } - # Close all open fifos - close values %fh; - }; - return ::spacefree(1, $script); -} - -sub binner_script() { - my $script = q{ - use B; - # Column separator - my $sep = shift; - # Which columns to shard on (count from 1) - my $col = shift; - # Which columns to shard on (count from 0) - my $col0 = $col - 1; - # Perl expression - my $perlexpr = shift; - my $bins = @ARGV; - # Open fifos for writing, fh{0..$bins} - my $t = 0; - my %fh; - # Let the last output fifo be the 0'th - open $fh{$t++}, ">", pop @ARGV; - for(@ARGV) { - open $fh{$t++}, ">", $_; - # open blocks until it is opened by reader - # so unlink only happens when it is ready - unlink $_; - } - if($perlexpr) { - my $subref = eval("sub { no strict; no warnings; $perlexpr }"); - while() { - # Split into $col columns (no need to split into more) - @F = split $sep, $_, $col+1; - { - local $_ = $F[$col0]; - &$subref(); - $fh = $fh{ $_%$bins }; - } - print $fh $_; - } - } else { - while() { - # Split into $col columns (no need to split into more) - @F = split $sep, $_, $col+1; - $fh = $fh{ $F[$col0]%$bins }; - print $fh $_; - } - } - # Close all open fifos - close values %fh; - }; - return ::spacefree(1, $script); -} - -sub pipe_shard_setup() { - # Create temporary fifos - # Run 'shard.pl sep col fifo1 fifo2 fifo3 ... fifoN' in the background - # This will spread the input to fifos - # Generate commands that reads from fifo1..N: - # cat fifo | user_command - # Changes: - # @Global::cat_prepends - my @shardfifos; - my @parcatfifos; - # TODO $opt::jobs should be evaluated (100%) - # TODO $opt::jobs should be number of total_jobs if there are arguments - max_jobs_running(); - my $njobs = $Global::max_jobs_running; - for my $m (0..$njobs-1) { - for my $n (0..$njobs-1) { - # sharding to A B C D - # parcatting all As together - $parcatfifos[$n][$m] = $shardfifos[$m][$n] = tmpfifo(); - } - } - my $shardbin = ($opt::shard || $opt::bin); - my $script; - if($opt::bin) { - $script = binner_script(); - } else { - $script = sharder_script(); - } - - # cat foo | sharder sep col fifo1 fifo2 fifo3 ... fifoN - - if($shardbin =~ /^[a-z_][a-z_0-9]*(\s|$)/i) { - # Group by column name - # (Yes, this will also wrongly match a perlexpr like: chop) - my($read,$char,@line); - # A full line, but nothing more (the rest must be read by the child) - # $Global::header used to prepend block to each job - do { - $read = sysread(STDIN,$char,1); - push @line, $char; - } while($read and $char ne "\n"); - $Global::header = join "", @line; - } - my ($col, $perlexpr, $subref) = - column_perlexpr($shardbin, $Global::header, $opt::colsep); - if(not fork()) { - # Let the sharder inherit our stdin - # and redirect stdout to null - open STDOUT, ">","/dev/null"; - # The PERL_HASH_SEED must be the same for all sharders - # so B::hash will return the same value for any given input - $ENV{'PERL_HASH_SEED'} = $$; - exec qw(parallel -0 --block 100k -q --pipe -j), $njobs, - qw(--roundrobin -u perl -e), $script, ($opt::colsep || ","), - $col, $perlexpr, '{}', (map { (':::+', @{$_}) } @parcatfifos); - } - # For each fifo - # (rm fifo1; grep 1) < fifo1 - # (rm fifo2; grep 2) < fifo2 - # (rm fifo3; grep 3) < fifo3 - my $parcat = Q(parcat_script()); - if(not $parcat) { - ::error("'parcat' must be in path."); - ::wait_and_exit(255); - } - @Global::cat_prepends = - map { "perl -e $parcat ". - join(" ",shell_quote(@$_))." | "} @parcatfifos; -} - -sub pipe_part_files(@) { - # Given the bigfile - # find header and split positions - # make commands that 'cat's the partial file - # Input: - # $file = the file to read - # Returns: - # @commands that will cat_partial each part - my ($file) = @_; - my $buf = ""; - if(not -f $file and not -b $file) { - ::error("--pipepart only works on seekable files, not streams/pipes.", - "$file is not a seekable file."); - ::wait_and_exit(255); - } - - my $fh = open_or_exit($file); - my $firstlinelen = 0; - if($opt::skip_first_line) { - my $newline; - # Read a full line one byte at a time - while($firstlinelen += sysread($fh,$newline,1,0)) { - $newline eq "\n" and last; - } - } - my $header = find_header(\$buf,$fh); - # find positions - my @pos = find_split_positions($file,int($Global::blocksize), - $header,$firstlinelen); - # Make @cat_prepends - my @cat_prepends = (); - for(my $i=0; $i<$#pos; $i++) { - push(@cat_prepends, - cat_partial($file, $firstlinelen, $firstlinelen+length($header), - $pos[$i], $pos[$i+1])); - } - return @cat_prepends; -} - -sub find_header($$) { - # Compute the header based on $opt::header - # Input: - # $buf_ref = reference to read-in buffer - # $fh = filehandle to read from - # Uses: - # $opt::header - # $Global::blocksize - # $Global::header - # Returns: - # $header string - my ($buf_ref, $fh) = @_; - my $header = ""; - # $Global::header may be set in group_by_loop() - if($Global::header) { return $Global::header } - if($opt::header) { - if($opt::header eq ":") { $opt::header = "(.*\n)"; } - # Number = number of lines - $opt::header =~ s/^(\d+)$/"(.*\n)"x$1/e; - while(sysread($fh,$$buf_ref,int($Global::blocksize),length $$buf_ref)) { - if($$buf_ref =~ s/^($opt::header)//) { - $header = $1; - last; - } - } - } - return $header; -} - -sub find_split_positions($$$) { - # Find positions in bigfile where recend is followed by recstart - # Input: - # $file = the file to read - # $block = (minimal) --block-size of each chunk - # $header = header to be skipped - # Uses: - # $opt::recstart - # $opt::recend - # Returns: - # @positions of block start/end - my($file, $block, $header, $firstlinelen) = @_; - my $skiplen = $firstlinelen + length $header; - my $size = -s $file; - if(-b $file) { - # $file is a blockdevice - $size = size_of_block_dev($file); - } - $block = int $block; - if($opt::groupby) { - return split_positions_for_group_by($file,$size,$block, - $header,$firstlinelen); - } - # The optimal dd blocksize for mint, redhat, solaris, openbsd = 2^17..2^20 - # The optimal dd blocksize for freebsd = 2^15..2^17 - # The optimal dd blocksize for ubuntu (AMD6376) = 2^16 - my $dd_block_size = 131072; # 2^17 - my @pos; - my ($recstart,$recend) = recstartrecend(); - my $recendrecstart = $recend.$recstart; - my $fh = ::open_or_exit($file); - push(@pos,$skiplen); - for(my $pos = $block+$skiplen; $pos < $size; $pos += $block) { - my $buf; - if($recendrecstart eq "") { - # records ends anywhere - push(@pos,$pos); - } else { - # Seek the the block start - if(not sysseek($fh, $pos, 0)) { - ::error("Cannot seek to $pos in $file"); - edit(255); - } - while(sysread($fh,$buf,$dd_block_size,length $buf)) { - if($opt::regexp) { - # If match /$recend$recstart/ => Record position - if($buf =~ m:^(.*$recend)$recstart:os) { - # Start looking for next record _after_ this match - $pos += length($1); - push(@pos,$pos); - last; - } - } else { - # If match $recend$recstart => Record position - # TODO optimize to only look at the appended - # $dd_block_size + len $recendrecstart - # TODO increase $dd_block_size to optimize for longer records - my $i = index64(\$buf,$recendrecstart); - if($i != -1) { - # Start looking for next record _after_ this match - $pos += $i + length($recend); - push(@pos,$pos); - last; - } - } - } - } - } - if($pos[$#pos] != $size) { - # Last splitpoint was not at end of the file: add $size as the last - push @pos, $size; - } - close $fh; - return @pos; -} - -sub split_positions_for_group_by($$$$) { - my($fh); - my %value; - sub value_at($) { - my $pos = shift; - if(not defined $value{$pos}) { - if($pos != 0) { - seek($fh, $pos-1, 0) || die; - # Read half line - <$fh>; - } - # Read full line - my $linepos = tell($fh); - if(not defined $value{$linepos}) { - $_ = <$fh>; - if(defined $_) { - # Not end of file - my @F; - if(defined $group_by::col) { - $opt::colsep ||= "\t"; - @F = split /$opt::colsep/, $_; - $_ = $F[$group_by::col]; - } - eval $group_by::perlexpr; - } - $value{$linepos} = [$_,$linepos]; - } - $value{$pos} = $value{$linepos}; - } - return (@{$value{$pos}}); - } - - sub binary_search_end($$$) { - my ($s,$spos,$epos) = @_; - # value_at($spos) == $s - # value_at($epos) != $s - my $posdif = $epos - $spos; - my ($v,$vpos); - while($posdif) { - ($v,$vpos) = value_at($spos+$posdif); - if($v eq $s) { - $spos = $vpos; - $posdif = $epos - $spos; - } else { - $epos = $vpos; - } - $posdif = int($posdif/2); - } - return($v,$vpos); - } - - sub binary_search_start($$$) { - my ($s,$spos,$epos) = @_; - # value_at($spos) != $s - # value_at($epos) == $s - my $posdif = $epos - $spos; - my ($v,$vpos); - while($posdif) { - ($v,$vpos) = value_at($spos+$posdif); - if($v eq $s) { - $epos = $vpos; - } else { - $spos = $vpos; - $posdif = $epos - $spos; - } - $posdif = int($posdif/2); - } - return($v,$vpos); - } - - my ($file,$size,$block,$header,$firstlinelen) = @_; - my @pos; - $fh = open_or_exit($file); - # Set $Global::group_by_column $Global::group_by_perlexpr - group_by_loop($fh,$opt::recsep); - if($opt::max_args) { - # Split after n values - my ($a,$apos); - # $xpos = linestart, $x = value at $xpos - $apos = $firstlinelen + length $header; - for(($a,$apos) = value_at($apos); $apos < $size;) { - push @pos, $apos; - ($a,$apos) = binary_search_end($a,$apos,$size); - if(eof($fh)) { - push @pos, $size; last; - } - } - # @pos = start of every value - # Merge n values - # -nX = keep every X'th position - my $i = 0; - @pos = grep { not ($i++ % $opt::max_args) } @pos; - } else { - # Split after any value group - # Preferable < $blocksize - my ($a,$b,$c,$apos,$bpos,$cpos); - # $xpos = linestart, $x = value at $xpos, $apos < $bpos < $cpos - $apos = $firstlinelen + length $header; - for(($a,$apos) = value_at($apos); $apos < $size;) { - push @pos, $apos; - $bpos = $apos + $block; - ($b,$bpos) = value_at($bpos); - if(eof($fh)) { - # EOF is less than 1 block away - push @pos, $size; last; - } - $cpos = $bpos + $block; - ($c,$cpos) = value_at($cpos); - if($a eq $b) { - while($b eq $c) { - # Move bpos, cpos a block forward until $a == $b != $c - $bpos = $cpos; - $cpos += $block; - ($c,$cpos) = value_at($cpos); - if($cpos >= $size) { - $cpos = $size; - last; - } - } - # $a == $b != $c - # Binary search for $b ending between ($bpos,$cpos) - ($b,$bpos) = binary_search_end($b,$bpos,$cpos); - } else { - if($b eq $c) { - # $a != $b == $c - # Binary search for $b starting between ($apos,$bpos) - ($b,$bpos) = binary_search_start($b,$apos,$bpos); - } else { - # $a != $b != $c - # Binary search for $b ending between ($bpos,$cpos) - ($b,$bpos) = binary_search_end($b,$bpos,$cpos); - } - } - ($a,$apos) = ($b,$bpos); - } - } - if($pos[$#pos] != $size) { - # Last splitpoint was not at end of the file: add it - push @pos, $size; - } - return @pos; -} - -sub cat_partial($@) { - # Efficient command to copy from byte X to byte Y - # Input: - # $file = the file to read - # ($start, $end, [$start2, $end2, ...]) = start byte, end byte - # Returns: - # Efficient command to copy $start..$end, $start2..$end2, ... to stdout - my($file, @start_end) = @_; - my($start, $i); - # Convert (start,end) to (start,len) - my @start_len = map { - if(++$i % 2) { $start = $_; } else { $_-$start } - } @start_end; - # The optimal block size differs - # It has been measured on: - # AMD 6376: n*4k-1; small n - # AMD Neo N36L: 44k-200k - # Intel i7-3632QM: 55k- - # ARM Cortex A53: 4k-28k - # Intel i5-2410M: 36k-46k - # - # I choose 2^15-1 = 32767 - # q{ - # expseq() { - # perl -E ' - # $last = pop @ARGV; - # $first = shift || 1; - # $inc = shift || 1.03; - # for($i=$first; $i<=$last;$i*=$inc) { say int $i } - # ' "$@" - # } - # - # seq 111111111 > big; - # f() { ppar --test $1 -a big --pipepart --block -1 'md5sum > /dev/null'; } - # export -f f; - # expseq 1000 1.001 300000 | shuf | parallel -j1 --jl jl-md5sum f; - # }; - my $script = spacefree - (0, - q{ - while(@ARGV) { - sysseek(STDIN,shift,0) || die; - $left = shift; - while($read = - sysread(STDIN,$buf, $left > 32767 ? 32767 : $left)){ - $left -= $read; - syswrite(STDOUT,$buf); - } - } - }); - return "<". Q($file) . - " perl -e '$script' @start_len |"; -} - -sub column_perlexpr($$$) { - # Compute the column number (if any), perlexpression from combined - # string (such as --shard key, --groupby key, {=n perlexpr=} - # Input: - # $column_perlexpr = string with column and perl expression - # $header = header from input file (if column is column name) - # $colsep = column separator regexp - # Returns: - # $col = column number - # $perlexpr = perl expression - # $subref = compiled perl expression as sub reference - my ($column_perlexpr, $header, $colsep) = @_; - my ($col, $perlexpr, $subref); - if($column_perlexpr =~ /^[-a-z0-9_]+(\s|$)/i) { - # Column name/number (possibly prefix) - if($column_perlexpr =~ s/^(-?\d+)(\s|$)//) { - # Column number (possibly prefix) - $col = $1; - } elsif($column_perlexpr =~ s/^([a-z0-9_]+)(\s+|$)//i) { - # Column name (possibly prefix) - my $colname = $1; - # Split on --copsep pattern - my @headers = split /$colsep/, $header; - my %headers; - @headers{@headers} = (1..($#headers+1)); - $col = $headers{$colname}; - if(not defined $col) { - ::error("Column '$colname' $colsep not found in header",keys %headers); - ::wait_and_exit(255); - } - } - } - # What is left of $column_perlexpr is $perlexpr (possibly empty) - $perlexpr = $column_perlexpr; - $subref = eval("sub { no strict; no warnings; $perlexpr }"); - return($col, $perlexpr, $subref); -} - -sub group_by_loop($$) { - # Generate perl code for group-by loop - # Insert a $recsep when the column value changes - # The column value can be computed with $perlexpr - my($fh,$recsep) = @_; - my $groupby = $opt::groupby; - if($groupby =~ /^[a-z_][a-z_0-9]*(\s|$)/i) { - # Group by column name - # (Yes, this will also wrongly match a perlexpr like: chop) - my($read,$char,@line); - # Read a full line, but nothing more - # (the rest must be read by the child) - # $Global::header used to prepend block to each job - do { - $read = sysread($fh,$char,1); - push @line, $char; - } while($read and $char ne "\n"); - $Global::header = join "", @line; - } - $opt::colsep ||= "\t"; - ($group_by::col, $group_by::perlexpr, $group_by::subref) = - column_perlexpr($groupby, $Global::header, $opt::colsep); - # Numbered 0..n-1 due to being used by $F[n] - if($group_by::col) { $group_by::col--; } - - my $loop = ::spacefree(0,q{ - BEGIN{ $last = "RECSEP"; } - { - local $_=COLVALUE; - PERLEXPR; - if(($last) ne $_) { - print "RECSEP"; - $last = $_; - } - } - }); - if(defined $group_by::col) { - $loop =~ s/COLVALUE/\$F[$group_by::col]/g; - } else { - $loop =~ s/COLVALUE/\$_/g; - } - $loop =~ s/PERLEXPR/$group_by::perlexpr/g; - $loop =~ s/RECSEP/$recsep/g; - return $loop; -} - -sub pipe_group_by_setup() { - # Record separator with 119 bit random value - $opt::recend = ''; - $opt::recstart = - join "", map { (0..9,"a".."z","A".."Z")[rand(62)] } (1..20); - $opt::remove_rec_sep = 1; - my @filter; - push @filter, "perl"; - if($opt::groupby =~ /^[a-z0-9_]+(\s|$)/i) { - # This is column number/name - # Use -a (auto-split) - push @filter, "-a"; - $opt::colsep ||= "\t"; - my $sep = $opt::colsep; - $sep =~ s/\t/\\t/g; - $sep =~ s/\"/\\"/g; - # man perlrun: -Fpattern [...] You can't use literal whitespace - $sep =~ s/ /\\040/g; - push @filter, "-F$sep"; - } - push @filter, "-pe"; - push @filter, group_by_loop(*STDIN,$opt::recstart); - ::debug("init", "@filter\n"); - open(STDIN, '-|', @filter) || die ("Cannot start @filter"); - if(which("mbuffer")) { - # You get a speed up of 30% by going through mbuffer - open(STDIN, '-|', "mbuffer", "-q","-m6M","-b5") || - die ("Cannot start mbuffer"); - } -} - -sub spreadstdin() { - # read a record - # Spawn a job and print the record to it. - # Uses: - # $Global::blocksize - # STDIN - # $opt::r - # $Global::max_lines - # $Global::max_number_of_args - # $opt::regexp - # $Global::start_no_new_jobs - # $opt::roundrobin - # %Global::running - # Returns: N/A - - my $buf = ""; - my ($recstart,$recend) = recstartrecend(); - my $recendrecstart = $recend.$recstart; - my $chunk_number = 1; - my $one_time_through; - my $two_gb = 2**31-1; - my $blocksize = int($Global::blocksize); - my $in = *STDIN; - my $timeout = $Global::blocktimeout; - - if($opt::skip_first_line) { - my $newline; - # Read a full line one byte at a time - while(sysread($in,$newline,1,0)) { - $newline eq "\n" and last; - } - } - my $header = find_header(\$buf,$in); - my $anything_written; - my $eof; - my $garbage_read; - - sub read_block() { - # Read a --blocksize from STDIN - # possibly interrupted by --blocktimeout - # Add up to the next full block - my $readsize = $blocksize - (length $buf) % $blocksize; - my ($nread,$alarm); - eval { - local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required - # --blocktimeout (or 0 if not set) - alarm $timeout; - if($] >= 5.026) { - do { - $nread = sysread $in, $buf, $readsize, length $buf; - $readsize -= $nread; - } while($readsize and $nread); - } else { - # Less efficient reading, but 32-bit sysread compatible - do { - $nread = sysread($in,substr($buf,length $buf,0),$readsize,0); - $readsize -= $nread; - } while($readsize and $nread); - } - alarm 0; - }; - if ($@) { - die unless $@ eq "alarm\n"; # propagate unexpected errors - $alarm = 1; - } else { - $alarm = 0; - } - $eof = not ($nread or $alarm); - } - - sub pass_n_line_records() { - # Pass records of N lines - my $n_lines = $buf =~ tr/\n/\n/; - my $last_newline_pos = rindex64(\$buf,"\n"); - # Go backwards until there are full n-line records - while($n_lines % $Global::max_lines) { - $n_lines--; - $last_newline_pos = rindex64(\$buf,"\n",$last_newline_pos-1); - } - # Chop at $last_newline_pos as that is where n-line record ends - $anything_written += - write_record_to_pipe($chunk_number++,\$header,\$buf, - $recstart,$recend,$last_newline_pos+1); - shorten(\$buf,$last_newline_pos+1); - } - - sub pass_n_regexps() { - # Pass records of N regexps - # -N => (start..*?end){n} - # -L -N => (start..*?end){n*l} - if(not $garbage_read) { - $garbage_read = 1; - if($buf !~ /^$recstart/o) { - # Buf does not start with $recstart => There is garbage. - # Make a single record of the garbage - if($buf =~ - /(?s:^)( - (?:(?:(?!$recend$recstart)(?s:.))*?$recend) - ) - # Followed by recstart - (?=$recstart)/mox and length $1 > 0) { - $anything_written += - write_record_to_pipe($chunk_number++,\$header,\$buf, - $recstart,$recend,length $1); - shorten(\$buf,length $1); - } - } - } - - my $n_records = - $Global::max_number_of_args * ($Global::max_lines || 1); - # (?!negative lookahead) is needed to avoid backtracking - # See: https://unix.stackexchange.com/questions/439356/ - # (?s:.) = (.|[\n]) but faster - while($buf =~ - /(?s:^)( - # n more times recstart.*recend - (?:$recstart(?:(?!$recend$recstart)(?s:.))*?$recend){$n_records} - ) - # Followed by recstart - (?=$recstart)/mox and length $1 > 0) { - $anything_written += - write_record_to_pipe($chunk_number++,\$header,\$buf, - $recstart,$recend,length $1); - shorten(\$buf,length $1); - } - } - - sub pass_regexp() { - # Find the last recend-recstart in $buf - $eof and return; - # (?s:.) = (.|[\n]) but faster - if($buf =~ /^((?s:.)*$recend)$recstart(?s:.)*?$/mox) { - $anything_written += - write_record_to_pipe($chunk_number++,\$header,\$buf, - $recstart,$recend,length $1); - shorten(\$buf,length $1); - } - } - - sub pass_csv_record() { - # Pass CVS record - # We define a CSV record as an even number of " + end of line - # This works if you use " as quoting character - my $last_newline_pos = length $buf; - # Go backwards from the last \n and search for a position - # where there is an even number of " - do { - # find last EOL - $last_newline_pos = rindex64(\$buf,"\n",$last_newline_pos-1); - # While uneven " - } while((substr($buf,0,$last_newline_pos) =~ y/"/"/)%2 - and $last_newline_pos >= 0); - # Chop at $last_newline_pos as that is where CSV record ends - $anything_written += - write_record_to_pipe($chunk_number++,\$header,\$buf, - $recstart,$recend,$last_newline_pos+1); - shorten(\$buf,$last_newline_pos+1); - } - - sub pass_n() { - # Pass n records of --recend/--recstart - # -N => (start..*?end){n} - my $i = 0; - my $read_n_lines = - $Global::max_number_of_args * ($Global::max_lines || 1); - while(($i = nindex(\$buf,$recendrecstart,$read_n_lines)) != -1 - and - length $buf) { - $i += length $recend; # find the actual splitting location - $anything_written += - write_record_to_pipe($chunk_number++,\$header,\$buf, - $recstart,$recend,$i); - shorten(\$buf,$i); - } - } - - sub pass() { - # Pass records of --recend/--recstart - # Split record at fixed string - # Find the last recend+recstart in $buf - $eof and return; - my $i = rindex64(\$buf,$recendrecstart); - if($i != -1) { - $i += length $recend; # find the actual splitting location - $anything_written += - write_record_to_pipe($chunk_number++,\$header,\$buf, - $recstart,$recend,$i); - shorten(\$buf,$i); - } - } - - sub increase_blocksize_maybe() { - if(not $anything_written - and not $opt::blocktimeout - and not $Global::no_autoexpand_block) { - # Nothing was written - maybe the block size < record size? - # Increase blocksize exponentially up to 2GB-1 (2GB causes problems) - if($blocksize < $two_gb) { - my $old_blocksize = $blocksize; - $blocksize = ::min(ceil($blocksize * 1.3 + 1), $two_gb); - ::warning("A record was longer than $old_blocksize. " . - "Increasing to --blocksize $blocksize."); - } - } - } - - while(1) { - $anything_written = 0; - read_block(); - if($opt::r) { - # Remove empty lines - $buf =~ s/^\s*\n//gm; - if(length $buf == 0) { - if($eof) { - last; - } else { - next; - } - } - } - if($Global::max_lines and not $Global::max_number_of_args) { - # Pass n-line records - pass_n_line_records(); - } elsif($opt::csv) { - # Pass a full CSV record - pass_csv_record(); - } elsif($opt::regexp) { - # Split record at regexp - if($Global::max_number_of_args) { - pass_n_regexps(); - } else { - pass_regexp(); - } - } else { - # Pass normal --recend/--recstart record - if($Global::max_number_of_args) { - pass_n(); - } else { - pass(); - } - } - $eof and last; - increase_blocksize_maybe(); - ::debug("init", "Round\n"); - } - ::debug("init", "Done reading input\n"); - - # If there is anything left in the buffer write it - write_record_to_pipe($chunk_number++, \$header, \$buf, $recstart, - $recend, length $buf); - - if($opt::retries) { - $Global::no_more_input = 1; - # We need to start no more jobs: At most we need to retry some - # of the already running. - my @running = values %Global::running; - # Stop any virgins. - for my $job (@running) { - if(defined $job and $job->virgin()) { - close $job->fh(0,"w"); - } - } - # Wait for running jobs to be done - my $sleep = 1; - while($Global::total_running > 0) { - $sleep = ::reap_usleep($sleep); - start_more_jobs(); - } - } - $Global::start_no_new_jobs ||= 1; - if($opt::roundrobin) { - # Flush blocks to roundrobin procs - my $sleep = 1; - while(%Global::running) { - my $something_written = 0; - for my $job (values %Global::running) { - if($job->block_length()) { - $something_written += $job->non_blocking_write(); - } else { - close $job->fh(0,"w"); - } - } - if($something_written) { - $sleep = $sleep/2+0.001; - } - $sleep = ::reap_usleep($sleep); - } - } -} - -sub recstartrecend() { - # Uses: - # $opt::recstart - # $opt::recend - # Returns: - # $recstart,$recend with default values and regexp conversion - my($recstart,$recend); - if(defined($opt::recstart) and defined($opt::recend)) { - # If both --recstart and --recend is given then both must match - $recstart = $opt::recstart; - $recend = $opt::recend; - } elsif(defined($opt::recstart)) { - # If --recstart is given it must match start of record - $recstart = $opt::recstart; - $recend = ""; - } elsif(defined($opt::recend)) { - # If --recend is given then it must match end of record - $recstart = ""; - $recend = $opt::recend; - if($opt::regexp and $recend eq '') { - # --regexp --recend '' - $recend = '(?s:.)'; - } - } - - if($opt::regexp) { - # Do not allow /x comments - to avoid having to quote space - $recstart = "(?-x:".$recstart.")"; - $recend = "(?-x:".$recend.")"; - # If $recstart/$recend contains '|' - # the | should only apply to the regexp - $recstart = "(?:".$recstart.")"; - $recend = "(?:".$recend.")"; - } else { - # $recstart/$recend = printf strings (\n) - $recstart =~ s/\\([0rnt\'\"\\])/"qq|\\$1|"/gee; - $recend =~ s/\\([0rnt\'\"\\])/"qq|\\$1|"/gee; - } - return ($recstart,$recend); -} - -sub nindex($$) { - # See if string is in buffer N times - # Returns: - # the position where the Nth copy is found - my ($buf_ref, $str, $n) = @_; - my $i = 0; - for(1..$n) { - $i = index64($buf_ref,$str,$i+1); - if($i == -1) { last } - } - return $i; -} - -{ - my @robin_queue; - my $sleep = 1; - - sub round_robin_write($$$$$) { - # Input: - # $header_ref = ref to $header string - # $block_ref = ref to $block to be written - # $recstart = record start string - # $recend = record end string - # $endpos = end position of $block - # Uses: - # %Global::running - # Returns: - # $something_written = amount of bytes written - my ($header_ref,$buffer_ref,$recstart,$recend,$endpos) = @_; - my $written = 0; - my $block_passed = 0; - while(not $block_passed) { - # Continue flushing existing buffers - # until one is empty and a new block is passed - if(@robin_queue) { - # Rotate queue once so new blocks get a fair chance - # to be given to another slot - push @robin_queue, shift @robin_queue; - } else { - # Make a queue to spread the blocks evenly - push @robin_queue, (sort { $a->seq() <=> $b->seq() } - values %Global::running); - } - do { - $written = 0; - for my $job (@robin_queue) { - if($job->block_length() > 0) { - $written += $job->non_blocking_write(); - } else { - $job->set_block($header_ref, $buffer_ref, - $endpos, $recstart, $recend); - $block_passed = 1; - $written += $job->non_blocking_write(); - last; - } - } - if($written) { - $sleep = $sleep/1.5+0.001; - } - # Don't sleep if something is written - } while($written and not $block_passed); - $sleep = ::reap_usleep($sleep); - } - return $written; - } -} - -sub index64($$$) { - # Do index on strings > 2GB. - # index in Perl < v5.22 does not work for > 2GB - # Input: - # as index except STR which must be passed as a reference - # Output: - # as index - my $ref = shift; - my $match = shift; - my $pos = shift || 0; - my $max2gb = 2**31-1; - my $strlen = length($$ref); - # No point in doing extra work if we don't need to. - if($strlen < $max2gb or $] > 5.022) { - return index($$ref, $match, $pos); - } - - my $matchlen = length($match); - my $ret; - my $offset = $pos; - while($offset < $strlen) { - $ret = index( - substr($$ref, $offset, $max2gb), - $match, $pos-$offset); - if($ret != -1) { - return $ret + $offset; - } - $offset += ($max2gb - $matchlen - 1); - } - return -1; -} - -sub rindex64($@) { - # Do rindex on strings > 2GB. - # rindex in Perl < v5.22 does not work for > 2GB - # Input: - # as rindex except STR which must be passed as a reference - # Output: - # as rindex - my $ref = shift; - my $match = shift; - my $pos = shift; - my $block_size = 2**31-1; - my $strlen = length($$ref); - # Default: search from end - $pos = defined $pos ? $pos : $strlen; - # No point in doing extra work if we don't need to. - if($strlen < $block_size or $] > 5.022) { - return rindex($$ref, $match, $pos); - } - - my $matchlen = length($match); - my $ret; - my $offset = $pos - $block_size + $matchlen; - if($offset < 0) { - # The offset is less than a $block_size - # Set the $offset to 0 and - # Adjust block_size accordingly - $block_size = $block_size + $offset; - $offset = 0; - } - while($offset >= 0) { - $ret = rindex( - substr($$ref, $offset, $block_size), - $match); - if($ret != -1) { - return $ret + $offset; - } - $offset -= ($block_size - $matchlen - 1); - } - return -1; -} - -sub shorten($$) { - # Do: substr($buf,0,$i) = ""; - # Some Perl versions do not support $i > 2GB, so do this in 2GB chunks - # Input: - # $buf_ref = \$buf - # $i = position to shorten to - # Returns: N/A - my ($buf_ref, $i) = @_; - my $two_gb = 2**31-1; - while($i > $two_gb) { - substr($$buf_ref,0,$two_gb) = ""; - $i -= $two_gb; - } - substr($$buf_ref,0,$i) = ""; -} - -sub write_record_to_pipe($$$$$$) { - # Fork then - # Write record from pos 0 .. $endpos to pipe - # Input: - # $chunk_number = sequence number - to see if already run - # $header_ref = reference to header string to prepend - # $buffer_ref = reference to record to write - # $recstart = start string of record - # $recend = end string of record - # $endpos = position in $buffer_ref where record ends - # Uses: - # $Global::job_already_run - # $opt::roundrobin - # @Global::virgin_jobs - # Returns: - # Number of chunks written (0 or 1) - my ($chunk_number, $header_ref, $buffer_ref, - $recstart, $recend, $endpos) = @_; - if($endpos == 0) { return 0; } - if(vec($Global::job_already_run,$chunk_number,1)) { return 1; } - if($opt::roundrobin) { - # Write the block to one of the already running jobs - return round_robin_write($header_ref, $buffer_ref, - $recstart, $recend, $endpos); - } - # If no virgin found, backoff - my $sleep = 0.0001; # 0.01 ms - better performance on highend - while(not @Global::virgin_jobs) { - ::debug("pipe", "No virgin jobs"); - $sleep = ::reap_usleep($sleep); - # Jobs may not be started because of loadavg - # or too little time between each ssh login - # or retrying failed jobs. - start_more_jobs(); - } - my $job = shift @Global::virgin_jobs; - $job->set_block($header_ref, $buffer_ref, $endpos, $recstart, $recend); - $job->write_block(); - return 1; -} - - -sub __SEM_MODE__() {} - - -sub acquire_semaphore() { - # Acquires semaphore. If needed: spawns to the background - # Uses: - # @Global::host - # Returns: - # The semaphore to be released when jobs is complete - $Global::host{':'} = SSHLogin->new(":"); - my $sem = Semaphore->new($Semaphore::name, - $Global::host{':'}->max_jobs_running()); - $sem->acquire(); - if($Semaphore::fg) { - # skip - } else { - if(fork()) { - exit(0); - } else { - # If run in the background, the PID will change - $sem->pid_change(); - } - } - return $sem; -} - - -sub __PARSE_OPTIONS__() {} - -sub shell_completion() { - if($opt::shellcompletion eq "zsh") { - # if shell == zsh - zsh_competion(); - } elsif($opt::shellcompletion eq "bash") { - # if shell == bash - bash_competion(); - } elsif($opt::shellcompletion eq "auto") { - if($Global::shell =~ m:/zsh$|^zsh$:) { - # if shell == zsh - zsh_competion(); - } elsif($Global::shell =~ m:/bash$|^bash$:) { - # if shell == bash - bash_competion(); - } else { - ::error("--shellcompletion is not implemented for ". - "'$Global::shell'."); - wait_and_exit(255); - } - } else { - ::error("--shellcompletion is not implemented for ". - "'$opt::shellcompletion'."); - wait_and_exit(255); - } -} - -sub bash_competion() { - # Print: - # complete -F _comp_parallel parallel; - # _comp_parallel() { - # COMPREPLY=($(compgen -W "--options" -- - # "${COMP_WORDS[$COMP_CWORD]}")); - # }; - my @bash_completion = - ("complete -F _comp_parallel parallel;", - '_comp_parallel() { COMPREPLY=($(compgen -W "'); - my @och = options_completion_hash(); - while(@och) { - $_ = shift @och; - # Split input like: - # "joblog|jl=s[Logfile for executed jobs]:logfile:_files" - if(/^(.*?)(\[.*?])?(:[^:]*)?(:.*)?$/) { - my $opt = $1; - my $desc = $2; - my $argdesc = $3; - my $func = $4; - # opt=s => opt - $opt =~ s/[:=].$//; - if($opt =~ /^_/) { - # internal options start with --_ - # skip - } else { - push @bash_completion, - (map { (length $_ == 1) ? "-$_ " : "--$_ " } - split /\|/, $opt); - } - } - shift @och; - } - push @bash_completion,'" -- "${COMP_WORDS[$COMP_CWORD]}")); };'."\n"; - print @bash_completion; -} - -sub zsh_competion() { - # Print code used for completion in zsh - my @zsh_completion = - ("compdef _comp_parallel parallel; ", - "setopt localoptions extended_glob; ", - "_comp_parallel() { ", - "_arguments "); - my @och = options_completion_hash(); - while(@och) { - $_ = shift @och; - # Split input like: - # "joblog|jl=s[Logfile for executed jobs]:logfile:_files" - if(/^(.*?)(\[.*?])?(:[^:]*)?(:.*)?$/) { - my $opt = $1; - my $desc = $2; - my $argdesc = $3; - my $func = $4; - # opt=s => opt - $opt =~ s/[:=].$//; - if($opt =~ /^_/) { - # internal options start with --_ - # skip - } else { - # {-o,--option} - my $zsh_opt = join(",", - (map { (length $_ == 1) ? "-$_" : "--$_" } - split /\|/, $opt)); - if($zsh_opt =~ /,/) { $zsh_opt = "{$zsh_opt}"; } - $desc =~ s/'/'"'"'/g; - $argdesc =~ s/'/'"'"'/g; - $func =~ s/'/'"'"'/g; - push @zsh_completion, $zsh_opt."'".$desc.$argdesc.$func."' "; - } - } - shift @och; - } - push @zsh_completion, - q{'(-)1:command:{_command_names -e}' }, - q{'*::arguments:_normal'}, - "};\n"; - print @zsh_completion; -} - -sub options_hash() { - # Returns: - # %hash = for GetOptions - my %och = options_completion_hash(); - my %oh; - my ($k,$v); - while(($k,$v) = each %och) { - # Remove description - $k =~ s/\[.*//; - $oh{$k} = $v; - } - return %oh; -} - -sub options_completion_hash() { - # Returns: - # %hash = for GetOptions and shell completion - return - ("debug|D=s" => \$opt::D, - "xargs[Insert as many arguments as the command line length permits]" - => \$opt::xargs, - "m[Multiple arguments]" => \$opt::m, - ("X[Insert as many arguments with context as the command line ". - "length permits]" - => \$opt::X), - "v[Verbose]" => \@opt::v, - "sql=s[Use --sql-master instead (obsolete)]:DBURL" => \$opt::retired, - ("sql-master|sqlmaster=s". - "[Submit jobs via SQL server. DBURL must point to a table, which ". - "will contain --joblog, the values, and output]:DBURL" - => \$opt::sqlmaster), - ("sql-worker|sqlworker=s". - "[Execute jobs via SQL server. Read the input sources variables ". - "from the table pointed to by DBURL.]:DBURL" - => \$opt::sqlworker), - ("sql-and-worker|sqlandworker=s". - "[--sql-master DBURL --sql-worker DBURL]:DBURL" - => \$opt::sqlandworker), - ("joblog|jl=s[Logfile for executed jobs]:logfile:_files" - => \$opt::joblog), - ("results|result|res=s[Save the output into files]:name:_files" - => \$opt::results), - "resume[Resumes from the last unfinished job]" => \$opt::resume, - ("resume-failed|resumefailed". - "[Retry all failed and resume from the last unfinished job]" - => \$opt::resume_failed), - ("retry-failed|retryfailed[Retry all failed jobs in joblog]" - => \$opt::retry_failed), - "silent[Silent]" => \$opt::silent, - ("keep-order|keeporder|k". - "[Keep sequence of output same as the order of input]" - => \$opt::keeporder), - ("no-keep-order|nokeeporder|nok|no-k". - "[Overrides an earlier --keep-order (e.g. if set in ". - "~/.parallel/config)]" - => \$opt::nokeeporder), - "group[Group output]" => \$opt::group, - "g" => \$opt::retired, - ("ungroup|u". - "[Output is printed as soon as possible and bypasses GNU parallel ". - "internal processing]" - => \$opt::ungroup), - ("latest-line|latestline|ll". - "[Print latest line of each job]" - => \$opt::latestline), - ("line-buffer|line-buffered|linebuffer|linebuffered|lb". - "[Buffer output on line basis]" - => \$opt::linebuffer), - ("tmux". - "[Use tmux for output. Start a tmux session and run each job in a ". - "window in that session. No other output will be produced]" - => \$opt::tmux), - ("tmux-pane|tmuxpane". - "[Use tmux for output but put output into panes in the first ". - "window. Useful if you want to monitor the progress of less than ". - "100 concurrent jobs]" - => \$opt::tmuxpane), - "null|0[Use NUL as delimiter]" => \$opt::null, - "quote|q[Quote command]" => \$opt::quote, - # Replacement strings - ("parens=s[Use parensstring instead of {==}]:parensstring" - => \$opt::parens), - ('rpl=s[Define replacement string]:"tag perl expression"' - => \@opt::rpl), - "plus[Add more replacement strings]" => \$opt::plus, - ("I=s". - "[Use the replacement string replace-str instead of {}]:replace-str" - => \$opt::I), - ("extensionreplace|er=s". - "[Use the replacement string replace-str instead of {.} for input ". - "line without extension]:replace-str" - => \$opt::U), - "U=s" => \$opt::retired, - ("basenamereplace|bnr=s". - "[Use the replacement string replace-str instead of {/} for ". - "basename of input line]:replace-str" - => \$opt::basenamereplace), - ("dirnamereplace|dnr=s". - "[Use the replacement string replace-str instead of {//} for ". - "dirname of input line]:replace-str" - => \$opt::dirnamereplace), - ("basenameextensionreplace|bner=s". - "[Use the replacement string replace-str instead of {/.} for ". - "basename of input line without extension]:replace-str" - => \$opt::basenameextensionreplace), - ("seqreplace=s". - "[Use the replacement string replace-str instead of {#} for job ". - "sequence number]:replace-str" - => \$opt::seqreplace), - ("slotreplace=s". - "[Use the replacement string replace-str instead of {%} for job ". - "slot number]:replace-str" - => \$opt::slotreplace), - ("delay=s". - "[Delay starting next job by duration]:duration" => \$opt::delay), - ("ssh-delay|sshdelay=f". - "[Delay starting next ssh by duration]:duration" - => \$opt::sshdelay), - ("load=s". - "[Only start jobs if load is less than max-load]:max-load" - => \$opt::load), - "noswap[Do not start job is computer is swapping]" => \$opt::noswap, - ("max-line-length-allowed|maxlinelengthallowed". - "[Print maximal command line length]" - => \$opt::max_line_length_allowed), - ("number-of-cpus|numberofcpus". - "[Print the number of physical CPU cores and exit (obsolete)]" - => \$opt::number_of_cpus), - ("number-of-sockets|numberofsockets". - "[Print the number of CPU sockets and exit]" - => \$opt::number_of_sockets), - ("number-of-cores|numberofcores". - "[Print the number of physical CPU cores and exit]" - => \$opt::number_of_cores), - ("number-of-threads|numberofthreads". - "[Print the number of hyperthreaded CPU cores and exit]" - => \$opt::number_of_threads), - ("use-sockets-instead-of-threads|usesocketsinsteadofthreads". - "[Determine how GNU Parallel counts the number of CPUs]" - => \$opt::use_sockets_instead_of_threads), - ("use-cores-instead-of-threads|usecoresinsteadofthreads". - "[Determine how GNU Parallel counts the number of CPUs]" - => \$opt::use_cores_instead_of_threads), - ("use-cpus-instead-of-cores|usecpusinsteadofcores". - "[Determine how GNU Parallel counts the number of CPUs]" - => \$opt::use_cpus_instead_of_cores), - ("shell-quote|shellquote|shell_quote". - "[Does not run the command but quotes it. Useful for making ". - "quoted composed commands for GNU parallel]" - => \@opt::shellquote), - ('nice=i[Run the command at this niceness]:niceness:($(seq -20 19))' - => \$opt::nice), - "tag[Tag lines with arguments]" => \$opt::tag, - ("tag-string|tagstring=s". - "[Tag lines with a string]:str" => \$opt::tagstring), - "ctag[Color tag]:str" => \$opt::ctag, - "ctag-string|ctagstring=s[Colour tagstring]:str" => \$opt::ctagstring, - "color|colour[Colourize output]" => \$opt::color, - ("color-failed|colour-failed|colorfailed|colourfailed|". - "color-fail|colour-fail|colorfail|colourfail|cf". - "[Colour failed jobs red]" - => \$opt::colorfailed), - ("onall[Run all the jobs on all computers given with --sshlogin]" - => \$opt::onall), - "nonall[--onall with no arguments]" => \$opt::nonall, - ("filter-hosts|filterhosts|filter-host[Remove down hosts]" - => \$opt::filter_hosts), - ('sshlogin|S=s'. - '[Distribute jobs to remote computers]'. - ':[@hostgroups/][ncpus/]sshlogin'. - '[,[@hostgroups/][ncpus/]sshlogin[,...]] or @hostgroup'. - ':_users') => \@opt::sshlogin, - ("sshloginfile|slf=s". - "[File with sshlogins on separate lines. Lines starting with '#' ". - "are ignored.]:filename:_files" - => \@opt::sshloginfile), - ("controlmaster|M". - "[Use ssh's ControlMaster to make ssh connections faster]" - => \$opt::controlmaster), - ("ssh=s". - "[Use this command instead of ssh for remote access]:sshcommand" - => \$opt::ssh), - ("transfer-file|transferfile|transfer-files|transferfiles|tf=s". - "[Transfer filename to remote computers]:filename:_files" - => \@opt::transfer_files), - ("return=s[Transfer files from remote computers]:filename:_files" - => \@opt::return), - ("trc=s[--transfer --return filename --cleanup]:filename:_files" - => \@opt::trc), - "transfer[Transfer files to remote computers]" => \$opt::transfer, - "cleanup[Remove transferred files]" => \$opt::cleanup, - ("basefile|bf=s". - "[Transfer file to each sshlogin before first job is started]". - ":file:_files" - => \@opt::basefile), - ("template|tmpl=s". - "[Replace replacement strings in file and save it in repl]". - ":file=repl:_files" - => \%opt::template), - "B=s" => \$opt::retired, - "ctrl-c|ctrlc" => \$opt::retired, - "no-ctrl-c|no-ctrlc|noctrlc" => \$opt::retired, - ("work-dir|workdir|wd=s". - "[Jobs will be run in the dir mydir. (default: the current dir ". - "for the local machine, the login dir for remote computers)]". - ":mydir:_cd" - => \$opt::workdir), - "W=s" => \$opt::retired, - ("rsync-opts|rsyncopts=s[Options to pass on to rsync]:options" - => \$opt::rsync_opts), - ("tmpdir|tempdir=s[Directory for temporary files]:dirname:_cd" - => \$opt::tmpdir), - ("use-compress-program|compress-program|". - "usecompressprogram|compressprogram=s". - "[Use prg for compressing temporary files]:prg:_commands" - => \$opt::compress_program), - ("use-decompress-program|decompress-program|". - "usedecompressprogram|decompressprogram=s". - "[Use prg for decompressing temporary files]:prg:_commands" - => \$opt::decompress_program), - "compress[Compress temporary files]" => \$opt::compress, - "open-tty|o[Open terminal tty]" => \$opt::open_tty, - "tty[Open terminal tty]" => \$opt::tty, - "T" => \$opt::retired, - "H=i" => \$opt::retired, - ("dry-run|dryrun|dr". - "[Print the job to run on stdout (standard output), but do not ". - "run the job]" - => \$opt::dryrun), - "progress[Show progress of computations]" => \$opt::progress, - ("eta[Show the estimated number of seconds before finishing]" - => \$opt::eta), - "bar[Show progress as a progress bar]" => \$opt::bar, - ("total-jobs|totaljobs|total=s". - "[Set total number of jobs]" => \$opt::totaljobs), - "shuf[Shuffle jobs]" => \$opt::shuf, - ("arg-sep|argsep=s". - "[Use sep-str instead of ::: as separator string]:sep-str" - => \$opt::arg_sep), - ("arg-file-sep|argfilesep=s". - "[Use sep-str instead of :::: as separator string ". - "between command and argument files]:sep-str" - => \$opt::arg_file_sep), - ('trim=s[Trim white space in input]:trim_method:'. - '((n\:"No trim" l\:"Left\ trim" r\:"Right trim" '. - 'lr\:"Both trim" rl\:"Both trim"))' - => \$opt::trim), - "env=s[Copy environment variable var]:var:_vars" => \@opt::env, - "recordenv|record-env[Record environment]" => \$opt::record_env, - ('session'. - '[Record names in current environment in $PARALLEL_IGNORED_NAMES '. - 'and exit. Only used with env_parallel. '. - 'Aliases, functions, and variables with names i]' - => \$opt::session), - ('plain[Ignore --profile, $PARALLEL, and ~/.parallel/config]' - => \$opt::plain), - ("profile|J=s". - "[Use profile profilename for options]:profilename:_files" - => \@opt::profile), - "tollef" => \$opt::tollef, - "gnu[Behave like GNU parallel]" => \$opt::gnu, - "link|xapply[Link input sources]" => \$opt::link, - "linkinputsource|xapplyinputsource=i" => \@opt::linkinputsource, - # Before changing these lines, please read - # https://www.gnu.org/software/parallel/parallel_design.html#citation-notice - # https://git.savannah.gnu.org/cgit/parallel.git/tree/doc/citation-notice-faq.txt - # You accept to be put in a public hall-of-shame by removing - # these lines -# ("bibtex|citation". -# "[Print the citation notice and BibTeX entry for GNU parallel, ". -# "silence citation notice for all future runs, and exit. ". -# "It will not run any commands]" -# => \$opt::citation), - "will-cite|willcite|nn|nonotice|no-notice" => \$opt::willcite, - # Termination and retries - ('halt-on-error|haltonerror|halt=s'. - '[When should GNU parallel terminate]'. - ':when:((now\:"kill all running jobs and halt immediately" '. - 'soon\:"wait for all running jobs to complete, start no new jobs"))' - => \$opt::halt), - 'limit=s[Dynamic job limit]:"command args"' => \$opt::limit, - ("memfree=s". - "[Minimum memory free when starting another job]:size" - => \$opt::memfree), - ("memsuspend=s". - "[Suspend jobs when there is less memory available]:size" - => \$opt::memsuspend), - "retries=s[Try failing jobs n times]:n" => \$opt::retries, - ("timeout=s". - "[Time out for command. If the command runs for longer than ". - "duration seconds it will get killed as per --term-seq]:duration" - => \$opt::timeout), - ("term-seq|termseq=s". - "[Termination sequence]:sequence" => \$opt::termseq), - # xargs-compatibility - implemented, man, testsuite - ("max-procs|maxprocs|P|jobs|j=s". - "[Add N to/Subtract N from/Multiply N% with/ the number of CPU ". - "threads or read parameter from file]:+N/-N/N%/N/procfile:_files" - => \$opt::jobs), - ("delimiter|d=s[Input items are terminated by delim]:delim" - => \$opt::d), - ("max-chars|maxchars|s=s[Limit length of command]:max-chars" - => \$opt::max_chars), - ("arg-file|argfile|a=s". - "[Use input-file as input source]:input-file:_files" => \@opt::a), - "no-run-if-empty|norunifempty|r[Do not run empty input]" => \$opt::r, - ("replace|i:s". - "[This option is deprecated; use -I instead]:replace-str" - => \$opt::i), - "E=s" => \$opt::eof, - ("eof|e:s[Set the end of file string to eof-str]:eof-str" - => \$opt::eof), - ("process-slot-var|processslotvar=s". - "[Set this variable to job slot number]:varname" - => \$opt::process_slot_var), - ("max-args|maxargs|n=s". - "[Use at most max-args arguments per command line]:max-args" - => \$opt::max_args), - ("max-replace-args|maxreplaceargs|N=s". - "[Use at most max-args arguments per command line]:max-args" - => \$opt::max_replace_args), - "col-sep|colsep|C=s[Column separator]:regexp" => \$opt::colsep, - "csv[Treat input as CSV-format]"=> \$opt::csv, - ("help|h[Print a summary of the options to GNU parallel and exit]" - => \$opt::help), - ("L=s[When used with --pipe: Read records of recsize]:recsize" - => \$opt::L), - ("max-lines|maxlines|l:f". - "[When used with --pipe: Read records of recsize lines]:recsize" - => \$opt::max_lines), - "interactive|p[Ask user before running a job]" => \$opt::interactive, - ("verbose|t[Print the job to be run on stderr (standard error)]" - => \$opt::verbose), - ("version|V[Print the version GNU parallel and exit]" - => \$opt::version), - ('min-version|minversion=i'. - '[Print the version GNU parallel and exit]'. - ':version:($(parallel --minversion 0))' - => \$opt::minversion), - ("show-limits|showlimits". - "[Display limits given by the operating system]" - => \$opt::show_limits), - ("exit|x[Exit if the size (see the -s option) is exceeded]" - => \$opt::x), - # Semaphore - "semaphore[Work as a counting semaphore]" => \$opt::semaphore, - ("semaphore-timeout|semaphoretimeout|st=s". - "[If secs > 0: If the semaphore is not released within secs ". - "seconds, take it anyway]:secs" - => \$opt::semaphoretimeout), - ("semaphore-name|semaphorename|id=s". - "[Use name as the name of the semaphore]:name" - => \$opt::semaphorename), - "fg[Run command in foreground]" => \$opt::fg, - "bg[Run command in background]" => \$opt::bg, - "wait[Wait for all commands to complete]" => \$opt::wait, - # Shebang #!/usr/bin/parallel --shebang - ("shebang|hashbang". - "[GNU parallel can be called as a shebang (#!) command as the ". - "first line of a script. The content of the file will be treated ". - "as inputsource]" - => \$opt::shebang), - ("_pipe-means-argfiles[internal]" - => \$opt::_pipe_means_argfiles), - "Y" => \$opt::retired, - ("skip-first-line|skipfirstline". - "[Do not use the first line of input]" - => \$opt::skip_first_line), - "_bug" => \$opt::_bug, - "_unsafe" => \$opt::_unsafe, - # --pipe - ("pipe|spreadstdin". - "[Spread input to jobs on stdin (standard input)]" => \$opt::pipe), - ("round-robin|roundrobin|round". - "[Distribute chunks of standard input in a round robin fashion]" - => \$opt::roundrobin), - "recstart=s" => \$opt::recstart, - ("recend=s". - "[Split record between endstring and startstring]:endstring" - => \$opt::recend), - ("regexp|regex". - "[Interpret --recstart and --recend as regular expressions]" - => \$opt::regexp), - ("remove-rec-sep|removerecsep|rrs". - "[Remove record separator]" => \$opt::remove_rec_sep), - ("output-as-files|outputasfiles|files[Save output to files]" - => \$opt::files), - ("output-as-files0|outputasfiles0|files0". - "[Save output to files separated by NUL]" - => \$opt::files0), - ("block-size|blocksize|block=s". - "[Size of block in bytes to read at a time]:size" - => \$opt::blocksize), - ("block-timeout|blocktimeout|bt=s". - "[Timeout for reading block when using --pipe]:duration" - => \$opt::blocktimeout), - "header=s[Use regexp as header]:regexp" => \$opt::header, - "cat[Create a temporary file with content]" => \$opt::cat, - "fifo[Create a temporary fifo with content]" => \$opt::fifo, - ("pipe-part|pipepart[Pipe parts of a physical file]" - => \$opt::pipepart), - "tee[Pipe all data to all jobs]" => \$opt::tee, - ("shard=s". - "[Use shardexpr as shard key and shard input to the jobs]:shardexpr" - => \$opt::shard), - ("bin=s". - "[Use binexpr as binning key and bin input to the jobs]:binexpr" - => \$opt::bin), - "group-by|groupby=s[Group input by value]:val" => \$opt::groupby, - # - ("hgrp|hostgrp|hostgroup|hostgroups[Enable hostgroups on arguments]" - => \$opt::hostgroups), - "embed[Embed GNU parallel in a shell script]" => \$opt::embed, - ("filter=s[Only run jobs where filter is true]:filter" - => \@opt::filter), - "_parset=s[Generate shell code for parset]" => \$opt::_parset, - ("shell-completion|shellcompletion=s". - "[Generate shell code for shell completion]:shell:(bash zsh)" - => \$opt::shellcompletion), - # Parameter for testing optimal values - "_test=s" => \$opt::_test, - ); -} - -sub get_options_from_array($@) { - # Run GetOptions on @array - # Input: - # $array_ref = ref to @ARGV to parse - # @keep_only = Keep only these options (e.g. --profile) - # Uses: - # @ARGV - # Returns: - # true if parsing worked - # false if parsing failed - # @$array_ref is changed - my ($array_ref, @keep_only) = @_; - if(not @$array_ref) { - # Empty array: No need to look more at that - return 1; - } - # A bit of shuffling of @ARGV needed as GetOptionsFromArray is not - # supported everywhere - my @save_argv; - my $this_is_ARGV = (\@::ARGV == $array_ref); - if(not $this_is_ARGV) { - @save_argv = @::ARGV; - @::ARGV = @{$array_ref}; - } - # If @keep_only set: Ignore all values except @keep_only - my %options = options_hash(); - if(@keep_only) { - my (%keep,@dummy); - @keep{@keep_only} = @keep_only; - for my $k (grep { not $keep{$_} } keys %options) { - # Store the value of the option in @dummy - $options{$k} = \@dummy; - } - } - my $retval = GetOptions(%options); - if(not $this_is_ARGV) { - @{$array_ref} = @::ARGV; - @::ARGV = @save_argv; - } - return $retval; -} - -sub parse_parset() { - $Global::progname = "parset"; - @Global::parset_vars = split /[ ,]/, $opt::_parset; - my $var_or_assoc = shift @Global::parset_vars; - # Legal names: var _v2ar arrayentry[2] - my @illegal = (grep { not /^[a-zA-Z_][a-zA-Z_0-9]*(\[\d+\])?$/ } - @Global::parset_vars); - if(@illegal) { - ::error - ("@illegal is an invalid variable name.", - "Variable names must be letter followed by letters or digits.", - "Usage:", - " parset varname GNU Parallel options and command"); - wait_and_exit(255); - } - if($var_or_assoc eq "assoc") { - my $var = shift @Global::parset_vars; - print "$var=("; - $Global::parset = "assoc"; - $Global::parset_endstring=")\n"; - } elsif($var_or_assoc eq "var") { - if($#Global::parset_vars > 0) { - $Global::parset = "var"; - } else { - my $var = shift @Global::parset_vars; - print "$var=("; - $Global::parset = "array"; - $Global::parset_endstring=")\n"; - } - } else { - ::die_bug("parset: unknown '$opt::_parset'"); - } -} - -sub parse_options(@) { - # Returns: N/A - init_globals(); - my @argv_before = @ARGV; - @ARGV = read_options(); - - # Before changing these line, please read - # https://www.gnu.org/software/parallel/parallel_design.html#citation-notice - # https://git.savannah.gnu.org/cgit/parallel.git/tree/doc/citation-notice-faq.txt - # You accept to be added to a public hall-of-shame by removing the lines -# if(defined $opt::citation) { -# citation(\@argv_before,\@ARGV); -# wait_and_exit(0); -# } - # no-* overrides * - if($opt::nokeeporder) { $opt::keeporder = undef; } - - if(@opt::v) { $Global::verbose = $#opt::v+1; } # Convert -v -v to v=2 - if($opt::_bug) { ::die_bug("test-bug"); } - $Global::debug = $opt::D; - # - ## Shell - # - $Global::shell = $ENV{'PARALLEL_SHELL'} || parent_shell($$) - || $ENV{'SHELL'} || "/bin/sh"; - if(not -x $Global::shell and not which($Global::shell)) { - ::error("Shell '$Global::shell' not found."); - wait_and_exit(255); - } - ::debug("init","Global::shell $Global::shell\n"); - $Global::cshell = $Global::shell =~ m:(/[-a-z]*)?csh:; - $Global::fish = $Global::shell =~ m:(/[-a-z]*)?fish:; - if(defined $opt::_parset) { parse_parset(); } - if(defined $opt::X) { $Global::ContextReplace = 1; } - if(defined $opt::silent) { $Global::verbose = 0; } - if(defined $opt::null) { $/ = "\0"; } - if(defined $opt::files) { $Global::files = 1; $Global::files_sep = "\n"; } - if(defined $opt::files0) { $Global::files = 1; $Global::files_sep = "\0"; } - if(defined $opt::d) { $/ = unquote_printf($opt::d) } - parse_replacement_string_options(); - $opt::tag ||= $opt::ctag; - $opt::tagstring ||= $opt::ctagstring; - if(defined $opt::ctag or defined $opt::ctagstring - or defined $opt::color) { - $Global::color = 1; - } - if($opt::linebuffer or $opt::latestline) { - $Global::linebuffer = 1; - Job::latestline_init(); - } - if(defined $opt::tag and not defined $opt::tagstring) { - # Default = {} - $opt::tagstring = $Global::parensleft.$Global::parensright; - } - if(defined $opt::tagstring) { - $opt::tagstring = unquote_printf($opt::tagstring); - if($opt::tagstring =~ - /\Q$Global::parensleft\E.*\S+.*\Q$Global::parensright\E/ - and - $Global::linebuffer) { - # --tagstring contains {= ... =} and --linebuffer => - # recompute replacement string for each use (do not cache) - $Global::cache_replacement_eval = 0; - } - } - if(defined $opt::interactive) { $Global::interactive = $opt::interactive; } - if(defined $opt::quote) { $Global::quoting = 1; } - if(defined $opt::r) { $Global::ignore_empty = 1; } - if(defined $opt::verbose) { $Global::stderr_verbose = 1; } - if(defined $opt::eof) { $Global::end_of_file_string = $opt::eof; } - if(defined $opt::max_args) { - $opt::max_args = multiply_binary_prefix($opt::max_args); - $Global::max_number_of_args = $opt::max_args; - if($opt::pipepart and $opt::groupby) { $Global::max_number_of_args = 1; } - } - if(defined $opt::blocktimeout) { - $Global::blocktimeout = int(multiply_time_units($opt::blocktimeout)); - if($Global::blocktimeout < 1) { - ::error("--block-timeout must be at least 1"); - wait_and_exit(255); - } - } - if(defined $opt::timeout) { - $Global::timeoutq = TimeoutQueue->new($opt::timeout); - } - if(defined $opt::tmpdir) { $ENV{'TMPDIR'} = $opt::tmpdir; } - $ENV{'PARALLEL_RSYNC_OPTS'} = $opt::rsync_opts || - $ENV{'PARALLEL_RSYNC_OPTS'} || '-rlDzR'; - # Default: Same nice level as GNU Parallel is started at - $opt::nice ||= eval { getpriority(0,0) } || 0; - if(defined $opt::help) { usage(); exit(0); } - if(defined $opt::shellcompletion) { shell_completion(); exit(0); } - if(defined $opt::embed) { embed(); exit(0); } - if(defined $opt::sqlandworker) { - $opt::sqlmaster = $opt::sqlworker = $opt::sqlandworker; - } - if(defined $opt::tmuxpane) { $opt::tmux = $opt::tmuxpane; } - if(defined $opt::colsep) { $Global::trim = 'lr'; } - if(defined $opt::csv) { - if(not $Global::use{"Text::CSV"} ||= eval "use Text::CSV; 1;") { - ::error("The perl module Text::CSV is not installed."); - ::error("Try installing libtext-csv-perl or perl-Text-CSV."); - wait_and_exit(255); - } - $opt::colsep = defined $opt::colsep ? $opt::colsep : ","; - my $csv_setting = { binary => 1, sep_char => $opt::colsep }; - my $sep = $csv_setting->{sep_char}; - $Global::csv = Text::CSV->new($csv_setting) - or die "Cannot use CSV: ".Text::CSV->error_diag (); - } - if(defined $opt::header) { - $opt::colsep = defined $opt::colsep ? $opt::colsep : "\t"; - } - if(defined $opt::trim) { $Global::trim = $opt::trim; } - if(defined $opt::arg_sep) { $Global::arg_sep = $opt::arg_sep; } - if(defined $opt::arg_file_sep) { - $Global::arg_file_sep = $opt::arg_file_sep; - } - if(not defined $opt::process_slot_var) { - $opt::process_slot_var = 'PARALLEL_JOBSLOT0'; - } - if(defined $opt::number_of_sockets) { - print SSHLogin::no_of_sockets(),"\n"; wait_and_exit(0); - } - if(defined $opt::number_of_cpus) { - print SSHLogin::no_of_cores(),"\n"; wait_and_exit(0); - } - if(defined $opt::number_of_cores) { - print SSHLogin::no_of_cores(),"\n"; wait_and_exit(0); - } - if(defined $opt::number_of_threads) { - print SSHLogin::no_of_threads(),"\n"; wait_and_exit(0); - } - if(defined $opt::max_line_length_allowed) { - print Limits::Command::real_max_length(),"\n"; wait_and_exit(0); - } - if(defined $opt::max_chars) { - $opt::max_chars = multiply_binary_prefix($opt::max_chars); - } - if(defined $opt::version) { version(); wait_and_exit(0); } - if(defined $opt::record_env) { record_env(); wait_and_exit(0); } - if(@opt::sshlogin) { @Global::sshlogin = @opt::sshlogin; } - if(@opt::sshloginfile) { read_sshloginfiles(@opt::sshloginfile); } - if(@opt::return) { push @Global::ret_files, @opt::return; } - if($opt::transfer) { - push @Global::transfer_files, $opt::i || $opt::I || "{}"; - } - push @Global::transfer_files, @opt::transfer_files; - if(%opt::template) { - while (my ($source, $template_name) = each %opt::template) { - if(open(my $tmpl, "<", $source)) { - local $/; # $/ = undef => slurp whole file - my $content = <$tmpl>; - push @Global::template_names, $template_name; - push @Global::template_contents, $content; - ::debug("tmpl","Name: $template_name\n$content\n"); - } else { - ::error("Cannot open '$source'."); - wait_and_exit(255); - } - } - } - if(not defined $opt::recstart and - not defined $opt::recend) { $opt::recend = "\n"; } - $Global::blocksize = multiply_binary_prefix($opt::blocksize || "1M"); - if($Global::blocksize > 2**31-1 and not $opt::pipepart) { - warning("--blocksize >= 2G causes problems. Using 2G-1."); - $Global::blocksize = 2**31-1; - } - if($^O eq "cygwin" and - ($opt::pipe or $opt::pipepart or $opt::roundrobin) - and $Global::blocksize > 65535) { - warning("--blocksize >= 64K causes problems on Cygwin."); - } - $opt::memfree = multiply_binary_prefix($opt::memfree); - $opt::memsuspend = multiply_binary_prefix($opt::memsuspend); - $Global::memlimit = $opt::memsuspend + $opt::memfree; - check_invalid_option_combinations(); - if((defined $opt::fifo or defined $opt::cat) and not $opt::pipepart) { - $opt::pipe = 1; - } - if(defined $opt::minversion) { - print $Global::version,"\n"; - if($Global::version < $opt::minversion) { - wait_and_exit(255); - } else { - wait_and_exit(0); - } - } - if(not defined $opt::delay) { - # Set --delay to --sshdelay if not set - $opt::delay = $opt::sshdelay; - } - $Global::sshdelayauto = $opt::sshdelay =~ s/auto$//; - $opt::sshdelay = multiply_time_units($opt::sshdelay); - $Global::delayauto = $opt::delay =~ s/auto$//; - $opt::delay = multiply_time_units($opt::delay); - if($opt::compress_program) { - $opt::compress = 1; - $opt::decompress_program ||= $opt::compress_program." -dc"; - } - - if(defined $opt::results) { - # Is the output a dir or CSV-file? - if($opt::results =~ /\.csv$/i) { - # CSV with , as separator - $Global::csvsep = ","; - $Global::membuffer ||= 1; - } elsif($opt::results =~ /\.tsv$/i) { - # CSV with TAB as separator - $Global::csvsep = "\t"; - $Global::membuffer ||= 1; - } elsif($opt::results =~ /\.json$/i) { - # JSON output - $Global::jsonout ||= 1; - $Global::membuffer ||= 1; - } - } - if($opt::compress) { - my ($compress, $decompress) = find_compression_program(); - $opt::compress_program ||= $compress; - $opt::decompress_program ||= $decompress; - if(($opt::results and not $Global::csvsep) or $Global::files) { - # No need for decompressing - $opt::decompress_program = "cat >/dev/null"; - } - } - if(defined $opt::dryrun) { - # Force grouping due to bug #51039: --dry-run --timeout 3600 -u breaks - $opt::ungroup = 0; - $opt::group = 1; - } - if(defined $opt::nonall) { - # Append a dummy empty argument if there are no arguments - # on the command line to avoid reading from STDIN. - # arg_sep = random 50 char - # \0noarg => nothing (not the empty string) - $Global::arg_sep = join "", - map { (0..9,"a".."z","A".."Z")[rand(62)] } (1..50); - push @ARGV, $Global::arg_sep, "\0noarg"; - } - if(defined $opt::tee) { - if(not defined $opt::jobs) { - $opt::jobs = 0; - } - } - if(defined $opt::tty) { - # Defaults for --tty: -j1 -u - # Can be overridden with -jXXX -g - if(not defined $opt::jobs) { - $opt::jobs = 1; - } - if(not defined $opt::group) { - $opt::ungroup = 1; - } - } - if(@opt::trc) { - push @Global::ret_files, @opt::trc; - if(not @Global::transfer_files) { - # Defaults to --transferfile {} - push @Global::transfer_files, $opt::i || $opt::I || "{}"; - } - $opt::cleanup = 1; - } - if(defined $opt::max_lines) { - if($opt::max_lines eq "-0") { - # -l -0 (swallowed -0) - $opt::max_lines = 1; - $opt::null = 1; - $/ = "\0"; - } else { - $opt::max_lines = multiply_binary_prefix($opt::max_lines); - if ($opt::max_lines == 0) { - # If not given (or if 0 is given) => 1 - $opt::max_lines = 1; - } - } - - $Global::max_lines = $opt::max_lines; - if(not $opt::pipe) { - # --pipe -L means length of record - not max_number_of_args - $Global::max_number_of_args ||= $Global::max_lines; - } - } - - # Read more than one arg at a time (-L, -N) - if(defined $opt::L) { - $opt::L = multiply_binary_prefix($opt::L); - $Global::max_lines = $opt::L; - if(not $opt::pipe) { - # --pipe -L means length of record - not max_number_of_args - $Global::max_number_of_args ||= $Global::max_lines; - } - } - if(defined $opt::max_replace_args) { - $opt::max_replace_args = - multiply_binary_prefix($opt::max_replace_args); - $Global::max_number_of_args = $opt::max_replace_args; - $Global::ContextReplace = 1; - } - if((defined $opt::L or defined $opt::max_replace_args) - and - not ($opt::xargs or $opt::m)) { - $Global::ContextReplace = 1; - } - # Deal with ::: :::+ :::: ::::+ and -a +file - @ARGV = read_args_from_command_line(); - parse_semaphore(); - - if(defined $opt::eta) { $opt::progress = $opt::eta; } - if(defined $opt::bar) { $opt::progress = $opt::bar; } - if(defined $opt::bar or defined $opt::latestline) { - my $fh = $Global::status_fd || *STDERR; - # Activate decode_utf8 - eval q{ - # Enable utf8 if possible - use utf8; - binmode $fh, "encoding(utf8)"; - *decode_utf8 = \&Encode::decode_utf8; - }; - if(eval { decode_utf8("x") }) { - # Great: decode works - } else { - # UTF8-decode not supported: Dummy decode - eval q{sub decode_utf8($;$) { $_[0]; }}; - } - # Activate decode_utf8 - eval q{ - # Enable utf8 if possible - use utf8; - use Encode qw( encode_utf8 ); - use Text::CharWidth qw( mbswidth ); - use Unicode::Normalize qw( NFC NFD ); - }; - if(eval { mbswidth("ヌー平行") }) { - # Great: mbswidth works - } else { - # mbswidth not supported: Dummy mbswidth - eval q{ sub mbswidth { return length @_; } }; - } - } - - # If you want GNU Parallel to be maintained in the future you - # should keep this. - # *YOU* will be harming free software by removing the notice. - # - # Funding a free software project is hard. GNU Parallel is no - # exception. On top of that it seems the less visible a project - # is, the harder it is to get funding. And the nature of GNU - # Parallel is that it will never be seen by "the guy with the - # checkbook", but only by the people doing the actual work. - # - # This problem has been covered by others - though no solution has - # been found: - # https://www.slideshare.net/NadiaEghbal/consider-the-maintainer - # https://www.numfocus.org/blog/why-is-numpy-only-now-getting-funded/ - # - # The FAQ tells you why the citation notice exists: - # https://git.savannah.gnu.org/cgit/parallel.git/tree/doc/citation-notice-faq.txt - # - # If you want GNU Parallel to be maintained in the future, and not - # just wither away like so many other free software tools, you - # need to help finance the development. - # - # The citation notice is a simple way of doing so, as citations - # makes it possible to me to get a job where I can maintain GNU - # Parallel as part of the job. - # - # This means you can help financing development - # - # WITHOUT PAYING A SINGLE CENT! - # - # Before implementing the citation notice it was discussed with - # the users: - # https://lists.gnu.org/archive/html/parallel/2013-11/msg00006.html - # - # Having to spend 10 seconds on running 'parallel --citation' once - # is no doubt not an ideal solution, but no one has so far come up - # with an ideal solution - neither for funding GNU Parallel nor - # other free software. - # - # If you believe you have the perfect solution, you should try it - # out, and if it works, you should post it on the email - # list. Ideas that will cost work and which have not been tested - # are, however, unlikely to be prioritized. - # - # Please note that GPL version 3 gives you the right to fork GNU - # Parallel under a new name, but it does not give you the right to - # distribute modified copies with the citation notice disabled in - # a way where the software can be confused with GNU Parallel. To - # do that you need to be the owner of the GNU Parallel - # trademark. The xt:Commerce case shows this. - # - # Description of the xt:Commerce case in OLG Duesseldorf - # https://web.archive.org/web/20180715073746/http://www.inta.org/INTABulletin/Pages/GERMANYGeneralPublicLicenseDoesNotPermitUseofThird-PartyTrademarksforAdvertisingModifiedVersionsofOpen-SourceSoftware.aspx - # - # The verdict in German - # https://www.admody.com/urteilsdatenbank/cafe6fdaeed3/OLG-Duesseldorf_Urteil_vom_28-September-2010_Az_I-20-U-41-09 - # https://web.archive.org/web/20180715073717/https://www.admody.com/urteilsdatenbank/cafe6fdaeed3/OLG-Duesseldorf_Urteil_vom_28-September-2010_Az_I-20-U-41-09 - # - # Other free software limiting derivates by the same name: - # https://en.wikipedia.org/wiki/Red_Hat_Enterprise_Linux_derivatives#Legal_aspects - # https://tm.joomla.org/trademark-faq.html - # https://www.mozilla.org/en-US/foundation/trademarks/faq/ - # - # Running 'parallel --citation' one single time takes less than 10 - # seconds, and will silence the citation notice for future - # runs. If that is too much trouble for you, why not use one of - # the alternatives instead? - # See a list in: 'man parallel_alternatives' - # - # If you want GNU Parallel to be maintained in the future, you - # should keep this line: -# citation_notice(); - # This is because _YOU_ actively make it harder to justify - # spending time developing GNU Parallel by removing it. - - # If you disagree, please read (especially 77-): - # https://www.fordfoundation.org/media/2976/roads-and-bridges-the-unseen-labor-behind-our-digital-infrastructure.pdf - - # *YOU* will be harming free software by removing the notice. You - # accept to be added to a public hall of shame by removing the - # line. That includes you, George and Andreas. - - parse_halt(); - - if($ENV{'PARALLEL_ENV'}) { - # Read environment and set $Global::parallel_env - # Must be done before is_acceptable_command_line_length() - my $penv = $ENV{'PARALLEL_ENV'}; - # unset $PARALLEL_ENV: It should not be given to children - # because it takes up a lot of env space - delete $ENV{'PARALLEL_ENV'}; - if(-e $penv) { - # This is a file/fifo: Replace envvar with content of file - open(my $parallel_env, "<", $penv) || - ::die_bug("Cannot read parallel_env from $penv"); - local $/; # Put <> in slurp mode - $penv = <$parallel_env>; - close $parallel_env; - } - # Map \001 to \n to make it easer to quote \n in $PARALLEL_ENV - $penv =~ s/\001/\n/g; - if($penv =~ /\0/) { - ::warning('\0 (NUL) in environment is not supported'); - } - $Global::parallel_env = $penv; - } - - parse_sshlogin(); - if(defined $opt::show_limits) { show_limits(); } - - if(remote_hosts() and - (defined $opt::X or defined $opt::m or defined $opt::xargs)) { - # As we do not know the max line length on the remote machine - # long commands generated by xargs may fail - # If $opt::max_replace_args is set, it is probably safe - ::warning("Using -X or -m with --sshlogin may fail."); - } - - if(not defined $opt::jobs) { $opt::jobs = "100%"; } - open_joblog(); - open_json_csv(); - if(defined $opt::sqlmaster or defined $opt::sqlworker) { - $Global::sql = SQL->new($opt::sqlmaster || $opt::sqlworker); - } - if(defined $opt::sqlworker) { $Global::membuffer ||= 1; } - # The sqlmaster groups the arguments, so the should just read one - if(defined $opt::sqlworker and not defined $opt::sqlmaster) { - $Global::max_number_of_args = 1; - } - if(defined $Global::color or defined $opt::colorfailed) { - Job::init_color(); - } -} - -sub check_invalid_option_combinations() { - if(defined $opt::timeout and - $opt::timeout !~ /^\d+(\.\d+)?%?$|^(\d+(\.\d+)?[dhms])+$/i) { - ::error("--timeout must be seconds or percentage."); - wait_and_exit(255); - } - if(defined $opt::fifo and defined $opt::cat) { - ::error("--fifo cannot be combined with --cat."); - ::wait_and_exit(255); - } - if(defined $opt::retries and defined $opt::roundrobin) { - ::error("--retries cannot be combined with --roundrobin."); - ::wait_and_exit(255); - } - if(defined $opt::pipepart and - (defined $opt::L or defined $opt::max_lines - or defined $opt::max_replace_args)) { - ::error("--pipepart is incompatible with --max-replace-args, ". - "--max-lines, and -L."); - wait_and_exit(255); - } - if(defined $opt::group and defined $opt::ungroup) { - ::error("--group cannot be combined with --ungroup."); - ::wait_and_exit(255); - } - if(defined $opt::group and defined $opt::linebuffer) { - ::error("--group cannot be combined with --line-buffer."); - ::wait_and_exit(255); - } - if(defined $opt::ungroup and defined $opt::linebuffer) { - ::error("--ungroup cannot be combined with --line-buffer."); - ::wait_and_exit(255); - } - if(defined $opt::tollef and not defined $opt::gnu) { - ::error("--tollef has been retired.", - "Remove --tollef or use --gnu to override --tollef."); - ::wait_and_exit(255); - } - if(defined $opt::retired) { - ::error("-g has been retired. Use --group.", - "-B has been retired. Use --bf.", - "-T has been retired. Use --tty.", - "-U has been retired. Use --er.", - "-W has been retired. Use --wd.", - "-Y has been retired. Use --shebang.", - "-H has been retired. Use --halt.", - "--sql has been retired. Use --sqlmaster.", - "--ctrlc has been retired.", - "--noctrlc has been retired."); - ::wait_and_exit(255); - } - if(defined $opt::groupby) { - if(not defined $opt::pipe and not defined $opt::pipepart) { - $opt::pipe = 1; - } - if(defined $opt::remove_rec_sep) { - ::error("--remove-rec-sep is not compatible with --groupby"); - ::wait_and_exit(255); - } - if(defined $opt::recstart) { - ::error("--recstart is not compatible with --groupby"); - ::wait_and_exit(255); - } - if($opt::recend ne "\n") { - ::error("--recend is not compatible with --groupby"); - ::wait_and_exit(255); - } - } - sub unsafe_warn { - # use --_unsafe to only generate a warning - if($opt::_unsafe) { ::warning(@_); } else { ::error(@_); exit(255); } - } - if(defined $opt::results) { - if($opt::nonall or $opt::onall) { - unsafe_warn("--(n)onall + --results not supported (yet)."); - } - } - sub test_safe_chars { - my $var = shift; - if($ENV{$var} =~ m{^[-a-z0-9_+,.%:/= ]*$}i) { - # OK - } else { - unsafe_warn("\$$var can only contain [-a-z0-9_+,.%:/= ]."); - } - } - if($ENV{'TMPDIR'} =~ /\n/) { - if(defined $opt::files) { - ::warning("Use --files0 when \$TMPDIR contains newline."); - } elsif($Global::cshell - and - (defined $opt::cat or defined $opt::fifo)) { - ::warning("--cat/--fifo fails under csh ". - "if \$TMPDIR contains newline."); - } - } elsif($ENV{'TMPDIR'} =~ /\257/) { - unsafe_warn("\$TMPDIR with \\257 (\257) is not supported."); - } else{ - test_safe_chars('TMPDIR'); - } - map { test_safe_chars($_); } qw(PARALLEL_HOME XDG_CONFIG_DIRS - PARALLEL_REMOTE_TMPDIR XDG_CACHE_HOME); -} - -sub init_globals() { - # Defaults: - $Global::version = 20231122; - $Global::progname = 'parallel'; - $::name = "GNU Parallel"; - $Global::infinity = 2**31; - $Global::debug = 0; - $Global::verbose = 0; - # Don't quote every part of the command line - $Global::quoting = 0; - # Quote replacement strings - $Global::quote_replace = 1; - $Global::total_completed = 0; - $Global::cache_replacement_eval = 1; - # Read only table with default --rpl values - %Global::replace = - ( - '{}' => '', - '{#}' => '1 $_=$job->seq()', - '{%}' => '1 $_=$job->slot()', - '{/}' => 's:.*/::', - '{//}' => - ('$Global::use{"File::Basename"} ||= eval "use File::Basename; 1;"; '. - '$_ = dirname($_);'), - '{/.}' => 's:.*/::; s:\.[^/.]*$::;', - '{.}' => 's:\.[^/.]*$::', - ); - %Global::plus = - ( - # {} = {+/}/{/} - # = {.}.{+.} = {+/}/{/.}.{+.} - # = {..}.{+..} = {+/}/{/..}.{+..} - # = {...}.{+...} = {+/}/{/...}.{+...} - '{+/}' => 's:/[^/]*$:: || s:.*$::', - # a.b => b; a => '' - '{+.}' => 's:.*\.:: || s:.*$::', - # a.b.c => b.c; a.b => ''; a => '' - '{+..}' => 's:.*\.([^/.]*\.[^/.]*)$:$1: || s:.*$::', - '{+...}' => 's:.*\.([^/.]*\.[^/.]*\.[^/.]*)$:$1: || s:.*$::', - '{..}' => 's:\.[^/.]*\.[^/.]*$::', - '{...}' => 's:\.[^/.]*\.[^/.]*\.[^/.]*$::', - '{/..}' => 's:.*/::; s:\.[^/.]*\.[^/.]*$::', - '{/...}' => 's:.*/::; s:\.[^/.]*\.[^/.]*\.[^/.]*$::', - # n choose k = Binomial coefficient - '{choose_k}' => ('for $t (2..$#arg)'. - '{ if($arg[$t-1] ge $arg[$t]) { skip() } }'), - # unique values: Skip job if any args are the same - '{uniq}' => 'if(::uniq(@arg) != @arg) { skip(); }', - # {##} = number of jobs - '{##}' => '1 $_=total_jobs()', - # {0%} = 0-padded jobslot - '{0%}' => ('1 $f=1+int((log($Global::max_jobs_running||1)/log(10)));'. - '$_=sprintf("%0${f}d",slot())'), - # {0%} = 0-padded seq - '{0#}' => ('1 $f=1+int((log(total_jobs())/log(10)));'. - '$_=sprintf("%0${f}d",seq())'), - - ## Bash inspired replacement strings - # Bash ${a:-myval} - '{:-([^}]+?)}' => '$_ ||= $$1', - # Bash ${a:2} - '{:(\d+?)}' => 'substr($_,0,$$1) = ""', - # Bash ${a:2:3} - '{:(\d+?):(\d+?)}' => '$_ = substr($_,$$1,$$2);', - # echo {#z.*z.} ::: z.z.z.foo => z.foo - # echo {##z.*z.} ::: z.z.z.foo => foo - # Bash ${a#bc} - '{#([^#}][^}]*?)}' => - '$nongreedy=::make_regexp_ungreedy($$1);s/^$nongreedy(.*)/$1/;', - # Bash ${a##bc} - '{##([^#}][^}]*?)}' => 's/^$$1//;', - # echo {%.z.*z} ::: foo.z.z.z => foo.z - # echo {%%.z.*z} ::: foo.z.z.z => foo - # Bash ${a%def} - '{%([^}]+?)}' => - '$nongreedy=::make_regexp_ungreedy($$1);s/(.*)$nongreedy$/$1/;', - # Bash ${a%%def} - '{%%([^}]+?)}' => 's/$$1$//;', - # Bash ${a/def/ghi} ${a/def/} - '{/([^#%}/]+?)/([^}]*?)}' => 's/$$1/$$2/;', - # Bash ${a/#def/ghi} ${a/#def/} - '{/#([^}]+?)/([^}]*?)}' => 's/^$$1/$$2/g;', - # Bash ${a/%def/ghi} ${a/%def/} - '{/%([^}]+?)/([^}]*?)}' => 's/$$1$/$$2/g;', - # Bash ${a//def/ghi} ${a//def/} - '{//([^}]+?)/([^}]*?)}' => 's/$$1/$$2/g;', - # Bash ${a^a} - '{^([^}]+?)}' => 's/^($$1)/uc($1)/e;', - # Bash ${a^^a} - '{^^([^}]+?)}' => 's/($$1)/uc($1)/eg;', - # Bash ${a,A} - '{,([^}]+?)}' => 's/^($$1)/lc($1)/e;', - # Bash ${a,,A} - '{,,([^}]+?)}' => 's/($$1)/lc($1)/eg;', - - # {slot} = $PARALLEL_JOBSLOT - '{slot}' => '1 $_="\${PARALLEL_JOBSLOT}";uq()', - # {host} = ssh host - '{host}' => '1 $_="\${PARALLEL_SSHHOST}";uq()', - # {sshlogin} = sshlogin - '{sshlogin}' => '1 $_="\${PARALLEL_SSHLOGIN}";uq()', - # {hgrp} = hostgroups of the host - '{hgrp}' => '1 $_="\${PARALLEL_HOSTGROUPS}";uq()', - # {agrp} = hostgroups of the argument - '{agrp}' => '1 $_="\${PARALLEL_ARGHOSTGROUPS}";uq()', - ); - # Modifiable copy of %Global::replace - %Global::rpl = %Global::replace; - $/ = "\n"; - $Global::ignore_empty = 0; - $Global::interactive = 0; - $Global::stderr_verbose = 0; - $Global::default_simultaneous_sshlogins = 9; - $Global::exitstatus = 0; - $Global::arg_sep = ":::"; - $Global::arg_file_sep = "::::"; - $Global::trim = 'n'; - $Global::max_jobs_running = 0; - $Global::job_already_run = ''; - $ENV{'TMPDIR'} ||= "/tmp"; - $ENV{'PARALLEL_REMOTE_TMPDIR'} ||= "/tmp"; - # bug #55398: set $OLDPWD when using --wd - $ENV{'OLDPWD'} = $ENV{'PWD'}; - if(not $ENV{HOME}) { - # $ENV{HOME} is sometimes not set if called from PHP - ::warning("\$HOME not set. Using /tmp."); - $ENV{HOME} = "/tmp"; - } - # no warnings to allow for undefined $XDG_* - no warnings 'uninitialized'; - # If $PARALLEL_HOME is set, but does not exist, try making it. - if(defined $ENV{'PARALLEL_HOME'}) { - eval { File::Path::mkpath($ENV{'PARALLEL_HOME'}); }; - } - # $xdg_config_home is needed to make env_parallel.fish stop complaining - my $xdg_config_home = $ENV{'XDG_CONFIG_HOME'}; - # Use the first config dir that exists from: - # $PARALLEL_HOME - # $XDG_CONFIG_HOME/parallel - # $(each XDG_CONFIG_DIRS)/parallel - # $HOME/.parallel - # - # Keep only dirs that exist - @Global::config_dirs = - (grep { -d $_ } - $ENV{'PARALLEL_HOME'}, - (map { "$_/parallel" } - $xdg_config_home, - split /:/, $ENV{'XDG_CONFIG_DIRS'}), - $ENV{'HOME'} . "/.parallel"); - # Use first dir as config dir - $Global::config_dir = $Global::config_dirs[0] || - $ENV{'HOME'} . "/.parallel"; - if($ENV{'PARALLEL_HOME'} =~ /./ and not -d $ENV{'PARALLEL_HOME'}) { - ::warning("\$PARALLEL_HOME ($ENV{'PARALLEL_HOME'}) does not exist."); - ::warning("Using $Global::config_dir"); - } - # Use the first cache dir that exists from: - # $PARALLEL_HOME - # $XDG_CACHE_HOME/parallel - # Keep only dirs that exist - @Global::cache_dirs = (grep { -d $_ } - $ENV{'PARALLEL_HOME'}, - $ENV{'XDG_CACHE_HOME'}."/parallel"); - $Global::cache_dir = $Global::cache_dirs[0] || - $ENV{'HOME'} . "/.parallel"; - Job::init_color(); -} - -sub parse_halt() { - # $opt::halt flavours - # Uses: - # $opt::halt - # $Global::halt_when - # $Global::halt_fail - # $Global::halt_success - # $Global::halt_pct - # $Global::halt_count - if(defined $opt::halt) { - my %halt_expansion = ( - "0" => "never", - "1" => "soon,fail=1", - "2" => "now,fail=1", - "-1" => "soon,success=1", - "-2" => "now,success=1", - ); - # Expand -2,-1,0,1,2 into long form - $opt::halt = $halt_expansion{$opt::halt} || $opt::halt; - # --halt 5% == --halt soon,fail=5% - $opt::halt =~ s/^(\d+)%$/soon,fail=$1%/; - # Split: soon,fail=5% - my ($when,$fail_success,$pct_count) = split /[,=]/, $opt::halt; - if(not grep { $when eq $_ } qw(never soon now)) { - ::error("--halt must have 'never', 'soon', or 'now'."); - ::wait_and_exit(255); - } - $Global::halt_when = $when; - if($when ne "never") { - if($fail_success eq "fail") { - $Global::halt_fail = 1; - } elsif($fail_success eq "success") { - $Global::halt_success = 1; - } elsif($fail_success eq "done") { - $Global::halt_done = 1; - } else { - ::error("--halt $when must be followed by ,success or ,fail."); - ::wait_and_exit(255); - } - if($pct_count =~ /^(\d+)%$/) { - $Global::halt_pct = $1/100; - } elsif($pct_count =~ /^(\d+)$/) { - $Global::halt_count = $1; - } else { - ::error("--halt $when,$fail_success ". - "must be followed by ,number or ,percent%."); - ::wait_and_exit(255); - } - } - } -} - -sub parse_replacement_string_options() { - # Deal with --rpl - # Uses: - # %Global::rpl - # $Global::parensleft - # $Global::parensright - # $opt::parens - # $Global::parensleft - # $Global::parensright - # $opt::plus - # %Global::plus - # $opt::I - # $opt::U - # $opt::i - # $opt::basenamereplace - # $opt::dirnamereplace - # $opt::seqreplace - # $opt::slotreplace - # $opt::basenameextensionreplace - - sub rpl($$) { - # Modify %Global::rpl - # Replace $old with $new - my ($old,$new) = @_; - if($old ne $new) { - $Global::rpl{$new} = $Global::rpl{$old}; - delete $Global::rpl{$old}; - } - } - my $parens = "{==}"; - if(defined $opt::parens) { $parens = $opt::parens; } - my $parenslen = 0.5*length $parens; - $Global::parensleft = substr($parens,0,$parenslen); - $Global::parensright = substr($parens,$parenslen); - if(defined $opt::plus) { %Global::rpl = (%Global::plus,%Global::rpl); } - if(defined $opt::I) { rpl('{}',$opt::I); } - if(defined $opt::i and $opt::i) { rpl('{}',$opt::i); } - if(defined $opt::U) { rpl('{.}',$opt::U); } - if(defined $opt::basenamereplace) { rpl('{/}',$opt::basenamereplace); } - if(defined $opt::dirnamereplace) { rpl('{//}',$opt::dirnamereplace); } - if(defined $opt::seqreplace) { rpl('{#}',$opt::seqreplace); } - if(defined $opt::slotreplace) { rpl('{%}',$opt::slotreplace); } - if(defined $opt::basenameextensionreplace) { - rpl('{/.}',$opt::basenameextensionreplace); - } - for(@opt::rpl) { - # Create $Global::rpl entries for --rpl options - # E.g: "{..} s:\.[^.]+$:;s:\.[^.]+$:;" - my ($shorthand,$long) = split/\s/,$_,2; - $Global::rpl{$shorthand} = $long; - } -} - -sub parse_semaphore() { - # Semaphore defaults - # Must be done before computing number of processes and max_line_length - # because when running as a semaphore GNU Parallel does not read args - # Uses: - # $opt::semaphore - # $Global::semaphore - # $opt::semaphoretimeout - # $Semaphore::timeout - # $opt::semaphorename - # $Semaphore::name - # $opt::fg - # $Semaphore::fg - # $opt::wait - # $Semaphore::wait - # $opt::bg - # @opt::a - # @Global::unget_argv - # $Global::default_simultaneous_sshlogins - # $opt::jobs - # $Global::interactive - $Global::semaphore ||= ($0 =~ m:(^|/)sem$:); # called as 'sem' - if(defined $opt::semaphore) { $Global::semaphore = 1; } - if(defined $opt::semaphoretimeout) { $Global::semaphore = 1; } - if(defined $opt::semaphorename) { $Global::semaphore = 1; } - if(defined $opt::fg and not $opt::tmux and not $opt::tmuxpane) { - $Global::semaphore = 1; - } - if(defined $opt::bg) { $Global::semaphore = 1; } - if(defined $opt::wait and not $opt::sqlmaster) { - $Global::semaphore = 1; @ARGV = "true"; - } - if($Global::semaphore) { - if(@opt::a) { - # Assign the first -a to STDIN - open(STDIN,"<",shift @opt::a); - if(@opt::a) { - # We currently have no way of dealing with more -a - ::error("A semaphore cannot take input from more files\n"); - ::wait_and_exit(255); - } - } - @opt::a = ("/dev/null"); - # Append a dummy empty argument - # \0 => nothing (not the empty string) - push(@Global::unget_argv, [Arg->new("\0noarg")]); - $Semaphore::timeout = int(multiply_time_units($opt::semaphoretimeout)) - || 0; - if(defined $opt::semaphorename) { - $Semaphore::name = $opt::semaphorename; - } else { - local $/ = "\n"; - $Semaphore::name = `tty`; - chomp $Semaphore::name; - } - $Semaphore::fg = $opt::fg; - $Semaphore::wait = $opt::wait; - $Global::default_simultaneous_sshlogins = 1; - if(not defined $opt::jobs) { - $opt::jobs = 1; - } - if($Global::interactive and $opt::bg) { - ::error("Jobs running in the ". - "background cannot be interactive."); - ::wait_and_exit(255); - } - } -} - -sub record_env() { - # Record current %ENV-keys in $PARALLEL_HOME/ignored_vars - # Returns: N/A - my $ignore_filename = $Global::config_dir . "/ignored_vars"; - if(open(my $vars_fh, ">", $ignore_filename)) { - print $vars_fh map { $_,"\n" } keys %ENV; - } else { - ::error("Cannot write to $ignore_filename."); - ::wait_and_exit(255); - } -} - -sub open_joblog() { - # Open joblog as specified by --joblog - # Uses: - # $opt::resume - # $opt::resume_failed - # $opt::joblog - # $opt::results - # $Global::job_already_run - # %Global::fh - my $append = 0; - if(($opt::resume or $opt::resume_failed) - and - not ($opt::joblog or $opt::results)) { - ::error("--resume and --resume-failed require --joblog or --results."); - ::wait_and_exit(255); - } - if(defined $opt::joblog and $opt::joblog =~ s/^\+//) { - # --joblog +filename = append to filename - $append = 1; - } - if($opt::joblog - and - ($opt::sqlmaster - or - not $opt::sqlworker)) { - # Do not log if --sqlworker - if($opt::resume || $opt::resume_failed || $opt::retry_failed) { - if(open(my $joblog_fh, "<", $opt::joblog)) { - # Enable utf8 if possible - eval q{ binmode $joblog_fh, "encoding(utf8)"; }; - # Read the joblog - # Override $/ with \n because -d might be set - local $/ = "\n"; - # If there is a header: Open as append later - $append = <$joblog_fh>; - my $joblog_regexp; - if($opt::retry_failed) { - # Make a regexp that matches commands with exit+signal=0 - # 4 host 1360490623.067 3.445 1023 1222 0 0 command - $joblog_regexp='^(\d+)(?:\t[^\t]+){5}\t0\t0\t'; - my @group; - while(<$joblog_fh>) { - if(/$joblog_regexp/o) { - # This is 30% faster than set_job_already_run($1); - vec($Global::job_already_run,($1||0),1) = 1; - $Global::total_completed++; - $group[$1-1] = "true"; - } elsif(/(\d+)\s+\S+(\s+[-0-9.]+){6}\s+(.*)$/) { - # Grab out the command - $group[$1-1] = $3; - } else { - chomp; - ::error("Format of '$opt::joblog' is wrong: $_"); - ::wait_and_exit(255); - } - } - if(@group) { - my ($outfh,$name) = ::tmpfile(SUFFIX => ".arg"); - unlink($name); - # Put args into argfile - if(grep /\0/, @group) { - # force --null to deal with \n in commandlines - ::warning("Command lines contain newline. ". - "Forcing --null."); - $opt::null = 1; - $/ = "\0"; - } - # Replace \0 with '\n' as used in print_joblog() - print $outfh (map { s/\0/\n/g; $_,$/ } - map { $_ } @group); - seek $outfh, 0, 0; - exit_if_disk_full(); - # Set filehandle to -a - @opt::a = ($outfh); - } - # Remove $command (so -a is run) - @ARGV = (); - } - if($opt::resume || $opt::resume_failed) { - if($opt::resume_failed) { - # Make a regexp that matches commands with exit+signal=0 - # 4 host 1360490623.067 3.445 1023 1222 0 0 command - $joblog_regexp='^(\d+)(?:\t[^\t]+){5}\t0\t0\t'; - } else { - # Just match the job number - $joblog_regexp='^(\d+)'; - } - while(<$joblog_fh>) { - if(/$joblog_regexp/o) { - # This is 30% faster than set_job_already_run($1); - vec($Global::job_already_run,($1||0),1) = 1; - $Global::total_completed++; - } elsif(not /\d+\s+[^\s]+\s+([-0-9.]+\s+){6}/) { - ::error("Format of '$opt::joblog' is wrong: $_"); - ::wait_and_exit(255); - } - } - } - close $joblog_fh; - } - # $opt::null may be set if the commands contain \n - if($opt::null) { $/ = "\0"; } - } - if($opt::dryrun) { - # Do not write to joblog in a dry-run - if(not open($Global::joblog, ">", "/dev/null")) { - ::error("Cannot write to --joblog $opt::joblog."); - ::wait_and_exit(255); - } - } elsif($append) { - # Append to joblog - if(not open($Global::joblog, ">>", $opt::joblog)) { - ::error("Cannot append to --joblog $opt::joblog."); - ::wait_and_exit(255); - } - } else { - if($opt::joblog eq "-") { - # Use STDOUT as joblog - $Global::joblog = $Global::fh{1}; - } elsif(not open($Global::joblog, ">", $opt::joblog)) { - # Overwrite the joblog - ::error("Cannot write to --joblog $opt::joblog."); - ::wait_and_exit(255); - } - print $Global::joblog - join("\t", "Seq", "Host", "Starttime", "JobRuntime", - "Send", "Receive", "Exitval", "Signal", "Command" - ). "\n"; - } - } -} - -sub open_json_csv() { - if($opt::results) { - # Output as JSON/CSV/TSV - if($opt::results eq "-.csv" - or - $opt::results eq "-.tsv" - or - $opt::results eq "-.json") { - # Output as JSON/CSV/TSV on stdout - open $Global::csv_fh, ">&", "STDOUT" or - ::die_bug("Can't dup STDOUT in csv: $!"); - # Do not print any other output to STDOUT - # by forcing all other output to /dev/null - open my $fd, ">", "/dev/null" or - ::die_bug("Can't >/dev/null in csv: $!"); - $Global::fh{1} = $fd; - $Global::fh{2} = $fd; - } elsif($Global::csvsep or $Global::jsonout) { - if(not open($Global::csv_fh,">",$opt::results)) { - ::error("Cannot open results file `$opt::results': ". - "$!."); - wait_and_exit(255); - } - } - } -} - -sub find_compression_program() { - # Find a fast compression program - # Returns: - # $compress_program = compress program with options - # $decompress_program = decompress program with options - - # Search for these. Sorted by speed on 128 core - - # seq 120000000|shuf > 1gb & - # apt-get update - # apt install make g++ htop - # wget -O - pi.dk/3 | bash - # apt install zstd clzip liblz4-tool lzop pigz pxz gzip plzip pbzip2 lzma xz-utils lzip bzip2 lbzip2 lrzip pixz - # git clone https://github.com/facebook/zstd.git - # (cd zstd/contrib/pzstd; make -j; cp pzstd /usr/local/bin) - # echo 'lrzip -L $((-$1))' >/usr/local/bin/lrz - # chmod +x /usr/local/bin/lrz - # wait - # onethread="zstd clzip lz4 lzop gzip lzma xz bzip2" - # multithread="pzstd pigz pxz plzip pbzip2 lzip lbzip2 lrz pixz" - # parallel --shuf -j1 --joblog jl-m --arg-sep , parallel --compress-program \'{3}" "-{2}\' cat ::: 1gb '>'/dev/null , 1 2 3 , {1..3} , $multithread - # parallel --shuf -j50% --delay 1 --joblog jl-s --arg-sep , parallel --compress-program \'{3}" "-{2}\' cat ::: 1gb '>'/dev/null , 1 2 3 , {1..3} , $onethread - # sort -nk4 jl-? - - # 1-core: - # 2-cores: pzstd zstd lz4 lzop pigz gzip lbzip2 pbzip2 lrz bzip2 lzma pxz plzip xz lzip clzip - # 4-cores: - # 8-cores: pzstd lz4 zstd pigz lzop lbzip2 pbzip2 gzip lzip lrz plzip pxz bzip2 lzma xz clzip - # 16-cores: pzstd lz4 pigz lzop lbzip2 pbzip2 plzip lzip lrz pxz gzip lzma xz bzip2 - # 32-cores: pzstd lbzip2 pbzip2 zstd pigz lz4 lzop plzip lzip lrz gzip pxz lzma bzip2 xz clzip - # 64-cores: pzstd lbzip2 pbzip2 pigz zstd pixz lz4 plzip lzop lzip lrz gzip pxz lzma bzip2 xz clzip - # 128-core: pzstd lbzip2 pbzip2 zstd pixz lz4 pigz lzop plzip lzip gzip lrz pxz bzip2 lzma xz clzip - - my @prg = qw(pzstd lbzip2 pbzip2 zstd pixz lz4 pigz lzop plzip lzip gzip - lrz pxz bzip2 lzma xz clzip); - for my $p (@prg) { - if(which($p)) { - return ("$p -c -1","$p -dc"); - } - } - # Fall back to cat - return ("cat","cat"); -} - -sub read_options() { - # Read options from command line, profile and $PARALLEL - # Uses: - # $opt::shebang_wrap - # $opt::shebang - # @ARGV - # $opt::plain - # @opt::profile - # $ENV{'HOME'} - # $ENV{'PARALLEL'} - # Returns: - # @ARGV_no_opt = @ARGV without --options - - # This must be done first as this may exec myself - if(defined $ARGV[0] and ($ARGV[0] =~ /^--shebang/ or - $ARGV[0] =~ /^--shebang-?wrap/ or - $ARGV[0] =~ /^--hashbang/)) { - # Program is called from #! line in script - # remove --shebang-wrap if it is set - $opt::shebang_wrap = ($ARGV[0] =~ s/^--shebang-?wrap *//); - # remove --shebang if it is set - $opt::shebang = ($ARGV[0] =~ s/^--shebang *//); - # remove --hashbang if it is set - $opt::shebang .= ($ARGV[0] =~ s/^--hashbang *//); - if($opt::shebang) { - my $argfile = Q(pop @ARGV); - # exec myself to split $ARGV[0] into separate fields - exec "$0 --skip-first-line -a $argfile @ARGV"; - } - if($opt::shebang_wrap) { - my @options; - my @parser; - if ($^O eq 'freebsd') { - # FreeBSD's #! puts different values in @ARGV than Linux' does - my @nooptions = @ARGV; - get_options_from_array(\@nooptions); - while($#ARGV > $#nooptions) { - push @options, shift @ARGV; - } - while(@ARGV and $ARGV[0] ne ":::") { - push @parser, shift @ARGV; - } - if(@ARGV and $ARGV[0] eq ":::") { - shift @ARGV; - } - } else { - @options = shift @ARGV; - } - my $script = Q(Q(shift @ARGV)); # TODO - test if script = " " - my @args = map{ Q($_) } @ARGV; - # exec myself to split $ARGV[0] into separate fields - exec "$0 --_pipe-means-argfiles @options @parser $script ". - "::: @args"; - } - } - if($ARGV[0] =~ / --shebang(-?wrap)? /) { - ::warning("--shebang and --shebang-wrap must be the first ". - "argument.\n"); - } - - Getopt::Long::Configure("bundling","require_order"); - my @ARGV_copy = @ARGV; - my @ARGV_orig = @ARGV; - # Check if there is a --profile to set @opt::profile - get_options_from_array(\@ARGV_copy,"profile|J=s","plain") || die_usage(); - my @ARGV_profile = (); - my @ARGV_env = (); - if(not $opt::plain) { - # Add options from $PARALLEL_HOME/config and other profiles - my @config_profiles = ( - "/etc/parallel/config", - (map { "$_/config" } @Global::config_dirs), - $ENV{'HOME'}."/.parallelrc"); - my @profiles = @config_profiles; - if(@opt::profile) { - # --profile overrides default profiles - @profiles = (); - for my $profile (@opt::profile) { - if($profile =~ m:^\./|^/:) { - # Look for ./profile in . - # Look for /profile in / - push @profiles, grep { -r $_ } $profile; - } else { - # Look for the $profile in @Global::config_dirs - push @profiles, grep { -r $_ } - map { "$_/$profile" } @Global::config_dirs; - } - } - } - for my $profile (@profiles) { - if(-r $profile) { - ::debug("init","Read $profile\n"); - local $/ = "\n"; - open (my $in_fh, "<", $profile) || - ::die_bug("read-profile: $profile"); - while(<$in_fh>) { - /^\s*\#/ and next; - chomp; - push @ARGV_profile, shell_words($_); - } - close $in_fh; - } else { - if(grep /^\Q$profile\E$/, @config_profiles) { - # config file is not required to exist - } else { - ::error("$profile not readable."); - wait_and_exit(255); - } - } - } - # Add options from shell variable $PARALLEL - if($ENV{'PARALLEL'}) { - push @ARGV_env, shell_words($ENV{'PARALLEL'}); - } - # Add options from env_parallel.csh via $PARALLEL_CSH - if($ENV{'PARALLEL_CSH'}) { - push @ARGV_env, shell_words($ENV{'PARALLEL_CSH'}); - } - } - Getopt::Long::Configure("bundling","require_order"); - get_options_from_array(\@ARGV_profile) || die_usage(); - get_options_from_array(\@ARGV_env) || die_usage(); - get_options_from_array(\@ARGV) || die_usage(); - # What were the options given on the command line? - # Used to start --sqlworker - my $ai = arrayindex(\@ARGV_orig, \@ARGV); - @Global::options_in_argv = @ARGV_orig[0..$ai-1]; - # Prepend non-options to @ARGV (such as commands like 'nice') - unshift @ARGV, @ARGV_profile, @ARGV_env; - return @ARGV; -} - -sub arrayindex() { - # Similar to Perl's index function, but for arrays - # Input: - # $arr_ref1 = ref to @array1 to search in - # $arr_ref2 = ref to @array2 to search for - # Returns: - # $pos = position of @array1 in @array2, -1 if not found - my ($arr_ref1,$arr_ref2) = @_; - my $array1_as_string = join "", map { "\0".$_ } @$arr_ref1; - my $array2_as_string = join "", map { "\0".$_ } @$arr_ref2; - my $i = index($array1_as_string,$array2_as_string,0); - if($i == -1) { return -1 } - my @before = split /\0/, substr($array1_as_string,0,$i); - return $#before; -} - -sub read_args_from_command_line() { - # Arguments given on the command line after: - # ::: ($Global::arg_sep) - # :::: ($Global::arg_file_sep) - # :::+ ($Global::arg_sep with --link) - # ::::+ ($Global::arg_file_sep with --link) - # Removes the arguments from @ARGV and: - # - puts filenames into -a - # - puts arguments into files and add the files to -a - # - adds --linkinputsource with 0/1 for each -a depending on :::+/::::+ - # Input: - # @::ARGV = command option ::: arg arg arg :::: argfiles - # Uses: - # $Global::arg_sep - # $Global::arg_file_sep - # $opt::_pipe_means_argfiles - # $opt::pipe - # @opt::a - # Returns: - # @argv_no_argsep = @::ARGV without ::: and :::: and following args - my %group_sep = ($Global::arg_sep => ":::", - $Global::arg_sep."+" => ":::+", - $Global::arg_file_sep => "::::", - $Global::arg_file_sep."+" => "::::+"); - sub is_linked($) { - # file is linked if file starts with + - local $_ = shift; - if(/^\+(.*)/) { - my $noplus = $1; - if(-e $_ and -e $noplus) { - ::error("It is unclear whether you mean +./$noplus or ./+$noplus"); - wait_and_exit(255); - } elsif(-e $_ and not -e $noplus) { - # This is ./+file = this is not linked - return 0; - } elsif(not -e $_ and -e $noplus) { - # This is +./file = this is linked - return 1; - } elsif(not -e $_ and not -e $noplus) { - # File does not exist, maybe it is stdin? - if($_ eq "-") { - # This is - = this is not linked - return 0; - } elsif($_ eq "+-") { - # This is +- = this is linked - return 1; - } else { - ::error("File not found: $_"); - wait_and_exit(255); - } - } else { - ::die_bug("noplus: $noplus $_"); - } - } - # not linked - return 0; - } - sub cmd_template() { - # remove command template from @ARGV - # keep ::: / :::: in @ARGV if any - my @cmd_template; - while(@ARGV) { - my $arg = shift @ARGV; - if($group_sep{$arg}) { - # Found separator: push it back and exit loop - unshift @ARGV, $arg; - last; - } - push @cmd_template, $arg; - } - return @cmd_template; - } - sub divide_into_groups() { - # Split arguments from @ARGV into groups: - # ::: 1 2 3 :::: a b c ::::+ d e f - # => - # [ ::: 1 2 3 ], [ :::: a b c ], [ ::::+ d e f ] - my @g; - my @grp; - while(@ARGV) { - my $arg = shift @ARGV; - if($group_sep{$arg}) { - # start a new group - push @grp, [@g]; - @g = ($group_sep{$arg}); - } else { - push @g, $arg; - } - } - push @grp, [@g]; - shift @grp; # The first will always be empty - return @grp; - } - sub save_to_file(@) { - # Put args into a file, return open file handle of file - # Create argfile - my ($fh,$name) = ::tmpfile(SUFFIX => ".arg"); - unlink($name); - # Put args into argfile - print $fh map { $_,$/ } @_; - seek $fh, 0, 0; - exit_if_disk_full(); - return $fh; - } - my @cmd = cmd_template(); - # The rest of @ARGV is ::: / :::: args - # If there are any -a: Rewrite them to use :::: - if(@opt::a) { unshift @ARGV, $Global::arg_file_sep, @opt::a; } - @opt::a = (); - # Convert ::: and :::: into (linked) files and put those into @opt::a - for my $g_ref (divide_into_groups()) { - my $group_sep = shift @$g_ref; - if($group_sep eq ":::" or $group_sep eq ":::+") { - # Group starts with ::: / :::+ - if($opt::_pipe_means_argfiles and $#$g_ref < 0) { - # TODO - # Deal with --shebang-wrap and ::: on the shebang line - } else { - push @opt::a, save_to_file(@$g_ref); - # if $group_sep == ":::+": it is linked - push @opt::linkinputsource, ($group_sep eq ":::+"); - } - } elsif($group_sep eq "::::" or $group_sep eq "::::+") { - # Group starts with :::: / ::::+ - for my $f (@$g_ref) { - if($group_sep eq "::::+") { - # Linking forced - push @opt::a, $f; - push @opt::linkinputsource, 1; - } elsif($group_sep eq "::::") { - # Auto detect linking - if(is_linked($f)) { - # +file - push @opt::linkinputsource, 1; - $f =~ s/^\+//; - } else { - # file (no plus) - push @opt::linkinputsource, 0; - } - push @opt::a, $f; - } else { - ::die_bug("arg link error"); - } - } - } else { - ::die_bug("arg link error"); - } - } - # Output: command to run with options - return @cmd; -} - -sub cleanup() { - # Returns: N/A - unlink keys %Global::unlink; - map { rmdir $_ } keys %Global::unlink; - if(@opt::basefile and $opt::cleanup) { cleanup_basefile(); } - for(keys %Global::sshmaster) { - # If 'ssh -M's are running: kill them - kill "TERM", $_; - } -} - - -sub __QUOTING_ARGUMENTS_FOR_SHELL__() {} - -sub shell_quote(@) { - # Input: - # @strings = strings to be quoted - # Returns: - # @shell_quoted_strings = string quoted as needed by the shell - return wantarray ? (map { Q($_) } @_) : (join" ",map { Q($_) } @_); -} - -sub shell_quote_scalar_rc($) { - # Quote for the rc-shell - my $a = $_[0]; - if(defined $a) { - if(($a =~ s/'/''/g) - + - ($a =~ s/[\n\002-\011\013-\032\\\#\?\`\(\)\{\}\[\]\^\*\<\=\>\~\|\; \"\!\$\&\'\202-\377]+/'$&'/go)) { - # A string was replaced - # No need to test for "" or \0 - } elsif($a eq "") { - $a = "''"; - } elsif($a eq "\0") { - $a = ""; - } - } - return $a; -} - -sub shell_quote_scalar_csh($) { - # Quote for (t)csh - my $a = $_[0]; - if(defined $a) { - # $a =~ s/([\002-\011\013-\032\\\#\?\`\(\)\{\}\[\]\^\*\>\<\~\|\; \"\!\$\&\'\202-\377])/\\$1/g; - # This is 1% faster than the above - if(($a =~ s/[\002-\011\013-\032\\\#\?\`\(\)\{\}\[\]\^\*\<\=\>\~\|\; \"\!\$\&\'\202-\377]/\\$&/go) - + - # quote newline in csh as \\\n - ($a =~ s/[\n]/"\\\n"/go)) { - # A string was replaced - # No need to test for "" or \0 - } elsif($a eq "") { - $a = "''"; - } elsif($a eq "\0") { - $a = ""; - } - } - return $a; -} - -sub shell_quote_scalar_default($) { - # Quote for other shells (Bourne compatibles) - # Inputs: - # $string = string to be quoted - # Returns: - # $shell_quoted = string quoted as needed by the shell - local $_ = $_[0]; - if(/[^-_.+a-z0-9\/]/i) { - s/'+/'"$&"'/g; # "-quote '-quotes: ''' => "'''" - $_ = "'$_'"; # '-quote entire string - s/^''//; # Remove unneeded '' at ends - s/''$//; # (faster than s/^''|''$//g) - return $_; - } elsif ($_ eq "") { - return "''"; - } else { - # No quoting needed - return $_; - } -} - -sub shell_quote_scalar($) { - # Quote the string so the shell will not expand any special chars - # Inputs: - # $string = string to be quoted - # Returns: - # $shell_quoted = string quoted as needed by the shell - - # Speed optimization: Choose the correct shell_quote_scalar_* - # and call that directly from now on - no warnings 'redefine'; - if($Global::cshell) { - # (t)csh - *shell_quote_scalar = \&shell_quote_scalar_csh; - } elsif($Global::shell =~ m:(^|/)rc$:) { - # rc-shell - *shell_quote_scalar = \&shell_quote_scalar_rc; - } else { - # other shells - *shell_quote_scalar = \&shell_quote_scalar_default; - } - # The sub is now redefined. Call it - return shell_quote_scalar($_[0]); -} - -sub Q($) { - # Q alias for ::shell_quote_scalar - my $ret = shell_quote_scalar($_[0]); - no warnings 'redefine'; - *Q = \&::shell_quote_scalar; - return $ret; -} - -sub shell_quote_file($) { - # Quote the string so shell will not expand any special chars - # and prepend ./ if needed - # Input: - # $filename = filename to be shell quoted - # Returns: - # $quoted_filename = filename quoted with \ and ./ if needed - my $a = shift; - if(defined $a) { - if($a =~ m:^/: or $a =~ m:^\./:) { - # /abs/path or ./rel/path => skip - } else { - # rel/path => ./rel/path - $a = "./".$a; - } - } - return Q($a); -} - -sub shell_words(@) { - # Input: - # $string = shell line - # Returns: - # @shell_words = $string split into words as shell would do - $Global::use{"Text::ParseWords"} ||= eval "use Text::ParseWords; 1;"; - return Text::ParseWords::shellwords(@_); -} - -sub perl_quote_scalar($) { - # Quote the string so perl's eval will not expand any special chars - # Inputs: - # $string = string to be quoted - # Returns: - # $perl_quoted = string quoted with \ as needed by perl's eval - my $a = $_[0]; - if(defined $a) { - $a =~ s/[\\\"\$\@]/\\$&/go; - } - return $a; -} - -# -w complains about prototype -sub pQ($) { - # pQ alias for ::perl_quote_scalar - my $ret = perl_quote_scalar($_[0]); - *pQ = \&::perl_quote_scalar; - return $ret; -} - -sub unquote_printf() { - # Convert \t \n \r \000 \0 - # Inputs: - # $string = string with \t \n \r \num \0 - # Returns: - # $replaced = string with TAB NEWLINE CR NUL - $_ = shift; - s/\\t/\t/g; - s/\\n/\n/g; - s/\\r/\r/g; - s/\\(\d\d\d)/eval 'sprintf "\\'.$1.'"'/ge; - s/\\(\d)/eval 'sprintf "\\'.$1.'"'/ge; - return $_; -} - - -sub __FILEHANDLES__() {} - - -sub save_stdin_stdout_stderr() { - # Remember the original STDIN, STDOUT and STDERR - # and file descriptors opened by the shell (e.g. 3>/tmp/foo) - # Uses: - # %Global::fh - # $Global::original_stderr - # $Global::original_stdin - # Returns: N/A - - # TODO Disabled until we have an open3 that will take n filehandles - # for my $fdno (1..61) { - # # /dev/fd/62 and above are used by bash for <(cmd) - # # Find file descriptors that are already opened (by the shell) - # Only focus on stdout+stderr for now - for my $fdno (1..2) { - my $fh; - # 2-argument-open is used to be compatible with old perl 5.8.0 - # bug #43570: Perl 5.8.0 creates 61 files - if(open($fh,">&=$fdno")) { - $Global::fh{$fdno}=$fh; - } - } - open $Global::original_stderr, ">&", "STDERR" or - ::die_bug("Can't dup STDERR: $!"); - open $Global::status_fd, ">&", "STDERR" or - ::die_bug("Can't dup STDERR: $!"); - open $Global::original_stdin, "<&", "STDIN" or - ::die_bug("Can't dup STDIN: $!"); -} - -sub enough_file_handles() { - # Check that we have enough filehandles available for starting - # another job - # Uses: - # $opt::ungroup - # %Global::fh - # Returns: - # 1 if ungrouped (thus not needing extra filehandles) - # 0 if too few filehandles - # 1 if enough filehandles - if(not $opt::ungroup) { - my %fh; - my $enough_filehandles = 1; - # perl uses 7 filehandles for something? - # open3 uses 2 extra filehandles temporarily - # We need a filehandle for each redirected file descriptor - # (normally just STDOUT and STDERR) - for my $i (1..(7+2+keys %Global::fh)) { - $enough_filehandles &&= open($fh{$i}, "<", "/dev/null"); - } - for (values %fh) { close $_; } - return $enough_filehandles; - } else { - # Ungrouped does not need extra file handles - return 1; - } -} - -sub open_or_exit($) { - # Open a file name or exit if the file cannot be opened - # Inputs: - # $file = filehandle or filename to open - # Uses: - # $Global::original_stdin - # Returns: - # $fh = file handle to read-opened file - my $file = shift; - if($file eq "-") { - return ($Global::original_stdin || *STDIN); - } - if(ref $file eq "GLOB") { - # This is an open filehandle - return $file; - } - my $fh = gensym; - if(not open($fh, "<", $file)) { - ::error("Cannot open input file `$file': No such file or directory."); - wait_and_exit(255); - } - return $fh; -} - -sub set_fh_blocking($) { - # Set filehandle as blocking - # Inputs: - # $fh = filehandle to be blocking - # Returns: - # N/A - my $fh = shift; - $Global::use{"Fcntl"} ||= eval "use Fcntl qw(:DEFAULT :flock); 1;"; - my $flags; - # Get the current flags on the filehandle - fcntl($fh, &F_GETFL, $flags) || die $!; - # Remove non-blocking from the flags - $flags &= ~&O_NONBLOCK; - # Set the flags on the filehandle - fcntl($fh, &F_SETFL, $flags) || die $!; -} - -sub set_fh_non_blocking($) { - # Set filehandle as non-blocking - # Inputs: - # $fh = filehandle to be blocking - # Returns: - # N/A - my $fh = shift; - $Global::use{"Fcntl"} ||= eval "use Fcntl qw(:DEFAULT :flock); 1;"; - my $flags; - # Get the current flags on the filehandle - fcntl($fh, &F_GETFL, $flags) || die $!; - # Add non-blocking to the flags - $flags |= &O_NONBLOCK; - # Set the flags on the filehandle - fcntl($fh, &F_SETFL, $flags) || die $!; -} - - -sub __RUNNING_THE_JOBS_AND_PRINTING_PROGRESS__() {} - - -# Variable structure: -# -# $Global::running{$pid} = Pointer to Job-object -# @Global::virgin_jobs = Pointer to Job-object that have received no input -# $Global::host{$sshlogin} = Pointer to SSHLogin-object -# $Global::total_running = total number of running jobs -# $Global::total_started = total jobs started -# $Global::max_procs_file = filename if --jobs is given a filename -# $Global::JobQueue = JobQueue object for the queue of jobs -# $Global::timeoutq = queue of times where jobs timeout -# $Global::newest_job = Job object of the most recent job started -# $Global::newest_starttime = timestamp of $Global::newest_job -# @Global::sshlogin -# $Global::minimal_command_line_length = min len supported by all sshlogins -# $Global::start_no_new_jobs = should more jobs be started? -# $Global::original_stderr = file handle for STDERR when the program started -# $Global::total_started = total number of jobs started -# $Global::joblog = filehandle of joblog -# $Global::debug = Is debugging on? -# $Global::exitstatus = status code of GNU Parallel -# $Global::quoting = quote the command to run - -sub init_run_jobs() { - # Set Global variables and progress signal handlers - # Do the copying of basefiles - # Returns: N/A - $Global::total_running = 0; - $Global::total_started = 0; - $SIG{USR1} = \&list_running_jobs; - $SIG{USR2} = \&toggle_progress; - if(@opt::basefile) { setup_basefile(); } -} - -{ - my $last_time; - my %last_mtime; - my $max_procs_file_last_mod; - - sub changed_procs_file { - # If --jobs is a file and it is modfied: - # Force recomputing of max_jobs_running for each $sshlogin - # Uses: - # $Global::max_procs_file - # %Global::host - # Returns: N/A - if($Global::max_procs_file) { - # --jobs filename - my $mtime = (stat($Global::max_procs_file))[9]; - $max_procs_file_last_mod ||= 0; - if($mtime > $max_procs_file_last_mod) { - # file changed: Force re-computing max_jobs_running - $max_procs_file_last_mod = $mtime; - for my $sshlogin (values %Global::host) { - $sshlogin->set_max_jobs_running(undef); - } - } - } - } - - sub changed_sshloginfile { - # If --slf is changed: - # reload --slf - # filter_hosts - # setup_basefile - # Uses: - # @opt::sshloginfile - # @Global::sshlogin - # %Global::host - # $opt::filter_hosts - # Returns: N/A - if(@opt::sshloginfile) { - # Is --sshloginfile changed? - for my $slf (@opt::sshloginfile) { - my $actual_file = expand_slf_shorthand($slf); - my $mtime = (stat($actual_file))[9]; - $last_mtime{$actual_file} ||= $mtime; - if($mtime - $last_mtime{$actual_file} > 1) { - ::debug("run", - "--sshloginfile $actual_file changed. reload\n"); - $last_mtime{$actual_file} = $mtime; - # Reload $slf - # Empty sshlogins - @Global::sshlogin = (); - for (values %Global::host) { - # Don't start new jobs on any host - # except the ones added back later - $_->set_max_jobs_running(0); - } - # This will set max_jobs_running on the SSHlogins - read_sshloginfile($actual_file); - parse_sshlogin(); - $opt::filter_hosts and filter_hosts(); - setup_basefile(); - } - } - } - } - - sub start_more_jobs { - # Run start_another_job() but only if: - # * not $Global::start_no_new_jobs set - # * not JobQueue is empty - # * not load on server is too high - # * not server swapping - # * not too short time since last remote login - # Uses: - # %Global::host - # $Global::start_no_new_jobs - # $Global::JobQueue - # $opt::pipe - # $opt::load - # $opt::noswap - # $opt::delay - # $Global::newest_starttime - # Returns: - # $jobs_started = number of jobs started - my $jobs_started = 0; - if($Global::start_no_new_jobs) { - return $jobs_started; - } - if(time - ($last_time||0) > 1) { - # At most do this every second - $last_time = time; - changed_procs_file(); - changed_sshloginfile(); - } - # This will start 1 job on each --sshlogin (if possible) - # thus distribute the jobs on the --sshlogins round robin - for my $sshlogin (values %Global::host) { - if($Global::JobQueue->empty() and not $opt::pipe) { - # No more jobs in the queue - last; - } - debug("run", "Running jobs before on ", $sshlogin->string(), ": ", - $sshlogin->jobs_running(), "\n"); - if ($sshlogin->jobs_running() < $sshlogin->max_jobs_running()) { - if($opt::delay - and - $opt::delay-0.008 > ::now()-$Global::newest_starttime) { - # It has been too short since last start - next; - } - if($opt::load and $sshlogin->loadavg_too_high()) { - # The load is too high or unknown - next; - } - if($opt::noswap and $sshlogin->swapping()) { - # The server is swapping - next; - } - if($opt::limit and $sshlogin->limit()) { - # Over limit - next; - } - if(($opt::memfree or $opt::memsuspend) - and - $sshlogin->memfree() < $Global::memlimit) { - # The server has not enough mem free - ::debug("mem", "Not starting job: not enough mem\n"); - next; - } - if($sshlogin->too_fast_remote_login()) { - # It has been too short since last login - next; - } - debug("run", $sshlogin->string(), - " has ", $sshlogin->jobs_running(), - " out of ", $sshlogin->max_jobs_running(), - " jobs running. Start another.\n"); - if(start_another_job($sshlogin) == 0) { - # No more jobs to start on this $sshlogin - debug("run","No jobs started on ", - $sshlogin->string(), "\n"); - next; - } - $sshlogin->inc_jobs_running(); - $sshlogin->set_last_login_at(::now()); - $jobs_started++; - } - debug("run","Running jobs after on ", $sshlogin->string(), ": ", - $sshlogin->jobs_running(), " of ", - $sshlogin->max_jobs_running(), "\n"); - } - - return $jobs_started; - } -} - -{ - my $no_more_file_handles_warned; - - sub start_another_job() { - # If there are enough filehandles - # and JobQueue not empty - # and not $job is in joblog - # Then grab a job from Global::JobQueue, - # start it at sshlogin - # mark it as virgin_job - # Inputs: - # $sshlogin = the SSHLogin to start the job on - # Uses: - # $Global::JobQueue - # $opt::pipe - # $opt::results - # $opt::resume - # @Global::virgin_jobs - # Returns: - # 1 if another jobs was started - # 0 otherwise - my $sshlogin = shift; - # Do we have enough file handles to start another job? - if(enough_file_handles()) { - if($Global::JobQueue->empty() and not $opt::pipe) { - # No more commands to run - debug("start", "Not starting: JobQueue empty\n"); - return 0; - } else { - my $job; - # Skip jobs already in job log - # Skip jobs already in results - do { - $job = get_job_with_sshlogin($sshlogin); - if(not defined $job) { - # No command available for that sshlogin - debug("start", "Not starting: no jobs available for ", - $sshlogin->string(), "\n"); - return 0; - } - if($job->is_already_in_joblog()) { - $job->free_slot(); - } - } while ($job->is_already_in_joblog() - or - ($opt::results and $opt::resume - and $job->is_already_in_results())); - debug("start", "Command to run on '", - $job->sshlogin()->string(), "': '", - $job->replaced(),"'\n"); - if($job->start()) { - if($opt::pipe) { - if($job->virgin()) { - push(@Global::virgin_jobs,$job); - } else { - # Block already set: This is a retry - $job->write_block(); - } - } - debug("start", "Started as seq ", $job->seq(), - " pid:", $job->pid(), "\n"); - return 1; - } else { - # Not enough processes to run the job. - # Put it back on the queue. - $Global::JobQueue->unget($job); - # Count down the number of jobs to run for this SSHLogin. - my $max = $sshlogin->max_jobs_running(); - if($max > 1) { $max--; } else { - my @arg; - for my $record (@{$job->{'commandline'}{'arg_list'}}) { - push @arg, map { $_->orig() } @$record; - } - ::error("No more processes: cannot run a single job. ". - "Something is wrong at @arg."); - ::wait_and_exit(255); - } - $sshlogin->set_max_jobs_running($max); - # Sleep up to 300 ms to give other processes time to die - ::usleep(rand()*300); - ::warning("No more processes: ". - "Decreasing number of running jobs to $max.", - "Try increasing 'ulimit -u' (try: ulimit -u `ulimit -Hu`)", - "or increasing 'nproc' in /etc/security/limits.conf", - "or increasing /proc/sys/kernel/pid_max"); - return 0; - } - } - } else { - # No more file handles - $no_more_file_handles_warned++ or - ::warning("No more file handles. ", - "Try running 'parallel -j0 -N 100 --pipe parallel -j0'", - "or increasing 'ulimit -n' (try: ulimit -n `ulimit -Hn`)", - "or increasing 'nofile' in /etc/security/limits.conf", - "or increasing /proc/sys/fs/file-max"); - debug("start", "No more file handles. "); - return 0; - } - } -} - -sub init_progress() { - # Uses: - # $opt::bar - # Returns: - # list of computers for progress output - $|=1; - if($opt::bar) { - return("",""); - } - my %progress = progress(); - return ("\nComputers / CPU cores / Max jobs to run\n", - $progress{'workerlist'}); -} - -sub drain_job_queue(@) { - # Uses: - # $opt::progress - # $Global::total_running - # $Global::max_jobs_running - # %Global::running - # $Global::JobQueue - # %Global::host - # $Global::start_no_new_jobs - # Returns: N/A - my @command = @_; - if($opt::progress) { - ::status_no_nl(init_progress()); - } - my $last_header = ""; - my $sleep = 0.2; - my $sleepsum = 0; - do { - while($Global::total_running > 0) { - debug("init",$Global::total_running, "==", scalar - keys %Global::running," slots: ", $Global::max_jobs_running); - if($opt::pipe) { - # When using --pipe sometimes file handles are not - # closed properly - for my $job (values %Global::running) { - close $job->fh(0,"w"); - } - } - if($opt::progress) { - my %progress = progress(); - if($last_header ne $progress{'header'}) { - ::status("", $progress{'header'}); - $last_header = $progress{'header'}; - } - ::status_no_nl("\r",$progress{'status'}); - } - if($Global::total_running < $Global::max_jobs_running - and not $Global::JobQueue->empty()) { - # These jobs may not be started because of loadavg - # or too little time between each ssh login. - if(start_more_jobs() > 0) { - # Exponential back-on if jobs were started - $sleep = $sleep/2+0.001; - } - } - # Exponential back-off sleeping - $sleep = ::reap_usleep($sleep); - $sleepsum += $sleep; - if($sleepsum >= 1000) { - # At most do this every second - $sleepsum = 0; - changed_procs_file(); - changed_sshloginfile(); - start_more_jobs(); - } - } - if(not $Global::JobQueue->empty()) { - # These jobs may not be started: - # * because there the --filter-hosts has removed all - if(not %Global::host) { - ::error("There are no hosts left to run on."); - ::wait_and_exit(255); - } - # * because of loadavg - # * because of too little time between each ssh login. - $sleep = ::reap_usleep($sleep); - start_more_jobs(); - if($Global::max_jobs_running == 0) { - ::warning("There are no job slots available. Increase --jobs."); - } - } - while($opt::sqlmaster and not $Global::sql->finished()) { - # SQL master - $sleep = ::reap_usleep($sleep); - start_more_jobs(); - if($Global::start_sqlworker) { - # Start an SQL worker as we are now sure there is work to do - $Global::start_sqlworker = 0; - if(my $pid = fork()) { - $Global::unkilled_sqlworker = $pid; - } else { - # Replace --sql/--sqlandworker with --sqlworker - my @ARGV = (map { s/^--sql(andworker)?$/--sqlworker/; $_ } - @Global::options_in_argv); - # exec the --sqlworker - exec($0,@ARGV,@command); - } - } - } - } while ($Global::total_running > 0 - or - not $Global::start_no_new_jobs and not $Global::JobQueue->empty() - or - $opt::sqlmaster and not $Global::sql->finished()); - $Global::all_jobs_done = 1; - if($opt::progress) { - my %progress = progress(); - ::status("\r".$progress{'status'}); - } -} - -sub toggle_progress() { - # Turn on/off progress view - # Uses: - # $opt::progress - # Returns: N/A - $opt::progress = not $opt::progress; - if($opt::progress) { - ::status_no_nl(init_progress()); - } -} - -sub progress() { - # Uses: - # $opt::bar - # $opt::eta - # %Global::host - # $Global::total_started - # Returns: - # $workerlist = list of workers - # $header = that will fit on the screen - # $status = message that will fit on the screen - if($opt::bar) { - return ("workerlist" => "", "header" => "", "status" => bar()); - } - my $eta = ""; - my ($status,$header)=("",""); - if($opt::eta) { - my($total, $completed, $left, $pctcomplete, $avgtime, $this_eta) = - compute_eta(); - $eta = sprintf("ETA: %ds Left: %d AVG: %.2fs ", - $this_eta, $left, $avgtime); - } - my $termcols = terminal_columns(); - my @workers = sort keys %Global::host; - my %sshlogin = map { $_ eq ":" ? ($_ => "local") : ($_ => $_) } @workers; - my $workerno = 1; - my %workerno = map { ($_=>$workerno++) } @workers; - my $workerlist = ""; - for my $w (@workers) { - $workerlist .= - $workerno{$w}.":".$sshlogin{$w} ." / ". - ($Global::host{$w}->ncpus() || "-")." / ". - $Global::host{$w}->max_jobs_running()."\n"; - } - $status = "c"x($termcols+1); - # Select an output format that will fit on a single line - if(length $status > $termcols) { - # sshlogin1:XX/XX/XX%/XX.Xs s2:XX/XX/XX%/XX.Xs s3:XX/XX/XX%/XX.Xs - $header = "Computer:jobs running/jobs completed/". - "%of started jobs/Average seconds to complete"; - $status = $eta . - join(" ",map - { - if($Global::total_started) { - my $completed = - ($Global::host{$_}->jobs_completed()||0); - my $running = $Global::host{$_}->jobs_running(); - my $time = $completed ? (time-$^T)/($completed) : "0"; - sprintf("%s:%d/%d/%d%%/%.1fs ", - $sshlogin{$_}, $running, $completed, - ($running+$completed)*100 - / $Global::total_started, $time); - } - } @workers); - } - if(length $status > $termcols) { - # 1:XX/XX/XX%/X.Xs 2:XX/XX/XX%/X.Xs 3:XX/XX/XX%/X.Xs 4:XX/XX/XX%/X.Xs - $header = "Computer:jobs running/jobs completed/%of started jobs"; - $status = $eta . - join(" ",map - { - if($Global::total_started) { - my $completed = - ($Global::host{$_}->jobs_completed()||0); - my $running = $Global::host{$_}->jobs_running(); - my $time = $completed ? (time-$^T)/($completed) : "0"; - sprintf("%s:%d/%d/%d%%/%.1fs ", - $workerno{$_}, $running, $completed, - ($running+$completed)*100 - / $Global::total_started, $time); - } - } @workers); - } - if(length $status > $termcols) { - # sshlogin1:XX/XX/XX% sshlogin2:XX/XX/XX% sshlogin3:XX/XX/XX% - $header = "Computer:jobs running/jobs completed/%of started jobs"; - $status = $eta . - join(" ",map - { - if($Global::total_started) { - sprintf("%s:%d/%d/%d%%", - $sshlogin{$_}, - $Global::host{$_}->jobs_running(), - ($Global::host{$_}->jobs_completed()||0), - ($Global::host{$_}->jobs_running()+ - ($Global::host{$_}->jobs_completed()||0))*100 - / $Global::total_started) - } - } - @workers); - } - if(length $status > $termcols) { - # 1:XX/XX/XX% 2:XX/XX/XX% 3:XX/XX/XX% 4:XX/XX/XX% 5:XX/XX/XX% - $header = "Computer:jobs running/jobs completed/%of started jobs"; - $status = $eta . - join(" ",map - { - if($Global::total_started) { - sprintf("%s:%d/%d/%d%%", - $workerno{$_}, - $Global::host{$_}->jobs_running(), - ($Global::host{$_}->jobs_completed()||0), - ($Global::host{$_}->jobs_running()+ - ($Global::host{$_}->jobs_completed()||0))*100 - / $Global::total_started) - } - } - @workers); - } - if(length $status > $termcols) { - # sshlogin1:XX/XX/XX% sshlogin2:XX/XX/XX% sshlogin3:XX/XX - $header = "Computer:jobs running/jobs completed"; - $status = $eta . - join(" ", - map { sprintf("%s:%d/%d", - $sshlogin{$_}, $Global::host{$_}->jobs_running(), - ($Global::host{$_}->jobs_completed()||0)) } - @workers); - } - if(length $status > $termcols) { - # sshlogin1:XX/XX sshlogin2:XX/XX sshlogin3:XX/XX sshlogin4:XX/XX - $header = "Computer:jobs running/jobs completed"; - $status = $eta . - join(" ", - map { sprintf("%s:%d/%d", - $sshlogin{$_}, $Global::host{$_}->jobs_running(), - ($Global::host{$_}->jobs_completed()||0)) } - @workers); - } - if(length $status > $termcols) { - # 1:XX/XX 2:XX/XX 3:XX/XX 4:XX/XX 5:XX/XX 6:XX/XX - $header = "Computer:jobs running/jobs completed"; - $status = $eta . - join(" ", - map { sprintf("%s:%d/%d", $workerno{$_}, - $Global::host{$_}->jobs_running(), - ($Global::host{$_}->jobs_completed()||0)) } - @workers); - } - if(length $status > $termcols) { - # sshlogin1:XX sshlogin2:XX sshlogin3:XX sshlogin4:XX sshlogin5:XX - $header = "Computer:jobs completed"; - $status = $eta . - join(" ", - map { sprintf("%s:%d", $sshlogin{$_}, - ($Global::host{$_}->jobs_completed()||0)) } - @workers); - } - if(length $status > $termcols) { - # 1:XX 2:XX 3:XX 4:XX 5:XX 6:XX - $header = "Computer:jobs completed"; - $status = $eta . - join(" ", - map { sprintf("%s:%d", - $workerno{$_}, - ($Global::host{$_}->jobs_completed()||0)) } - @workers); - } - return ("workerlist" => $workerlist, "header" => $header, - "status" => $status); -} - -{ - - my ($first_completed, $smoothed_avg_time, $last_eta); - - sub compute_eta { - # Calculate important numbers for ETA - # Returns: - # $total = number of jobs in total - # $completed = number of jobs completed - # $left = number of jobs left - # $pctcomplete = percent of jobs completed - # $avgtime = averaged time - # $eta = smoothed eta - my $completed = $Global::total_completed; - # In rare cases with -X will $completed > total_jobs() - my $total = ::max($Global::JobQueue->total_jobs(),$completed); - my $left = $total - $completed; - if(not $completed) { - return($total, $completed, $left, 0, 0, 0); - } - my $pctcomplete = ::min($completed / $total,100); - $first_completed ||= time; - my $timepassed = (time - $first_completed); - my $avgtime = $timepassed / $completed; - $smoothed_avg_time ||= $avgtime; - # Smooth the eta so it does not jump wildly - $smoothed_avg_time = (1 - $pctcomplete) * $smoothed_avg_time + - $pctcomplete * $avgtime; - my $eta = int($left * $smoothed_avg_time); - if($eta*0.90 < $last_eta and $last_eta < $eta) { - # Eta jumped less that 10% up: Keep the last eta instead - $eta = $last_eta; - } else { - $last_eta = $eta; - } - return($total, $completed, $left, $pctcomplete, $avgtime, $eta); - } -} - -{ - my ($rev,$reset); - - sub bar() { - # Return: - # $status = bar with eta, completed jobs, arg and pct - $rev ||= "\033[7m"; - $reset ||= "\033[0m"; - my($total, $completed, $left, $pctcomplete, $avgtime, $eta) = - compute_eta(); - if($Global::all_jobs_done) { $eta = now()-$Global::start_time; } - my $arg = $Global::newest_job ? - $Global::newest_job->{'commandline'}-> - replace_placeholders(["\257<\257>"],0,0) : ""; - $arg = decode_utf8($arg); - my $eta_dhms = ::seconds_to_time_units($eta); - my $bar_text = - sprintf("%d%% %d:%d=%s %s", - $pctcomplete*100, $completed, $left, $eta_dhms, $arg); - my $terminal_width = terminal_columns(); - my $s = sprintf("%-${terminal_width}s", - substr($bar_text." "x$terminal_width, - 0,$terminal_width)); - my $width = int($terminal_width * $pctcomplete); - substr($s,$width,0) = $reset; - my $zenity = sprintf("%-${terminal_width}s", - substr("# $eta sec $arg", - 0,$terminal_width)); - # Prefix with zenity header - $s = "\r" . $zenity . "\r" . $pctcomplete*100 . - "\r" . $rev . $s . $reset; - return $s; - } -} - -{ - my ($rows,$columns,$last_update_time); - - sub compute_terminal_size() { - # && true is to force spawning a shell and not just exec'ing - my @tput = qx{ tput lines cols /dev/null && true }; - $rows = 0 + $tput[0]; - $columns = 0 + $tput[1]; - if(not ($rows && $columns)) { - # && true is to force spawning a shell and not just exec'ing - my $stty = qx{ stty -a /dev/null && true }; - # FreeBSD/OpenBSD/NetBSD/Dragonfly/MirOS - # MacOSX/IRIX/AIX/Tru64 - $stty =~ /(\d+) columns/ and do { $columns = $1; }; - $stty =~ /(\d+) rows/ and do { $rows = $1; }; - # GNU/Linux/Solaris - $stty =~ /columns (\d+)/ and do { $columns = $1; }; - $stty =~ /rows (\d+)/ and do { $rows = $1; }; - # Solaris-x86/HPUX/SCOsysV/UnixWare/OpenIndiana - $stty =~ /columns = (\d+)/ and do { $columns = $1; }; - $stty =~ /rows = (\d+)/ and do { $rows = $1; }; - # QNX - $stty =~ /rows=(\d+),(\d+)/ and do { ($rows,$columns) = ($1,$2); }; - } - if(not ($rows && $columns)) { - # && true is to force spawning a shell and not just exec'ing - my $resize = qx{ resize 2>/dev/null && true }; - $resize =~ /COLUMNS=(\d+);/ and do { $columns ||= $1; }; - $resize =~ /LINES=(\d+);/ and do { $rows ||= $1; }; - } - $rows ||= 24; - $columns ||= 80; - } - - sub update_terminal_size() { - # Only update once per second. - if($last_update_time < time) { - $last_update_time = time; - compute_terminal_size(); - # Set signal WINdow CHange to force recompute - $SIG{WINCH} = \&compute_terminal_size; - } - } - - sub terminal_rows() { - # Get the number of rows of the terminal. - # Returns: - # number of rows of the screen - update_terminal_size(); - return $rows; - } - - sub terminal_columns() { - # Get the number of columns of the terminal. - # Returns: - # number of columns of the screen - update_terminal_size(); - return $columns; - } -} - -sub untabify($) { - # Convert \t into spaces - my @out; - my ($src); - # Deal with multi-byte characters - for my $src (split("\t",$_[0])) { - push @out, $src. " "x(8-mbswidth($src)%8); - } - return join "",@out; -} - -# Prototype forwarding -sub get_job_with_sshlogin($); -sub get_job_with_sshlogin($) { - # Input: - # $sshlogin = which host should the job be run on? - # Uses: - # $opt::hostgroups - # $Global::JobQueue - # Returns: - # $job = next job object for $sshlogin if any available - my $sshlogin = shift; - my $job; - - if ($opt::hostgroups) { - my @other_hostgroup_jobs = (); - - while($job = $Global::JobQueue->get()) { - if($sshlogin->in_hostgroups($job->hostgroups())) { - # Found a job to be run on a hostgroup of this - # $sshlogin - last; - } else { - # This job was not in the hostgroups of $sshlogin - push @other_hostgroup_jobs, $job; - } - } - $Global::JobQueue->unget(@other_hostgroup_jobs); - if(not defined $job) { - # No more jobs - return undef; - } - } else { - $job = $Global::JobQueue->get(); - if(not defined $job) { - # No more jobs - ::debug("start", "No more jobs: JobQueue empty\n"); - return undef; - } - } - if(not $job->suspended()) { - $job->set_sshlogin($sshlogin); - } - if(defined $opt::retries and $job->failed_here()) { - # This command with these args failed for this sshlogin - my ($no_of_failed_sshlogins,$min_failures) = $job->min_failed(); - # Only look at the Global::host that have > 0 jobslots - if($no_of_failed_sshlogins == - grep { $_->max_jobs_running() > 0 } values %Global::host - and $job->failed_here() == $min_failures) { - # It failed the same or more times on another host: - # run it on this host - } else { - # If it failed fewer times on another host: - # Find another job to run - my $nextjob; - if(not $Global::JobQueue->empty()) { - # This can potentially recurse for all args - no warnings 'recursion'; - $nextjob = get_job_with_sshlogin($sshlogin); - } - # Push the command back on the queue - $Global::JobQueue->unget($job); - return $nextjob; - } - } - return $job; -} - - -sub __REMOTE_SSH__() {} - - -sub read_sshloginfiles(@) { - # Read a list of --slf's - # Input: - # @files = files or symbolic file names to read - # Returns: N/A - for my $s (@_) { - read_sshloginfile(expand_slf_shorthand($s)); - } -} - -sub expand_slf_shorthand($) { - # Expand --slf shorthand into a read file name - # Input: - # $file = file or symbolic file name to read - # Returns: - # $file = actual file name to read - my $file = shift; - if($file eq "-") { - # skip: It is stdin - } elsif($file eq "..") { - $file = $Global::config_dir."/sshloginfile"; - } elsif($file eq ".") { - $file = "/etc/parallel/sshloginfile"; - } elsif(not -r $file) { - for(@Global::config_dirs) { - if(not -r $_."/".$file) { - # Try prepending $PARALLEL_HOME - ::error("Cannot open $file."); - ::wait_and_exit(255); - } else { - $file = $_."/".$file; - last; - } - } - } - return $file; -} - -sub read_sshloginfile($) { - # Read sshloginfile into @Global::sshlogin - # Input: - # $file = file to read - # Uses: - # @Global::sshlogin - # Returns: N/A - local $/ = "\n"; - my $file = shift; - my $close = 1; - my $in_fh; - ::debug("init","--slf ",$file); - if($file eq "-") { - $in_fh = *STDIN; - $close = 0; - } else { - if(not open($in_fh, "<", $file)) { - # Try the filename - ::error("Cannot open $file."); - ::wait_and_exit(255); - } - } - while(<$in_fh>) { - chomp; - /^\s*#/ and next; - /^\s*$/ and next; - push @Global::sshlogin, $_; - } - if($close) { - close $in_fh; - } -} - -sub parse_sshlogin() { - # Parse @Global::sshlogin into %Global::host. - # Keep only hosts that are in one of the given ssh hostgroups. - # Uses: - # @Global::sshlogin - # $Global::minimal_command_line_length - # %Global::host - # $opt::transfer - # @opt::return - # $opt::cleanup - # @opt::basefile - # @opt::trc - # Returns: N/A - my @login; - if(not @Global::sshlogin) { @Global::sshlogin = (":"); } - for my $sshlogin (@Global::sshlogin) { - # Split up -S sshlogin,sshlogin - # Parse ,, and \, as , but do not split on that - # -S "ssh -J jump1,,jump2 host1,host2" => - # ssh -J jump1,jump2 host1 - # host2 - # Protect \, and ,, as \0 - $sshlogin =~ s/\\,|,,/\0/g; - for my $s (split /,|\n/, $sshlogin) { - # Replace \0 => , - $s =~ s/\0/,/g; - if ($s eq ".." or $s eq "-") { - # This may add to @Global::sshlogin - possibly bug - read_sshloginfile(expand_slf_shorthand($s)); - } else { - $s =~ s/\s*$//; - push (@login, $s); - } - } - } - $Global::minimal_command_line_length = 100_000_000; - my @allowed_hostgroups; - for my $ncpu_sshlogin_string (::uniq(@login)) { - my $sshlogin = SSHLogin->new($ncpu_sshlogin_string); - my $sshlogin_string = $sshlogin->string(); - if($sshlogin_string eq "") { - # This is an ssh group: -S @webservers - push @allowed_hostgroups, $sshlogin->hostgroups(); - next; - } - if($Global::host{$sshlogin_string}) { - # This sshlogin has already been added: - # It is probably a host that has come back - # Set the max_jobs_running back to the original - debug("run","Already seen $sshlogin_string\n"); - if($sshlogin->{'ncpus'}) { - # If ncpus set by '#/' of the sshlogin, overwrite it: - $Global::host{$sshlogin_string}->set_ncpus($sshlogin->ncpus()); - } - $Global::host{$sshlogin_string}->set_max_jobs_running(undef); - next; - } - $sshlogin->set_maxlength(Limits::Command::max_length()); - - $Global::minimal_command_line_length = - ::min($Global::minimal_command_line_length, $sshlogin->maxlength()); - $Global::host{$sshlogin_string} = $sshlogin; - } - $Global::usable_command_line_length = - # Usable len = maxlen - 3000 for wrapping, div 2 for hexing - int(($Global::minimal_command_line_length - 3000)/2); - if($opt::max_chars) { - if($opt::max_chars <= $Global::usable_command_line_length) { - $Global::usable_command_line_length = $opt::max_chars; - } else { - ::warning("Value for option -s should be < ". - $Global::usable_command_line_length."."); - } - } - if(@allowed_hostgroups) { - # Remove hosts that are not in these groups - while (my ($string, $sshlogin) = each %Global::host) { - if(not $sshlogin->in_hostgroups(@allowed_hostgroups)) { - delete $Global::host{$string}; - } - } - } - - # debug("start", "sshlogin: ", my_dump(%Global::host),"\n"); - if(@Global::transfer_files or @opt::return - or $opt::cleanup or @opt::basefile) { - if(not remote_hosts()) { - # There are no remote hosts - if(@opt::trc) { - ::warning("--trc ignored as there are no remote --sshlogin."); - } elsif (defined $opt::transfer) { - ::warning("--transfer ignored as there are ". - "no remote --sshlogin."); - } elsif (@opt::transfer_files) { - ::warning("--transferfile ignored as there ". - "are no remote --sshlogin."); - } elsif (@opt::return) { - ::warning("--return ignored as there are no remote --sshlogin."); - } elsif (defined $opt::cleanup and not %opt::template) { - ::warning("--cleanup ignored as there ". - "are no remote --sshlogin."); - } elsif (@opt::basefile) { - ::warning("--basefile ignored as there ". - "are no remote --sshlogin."); - } - } - } -} - -sub remote_hosts() { - # Return sshlogins that are not ':' - # Uses: - # %Global::host - # Returns: - # list of sshlogins with ':' removed - return grep !/^:$/, keys %Global::host; -} - -sub setup_basefile() { - # Transfer basefiles to each $sshlogin - # This needs to be done before first jobs on $sshlogin is run - # Uses: - # %Global::host - # @opt::basefile - # Returns: N/A - my @cmd; - my $rsync_destdir; - my $workdir; - for my $sshlogin (values %Global::host) { - if($sshlogin->local()) { next } - for my $file (@opt::basefile) { - if($file !~ m:^/: and $opt::workdir eq "...") { - ::error("Work dir '...' will not work with relative basefiles."); - ::wait_and_exit(255); - } - if(not $workdir) { - my $dummycmdline = - CommandLine->new(1,["true"],{},0,0,[],[],[],[],{},{}); - my $dummyjob = Job->new($dummycmdline); - $workdir = $dummyjob->workdir(); - } - push @cmd, $sshlogin->rsync_transfer_cmd($file,$workdir); - } - } - debug("init", "basesetup: @cmd\n"); - my ($exitstatus,$stdout_ref,$stderr_ref) = - run_gnu_parallel((join "\n",@cmd),"-j0","--retries",5); - if($exitstatus) { - my @stdout = @$stdout_ref; - my @stderr = @$stderr_ref; - ::error("Copying of --basefile failed: @stdout@stderr"); - ::wait_and_exit(255); - } -} - -sub cleanup_basefile() { - # Remove the basefiles transferred - # Uses: - # %Global::host - # @opt::basefile - # Returns: N/A - my @cmd; - my $workdir; - if(not $workdir) { - my $dummycmdline = CommandLine->new(1,["true"],{},0,0,[],[],[],[],{},{}); - my $dummyjob = Job->new($dummycmdline); - $workdir = $dummyjob->workdir(); - } - for my $sshlogin (values %Global::host) { - if($sshlogin->local()) { next } - for my $file (@opt::basefile) { - push @cmd, $sshlogin->cleanup_cmd($file,$workdir); - } - } - debug("init", "basecleanup: @cmd\n"); - my ($exitstatus,$stdout_ref,$stderr_ref) = - run_gnu_parallel(join("\n",@cmd),"-j0","--retries",5); - if($exitstatus) { - my @stdout = @$stdout_ref; - my @stderr = @$stderr_ref; - ::error("Cleanup of --basefile failed: @stdout@stderr"); - ::wait_and_exit(255); - } -} - -sub run_gnu_parallel() { - my ($stdin,@args) = @_; - my $cmd = join "",map { " $_ & " } split /\n/, $stdin; - print $Global::original_stderr ` $cmd wait` ; - return 0 -} - -sub _run_gnu_parallel() { - # Run GNU Parallel - # This should ideally just fork an internal copy - # and not start it through a shell - # Input: - # $stdin = data to provide on stdin for GNU Parallel - # @args = command line arguments - # Returns: - # $exitstatus = exitcode of GNU Parallel run - # \@stdout = standard output - # \@stderr = standard error - my ($stdin,@args) = @_; - my ($exitstatus,@stdout,@stderr); - my ($stdin_fh,$stdout_fh)=(gensym(),gensym()); - my ($stderr_fh, $stderrname) = ::tmpfile(SUFFIX => ".par"); - unlink $stderrname; - - my $pid = ::open3($stdin_fh,$stdout_fh,$stderr_fh, - $0,qw(--plain --shell /bin/sh --will-cite), @args); - if(my $writerpid = fork()) { - close $stdin_fh; - @stdout = <$stdout_fh>; - # Now stdout is closed: - # These pids should be dead or die very soon - while(kill 0, $writerpid) { ::usleep(1); } - die; -# reap $writerpid; -# while(kill 0, $pid) { ::usleep(1); } -# reap $writerpid; - $exitstatus = $?; - seek $stderr_fh, 0, 0; - @stderr = <$stderr_fh>; - close $stdout_fh; - close $stderr_fh; - } else { - close $stdout_fh; - close $stderr_fh; - print $stdin_fh $stdin; - close $stdin_fh; - exit(0); - } - return ($exitstatus,\@stdout,\@stderr); -} - -sub filter_hosts() { - # Remove down --sshlogins from active duty. - # Find ncpus, ncores, maxlen, time-to-login for each host. - # Uses: - # %Global::host - # $Global::minimal_command_line_length - # $opt::use_sockets_instead_of_threads - # $opt::use_cores_instead_of_threads - # $opt::use_cpus_instead_of_cores - # Returns: N/A - - my ($nsockets_ref,$ncores_ref, $nthreads_ref, $time_to_login_ref, - $maxlen_ref, $echo_ref, $down_hosts_ref) = - parse_host_filtering(parallelized_host_filtering()); - - delete @Global::host{@$down_hosts_ref}; - @$down_hosts_ref and ::warning("Removed @$down_hosts_ref."); - - $Global::minimal_command_line_length = 100_000_000; - while (my ($string, $sshlogin) = each %Global::host) { - if($sshlogin->local()) { next } - my ($nsockets,$ncores,$nthreads,$time_to_login,$maxlen) = - ($nsockets_ref->{$string},$ncores_ref->{$string}, - $nthreads_ref->{$string},$time_to_login_ref->{$string}, - $maxlen_ref->{$string}); - defined $nsockets or ::die_bug("nsockets missing: $string"); - defined $ncores or ::die_bug("ncores missing: $string"); - defined $nthreads or ::die_bug("nthreads missing: $string"); - defined $time_to_login or ::die_bug("time_to_login missing: $string"); - defined $maxlen or ::die_bug("maxlen missing: $string"); - # ncpus may be set by 4/hostname or may be undefined yet - my $ncpus = $sshlogin->{'ncpus'}; - # $nthreads may be 0 if GNU Parallel is not installed remotely - $ncpus = $nthreads || $ncpus || $sshlogin->ncpus(); - if($opt::use_cpus_instead_of_cores) { - $ncpus = $ncores || $ncpus; - } elsif($opt::use_sockets_instead_of_threads) { - $ncpus = $nsockets || $ncpus; - } elsif($opt::use_cores_instead_of_threads) { - $ncpus = $ncores || $ncpus; - } - $sshlogin->set_ncpus($ncpus); - $sshlogin->set_time_to_login($time_to_login); - $maxlen = $maxlen || Limits::Command::max_length(); - $sshlogin->set_maxlength($maxlen); - ::debug("init", "Timing from -S:$string ", - " ncpus:", $ncpus, - " nsockets:",$nsockets, - " ncores:", $ncores, - " nthreads:",$nthreads, - " time_to_login:", $time_to_login, - " maxlen:", $maxlen, - " min_max_len:", $Global::minimal_command_line_length,"\n"); - } -} - -sub parse_host_filtering() { - # Input: - # @lines = output from parallelized_host_filtering() - # Returns: - # \%nsockets = number of sockets of {host} - # \%ncores = number of cores of {host} - # \%nthreads = number of hyperthreaded cores of {host} - # \%time_to_login = time_to_login on {host} - # \%maxlen = max command len on {host} - # \%echo = echo received from {host} - # \@down_hosts = list of hosts with no answer - local $/ = "\n"; - my (%nsockets, %ncores, %nthreads, %time_to_login, %maxlen, %echo, - @down_hosts); - for (@_) { - ::debug("init","Read: ",$_); - chomp; - my @col = split /\t/, $_; - if($col[0] =~ /^parallel: Warning:/) { - # Timed out job: Ignore it - next; - } elsif(defined $col[6]) { - # This is a line from --joblog - # seq host time spent sent received exit signal command - # 2 : 1372607672.654 0.675 0 0 0 0 eval true\ m\;ssh\ m\ parallel\ --number-of-cores - if($col[0] eq "Seq" and $col[1] eq "Host" and - $col[2] eq "Starttime") { - # Header => skip - next; - } - # Get server from: eval true server\; - $col[8] =~ /eval .?true.?\s([^\;]+);/ or - ::die_bug("col8 does not contain host: $col[8] in $_"); - my $host = $1; - $host =~ tr/\\//d; - $Global::host{$host} or next; - if($col[6] eq "255" or $col[6] eq "-1" or $col[6] eq "1") { - # exit == 255 or exit == timeout (-1): ssh failed/timedout - # exit == 1: lsh failed - # Remove sshlogin - ::debug("init", "--filtered $host\n"); - push(@down_hosts, $host); - } elsif($col[6] eq "127") { - # signal == 127: parallel not installed remote - # Set nsockets, ncores, nthreads = 1 - ::warning("Could not figure out ". - "number of cpus on $host. Using 1."); - $nsockets{$host} = 1; - $ncores{$host} = 1; - $nthreads{$host} = 1; - $maxlen{$host} = Limits::Command::max_length(); - } elsif($col[0] =~ /^\d+$/ and $Global::host{$host}) { - # Remember how log it took to log in - # 2 : 1372607672.654 0.675 0 0 0 0 eval true\ m\;ssh\ m\ echo - $time_to_login{$host} = ::min($time_to_login{$host},$col[3]); - } else { - ::die_bug("host check unmatched long jobline: $_"); - } - } elsif($Global::host{$col[0]}) { - # This output from --number-of-cores, --number-of-cpus, - # --max-line-length-allowed - # ncores: server 8 - # ncpus: server 2 - # maxlen: server 131071 - if(/parallel: Warning: Cannot figure out number of/) { - next; - } - if(/\t(perl: warning:|LANGUAGE =|LC_ALL =|LANG =|are supported and installed|Disconnected from|Received disconnect from)/ - or - /\tWarning: / - or - /\t(Host key fingerprint is|\+-.*-\+|\|.*\|)/ - or - /\t\S+: Undefined variable./ - ) { - # Skip these (from perl): - # perl: warning: Setting locale failed. - # perl: warning: Please check that your locale settings: - # LANGUAGE = (unset), - # LC_ALL = (unset), - # LANG = "en_US.UTF-8" - # are supported and installed on your system. - # perl: warning: Falling back to the standard locale ("C"). - # Disconnected from 127.0.0.1 port 22 - # - # Skip these (from ssh): - # Warning: Permanently added * to the list of known hosts. - # Warning: Identity file * not accessible: * - # (VisualHostKey=yes) - # Host key fingerprint is SHA256:... - # +--[ED25519 256]--+ - # | o | - # +----[SHA256]-----+ - # - # Skip these (from csh): - # MANPATH: Undefined variable. - } elsif(not defined $nsockets{$col[0]}) { - $nsockets{$col[0]} = $col[1]; - } elsif(not defined $ncores{$col[0]}) { - $ncores{$col[0]} = $col[1]; - } elsif(not defined $nthreads{$col[0]}) { - $nthreads{$col[0]} = $col[1]; - } elsif(not defined $maxlen{$col[0]}) { - $maxlen{$col[0]} = $col[1]; - } elsif(not defined $echo{$col[0]}) { - $echo{$col[0]} = $col[1]; - } else { - ::die_bug("host check too many col0: $_"); - } - } else { - ::die_bug("host check unmatched short jobline ($col[0]): $_"); - } - } - @down_hosts = uniq(@down_hosts); - return(\%nsockets, \%ncores, \%nthreads, \%time_to_login, - \%maxlen, \%echo, \@down_hosts); -} - -sub parallelized_host_filtering() { - # Uses: - # %Global::host - # Returns: - # text entries with: - # * joblog line - # * hostname \t number of cores - # * hostname \t number of cpus - # * hostname \t max-line-length-allowed - # * hostname \t empty - - sub sshwrapped { - # Wrap with ssh and --env - # Return $default_value if command fails - my $sshlogin = shift; - my $command = shift; - # wrapper that returns output "0\n" if the command fails - # E.g. parallel not installed => "0\n" - my $wcmd = q(perl -e '$a=`).$command.q(`; print $? ? "0".v010 : $a'); - my $commandline = CommandLine->new(1,[$wcmd],{},0,0,[],[],[],[],{},{}); - my $job = Job->new($commandline); - $job->set_sshlogin($sshlogin); - $job->wrapped(); - return($job->{'wrapped'}); - } - - my(@sockets, @cores, @threads, @maxline, @echo); - while (my ($host, $sshlogin) = each %Global::host) { - if($host eq ":") { next } - # The 'true' is used to get the $host out later - push(@sockets, $host."\t"."true $host; ". - sshwrapped($sshlogin,"parallel --number-of-sockets")."\n\0"); - push(@cores, $host."\t"."true $host; ". - sshwrapped($sshlogin,"parallel --number-of-cores")."\n\0"); - push(@threads, $host."\t"."true $host; ". - sshwrapped($sshlogin,"parallel --number-of-threads")."\n\0"); - push(@maxline, $host."\t"."true $host; ". - sshwrapped($sshlogin, - "parallel --max-line-length-allowed")."\n\0"); - # 'echo' is used to get the fastest possible ssh login time - push(@echo, $host."\t"."true $host; ". - $sshlogin->wrap("echo $host")."\n\0"); - } - # --timeout 10: Setting up an SSH connection and running a simple - # command should never take > 10 sec. - # --delay 0.1: If multiple sshlogins use the same proxy the delay - # will make it less likely to overload the ssh daemon. - # --retries 3: If the ssh daemon is overloaded, try 3 times - my $cmd = - "$0 -j0 --timeout 10 --joblog - --plain --delay 0.1 --retries 3 ". - "--tag --tagstring '{1}' -0 --colsep '\t' -k eval '{2}' && true "; - $cmd = $Global::shell." -c ".Q($cmd); - ::debug("init", $cmd, "\n"); - my @out; - my $prepend = ""; - - my ($host_fh,$in,$err); - open3($in, $host_fh, $err, $cmd) || ::die_bug("parallel host check: $cmd"); - ::debug("init", map { $_,"\n" } @sockets, @cores, @threads, @maxline, @echo); - - if(not fork()) { - # Give the commands to run to the $cmd - close $host_fh; - print $in @sockets, @cores, @threads, @maxline, @echo; - close $in; - exit(); - } - close $in; - # If -0: $/ must be \n - local $/ = "\n"; - for(<$host_fh>) { - # TODO incompatible with '-quoting. Needs to be fixed differently - #if(/\'$/) { - # # if last char = ' then append next line - # # This may be due to quoting of \n in environment var - # $prepend .= $_; - # next; - #} - $_ = $prepend . $_; - $prepend = ""; - push @out, $_; - } - close $host_fh; - return @out; -} - -sub onall($@) { - # Runs @command on all hosts. - # Uses parallel to run @command on each host. - # --jobs = number of hosts to run on simultaneously. - # For each host a parallel command with the args will be running. - # Uses: - # $Global::debug - # $Global::exitstatus - # $Global::joblog - # $Global::quoting - # $opt::D - # $opt::arg_file_sep - # $opt::arg_sep - # $opt::colsep - # $opt::files - # $opt::files0 - # $opt::group - # $opt::joblog - # $opt::jobs - # $opt::keeporder - # $opt::linebuffer - # $opt::max_chars - # $opt::plain - # $opt::retries - # $opt::tag - # $opt::tee - # $opt::timeout - # $opt::ungroup - # %Global::host - # @opt::basefile - # @opt::env - # @opt::v - # Input: - # @command = command to run on all hosts - # Returns: N/A - sub tmp_joblog { - # Input: - # $joblog = filename of joblog - undef if none - # Returns: - # $tmpfile = temp file for joblog - undef if none - my $joblog = shift; - if(not defined $joblog) { - return undef; - } - my ($fh, $tmpfile) = ::tmpfile(SUFFIX => ".log"); - close $fh; - return $tmpfile; - } - my ($input_source_fh_ref,@command) = @_; - if($Global::quoting) { - @command = shell_quote(@command); - } - - # Copy all @input_source_fh (-a and :::) into tempfiles - my @argfiles = (); - for my $fh (@$input_source_fh_ref) { - my ($outfh, $name) = ::tmpfile(SUFFIX => ".all", UNLINK => not $opt::D); - print $outfh (<$fh>); - close $outfh; - push @argfiles, $name; - } - if(@opt::basefile) { setup_basefile(); } - # for each sshlogin do: - # parallel -S $sshlogin $command :::: @argfiles - # - # Pass some of the options to the sub-parallels, not all of them as - # -P should only go to the first, and -S should not be copied at all. - my $options = - join(" ", - ((defined $opt::sshdelay) ? "--delay ".$opt::sshdelay : ""), - ((defined $opt::memfree) ? "--memfree ".$opt::memfree : ""), - ((defined $opt::memsuspend) ? "--memfree ".$opt::memsuspend : ""), - ((defined $opt::D) ? "-D $opt::D" : ""), - ((defined $opt::group) ? "--group" : ""), - ((defined $opt::jobs) ? "-P $opt::jobs" : ""), - ((defined $opt::keeporder) ? "--keeporder" : ""), - ((defined $opt::linebuffer) ? "--linebuffer" : ""), - ((defined $opt::max_chars) ? "--max-chars ".$opt::max_chars : ""), - ((defined $opt::plain) ? "--plain" : ""), - (($opt::ungroup == 1) ? "-u" : ""), - ((defined $opt::tee) ? "--tee" : ""), - ); - my $suboptions = - join(" ", - ((defined $opt::sshdelay) ? "--delay ".$opt::sshdelay : ""), - ((defined $opt::D) ? "-D $opt::D" : ""), - ((defined $opt::arg_file_sep) ? "--arg-file-sep ".$opt::arg_file_sep : ""), - ((defined $opt::arg_sep) ? "--arg-sep ".$opt::arg_sep : ""), - ((defined $opt::colsep) ? "--colsep ".shell_quote($opt::colsep) : ""), - ((defined $opt::files) ? "--files" : ""), - ((defined $opt::files0) ? "--files0" : ""), - ((defined $opt::group) ? "--group" : ""), - ((defined $opt::cleanup) ? "--cleanup" : ""), - ((defined $opt::keeporder) ? "--keeporder" : ""), - ((defined $opt::linebuffer) ? "--linebuffer" : ""), - ((defined $opt::max_chars) ? "--max-chars ".$opt::max_chars : ""), - ((defined $opt::plain) ? "--plain" : ""), - ((defined $opt::plus) ? "--plus" : ""), - ((defined $opt::retries) ? "--retries ".$opt::retries : ""), - ((defined $opt::timeout) ? "--timeout ".$opt::timeout : ""), - (($opt::ungroup == 1) ? "-u" : ""), - ((defined $opt::ssh) ? "--ssh '".$opt::ssh."'" : ""), - ((defined $opt::tee) ? "--tee" : ""), - ((defined $opt::workdir) ? "--wd ".Q($opt::workdir) : ""), - (@Global::transfer_files ? map { "--tf ".Q($_) } - @Global::transfer_files : ""), - (@Global::ret_files ? map { "--return ".Q($_) } - @Global::ret_files : ""), - (@opt::env ? map { "--env ".Q($_) } @opt::env : ""), - (map { "-v" } @opt::v), - ); - ::debug("init", "| $0 $options\n"); - open(my $parallel_fh, "|-", "$0 -0 --will-cite -j0 $options") || - ::die_bug("This does not run GNU Parallel: $0 $options"); - my @joblogs; - for my $host (sort keys %Global::host) { - my $sshlogin = $Global::host{$host}; - my $qsshlogin = Q($sshlogin->string()); - my $joblog = tmp_joblog($opt::joblog); - if($joblog) { - push @joblogs, $joblog; - $joblog = "--joblog ".::Q($joblog); - } - my $quad = $opt::arg_file_sep || "::::"; - # If PARALLEL_ENV is set: Pass it on - my $penv=$Global::parallel_env ? - "PARALLEL_ENV=".Q($Global::parallel_env) : - ''; - my $results; - if(defined $opt::results) { - $results = Q($opt::results) . $qsshlogin; - } - ::debug("init", "$penv $0 $suboptions -j1 $joblog ", - ((defined $opt::tag) ? "--tagstring ".$qsshlogin : ""), - ((defined $opt::results) ? "--results ".$results : ""), - " -S $qsshlogin ", - join(" ",shell_quote(@command,$quad,@argfiles)),"\n"); - print $parallel_fh "$penv $0 $suboptions -j1 $joblog ", - ((defined $opt::tag) ? "--tagstring ".$qsshlogin : ""), - ((defined $opt::results) ? "--results ".$results : ""), - " -S $qsshlogin ", - join(" ",shell_quote(@command,$quad,@argfiles)),"\0"; - } - close $parallel_fh; - $Global::exitstatus = $? >> 8; - debug("init", "--onall exitvalue ", $?); - if(@opt::basefile and $opt::cleanup) { cleanup_basefile(); } - $Global::debug or unlink(@argfiles); - my %seen; - for my $joblog (@joblogs) { - # Append to $joblog - open(my $fh, "<", $joblog) || - ::die_bug("Cannot open tmp joblog $joblog"); - # Skip first line (header); - <$fh>; - print $Global::joblog (<$fh>); - close $fh; - unlink($joblog); - } -} - - -sub __SIGNAL_HANDLING__() {} - - -sub sigtstp() { - # Send TSTP signal (Ctrl-Z) to all children process groups - # Uses: - # %SIG - # Returns: N/A - signal_children("TSTP"); -} - -sub sigpipe() { - # Send SIGPIPE signal to all children process groups - # Uses: - # %SIG - # Returns: N/A - signal_children("PIPE"); -} - -sub signal_children() { - # Send signal to all children process groups - # and GNU Parallel itself - # Uses: - # %SIG - # Returns: N/A - my $signal = shift; - debug("run", "Sending $signal "); - kill $signal, map { -$_ } keys %Global::running; - # Use default signal handler for GNU Parallel itself - $SIG{$signal} = undef; - kill $signal, $$; -} - -sub save_original_signal_handler() { - # Remember the original signal handler - # Uses: - # %Global::original_sig - # Returns: N/A - $SIG{INT} = sub { - if($opt::tmux) { ::qqx("tmux kill-session -t p$$"); } - wait_and_exit(255); - }; - $SIG{TERM} = sub { - if($opt::tmux) { ::qqx("tmux kill-session -t p$$"); } - wait_and_exit(255); - }; - %Global::original_sig = %SIG; - $SIG{TERM} = sub {}; # Dummy until jobs really start - $SIG{ALRM} = 'IGNORE'; - # Allow Ctrl-Z to suspend and `fg` to continue - $SIG{TSTP} = \&sigtstp; - $SIG{PIPE} = \&sigpipe; - $SIG{CONT} = sub { - # Set $SIG{TSTP} again (it is undef'ed in sigtstp() ) - $SIG{TSTP} = \&sigtstp; - for my $job (values %Global::running) { - if($job->suspended()) { - # Force jobs to suspend, if they are marked as suspended. - # --memsupspend can suspend a job that will be resumed - # if the user presses CTRL-Z followed by `fg`. - $job->suspend(); - } else { - # Resume the rest of the jobs - $job->resume(); - } - } - }; -} - -sub list_running_jobs() { - # Print running jobs on tty - # Uses: - # %Global::running - # Returns: N/A - for my $job (values %Global::running) { - ::status("$Global::progname: ".$job->replaced()); - } -} - -sub start_no_new_jobs() { - # Start no more jobs - # Uses: - # %Global::original_sig - # %Global::unlink - # $Global::start_no_new_jobs - # Returns: N/A - unlink keys %Global::unlink; - ::status - ("$Global::progname: SIGHUP received. No new jobs will be started.", - "$Global::progname: Waiting for these ".(keys %Global::running). - " jobs to finish. Send SIGTERM to stop now."); - list_running_jobs(); - $Global::start_no_new_jobs ||= 1; -} - -sub reapers() { - # Run reaper until there are no more left - # Returns: - # @pids_reaped = pids of reaped processes - my @pids_reaped; - my $pid; - while($pid = reaper()) { - push @pids_reaped, $pid; - } - return @pids_reaped; -} - -sub reaper() { - # A job finished: - # * Set exitstatus, exitsignal, endtime. - # * Free ressources for new job - # * Update median runtime - # * Print output - # * If --halt = now: Kill children - # * Print progress - # Uses: - # %Global::running - # $opt::timeout - # $Global::timeoutq - # $opt::keeporder - # $Global::total_running - # Returns: - # $stiff = PID of child finished - my $stiff; - debug("run", "Reaper "); - if(($stiff = waitpid(-1, &WNOHANG)) <= 0) { - # No jobs waiting to be reaped - return 0; - } - - # $stiff = pid of dead process - my $job = $Global::running{$stiff}; - - # '-a <(seq 10)' will give us a pid not in %Global::running - # The same will one of the ssh -M: ignore - $job or return 0; - delete $Global::running{$stiff}; - $Global::total_running--; - if($job->{'commandline'}{'skip'}) { - # $job->skip() was called - $job->set_exitstatus(-2); - $job->set_exitsignal(0); - } else { - $job->set_exitsignal($? & 127); - if($job->exitstatus()) { - # Exit status already set - probably by --timeout - } elsif($? & 127) { - # Killed by signal. Many shells return: 128 | $signal - $job->set_exitstatus(128 | $?); - } else { - # Normal exit - $job->set_exitstatus($? >> 8); - } - } - - debug("run", "\nseq ",$job->seq()," died (", $job->exitstatus(), ")"); - if($Global::delayauto or $Global::sshdelayauto) { - if($job->exitstatus()) { - # Job failed: Increase delay (if $opt::(ssh)delay set) - $opt::delay &&= $opt::delay * 1.3; - $opt::sshdelay &&= $opt::sshdelay * 1.3; - } else { - # Job succeeded: Decrease delay (if $opt::(ssh)delay set) - $opt::delay &&= $opt::delay * 0.9; - $opt::sshdelay &&= $opt::sshdelay * 0.9; - } - debug("run", "delay:$opt::delay ssh:$opt::sshdelay "); - } - $job->set_endtime(::now()); - my $sshlogin = $job->sshlogin(); - $sshlogin->dec_jobs_running(); - if($job->should_be_retried()) { - # Free up file handles - $job->free_ressources(); - } else { - # The job is done - $sshlogin->inc_jobs_completed(); - # Free the jobslot - $job->free_slot(); - if($opt::timeout and not $job->exitstatus()) { - # Update average runtime for timeout only for successful jobs - $Global::timeoutq->update_median_runtime($job->runtime()); - } - if($opt::keeporder and not $opt::latestline) { - # --latestline fixes --keeporder in Job::row() - $job->print_earlier_jobs(); - } else { - $job->print(); - } - if($job->should_we_halt() eq "now") { - # Kill children - ::kill_sleep_seq($job->pid()); - ::killall(); - ::wait_and_exit($Global::halt_exitstatus); - } - } - $job->cleanup(); - - if($opt::progress) { - my %progress = progress(); - ::status_no_nl("\r",$progress{'status'}); - } - - debug("run", "jobdone \n"); - return $stiff; -} - - -sub __USAGE__() {} - - -sub killall() { - # Kill all jobs by killing their process groups - # Uses: - # $Global::start_no_new_jobs = we are stopping - # $Global::killall = Flag to not run reaper - $Global::start_no_new_jobs ||= 1; - # Do not reap killed children: Ignore them instead - $Global::killall ||= 1; - kill_sleep_seq(keys %Global::running); -} - -sub kill_sleep_seq(@) { - # Send jobs TERM,TERM,KILL to processgroups - # Input: - # @pids = list of pids that are also processgroups - # Convert pids to process groups ($processgroup = -$pid) - my @pgrps = map { -$_ } @_; - my @term_seq = split/,/,$opt::termseq; - if(not @term_seq) { - @term_seq = ("TERM",200,"TERM",100,"TERM",50,"KILL",25); - } - # for each signal+waittime: kill process groups still not dead - while(@term_seq) { - @pgrps = kill_sleep(shift @term_seq, shift @term_seq, @pgrps); - } -} - -sub kill_sleep() { - # Kill pids with a signal and wait a while for them to die - # Input: - # $signal = signal to send to @pids - # $sleep_max = number of ms to sleep at most before returning - # @pids = pids to kill (actually process groups) - # Uses: - # $Global::killall = set by killall() to avoid calling reaper - # Returns: - # @pids = pids still alive - my ($signal, $sleep_max, @pids) = @_; - ::debug("kill","kill_sleep $signal ",(join " ",sort @pids),"\n"); - kill $signal, @pids; - my $sleepsum = 0; - my $sleep = 0.001; - - while(@pids and $sleepsum < $sleep_max) { - if($Global::killall) { - # Killall => don't run reaper - while(waitpid(-1, &WNOHANG) > 0) { - $sleep = $sleep/2+0.001; - } - } elsif(reapers()) { - $sleep = $sleep/2+0.001; - } - $sleep *= 1.1; - ::usleep($sleep); - $sleepsum += $sleep; - # Keep only living children - @pids = grep { kill(0, $_) } @pids; - } - return @pids; -} - -sub wait_and_exit($) { - # If we do not wait, we sometimes get segfault - # Returns: N/A - my $error = shift; - unlink keys %Global::unlink; - if($error) { - # Kill all jobs without printing - killall(); - } - for (keys %Global::unkilled_children) { - # Kill any (non-jobs) children (e.g. reserved processes) - kill 9, $_; - waitpid($_,0); - delete $Global::unkilled_children{$_}; - } - if($Global::unkilled_sqlworker) { - waitpid($Global::unkilled_sqlworker,0); - } - # Avoid: Warning: unable to close filehandle properly: No space - # left on device during global destruction. - $SIG{__WARN__} = sub {}; - if($opt::_parset) { - # Make the shell script return $error - print "$Global::parset_endstring\nreturn $error"; - } - exit($error); -} - -sub die_usage() { - # Returns: N/A - usage(); - wait_and_exit(255); -} - -sub usage() { - # Returns: N/A - print join - ("\n", - "Usage:", - "", - "$Global::progname [options] [command [arguments]] < list_of_arguments", - "$Global::progname [options] [command [arguments]] (::: arguments|:::: argfile(s))...", - "cat ... | $Global::progname --pipe [options] [command [arguments]]", - "", - "-j n Run n jobs in parallel", - "-k Keep same order", - "-X Multiple arguments with context replace", - "--colsep regexp Split input on regexp for positional replacements", - "{} {.} {/} {/.} {#} {%} {= perl code =} Replacement strings", - "{3} {3.} {3/} {3/.} {=3 perl code =} Positional replacement strings", - "With --plus: {} = {+/}/{/} = {.}.{+.} = {+/}/{/.}.{+.} = {..}.{+..} =", - " {+/}/{/..}.{+..} = {...}.{+...} = {+/}/{/...}.{+...}", - "", - "-S sshlogin Example: foo\@server.example.com", - "--slf .. Use ~/.parallel/sshloginfile as the list of sshlogins", - "--trc {}.bar Shorthand for --transfer --return {}.bar --cleanup", - "--onall Run the given command with argument on all sshlogins", - "--nonall Run the given command with no arguments on all sshlogins", - "", - "--pipe Split stdin (standard input) to multiple jobs.", - "--recend str Record end separator for --pipe.", - "--recstart str Record start separator for --pipe.", - "", - "GNU Parallel can do much more. See 'man $Global::progname' for details", - "", - "Academic tradition requires you to cite works you base your article on.", - "If you use programs that use GNU Parallel to process data for an article in a", - "scientific publication, please cite:", - "", - " Tange, O. (2023, November 22). GNU Parallel 20231122 ('Grindavík').", - " Zenodo. https://doi.org/10.5281/zenodo.10199085", - "", - # Before changing these lines, please read - # https://www.gnu.org/software/parallel/parallel_design.html#citation-notice - # https://git.savannah.gnu.org/cgit/parallel.git/tree/doc/citation-notice-faq.txt - # You accept to be put in a public hall of shame by removing - # these lines -# "This helps funding further development; AND IT WON'T COST YOU A CENT.", -# "If you pay 10000 EUR you should feel free to use GNU Parallel without citing.", -# "", - "",); -} - -sub citation_notice() { - # if --will-cite or --plain: do nothing - # if stderr redirected: do nothing - # if $PARALLEL_HOME/will-cite: do nothing - # else: print citation notice to stderr - if($opt::willcite - or - $opt::plain - or - not -t $Global::original_stderr - or - grep { -e "$_/will-cite" } @Global::config_dirs) { - # skip - } else { - ::status - ("Academic tradition requires you to cite works you base your article on.", - "If you use programs that use GNU Parallel to process data for an article in a", - "scientific publication, please cite:", - "", - " Tange, O. (2023, November 22). GNU Parallel 20231122 ('Grindavík').", - " Zenodo. https://doi.org/10.5281/zenodo.10199085", - "", - # Before changing these line, please read - # https://www.gnu.org/software/parallel/parallel_design.html#citation-notice and - # https://git.savannah.gnu.org/cgit/parallel.git/tree/doc/citation-notice-faq.txt - # You accept to be put in a public hall of shame by - # removing these lines - "This helps funding further development; AND IT WON'T COST YOU A CENT.", - "If you pay 10000 EUR you should feel free to use GNU Parallel without citing.", - "", - "More about funding GNU Parallel and the citation notice:", - "https://www.gnu.org/software/parallel/parallel_design.html#citation-notice", - "", - "To silence this citation notice: run 'parallel --citation' once.", - "" - ); - mkdir $Global::config_dir; - # Number of times the user has run GNU Parallel without showing - # willingness to cite - my $runs = 0; - if(open (my $fh, "<", $Global::config_dir. - "/runs-without-willing-to-cite")) { - $runs = <$fh>; - close $fh; - } - $runs++; - if(open (my $fh, ">", $Global::config_dir. - "/runs-without-willing-to-cite")) { - print $fh $runs; - close $fh; - if($runs >= 10) { - ::status("Come on: You have run parallel $runs times. ". - "Isn't it about time ", - "you run 'parallel --citation' once to silence ". - "the citation notice?", - ""); - } - } - } -} - -sub status(@) { - my @w = @_; - my $fh = $Global::status_fd || *STDERR; - print $fh map { ($_, "\n") } @w; - flush $fh; -} - -sub status_no_nl(@) { - my @w = @_; - my $fh = $Global::status_fd || *STDERR; - print $fh @w; - flush $fh; -} - -sub warning(@) { - my @w = @_; - my $prog = $Global::progname || "parallel"; - status_no_nl(map { ($prog, ": Warning: ", $_, "\n"); } @w); -} - -{ - my %warnings; - sub warning_once(@) { - my @w = @_; - my $prog = $Global::progname || "parallel"; - $warnings{@w}++ or - status_no_nl(map { ($prog, ": Warning: ", $_, "\n"); } @w); - } -} - -sub error(@) { - my @w = @_; - my $prog = $Global::progname || "parallel"; - status(map { ($prog.": Error: ". $_); } @w); -} - -sub die_bug($) { - my $bugid = shift; - print STDERR - ("$Global::progname: This should not happen. You have found a bug. ", - "Please follow\n", - "https://www.gnu.org/software/parallel/man.html#reporting-bugs\n", - "\n", - "Include this in the report:\n", - "* The version number: $Global::version\n", - "* The bugid: $bugid\n", - "* The command line being run\n", - "* The files being read (put the files on a webserver if they are big)\n", - "\n", - "If you get the error on smaller/fewer files, please include those instead.\n"); - ::wait_and_exit(255); -} - -sub version() { - # Returns: N/A - print join - ("\n", - "GNU $Global::progname $Global::version", - "Copyright (C) 2007-2023 Ole Tange, http://ole.tange.dk and Free Software", - "Foundation, Inc.", - "License GPLv3+: GNU GPL version 3 or later ", - "This is free software: you are free to change and redistribute it.", - "GNU $Global::progname comes with no warranty.", - "", - "Web site: https://www.gnu.org/software/${Global::progname}\n", - "When using programs that use GNU Parallel to process data for publication", - "please cite as described in the manpage.\n", - ); -} - -sub citation() { - # Returns: N/A - my ($all_argv_ref,$argv_options_removed_ref) = @_; - my $all_argv = "@$all_argv_ref"; - my $no_opts = "@$argv_options_removed_ref"; - $all_argv=~s/--citation//; - if($all_argv ne $no_opts) { - ::warning("--citation ignores all other options and arguments."); - ::status(""); - } - - ::status( - "Academic tradition requires you to cite works you base your article on.", - "If you use programs that use GNU Parallel to process data for an article in a", - "scientific publication, please cite:", - "", - "\@software{tange_2023_10199085,", - " author = {Tange, Ole},", - " title = {GNU Parallel 20231122 ('Grindavík')},", - " month = Nov,", - " year = 2023,", - " note = {{GNU Parallel is a general parallelizer to run", - " multiple serial command line programs in parallel", - " without changing them.}},", - " publisher = {Zenodo},", - " doi = {10.5281/zenodo.10199085},", - " url = {https://doi.org/10.5281/zenodo.10199085}", - "}", - "", - "(Feel free to use \\nocite{tange_2023_10199085})", - "", - # Before changing these lines, please read - # https://www.gnu.org/software/parallel/parallel_design.html#citation-notice and - # https://git.savannah.gnu.org/cgit/parallel.git/tree/doc/citation-notice-faq.txt - # You accept to be put in a public hall of shame by removing - # these lines - "This helps funding further development; AND IT WON'T COST YOU A CENT.", - "If you pay 10000 EUR you should feel free to use GNU Parallel without citing.", - "", - "More about funding GNU Parallel and the citation notice:", - "https://lists.gnu.org/archive/html/parallel/2013-11/msg00006.html", - "https://www.gnu.org/software/parallel/parallel_design.html#citation-notice", - "https://git.savannah.gnu.org/cgit/parallel.git/tree/doc/citation-notice-faq.txt", - "" - ); - while(not grep { -e "$_/will-cite" } @Global::config_dirs) { - print "\nType: 'will cite' and press enter.\n> "; - my $input = ; - if(not defined $input) { - exit(255); - } - if($input =~ /will cite/i) { - if(mkdir $Global::config_dir) { - # Recompute @Global::config_dirs so we can break out of the loop. - init_globals(); - } - if(open (my $fh, ">", $Global::config_dir."/will-cite")) { - close $fh; - ::status( - "", - "Thank you for your support: You are the reason why there is funding to", - "continue maintaining GNU Parallel. On behalf of future versions of", - "GNU Parallel, which would not exist without your support:", - "", - " THANK YOU SO MUCH", - "", - "It is really appreciated. The citation notice is now silenced.", - ""); - } else { - ::status( - "", - "Thank you for your support. It is much appreciated. The citation", - "cannot permanently be silenced. Use '--will-cite' instead.", - "", - "If you use '--will-cite' in scripts to be run by others you are making", - "it harder for others to see the citation notice. The development of", - "GNU Parallel is indirectly financed through citations, so if users", - "do not know they should cite then you are making it harder to finance", - "development. However, if you pay 10000 EUR, you should feel free to", - "use '--will-cite' in scripts.", - ""); - last; - } - } - } -} - -sub show_limits() { - # Returns: N/A - print("Maximal size of command: ",Limits::Command::real_max_length(),"\n", - "Maximal usable size of command: ", - $Global::usable_command_line_length,"\n", - "\n", - "Execution will continue now, ", - "and it will try to read its input\n", - "and run commands; if this is not ", - "what you wanted to happen, please\n", - "press CTRL-D or CTRL-C\n"); -} - -sub embed() { - # Give an embeddable version of GNU Parallel - # Tested with: bash, zsh, ksh, ash, dash, sh - my $randomstring = "cut-here-".join"", - map { (0..9,"a".."z","A".."Z")[rand(62)] } (1..20); - if(not -f $0 or not -r $0) { - ::error("--embed only works if parallel is a readable file"); - exit(255); - } - if(open(my $fh, "<", $0)) { - # Read the source from $0 - my @source = <$fh>; - my $user = $ENV{LOGNAME} || $ENV{USERNAME} || $ENV{USER}; - my @env_parallel_source = (); - my $shell = $Global::shell; - $shell =~ s:.*/::; - for(which("env_parallel.$shell")) { - -r $_ or next; - # Read the source of env_parallel.shellname - open(my $env_parallel_source_fh, $_) || die; - @env_parallel_source = <$env_parallel_source_fh>; - close $env_parallel_source_fh; - last; - } - print "#!$Global::shell - -# Copyright (C) 2007-2023 $user, Ole Tange, http://ole.tange.dk -# and Free Software Foundation, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, see -# or write to the Free Software Foundation, Inc., 51 Franklin St, -# Fifth Floor, Boston, MA 02110-1301 USA -"; - - print q! -# Embedded GNU Parallel created with --embed -parallel() { - # Start GNU Parallel without leaving temporary files - # - # Not all shells support 'perl <(cat ...)' - # This is a complex way of doing: - # perl <(cat <<'cut-here' - # [...] - # ) "$@" - # and also avoiding: - # [1]+ Done cat - - # Make a temporary fifo that perl can read from - _fifo_with_GNU_Parallel_source=`perl -e 'use POSIX qw(mkfifo); - do { - $f = "/tmp/parallel-".join"", - map { (0..9,"a".."z","A".."Z")[rand(62)] } (1..5); - } while(-e $f); - mkfifo($f,0600); - print $f;'` - # Put source code into temporary file - # so it is easy to copy to the fifo - _file_with_GNU_Parallel_source=`mktemp`; -!, - "cat <<'$randomstring' > \$_file_with_GNU_Parallel_source\n", - @source, - $randomstring,"\n", - q! - # Copy the source code from the file to the fifo - # and remove the file and fifo ASAP - # 'sh -c' is needed to avoid - # [1]+ Done cat - sh -c "(rm $_file_with_GNU_Parallel_source; cat >$_fifo_with_GNU_Parallel_source; rm $_fifo_with_GNU_Parallel_source) < $_file_with_GNU_Parallel_source &" - - # Read the source from the fifo - perl $_fifo_with_GNU_Parallel_source "$@" -} -!, - @env_parallel_source, - q! - -# This will call the functions above -parallel -k echo ::: Put your code here -env_parallel --session -env_parallel -k echo ::: Put your code here -parset p,y,c,h -k echo ::: Put your code here -echo $p $y $c $h -echo You can also activate GNU Parallel for interactive use by: -echo . "$0" -!; - } else { - ::error("Cannot open $0"); - exit(255); - } - ::status("Redirect the output to a file and add your changes at the end:", - " $0 --embed > new_script"); -} - - -sub __GENERIC_COMMON_FUNCTION__() {} - - -sub mkdir_or_die($) { - # If dir is not executable: die - my $dir = shift; - # The eval is needed to catch exception from mkdir - eval { File::Path::mkpath($dir); }; - if(not -x $dir) { - ::error("Cannot change into non-executable dir $dir: $!"); - ::wait_and_exit(255); - } -} - -sub tmpfile(@) { - # Create tempfile as $TMPDIR/parXXXXX - # Returns: - # $filehandle = opened file handle - # $filename = file name created - my($filehandle,$filename) = - ::tempfile(DIR=>$ENV{'TMPDIR'}, TEMPLATE => 'parXXXXX', @_); - if(wantarray) { - return($filehandle,$filename); - } else { - # Separate unlink due to NFS dealing badly with File::Temp - unlink $filename; - return $filehandle; - } -} - -sub tmpname($) { - # Select a name that does not exist - # Do not create the file as it may be used for creating a socket (by tmux) - # Remember the name in $Global::unlink to avoid hitting the same name twice - my $name = shift; - my($tmpname); - if(not -w $ENV{'TMPDIR'}) { - my $qtmp = ::Q($ENV{'TMPDIR'}); - if(not -e $ENV{'TMPDIR'}) { - ::error("Tmpdir $qtmp does not exist.","Try: mkdir -p $qtmp"); - } else { - ::error("Tmpdir $qtmp is not writable.","Try: chmod +w $qtmp"); - } - ::wait_and_exit(255); - } - do { - $tmpname = $ENV{'TMPDIR'}."/".$name. - join"", map { (0..9,"a".."z","A".."Z")[rand(62)] } (1..5); - } while(-e $tmpname or $Global::unlink{$tmpname}++); - return $tmpname; -} - -sub tmpfifo() { - # Find an unused name and mkfifo on it - my $tmpfifo = tmpname("fif"); - mkfifo($tmpfifo,0600); - return $tmpfifo; -} - -sub rm(@) { - # Remove file and remove it from %Global::unlink - # Uses: - # %Global::unlink - delete @Global::unlink{@_}; - unlink @_; -} - -sub size_of_block_dev() { - # Like -s but for block devices - # Input: - # $blockdev = file name of block device - # Returns: - # $size = in bytes, undef if error - my $blockdev = shift; - if(open(my $fh, "<", $blockdev)) { - seek($fh,0,2) || ::die_bug("cannot seek $blockdev"); - my $size = tell($fh); - close $fh; - return $size; - } else { - ::error("cannot open $blockdev"); - wait_and_exit(255); - } -} - -sub qqx(@) { - # Like qx but with clean environment (except for @keep) - # and STDERR ignored - # This is needed if the environment contains functions - # that /bin/sh does not understand - my %env; - # ssh with ssh-agent needs PATH SSH_AUTH_SOCK SSH_AGENT_PID - # ssh with Kerberos needs KRB5CCNAME - # sshpass needs SSHPASS - # tmux needs LC_CTYPE - # lsh needs HOME LOGNAME - my @keep = qw(PATH SSH_AUTH_SOCK SSH_AGENT_PID KRB5CCNAME LC_CTYPE - HOME LOGNAME SSHPASS); - @env{@keep} = @ENV{@keep}; - local %ENV; - %ENV = %env; - if($Global::debug) { - # && true is to force spawning a shell and not just exec'ing - return qx{ @_ && true }; - } else { - # CygWin does not respect 2>/dev/null - # so we do that by hand - # This trick does not work: - # https://stackoverflow.com/q/13833088/363028 - # local *STDERR; - # open(STDERR, ">", "/dev/null"); - open(local *CHILD_STDIN, '<', '/dev/null') or die $!; - open(local *CHILD_STDERR, '>', '/dev/null') or die $!; - my $out; - # eval is needed if open3 fails (e.g. command line too long) - eval { - my $pid = open3( - '<&CHILD_STDIN', - $out, - '>&CHILD_STDERR', - # && true is to force spawning a shell and not just exec'ing - "@_ && true"); - my @arr = <$out>; - close $out; - # Make sure $? is set - waitpid($pid, 0); - return wantarray ? @arr : join "",@arr; - } or do { - # If eval fails, force $?=false - `false`; - }; - } -} - -sub uniq(@) { - # Remove duplicates and return unique values - return keys %{{ map { $_ => 1 } @_ }}; -} - -sub min(@) { - # Returns: - # Minimum value of array - my $min; - for (@_) { - # Skip undefs - defined $_ or next; - defined $min or do { $min = $_; next; }; # Set $_ to the first non-undef - $min = ($min < $_) ? $min : $_; - } - return $min; -} - -sub max(@) { - # Returns: - # Maximum value of array - my $max; - for (@_) { - # Skip undefs - defined $_ or next; - defined $max or do { $max = $_; next; }; # Set $_ to the first non-undef - $max = ($max > $_) ? $max : $_; - } - return $max; -} - -sub sum(@) { - # Returns: - # Sum of values of array - my @args = @_; - my $sum = 0; - for (@args) { - # Skip undefs - $_ and do { $sum += $_; } - } - return $sum; -} - -sub undef_as_zero($) { - my $a = shift; - return $a ? $a : 0; -} - -sub undef_as_empty($) { - my $a = shift; - return $a ? $a : ""; -} - -sub undef_if_empty($) { - if(defined($_[0]) and $_[0] eq "") { - return undef; - } - return $_[0]; -} - -sub multiply_binary_prefix(@) { - # Evalualte numbers with binary prefix - # Ki=2^10, Mi=2^20, Gi=2^30, Ti=2^40, Pi=2^50, Ei=2^70, Zi=2^80, Yi=2^80 - # ki=2^10, mi=2^20, gi=2^30, ti=2^40, pi=2^50, ei=2^70, zi=2^80, yi=2^80 - # K =2^10, M =2^20, G =2^30, T =2^40, P =2^50, E =2^70, Z =2^80, Y =2^80 - # k =10^3, m =10^6, g =10^9, t=10^12, p=10^15, e=10^18, z=10^21, y=10^24 - # 13G = 13*1024*1024*1024 = 13958643712 - # Input: - # $s = string with prefixes - # Returns: - # $value = int with prefixes multiplied - my @v = @_; - for(@v) { - defined $_ or next; - s/ki/*1024/gi; - s/mi/*1024*1024/gi; - s/gi/*1024*1024*1024/gi; - s/ti/*1024*1024*1024*1024/gi; - s/pi/*1024*1024*1024*1024*1024/gi; - s/ei/*1024*1024*1024*1024*1024*1024/gi; - s/zi/*1024*1024*1024*1024*1024*1024*1024/gi; - s/yi/*1024*1024*1024*1024*1024*1024*1024*1024/gi; - s/xi/*1024*1024*1024*1024*1024*1024*1024*1024*1024/gi; - - s/K/*1024/g; - s/M/*1024*1024/g; - s/G/*1024*1024*1024/g; - s/T/*1024*1024*1024*1024/g; - s/P/*1024*1024*1024*1024*1024/g; - s/E/*1024*1024*1024*1024*1024*1024/g; - s/Z/*1024*1024*1024*1024*1024*1024*1024/g; - s/Y/*1024*1024*1024*1024*1024*1024*1024*1024/g; - s/X/*1024*1024*1024*1024*1024*1024*1024*1024*1024/g; - - s/k/*1000/g; - s/m/*1000*1000/g; - s/g/*1000*1000*1000/g; - s/t/*1000*1000*1000*1000/g; - s/p/*1000*1000*1000*1000*1000/g; - s/e/*1000*1000*1000*1000*1000*1000/g; - s/z/*1000*1000*1000*1000*1000*1000*1000/g; - s/y/*1000*1000*1000*1000*1000*1000*1000*1000/g; - s/x/*1000*1000*1000*1000*1000*1000*1000*1000*1000/g; - - $_ = eval $_; - } - return wantarray ? @v : $v[0]; -} - -sub multiply_time_units($) { - # Evalualte numbers with time units - # s=1, m=60, h=3600, d=86400 - # Input: - # $s = string time units - # Returns: - # $value = int in seconds - my @v = @_; - for(@v) { - defined $_ or next; - if(/[dhms]/i) { - s/s/*1+/gi; - s/m/*60+/gi; - s/h/*3600+/gi; - s/d/*86400+/gi; - # 1m/3 => 1*60+/3 => 1*60/3 - s/\+(\D)/$1/gi; - } - $_ = eval $_."-0"; - } - return wantarray ? @v : $v[0]; -} - -sub seconds_to_time_units() { - # Convert seconds into ??d??h??m??s - # s=1, m=60, h=3600, d=86400 - # Input: - # $s = int in seconds - # Returns: - # $str = string time units - my $s = shift; - my $str; - my $d = int($s/86400); - $s -= $d * 86400; - my $h = int($s/3600); - $s -= $h * 3600; - my $m = int($s/60); - $s -= $m * 60; - if($d) { - $str = sprintf("%dd%02dh%02dm%02ds",$d,$h,$m,$s); - } elsif($h) { - $str = sprintf("%dh%02dm%02ds",$h,$m,$s); - } elsif($m) { - $str = sprintf("%dm%02ds",$m,$s); - } else { - $str = sprintf("%ds",$s); - } - return $str; -} - -{ - my ($disk_full_fh, $b8193, $error_printed); - sub exit_if_disk_full() { - # Checks if $TMPDIR is full by writing 8kb to a tmpfile - # If the disk is full: Exit immediately. - # Returns: - # N/A - if(not $disk_full_fh) { - $disk_full_fh = ::tmpfile(SUFFIX => ".df"); - $b8193 = "b"x8193; - } - # Linux does not discover if a disk is full if writing <= 8192 - # Tested on: - # bfs btrfs cramfs ext2 ext3 ext4 ext4dev jffs2 jfs minix msdos - # ntfs reiserfs tmpfs ubifs vfat xfs - # TODO this should be tested on different OS similar to this: - # - # doit() { - # sudo mount /dev/ram0 /mnt/loop; sudo chmod 1777 /mnt/loop - # seq 100000 | parallel --tmpdir /mnt/loop/ true & - # seq 6900000 > /mnt/loop/i && echo seq OK - # seq 6980868 > /mnt/loop/i - # seq 10000 > /mnt/loop/ii - # sleep 3 - # sudo umount /mnt/loop/ || sudo umount -l /mnt/loop/ - # echo >&2 - # } - print $disk_full_fh $b8193; - if(not $disk_full_fh - or - tell $disk_full_fh != 8193) { - # On raspbian the disk can be full except for 10 chars. - if(not $error_printed) { - ::error("Output is incomplete.", - "Cannot append to buffer file in $ENV{'TMPDIR'}.", - "Is the disk full?", - "Change \$TMPDIR with --tmpdir or use --compress."); - $error_printed = 1; - } - ::wait_and_exit(255); - } - truncate $disk_full_fh, 0; - seek($disk_full_fh, 0, 0) || die; - } -} - -sub spacefree($$) { - # Remove comments and spaces - # Inputs: - # $spaces = keep 1 space? - # $s = string to remove spaces from - # Returns: - # $s = with spaces removed - my $spaces = shift; - my $s = shift; - $s =~ s/#.*//mg; - if(1 == $spaces) { - $s =~ s/\s+/ /mg; - } elsif(2 == $spaces) { - # Keep newlines - $s =~ s/\n\n+/\n/sg; - $s =~ s/[ \t]+/ /mg; - } elsif(3 == $spaces) { - # Keep perl code required space - $s =~ s{([^a-zA-Z0-9/])\s+}{$1}sg; - $s =~ s{([a-zA-Z0-9/])\s+([^:a-zA-Z0-9/])}{$1$2}sg; - } else { - $s =~ s/\s//mg; - } - return $s; -} - -{ - my $hostname; - sub hostname() { - local $/ = "\n"; - if(not $hostname) { - $hostname = `hostname`; - chomp($hostname); - $hostname ||= "nohostname"; - } - return $hostname; - } -} - -sub which(@) { - # Input: - # @programs = programs to find the path to - # Returns: - # @full_path = full paths to @programs. Nothing if not found - my @which; - for my $prg (@_) { - push(@which, grep { not -d $_ and -x $_ } - map { $_."/".$prg } split(":",$ENV{'PATH'})); - if($prg =~ m:/:) { - # Test if program with full path exists - push(@which, grep { not -d $_ and -x $_ } $prg); - } - } - ::debug("which", "$which[0] in $ENV{'PATH'}\n"); - return wantarray ? @which : $which[0]; -} - -{ - my ($regexp,$shell,%fakename); - - sub parent_shell { - # Input: - # $pid = pid to see if (grand)*parent is a shell - # Returns: - # $shellpath = path to shell - undef if no shell found - my $pid = shift; - ::debug("init","Parent of $pid\n"); - if(not $regexp) { - # All shells known to mankind - # - # ash bash csh dash fdsh fish fizsh ksh ksh93 mksh pdksh - # posh rbash rc rush rzsh sash sh static-sh tcsh yash zsh - - my @shells = (qw(ash bash bsd-csh csh dash fdsh fish fizsh ksh - ksh93 lksh mksh pdksh posh rbash rc rush rzsh sash sh - static-sh tcsh yash zsh -sh -csh -bash), - '-sh (sh)' # sh on FreeBSD - ); - # Can be formatted as: - # [sh] -sh sh busybox sh -sh (sh) - # /bin/sh /sbin/sh /opt/csw/sh - # But not: foo.sh sshd crash flush pdflush scosh fsflush ssh - $shell = "(?:".join("|",map { "\Q$_\E" } @shells).")"; - $regexp = '^((\[)(-?)('. $shell. ')(\])|(|\S+/|busybox )'. - '(-?)('. $shell. '))( *$| [^(])'; - %fakename = ( - # sh disguises itself as -sh (sh) on FreeBSD - "-sh (sh)" => ["sh"], - # csh and tcsh disguise themselves as -sh/-csh - # E.g.: ssh -tt csh@lo 'ps aux;true' |egrep ^csh - # but sh also disguises itself as -sh - # (TODO When does that happen?) - "-sh" => ["sh"], - "-csh" => ["tcsh", "csh"], - # ash disguises itself as -ash - "-ash" => ["ash", "dash", "sh"], - # dash disguises itself as -dash - "-dash" => ["dash", "ash", "sh"], - # bash disguises itself as -bash - "-bash" => ["bash", "sh"], - # ksh disguises itself as -ksh - "-ksh" => ["ksh", "sh"], - # zsh disguises itself as -zsh - "-zsh" => ["zsh", "sh"], - ); - } - if($^O eq "linux") { - # Optimized for GNU/Linux - my $testpid = $pid; - my $shellpath; - my $shellline; - while($testpid) { - if(open(my $fd, "<", "/proc/$testpid/cmdline")) { - local $/="\0"; - chomp($shellline = <$fd>); - if($shellline =~ /$regexp/o) { - my $shellname = $4 || $8; - my $dash = $3 || $7; - if($shellname eq "sh" and $dash) { - # -sh => csh or sh - if($shellpath = readlink "/proc/$testpid/exe") { - ::debug("init","procpath $shellpath\n"); - if($shellpath =~ m:/$shell$:o) { - ::debug("init", - "proc which ".$shellpath." => "); - return $shellpath; - } - } - } - ::debug("init", "which ".$shellname." => "); - $shellpath = (which($shellname, - @{$fakename{$shellname}}))[0]; - ::debug("init", "shell path $shellpath\n"); - return $shellpath; - } - } - # Get parent pid - if(open(my $fd, "<", "/proc/$testpid/stat")) { - my $line = <$fd>; - close $fd; - # Parent pid is field 4 - $testpid = (split /\s+/, $line)[3]; - } else { - # Something is wrong: fall back to old method - last; - } - } - } - # if -sh or -csh try readlink /proc/$$/exe - my ($children_of_ref, $parent_of_ref, $name_of_ref) = pid_table(); - my $shellpath; - my $testpid = $pid; - while($testpid) { - if($name_of_ref->{$testpid} =~ /$regexp/o) { - my $shellname = $4 || $8; - my $dash = $3 || $7; - if($shellname eq "sh" and $dash) { - # -sh => csh or sh - if($shellpath = readlink "/proc/$testpid/exe") { - ::debug("init","procpath $shellpath\n"); - if($shellpath =~ m:/$shell$:o) { - ::debug("init", "proc which ".$shellpath." => "); - return $shellpath; - } - } - } - ::debug("init", "which ".$shellname." => "); - $shellpath = (which($shellname,@{$fakename{$shellname}}))[0]; - ::debug("init", "shell path $shellpath\n"); - $shellpath and last; - } - if($testpid == $parent_of_ref->{$testpid}) { - # In Solaris zones, the PPID of the zsched process is itself - last; - } - $testpid = $parent_of_ref->{$testpid}; - } - return $shellpath; - } -} - -{ - my %pid_parentpid_cmd; - - sub pid_table() { - # Returns: - # %children_of = { pid -> children of pid } - # %parent_of = { pid -> pid of parent } - # %name_of = { pid -> commandname } - - if(not %pid_parentpid_cmd) { - # Filter for SysV-style `ps` - my $sysv = q( ps -ef |). - q(perl -ane '1..1 and /^(.*)CO?MM?A?N?D/ and $s=length $1;). - q(s/^.{$s}//; print "@F[1,2] $_"' ); - # Minix uses cols 2,3 and can have newlines in the command - # so lines not having numbers in cols 2,3 must be ignored - my $minix = q( ps -ef |). - q(perl -ane '1..1 and /^(.*)CO?MM?A?N?D/ and $s=length $1;). - q(s/^.{$s}// and $F[2]>0 and $F[3]>0 and print "@F[2,3] $_"' ); - # BSD-style `ps` - my $bsd = q(ps -o pid,ppid,command -ax); - %pid_parentpid_cmd = - ( - 'aix' => $sysv, - 'android' => $sysv, - 'cygwin' => $sysv, - 'darwin' => $bsd, - 'dec_osf' => $sysv, - 'dragonfly' => $bsd, - 'freebsd' => $bsd, - 'gnu' => $sysv, - 'hpux' => $sysv, - 'linux' => $sysv, - 'mirbsd' => $bsd, - 'minix' => $minix, - 'msys' => $sysv, - 'MSWin32' => $sysv, - 'netbsd' => $bsd, - 'nto' => $sysv, - 'openbsd' => $bsd, - 'solaris' => $sysv, - 'svr5' => $sysv, - 'syllable' => "echo ps not supported", - ); - } - $pid_parentpid_cmd{$^O} or - ::die_bug("pid_parentpid_cmd for $^O missing"); - - my (@pidtable,%parent_of,%children_of,%name_of); - # Table with pid -> children of pid - @pidtable = `$pid_parentpid_cmd{$^O}`; - my $p=$$; - for (@pidtable) { - # must match: 24436 21224 busybox ash - # must match: 24436 21224 <> - # must match: 24436 21224 <> - # or: perl -e 'while($0=" "){}' - if(/^\s*(\S+)\s+(\S+)\s+(\S+.*)/ - or - /^\s*(\S+)\s+(\S+)\s+()$/) { - $parent_of{$1} = $2; - push @{$children_of{$2}}, $1; - $name_of{$1} = $3; - } else { - ::die_bug("pidtable format: $_"); - } - } - return(\%children_of, \%parent_of, \%name_of); - } -} - -sub now() { - # Returns time since epoch as in seconds with 3 decimals - # Uses: - # @Global::use - # Returns: - # $time = time now with millisecond accuracy - if(not $Global::use{"Time::HiRes"}) { - if(eval "use Time::HiRes qw ( time );") { - eval "sub TimeHiRestime { return Time::HiRes::time };"; - } else { - eval "sub TimeHiRestime { return time() };"; - } - $Global::use{"Time::HiRes"} = 1; - } - - return (int(TimeHiRestime()*1000))/1000; -} - -sub usleep($) { - # Sleep this many milliseconds. - # Input: - # $ms = milliseconds to sleep - my $ms = shift; - ::debug("timing",int($ms),"ms "); - select(undef, undef, undef, $ms/1000); -} - -sub make_regexp_ungreedy { - my $regexp = shift; - my $class_state = 0; - my $escape_state = 0; - my $found = 0; - my $ungreedy = ""; - my $c; - - for $c (split (//, $regexp)) { - if ($found) { - if($c ne "?") { $ungreedy .= "?"; } - $found = 0; - } - $ungreedy .= $c; - - if ($escape_state) { $escape_state = 0; next; } - if ($c eq "\\") { $escape_state = 1; next; } - if ($c eq '[') { $class_state = 1; next; } - if ($class_state) { - if($c eq ']') { $class_state = 0; } - next; - } - # Quantifiers: + * {...} - if ($c =~ /[*}+]/) { $found = 1; } - } - if($found) { $ungreedy .= '?'; } - return $ungreedy; -} - - -sub __KILLER_REAPER__() {} - -sub reap_usleep() { - # Reap dead children. - # If no dead children: Sleep specified amount with exponential backoff - # Input: - # $ms = milliseconds to sleep - # Returns: - # $ms/2+0.001 if children reaped - # $ms*1.1 if no children reaped - my $ms = shift; - if(reapers()) { - if(not $Global::total_completed % 100) { - if($opt::timeout) { - # Force cleaning the timeout queue for every 100 jobs - # Fixes potential memleak - $Global::timeoutq->process_timeouts(); - } - } - # Sleep exponentially shorter (1/2^n) if a job finished - return $ms/2+0.001; - } else { - if($opt::timeout) { - $Global::timeoutq->process_timeouts(); - } - if($opt::memfree) { - kill_youngster_if_not_enough_mem($opt::memfree*0.5); - } - if($opt::memsuspend) { - suspend_young_if_not_enough_mem($opt::memsuspend); - } - if($opt::limit) { - kill_youngest_if_over_limit(); - } - exit_if_disk_full(); - if($Global::linebuffer) { - my $something_printed = 0; - if($opt::keeporder and not $opt::latestline) { - for my $job (values %Global::running) { - $something_printed += $job->print_earlier_jobs(); - } - } else { - for my $job (values %Global::running) { - $something_printed += $job->print(); - } - } - if($something_printed) { $ms = $ms/2+0.001; } - } - if($ms > 0.002) { - # When a child dies, wake up from sleep (or select(,,,)) - $SIG{CHLD} = sub { kill "ALRM", $$ }; - if($opt::delay and not $Global::linebuffer) { - # The 0.004s is approximately the time it takes for one round - my $next_earliest_start = - $Global::newest_starttime + $opt::delay - 0.004; - my $remaining_ms = 1000 * ($next_earliest_start - ::now()); - # The next job can only start at $next_earliest_start - # so sleep until then (but sleep at least $ms) - usleep(::max($ms,$remaining_ms)); - } else { - usleep($ms); - } - # --compress needs $SIG{CHLD} unset - $SIG{CHLD} = 'DEFAULT'; - } - # Sleep exponentially longer (1.1^n) if a job did not finish, - # though at most 1000 ms. - return (($ms < 1000) ? ($ms * 1.1) : ($ms)); - } -} - -sub kill_youngest_if_over_limit() { - # Check each $sshlogin we are over limit - # If over limit: kill off the youngest child - # Put the child back in the queue. - # Uses: - # %Global::running - my %jobs_of; - my @sshlogins; - - for my $job (values %Global::running) { - if(not $jobs_of{$job->sshlogin()}) { - push @sshlogins, $job->sshlogin(); - } - push @{$jobs_of{$job->sshlogin()}}, $job; - } - for my $sshlogin (@sshlogins) { - for my $job (sort { $b->seq() <=> $a->seq() } - @{$jobs_of{$sshlogin}}) { - if($sshlogin->limit() == 2) { - $job->kill(); - last; - } - } - } -} - -sub suspend_young_if_not_enough_mem() { - # Check each $sshlogin if there is enough mem. - # If less than $limit free mem: suspend some of the young children - # Else: Resume all jobs - # Uses: - # %Global::running - my $limit = shift; - my %jobs_of; - my @sshlogins; - - for my $job (values %Global::running) { - if(not $jobs_of{$job->sshlogin()}) { - push @sshlogins, $job->sshlogin(); - } - push @{$jobs_of{$job->sshlogin()}}, $job; - } - for my $sshlogin (@sshlogins) { - my $free = $sshlogin->memfree(); - if($free < 2*$limit) { - # Suspend all jobs (resume some of them later) - map { $_->suspended() or $_->suspend(); } @{$jobs_of{$sshlogin}}; - my @jobs = (sort { $b->seq() <=> $a->seq() } - @{$jobs_of{$sshlogin}}); - # how many should be running? - # limit*1 => 1; - # limit*1.5 => 2; - # limit*1.75 => 4; - # free < limit*(2-1/2^n); - # => - # 1/(2-free/limit) < 2^n; - my $run = int(1/(2-$free/$limit)); - $run = ::min($run,$#jobs); - # Resume the oldest running - for my $job ((sort { $a->seq() <=> $b->seq() } @jobs)[0..$run]) { - ::debug("mem","\nResume ",$run+1, " jobs. Seq ", - $job->seq(), " resumed ", - $sshlogin->memfree()," < ",2*$limit); - $job->resume(); - } - } else { - for my $job (@{$jobs_of{$sshlogin}}) { - if($job->suspended()) { - $job->resume(); - ::debug("mem","\nResume ",$#{$jobs_of{$sshlogin}}+1, - " jobs. Seq ", $job->seq(), " resumed ", - $sshlogin->memfree()," > ",2*$limit); - last; - } - } - } - } -} - -sub kill_youngster_if_not_enough_mem() { - # Check each $sshlogin if there is enough mem. - # If less than 50% enough free mem: kill off the youngest child - # Put the child back in the queue. - # Uses: - # %Global::running - my $limit = shift; - my %jobs_of; - my @sshlogins; - - for my $job (values %Global::running) { - if(not $jobs_of{$job->sshlogin()}) { - push @sshlogins, $job->sshlogin(); - } - push @{$jobs_of{$job->sshlogin()}}, $job; - } - for my $sshlogin (@sshlogins) { - for my $job (sort { $b->seq() <=> $a->seq() } - @{$jobs_of{$sshlogin}}) { - if($sshlogin->memfree() < $limit) { - ::debug("mem","\n",map { $_->seq()." " } - (sort { $b->seq() <=> $a->seq() } - @{$jobs_of{$sshlogin}})); - ::debug("mem","\n", $job->seq(), "killed ", - $sshlogin->memfree()," < ",$limit); - $job->kill(); - $sshlogin->memfree_recompute(); - } else { - last; - } - } - ::debug("mem","Free mem OK? ", - $sshlogin->memfree()," > ",$limit); - } -} - - -sub __DEBUGGING__() {} - - -sub debug(@) { - # Uses: - # $Global::debug - # %Global::fh - # Returns: N/A - $Global::debug or return; - @_ = grep { defined $_ ? $_ : "" } @_; - if($Global::debug eq "all" or $Global::debug eq $_[0]) { - if($Global::fh{2}) { - # Original stderr was saved - my $stderr = $Global::fh{2}; - print $stderr @_[1..$#_]; - } else { - print STDERR @_[1..$#_]; - } - } -} - -sub my_memory_usage() { - # Returns: - # memory usage if found - # 0 otherwise - use strict; - use FileHandle; - - local $/ = "\n"; - my $pid = $$; - if(-e "/proc/$pid/stat") { - my $fh = FileHandle->new("; - chomp $data; - $fh->close; - - my @procinfo = split(/\s+/,$data); - - return undef_as_zero($procinfo[22]); - } else { - return 0; - } -} - -sub my_size() { - # Returns: - # $size = size of object if Devel::Size is installed - # -1 otherwise - my @size_this = (@_); - eval "use Devel::Size qw(size total_size)"; - if ($@) { - return -1; - } else { - return total_size(@_); - } -} - -sub my_dump(@) { - # Returns: - # ascii expression of object if Data::Dump(er) is installed - # error code otherwise - my @dump_this = (@_); - eval "use Data::Dump qw(dump);"; - if ($@) { - # Data::Dump not installed - eval "use Data::Dumper;"; - if ($@) { - my $err = "Neither Data::Dump nor Data::Dumper is installed\n". - "Not dumping output\n"; - ::status($err); - return $err; - } else { - return Dumper(@dump_this); - } - } else { - # Create a dummy Data::Dump:dump as Hans Schou sometimes has - # it undefined - eval "sub Data::Dump:dump {}"; - eval "use Data::Dump qw(dump);"; - return (Data::Dump::dump(@dump_this)); - } -} - -sub my_croak(@) { - eval "use Carp; 1"; - $Carp::Verbose = 1; - croak(@_); -} - -sub my_carp() { - eval "use Carp; 1"; - $Carp::Verbose = 1; - carp(@_); -} - - -sub __OBJECT_ORIENTED_PARTS__() {} - - -package SSHLogin; - -sub new($$) { - my $class = shift; - my $s = shift; - my $origs = $s; - my %hostgroups; - my $ncpus; - my $sshcommand; - my $user; - my $password; - my $host; - my $port; - my $local; - my $string; - # SSHLogins can have these formats: - # @grp+grp/ncpu//usr/bin/ssh user@server - # ncpu//usr/bin/ssh user@server - # /usr/bin/ssh user@server - # user@server - # ncpu/user@server - # @grp+grp/user@server - # above with: user:password@server - # above with: user@server:port - # So: - # [@grp+grp][ncpu/][ssh command ][[user][:password]@][server[:port]] - - # [@grp+grp]/ncpu//usr/bin/ssh user:pass@server:port - if($s =~ s:^\@([^/]+)/?::) { - # Look for SSHLogin hostgroups - %hostgroups = map { $_ => 1 } split(/\+|,/, $1); - } - # An SSHLogin is always in the hostgroup of its "numcpu/host" - $hostgroups{$s} = 1; - - # [ncpu/]/usr/bin/ssh user:pass@server:port - if ($s =~ s:^(\d+)/::) { $ncpus = $1; } - - # [/usr/bin/ssh ]user:pass@server:port - if($s =~ s/^(.*) //) { $sshcommand = $1; } - - # [user:pass@]server:port - if($s =~ s/^([^@]+)@//) { - my $userpw = $1; - # user[:pass] - if($userpw =~ s/:(.*)//) { - $password = $1; - if($password eq "") { $password = $ENV{'SSHPASS'} } - if(not ::which("sshpass")) { - ::error("--sshlogin with password requires sshpass installed"); - ::wait_and_exit(255); - } - } - $user = $userpw; - } - # [server]:port - if(not $s =~ /:.*:/ - and - $s =~ s/^([-a-z0-9._]+)//i) { - # Not IPv6 (IPv6 has 2 or more ':') - $host = $1; - } elsif($s =~ s/^(\\[\[\]box0-9a-f.]+)//i) { - # RFC2673 allows for: - # \[b11010000011101] \[o64072/14] \[xd074/14] \[208.116.0.0/14] - $host = $1; - } elsif($s =~ s/^\[([0-9a-f:]+)\]//i - or - $s =~ s/^([0-9a-f:]+)//i) { - # RFC5952 - # [2001:db8::1]:80 - # 2001:db8::1.80 - # 2001:db8::1p80 - # 2001:db8::1#80 - # 2001:db8::1:80 - not supported - # 2001:db8::1 port 80 - not supported - $host = $1; - } - - # [:port] - if($s =~ s/^:(\w+)//i) { - $port = $1; - } elsif($s =~ s/^[p\.\#](\w+)//i) { - # RFC5952 - # 2001:db8::1.80 - # 2001:db8::1p80 - # 2001:db8::1#80 - $port = $1; - } - - if($s and $s ne ':') { - ::die_bug("SSHLogin parser failed on '$origs' => '$s'"); - } - - $string = - # Only include the sshcommand in $string if it is set by user - ($sshcommand && $sshcommand." "). - ($user && $user."@"). - ($host && $host). - ($port && ":$port"); - if($host eq ':') { - $local = 1; - $string = ":"; - } else { - $sshcommand ||= $opt::ssh || $ENV{'PARALLEL_SSH'} || "ssh"; - } - # An SSHLogin is always in the hostgroup of its $string-name - $hostgroups{$string} = 1; - @Global::hostgroups{keys %hostgroups} = values %hostgroups; - # Used for file names for loadavg - my $no_slash_string = $string; - $no_slash_string =~ s/[^-a-z0-9:]/_/gi; - return bless { - 'string' => $string, - 'jobs_running' => 0, - 'jobs_completed' => 0, - 'maxlength' => undef, - 'max_jobs_running' => undef, - 'orig_max_jobs_running' => undef, - 'ncpus' => $ncpus, - 'sshcommand' => $sshcommand, - 'user' => $user, - 'password' => $password, - 'host' => $host, - 'port' => $port, - 'hostgroups' => \%hostgroups, - 'local' => $local, - 'control_path_dir' => undef, - 'control_path' => undef, - 'time_to_login' => undef, - 'last_login_at' => undef, - 'loadavg_file' => $Global::cache_dir . "/tmp/sshlogin/" . - $no_slash_string . "/loadavg", - 'loadavg' => undef, - 'last_loadavg_update' => 0, - 'swap_activity_file' => $Global::cache_dir . "/tmp/sshlogin/" . - $no_slash_string . "/swap_activity", - 'swap_activity' => undef, - }, ref($class) || $class; -} - -sub DESTROY($) { - my $self = shift; - # Remove temporary files if they are created. - ::rm($self->{'loadavg_file'}); - ::rm($self->{'swap_activity_file'}); -} - -sub string($) { - my $self = shift; - return $self->{'string'}; -} - -sub host($) { - my $self = shift; - return $self->{'host'}; -} - -sub sshcmd($) { - # Give the ssh command without hostname - # Returns: - # "sshpass -e ssh -p port -l user" - my $self = shift; - my @local; - # [sshpass -e] ssh -p port -l user - if($self->{'password'}) { push @local, "sshpass -e"; } - # [ssh] -p port -l user - # TODO sshpass + space - push @local, $self->{'sshcommand'}; - # [-p port] -l user - if($self->{'port'}) { push @local, '-p',$self->{'port'}; } - # [-l user] - if($self->{'user'}) { push @local, '-l',$self->{'user'}; } - if($opt::controlmaster) { - # Use control_path to make ssh faster - my $control_path = $self->control_path_dir()."/ssh-%r@%h:%p"; - - if(not $self->{'control_path'}{$control_path}++) { - # Master is not running for this control_path - # Start it - my $pid = fork(); - if($pid) { - $Global::sshmaster{$pid} ||= 1; - } else { - push @local, "-S", $control_path; - $SIG{'TERM'} = undef; - # Run a sleep that outputs data, so it will discover - # if the ssh connection closes. - my $sleep = ::Q('$|=1;while(1){sleep 1;print "foo\n"}'); - # Ignore the 'foo' being printed - open(STDOUT,">","/dev/null"); - # STDERR >/dev/null to ignore - open(STDERR,">","/dev/null"); - open(STDIN,"<","/dev/null"); - exec(@local, "-MT", $self->{'host'}, "--", - "perl", "-e", $sleep); - } - } - push @local, "-S", ::Q($control_path); - } - - return "@local"; -} - -sub wrap($@) { - # Input: - # @cmd = shell command to run on remote - # Returns: - # $sshwrapped = ssh remote @cmd - my $self = shift; - my @remote = @_; - return(join " ", - $self->sshcmd(), $self->{'host'}, "--", "exec", @remote); -} - -sub hexwrap($@) { - # Input: - # @cmd = perl expresion to eval - # Returns: - # $hexencoded = perl command that decodes hex and evals @cmd - my $self = shift; - my $cmd = join("",@_); - - # "#" is needed because Perl on MacOS X adds NULs - # when running pack q/H10000000/ - my $hex = unpack "H*", $cmd."#"; - # csh does not deal well with > 1000 chars in one word - # Insert space every 1000 char - $hex =~ s/\G.{1000}\K/ /sg; - # Explanation: - # Write this without special chars: eval pack 'H*', join '',@ARGV - # GNU_Parallel_worker = String so people can see this is from GNU Parallel - # eval+ = way to write 'eval ' without space (gives warning) - # pack+ = way to write 'pack ' without space - # q/H10000000/, = almost the same as "H*" but does not use * - # join+q//, = join '', - return('perl -X -e '. - 'GNU_Parallel_worker,eval+pack+q/H10000000/,join+q//,@ARGV '. - $hex); -} - -sub jobs_running($) { - my $self = shift; - return ($self->{'jobs_running'} || "0"); -} - -sub inc_jobs_running($) { - my $self = shift; - $self->{'jobs_running'}++; -} - -sub dec_jobs_running($) { - my $self = shift; - $self->{'jobs_running'}--; -} - -sub set_maxlength($$) { - my $self = shift; - $self->{'maxlength'} = shift; -} - -sub maxlength($) { - my $self = shift; - return $self->{'maxlength'}; -} - -sub jobs_completed() { - my $self = shift; - return $self->{'jobs_completed'}; -} - -sub in_hostgroups() { - # Input: - # @hostgroups = the hostgroups to look for - # Returns: - # true if intersection of @hostgroups and the hostgroups of this - # SSHLogin is non-empty - my $self = shift; - return grep { defined $self->{'hostgroups'}{$_} } @_; -} - -sub hostgroups() { - my $self = shift; - return keys %{$self->{'hostgroups'}}; -} - -sub inc_jobs_completed($) { - my $self = shift; - $self->{'jobs_completed'}++; - $Global::total_completed++; -} - -sub set_max_jobs_running($$) { - my $self = shift; - if(defined $self->{'max_jobs_running'}) { - $Global::max_jobs_running -= $self->{'max_jobs_running'}; - } - $self->{'max_jobs_running'} = shift; - - if(defined $self->{'max_jobs_running'}) { - # max_jobs_running could be resat if -j is a changed file - $Global::max_jobs_running += $self->{'max_jobs_running'}; - } - # Initialize orig to the first non-zero value that comes around - $self->{'orig_max_jobs_running'} ||= $self->{'max_jobs_running'}; -} - -sub memfree() { - # Returns: - # $memfree in bytes - my $self = shift; - $self->memfree_recompute(); - # Return 1 if not defined. - return (not defined $self->{'memfree'} or $self->{'memfree'}) -} - -sub memfree_recompute() { - my $self = shift; - my $script = memfreescript(); - - # TODO add sshlogin and backgrounding - # Run the script twice if it gives 0 (typically intermittent error) - $self->{'memfree'} = ::qqx($script) || ::qqx($script); - if(not $self->{'memfree'}) { - ::die_bug("Less than 1 byte memory free"); - } - #::debug("mem","New free:",$self->{'memfree'}," "); -} - -{ - my $script; - - sub memfreescript() { - # Returns: - # shellscript for giving available memory in bytes - if(not $script) { - my %script_of = ( - # /proc/meminfo - # MemFree: 7012 kB - # Buffers: 19876 kB - # Cached: 431192 kB - # SwapCached: 0 kB - "linux" => ( - q{ - print 1024 * qx{ - awk '/^((Swap)?Cached|MemFree|Buffers):/ - { sum += \$2} END { print sum }' - /proc/meminfo } - }), - # Android uses same code as GNU/Linux - "android" => ( - q{ - print 1024 * qx{ - awk '/^((Swap)?Cached|MemFree|Buffers):/ - { sum += \$2} END { print sum }' - /proc/meminfo } - }), - # $ vmstat 1 1 - # procs memory page faults cpu - # r b w avm free re at pi po fr de sr in sy cs us sy id - # 1 0 0 242793 389737 5 1 0 0 0 0 0 107 978 60 1 1 99 - "hpux" => ( - q{ - print (((reverse `vmstat 1 1`)[0] - =~ /(?:\d+\D+){4}(\d+)/)[0]*1024) - }), - # $ vmstat 1 2 - # kthr memory page disk faults cpu - # r b w swap free re mf pi po fr de sr s3 s4 -- -- in sy cs us sy id - # 0 0 0 6496720 5170320 68 260 8 2 1 0 0 -0 3 0 0 309 1371 255 1 2 97 - # 0 0 0 6434088 5072656 7 15 8 0 0 0 0 0 261 0 0 1889 1899 3222 0 8 92 - # - # The second free value is correct - "solaris" => ( - q{ - print (((reverse `vmstat 1 2`)[0] - =~ /(?:\d+\D+){4}(\d+)/)[0]*1024) - }), - # hw.pagesize: 4096 - # vm.stats.vm.v_cache_count: 0 - # vm.stats.vm.v_inactive_count: 79574 - # vm.stats.vm.v_free_count: 4507 - "freebsd" => ( - q{ - for(qx{/sbin/sysctl -a}) { - if (/^([^:]+):\s+(.+)\s*$/s) { - $sysctl->{$1} = $2; - } - } - print $sysctl->{"hw.pagesize"} * - ($sysctl->{"vm.stats.vm.v_cache_count"} - + $sysctl->{"vm.stats.vm.v_inactive_count"} - + $sysctl->{"vm.stats.vm.v_free_count"}); - }), - # Mach Virtual Memory Statistics: (page size of 4096 bytes) - # Pages free: 198061. - # Pages active: 159701. - # Pages inactive: 47378. - # Pages speculative: 29707. - # Pages wired down: 89231. - # "Translation faults": 928901425. - # Pages copy-on-write: 156988239. - # Pages zero filled: 271267894. - # Pages reactivated: 48895. - # Pageins: 1798068. - # Pageouts: 257. - # Object cache: 6603 hits of 1713223 lookups (0% hit rate) - 'darwin' => ( - q{ - $vm = `vm_stat`; - print (($vm =~ /page size of (\d+)/)[0] * - (($vm =~ /Pages free:\s+(\d+)/)[0] + - ($vm =~ /Pages inactive:\s+(\d+)/)[0])); - }), - ); - my $perlscript = ""; - # Make a perl script that detects the OS ($^O) and runs - # the appropriate command - for my $os (keys %script_of) { - $perlscript .= 'if($^O eq "'.$os.'") { '.$script_of{$os}.'}'; - } - $script = "perl -e " . ::Q(::spacefree(1,$perlscript)); - } - return $script; - } -} - -sub limit($) { - # Returns: - # 0 = Below limit. Start another job. - # 1 = Over limit. Start no jobs. - # 2 = Kill youngest job - my $self = shift; - - if(not defined $self->{'limitscript'}) { - my %limitscripts = - ("io" => q! - io() { - limit=$1; - io_file=$2; - # Do the measurement in the background - ((tmp=$(tempfile); - LANG=C iostat -x 1 2 > $tmp; - mv $tmp $io_file) /dev/null & ); - perl -e '-e $ARGV[0] or exit(1); - for(reverse <>) { - /Device/ and last; - /(\S+)$/ and $max = $max > $1 ? $max : $1; } - exit ('$limit' < $max)' $io_file; - }; - io %s %s - !, - "mem" => q! - mem() { - limit=$1; - awk '/^((Swap)?Cached|MemFree|Buffers):/{ sum += $2} - END { - if (sum*1024 < '$limit'/2) { exit 2; } - else { exit (sum*1024 < '$limit') } - }' /proc/meminfo; - }; - mem %s; - !, - "load" => q! - load() { - limit=$1; - ps ax -o state,command | - grep -E '^[DOR].[^[]' | - wc -l | - perl -ne 'exit ('$limit' < $_)'; - }; - load %s - !, - ); - my ($cmd,@args) = split /\s+/,$opt::limit; - if($limitscripts{$cmd}) { - my $tmpfile = ::tmpname("parlmt"); - ++$Global::unlink{$tmpfile}; - $self->{'limitscript'} = - ::spacefree(1, sprintf($limitscripts{$cmd}, - ::multiply_binary_prefix(@args),$tmpfile)); - } else { - $self->{'limitscript'} = $opt::limit; - } - } - - my %env = %ENV; - local %ENV = %env; - $ENV{'SSHLOGIN'} = $self->string(); - system($Global::shell,"-c",$self->{'limitscript'}); - #::qqx($self->{'limitscript'}); - ::debug("limit","limit `".$self->{'limitscript'}."` result ".($?>>8)."\n"); - return $?>>8; -} - - -sub swapping($) { - my $self = shift; - my $swapping = $self->swap_activity(); - return (not defined $swapping or $swapping) -} - -sub swap_activity($) { - # If the currently known swap activity is too old: - # Recompute a new one in the background - # Returns: - # last swap activity computed - my $self = shift; - # Should we update the swap_activity file? - my $update_swap_activity_file = 0; - # Test with (on 64 core machine): - # seq 100 | parallel --lb -j100 'seq 1000 | parallel --noswap -j 1 true' - if(open(my $swap_fh, "<", $self->{'swap_activity_file'})) { - my $swap_out = <$swap_fh>; - close $swap_fh; - if($swap_out =~ /^(\d+)$/) { - $self->{'swap_activity'} = $1; - ::debug("swap", "New swap_activity: ", $self->{'swap_activity'}); - } - ::debug("swap", "Last update: ", $self->{'last_swap_activity_update'}); - if(time - $self->{'last_swap_activity_update'} > 10) { - # last swap activity update was started 10 seconds ago - ::debug("swap", "Older than 10 sec: ", $self->{'swap_activity_file'}); - $update_swap_activity_file = 1; - } - } else { - ::debug("swap", "No swap_activity file: ", $self->{'swap_activity_file'}); - $self->{'swap_activity'} = undef; - $update_swap_activity_file = 1; - } - if($update_swap_activity_file) { - ::debug("swap", "Updating swap_activity file ", $self->{'swap_activity_file'}); - $self->{'last_swap_activity_update'} = time; - my $dir = ::dirname($self->{'swap_activity_file'}); - -d $dir or eval { File::Path::mkpath($dir); }; - my $swap_activity; - $swap_activity = swapactivityscript(); - if(not $self->local()) { - $swap_activity = $self->wrap($swap_activity); - } - # Run swap_activity measuring. - # As the command can take long to run if run remote - # save it to a tmp file before moving it to the correct file - my $file = $self->{'swap_activity_file'}; - my ($dummy_fh, $tmpfile) = ::tmpfile(SUFFIX => ".swp"); - ::debug("swap", "\n", $swap_activity, "\n"); - my $qtmp = ::Q($tmpfile); - my $qfile = ::Q($file); - ::qqx("($swap_activity > $qtmp && mv $qtmp $qfile || rm $qtmp &)"); - } - return $self->{'swap_activity'}; -} - -{ - my $script; - - sub swapactivityscript() { - # Returns: - # shellscript for detecting swap activity - # - # arguments for vmstat are OS dependant - # swap_in and swap_out are in different columns depending on OS - # - if(not $script) { - my %vmstat = ( - # linux: $7*$8 - # $ vmstat 1 2 - # procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu---- - # r b swpd free buff cache si so bi bo in cs us sy id wa - # 5 0 51208 1701096 198012 18857888 0 0 37 153 28 19 56 11 33 1 - # 3 0 51208 1701288 198012 18857972 0 0 0 0 3638 10412 15 3 82 0 - 'linux' => ['vmstat 1 2 | tail -n1', '$7*$8'], - - # solaris: $6*$7 - # $ vmstat -S 1 2 - # kthr memory page disk faults cpu - # r b w swap free si so pi po fr de sr s3 s4 -- -- in sy cs us sy id - # 0 0 0 4628952 3208408 0 0 3 1 1 0 0 -0 2 0 0 263 613 246 1 2 97 - # 0 0 0 4552504 3166360 0 0 0 0 0 0 0 0 0 0 0 246 213 240 1 1 98 - 'solaris' => ['vmstat -S 1 2 | tail -1', '$6*$7'], - - # darwin (macosx): $21*$22 - # $ vm_stat -c 2 1 - # Mach Virtual Memory Statistics: (page size of 4096 bytes) - # free active specul inactive throttle wired prgable faults copy 0fill reactive purged file-backed anonymous cmprssed cmprssor dcomprs comprs pageins pageout swapins swapouts - # 346306 829050 74871 606027 0 240231 90367 544858K 62343596 270837K 14178 415070 570102 939846 356 370 116 922 4019813 4 0 0 - # 345740 830383 74875 606031 0 239234 90369 2696 359 553 0 0 570110 941179 356 370 0 0 0 0 0 0 - 'darwin' => ['vm_stat -c 2 1 | tail -n1', '$21*$22'], - - # ultrix: $12*$13 - # $ vmstat -S 1 2 - # procs faults cpu memory page disk - # r b w in sy cs us sy id avm fre si so pi po fr de sr s0 - # 1 0 0 4 23 2 3 0 97 7743 217k 0 0 0 0 0 0 0 0 - # 1 0 0 6 40 8 0 1 99 7743 217k 0 0 3 0 0 0 0 0 - 'ultrix' => ['vmstat -S 1 2 | tail -1', '$12*$13'], - - # aix: $6*$7 - # $ vmstat 1 2 - # System configuration: lcpu=1 mem=2048MB - # - # kthr memory page faults cpu - # ----- ----------- ------------------------ ------------ ----------- - # r b avm fre re pi po fr sr cy in sy cs us sy id wa - # 0 0 333933 241803 0 0 0 0 0 0 10 143 90 0 0 99 0 - # 0 0 334125 241569 0 0 0 0 0 0 37 5368 184 0 9 86 5 - 'aix' => ['vmstat 1 2 | tail -n1', '$6*$7'], - - # freebsd: $8*$9 - # $ vmstat -H 1 2 - # procs memory page disks faults cpu - # r b w avm fre flt re pi po fr sr ad0 ad1 in sy cs us sy id - # 1 0 0 596716 19560 32 0 0 0 33 8 0 0 11 220 277 0 0 99 - # 0 0 0 596716 19560 2 0 0 0 0 0 0 0 11 144 263 0 1 99 - 'freebsd' => ['vmstat -H 1 2 | tail -n1', '$8*$9'], - - # mirbsd: $8*$9 - # $ vmstat 1 2 - # procs memory page disks traps cpu - # r b w avm fre flt re pi po fr sr wd0 cd0 int sys cs us sy id - # 0 0 0 25776 164968 34 0 0 0 0 0 0 0 230 259 38 4 0 96 - # 0 0 0 25776 164968 24 0 0 0 0 0 0 0 237 275 37 0 0 100 - 'mirbsd' => ['vmstat 1 2 | tail -n1', '$8*$9'], - - # netbsd: $7*$8 - # $ vmstat 1 2 - # procs memory page disks faults cpu - # r b avm fre flt re pi po fr sr w0 w1 in sy cs us sy id - # 0 0 138452 6012 54 0 0 0 1 2 3 0 4 100 23 0 0 100 - # 0 0 138456 6008 1 0 0 0 0 0 0 0 7 26 19 0 0 100 - 'netbsd' => ['vmstat 1 2 | tail -n1', '$7*$8'], - - # openbsd: $8*$9 - # $ vmstat 1 2 - # procs memory page disks traps cpu - # r b w avm fre flt re pi po fr sr wd0 wd1 int sys cs us sy id - # 0 0 0 76596 109944 73 0 0 0 0 0 0 1 5 259 22 0 1 99 - # 0 0 0 76604 109936 24 0 0 0 0 0 0 0 7 114 20 0 1 99 - 'openbsd' => ['vmstat 1 2 | tail -n1', '$8*$9'], - - # hpux: $8*$9 - # $ vmstat 1 2 - # procs memory page faults cpu - # r b w avm free re at pi po fr de sr in sy cs us sy id - # 1 0 0 247211 216476 4 1 0 0 0 0 0 102 73005 54 6 11 83 - # 1 0 0 247211 216421 43 9 0 0 0 0 0 144 1675 96 25269512791222387000 25269512791222387000 105 - 'hpux' => ['vmstat 1 2 | tail -n1', '$8*$9'], - - # dec_osf (tru64): $11*$12 - # $ vmstat 1 2 - # Virtual Memory Statistics: (pagesize = 8192) - # procs memory pages intr cpu - # r w u act free wire fault cow zero react pin pout in sy cs us sy id - # 3 181 36 51K 1895 8696 348M 59M 122M 259 79M 0 5 218 302 4 1 94 - # 3 181 36 51K 1893 8696 3 15 21 0 28 0 4 81 321 1 1 98 - 'dec_osf' => ['vmstat 1 2 | tail -n1', '$11*$12'], - - # gnu (hurd): $7*$8 - # $ vmstat -k 1 2 - # (pagesize: 4, size: 512288, swap size: 894972) - # free actv inact wired zeroed react pgins pgouts pfaults cowpfs hrat caobj cache swfree - # 371940 30844 89228 20276 298348 0 48192 19016 756105 99808 98% 876 20628 894972 - # 371940 30844 89228 20276 +0 +0 +0 +0 +42 +2 98% 876 20628 894972 - 'gnu' => ['vmstat -k 1 2 | tail -n1', '$7*$8'], - - # -nto (qnx has no swap) - #-irix - #-svr5 (scosysv) - ); - my $perlscript = ""; - # Make a perl script that detects the OS ($^O) and runs - # the appropriate vmstat command - for my $os (keys %vmstat) { - $vmstat{$os}[1] =~ s/\$/\\\\\\\$/g; # $ => \\\$ - $perlscript .= 'if($^O eq "'.$os.'") { print `'.$vmstat{$os}[0].' | awk "{print ' . - $vmstat{$os}[1] . '}"` }'; - } - $script = "perl -e " . ::Q($perlscript); - } - return $script; - } -} - -sub too_fast_remote_login($) { - my $self = shift; - if($self->{'last_login_at'} and $self->{'time_to_login'}) { - # sshd normally allows 10 simultaneous logins - # A login takes time_to_login - # So time_to_login/5 should be safe - # If now <= last_login + time_to_login/5: Then it is too soon. - my $too_fast = (::now() <= $self->{'last_login_at'} - + $self->{'time_to_login'}/5); - ::debug("run", "Too fast? $too_fast "); - return $too_fast; - } else { - # No logins so far (or time_to_login not computed): it is not too fast - return 0; - } -} - -sub last_login_at($) { - my $self = shift; - return $self->{'last_login_at'}; -} - -sub set_last_login_at($$) { - my $self = shift; - $self->{'last_login_at'} = shift; -} - -sub loadavg_too_high($) { - my $self = shift; - my $loadavg = $self->loadavg(); - if(defined $loadavg) { - ::debug("load", "Load $loadavg > ",$self->max_loadavg()); - return $loadavg >= $self->max_loadavg(); - } else { - # Unknown load: Assume load is too high - return 1; - } -} - - - -sub loadavg($) { - # If the currently know loadavg is too old: - # Recompute a new one in the background - # The load average is computed as the number of processes waiting - # for disk or CPU right now. So it is the server load this instant - # and not averaged over several minutes. This is needed so GNU - # Parallel will at most start one job that will push the load over - # the limit. - # - # Returns: - # $last_loadavg = last load average computed (undef if none) - - my $self = shift; - sub loadavg_cmd() { - if(not $Global::loadavg_cmd) { - # aix => "ps -ae -o state,command" # state wrong - # bsd => "ps ax -o state,command" - # sysv => "ps -ef -o s -o comm" - # cygwin => perl -ne 'close STDERR; /Name/ and print"\n"; \ - # /(Name|Pid|Ppid|State):\s+(\S+)/ and print "$2\t";' /proc/*/status | - # awk '{print $2,$1}' - # dec_osf => bsd - # dragonfly => bsd - # freebsd => bsd - # gnu => bsd - # hpux => ps -el|awk '{print $2,$14,$15}' - # irix => ps -ef -o state -o comm - # linux => bsd - # minix => ps el|awk '{print \$1,\$11}' - # mirbsd => bsd - # netbsd => bsd - # openbsd => bsd - # solaris => sysv - # svr5 => sysv - # ultrix => ps -ax | awk '{print $3,$5}' - # unixware => ps -el|awk '{print $2,$14,$15}' - my $ps = ::spacefree(1,q{ - $sysv="ps -ef -o s -o comm"; - $sysv2="ps -ef -o state -o comm"; - $bsd="ps ax -o state,command"; - # Treat threads as processes - $bsd2="ps axH -o state,command"; - $psel="ps -el|awk '{ print \$2,\$14,\$15 }'"; - $cygwin=q{ perl -ne 'close STDERR; /Name/ and print"\n"; - /(Name|Pid|Ppid|State):\s+(\S+)/ and print "$2\t";' /proc/*/status | - awk '{print $2,$1}' }; - $dummy="echo S COMMAND;echo R dummy"; - %ps=( - # TODO Find better code for AIX/Android - 'aix' => "uptime", - 'android' => "uptime", - 'cygwin' => $cygwin, - 'darwin' => $bsd, - 'dec_osf' => $sysv2, - 'dragonfly' => $bsd, - 'freebsd' => $bsd2, - 'gnu' => $bsd, - 'hpux' => $psel, - 'irix' => $sysv2, - 'linux' => $bsd2, - 'minix' => "ps el|awk '{print \$1,\$11}'", - 'mirbsd' => $bsd, - 'msys' => $cygwin, - 'netbsd' => $bsd, - 'nto' => $dummy, - 'openbsd' => $bsd, - 'solaris' => $sysv, - 'svr5' => $psel, - 'ultrix' => "ps -ax | awk '{print \$3,\$5}'", - 'MSWin32' => $sysv, - ); - print `$ps{$^O}`; - }); - # The command is too long for csh, so base64_wrap the command - $Global::loadavg_cmd = $self->hexwrap($ps); - } - return $Global::loadavg_cmd; - } - # Should we update the loadavg file? - my $update_loadavg_file = 0; - if(open(my $load_fh, "<", $self->{'loadavg_file'})) { - local $/; # $/ = undef => slurp whole file - my $load_out = <$load_fh>; - close $load_fh; - if($load_out =~ /\S/) { - # Content can be empty if ~/ is on NFS - # due to reading being non-atomic. - # - # Count lines starting with D,O,R but command does not start with [ - my $load =()= ($load_out=~/(^\s?[DOR]\S* +(?=[^\[])\S)/gm); - if($load > 0) { - # load is overestimated by 1 - $self->{'loadavg'} = $load - 1; - ::debug("load", "New loadavg: ", $self->{'loadavg'},"\n"); - } elsif ($load_out=~/average: (\d+.\d+)/) { - # AIX does not support instant load average - # 04:11AM up 21 days, 12:55, 1 user, load average: 1.85, 1.57, 1.55 - $self->{'loadavg'} = $1; - } else { - ::die_bug("loadavg_invalid_content: " . - $self->{'loadavg_file'} . "\n$load_out"); - } - } - $update_loadavg_file = 1; - } else { - ::debug("load", "No loadavg file: ", $self->{'loadavg_file'}); - $self->{'loadavg'} = undef; - $update_loadavg_file = 1; - } - if($update_loadavg_file) { - ::debug("load", "Updating loadavg file", $self->{'loadavg_file'}, "\n"); - $self->{'last_loadavg_update'} = time; - my $dir = ::dirname($self->{'swap_activity_file'}); - -d $dir or eval { File::Path::mkpath($dir); }; - -w $dir or ::die_bug("Cannot write to $dir"); - my $cmd = ""; - if($self->{'string'} ne ":") { - $cmd = $self->wrap(loadavg_cmd()); - } else { - $cmd .= loadavg_cmd(); - } - # As the command can take long to run if run remote - # save it to a tmp file before moving it to the correct file - ::debug("load", "Update load\n"); - my $file = ::Q($self->{'loadavg_file'}); - # tmpfile on same filesystem as $file - my $tmpfile = $file.$$; - $ENV{'SSHPASS'} = $self->{'password'}; - ::qqx("($cmd > $tmpfile && mv $tmpfile $file || rm $tmpfile & )"); - } - return $self->{'loadavg'}; -} - -sub max_loadavg($) { - my $self = shift; - # If --load is a file it might be changed - if($Global::max_load_file) { - my $mtime = (stat($Global::max_load_file))[9]; - if($mtime > $Global::max_load_file_last_mod) { - $Global::max_load_file_last_mod = $mtime; - for my $sshlogin (values %Global::host) { - $sshlogin->set_max_loadavg(undef); - } - } - } - if(not defined $self->{'max_loadavg'}) { - $self->{'max_loadavg'} = - $self->compute_max_loadavg($opt::load); - } - ::debug("load", "max_loadavg: ", $self->string(), " ", $self->{'max_loadavg'}); - return $self->{'max_loadavg'}; -} - -sub set_max_loadavg($$) { - my $self = shift; - $self->{'max_loadavg'} = shift; -} - -sub compute_max_loadavg($) { - # Parse the max loadaverage that the user asked for using --load - # Returns: - # max loadaverage - my $self = shift; - my $loadspec = shift; - my $load; - if(defined $loadspec) { - if($loadspec =~ /^\+(\d+)$/) { - # E.g. --load +2 - my $j = $1; - $load = - $self->ncpus() + $j; - } elsif ($loadspec =~ /^-(\d+)$/) { - # E.g. --load -2 - my $j = $1; - $load = - $self->ncpus() - $j; - } elsif ($loadspec =~ /^(\d+)\%$/) { - my $j = $1; - $load = - $self->ncpus() * $j / 100; - } elsif ($loadspec =~ /^(\d+(\.\d+)?)$/) { - $load = $1; - } elsif (-f $loadspec) { - $Global::max_load_file = $loadspec; - $Global::max_load_file_last_mod = (stat($Global::max_load_file))[9]; - if(open(my $in_fh, "<", $Global::max_load_file)) { - my $opt_load_file = join("",<$in_fh>); - close $in_fh; - $load = $self->compute_max_loadavg($opt_load_file); - } else { - ::error("Cannot open $loadspec."); - ::wait_and_exit(255); - } - } else { - ::error("Parsing of --load failed."); - ::die_usage(); - } - if($load < 0.01) { - $load = 0.01; - } - } - return $load; -} - -sub time_to_login($) { - my $self = shift; - return $self->{'time_to_login'}; -} - -sub set_time_to_login($$) { - my $self = shift; - $self->{'time_to_login'} = shift; -} - -sub max_jobs_running($) { - my $self = shift; - if(not defined $self->{'max_jobs_running'}) { - my $nproc = $self->compute_number_of_processes($opt::jobs); - $self->set_max_jobs_running($nproc); - } - return $self->{'max_jobs_running'}; -} - -sub orig_max_jobs_running($) { - my $self = shift; - return $self->{'orig_max_jobs_running'}; -} - -sub compute_number_of_processes($) { - # Number of processes wanted and limited by system resources - # Returns: - # Number of processes - my $self = shift; - my $opt_P = shift; - my $wanted_processes = $self->user_requested_processes($opt_P); - if(not defined $wanted_processes) { - $wanted_processes = $Global::default_simultaneous_sshlogins; - } - ::debug("load", "Wanted procs: $wanted_processes\n"); - my $system_limit = - $self->processes_available_by_system_limit($wanted_processes); - ::debug("load", "Limited to procs: $system_limit\n"); - return $system_limit; -} - -{ - my @children; - my $max_system_proc_reached; - my $more_filehandles; - my %fh; - my $tmpfhname; - my $count_jobs_already_read; - my @jobs; - my $job; - my @args; - my $arg; - - sub reserve_filehandles($) { - # Reserves filehandle - my $n = shift; - for (1..$n) { - $more_filehandles &&= open($fh{$tmpfhname++}, "<", "/dev/null"); - } - } - - sub reserve_process() { - # Spawn a dummy process - my $child; - if($child = fork()) { - push @children, $child; - $Global::unkilled_children{$child} = 1; - } elsif(defined $child) { - # This is the child - # The child takes one process slot - # It will be killed later - $SIG{'TERM'} = $Global::original_sig{'TERM'}; - if($^O eq "cygwin" or $^O eq "msys" or $^O eq "nto") { - # The exec does not work on Cygwin and QNX - sleep 10101010; - } else { - # 'exec sleep' takes less RAM than sleeping in perl - exec 'sleep', 10101; - } - exit(0); - } else { - # Failed to spawn - $max_system_proc_reached = 1; - } - } - - sub get_args_or_jobs() { - # Get an arg or a job (depending on mode) - if($Global::semaphore or ($opt::pipe and not $opt::tee)) { - # Skip: No need to get args - return 1; - } elsif(defined $opt::retries and $count_jobs_already_read) { - # For retries we may need to run all jobs on this sshlogin - # so include the already read jobs for this sshlogin - $count_jobs_already_read--; - return 1; - } else { - if($opt::X or $opt::m) { - # The arguments may have to be re-spread over several jobslots - # So pessimistically only read one arg per jobslot - # instead of a full commandline - if($Global::JobQueue->{'commandlinequeue'}->{'arg_queue'}->empty()) { - if($Global::JobQueue->empty()) { - return 0; - } else { - $job = $Global::JobQueue->get(); - push(@jobs, $job); - return 1; - } - } else { - $arg = $Global::JobQueue->{'commandlinequeue'}->{'arg_queue'}->get(); - push(@args, $arg); - return 1; - } - } else { - # If there are no more command lines, then we have a process - # per command line, so no need to go further - if($Global::JobQueue->empty()) { - return 0; - } else { - $job = $Global::JobQueue->get(); - # Replacement must happen here due to seq() - $job and $job->replaced(); - push(@jobs, $job); - return 1; - } - } - } - } - - sub cleanup() { - # Cleanup: Close the files - for (values %fh) { close $_ } - # Cleanup: Kill the children - for my $pid (@children) { - kill 9, $pid; - waitpid($pid,0); - delete $Global::unkilled_children{$pid}; - } - # Cleanup: Unget the command_lines or the @args - $Global::JobQueue->{'commandlinequeue'}{'arg_queue'}->unget(@args); - @args = (); - $Global::JobQueue->unget(@jobs); - @jobs = (); - } - - sub processes_available_by_system_limit($) { - # If the wanted number of processes is bigger than the system limits: - # Limit them to the system limits - # Limits are: File handles, number of input lines, processes, - # and taking > 1 second to spawn 10 extra processes - # Returns: - # Number of processes - my $self = shift; - my $wanted_processes = shift; - my $system_limit = 0; - my $slow_spawning_warning_printed = 0; - my $time = time; - $more_filehandles = 1; - $tmpfhname = "TmpFhNamE"; - - # perl uses 7 filehandles for something? - # parallel uses 1 for memory_usage - # parallel uses 4 for ? - reserve_filehandles(12); - # Two processes for load avg and ? - reserve_process(); - reserve_process(); - - # For --retries count also jobs already run - $count_jobs_already_read = $Global::JobQueue->next_seq(); - my $wait_time_for_getting_args = 0; - my $start_time = time; - if($wanted_processes < $Global::infinity) { - $Global::dummy_jobs = 1; - } - while(1) { - $system_limit >= $wanted_processes and last; - not $more_filehandles and last; - $max_system_proc_reached and last; - - my $before_getting_arg = time; - if(!$Global::dummy_jobs) { - get_args_or_jobs() or last; - } - $wait_time_for_getting_args += time - $before_getting_arg; - $system_limit++; - - # Every simultaneous process uses 2 filehandles to write to - # and 2 filehandles to read from - reserve_filehandles(4); - - # System process limit - reserve_process(); - - my $forktime = time - $time - $wait_time_for_getting_args; - ::debug("run", "Time to fork $system_limit procs: ". - $wait_time_for_getting_args, " ", $forktime, - " (processes so far: ", $system_limit,")\n"); - if($system_limit > 10 and - $forktime > 1 and - $forktime > $system_limit * 0.01) { - # It took more than 0.01 second to fork a processes on avg. - # Give the user a warning. He can press Ctrl-C if this - # sucks. - ::warning_once( - "Starting $system_limit processes took > $forktime sec.", - "Consider adjusting -j. Press CTRL-C to stop."); - } - } - cleanup(); - - if($system_limit < $wanted_processes) { - # The system_limit is less than the wanted_processes - if($system_limit < 1 and not $Global::JobQueue->empty()) { - ::warning("Cannot spawn any jobs.", - "Try increasing 'ulimit -u' (try: ulimit -u `ulimit -Hu`)", - "or increasing 'nproc' in /etc/security/limits.conf", - "or increasing /proc/sys/kernel/pid_max"); - ::wait_and_exit(255); - } - if(not $more_filehandles) { - ::warning("Only enough file handles to run ". - $system_limit. " jobs in parallel.", - "Try running 'parallel -j0 -N $system_limit --pipe parallel -j0'", - "or increasing 'ulimit -n' (try: ulimit -n `ulimit -Hn`)", - "or increasing 'nofile' in /etc/security/limits.conf", - "or increasing /proc/sys/fs/file-max"); - } - if($max_system_proc_reached) { - ::warning("Only enough available processes to run ". - $system_limit. " jobs in parallel.", - "Try increasing 'ulimit -u' (try: ulimit -u `ulimit -Hu`)", - "or increasing 'nproc' in /etc/security/limits.conf", - "or increasing /proc/sys/kernel/pid_max"); - } - } - if($] == 5.008008 and $system_limit > 1000) { - # https://savannah.gnu.org/bugs/?36942 - $system_limit = 1000; - } - if($Global::JobQueue->empty()) { - $system_limit ||= 1; - } - if($self->string() ne ":" and - $system_limit > $Global::default_simultaneous_sshlogins) { - $system_limit = - $self->simultaneous_sshlogin_limit($system_limit); - } - return $system_limit; - } -} - -sub simultaneous_sshlogin_limit($) { - # Test by logging in wanted number of times simultaneously - # Returns: - # min($wanted_processes,$working_simultaneous_ssh_logins-1) - my $self = shift; - my $wanted_processes = shift; - if($self->{'time_to_login'}) { - return $wanted_processes; - } - - # Try twice because it guesses wrong sometimes - # Choose the minimal - my $ssh_limit = - ::min($self->simultaneous_sshlogin($wanted_processes), - $self->simultaneous_sshlogin($wanted_processes)); - if($ssh_limit < $wanted_processes) { - my $serverlogin = $self->string(); - ::warning("ssh to $serverlogin only allows ". - "for $ssh_limit simultaneous logins.", - "You may raise this by changing", - "/etc/ssh/sshd_config:MaxStartups and MaxSessions on $serverlogin.", - "You can also try --sshdelay 0.1", - "Using only ".($ssh_limit-1)." connections ". - "to avoid race conditions."); - # Race condition can cause problem if using all sshs. - if($ssh_limit > 1) { $ssh_limit -= 1; } - } - return $ssh_limit; -} - -sub simultaneous_sshlogin($) { - # Using $sshlogin try to see if we can do $wanted_processes - # simultaneous logins - # (ssh host echo simul-login & ssh host echo simul-login & ...) | - # grep simul|wc -l - # Input: - # $wanted_processes = Try for this many logins in parallel - # Returns: - # $ssh_limit = Number of succesful parallel logins - local $/ = "\n"; - my $self = shift; - my $wanted_processes = shift; - my $sshdelay = $opt::sshdelay ? "sleep $opt::sshdelay;" : ""; - # TODO sh -c wrapper to work for csh - my $cmd = ($sshdelay.$self->wrap("echo simultaneouslogin"). - "&1 &")x$wanted_processes; - ::debug("init","Trying $wanted_processes logins at ".$self->string()."\n"); - open (my $simul_fh, "-|", "($cmd)|grep simultaneouslogin | wc -l") or - ::die_bug("simultaneouslogin"); - my $ssh_limit = <$simul_fh>; - close $simul_fh; - chomp $ssh_limit; - return $ssh_limit; -} - -sub set_ncpus($$) { - my $self = shift; - $self->{'ncpus'} = shift; -} - -sub user_requested_processes($) { - # Parse the number of processes that the user asked for using -j - # Input: - # $opt_P = string formatted as for -P - # Returns: - # $processes = the number of processes to run on this sshlogin - my $self = shift; - my $opt_P = shift; - my $processes; - if(defined $opt_P) { - if (-f $opt_P) { - $Global::max_procs_file = $opt_P; - if(open(my $in_fh, "<", $Global::max_procs_file)) { - my $opt_P_file = join("",<$in_fh>); - close $in_fh; - if($opt_P_file !~ /\S/) { - ::warning_once("$Global::max_procs_file is empty. ". - "Treated as 100%"); - $opt_P_file = "100%"; - } - $processes = $self->user_requested_processes($opt_P_file); - } else { - ::error("Cannot open $opt_P."); - ::wait_and_exit(255); - } - } else { - if($opt_P eq "0") { - # -P 0 = infinity (or at least close) - $processes = $Global::infinity; - } else { - # -P +3 and -P -1 - $opt_P =~ s/^([-+])/\$self->ncpus()$1/; - # -P 40% - $opt_P =~ s:%$:*\$self->ncpus()/100:; - $processes = eval $opt_P; - if($processes <= 0) { - # Do not go below 1 - $processes = 1; - } - } - } - $processes = ::ceil($processes); - } - return $processes; -} - -sub ncpus($) { - # Number of CPU threads - # --use_sockets_instead_of_threads = count socket instead - # --use_cores_instead_of_threads = count physical cores instead - # Returns: - # $ncpus = number of cpu (threads) on this sshlogin - local $/ = "\n"; - my $self = shift; - if(not defined $self->{'ncpus'}) { - if($self->local()) { - if($opt::use_sockets_instead_of_threads) { - $self->{'ncpus'} = socket_core_thread()->{'sockets'}; - } elsif($opt::use_cores_instead_of_threads) { - $self->{'ncpus'} = socket_core_thread()->{'cores'}; - } else { - $self->{'ncpus'} = socket_core_thread()->{'threads'}; - } - } else { - my $ncpu; - $ENV{'SSHPASS'} = $self->{'password'}; - ::debug("init",("echo | ".$self->wrap("parallel --number-of-sockets"))); - if($opt::use_sockets_instead_of_threads - or - $opt::use_cpus_instead_of_cores) { - $ncpu = ::qqx("echo | ".$self->wrap("parallel --number-of-sockets")); - } elsif($opt::use_cores_instead_of_threads) { - $ncpu = ::qqx("echo | ".$self->wrap("parallel --number-of-cores")); - } else { - $ncpu = ::qqx("echo | ".$self->wrap("parallel --number-of-threads")); - } - chomp $ncpu; - if($ncpu =~ /^\s*[0-9]+\s*$/s) { - $self->{'ncpus'} = $ncpu; - } else { - ::warning("Could not figure out ". - "number of cpus on ".$self->string." ($ncpu). Using 1."); - $self->{'ncpus'} = 1; - } - } - } - return $self->{'ncpus'}; -} - - -sub nproc() { - # Returns: - # Number of threads using `nproc` - my $no_of_threads = ::qqx("nproc"); - chomp $no_of_threads; - return $no_of_threads; -} - -sub no_of_sockets() { - return socket_core_thread()->{'sockets'}; -} - -sub no_of_cores() { - return socket_core_thread()->{'cores'}; -} - -sub no_of_threads() { - return socket_core_thread()->{'threads'}; -} - -sub socket_core_thread() { - # Returns: - # { - # 'sockets' => #sockets = number of socket with CPU present - # 'cores' => #cores = number of physical cores - # 'threads' => #threads = number of compute cores (hyperthreading) - # 'active' => #taskset_threads = number of taskset limited cores - # } - my $cpu; - if ($^O eq 'linux') { - $cpu = sct_gnu_linux($cpu); - } elsif ($^O eq 'android') { - $cpu = sct_android($cpu); - } elsif ($^O eq 'freebsd') { - $cpu = sct_freebsd($cpu); - } elsif ($^O eq 'netbsd') { - $cpu = sct_netbsd($cpu); - } elsif ($^O eq 'openbsd') { - $cpu = sct_openbsd($cpu); - } elsif ($^O eq 'gnu') { - $cpu = sct_hurd($cpu); - } elsif ($^O eq 'darwin') { - $cpu = sct_darwin($cpu); - } elsif ($^O eq 'solaris') { - $cpu = sct_solaris($cpu); - } elsif ($^O eq 'aix') { - $cpu = sct_aix($cpu); - } elsif ($^O eq 'hpux') { - $cpu = sct_hpux($cpu); - } elsif ($^O eq 'nto') { - $cpu = sct_qnx($cpu); - } elsif ($^O eq 'svr5') { - $cpu = sct_openserver($cpu); - } elsif ($^O eq 'irix') { - $cpu = sct_irix($cpu); - } elsif ($^O eq 'dec_osf') { - $cpu = sct_tru64($cpu); - } else { - # Try all methods until we find something that works - $cpu = (sct_gnu_linux($cpu) - || sct_android($cpu) - || sct_freebsd($cpu) - || sct_netbsd($cpu) - || sct_openbsd($cpu) - || sct_hurd($cpu) - || sct_darwin($cpu) - || sct_solaris($cpu) - || sct_aix($cpu) - || sct_hpux($cpu) - || sct_qnx($cpu) - || sct_openserver($cpu) - || sct_irix($cpu) - || sct_tru64($cpu) - ); - } - if(not $cpu) { - # Fall back: Set all to nproc - my $nproc = nproc(); - if($nproc) { - $cpu->{'sockets'} = - $cpu->{'cores'} = - $cpu->{'threads'} = - $cpu->{'active'} = - $nproc; - } - } - if(not $cpu) { - ::warning("Cannot figure out number of cpus. Using 1."); - $cpu->{'sockets'} = - $cpu->{'cores'} = - $cpu->{'threads'} = - $cpu->{'active'} = - 1 - } - $cpu->{'sockets'} ||= 1; - $cpu->{'threads'} ||= $cpu->{'cores'}; - $cpu->{'active'} ||= $cpu->{'threads'}; - chomp($cpu->{'sockets'}, - $cpu->{'cores'}, - $cpu->{'threads'}, - $cpu->{'active'}); - # Choose minimum of active and actual - my $mincpu; - $mincpu->{'sockets'} = ::min($cpu->{'sockets'},$cpu->{'active'}); - $mincpu->{'cores'} = ::min($cpu->{'cores'},$cpu->{'active'}); - $mincpu->{'threads'} = ::min($cpu->{'threads'},$cpu->{'active'}); - return $mincpu; -} - -sub sct_gnu_linux($) { - # Returns: - # { 'sockets' => #sockets - # 'cores' => #cores - # 'threads' => #threads - # 'active' => #taskset_threads } - my $cpu = shift; - - sub read_topology($) { - my $prefix = shift; - my %sibiling; - my %socket; - my $thread; - for($thread = 0; - -r "$prefix/cpu$thread/topology/physical_package_id"; - $thread++) { - open(my $fh,"<", - "$prefix/cpu$thread/topology/physical_package_id") - || die; - $socket{<$fh>}++; - close $fh; - } - for($thread = 0; - -r "$prefix/cpu$thread/topology/thread_siblings"; - $thread++) { - open(my $fh,"<", - "$prefix/cpu$thread/topology/thread_siblings") - || die; - $sibiling{<$fh>}++; - close $fh; - } - $cpu->{'sockets'} = keys %socket; - $cpu->{'cores'} = keys %sibiling; - $cpu->{'threads'} = $thread; - } - - sub read_cpuinfo(@) { - my @cpuinfo = @_; - $cpu->{'sockets'} = 0; - $cpu->{'cores'} = 0; - $cpu->{'threads'} = 0; - my %seen; - my %phy_seen; - my $physicalid; - for(@cpuinfo) { - # physical id : 0 - if(/^physical id.*[:](.*)/) { - $physicalid = $1; - if(not $phy_seen{$1}++) { - $cpu->{'sockets'}++; - } - } - # core id : 3 - if(/^core id.*[:](.*)/ and not $seen{$physicalid,$1}++) { - $cpu->{'cores'}++; - } - # processor : 2 - /^processor.*[:]\s*\d/i and $cpu->{'threads'}++; - } - $cpu->{'cores'} ||= $cpu->{'threads'}; - $cpu->{'cpus'} ||= $cpu->{'threads'}; - $cpu->{'sockets'} ||= 1; - } - - sub read_lscpu(@) { - my @lscpu = @_; - my $threads_per_core; - my $cores_per_socket; - for(@lscpu) { - /^CPU.s.:\s*(\d+)/ and $cpu->{'threads'} = $1; - /^Thread.s. per core:\s*(\d+)/ and $threads_per_core = $1; - /^Core.s. per socket:\s*(\d+)/ and $cores_per_socket = $1; - /^(CPU )?Socket.s.:\s*(\d+)/i and $cpu->{'sockets'} = $2; - } - if($cores_per_socket and $cpu->{'sockets'}) { - $cpu->{'cores'} = $cores_per_socket * $cpu->{'sockets'}; - } - if($threads_per_core and $cpu->{'cores'}) { - $cpu->{'threads'} = $threads_per_core * $cpu->{'cores'}; - } - if($threads_per_core and $cpu->{'threads'}) { - $cpu->{'cores'} = $cpu->{'threads'} / $threads_per_core; - } - $cpu->{'cpus'} ||= $cpu->{'threads'}; - } - - local $/ = "\n"; # If delimiter is set, then $/ will be wrong - my @cpuinfo; - my @lscpu; - if($ENV{'PARALLEL_CPUINFO'}) { - # Use CPUINFO from environment - used for testing only - read_cpuinfo(split/(?<=\n)/,$ENV{'PARALLEL_CPUINFO'}); - } elsif($ENV{'PARALLEL_LSCPU'}) { - # Use LSCPU from environment - used for testing only - read_lscpu(split/(?<=\n)/,$ENV{'PARALLEL_LSCPU'}); - } elsif(-r "$ENV{'PARALLEL_CPUPREFIX'}/cpu0/topology/thread_siblings") { - # Use CPUPREFIX from environment - used for testing only - read_topology($ENV{'PARALLEL_CPUPREFIX'}); - } elsif($cpu->{'sockets'} and $cpu->{'cores'} and $cpu->{'threads'}) { - # Skip /proc/cpuinfo - already set - } else { - # Not debugging: Look at this computer - if(!($cpu->{'sockets'} and $cpu->{'cores'} and $cpu->{'threads'}) - and - open(my $in_fh, "-|", "lscpu")) { - # Parse output from lscpu - read_lscpu(<$in_fh>); - close $in_fh; - } - if(!($cpu->{'sockets'} and $cpu->{'cores'} and $cpu->{'threads'}) - and - -r "/sys/devices/system/cpu/cpu0/topology/thread_siblings") { - read_topology("/sys/devices/system/cpu"); - } - if(!($cpu->{'sockets'} and $cpu->{'cores'} and $cpu->{'threads'}) - and - open(my $in_fh, "<", "/proc/cpuinfo")) { - # Read /proc/cpuinfo - read_cpuinfo(<$in_fh>); - close $in_fh; - } - } - if(-e "/proc/self/status" - and not $ENV{'PARALLEL_CPUINFO'} - and not $ENV{'PARALLEL_LSCPU'}) { - # if 'taskset' is used to limit number of threads - if(open(my $in_fh, "<", "/proc/self/status")) { - while(<$in_fh>) { - if(/^Cpus_allowed:\s*(\S+)/) { - my $a = $1; - $a =~ tr/,//d; - $cpu->{'active'} = unpack ("%32b*", pack ("H*",$a)); - } - } - close $in_fh; - } - } - return $cpu; -} - -sub sct_android($) { - # Returns: - # { 'sockets' => #sockets - # 'cores' => #cores - # 'threads' => #threads - # 'active' => #taskset_threads } - # Use GNU/Linux - return sct_gnu_linux($_[0]); -} - -sub sct_freebsd($) { - # Returns: - # { 'sockets' => #sockets - # 'cores' => #cores - # 'threads' => #threads - # 'active' => #taskset_threads } - local $/ = "\n"; - my $cpu = shift; - $cpu->{'cores'} ||= - (::qqx(qq{ sysctl -a dev.cpu | grep \%parent | awk '{ print \$2 }' | uniq | wc -l | awk '{ print \$1 }' }) - or - ::qqx(qq{ sysctl hw.ncpu | awk '{ print \$2 }' })); - $cpu->{'threads'} ||= - (::qqx(qq{ sysctl hw.ncpu | awk '{ print \$2 }' }) - or - ::qqx(qq{ sysctl -a dev.cpu | grep \%parent | awk '{ print \$2 }' | uniq | wc -l | awk '{ print \$1 }' })); - return $cpu; -} - -sub sct_netbsd($) { - # Returns: - # { 'sockets' => #sockets - # 'cores' => #cores - # 'threads' => #threads - # 'active' => #taskset_threads } - local $/ = "\n"; - my $cpu = shift; - $cpu->{'cores'} ||= ::qqx("sysctl -n hw.ncpu"); - return $cpu; -} - -sub sct_openbsd($) { - # Returns: - # { 'sockets' => #sockets - # 'cores' => #cores - # 'threads' => #threads - # 'active' => #taskset_threads } - local $/ = "\n"; - my $cpu = shift; - $cpu->{'cores'} ||= ::qqx('sysctl -n hw.ncpu'); - return $cpu; -} - -sub sct_hurd($) { - # Returns: - # { 'sockets' => #sockets - # 'cores' => #cores - # 'threads' => #threads - # 'active' => #taskset_threads } - local $/ = "\n"; - my $cpu = shift; - $cpu->{'cores'} ||= ::qqx("nproc"); - return $cpu; -} - -sub sct_darwin($) { - # Returns: - # { 'sockets' => #sockets - # 'cores' => #cores - # 'threads' => #threads - # 'active' => #taskset_threads } - local $/ = "\n"; - my $cpu = shift; - $cpu->{'cores'} ||= - (::qqx('sysctl -n hw.physicalcpu') - or - ::qqx(qq{ sysctl -a hw | grep [^a-z]physicalcpu[^a-z] | awk '{ print \$2 }' })); - $cpu->{'threads'} ||= - (::qqx('sysctl -n hw.logicalcpu') - or - ::qqx(qq{ sysctl -a hw | grep [^a-z]logicalcpu[^a-z] | awk '{ print \$2 }' })); - return $cpu; -} - -sub sct_solaris($) { - # Returns: - # { 'sockets' => #sockets - # 'cores' => #cores - # 'threads' => #threads - # 'active' => #taskset_threads } - local $/ = "\n"; - my $cpu = shift; - if(not $cpu->{'cores'}) { - if(-x "/usr/bin/kstat") { - my @chip_id = ::qqx("/usr/bin/kstat cpu_info|grep chip_id"); - if($#chip_id >= 0) { - $cpu->{'sockets'} ||= $#chip_id +1; - } - my @core_id = ::qqx("/usr/bin/kstat -m cpu_info|grep -w core_id|uniq"); - if($#core_id >= 0) { - $cpu->{'cores'} ||= $#core_id +1; - } - } - if(-x "/usr/sbin/psrinfo") { - my @psrinfo = ::qqx("/usr/sbin/psrinfo -p"); - if($#psrinfo >= 0) { - $cpu->{'sockets'} ||= $psrinfo[0]; - } - } - if(-x "/usr/sbin/prtconf") { - my @prtconf = ::qqx("/usr/sbin/prtconf | grep cpu..instance"); - if($#prtconf >= 0) { - $cpu->{'cores'} ||= $#prtconf +1; - } - } - } - return $cpu; -} - -sub sct_aix($) { - # Returns: - # { 'sockets' => #sockets - # 'cores' => #cores - # 'threads' => #threads - # 'active' => #taskset_threads } - local $/ = "\n"; - my $cpu = shift; - if(not $cpu->{'cores'}) { - if(-x "/usr/sbin/lscfg") { - if(open(my $in_fh, "-|", - "/usr/sbin/lscfg -vs |grep proc | wc -l|tr -d ' '")) { - $cpu->{'cores'} = <$in_fh>; - close $in_fh; - } - } - } - if(not $cpu->{'threads'}) { - if(-x "/usr/bin/vmstat") { - if(open(my $in_fh, "-|", "/usr/bin/vmstat 1 1")) { - while(<$in_fh>) { - /lcpu=([0-9]*) / and $cpu->{'threads'} = $1; - } - close $in_fh; - } - } - } - return $cpu; -} - -sub sct_hpux($) { - # Returns: - # { 'sockets' => #sockets - # 'cores' => #cores - # 'threads' => #threads - # 'active' => #taskset_threads } - local $/ = "\n"; - my $cpu = shift; - $cpu->{'cores'} ||= - ::qqx(qq{ /usr/bin/mpsched -s 2>&1 | grep 'Locality Domain Count' | awk '{ print \$4 }'}); - $cpu->{'threads'} ||= - ::qqx(qq{ /usr/bin/mpsched -s 2>&1 | perl -ne '/Processor Count\\D+(\\d+)/ and print "\$1"'}); - return $cpu; -} - -sub sct_qnx($) { - # Returns: - # { 'sockets' => #sockets - # 'cores' => #cores - # 'threads' => #threads - # 'active' => #taskset_threads } - local $/ = "\n"; - my $cpu = shift; - # BUG: It is not known how to calculate this. - - return $cpu; -} - -sub sct_openserver($) { - # Returns: - # { 'sockets' => #sockets - # 'cores' => #cores - # 'threads' => #threads - # 'active' => #taskset_threads } - local $/ = "\n"; - my $cpu = shift; - if(not $cpu->{'cores'}) { - if(-x "/usr/sbin/psrinfo") { - my @psrinfo = ::qqx("/usr/sbin/psrinfo"); - if($#psrinfo >= 0) { - $cpu->{'cores'} = $#psrinfo +1; - } - } - } - $cpu->{'sockets'} ||= $cpu->{'cores'}; - return $cpu; -} - -sub sct_irix($) { - # Returns: - # { 'sockets' => #sockets - # 'cores' => #cores - # 'threads' => #threads - # 'active' => #taskset_threads } - local $/ = "\n"; - my $cpu = shift; - $cpu->{'cores'} ||= - ::qqx(qq{ hinv | grep HZ | grep Processor | awk '{print \$1}' }); - return $cpu; -} - -sub sct_tru64($) { - # Returns: - # { 'sockets' => #sockets - # 'cores' => #cores - # 'threads' => #threads - # 'active' => #taskset_threads } - local $/ = "\n"; - my $cpu = shift; - $cpu->{'cores'} ||= ::qqx("sizer -pr"); - $cpu->{'sockets'} ||= $cpu->{'cores'}; - $cpu->{'threads'} ||= $cpu->{'cores'}; - - return $cpu; -} - -sub sshcommand($) { - # Returns: - # $sshcommand = the command (incl options) to run when using ssh - my $self = shift; - if (not defined $self->{'sshcommand'}) { - ::die_bug("sshcommand not set"); - } - return $self->{'sshcommand'}; -} - -sub local($) { - my $self = shift; - return $self->{'local'}; -} - -sub control_path_dir($) { - # Returns: - # $control_path_dir = dir of control path (for -M) - my $self = shift; - if(not defined $self->{'control_path_dir'}) { - $self->{'control_path_dir'} = - # Use $ENV{'TMPDIR'} as that is typically not - # NFS mounted. - # The file system must support UNIX domain sockets - File::Temp::tempdir($ENV{'TMPDIR'} - . "/ctrlpath-XXXX", - CLEANUP => 1); - } - return $self->{'control_path_dir'}; -} - -sub rsync_transfer_cmd($) { - # Command to run to transfer a file - # Input: - # $file = filename of file to transfer - # $workdir = destination dir - # Returns: - # $cmd = rsync command to run to transfer $file ("" if unreadable) - my $self = shift; - my $file = shift; - my $workdir = shift; - if(not -r $file) { - ::warning($file. " is not readable and will not be transferred."); - return "true"; - } - my $rsync_destdir; - my $relpath = ($file !~ m:^/:) || ($file =~ m:/\./:); # Is the path relative or /./? - if($relpath) { - $rsync_destdir = ::shell_quote_file($workdir); - } else { - # rsync /foo/bar / - $rsync_destdir = "/"; - } - $file = ::shell_quote_file($file); - # Make dir if it does not exist - return($self->wrap("mkdir -p $rsync_destdir") . " && " . - $self->rsync()." $file ".$self->{'host'}.":$rsync_destdir"); -} - -{ - my $rsync_fix; - my $rsync_version; - - sub rsync($) { - # rsync 3.1.x uses protocol 31 which is unsupported by 2.5.7. - # If the version >= 3.1.0: downgrade to protocol 30 - # rsync 3.2.4 introduces a quoting bug: Add --old-args for that - # Returns: - # $rsync = "rsync" or "rsync --protocol 30 --old-args" - sub rsync_version { - if(not $rsync_version) { - my @out = `rsync --version`; - if(not @out) { - if(::which("rsync")) { - ::die_bug("'rsync --version' gave no output."); - } else { - ::error("'rsync' is not in \$PATH."); - ::wait_and_exit(255); - } - } - for (@out) { - # rsync version 3.1.3 protocol version 31 - # rsync version v3.2.3 protocol version 31 - if(/version v?(\d+)\.(\d+)(\.(\d+))?/) { - # 3.2.27 => 03.0227 - $rsync_version = sprintf "%02d.%02d%02d",$1,$2,$4; - } - } - $rsync_version or - ::die_bug("Cannot figure out version of rsync: @out"); - } - } - - sub rsync_fixup { - # rsync 3.1.x uses protocol 31 which is unsupported by 2.5.7. - # If the version >= 3.1.0: downgrade to protocol 30 - # Returns: - # $rsync = "rsync" or "rsync --protocol 30" - if(not $rsync_fix) { - rsync_version(); - if($rsync_version >= 3.01) { - # Version 3.1.0 or later: Downgrade to protocol 30 - $rsync_fix .= " --protocol 30"; - } - if($rsync_version >= 3.0204) { - # Version 3.2.4 .. 3.2.8: --old-args - $rsync_fix .= " --old-args"; - } - } - return $rsync_fix; - } - my $self = shift; - - return "rsync".rsync_fixup()." ".$ENV{'PARALLEL_RSYNC_OPTS'}. - " -e".::Q($self->sshcmd()); - } -} - -sub cleanup_cmd($$$) { - # Command to run to remove the remote file - # Input: - # $file = filename to remove - # $workdir = destination dir - # Returns: - # $cmd = ssh command to run to remove $file and empty parent dirs - my $self = shift; - my $file = shift; - my $workdir = shift; - my $f = $file; - if($f =~ m:/\./:) { - # foo/bar/./baz/quux => workdir/baz/quux - # /foo/bar/./baz/quux => workdir/baz/quux - $f =~ s:.*/\./:$workdir/:; - } elsif($f =~ m:^[^/]:) { - # foo/bar => workdir/foo/bar - $f = $workdir."/".$f; - } - my @subdirs = split m:/:, ::dirname($f); - my @rmdir; - my $dir = ""; - for(@subdirs) { - $dir .= $_."/"; - unshift @rmdir, ::shell_quote_file($dir); - } - my $rmdir = @rmdir ? "rmdir @rmdir 2>/dev/null;" : ""; - if(defined $opt::workdir and $opt::workdir eq "...") { - $rmdir .= "rm -rf " . ::shell_quote_file($workdir).';'; - } - my $rmf = "sh -c ". - ::Q("rm -f ".::shell_quote_file($f)." 2>/dev/null;".$rmdir); - return $self->wrap(::Q($rmf)); -} - -package JobQueue; - -sub new($) { - my $class = shift; - my $commandref = shift; - my $read_from = shift; - my $context_replace = shift; - my $max_number_of_args = shift; - my $transfer_files = shift; - my $return_files = shift; - my $template_names = shift; - my $template_contents = shift; - my $commandlinequeue = CommandLineQueue->new - ($commandref, $read_from, $context_replace, $max_number_of_args, - $transfer_files, $return_files, $template_names, $template_contents); - my @unget = (); - return bless { - 'unget' => \@unget, - 'commandlinequeue' => $commandlinequeue, - 'this_job_no' => 0, - 'total_jobs' => undef, - }, ref($class) || $class; -} - -sub get($) { - my $self = shift; - - $self->{'this_job_no'}++; - if(@{$self->{'unget'}}) { - my $job = shift @{$self->{'unget'}}; - # {%} may have changed, so flush computed values - $job && $job->flush_cache(); - return $job; - } else { - my $commandline = $self->{'commandlinequeue'}->get(); - if(defined $commandline) { - return Job->new($commandline); - } else { - $self->{'this_job_no'}--; - return undef; - } - } -} - -sub unget($) { - my $self = shift; - unshift @{$self->{'unget'}}, @_; - $self->{'this_job_no'} -= @_; -} - -sub empty($) { - my $self = shift; - my $empty = (not @{$self->{'unget'}}) && - $self->{'commandlinequeue'}->empty(); - ::debug("run", "JobQueue->empty $empty "); - return $empty; -} - -sub total_jobs($) { - my $self = shift; - if(not defined $self->{'total_jobs'}) { - if($opt::pipe and not $opt::tee) { - ::error("--pipe is incompatible with --eta/--bar/--shuf"); - ::wait_and_exit(255); - } - if($opt::totaljobs) { - $self->{'total_jobs'} = $opt::totaljobs; - } elsif($opt::sqlworker) { - $self->{'total_jobs'} = $Global::sql->total_jobs(); - } else { - my $record; - my @arg_records; - my $record_queue = $self->{'commandlinequeue'}{'arg_queue'}; - my $start = time; - while($record = $record_queue->get()) { - push @arg_records, $record; - if(time - $start > 10) { - ::warning("Reading ".scalar(@arg_records). - " arguments took longer than 10 seconds."); - $opt::eta && ::warning("Consider removing --eta."); - $opt::bar && ::warning("Consider removing --bar."); - $opt::shuf && ::warning("Consider removing --shuf."); - last; - } - } - while($record = $record_queue->get()) { - push @arg_records, $record; - } - if($opt::shuf and @arg_records) { - my $i = @arg_records; - while (--$i) { - my $j = int rand($i+1); - @arg_records[$i,$j] = @arg_records[$j,$i]; - } - } - $record_queue->unget(@arg_records); - # $#arg_records = number of args - 1 - # We have read one @arg_record for this job (so add 1 more) - my $num_args = $#arg_records + 2; - # This jobs is not started so -1 - my $started_jobs = $self->{'this_job_no'} - 1; - my $max_args = ::max($Global::max_number_of_args,1); - $self->{'total_jobs'} = ::ceil($num_args / $max_args) - + $started_jobs; - ::debug("init","Total jobs: ".$self->{'total_jobs'}. - " ($num_args/$max_args + $started_jobs)\n"); - } - } - return $self->{'total_jobs'}; -} - -sub flush_total_jobs($) { - # Unset total_jobs to force recomputing - my $self = shift; - ::debug("init","flush Total jobs: "); - $self->{'total_jobs'} = undef; -} - -sub next_seq($) { - my $self = shift; - - return $self->{'commandlinequeue'}->seq(); -} - -sub quote_args($) { - my $self = shift; - return $self->{'commandlinequeue'}->quote_args(); -} - - -package Job; - -sub new($) { - my $class = shift; - my $commandlineref = shift; - return bless { - 'commandline' => $commandlineref, # CommandLine object - 'workdir' => undef, # --workdir - # filehandle for stdin (used for --pipe) - # filename for writing stdout to (used for --files) - # remaining data not sent to stdin (used for --pipe) - # tmpfiles to cleanup when job is done - 'unlink' => [], - # amount of data sent via stdin (used for --pipe) - 'transfersize' => 0, # size of files using --transfer - 'returnsize' => 0, # size of files using --return - 'pid' => undef, - # hash of { SSHLogins => number of times the command failed there } - 'failed' => undef, - 'sshlogin' => undef, - # The commandline wrapped with rsync and ssh - 'sshlogin_wrap' => undef, - 'exitstatus' => undef, - 'exitsignal' => undef, - # Timestamp for timeout if any - 'timeout' => undef, - 'virgin' => 1, - # Output used for SQL and CSV-output - 'output' => { 1 => [], 2 => [] }, - 'halfline' => { 1 => [], 2 => [] }, - }, ref($class) || $class; -} - -sub flush_cache($) { - my $self = shift; - $self->{'commandline'}->flush_cache(); -} - -sub replaced($) { - my $self = shift; - $self->{'commandline'} or ::die_bug("commandline empty"); - return $self->{'commandline'}->replaced(); -} - -{ - my $next_available_row; - - sub row($) { - my $self = shift; - if(not defined $self->{'row'}) { - if($opt::keeporder) { - $self->{'row'} = $self->seq(); - } else { - $self->{'row'} = ++$next_available_row; - } - } - return $self->{'row'}; - } -} - -sub seq($) { - my $self = shift; - return $self->{'commandline'}->seq(); -} - -sub set_seq($$) { - my $self = shift; - return $self->{'commandline'}->set_seq(shift); -} - -sub slot($) { - my $self = shift; - return $self->{'commandline'}->slot(); -} - -sub free_slot($) { - my $self = shift; - push @Global::slots, $self->slot(); -} - -{ - my($cattail); - - sub cattail() { - # Returns: - # $cattail = perl program for: - # cattail "decomp-prg" wpid [file_stdin] [file_to_unlink] - # decomp-prg = decompress program - # wpid = pid of writer program - # file_stdin = file_to_decompress - # file_to_unlink = unlink this file - if(not $cattail) { - $cattail = q{ - # cat followed by tail (possibly with rm as soon at the file is opened) - # If $writerpid dead: finish after this round - use Fcntl; - $|=1; - - my ($comfile, $cmd, $writerpid, $read_file, $unlink_file) = @ARGV; - if($read_file) { - open(IN,"<",$read_file) || die("cattail: Cannot open $read_file"); - } else { - *IN = *STDIN; - } - while(! -s $comfile) { - # Writer has not opened the buffer file, so we cannot remove it yet - $sleep = ($sleep < 30) ? ($sleep * 1.001 + 0.01) : ($sleep); - usleep($sleep); - } - # The writer and we have both opened the file, so it is safe to unlink it - unlink $unlink_file; - unlink $comfile; - - my $first_round = 1; - my $flags; - fcntl(IN, F_GETFL, $flags) || die $!; # Get the current flags on the filehandle - $flags |= O_NONBLOCK; # Add non-blocking to the flags - fcntl(IN, F_SETFL, $flags) || die $!; # Set the flags on the filehandle - - while(1) { - # clear EOF - seek(IN,0,1); - my $writer_running = kill 0, $writerpid; - $read = sysread(IN,$buf,131072); - if($read) { - if($first_round) { - # Only start the command if there any input to process - $first_round = 0; - open(OUT,"|-",$cmd) || die("cattail: Cannot run $cmd"); - } - - # Blocking print - while($buf) { - my $bytes_written = syswrite(OUT,$buf); - # syswrite may be interrupted by SIGHUP - substr($buf,0,$bytes_written) = ""; - } - # Something printed: Wait less next time - $sleep /= 2; - } else { - if(eof(IN) and not $writer_running) { - # Writer dead: There will never be sent more to the decompressor - close OUT; - exit; - } - # TODO This could probably be done more efficiently using select(2) - # Nothing read: Wait longer before next read - # Up to 100 milliseconds - $sleep = ($sleep < 100) ? ($sleep * 1.001 + 0.01) : ($sleep); - usleep($sleep); - } - } - - sub usleep { - # Sleep this many milliseconds. - my $secs = shift; - select(undef, undef, undef, $secs/1000); - } - }; - $cattail =~ s/#.*//mg; - $cattail =~ s/\s+/ /g; - } - return $cattail; - } -} - -sub openoutputfiles($) { - # Open files for STDOUT and STDERR - # Set file handles in $self->fh - my $self = shift; - my ($outfhw, $errfhw, $outname, $errname); - - if($opt::latestline) { - # Do not save to files: Use non-blocking pipe - my ($outfhr, $errfhr); - pipe($outfhr, $outfhw) || die; - $self->set_fh(1,'w',$outfhw); - $self->set_fh(2,'w',$outfhw); - $self->set_fh(1,'r',$outfhr); - $self->set_fh(2,'r',$outfhr); - # Make it possible to read non-blocking from the pipe - for my $fdno (1,2) { - ::set_fh_non_blocking($self->fh($fdno,'r')); - } - # Return immediately because we do not need setting filenames - return; - } elsif($Global::linebuffer and not - ($opt::keeporder or $Global::files or $opt::results or - $opt::compress or $opt::compress_program or - $opt::decompress_program)) { - # Do not save to files: Use non-blocking pipe - my ($outfhr, $errfhr); - pipe($outfhr, $outfhw) || die; - pipe($errfhr, $errfhw) || die; - $self->set_fh(1,'w',$outfhw); - $self->set_fh(2,'w',$errfhw); - $self->set_fh(1,'r',$outfhr); - $self->set_fh(2,'r',$errfhr); - # Make it possible to read non-blocking from the pipe - for my $fdno (1,2) { - ::set_fh_non_blocking($self->fh($fdno,'r')); - } - # Return immediately because we do not need setting filenames - return; - } elsif($opt::results and not $Global::csvsep and not $Global::jsonout) { - # If --results, but not --results *.csv/*.tsv - my $out = $self->{'commandline'}->results_out(); - my $seqname; - if($out eq $opt::results or $out =~ m:/$:) { - # $opt::results = simple string or ending in / - # => $out is a dir/ - # prefix/name1/val1/name2/val2/seq - $seqname = $out."seq"; - # prefix/name1/val1/name2/val2/stdout - $outname = $out."stdout"; - # prefix/name1/val1/name2/val2/stderr - $errname = $out."stderr"; - } else { - # $opt::results = replacement string not ending in / - # => $out is a file - $outname = $out; - $errname = "$out.err"; - $seqname = "$out.seq"; - } - my $seqfhw; - if(not open($seqfhw, "+>", $seqname)) { - ::error("Cannot write to `$seqname'."); - ::wait_and_exit(255); - } - print $seqfhw $self->seq(); - close $seqfhw; - if(not open($outfhw, "+>", $outname)) { - ::error("Cannot write to `$outname'."); - ::wait_and_exit(255); - } - if(not open($errfhw, "+>", $errname)) { - ::error("Cannot write to `$errname'."); - ::wait_and_exit(255); - } - $self->set_fh(1,"unlink",""); - $self->set_fh(2,"unlink",""); - if($opt::sqlworker) { - # Save the filenames in SQL table - $Global::sql->update("SET Stdout = ?, Stderr = ? ". - "WHERE Seq = ". $self->seq(), - $outname, $errname); - } - } elsif(not $opt::ungroup) { - # To group we create temporary files for STDOUT and STDERR - # To avoid the cleanup unlink the files immediately (but keep them open) - if($Global::files) { - ($outfhw, $outname) = ::tmpfile(SUFFIX => ".par"); - ($errfhw, $errname) = ::tmpfile(SUFFIX => ".par"); - # --files => only remove stderr - $self->set_fh(1,"unlink",""); - $self->set_fh(2,"unlink",$errname); - } else { - ($outfhw, $outname) = ::tmpfile(SUFFIX => ".par"); - ($errfhw, $errname) = ::tmpfile(SUFFIX => ".par"); - $self->set_fh(1,"unlink",$outname); - $self->set_fh(2,"unlink",$errname); - } - } else { - # --ungroup - open($outfhw,">&",$Global::fh{1}) || die; - open($errfhw,">&",$Global::fh{2}) || die; - # File name must be empty as it will otherwise be printed - $outname = ""; - $errname = ""; - $self->set_fh(1,"unlink",$outname); - $self->set_fh(2,"unlink",$errname); - } - # Set writing FD - $self->set_fh(1,'w',$outfhw); - $self->set_fh(2,'w',$errfhw); - $self->set_fh(1,'name',$outname); - $self->set_fh(2,'name',$errname); - if($opt::compress) { - $self->filter_through_compress(); - } elsif(not $opt::ungroup) { - $self->grouped(); - } - if($Global::linebuffer) { - # Make it possible to read non-blocking from - # the buffer files - # Used for --linebuffer with -k, --files, --res, --compress* - for my $fdno (1,2) { - ::set_fh_non_blocking($self->fh($fdno,'r')); - } - } -} - -sub print_verbose_dryrun($) { - # If -v set: print command to stdout (possibly buffered) - # This must be done before starting the command - my $self = shift; - if($Global::verbose or $opt::dryrun) { - my $fh = $self->fh(1,"w"); - if($Global::verbose <= 1) { - print $fh $self->replaced(),"\n"; - } else { - # Verbose level > 1: Print the rsync and stuff - print $fh $self->wrapped(),"\n"; - } - } - if($opt::sqlworker) { - $Global::sql->update("SET Command = ? WHERE Seq = ".$self->seq(), - $self->replaced()); - } -} - -sub add_rm($) { - # Files to remove when job is done - my $self = shift; - push @{$self->{'unlink'}}, @_; -} - -sub get_rm($) { - # Files to remove when job is done - my $self = shift; - return @{$self->{'unlink'}}; -} - -sub cleanup($) { - # Remove files when job is done - my $self = shift; - unlink $self->get_rm(); - delete @Global::unlink{$self->get_rm()}; -} - -sub grouped($) { - my $self = shift; - # Set reading FD if using --group (--ungroup does not need) - for my $fdno (1,2) { - # Re-open the file for reading - # so fdw can be closed seperately - # and fdr can be seeked seperately (for --line-buffer) - open(my $fdr,"<", $self->fh($fdno,'name')) || - ::die_bug("fdr: Cannot open ".$self->fh($fdno,'name')); - $self->set_fh($fdno,'r',$fdr); - # Unlink if not debugging - $Global::debug or ::rm($self->fh($fdno,"unlink")); - } -} - -sub empty_input_wrapper($) { - # If no input: exit(0) - # If some input: Pass input as input to command on STDIN - # This avoids starting the command if there is no input. - # Input: - # $command = command to pipe data to - # Returns: - # $wrapped_command = the wrapped command - my $command = shift; - # The optimal block size differs - # It has been measured on: - # AMD 6376: 59000 - # /dev/null'; - my $script = - ::spacefree(0,q{ - if(sysread(STDIN, $buf, 1)) { - open($fh, "|-", @ARGV) || die; - syswrite($fh, $buf); - while($read = sysread(STDIN, $buf, 59000)) { - syswrite($fh, $buf); - } - close $fh; - exit ($?&127 ? 128+($?&127) : 1+$?>>8) - } - }); - ::debug("run",'Empty wrap: perl -e '.::Q($script)."\n"); - if($Global::cshell - and - length $command > 499) { - # csh does not like words longer than 1000 (499 quoted) - # $command = "perl -e '".base64_zip_eval()."' ". - # join" ",string_zip_base64( - # 'exec "'.::perl_quote_scalar($command).'"'); - return 'perl -e '.::Q($script)." ". - base64_wrap("exec \"$Global::shell\",'-c',\"". - ::perl_quote_scalar($command).'"'); - } else { - return 'perl -e '.::Q($script)." ". - $Global::shell." -c ".::Q($command); - } -} - -sub filter_through_compress($) { - my $self = shift; - # Send stdout to stdin for $opt::compress_program(1) - # Send stderr to stdin for $opt::compress_program(2) - # cattail get pid: $pid = $self->fh($fdno,'rpid'); - my $cattail = cattail(); - - for my $fdno (1,2) { - # Make a communication file. - my ($fh, $comfile) = ::tmpfile(SUFFIX => ".pac"); - close $fh; - # Compressor: (echo > $comfile; compress pipe) > output - # When the echo is written to $comfile, - # it is known that output file is opened, - # thus output file can then be removed by the decompressor. - # empty_input_wrapper is needed for plzip - my $qcom = ::Q($comfile); - my $wpid = open(my $fdw,"|-", "(echo > $qcom; ". - empty_input_wrapper($opt::compress_program).") >". - ::Q($self->fh($fdno,'name'))) || die $?; - $self->set_fh($fdno,'w',$fdw); - $self->set_fh($fdno,'wpid',$wpid); - # Decompressor: open output; -s $comfile > 0: rm $comfile output; - # decompress output > stdout - my $rpid = open(my $fdr, "-|", "perl", "-e", $cattail, $comfile, - $opt::decompress_program, $wpid, - $self->fh($fdno,'name'),$self->fh($fdno,'unlink')) - || die $?; - $self->set_fh($fdno,'r',$fdr); - $self->set_fh($fdno,'rpid',$rpid); - } -} - -sub set_fh($$$$) { - # Set file handle - my ($self, $fd_no, $key, $fh) = @_; - $self->{'fd'}{$fd_no,$key} = $fh; -} - -sub fh($) { - # Get file handle - my ($self, $fd_no, $key) = @_; - return $self->{'fd'}{$fd_no,$key}; -} - -sub write_block($) { - my $self = shift; - my $stdin_fh = $self->fh(0,"w"); - if(fork()) { - # Close in parent - close $stdin_fh; - } else { - # If writing is to a closed pipe: - # Do not call signal handler, but let nothing be written - local $SIG{PIPE} = undef; - - for my $part ( - grep { defined $_ } - $self->{'header'},$self->{'block'}) { - # syswrite may not write all in one go, - # so make sure everything is written. - my $written; - while($written = syswrite($stdin_fh,$$part)) { - substr($$part,0,$written) = ""; - } - } - close $stdin_fh; - exit(0); - } -} - -sub write($) { - my $self = shift; - my $remaining_ref = shift; - my $stdin_fh = $self->fh(0,"w"); - - my $len = length $$remaining_ref; - # syswrite may not write all in one go, - # so make sure everything is written. - my $written; - - # If writing is to a closed pipe: - # Do not call signal handler, but let nothing be written - local $SIG{PIPE} = undef; - while($written = syswrite($stdin_fh,$$remaining_ref)){ - substr($$remaining_ref,0,$written) = ""; - } -} - -sub set_block($$$$$$) { - # Copy stdin buffer from $block_ref up to $endpos - # Prepend with $header_ref if virgin (i.e. not --roundrobin) - # Remove $recstart and $recend if needed - # Input: - # $header_ref = ref to $header to prepend - # $buffer_ref = ref to $buffer containing the block - # $endpos = length of $block to pass on - # $recstart = --recstart regexp - # $recend = --recend regexp - # Returns: - # N/A - my $self = shift; - my ($header_ref,$buffer_ref,$endpos,$recstart,$recend) = @_; - $self->{'header'} = $header_ref; - if($opt::roundrobin or $opt::remove_rec_sep or defined $opt::retries) { - my $a = ""; - if(($opt::roundrobin or defined $opt::retries) and $self->virgin()) { - $a .= $$header_ref; - } - # Job is no longer virgin - $self->set_virgin(0); - # Make a full copy because $buffer will change - $a .= substr($$buffer_ref,0,$endpos); - $self->{'block'} = \$a; - if($opt::remove_rec_sep) { - remove_rec_sep($self->{'block'},$recstart,$recend); - } - $self->{'block_length'} = length ${$self->{'block'}}; - } else { - $self->set_virgin(0); - for(substr($$buffer_ref,0,$endpos)) { - $self->{'block'} = \$_; - } - $self->{'block_length'} = $endpos + length ${$self->{'header'}}; - } - $self->{'block_pos'} = 0; - $self->add_transfersize($self->{'block_length'}); -} - -sub block_ref($) { - my $self = shift; - return $self->{'block'}; -} - -sub block_length($) { - my $self = shift; - return $self->{'block_length'}; -} - -sub remove_rec_sep($) { - # Remove --recstart and --recend from $block - # Input: - # $block_ref = reference to $block to be modified - # $recstart = --recstart - # $recend = --recend - # Uses: - # $opt::regexp = Are --recstart/--recend regexp? - # Returns: - # N/A - my ($block_ref,$recstart,$recend) = @_; - # Remove record separator - if($opt::regexp) { - $$block_ref =~ s/$recend$recstart//gom; - $$block_ref =~ s/^$recstart//os; - $$block_ref =~ s/$recend$//os; - } else { - $$block_ref =~ s/\Q$recend$recstart\E//gom; - $$block_ref =~ s/^\Q$recstart\E//os; - $$block_ref =~ s/\Q$recend\E$//os; - } -} - -sub non_blocking_write($) { - my $self = shift; - my $something_written = 0; - - my $in = $self->fh(0,"w"); - my $rv = syswrite($in, - substr(${$self->{'block'}},$self->{'block_pos'})); - if (!defined($rv) && $! == ::EAGAIN()) { - # would block - but would have written - $something_written = 0; - # avoid triggering auto expanding block size - $Global::no_autoexpand_block ||= 1; - } elsif ($self->{'block_pos'}+$rv != $self->{'block_length'}) { - # incomplete write - # Remove the written part - $self->{'block_pos'} += $rv; - $something_written = $rv; - } else { - # successfully wrote everything - # Empty block to free memory - my $a = ""; - $self->set_block(\$a,\$a,0,"",""); - $something_written = $rv; - } - ::debug("pipe", "Non-block: ", $something_written); - return $something_written; -} - - -sub virgin($) { - my $self = shift; - return $self->{'virgin'}; -} - -sub set_virgin($$) { - my $self = shift; - $self->{'virgin'} = shift; -} - -sub pid($) { - my $self = shift; - return $self->{'pid'}; -} - -sub set_pid($$) { - my $self = shift; - $self->{'pid'} = shift; -} - -sub starttime($) { - # Returns: - # UNIX-timestamp this job started - my $self = shift; - return sprintf("%.3f",$self->{'starttime'}); -} - -sub set_starttime($@) { - my $self = shift; - my $starttime = shift || ::now(); - $self->{'starttime'} = $starttime; - $opt::sqlworker and - $Global::sql->update("SET Starttime = ? WHERE Seq = ".$self->seq(), - $starttime); -} - -sub runtime($) { - # Returns: - # Run time in seconds with 3 decimals - my $self = shift; - return sprintf("%.3f", - int(($self->endtime() - $self->starttime())*1000)/1000); -} - -sub endtime($) { - # Returns: - # UNIX-timestamp this job ended - # 0 if not ended yet - my $self = shift; - return ($self->{'endtime'} || 0); -} - -sub set_endtime($$) { - my $self = shift; - my $endtime = shift; - $self->{'endtime'} = $endtime; - $opt::sqlworker and - $Global::sql->update("SET JobRuntime = ? WHERE Seq = ".$self->seq(), - $self->runtime()); -} - -sub is_timedout($) { - # Is the job timedout? - # Input: - # $delta_time = time that the job may run - # Returns: - # True or false - my $self = shift; - my $delta_time = shift; - return time > $self->{'starttime'} + $delta_time; -} - -sub kill($) { - my $self = shift; - $self->set_exitstatus(-1); - ::kill_sleep_seq($self->pid()); -} - -sub suspend($) { - my $self = shift; - my @pgrps = map { -$_ } $self->pid(); - kill "STOP", @pgrps; - $self->set_suspended(1); -} - -sub set_suspended($$) { - my $self = shift; - $self->{'suspended'} = shift; -} - -sub suspended($) { - my $self = shift; - return $self->{'suspended'}; -} - -sub resume($) { - my $self = shift; - my @pgrps = map { -$_ } $self->pid(); - kill "CONT", @pgrps; - $self->set_suspended(0); -} - -sub failed($) { - # return number of times failed for this $sshlogin - # Input: - # $sshlogin - # Returns: - # Number of times failed for $sshlogin - my $self = shift; - my $sshlogin = shift; - return $self->{'failed'}{$sshlogin}; -} - -sub failed_here($) { - # return number of times failed for the current $sshlogin - # Returns: - # Number of times failed for this sshlogin - my $self = shift; - return $self->{'failed'}{$self->sshlogin()}; -} - -sub add_failed($) { - # increase the number of times failed for this $sshlogin - my $self = shift; - my $sshlogin = shift; - $self->{'failed'}{$sshlogin}++; -} - -sub add_failed_here($) { - # increase the number of times failed for the current $sshlogin - my $self = shift; - $self->{'failed'}{$self->sshlogin()}++; -} - -sub reset_failed($) { - # increase the number of times failed for this $sshlogin - my $self = shift; - my $sshlogin = shift; - delete $self->{'failed'}{$sshlogin}; -} - -sub reset_failed_here($) { - # increase the number of times failed for this $sshlogin - my $self = shift; - delete $self->{'failed'}{$self->sshlogin()}; -} - -sub min_failed($) { - # Returns: - # the number of sshlogins this command has failed on - # the minimal number of times this command has failed - my $self = shift; - my $min_failures = - ::min(map { $self->{'failed'}{$_} } keys %{$self->{'failed'}}); - my $number_of_sshlogins_failed_on = scalar keys %{$self->{'failed'}}; - return ($number_of_sshlogins_failed_on,$min_failures); -} - -sub total_failed($) { - # Returns: - # $total_failures = the number of times this command has failed - my $self = shift; - my $total_failures = 0; - for (values %{$self->{'failed'}}) { - $total_failures += $_; - } - return $total_failures; -} - -{ - my $script; - - sub postpone_exit_and_cleanup { - # Command to remove files and dirs (given as args) without - # affecting the exit value in $?/$status. - if(not $script) { - $script = "perl -e '". - ::spacefree(0,q{ - $bash=shift; - $csh=shift; - for(@ARGV){ - unlink; - rmdir; - } - if($bash=~s/(\d+)h/$1/) { - exit $bash; - } - exit $csh; - }). - # `echo \$?h` is needed to make fish not complain - "' ".'"`echo \\\\\\\\\$?h`" "$status" '; - } - return $script - } -} - -{ - my $script; - - sub fifo_wrap() { - # Script to create a fifo, run a command on the fifo - # while copying STDIN to the fifo, and finally - # remove the fifo and return the exit code of the command. - if(not $script) { - # {} == $PARALLEL_TMP for --fifo - # To make it csh compatible a wrapper needs to: - # * mkfifo - # * spawn $command & - # * cat > fifo - # * waitpid to get the exit code from $command - # * be less than 1000 chars long - - # The optimal block size differs - # It has been measured on: - # AMD 6376: 4095 - # ppar -a big --pipepart --block -1 --test $1 --fifo 'cat {} >/dev/null'; - $script = "perl -e '". - (::spacefree - (0, q{ - ($s,$c,$f) = @ARGV; - # mkfifo $PARALLEL_TMP - system "mkfifo", $f; - # spawn $shell -c $command & - $pid = fork || exec $s, "-c", $c; - open($o,">",$f) || die $!; - # cat > $PARALLEL_TMP - while(sysread(STDIN,$buf,4095)){ - syswrite $o, $buf; - } - close $o; - # waitpid to get the exit code from $command - waitpid $pid,0; - # Cleanup - unlink $f; - exit $?/256; - }))."'"; - } - return $script; - } -} - -sub wrapped($) { - # Wrap command with: - # * --shellquote - # * --nice - # * --cat - # * --fifo - # * --sshlogin - # * --pipepart (@Global::cat_prepends) - # * --tee (@Global::cat_prepends) - # * --pipe - # * --tmux - # The ordering of the wrapping is important: - # * --nice/--cat/--fifo should be done on the remote machine - # * --pipepart/--pipe should be done on the local machine inside --tmux - # Uses: - # @opt::shellquote - # $opt::nice - # $Global::shell - # $opt::cat - # $opt::fifo - # @Global::cat_prepends - # $opt::pipe - # $opt::tmux - # Returns: - # $self->{'wrapped'} = the command wrapped with the above - my $self = shift; - if(not defined $self->{'wrapped'}) { - my $command = $self->replaced(); - # Bug in Bash and Ksh when running multiline aliases - # This will force them to run correctly, but will fail in - # tcsh so we do not do it. - # $command .= "\n\n"; - if(@opt::shellquote) { - # Quote one time for each --shellquote - my $c = $command; - for(@opt::shellquote) { - $c = ::Q($c); - } - # Prepend "echo" (it is written in perl because - # quoting '-e' causes problem in some versions and - # csh's version does something wrong) - $command = q(perl -e '$,=" "; print "@ARGV\n";' -- ) . ::Q($c); - } - if($Global::parallel_env) { - # If $PARALLEL_ENV set, put that in front of the command - # Used for env_parallel.* - if($Global::shell =~ /zsh/) { - # The extra 'eval' will make aliases work, too - $command = $Global::parallel_env."\n". - "eval ".::Q($command); - } else { - $command = $Global::parallel_env."\n".$command; - } - } - if($opt::cat) { - # In '--cat' and '--fifo' {} == $PARALLEL_TMP. - # This is to make it possible to compute $PARALLEL_TMP on - # the fly when running remotely. - # $ENV{PARALLEL_TMP} is set in the remote wrapper before - # the command is run. - # - # Prepend 'cat > $PARALLEL_TMP;' - # Append 'unlink $PARALLEL_TMP without affecting $?' - $command = - 'cat > "$PARALLEL_TMP";'. - $command.";". postpone_exit_and_cleanup(). - '"$PARALLEL_TMP"'; - } elsif($opt::fifo) { - # Prepend fifo-wrapper. In essence: - # mkfifo {} - # ( $command ) & - # # $command must read {}, otherwise this 'cat' will block - # cat > {}; - # wait; rm {} - # without affecting $? - $command = fifo_wrap(). " ". - $Global::shell. " ". ::Q($command). ' "$PARALLEL_TMP"'. ';'; - } - # Wrap with ssh + tranferring of files - $command = $self->sshlogin_wrap($command); - if(@Global::cat_prepends) { - # --pipepart: prepend: - # < /tmp/foo perl -e 'while(@ARGV) { - # sysseek(STDIN,shift,0) || die; $left = shift; - # while($read = sysread(STDIN,$buf, ($left > 60800 ? 60800 : $left))){ - # $left -= $read; syswrite(STDOUT,$buf); - # } - # }' 0 0 0 11 | - # - # --pipepart --tee: prepend: - # < dash-a-file - # - # --pipe --tee: wrap: - # (rm fifo; ... ) < fifo - # - # --pipe --shard X: - # (rm fifo; ... ) < fifo - $command = (shift @Global::cat_prepends). "($command)". - (shift @Global::cat_appends); - } elsif($opt::pipe and not $opt::roundrobin) { - # Wrap with EOF-detector to avoid starting $command if EOF. - $command = empty_input_wrapper($command); - } - if($opt::tmux) { - # Wrap command with 'tmux' - $command = $self->tmux_wrap($command); - } - if($Global::cshell - and - length $command > 499) { - # csh does not like words longer than 1000 (499 quoted) - # $command = "perl -e '".base64_zip_eval()."' ". - # join" ",string_zip_base64( - # 'exec "'.::perl_quote_scalar($command).'"'); - $command = base64_wrap("exec \"$Global::shell\",'-c',\"". - ::perl_quote_scalar($command).'"'); - } - $self->{'wrapped'} = $command; - } - return $self->{'wrapped'}; -} - -sub set_sshlogin($$) { - my $self = shift; - my $sshlogin = shift; - $self->{'sshlogin'} = $sshlogin; - delete $self->{'sshlogin_wrap'}; # If sshlogin is changed the wrap is wrong - delete $self->{'wrapped'}; - - if($opt::sqlworker) { - # Identify worker as --sqlworker often runs on different machines - # If local: Use hostname - my $host = $sshlogin->local() ? ::hostname() : $sshlogin->host(); - $Global::sql->update("SET Host = ? WHERE Seq = ".$self->seq(), $host); - } -} - -sub sshlogin($) { - my $self = shift; - return $self->{'sshlogin'}; -} - -sub string_base64($) { - # Base64 encode strings into 1000 byte blocks. - # 1000 bytes is the largest word size csh supports - # Input: - # @strings = to be encoded - # Returns: - # @base64 = 1000 byte block - $Global::use{"MIME::Base64"} ||= eval "use MIME::Base64; 1;"; - my @base64 = unpack("(A1000)*",encode_base64((join"",@_),"")); - return @base64; -} - -sub string_zip_base64($) { - # Pipe string through 'bzip2 -9' and base64 encode it into 1000 - # byte blocks. - # 1000 bytes is the largest word size csh supports - # Zipping will make exporting big environments work, too - # Input: - # @strings = to be encoded - # Returns: - # @base64 = 1000 byte block - my($zipin_fh, $zipout_fh,@base64); - ::open3($zipin_fh,$zipout_fh,">&STDERR","bzip2 -9"); - if(fork) { - close $zipin_fh; - $Global::use{"MIME::Base64"} ||= eval "use MIME::Base64; 1;"; - # Split base64 encoded into 1000 byte blocks - @base64 = unpack("(A1000)*",encode_base64((join"",<$zipout_fh>),"")); - close $zipout_fh; - } else { - close $zipout_fh; - print $zipin_fh @_; - close $zipin_fh; - exit; - } - ::debug("base64","Orig:@_\nAs bzip2 base64:@base64\n"); - return @base64; -} - -sub base64_zip_eval() { - # Script that: - # * reads base64 strings from @ARGV - # * decodes them - # * pipes through 'bzip2 -dc' - # * evals the result - # Reverse of string_zip_base64 + eval - # Will be wrapped in ' so single quote is forbidden - # Returns: - # $script = 1-liner for perl -e - my $script = ::spacefree(0,q{ - @GNU_Parallel = split /_/, "use_IPC::Open3;_use_MIME::Base64"; - eval"@GNU_Parallel"; - $chld = $SIG{CHLD}; - $SIG{CHLD} = "IGNORE"; - # Search for bzip2. Not found => use default path - my $zip = (grep { -x $_ } "/usr/local/bin/bzip2")[0] || "bzip2"; - # $in = stdin on $zip, $out = stdout from $zip - # Forget my() to save chars for csh - # my($in, $out,$eval); - open3($in,$out,">&STDERR",$zip,"-dc"); - if(my $perlpid = fork) { - close $in; - $eval = join "", <$out>; - close $out; - } else { - close $out; - # Pipe decoded base64 into 'bzip2 -dc' - print $in (decode_base64(join"",@ARGV)); - close $in; - exit; - } - wait; - $SIG{CHLD} = $chld; - eval $eval; - }); - ::debug("base64",$script,"\n"); - return $script; -} - -sub base64_wrap($) { - # base64 encode Perl code - # Split it into chunks of < 1000 bytes - # Prepend it with a decoder that eval's it - # Input: - # $eval_string = Perl code to run - # Returns: - # $shell_command = shell command that runs $eval_string - my $eval_string = shift; - return - "perl -e ". - ::Q(base64_zip_eval())." ". - join" ",::shell_quote(string_zip_base64($eval_string)); -} - -sub base64_eval($) { - # Script that: - # * reads base64 strings from @ARGV - # * decodes them - # * evals the result - # Reverse of string_base64 + eval - # Will be wrapped in ' so single quote is forbidden. - # Spaces are stripped so spaces cannot be significant. - # The funny 'use IPC::Open3'-syntax is to avoid spaces and - # to make it clear that this is a GNU Parallel command - # when looking at the process table. - # Returns: - # $script = 1-liner for perl -e - my $script = ::spacefree(0,q{ - @GNU_Parallel=("use","IPC::Open3;","use","MIME::Base64"); - eval "@GNU_Parallel"; - my $eval = decode_base64(join"",@ARGV); - eval $eval; - }); - ::debug("base64",$script,"\n"); - return $script; -} - -sub sshlogin_wrap($) { - # Wrap the command with the commands needed to run remotely - # Input: - # $command = command to run - # Returns: - # $self->{'sshlogin_wrap'} = command wrapped with ssh+transfer commands - sub monitor_parent_sshd_script { - # This script is to solve the problem of - # * not mixing STDERR and STDOUT - # * terminating with ctrl-c - # If its parent is ssh: all good - # If its parent is init(1): ssh died, so kill children - my $monitor_parent_sshd_script; - - if(not $monitor_parent_sshd_script) { - $monitor_parent_sshd_script = - # This will be packed in ', so only use " - ::spacefree - (0,'$shell = "'.($ENV{'PARALLEL_SHELL'} || '$ENV{SHELL}').'";'. - '$tmpdir = $ENV{"TMPDIR"} || "'. - ::perl_quote_scalar($ENV{'PARALLEL_REMOTE_TMPDIR'}).'";'. - '$nice = '.$opt::nice.';'. - '$termseq = "'.$opt::termseq.'";'. - # } - q{ - # Check that $tmpdir is writable - -w $tmpdir || - die("$tmpdir\040is\040not\040writable.". - "\040Set\040PARALLEL_REMOTE_TMPDIR"); - # Set $PARALLEL_TMP to a non-existent file name in $TMPDIR - do { - $ENV{PARALLEL_TMP} = $tmpdir."/par". - join"", map { (0..9,"a".."z","A".."Z")[rand(62)] } (1..5); - } while(-e $ENV{PARALLEL_TMP}); - # Set $script to a non-existent file name in $TMPDIR - do { - $script = $tmpdir."/par-job-$ENV{PARALLEL_SEQ}_". - join"", map { (0..9,"a".."z","A".."Z")[rand(62)] } (1..5); - } while(-e $script); - # Create a script from the hex code - # that removes itself and runs the commands - open($fh,">",$script) || die; - # \040 = space - but we remove spaces in the script - # ' needed due to rc-shell - print($fh("rm\040\'$script\'\n",$bashfunc.$cmd)); - close $fh; - my $parent = getppid; - my $done = 0; - $SIG{CHLD} = sub { $done = 1; }; - $pid = fork; - unless($pid) { - # Make own process group to be able to kill HUP it later - eval { setpgrp }; - # Set nice value - eval { setpriority(0,0,$nice) }; - # Run the script - exec($shell,$script); - die("exec\040failed: $!"); - } - while((not $done) and (getppid == $parent)) { - # Parent pid is not changed, so sshd is alive - # Exponential sleep up to 1 sec - $s = $s < 1 ? 0.001 + $s * 1.03 : $s; - select(undef, undef, undef, $s); - } - if(not $done) { - # sshd is dead: User pressed Ctrl-C - # Kill as per --termseq - my @term_seq = split/,/,$termseq; - if(not @term_seq) { - @term_seq = ("TERM",200,"TERM",100,"TERM",50,"KILL",25); - } - while(@term_seq && kill(0,-$pid)) { - kill(shift @term_seq, -$pid); - select(undef, undef, undef, (shift @term_seq)/1000); - } - } - wait; - exit ($?&127 ? 128+($?&127) : 1+$?>>8) - }); - } - return $monitor_parent_sshd_script; - } - - sub vars_to_export { - # Uses: - # @opt::env - my @vars = ("parallel_bash_environment"); - for my $varstring (@opt::env) { - # Split up --env VAR1,VAR2 - push @vars, split /,/, $varstring; - } - for (@vars) { - if(-r $_ and not -d) { - # Read as environment definition bug #44041 - # TODO parse this - my $fh = ::open_or_exit($_); - $Global::envdef = join("",<$fh>); - close $fh; - } - } - if(grep { /^_$/ } @vars) { - local $/ = "\n"; - # --env _ - # Include all vars that are not in a clean environment - if(open(my $vars_fh, "<", $Global::config_dir . "/ignored_vars")) { - my @ignore = <$vars_fh>; - chomp @ignore; - my %ignore; - @ignore{@ignore} = @ignore; - close $vars_fh; - push @vars, grep { not defined $ignore{$_} } keys %ENV; - @vars = grep { not /^_$/ } @vars; - } else { - ::error("Run '$Global::progname --record-env' ". - "in a clean environment first."); - ::wait_and_exit(255); - } - } - # Duplicate vars as BASH functions to include post-shellshock functions (v1+v2) - # So --env myfunc should look for BASH_FUNC_myfunc() and BASH_FUNC_myfunc%% - - push(@vars, "PARALLEL_PID", "PARALLEL_SEQ", - "PARALLEL_SSHLOGIN", "PARALLEL_SSHHOST", - "PARALLEL_HOSTGROUPS", "PARALLEL_ARGHOSTGROUPS", - "PARALLEL_JOBSLOT", $opt::process_slot_var, - map { ("BASH_FUNC_$_()", "BASH_FUNC_$_%%") } @vars); - # Keep only defined variables - return grep { defined($ENV{$_}) } @vars; - } - - sub env_as_eval { - # Returns: - # $eval = '$ENV{"..."}=...; ...' - my @vars = vars_to_export(); - my $csh_friendly = not grep { /\n/ } @ENV{@vars}; - my @bash_functions = grep { substr($ENV{$_},0,4) eq "() {" } @vars; - my @non_functions = (grep { !/PARALLEL_ENV/ } - grep { substr($ENV{$_},0,4) ne "() {" } @vars); - - # eval of @envset will set %ENV - my $envset = join"", map { - '$ENV{"'.::perl_quote_scalar($_).'"}="'. - ::perl_quote_scalar($ENV{$_}).'";'; } @non_functions; - - # running @bashfunc on the command line, will set the functions - my @bashfunc = map { - my $v=$_; - s/BASH_FUNC_(.*)(\(\)|%%)/$1/; - "$_$ENV{$v};\nexport -f $_ 2> /dev/null;\n" } @bash_functions; - # eval $bashfuncset will set $bashfunc - my $bashfuncset; - if(@bashfunc) { - # Functions are not supported for all shells - if($Global::shell !~ m:(^|/)(ash|bash|rbash|zsh|rzsh|dash|ksh):) { - ::warning("Shell functions may not be supported in $Global::shell."); - } - $bashfuncset = - '@bash_functions=qw('."@bash_functions".");". - ::spacefree(1,'$shell="'.($ENV{'PARALLEL_SHELL'} || '$ENV{SHELL}').'";'.q{ - if($shell=~/csh/) { - print STDERR "CSH/TCSH DO NOT SUPPORT newlines IN VARIABLES/FUNCTIONS. Unset @bash_functions\n"; - exec "false"; - } - }). - "\n".'$bashfunc = "'.::perl_quote_scalar("@bashfunc").'";'; - } else { - $bashfuncset = '$bashfunc = "";' - } - if($ENV{'parallel_bash_environment'}) { - $bashfuncset .= '$bashfunc .= "eval\ \"\$parallel_bash_environment\"\;";'; - } - ::debug("base64",$envset,$bashfuncset,"\n"); - return $csh_friendly,$envset,$bashfuncset; - } - - my $self = shift; - my $command = shift; - # TODO test that *sh -c 'parallel --env' use *sh - if(not defined $self->{'sshlogin_wrap'}{$command}) { - my $sshlogin = $self->sshlogin(); - $ENV{'PARALLEL_SEQ'} = $self->seq(); - $ENV{$opt::process_slot_var} = -1 + - ($ENV{'PARALLEL_JOBSLOT'} = $self->slot()); - $ENV{'PARALLEL_SSHLOGIN'} = $sshlogin->string(); - $ENV{'PARALLEL_SSHHOST'} = $sshlogin->host(); - if ($opt::hostgroups) { - $ENV{'PARALLEL_HOSTGROUPS'} = join '+', $sshlogin->hostgroups(); - $ENV{'PARALLEL_ARGHOSTGROUPS'} = join '+', $self->hostgroups(); - } - $ENV{'PARALLEL_PID'} = $$; - if($sshlogin->local()) { - if($opt::workdir) { - # Create workdir if needed. Then cd to it. - my $wd = $self->workdir(); - if($opt::workdir eq "." or $opt::workdir eq "...") { - # If $wd does not start with '/': Prepend $HOME - $wd =~ s:^([^/]):$ENV{'HOME'}/$1:; - } - ::mkdir_or_die($wd); - my $post = ""; - if($opt::workdir eq "...") { - $post = ";".exitstatuswrapper("rm -rf ".::Q($wd).";"); - - } - $command = "cd ".::Q($wd)." || exit 255; " . - $command . $post;; - } - if(@opt::env) { - # Prepend with environment setter, which sets functions in zsh - my ($csh_friendly,$envset,$bashfuncset) = env_as_eval(); - my $perl_code = $envset.$bashfuncset. - '@ARGV="'.::perl_quote_scalar($command).'";'. - "exec\"$Global::shell\",\"-c\",\(\$bashfunc.\"\@ARGV\"\)\;die\"exec:\$\!\\n\"\;"; - if(length $perl_code > 999 - or - not $csh_friendly - or - $command =~ /\n/) { - # csh does not deal well with > 1000 chars in one word - # csh does not deal well with $ENV with \n - $self->{'sshlogin_wrap'}{$command} = base64_wrap($perl_code); - } else { - $self->{'sshlogin_wrap'}{$command} = "perl -e ".::Q($perl_code); - } - } else { - $self->{'sshlogin_wrap'}{$command} = $command; - } - } else { - my $pwd = ""; - if($opt::workdir) { - # Create remote workdir if needed. Then cd to it. - my $wd = ::pQ($self->workdir()); - $pwd = qq{system("mkdir","-p","--","$wd"); chdir "$wd" ||}. - qq{print(STDERR "parallel: Cannot chdir to $wd\\n") &&}. - qq{exit 255;}; - } - my ($csh_friendly,$envset,$bashfuncset) = env_as_eval(); - my $cmd = $command; - # q// does not quote \, so we must do that - $cmd =~ s/\\/\\\\/g; - - my $remote_command = $sshlogin->hexwrap - ($pwd.$envset.$bashfuncset.'$cmd='."q\0".$cmd."\0;". - monitor_parent_sshd_script()); - my ($pre,$post,$cleanup)=("","",""); - # --transfer - $pre .= $self->sshtransfer(); - # --return - $post .= $self->sshreturn(); - # --cleanup - $post .= $self->sshcleanup(); - if($post) { - # We need to save the exit status of the job - $post = exitstatuswrapper($post); - } - $self->{'sshlogin_wrap'}{$command} = - ($pre - . $sshlogin->wrap($remote_command) - . ";" - . $post); - } - } - return $self->{'sshlogin_wrap'}{$command}; -} - -sub fill_templates($) { - # Replace replacement strings in template(s) - # Returns: - # @templates - File names of replaced templates - my $self = shift; - - if(%opt::template) { - my @template_name = - map { $self->{'commandline'}->replace_placeholders([$_],0,0) } - @{$self->{'commandline'}{'template_names'}}; - ::debug("tmpl","Names: @template_name\n"); - for(my $i = 0; $i <= $#template_name; $i++) { - open(my $fh, ">", $template_name[$i]) || die; - print $fh $self->{'commandline'}-> - replace_placeholders([$self->{'commandline'} - {'template_contents'}[$i]],0,0); - close $fh; - } - if($opt::cleanup) { - $self->add_rm(@template_name); - } - } -} - -sub filter($) { - # Replace replacement strings in filter(s) and evaluate them - # Returns: - # $run - 1=yes, undef=no - my $self = shift; - my $run = 1; - if(@opt::filter) { - for my $eval ($self->{'commandline'}-> - replace_placeholders(\@opt::filter,0,0)) { - $run &&= eval $eval; - } - $self->{'commandline'}{'skip'} ||= not $run; - } - return $run; -} - -sub transfer($) { - # Files to transfer - # Non-quoted and with {...} substituted - # Returns: - # @transfer - File names of files to transfer - my $self = shift; - - my $transfersize = 0; - my @transfer = $self->{'commandline'}-> - replace_placeholders($self->{'commandline'}{'transfer_files'},0,0); - for(@transfer) { - # filesize - if(-e $_) { - $transfersize += (stat($_))[7]; - } - } - $self->add_transfersize($transfersize); - return @transfer; -} - -sub transfersize($) { - my $self = shift; - return $self->{'transfersize'}; -} - -sub add_transfersize($) { - my $self = shift; - my $transfersize = shift; - $self->{'transfersize'} += $transfersize; - $opt::sqlworker and - $Global::sql->update("SET Send = ? WHERE Seq = ".$self->seq(), - $self->{'transfersize'}); -} - -sub sshtransfer($) { - # Returns for each transfer file: - # rsync $file remote:$workdir - my $self = shift; - my @pre; - my $sshlogin = $self->sshlogin(); - my $workdir = $self->workdir(); - for my $file ($self->transfer()) { - push @pre, $sshlogin->rsync_transfer_cmd($file,$workdir).";"; - } - return join("",@pre); -} - -sub return($) { - # Files to return - # Non-quoted and with {...} substituted - # Returns: - # @non_quoted_filenames - my $self = shift; - return $self->{'commandline'}-> - replace_placeholders($self->{'commandline'}{'return_files'},0,0); -} - -sub returnsize($) { - # This is called after the job has finished - # Returns: - # $number_of_bytes transferred in return - my $self = shift; - for my $file ($self->return()) { - if(-e $file) { - $self->{'returnsize'} += (stat($file))[7]; - } - } - return $self->{'returnsize'}; -} - -sub add_returnsize($) { - my $self = shift; - my $returnsize = shift; - $self->{'returnsize'} += $returnsize; - $opt::sqlworker and - $Global::sql->update("SET Receive = ? WHERE Seq = ".$self->seq(), - $self->{'returnsize'}); -} - -sub sshreturn($) { - # Returns for each return-file: - # rsync remote:$workdir/$file . - my $self = shift; - my $sshlogin = $self->sshlogin(); - my $pre = ""; - for my $file ($self->return()) { - $file =~ s:^\./::g; # Remove ./ if any - my $relpath = ($file !~ m:^/:) || - ($file =~ m:/\./:); # Is the path relative or /./? - my $cd = ""; - my $wd = ""; - if($relpath) { - # rsync -avR /foo/./bar/baz.c remote:/tmp/ - # == (on old systems) - # rsync -avR --rsync-path="cd /foo; rsync" remote:bar/baz.c /tmp/ - $wd = ::shell_quote_file($self->workdir()."/"); - } - # Only load File::Basename if actually needed - $Global::use{"File::Basename"} ||= eval "use File::Basename; 1;"; - # dir/./file means relative to dir, so remove dir on remote - $file =~ m:(.*)/\./:; - my $basedir = $1 ? ::shell_quote_file($1."/") : ""; - my $nobasedir = $file; - $nobasedir =~ s:.*/\./::; - $cd = ::shell_quote_file(::dirname($nobasedir)); - my $rsync_cd = '--rsync-path='.::Q("cd $wd$cd; rsync"); - my $basename = ::Q(::shell_quote_file(::basename($file))); - # --return - # mkdir -p /home/tange/dir/subdir/; - # rsync (--protocol 30) -rlDzR - # --rsync-path="cd /home/tange/dir/subdir/; rsync" - # server:file.gz /home/tange/dir/subdir/ - $pre .= "mkdir -p $basedir$cd" . " && " . - $sshlogin->rsync(). " $rsync_cd -- ".$sshlogin->host().':'. - $basename . " ".$basedir.$cd.";"; - } - return $pre; -} - -sub sshcleanup($) { - # Return the sshcommand needed to remove the file - # Returns: - # ssh command needed to remove files from sshlogin - my $self = shift; - my $sshlogin = $self->sshlogin(); - my $workdir = $self->workdir(); - my $cleancmd = ""; - - for my $file ($self->remote_cleanup()) { - my @subworkdirs = parentdirs_of($file); - $cleancmd .= $sshlogin->cleanup_cmd($file,$workdir).";"; - } - if(defined $opt::workdir and $opt::workdir eq "...") { - $cleancmd .= $sshlogin->wrap("rm -rf " . ::Q($workdir).';'); - } - return $cleancmd; -} - -sub remote_cleanup($) { - # Returns: - # Files to remove at cleanup - my $self = shift; - if($opt::cleanup) { - my @transfer = $self->transfer(); - my @return = $self->return(); - return (@transfer,@return); - } else { - return (); - } -} - -sub exitstatuswrapper(@) { - # Input: - # @shellcode = shell code to execute - # Returns: - # shell script that returns current status after executing @shellcode - if($Global::cshell) { - return ('set _EXIT_status=$status; ' . - join(" ",@_). - 'exit $_EXIT_status;'); - } elsif($Global::fish) { - return ('export _EXIT_status=$status; ' . - join(" ",@_). - 'exit $_EXIT_status;'); - } else { - return ('_EXIT_status=$?; ' . - join(" ",@_). - 'exit $_EXIT_status;'); - } -} - -sub workdir($) { - # Returns: - # the workdir on a remote machine - my $self = shift; - if(not defined $self->{'workdir'}) { - my $workdir; - if(defined $opt::workdir) { - if($opt::workdir eq ".") { - # . means current dir - my $home = $ENV{'HOME'}; - eval 'use Cwd'; - my $cwd = cwd(); - $workdir = $cwd; - if($home) { - # If homedir exists: remove the homedir from - # workdir if cwd starts with homedir - # E.g. /home/foo/my/dir => my/dir - # E.g. /tmp/my/dir => /tmp/my/dir - my ($home_dev, $home_ino) = (stat($home))[0,1]; - my $parent = ""; - my @dir_parts = split(m:/:,$cwd); - my $part; - while(defined ($part = shift @dir_parts)) { - $part eq "" and next; - $parent .= "/".$part; - my ($parent_dev, $parent_ino) = (stat($parent))[0,1]; - if($parent_dev == $home_dev and $parent_ino == $home_ino) { - # dev and ino is the same: We found the homedir. - $workdir = join("/",@dir_parts); - last; - } - } - } - if($workdir eq "") { - $workdir = "."; - } - } elsif($opt::workdir eq "...") { - $workdir = ".parallel/tmp/" . ::hostname() . "-" . $$ - . "-" . $self->seq(); - } else { - $workdir = $self->{'commandline'}-> - replace_placeholders([$opt::workdir],0,0); - #$workdir = $opt::workdir; - # Rsync treats /./ special. We dont want that - $workdir =~ s:/\./:/:g; # Remove /./ - $workdir =~ s:(.)/+$:$1:; # Remove ending / if any - $workdir =~ s:^\./::g; # Remove starting ./ if any - } - } else { - $workdir = "."; - } - $self->{'workdir'} = $workdir; - } - return $self->{'workdir'}; -} - -sub parentdirs_of($) { - # Return: - # all parentdirs except . of this dir or file - sorted desc by length - my $d = shift; - my @parents = (); - while($d =~ s:/[^/]+$::) { - if($d ne ".") { - push @parents, $d; - } - } - return @parents; -} - -sub start($) { - # Setup STDOUT and STDERR for a job and start it. - # Returns: - # job-object or undef if job not to run - - sub open3_setpgrp_internal { - # Run open3+setpgrp followed by the command - # Input: - # $stdin_fh = Filehandle to use as STDIN - # $stdout_fh = Filehandle to use as STDOUT - # $stderr_fh = Filehandle to use as STDERR - # $command = Command to run - # Returns: - # $pid = Process group of job started - my ($stdin_fh,$stdout_fh,$stderr_fh,$command) = @_; - my $pid; - local (*OUT,*ERR); - open OUT, '>&', $stdout_fh or ::die_bug("Can't dup STDOUT: $!"); - open ERR, '>&', $stderr_fh or ::die_bug("Can't dup STDERR: $!"); - # The eval is needed to catch exception from open3 - eval { - if(not $pid = ::open3($stdin_fh, ">&OUT", ">&ERR", "-")) { - # Each child gets its own process group to make it safe to killall - eval{ setpgrp(0,0) }; - eval{ setpriority(0,0,$opt::nice) }; - exec($Global::shell,"-c",$command) - || ::die_bug("open3-$stdin_fh ".substr($command,0,200)); - } - }; - return $pid; - } - - sub open3_setpgrp_external { - # Run open3 on $command wrapped with a perl script doing setpgrp - # Works on systems that do not support open3(,,,"-") - # Input: - # $stdin_fh = Filehandle to use as STDIN - # $stdout_fh = Filehandle to use as STDOUT - # $stderr_fh = Filehandle to use as STDERR - # $command = Command to run - # Returns: - # $pid = Process group of job started - my ($stdin_fh,$stdout_fh,$stderr_fh,$command) = @_; - local (*OUT,*ERR); - open OUT, '>&', $stdout_fh or ::die_bug("Can't dup STDOUT: $!"); - open ERR, '>&', $stderr_fh or ::die_bug("Can't dup STDERR: $!"); - - my $pid; - my @setpgrp_wrap = - ('perl','-e', - "eval\{setpgrp\}\;eval\{setpriority\(0,0,$opt::nice\)\}\;". - "exec '$Global::shell', '-c', \@ARGV"); - # The eval is needed to catch exception from open3 - eval { - $pid = ::open3($stdin_fh, ">&OUT", ">&ERR", @setpgrp_wrap, $command) - || ::die_bug("open3-$stdin_fh"); - 1; - }; - return $pid; - } - - sub redefine_open3_setpgrp { - my $setgprp_cache = shift; - # Select and run open3_setpgrp_internal/open3_setpgrp_external - no warnings 'redefine'; - my ($outfh,$name) = ::tmpfile(SUFFIX => ".tst"); - # Test to see if open3(x,x,x,"-") is fully supported - # Can an exported bash function be called via open3? - my $script = 'if($pid=::open3($i,$o,$e,"-")) { wait; } '. - 'else { exec("bash","-c","testfun && true"); }'; - my $bash = - ::shell_quote_scalar_default( - "testfun() { rm $name; }; export -f testfun; ". - "perl -MIPC::Open3 -e ". - ::Q(::Q($script)) - ); - my $redefine_eval; - # Redirect STDERR temporarily, - # so errors on MacOS X are ignored. - open my $saveerr, ">&STDERR"; - open STDERR, '>', "/dev/null"; - # Run the test - ::debug("init",qq{bash -c $bash 2>/dev/null}); - qx{ bash -c $bash 2>/dev/null }; - open STDERR, ">&", $saveerr; - - if(-e $name) { - # Does not support open3(x,x,x,"-") - # or does not have bash: - # Use (slow) external version - unlink($name); - $redefine_eval = '*open3_setpgrp = \&open3_setpgrp_external'; - ::debug("init","open3_setpgrp_external chosen\n"); - } else { - # Supports open3(x,x,x,"-") - # This is 0.5 ms faster to run - $redefine_eval = '*open3_setpgrp = \&open3_setpgrp_internal'; - ::debug("init","open3_setpgrp_internal chosen\n"); - } - if(open(my $fh, ">", $setgprp_cache)) { - print $fh $redefine_eval; - close $fh; - } else { - ::debug("init","Cannot write to $setgprp_cache"); - } - eval $redefine_eval; - } - - sub open3_setpgrp { - my $setgprp_cache = $Global::cache_dir . "/tmp/sshlogin/" . - ::hostname() . "/setpgrp_func"; - sub read_cache() { - -e $setgprp_cache || return 0; - local $/ = undef; - open(my $fh, "<", $setgprp_cache) || return 0; - eval <$fh> || return 0; - close $fh; - return 1; - } - if(not read_cache()) { - redefine_open3_setpgrp($setgprp_cache); - } - # The sub is now redefined. Call it - return open3_setpgrp(@_); - } - - my $job = shift; - # Get the shell command to be executed (possibly with ssh infront). - my $command = $job->wrapped(); - my $pid; - - if($Global::interactive or $Global::stderr_verbose) { - $job->interactive_start(); - } - # Must be run after $job->interactive_start(): - # $job->interactive_start() may call $job->skip() - if($job->{'commandline'}{'skip'} - or - not $job->filter()) { - # $job->skip() was called or job filtered - $command = "true"; - } - $job->openoutputfiles(); - $job->print_verbose_dryrun(); - my($stdout_fh,$stderr_fh) = ($job->fh(1,"w"),$job->fh(2,"w")); - if($opt::dryrun or $opt::sqlmaster) { $command = "true"; } - $ENV{'PARALLEL_SEQ'} = $job->seq(); - $ENV{'PARALLEL_PID'} = $$; - $ENV{$opt::process_slot_var} = -1 + - ($ENV{'PARALLEL_JOBSLOT'} = $job->slot()); - $ENV{'PARALLEL_TMP'} = ::tmpname("par"); - $job->add_rm($ENV{'PARALLEL_TMP'}); - $job->fill_templates(); - $ENV{'SSHPASS'} = $job->{'sshlogin'}->{'password'}; - ::debug("run", $Global::total_running, " processes . Starting (", - $job->seq(), "): $command\n"); - if($opt::pipe) { - my ($stdin_fh) = ::gensym(); - $pid = open3_setpgrp($stdin_fh,$stdout_fh,$stderr_fh,$command); - if($opt::roundrobin and not $opt::keeporder) { - # --keep-order will make sure the order will be reproducible - ::set_fh_non_blocking($stdin_fh); - } - $job->set_fh(0,"w",$stdin_fh); - if($opt::tee or $opt::shard or $opt::bin) { $job->set_virgin(0); } - } elsif(($opt::tty or $opt::open_tty) and -c "/dev/tty" and - open(my $devtty_fh, "<", "/dev/tty")) { - # Give /dev/tty to the command if no one else is using it - # The eval is needed to catch exception from open3 - local (*IN,*OUT,*ERR); - open OUT, '>&', $stdout_fh or ::die_bug("Can't dup STDOUT: $!"); - open ERR, '>&', $stderr_fh or ::die_bug("Can't dup STDERR: $!"); - *IN = $devtty_fh; - # The eval is needed to catch exception from open3 - my @wrap = ('perl','-e', - "eval\{setpriority\(0,0,$opt::nice\)\}\;". - "exec '$Global::shell', '-c', \@ARGV"); - eval { - $pid = ::open3("<&IN", ">&OUT", ">&ERR", @wrap, $command) - || ::die_bug("open3-/dev/tty"); - 1; - }; - close $devtty_fh; - $job->set_virgin(0); - } elsif($Global::semaphore) { - # Allow sem to read from stdin - $pid = open3_setpgrp("<&STDIN",$stdout_fh,$stderr_fh,$command); - $job->set_virgin(0); - } else { - $pid = open3_setpgrp(::gensym(),$stdout_fh,$stderr_fh,$command); - $job->set_virgin(0); - } - if($pid) { - # A job was started - $Global::total_running++; - $Global::total_started++; - $job->set_pid($pid); - $job->set_starttime(); - $Global::running{$job->pid()} = $job; - if($opt::timeout) { - $Global::timeoutq->insert($job); - } - $Global::newest_job = $job; - $Global::newest_starttime = ::now(); - return $job; - } else { - # No more processes - ::debug("run", "Cannot spawn more jobs.\n"); - return undef; - } -} - -sub interactive_start($) { - my $self = shift; - my $command = $self->wrapped(); - if($Global::interactive) { - my $answer; - ::status_no_nl("$command ?..."); - do{ - open(my $tty_fh, "<", "/dev/tty") || ::die_bug("interactive-tty"); - $answer = <$tty_fh>; - close $tty_fh; - # Sometime we get an empty string (not even \n) - # Do not know why, so let us just ignore it and try again - } while(length $answer < 1); - if (not ($answer =~ /^\s*y/i)) { - $self->{'commandline'}->skip(); - } - } else { - print $Global::original_stderr "$command\n"; - } -} - -{ - my $tmuxsocket; - my $qsocket; - - sub tmux_wrap($) { - # Wrap command with tmux for session pPID - # Input: - # $actual_command = the actual command being run (incl ssh wrap) - my $self = shift; - my $actual_command = shift; - # Temporary file name. Used for fifo to communicate exit val - my $tmpfifo = ::tmpname("tmx"); - $self->add_rm($tmpfifo); - if(length($tmpfifo) >=100) { - ::error("tmux does not support sockets with path > 100."); - ::wait_and_exit(255); - } - if($opt::tmuxpane) { - # Move the command into a pane in window 0 - $actual_command = $ENV{'PARALLEL_TMUX'}.' joinp -t :0 ; '. - $ENV{'PARALLEL_TMUX'}.' select-layout -t :0 tiled ; '. - $actual_command; - } - my $visual_command = $self->replaced(); - my $title = $visual_command; - if($visual_command =~ /\0/) { - ::error("Command line contains NUL. tmux is confused by NUL."); - ::wait_and_exit(255); - } - # ; causes problems - # ascii 194-245 annoys tmux - $title =~ tr/[\011-\016;\302-\365]/ /s; - $title = ::Q($title); - - my $l_act = length($actual_command); - my $l_tit = length($title); - my $l_fifo = length($tmpfifo); - # The line to run contains a 118 chars extra code + the title 2x - my $l_tot = 2 * $l_tit + $l_act + $l_fifo; - - my $quoted_space75 = ::Q(" ")x75; - while($l_tit < 1000 and - ( - (890 < $l_tot and $l_tot < 1350) - or - (9250 < $l_tot and $l_tot < 9800) - )) { - # tmux blocks for certain lengths: - # 900 < title + command < 1200 - # 9250 < title + command < 9800 - # but only if title < 1000, so expand the title with 75 spaces - # The measured lengths are: - # 996 < (title + whole command) < 1127 - # 9331 < (title + whole command) < 9636 - $title .= $quoted_space75; - $l_tit = length($title); - $l_tot = 2 * $l_tit + $l_act + $l_fifo; - } - - my $tmux; - $ENV{'PARALLEL_TMUX'} ||= "tmux"; - if(not $tmuxsocket) { - $tmuxsocket = ::tmpname("tms"); - $qsocket = ::Q($tmuxsocket); - ::debug("tmux", "Start: $ENV{'PARALLEL_TMUX'} -S $qsocket attach"); - if($opt::fg) { - if(not fork) { - # Run tmux in the foreground - # Wait for the socket to appear - while (not -e $tmuxsocket) { } - `$ENV{'PARALLEL_TMUX'} -S $qsocket attach`; - exit; - } - } - ::status("See output with: $ENV{'PARALLEL_TMUX'} -S $qsocket attach"); - } - $tmux = "sh -c ".::Q( - $ENV{'PARALLEL_TMUX'}. - " -S $qsocket new-session -s p$$ -d \"sleep .2\" >/dev/null 2>&1").";" . - $ENV{'PARALLEL_TMUX'}. - " -S $qsocket new-window -t p$$ -n $title"; - - ::debug("tmux", "title len:", $l_tit, " act ", $l_act, " max ", - $Limits::Command::line_max_len, " tot ", - $l_tot, "\n"); - return "mkfifo ".::Q($tmpfifo)." && $tmux ". - # Run in tmux - ::Q - ( - "(".$actual_command.');'. - # The triple print is needed - otherwise the testsuite fails - q[ perl -e 'while($t++<3){ print $ARGV[0],"\n" }' $?h/$status >> ]. - ::Q($tmpfifo)."&". - "echo $title; echo \007Job finished at: `date`;sleep 10" - ). - # Run outside tmux - # Read a / separated line: 0h/2 for csh, 2/0 for bash. - # If csh the first will be 0h, so use the second as exit value. - # Otherwise just use the first value as exit value. - q{; exec perl -e '$/="/";$_=<>;$c=<>;unlink $ARGV; }. - q{/(\d+)h/ and exit($1);exit$c' }.::Q($tmpfifo); - } -} - -sub is_already_in_results($) { - # Do we already have results for this job? - # Returns: - # $job_already_run = bool whether there is output for this or not - my $job = $_[0]; - if($Global::csvsep) { - if($opt::joblog) { - # OK: You can look for job run in joblog - return 0 - } else { - ::warning_once( - "--resume --results .csv/.tsv/.json is not supported yet\n"); - # TODO read and parse the file - return 0 - } - } - my $out = $job->{'commandline'}->results_out(); - ::debug("run", "Test ${out}stdout", -e "${out}stdout", "\n"); - return(-e $out."stdout" or -f $out); -} - -sub is_already_in_joblog($) { - my $job = shift; - return vec($Global::job_already_run,$job->seq(),1); -} - -sub set_job_in_joblog($) { - my $job = shift; - vec($Global::job_already_run,$job->seq(),1) = 1; -} - -sub should_be_retried($) { - # Should this job be retried? - # Returns - # 0 - do not retry - # 1 - job queued for retry - my $self = shift; - if (not defined $opt::retries) { return 0; } - if(not $self->exitstatus() and not $self->exitsignal()) { - # Completed with success. If there is a recorded failure: forget it - $self->reset_failed_here(); - return 0; - } else { - # The job failed. Should it be retried? - $self->add_failed_here(); - my $retries = $self->{'commandline'}-> - replace_placeholders([$opt::retries],0,0); - # 0 = Inf - if($retries == 0) { $retries = 2**31; } - # Ignore files already unlinked to avoid memory leak - $self->{'unlink'} = [ grep { -e $_ } @{$self->{'unlink'}} ]; - map { -e $_ or delete $Global::unlink{$_} } keys %Global::unlink; - if($self->total_failed() == $retries) { - # This has been retried enough - return 0; - } else { - # This command should be retried - $self->set_endtime(undef); - $self->reset_exitstatus(); - $Global::JobQueue->unget($self); - ::debug("run", "Retry ", $self->seq(), "\n"); - return 1; - } - } -} - -{ - my (%print_later,$job_seq_to_print); - - sub print_earlier_jobs($) { - # Print jobs whose output is postponed due to --keep-order - # Returns: N/A - my $job = shift; - $print_later{$job->seq()} = $job; - $job_seq_to_print ||= 1; - my $returnsize = 0; - ::debug("run", "Looking for: $job_seq_to_print ", - "This: ", $job->seq(), "\n"); - for(;vec($Global::job_already_run,$job_seq_to_print,1); - $job_seq_to_print++) {} - while(my $j = $print_later{$job_seq_to_print}) { - $returnsize += $j->print(); - if($j->endtime()) { - # Job finished - look at the next - delete $print_later{$job_seq_to_print}; - $job_seq_to_print++; - next; - } else { - # Job not finished yet - look at it again next round - last; - } - } - return $returnsize; - } -} - -sub print($) { - # Print the output of the jobs - # Returns: N/A - my $self = shift; - - ::debug("print", ">>joboutput ", $self->replaced(), "\n"); - if($opt::dryrun) { - # Nothing was printed to this job: - # cleanup tmp files if --files was set - ::rm($self->fh(1,"name")); - } - if($opt::pipe and $self->virgin() and not $opt::tee) { - # Skip --joblog, --dryrun, --verbose - } else { - if($opt::ungroup) { - # NULL returnsize = 0 returnsize - $self->returnsize() or $self->add_returnsize(0); - if($Global::joblog and defined $self->{'exitstatus'}) { - # Add to joblog when finished - $self->print_joblog(); - # Printing is only relevant for grouped/--line-buffer output. - $opt::ungroup and return; - } - } - # Check for disk full - ::exit_if_disk_full(); - } - - my $returnsize = $self->returnsize(); - my @fdno; - if($opt::latestline) { - @fdno = (1); - } else { - @fdno = (sort { $a <=> $b } keys %Global::fh); - } - for my $fdno (@fdno) { - # Sort by file descriptor numerically: 1,2,3,..,9,10,11 - $fdno == 0 and next; - my $out_fh = $Global::fh{$fdno}; - my $in_fh = $self->fh($fdno,"r"); - if(not $in_fh) { - if(not $Job::file_descriptor_warning_printed{$fdno}++) { - # ::warning("File descriptor $fdno not defined\n"); - } - next; - } - ::debug("print", "File descriptor $fdno (", $self->fh($fdno,"name"), "):\n"); - if($Global::linebuffer) { - # Line buffered print out - $self->print_linebuffer($fdno,$in_fh,$out_fh); - } elsif($Global::files) { - $self->print_files($fdno,$in_fh,$out_fh); - } elsif($opt::results) { - $self->print_results($fdno,$in_fh,$out_fh); - } else { - $self->print_normal($fdno,$in_fh,$out_fh); - } - flush $out_fh; - } - ::debug("print", "<{'exitstatus'} - and not ($self->virgin() and $opt::pipe)) { - if($Global::joblog and not $opt::sqlworker) { - # Add to joblog when finished - $self->print_joblog(); - } - if($opt::sqlworker and not $opt::results) { - $Global::sql->output($self); - } - if($Global::csvsep) { - # Add output to CSV when finished - $self->print_csv(); - } - if($Global::jsonout) { - $self->print_json(); - } - } - return $returnsize - $self->returnsize(); -} - -{ - my %jsonmap; - - sub print_json($) { - my $self = shift; - sub jsonquote($) { - my $a = shift; - if(not $jsonmap{"\001"}) { - map { $jsonmap{sprintf("%c",$_)} = - sprintf '\u%04x', $_ } 0..31; - } - $a =~ s/\\/\\\\/g; - $a =~ s/\"/\\"/g; - $a =~ s/([\000-\037])/$jsonmap{$1}/g; - return $a; - } - - my $cmd; - if($Global::verbose <= 1) { - $cmd = jsonquote($self->replaced()); - } else { - # Verbose level > 1: Print the rsync and stuff - $cmd = jsonquote(join " ", @{$self->{'commandline'}}); - } - my $record_ref = $self->{'commandline'}{'arg_list_flat_orig'}; - - # Memory optimization: Overwrite with the joined output - $self->{'output'}{1} = join("", @{$self->{'output'}{1}}); - $self->{'output'}{2} = join("", @{$self->{'output'}{2}}); - # { - # "Seq": 12, - # "Host": "/usr/bin/ssh foo@lo", - # "Starttime": 1608344711.743, - # "JobRuntime": 0.01, - # "Send": 0, - # "Receive": 10, - # "Exitval": 0, - # "Signal": 0, - # "Command": "echo 1", - # "V": [ - # "1" - # ], - # "Stdout": "1\n", - # "Stderr": "" - # } - # - printf($Global::csv_fh - q({ "Seq": %s, "Host": "%s", "Starttime": %s, "JobRuntime": %s, ). - q("Send": %s, "Receive": %s, "Exitval": %s, "Signal": %s, ). - q("Command": "%s", "V": [ %s ], "Stdout": "%s", "Stderr": "%s" }). - "\n", - $self->seq(), - jsonquote($self->sshlogin()->string()), - $self->starttime(), sprintf("%0.3f",$self->runtime()), - $self->transfersize(), $self->returnsize(), - $self->exitstatus(), $self->exitsignal(), $cmd, - (join ",", - map { '"'.jsonquote($_).'"' } @$record_ref[1..$#$record_ref], - ), - jsonquote($self->{'output'}{1}), - jsonquote($self->{'output'}{2}) - ); - } -} - -{ - my $header_printed; - - sub print_csv($) { - my $self = shift; - my $cmd; - if($Global::verbose <= 1) { - $cmd = $self->replaced(); - } else { - # Verbose level > 1: Print the rsync and stuff - $cmd = join " ", @{$self->{'commandline'}}; - } - my $record_ref = $self->{'commandline'}{'arg_list_flat_orig'}; - - if(not $header_printed) { - # Variable headers - # Normal => V1..Vn - # --header : => first value from column - my @V; - if($opt::header) { - my $i = 1; - @V = (map { $Global::input_source_header{$i++} } - @$record_ref[1..$#$record_ref]); - } else { - my $V = "V1"; - @V = (map { $V++ } @$record_ref[1..$#$record_ref]); - } - print $Global::csv_fh - (map { $$_ } - combine_ref("Seq", "Host", "Starttime", "JobRuntime", - "Send", "Receive", "Exitval", "Signal", "Command", - @V, - "Stdout","Stderr" - )),"\n"; - $header_printed++; - } - # Memory optimization: Overwrite with the joined output - $self->{'output'}{1} = join("", @{$self->{'output'}{1}}); - $self->{'output'}{2} = join("", @{$self->{'output'}{2}}); - print $Global::csv_fh - (map { $$_ } - combine_ref - ($self->seq(), - $self->sshlogin()->string(), - $self->starttime(), sprintf("%0.3f",$self->runtime()), - $self->transfersize(), $self->returnsize(), - $self->exitstatus(), $self->exitsignal(), \$cmd, - \@$record_ref[1..$#$record_ref], - \$self->{'output'}{1}, - \$self->{'output'}{2})),"\n"; - } -} - -sub combine_ref($) { - # Inspired by Text::CSV_PP::_combine (by Makamaka Hannyaharamitu) - my @part = @_; - my $sep = $Global::csvsep; - my $quot = '"'; - my @out = (); - - my $must_be_quoted; - for my $column (@part) { - # Memory optimization: Content transferred as reference - if(ref $column ne "SCALAR") { - # Convert all columns to scalar references - my $v = $column; - $column = \$v; - } - if(not defined $$column) { - $$column = ''; - next; - } - - $must_be_quoted = 0; - - if($$column =~ s/$quot/$quot$quot/go){ - # Replace " => "" - $must_be_quoted ||=1; - } - if($$column =~ /[\s\Q$sep\E]/o){ - # Put quotes around if the column contains , - $must_be_quoted ||=1; - } - - $Global::use{"bytes"} ||= eval "use bytes; 1;"; - if ($$column =~ /\0/) { - # Contains \0 => put quotes around - $must_be_quoted ||=1; - } - if($must_be_quoted){ - push @out, \$sep, \$quot, $column, \$quot; - } else { - push @out, \$sep, $column; - } - } - # Remove the first $sep: ,val,"val" => val,"val" - shift @out; - return @out; -} - -sub print_files($) { - # Print the name of the file containing stdout on stdout - # Uses: - # $opt::pipe - # $opt::group = Print when job is done - # $opt::linebuffer = Print ASAP - # Returns: N/A - my $self = shift; - my ($fdno,$in_fh,$out_fh) = @_; - - # If the job is dead: close printing fh. Needed for --compress - close $self->fh($fdno,"w"); - if($? and $opt::compress) { - ::error($opt::compress_program." failed."); - $self->set_exitstatus(255); - } - if($opt::compress) { - # Kill the decompressor which will not be needed - CORE::kill "TERM", $self->fh($fdno,"rpid"); - } - close $in_fh; - - if($opt::pipe and $self->virgin()) { - # Nothing was printed to this job: - # cleanup unused tmp files because --files was set - for my $fdno (1,2) { - ::rm($self->fh($fdno,"name")); - ::rm($self->fh($fdno,"unlink")); - } - } elsif($fdno == 1 and $self->fh($fdno,"name")) { - print $out_fh $self->tag(),$self->fh($fdno,"name"), $Global::files_sep; - if($Global::membuffer) { - push @{$self->{'output'}{$fdno}}, - $self->tag(), $self->fh($fdno,"name"); - } - $self->add_returnsize(-s $self->fh($fdno,"name")); - # Mark as printed - do not print again - $self->set_fh($fdno,"name",undef); - } -} - - -# Different print types -# (--ll | --ll --bar | --lb | --group | --parset | --sql-worker) -# (--files | --results (.json|.csv|.tsv) ) -# --color-failed -# --color -# --keep-order -# --tag -# --bar -{ - my ($up,$eol,$currow,$maxrow); - my ($minvisible,%print_later,%notvisible); - my (%binmodeset,%tab); - - sub latestline_init() { - # cursor_up cuu1 = up one line - $up = `sh -c "tput cuu1 /dev/null`; - chomp($up); - $eol = `sh -c "tput el /dev/null`; - chomp($eol); - if($eol eq "") { $eol = "\033[K"; } - $currow = 1; - $maxrow = 1; - $minvisible = 1; - for(0..8) { - $tab{$_} = " "x(8-($_%8)); - } - } - - sub mbtrunc($$) { - # Simple mbtrunc to avoid using Text::WideChar::Util - my $str = shift; - my $len = shift; - if(::mbswidth($str) == length($str)) { - $str = substr($str,0,$len); - } else { - # mb chars (ヌー平行) are wider than 1 char on screen - # We need at most $len chars - they may be wide - $str =~ s/(.{$len}).*/$1/; - my $rlen = int((::mbswidth($str) - $len)/2+0.5); - do { - $str =~ s/.{$rlen}$//; - $rlen = int((::mbswidth($str) - $len)/2+0.5); - } while($rlen >= 1); - } - return $str; - } - - sub print_latest_line($) { - my $self = shift; - my $out_fh = shift; - if(not defined $self->{$out_fh,'latestline'}) { return; } - my $row = $self->row(); - # Is row visible? - if(not ($minvisible <= $row - and - $row < $minvisible + ::terminal_rows() - 1)) { - return; - } - if(not $binmodeset{$out_fh}++) { - # Enable utf8 if possible - eval q{ binmode $out_fh, "encoding(utf8)"; }; - } - my ($color,$reset_color) = $self->color(); - my $termcol = ::terminal_columns(); - my $untabify_tag = ::decode_utf8($self->untabtag()); - my $untabify_str = - ::untabify(::decode_utf8($self->{$out_fh,'latestline'})); - # -1 to make space for $truncated_str - my $maxtaglen = $termcol - 1; - $untabify_tag = mbtrunc($untabify_tag,$maxtaglen); - my $taglen = ::mbswidth($untabify_tag); - my $maxstrlen = $termcol - $taglen - 1; - $untabify_str = mbtrunc($untabify_str,$maxstrlen); - my $strlen = ::mbswidth($untabify_str); - my $truncated_tag = ""; - my $truncated_str = ""; - if($termcol - $taglen < 2) { - $truncated_tag = ">"; - } else { - if($termcol - $taglen - $strlen <= 2) { - $truncated_str = ">"; - } - } - $maxrow = ($row > $maxrow) ? $row : $maxrow; - printf($out_fh - ("%s%s%s%s". # up down \r eol - "%s%s". # tag trunc_tag - "%s%s%s%s". # color line trunc reset_color - "%s" # down - ), - "$up"x($currow - $row), "\n"x($row - $currow), "\r", $eol, - $untabify_tag,$truncated_tag, - $color, $untabify_str, $truncated_str, $reset_color, - "\n"x($maxrow - $row + 1)); - $currow = $maxrow + 1; - } - - sub print_linebuffer($) { - my $self = shift; - my ($fdno,$in_fh,$out_fh) = @_; - if(defined $self->{'exitstatus'}) { - # If the job is dead: close printing fh. Needed for --compress - close $self->fh($fdno,"w"); - if($opt::compress) { - if($?) { - ::error($opt::compress_program." failed."); - $self->set_exitstatus(255); - } - # Blocked reading in final round - for my $fdno (1,2) { ::set_fh_blocking($self->fh($fdno,'r')); } - } - if($opt::latestline) { $print_later{$self->row()} = $self; } - } - if(not $self->virgin()) { - if($Global::files or ($opt::results and not $Global::csvsep)) { - # Print filename - if($fdno == 1 and not $self->fh($fdno,"printed")) { - print $out_fh $self->tag(),$self->fh($fdno,"name"),"\n"; - if($Global::membuffer) { - push(@{$self->{'output'}{$fdno}}, $self->tag(), - $self->fh($fdno,"name")); - } - $self->set_fh($fdno,"printed",1); - } - # No need for reading $in_fh, as it is from "cat >/dev/null" - } else { - # Read halflines and print full lines - my $outputlength = 0; - my $halfline_ref = $self->{'halfline'}{$fdno}; - my ($buf,$i,$rv); - # 1310720 gives 1.2 GB/s - # 131072 gives 0.9 GB/s - # The optimal block size differs - # It has been measured on: - # AMD 6376: 60800 (>70k is also reasonable) - # Intel i7-3632QM: 52-59k, 170-175k - # seq 64 | ppar --_test $1 --lb \ - # 'yes {} `seq 1000`|head -c 10000000' >/dev/null - while($rv = sysread($in_fh, $buf, 60800)) { - $outputlength += $rv; - # TODO --recend - # Treat both \n and \r as line end - # Only test for \r if there is no \n - # Test: - # perl -e '$a="x"x1000000; - # $b="$a\r$a\n$a\r$a\n"; - # map { print $b,$_ } 1..10' - $i = ((rindex($buf,"\n")+1) || (rindex($buf,"\r")+1)); - if($i) { - if($opt::latestline) { - # Keep the latest full line - my $l = join('', @$halfline_ref, - substr($buf,0,$i-1)); - # "ab\rb\n" = "bb", but we cannot process that correctly. - # Line may be: - # foo \r bar \n - # foo \r bar \r baz \r - # If so: Remove 'foo \r' - $l =~ s/.*\r//g; - my $j = ((rindex($l,"\n")+1) || - (rindex($l,"\r")+1)); - $self->{$out_fh,'latestline'} = substr($l,$j); - # Remove the processed part - # by keeping the unprocessed part - @$halfline_ref = (substr($buf,$i)); - } else { - # One or more complete lines were found - if($Global::color) { - my $print = join("",@$halfline_ref, - substr($buf,0,$i)); - chomp($print); - my ($color,$reset_color) = $self->color(); - my $colortag = $color.$self->tag(); - # \n => reset \n color tag - $print =~ s{([\n\r])(?=.|$)} - {$reset_color$1$colortag}gs; - print($out_fh $colortag, $print, - $reset_color, "\n"); - } elsif($opt::tag or defined $opt::tagstring) { - # Replace ^ with $tag within the full line - if($Global::cache_replacement_eval) { - # Replace with the same value for tag - my $tag = $self->tag(); - unshift @$halfline_ref, $tag; - # TODO --recend that can be partially in - # @$halfline_ref - substr($buf,0,$i-1) =~ - s/([\n\r])(?=.|$)/$1$tag/gs; - } else { - # Replace with freshly computed tag-value - unshift @$halfline_ref, $self->tag(); - substr($buf,0,$i-1) =~ - s/([\n\r])(?=.|$)/$1.$self->tag()/gse; - } - # The length changed, - # so find the new ending pos - $i = ::max((rindex($buf,"\n")+1), - (rindex($buf,"\r")+1)); - # Print the partial line (halfline) - # and the last half - print $out_fh @$halfline_ref, substr($buf,0,$i); - } else { - # Print the partial line (halfline) - # and the last half - print $out_fh @$halfline_ref, substr($buf,0,$i); - } - # Buffer in memory for SQL and CSV-output - if($Global::membuffer) { - push(@{$self->{'output'}{$fdno}}, - @$halfline_ref, substr($buf,0,$i)); - } - # Remove the printed part by keeping the unprinted - @$halfline_ref = (substr($buf,$i)); - } - } else { - # No newline, so append to the halfline - push @$halfline_ref, $buf; - } - } - $self->add_returnsize($outputlength); - if($opt::latestline) { $self->print_latest_line($out_fh); } - } - if(defined $self->{'exitstatus'}) { - if($Global::files or ($opt::results and not $Global::csvsep)) { - $self->add_returnsize(-s $self->fh($fdno,"name")); - } else { - if($opt::latestline) { - # Force re-computing color if --colorfailed - if($opt::colorfailed) { delete $self->{'color'}; } - if($self->{$out_fh,'latestline'} ne "") { - $self->print_latest_line($out_fh); - } - if(@{$self->{'halfline'}{$fdno}}) { - my $l = join('', @{$self->{'halfline'}{$fdno}}); - if($l ne "") { - $self->{$out_fh,'latestline'} = $l; - } - } else { - $self->{$out_fh,'latestline'} = undef; - } - # Print latest line from jobs that are already done - while($print_later{$minvisible}) { - $print_later{$minvisible}->print_latest_line($out_fh); - delete $print_later{$minvisible}; - $minvisible++; - } - # Print latest line from jobs that are on screen now - for(my $row = $minvisible; - $row < $minvisible -1 + ::terminal_rows(); - $row++) { - $print_later{$row} and - $print_later{$row}->print_latest_line($out_fh); - } - } else { - # If the job is dead: print the remaining partial line - # read remaining (already done for $opt::latestline) - my $halfline_ref = $self->{'halfline'}{$fdno}; - if(grep /./, @$halfline_ref) { - my $returnsize = 0; - for(@{$self->{'halfline'}{$fdno}}) { - $returnsize += length $_; - } - $self->add_returnsize($returnsize); - if($opt::tag or defined $opt::tagstring) { - # Prepend $tag the the remaining half line - unshift @$halfline_ref, $self->tag(); - } - # Print the partial line (halfline) - print $out_fh @{$self->{'halfline'}{$fdno}}; - # Buffer in memory for SQL and CSV-output - if($Global::membuffer) { - push(@{$self->{'output'}{$fdno}}, @$halfline_ref); - } - @$halfline_ref = (); - } - } - } - if($self->fh($fdno,"rpid") and - CORE::kill 0, $self->fh($fdno,"rpid")) { - # decompress still running - } else { - # decompress done: close fh - close $in_fh; - if($? and $opt::compress) { - ::error($opt::decompress_program." failed."); - $self->set_exitstatus(255); - } - } - } - } - } -} - -sub free_ressources() { - my $self = shift; - if(not $opt::ungroup) { - my $fh; - for my $fdno (sort { $a <=> $b } keys %Global::fh) { - $fh = $self->fh($fdno,"w"); - $fh and close $fh; - $fh = $self->fh($fdno,"r"); - $fh and close $fh; - } - } -} - -sub print_parset($) { - # Wrap output with shell script code to set as variables - my $self = shift; - my ($fdno,$in_fh,$out_fh) = @_; - my $outputlength = 0; - - ::debug("parset","print $Global::parset"); - if($Global::parset eq "assoc") { - # Start: (done in parse_parset()) - # eval "`echo 'declare -A myassoc; myassoc=( - # Each: (done here) - # [$'a\tb']=$'a\tb\tc ddd' - # End: (done in wait_and_exit()) - # )'`" - print '[',::Q($self->{'commandline'}-> - replace_placeholders(["\257<\257>"],0,0)),']='; - } elsif($Global::parset eq "array") { - # Start: (done in parse_parset()) - # eval "`echo 'myassoc=( - # Each: (done here) - # $'a\tb\tc ddd' - # End: (done in wait_and_exit()) - # )'`" - } elsif($Global::parset eq "var") { - # Start: (done in parse_parset()) - # - # Each: (done here) - # var=$'a\tb\tc ddd' - # End: (done in wait_and_exit()) - # - if(not @Global::parset_vars) { - ::error("Too few named destination variables"); - ::wait_and_exit(255); - } - print shift @Global::parset_vars,"="; - } - local $/ = "\n"; - my $tag = $self->tag(); - my @out; - while(<$in_fh>) { - $outputlength += length $_; - # Tag lines with \r, too - $_ =~ s/(?<=[\r])(?=.|$)/$tag/gs; - push @out, $tag,$_; - } - # Remove last newline - # This often makes it easier to use the output in shell - @out and ${out[$#out]} =~ s/\n$//s; - print ::Q(join("",@out)),"\n"; - return $outputlength; -} - -sub print_normal($) { - my $self = shift; - my ($fdno,$in_fh,$out_fh) = @_; - my $buf; - close $self->fh($fdno,"w"); - if($? and $opt::compress) { - ::error($opt::compress_program." failed."); - $self->set_exitstatus(255); - } - if(not $self->virgin()) { - seek $in_fh, 0, 0; - # $in_fh is now ready for reading at position 0 - my $outputlength = 0; - my @output; - - if($Global::parset and $fdno == 1) { - $outputlength += $self->print_parset($fdno,$in_fh,$out_fh); - } elsif(defined $opt::tag or defined $opt::tagstring - or $Global::color or $opt::colorfailed) { - if($Global::color or $opt::colorfailed) { - my ($color,$reset_color) = $self->color(); - my $colortag = $color.$self->tag(); - # Read line by line - local $/ = "\n"; - while(<$in_fh>) { - $outputlength += length $_; - # Tag lines with \r, too - chomp; - s{([\n\r])(?=.|$)}{$reset_color$1$colortag}gs; - print $out_fh $colortag,$_,$reset_color,"\n"; - } - } else { - my $tag = $self->tag(); - my $pretag = 1; - my $s; - while(sysread($in_fh,$buf,32767)) { - $outputlength += length $buf; - $buf =~ s/(?<=[\r\n])(?=.)/$tag/gs; - print $out_fh ($pretag ? $tag : ""),$buf; - if($Global::membuffer) { - push @{$self->{'output'}{$fdno}}, - ($pretag ? $tag : ""),$buf; - } - # Should next print start with a tag? - $s = substr($buf, -1); - # This is faster than ($s eq "\n") || ($s eq "\r") - $pretag = ($s eq "\n") ? 1 : ($s eq "\r"); - } - } - } else { - # Most efficient way of copying data from $in_fh to $out_fh - # Intel i7-3632QM: 25k- - while(sysread($in_fh,$buf,32767)) { - print $out_fh $buf; - $outputlength += length $buf; - if($Global::membuffer) { - push @{$self->{'output'}{$fdno}}, $buf; - } - } - } - if($fdno == 1) { - $self->add_returnsize($outputlength); - } - close $in_fh; - if($? and $opt::compress) { - ::error($opt::decompress_program." failed."); - $self->set_exitstatus(255); - } - } -} - -sub print_results($) { - my $self = shift; - my ($fdno,$in_fh,$out_fh) = @_; - my $buf; - close $self->fh($fdno,"w"); - if($? and $opt::compress) { - ::error($opt::compress_program." failed."); - $self->set_exitstatus(255); - } - if(not $self->virgin()) { - seek $in_fh, 0, 0; - # $in_fh is now ready for reading at position 0 - my $outputlength = 0; - my @output; - - if($Global::membuffer) { - # Read data into membuffer - if($opt::tag or $opt::tagstring) { - # Read line by line - local $/ = "\n"; - my $tag = $self->tag(); - while(<$in_fh>) { - $outputlength += length $_; - # Tag lines with \r, too - $_ =~ s/(?<=[\r])(?=.|$)/$tag/gs; - push @{$self->{'output'}{$fdno}}, $tag, $_; - } - } else { - # Most efficient way of copying data from $in_fh to $out_fh - while(sysread($in_fh,$buf,60000)) { - $outputlength += length $buf; - push @{$self->{'output'}{$fdno}}, $buf; - } - } - } else { - # Not membuffer: No need to read the file - if($opt::compress) { - $outputlength = -1; - } else { - # Determine $outputlength = file length - seek($in_fh, 0, 2) || ::die_bug("cannot seek result"); - $outputlength = tell($in_fh); - } - } - if($fdno == 1) { $self->add_returnsize($outputlength); } - close $in_fh; - if($? and $opt::compress) { - ::error($opt::decompress_program." failed."); - $self->set_exitstatus(255); - } - } -} - -sub print_joblog($) { - my $self = shift; - my $cmd; - if($Global::verbose <= 1) { - $cmd = $self->replaced(); - } else { - # Verbose level > 1: Print the rsync and stuff - $cmd = $self->wrapped(); - } - # Newlines make it hard to parse the joblog - $cmd =~ s/\n/\0/g; - print $Global::joblog - join("\t", $self->seq(), $self->sshlogin()->string(), - $self->starttime(), sprintf("%10.3f",$self->runtime()), - $self->transfersize(), $self->returnsize(), - $self->exitstatus(), $self->exitsignal(), $cmd - ). "\n"; - flush $Global::joblog; - $self->set_job_in_joblog(); -} - -sub tag($) { - my $self = shift; - if(not defined $self->{'tag'} or not $Global::cache_replacement_eval) { - if(defined $opt::tag or defined $opt::tagstring) { - $self->{'tag'} = - ($self->{'commandline'}-> - replace_placeholders([$opt::tagstring],0,0)). - "\t"; - } else { - # No tag - $self->{'tag'} = ""; - } - } - return $self->{'tag'}; -} - -sub untabtag($) { - # tag with \t replaced with spaces - my $self = shift; - my $tag = $self->tag(); - if(not defined $self->{'untab'}{$tag}) { - $self->{'untab'}{$tag} = ::untabify($tag); - } - return $self->{'untab'}{$tag}; -} - -{ - my (@color,$eol,$reset_color,$init); - - sub init_color() { - if(not $init) { - $init = 1; - # color combinations that are readable: black/white text - # on colored background, but not white on yellow - my @color_combinations = - # Force each color code to have the same length in chars - # This will make \t work as expected - ((map { [sprintf("%03d",$_),"000"] } - 6..7,9..11,13..15,40..51,75..87,113..123,147..159, - 171..182,185..231,249..254), - (map { [sprintf("%03d",$_),231] } - 1..9,12..13,16..45,52..81,88..114,124..149, - 160..178,180,182..184,196..214,232..250)); - # reorder list so adjacent colors are dissimilar - # %23 and %7 were found experimentally - my @order = reverse sort { - (($a%23) <=> ($b%23)) - or - (($b%7) <=> ($a%7)); - } 0..$#color_combinations; - @order = @order[54 .. $#color_combinations, 0 .. 53]; - @color = map { - # TODO Can this be done with `tput` codes? - "\033[48;5;".$_->[0].";38;5;".$_->[1]."m" - } @color_combinations[ @order ]; - - # clr_eol el = clear to end of line - $eol = `sh -c "tput el /dev/null`; - chomp($eol); - if($eol eq "") { $eol = "\033[K"; } - # exit_attribute_mode sgr0 = turn off all attributes - $reset_color = `sh -c "tput sgr0 /dev/null`; - chomp($reset_color); - if($reset_color eq "") { $reset_color = "\033[m"; } - } - } - - sub color($) { - my $self = shift; - if(not defined $self->{'color'}) { - if($Global::color) { - # Choose a value based on the seq - $self->{'color'} = $color[$self->seq() % ($#color+1)].$eol; - $self->{'reset_color'} = $reset_color; - } else { - $self->{'color'} = ""; - $self->{'reset_color'} = ""; - } - if($opt::colorfailed) { - if($self->exitstatus()) { - # White on Red - # Can this be done more generally? - $self->{'color'} = - "\033[48;5;"."196".";38;5;"."231"."m".$eol; - $self->{'reset_color'} = $reset_color; - } - } - } - return ($self->{'color'},$self->{'reset_color'}); - } -} - -sub hostgroups($) { - my $self = shift; - if(not defined $self->{'hostgroups'}) { - $self->{'hostgroups'} = - $self->{'commandline'}->{'arg_list'}[0][0]->{'hostgroups'}; - } - return @{$self->{'hostgroups'}}; -} - -sub exitstatus($) { - my $self = shift; - return $self->{'exitstatus'}; -} - -sub set_exitstatus($$) { - my $self = shift; - my $exitstatus = shift; - if($exitstatus) { - # Overwrite status if non-zero - $self->{'exitstatus'} = $exitstatus; - } else { - # Set status but do not overwrite - # Status may have been set by --timeout - $self->{'exitstatus'} ||= $exitstatus; - } - $opt::sqlworker and - $Global::sql->update("SET Exitval = ? WHERE Seq = ".$self->seq(), - $exitstatus); -} - -sub reset_exitstatus($) { - my $self = shift; - undef $self->{'exitstatus'}; -} - -sub exitsignal($) { - my $self = shift; - return $self->{'exitsignal'}; -} - -sub set_exitsignal($$) { - my $self = shift; - my $exitsignal = shift; - $self->{'exitsignal'} = $exitsignal; - $opt::sqlworker and - $Global::sql->update("SET _Signal = ? WHERE Seq = ".$self->seq(), - $exitsignal); -} - -{ - my $total_jobs; - - sub should_we_halt { - # Should we halt? Immediately? Gracefully? - # Returns: N/A - my $job = shift; - my $limit; - if($Global::semaphore) { - # Emulate Bash's +128 if there is a signal - $Global::halt_exitstatus = - ($job->exitstatus() - or - $job->exitsignal() ? $job->exitsignal() + 128 : 0); - } - if($job->exitstatus() or $job->exitsignal()) { - # Job failed - $Global::exitstatus++; - $Global::total_failed++; - if($Global::halt_fail) { - ::status("$Global::progname: This job failed:", - $job->replaced()); - $limit = $Global::total_failed; - } - } elsif($Global::halt_success) { - ::status("$Global::progname: This job succeeded:", - $job->replaced()); - $limit = $Global::total_completed - $Global::total_failed; - } - if($Global::halt_done) { - ::status("$Global::progname: This job finished:", - $job->replaced()); - $limit = $Global::total_completed; - } - if(not defined $limit) { - return "" - } - # --halt # => 1..100 (number of jobs failed, 101 means > 100) - # --halt % => 1..100 (pct of jobs failed) - if($Global::halt_pct and not $Global::halt_count) { - $total_jobs ||= $Global::JobQueue->total_jobs(); - # From the pct compute the number of jobs that must fail/succeed - $Global::halt_count = $total_jobs * $Global::halt_pct; - } - if($limit >= $Global::halt_count) { - # At least N jobs have failed/succeded/completed - # or at least N% have failed/succeded/completed - # So we should prepare for exit - if($Global::halt_fail or $Global::halt_done) { - # Set exit status - if(not defined $Global::halt_exitstatus) { - if($Global::halt_pct) { - # --halt now,fail=X% or soon,fail=X% - # --halt now,done=X% or soon,done=X% - $Global::halt_exitstatus = - ::ceil($Global::total_failed / $total_jobs * 100); - } elsif($Global::halt_count) { - # --halt now,fail=X or soon,fail=X - # --halt now,done=X or soon,done=X - $Global::halt_exitstatus = - ::min($Global::total_failed,101); - } - if($Global::halt_count and $Global::halt_count == 1) { - # --halt now,fail=1 or soon,fail=1 - # --halt now,done=1 or soon,done=1 - # Emulate Bash's +128 if there is a signal - $Global::halt_exitstatus = - ($job->exitstatus() - or - $job->exitsignal() ? $job->exitsignal() + 128 : 0); - } - } - ::debug("halt","Pct: ",$Global::halt_pct, - " count: ",$Global::halt_count, - " status: ",$Global::halt_exitstatus,"\n"); - } elsif($Global::halt_success) { - $Global::halt_exitstatus = 0; - } - if($Global::halt_when eq "soon") { - $Global::start_no_new_jobs ||= 1; - if(scalar(keys %Global::running) > 0) { - # Only warn if there are more jobs running - ::status - ("$Global::progname: Starting no more jobs. ". - "Waiting for ". (keys %Global::running). - " jobs to finish."); - } - } - return($Global::halt_when); - } - return ""; - } -} - - -package CommandLine; - -sub new($) { - my $class = shift; - my $seq = shift; - my $commandref = shift; - $commandref || die; - my $arg_queue = shift; - my $context_replace = shift; - my $max_number_of_args = shift; # for -N and normal (-n1) - my $transfer_files = shift; - my $return_files = shift; - my $template_names = shift; - my $template_contents = shift; - my $replacecount_ref = shift; - my $len_ref = shift; - my %replacecount = %$replacecount_ref; - my %len = %$len_ref; - for (keys %$replacecount_ref) { - # Total length of this replacement string {} replaced with all args - $len{$_} = 0; - } - return bless { - 'command' => $commandref, - 'seq' => $seq, - 'len' => \%len, - 'arg_list' => [], - 'arg_list_flat' => [], - 'arg_list_flat_orig' => [undef], - 'arg_queue' => $arg_queue, - 'max_number_of_args' => $max_number_of_args, - 'replacecount' => \%replacecount, - 'context_replace' => $context_replace, - 'transfer_files' => $transfer_files, - 'return_files' => $return_files, - 'template_names' => $template_names, - 'template_contents' => $template_contents, - 'replaced' => undef, - }, ref($class) || $class; -} - -sub flush_cache() { - my $self = shift; - for my $arglist (@{$self->{'arg_list'}}) { - for my $arg (@$arglist) { - $arg->flush_cache(); - } - } - $self->{'arg_queue'}->flush_cache(); - $self->{'replaced'} = undef; -} - -sub seq($) { - my $self = shift; - return $self->{'seq'}; -} - -sub set_seq($$) { - my $self = shift; - $self->{'seq'} = shift; -} - -sub slot($) { - # Find the number of a free job slot and return it - # Uses: - # @Global::slots - list with free jobslots - # Returns: - # $jobslot = number of jobslot - my $self = shift; - if(not $self->{'slot'}) { - if(not @Global::slots) { - # $max_slot_number will typically be $Global::max_jobs_running - push @Global::slots, ++$Global::max_slot_number; - } - $self->{'slot'} = shift @Global::slots; - } - return $self->{'slot'}; -} - -{ - my $already_spread; - my $darwin_max_len; - - sub populate($) { - # Add arguments from arg_queue until the number of arguments or - # max line length is reached - # Uses: - # $Global::usable_command_line_length - # $opt::cat - # $opt::fifo - # $Global::JobQueue - # $opt::m - # $opt::X - # $Global::max_jobs_running - # Returns: N/A - my $self = shift; - my $next_arg; - my $max_len = $Global::usable_command_line_length || die; - if($^O eq "darwin") { - # Darwin's limit is affected by: - # * number of environment names (variables+functions) - # * size of environment - # * the length of arguments: - # a one-char argument lowers the limit by 5 - # To be safe assume all arguments are one-char - # The max_len is cached between runs, but if the size of - # the environment is different we need to recompute the - # usable max length for this run of GNU Parallel - # See https://unix.stackexchange.com/a/604943/2972 - if(not $darwin_max_len) { - my $envc = (keys %ENV); - my $envn = length join"",(keys %ENV); - my $envv = length join"",(values %ENV); - $darwin_max_len = -146+($max_len - $envn - $envv) - $envc*10; - ::debug("init", - "length: $darwin_max_len ". - "3+($max_len - $envn - $envv)/5 - $envc*2"); - } - $max_len = $darwin_max_len; - } - if($opt::cat or $opt::fifo) { - # Get the empty arg added by --pipepart (if any) - $Global::JobQueue->{'commandlinequeue'}->{'arg_queue'}->get(); - # $PARALLEL_TMP will point to a tempfile that will be used as {} - $Global::JobQueue->{'commandlinequeue'}->{'arg_queue'}-> - unget([Arg->new('"$PARALLEL_TMP"')]); - } - while (not $self->{'arg_queue'}->empty()) { - $next_arg = $self->{'arg_queue'}->get(); - if(not defined $next_arg) { - next; - } - $self->push($next_arg); - if($self->len() >= $max_len) { - # Command length is now > max_length - # If there are arguments: remove the last - # If there are no arguments: Error - # TODO stuff about -x opt_x - if($self->number_of_args() > 1) { - # There is something to work on - $self->{'arg_queue'}->unget($self->pop()); - last; - } else { - my $args = join(" ", map { $_->orig() } @$next_arg); - ::error("Command line too long (". - $self->len(). " >= ". - $max_len. - ") at input ". - $self->{'arg_queue'}->arg_number(). - ": ". - ((length $args > 50) ? - (substr($args,0,50))."..." : - $args)); - $self->{'arg_queue'}->unget($self->pop()); - ::wait_and_exit(255); - } - } - - if(defined $self->{'max_number_of_args'}) { - if($self->number_of_args() >= $self->{'max_number_of_args'}) { - last; - } - } - } - if(($opt::m or $opt::X) and not $already_spread - and $self->{'arg_queue'}->empty() and $Global::max_jobs_running) { - # -m or -X and EOF => Spread the arguments over all jobslots - # (unless they are already spread) - $already_spread ||= 1; - if($self->number_of_args() > 1) { - $self->{'max_number_of_args'} = - ::ceil($self->number_of_args()/$Global::max_jobs_running); - $Global::JobQueue->{'commandlinequeue'}->{'max_number_of_args'} = - $self->{'max_number_of_args'}; - $self->{'arg_queue'}->unget($self->pop_all()); - while($self->number_of_args() < $self->{'max_number_of_args'}) { - $self->push($self->{'arg_queue'}->get()); - } - } - $Global::JobQueue->flush_total_jobs(); - } - - if($opt::sqlmaster) { - # Insert the V1..Vn for this $seq in SQL table - # instead of generating one - $Global::sql->insert_records($self->seq(), $self->{'command'}, - $self->{'arg_list_flat_orig'}); - } - } -} - -sub push($) { - # Add one or more records as arguments - # Returns: N/A - my $self = shift; - my $record = shift; - push @{$self->{'arg_list_flat_orig'}}, map { $_->orig() } @$record; - push @{$self->{'arg_list_flat'}}, @$record; - push @{$self->{'arg_list'}}, $record; - # Make @arg available for {= =} - *Arg::arg = $self->{'arg_list_flat_orig'}; - - my $quote_arg = ($Global::quote_replace and not $Global::quoting); - my $col; - for my $perlexpr (keys %{$self->{'replacecount'}}) { - if($perlexpr =~ /^(-?\d+)(?:\D.*|)$/) { - # Positional replacement string - # Deal with negative positional replacement string - $col = ($1 < 0) ? $1 : $1-1; - if(defined($record->[$col])) { - $self->{'len'}{$perlexpr} += - length $record->[$col]->replace($perlexpr,$quote_arg,$self); - } - } else { - for my $arg (@$record) { - if(defined $arg) { - $self->{'len'}{$perlexpr} += - length $arg->replace($perlexpr,$quote_arg,$self); - } - } - } - } -} - -sub pop($) { - # Remove last argument - # Returns: - # the last record - my $self = shift; - my $record = pop @{$self->{'arg_list'}}; - # pop off arguments from @$record - splice @{$self->{'arg_list_flat_orig'}}, -($#$record+1), $#$record+1; - splice @{$self->{'arg_list_flat'}}, -($#$record+1), $#$record+1; - my $quote_arg = ($Global::quote_replace and not $Global::quoting); - for my $perlexpr (keys %{$self->{'replacecount'}}) { - if($perlexpr =~ /^(\d+) /) { - # Positional - defined($record->[$1-1]) or next; - $self->{'len'}{$perlexpr} -= - length $record->[$1-1]->replace($perlexpr,$quote_arg,$self); - } else { - for my $arg (@$record) { - if(defined $arg) { - $self->{'len'}{$perlexpr} -= - length $arg->replace($perlexpr,$quote_arg,$self); - } - } - } - } - return $record; -} - -sub pop_all($) { - # Remove all arguments and zeros the length of replacement perlexpr - # Returns: - # all records - my $self = shift; - my @popped = @{$self->{'arg_list'}}; - for my $perlexpr (keys %{$self->{'replacecount'}}) { - $self->{'len'}{$perlexpr} = 0; - } - $self->{'arg_list'} = []; - $self->{'arg_list_flat_orig'} = [undef]; - $self->{'arg_list_flat'} = []; - return @popped; -} - -sub number_of_args($) { - # The number of records - # Returns: - # number of records - my $self = shift; - # This is really the number of records - return $#{$self->{'arg_list'}}+1; -} - -sub number_of_recargs($) { - # The number of args in records - # Returns: - # number of args records - my $self = shift; - my $sum = 0; - my $nrec = scalar @{$self->{'arg_list'}}; - if($nrec) { - $sum = $nrec * (scalar @{$self->{'arg_list'}[0]}); - } - return $sum; -} - -sub args_as_string($) { - # Returns: - # all unmodified arguments joined with ' ' (similar to {}) - my $self = shift; - return (join " ", map { $_->orig() } - map { @$_ } @{$self->{'arg_list'}}); -} - -sub results_out($) { - sub max_file_name_length { - # Figure out the max length of a subdir - # TODO and the max total length - # Ext4 = 255,130816 - # Uses: - # $Global::max_file_length is set - # Returns: - # $Global::max_file_length - my $testdir = shift; - - my $upper = 100_000_000; - # Dir length of 8 chars is supported everywhere - my $len = 8; - my $dir = "d"x$len; - do { - rmdir($testdir."/".$dir); - $len *= 16; - $dir = "d"x$len; - } while ($len < $upper and mkdir $testdir."/".$dir); - # Then search for the actual max length between $len/16 and $len - my $min = $len/16; - my $max = $len; - while($max-$min > 5) { - # If we are within 5 chars of the exact value: - # it is not worth the extra time to find the exact value - my $test = int(($min+$max)/2); - $dir = "d"x$test; - if(mkdir $testdir."/".$dir) { - rmdir($testdir."/".$dir); - $min = $test; - } else { - $max = $test; - } - } - $Global::max_file_length = $min; - return $min; - } - - my $self = shift; - my $out = $self->replace_placeholders([$opt::results],0,0); - if($out eq $opt::results) { - # $opt::results simple string: Append args_as_dirname - my $args_as_dirname = $self->args_as_dirname(0); - # Output in: prefix/name1/val1/name2/val2/stdout - $out = $opt::results."/".$args_as_dirname; - if(-d $out or eval{ File::Path::mkpath($out); }) { - # OK - } else { - # mkpath failed: Argument too long or not quoted - # Set $Global::max_file_length, which will keep the individual - # dir names shorter than the max length - max_file_name_length($opt::results); - # Quote dirnames with + - $args_as_dirname = $self->args_as_dirname(1); - # prefix/name1/val1/name2/val2/ - $out = $opt::results."/".$args_as_dirname; - File::Path::mkpath($out); - } - $out .="/"; - } else { - if($out =~ m:/$:s) { - # / = dir - if(-d $out or eval{ File::Path::mkpath($out); }) { - # OK - } else { - ::error("Cannot make dir '$out'."); - ::wait_and_exit(255); - } - } else { - $out =~ m:(.*)/:s; - File::Path::mkpath($1); - } - } - return $out; -} - -{ - my %map; - my %stringmap; - my $sep; - - # test: '' . .. a. a.. + ++ 0..255 on fat12 ext4 - sub args_as_dirname($) { - # Returns: - # all arguments joined with '/' (similar to {}) - # Chars that are not safe on all file systems are quoted. - sub init() { - # ext4: / \t \n \0 \\ \r - # fat: 0..31 " * / : < > ? \ | Maybe also: # [ ] ; = , - # exfat: 128..255 - # Other FS: , [ ] { } ( ) ! ; " ' * ? < > | - # - # Quote these as: - # + = ++ - # \0 = +0 - # \t = +t - # \\ = +b (backslash) - # \n = +n - # \r = +r - # / = +z (zlash) - # ? = +y (whY?) - # " = +d (double quote) - # ' = +q (quote) - # * = +a (asterisk) - # < = +l (less than) - # > = +g (greater than) - # : = +k (kolon) - # ! = +x (eXclamation) - # | = +p (pipe) - # # = +h (hash) - # ; = +s (semicolon) - # = = +e (equal) - # , = +c (comma) - # 1..32 128..255 = +XX (hex value) - # [ ] = +e +f - # ( ) = +i +j - # { } = +v +w - # Quote '' as +m (eMpty) - # Quote . as +_ - # Quote .. as +__ - # (Unused: ou) - %map = qw( - + ++ - \0 +0 - \t +t - \\ +b - \n +n - \r +r - / +z - ? +y - " +d - ' +q - * +a - < +l - > +g - : +k - ! +x - | +p - # +h - ; +s - = +e - , +c - [ +e - ( +i - { +v - ] +f - ) +j - } +w - ); - # 1..32 128..255 = +XX (hex value) - map { $map{sprintf "%c",$_} = sprintf "+%02x",$_ } 1..32, 128..255; - # Default value = itself - map { $map{sprintf "%c",$_} ||= sprintf "%c",$_ } 0..255; - # Quote '' as +m (eMpty) - $stringmap{""} = "+m"; - # Quote . as +_ - $stringmap{"."} = "+_"; - # Quote .. as +__ - $stringmap{".."} = "+__"; - # Set dir separator - eval 'use File::Spec; $sep = File::Spec->catfile("", "");'; - $sep ||= '/'; - } - # If $Global::max_file_length: Keep subdirs < $Global::max_file_length - my $self = shift; - my $quote = shift; - my @res = (); - if(not $sep) { init(); } - - for my $rec_ref (@{$self->{'arg_list'}}) { - # If headers are used, sort by them. - # Otherwise keep the order from the command line. - my @header_indexes_sorted = header_indexes_sorted($#$rec_ref+1); - for my $n (@header_indexes_sorted) { - CORE::push(@res, - $Global::input_source_header{$n}, - $quote ? - ( - grep { $_ ne "\0noarg" } map { - my $s = $_; - # Quote + as ++ - $s =~ s/(.)/$map{$1}/gs; - if($Global::max_file_length) { - # Keep each subdir shorter than the longest - # allowed file name - $s = substr($s,0,$Global::max_file_length); - } - $s; } - $rec_ref->[$n-1]->orig() - ) : - ( - grep { $_ ne "\0noarg" } map { - my $s = $_; - # Quote / as +z and + as ++ - $s =~ s/($sep|\+)/$map{$1}/gos; - if($Global::max_file_length) { - # Keep each subdir shorter than the longest - # allowed file name - $s = substr($s,0,$Global::max_file_length); - } - $s; } - $rec_ref->[$n-1]->orig() - ) - ); - } - } - return join $sep, map { $stringmap{$_} || $_ } @res; - } -} - -sub header_indexes_sorted($) { - # Sort headers first by number then by name. - # E.g.: 1a 1b 11a 11b - # Returns: - # Indexes of %Global::input_source_header sorted - my $max_col = shift; - - no warnings 'numeric'; - for my $col (1 .. $max_col) { - # Make sure the header is defined. If it is not: use column number - if(not defined $Global::input_source_header{$col}) { - $Global::input_source_header{$col} = $col; - } - } - my @header_indexes_sorted = sort { - # Sort headers numerically then asciibetically - $Global::input_source_header{$a} <=> $Global::input_source_header{$b} - or - $Global::input_source_header{$a} cmp $Global::input_source_header{$b} - } 1 .. $max_col; - return @header_indexes_sorted; -} - -sub len($) { - # Uses: - # @opt::shellquote - # The length of the command line with args substituted - my $self = shift; - my $len = 0; - # Add length of the original command with no args - # Length of command w/ all replacement args removed - $len += $self->{'len'}{'noncontext'} + @{$self->{'command'}} -1; - ::debug("length", "noncontext + command: $len\n"); - # MacOS has an overhead of 8 bytes per argument - my $darwin = ($^O eq "darwin") ? 8 : 0; - my $recargs = $self->number_of_recargs(); - if($self->{'context_replace'}) { - # Context is duplicated for each arg - $len += $recargs * $self->{'len'}{'context'}; - for my $replstring (keys %{$self->{'replacecount'}}) { - # If the replacements string is more than once: mulitply its length - $len += $self->{'len'}{$replstring} * - $self->{'replacecount'}{$replstring}; - ::debug("length", $replstring, " ", $self->{'len'}{$replstring}, "*", - $self->{'replacecount'}{$replstring}, "\n"); - } - # echo 11 22 33 44 55 66 77 88 99 1010 - # echo 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 - # 5 + ctxgrp*arg - ::debug("length", "Ctxgrp: ", $self->{'len'}{'contextgroups'}, - " Groups: ", $self->{'len'}{'noncontextgroups'}, "\n"); - # Add space between context groups - $len += ($recargs-1) * ($self->{'len'}{'contextgroups'}); - if($darwin) { - $len += $recargs * $self->{'len'}{'contextgroups'} * $darwin; - } - } else { - # Each replacement string may occur several times - # Add the length for each time - $len += 1*$self->{'len'}{'context'}; - ::debug("length", "context+noncontext + command: $len\n"); - for my $replstring (keys %{$self->{'replacecount'}}) { - # (space between recargs + length of replacement) - # * number this replacement is used - $len += ($recargs -1 + $self->{'len'}{$replstring}) * - $self->{'replacecount'}{$replstring}; - if($darwin) { - $len += ($recargs * $self->{'replacecount'}{$replstring} - * $darwin); - } - } - } - if(defined $Global::parallel_env) { - # If we are using --env, add the prefix for that, too. - $len += length $Global::parallel_env; - } - if($Global::quoting) { - # Pessimistic length if -q is set - # Worse than worst case: ' => "'" + " => '"' - # TODO can we count the number of expanding chars? - # and count them in arguments, too? - $len *= 3; - } - if(@opt::shellquote) { - # Pessimistic length if --shellquote is set - # Worse than worst case: ' => "'" - for(@opt::shellquote) { - $len *= 3; - } - $len *= 5; - } - if(@opt::sshlogin) { - # Pessimistic length if remote - # Worst case is BASE64 encoding 3 bytes -> 4 bytes - $len = int($len*4/3); - } - return $len; -} - -sub replaced($) { - # Uses: - # $Global::quote_replace - # $Global::quoting - # Returns: - # $replaced = command with place holders replaced and prepended - my $self = shift; - if(not defined $self->{'replaced'}) { - # Don't quote arguments if the input is the full command line - my $quote_arg = ($Global::quote_replace and not $Global::quoting); - # or if ($opt::cat or $opt::pipe) as they use $PARALLEL_TMP - $quote_arg = ($opt::cat || $opt::fifo) ? 0 : $quote_arg; - $self->{'replaced'} = $self-> - replace_placeholders($self->{'command'},$Global::quoting, - $quote_arg); - my $len = length $self->{'replaced'}; - if ($len != $self->len()) { - ::debug("length", $len, " != ", $self->len(), - " ", $self->{'replaced'}, "\n"); - } else { - ::debug("length", $len, " == ", $self->len(), - " ", $self->{'replaced'}, "\n"); - } - } - return $self->{'replaced'}; -} - -sub replace_placeholders($$$$) { - # Replace foo{}bar with fooargbar - # Input: - # $targetref = command as shell words - # $quote = should everything be quoted? - # $quote_arg = should replaced arguments be quoted? - # Uses: - # @Arg::arg = arguments as strings to be use in {= =} - # Returns: - # @target with placeholders replaced - my $self = shift; - my $targetref = shift; - my $quote = shift; - my $quote_arg = shift; - my %replace; - - # Token description: - # \0spc = unquoted space - # \0end = last token element - # \0ign = dummy token to be ignored - # \257<...\257> = replacement expression - # " " = quoted space, that splits -X group - # text = normal text - possibly part of -X group - my $spacer = 0; - my @tokens = grep { length $_ > 0 } map { - if(/^\257<|^ $/) { - # \257<...\257> or space - $_ - } else { - # Split each space/tab into a token - split /(?=\s)|(?<=\s)/ - } - } - # Split \257< ... \257> into own token - map { split /(?=\257<)|(?<=\257>)/ } - # Insert "\0spc" between every element - # This space should never be quoted - map { $spacer++ ? ("\0spc",$_) : $_ } - map { $_ eq "" ? "\0empty" : $_ } - @$targetref; - - if(not @tokens) { - # @tokens is empty: Return empty array - return @tokens; - } - ::debug("replace", "Tokens ".join":",@tokens,"\n"); - # Make it possible to use $arg[2] in {= =} - *Arg::arg = $self->{'arg_list_flat_orig'}; - # Flat list: - # $self->{'arg_list'} = [ [Arg11, Arg12], [Arg21, Arg22], [Arg31, Arg32] ] - # $self->{'arg_list_flat'} = [ Arg11, Arg12, Arg21, Arg22, Arg31, Arg32 ] - if(not @{$self->{'arg_list_flat'}}) { - @{$self->{'arg_list_flat'}} = Arg->new(""); - } - my $argref = $self->{'arg_list_flat'}; - # Number of arguments - used for positional arguments - my $n = $#$argref+1; - - # $self is actually a CommandLine-object, - # but it looks nice to be able to say {= $job->slot() =} - my $job = $self; - # @replaced = tokens with \257< \257> replaced - my @replaced; - if($self->{'context_replace'}) { - my @ctxgroup; - for my $t (@tokens,"\0end") { - # \0end = last token was end of tokens. - if($t eq "\t" or $t eq " " or $t eq "\0end" or $t eq "\0spc") { - # Context group complete: Replace in it - if(grep { /^\257} - { - if($1) { - # Positional replace - # Find the relevant arg and replace it - ($argref->[$1 > 0 ? $1-1 : $n+$1] ? # If defined: replace - $argref->[$1 > 0 ? $1-1 : $n+$1]-> - replace($2,$quote_arg,$self) - : ""); - } else { - # Normal replace - $normal_replace ||= 1; - ($arg ? $arg->replace($2,$quote_arg,$self) : ""); - } - }sgxe) { - # Token is \257<..\257> - } else { - if($Global::escape_string_present) { - # Command line contains \257: - # Unescape it \257\256 => \257 - $a =~ s/\257\256/\257/g; - } - } - $a - } @ctxgroup; - $normal_replace or last; - $space = "\0spc"; - } - } else { - # Context group has no a replacement string: Copy it once - CORE::push @replaced, map { - $Global::escape_string_present and s/\257\256/\257/g; $_; - } @ctxgroup; - } - # New context group - @ctxgroup=(); - } - if($t eq "\0spc" or $t eq " ") { - CORE::push @replaced,$t; - } else { - CORE::push @ctxgroup,$t; - } - } - } else { - # @group = @token - # Replace in group - # Push output - # repquote = no if {} first on line, no if $quote, yes otherwise - for my $t (@tokens) { - if($t =~ /^\257} - { - if($1) { - # Positional replace - # Find the relevant arg and replace it - ($argref->[$1 > 0 ? $1-1 : $n+$1] ? - # If defined: replace - $argref->[$1 > 0 ? $1-1 : $n+$1]-> - replace($2,$quote_arg,$self) - : ""); - } else { - # Normal replace - $normal_replace ||= 1; - ($arg ? $arg->replace($2,$quote_arg,$self) : ""); - } - }sgxe; - CORE::push @replaced, $space, $a; - $normal_replace or last; - $space = "\0spc"; - } - } else { - # No replacement - CORE::push @replaced, map { - $Global::escape_string_present and s/\257\256/\257/g; $_; - } $t; - } - } - } - *Arg::arg = []; - ::debug("replace","Replaced: ".join":",@replaced,"\n"); - - # Put tokens into groups that may be quoted. - my @quotegroup; - my @quoted; - for (map { $_ eq "\0empty" ? "" : $_ } - grep { $_ ne "\0ign" and $_ ne "\0noarg" and $_ ne "'\0noarg'" } - @replaced, "\0end") { - if($_ eq "\0spc" or $_ eq "\0end") { - # \0spc splits quotable groups - if($quote) { - if(@quotegroup) { - CORE::push @quoted, ::Q(join"",@quotegroup);; - } - } else { - CORE::push @quoted, join"",@quotegroup; - } - @quotegroup = (); - } else { - CORE::push @quotegroup, $_; - } - } - ::debug("replace","Quoted: ".join":",@quoted,"\n"); - return wantarray ? @quoted : "@quoted"; -} - -sub skip($) { - # Skip this job - my $self = shift; - $self->{'skip'} = 1; -} - - -package CommandLineQueue; - -sub new($) { - my $class = shift; - my $commandref = shift; - my $read_from = shift; - my $context_replace = shift || 0; - my $max_number_of_args = shift; - my $transfer_files = shift; - my $return_files = shift; - my $template_names = shift; - my $template_contents = shift; - my @unget = (); - my $posrpl; - my ($replacecount_ref, $len_ref); - my @command = @$commandref; - my $seq = 1; - # Replace replacement strings with {= perl expr =} - # '{=' 'perlexpr' '=}' => '{= perlexpr =}' - @command = merge_rpl_parts(@command); - - # Protect matching inside {= perl expr =} - # by replacing {= and =} with \257< and \257> - # in options that can contain replacement strings: - # @command, --transferfile, --return, - # --tagstring, --workdir, --results - for(@command, @$transfer_files, @$return_files, - @$template_names, @$template_contents, - $opt::tagstring, $opt::workdir, $opt::results, $opt::retries, - @opt::filter) { - # Skip if undefined - defined($_) or next; - # Escape \257 => \257\256 - $Global::escape_string_present += s/\257/\257\256/g; - # Needs to match rightmost left parens (Perl defaults to leftmost) - # to deal with: {={==} and {={==}=} - # Replace {= -> \257< and =} -> \257> - # - # Complex way to do: - # s/{=(.*)=}/\257<$1\257>/g - # which would not work - s[\Q$Global::parensleft\E # Match {= - # Match . unless the next string is {= or =} - # needed to force matching the shortest {= =} - ((?:(?! \Q$Global::parensleft\E|\Q$Global::parensright\E ).)*?) - \Q$Global::parensright\E ] # Match =} - {\257<$1\257>}gxs; - for my $rpl (sort { length $b <=> length $a } keys %Global::rpl) { - # Replace long --rpl's before short ones, as a short may be a - # substring of a long: - # --rpl '% s/a/b/' --rpl '%% s/b/a/' - # - # Replace the shorthand string (--rpl) - # with the {= perl expr =} - # - # Avoid searching for shorthand strings inside existing {= perl expr =} - # - # Replace $$1 in {= perl expr =} with groupings in shorthand string - # - # --rpl '{/(\.\S+)/(\.\S+)} s/$$1/$$2/g;' - # echo {/.tar/.gz} ::: UU.tar.gz - my ($prefix,$grp_regexp,$postfix) = - $rpl =~ /^( [^(]* ) # Prefix - e.g. {%% - ( \(.*\) )? # Group capture regexp - e.g (.*) - ( [^)]* )$ # Postfix - e.g } - /xs; - $grp_regexp ||= ''; - my $rplval = $Global::rpl{$rpl}; - while(s{( (?: ^|\257> ) (?: [^\257]*|[\257][^<>] )*? ) - # Don't replace after \257 unless \257> - \Q$prefix\E $grp_regexp \Q$postfix\E} - { - # The start remains the same - my $unchanged = $1; - # Dummy entry to start at 1. - my @grp = (1); - # $2 = first ()-group in $grp_regexp - # Put $2 in $grp[1], Put $3 in $grp[2] - # so first ()-group in $grp_regexp is $grp[1]; - for(my $i = 2; defined $grp[$#grp]; $i++) { - push @grp, eval '$'.$i; - } - my $rv = $rplval; - # replace $$1 with $_pAr_gRp1, $$2 with $_pAr_gRp2 - # in the code to be executed - $rv =~ s/\$\$ (\d+)/\$_pAr_gRp$1/gx; - # prepend with $_pAr_gRp1 = perlquote($1), - my $set_args = ""; - for(my $i = 1;defined $grp[$i]; $i++) { - $set_args .= "\$_pAr_gRp$i = \"" . - ::perl_quote_scalar($grp[$i]) . "\";"; - } - $unchanged . "\257<" . $set_args . $rv . "\257>" - }gxes) { - } - # Do the same for the positional replacement strings - $posrpl = $rpl; - if($posrpl =~ s/^\{//) { - # Only do this if the shorthand start with { - $prefix=~s/^\{//; - # Don't replace after \257 unless \257> - while(s{( (?: ^|\257> ) (?: [^\257]*|[\257][^<>] )*? ) - \{(-?\d+) \s* \Q$prefix\E $grp_regexp \Q$postfix\E} - { - # The start remains the same - my $unchanged = $1; - my $position = $2; - # Dummy entry to start at 1. - my @grp = (1); - # $3 = first ()-group in $grp_regexp - # Put $3 in $grp[1], Put $4 in $grp[2] - # so first ()-group in $grp_regexp is $grp[1]; - for(my $i = 3; defined $grp[$#grp]; $i++) { - push @grp, eval '$'.$i; - } - my $rv = $rplval; - # replace $$1 with $_pAr_gRp1, $$2 with $_pAr_gRp2 - # in the code to be executed - $rv =~ s/\$\$ (\d+)/\$_pAr_gRp$1/gx; - # prepend with $_pAr_gRp1 = perlquote($1), - my $set_args = ""; - for(my $i = 1;defined $grp[$i]; $i++) { - $set_args .= "\$_pAr_gRp$i = \"" . - ::perl_quote_scalar($grp[$i]) . "\";"; - } - $unchanged . "\257<" . $position . $set_args . $rv . "\257>" - }gxes) { - } - } - } - } - # Add {} if no replacement strings in @command - ($replacecount_ref, $len_ref, @command) = - replacement_counts_and_lengths($transfer_files, $return_files, - $template_names, $template_contents, - @command); - if("@command" =~ /^[^ \t\n=]*\257append()) { - $seq = $Global::sql->max_seq() + 1; - } - - return bless { - ('unget' => \@unget, - 'command' => \@command, - 'replacecount' => $replacecount_ref, - 'arg_queue' => RecordQueue->new($read_from,$opt::colsep), - 'context_replace' => $context_replace, - 'len' => $len_ref, - 'max_number_of_args' => $max_number_of_args, - 'size' => undef, - 'transfer_files' => $transfer_files, - 'return_files' => $return_files, - 'template_names' => $template_names, - 'template_contents' => $template_contents, - 'seq' => $seq, - ) - }, ref($class) || $class; -} - -sub merge_rpl_parts($) { - # '{=' 'perlexpr' '=}' => '{= perlexpr =}' - # Input: - # @in = the @command as given by the user - # Uses: - # $Global::parensleft - # $Global::parensright - # Returns: - # @command with parts merged to keep {= and =} as one - my @in = @_; - my @out; - my $l = quotemeta($Global::parensleft); - my $r = quotemeta($Global::parensright); - - while(@in) { - my $s = shift @in; - $_ = $s; - # Remove matching (right most) parens - while(s/(.*)$l.*?$r/$1/os) {} - if(/$l/o) { - # Missing right parens - while(@in) { - $s .= " ".shift @in; - $_ = $s; - while(s/(.*)$l.*?$r/$1/os) {} - if(not /$l/o) { - last; - } - } - } - push @out, $s; - } - return @out; -} - -sub replacement_counts_and_lengths($$@) { - # Count the number of different replacement strings. - # Find the lengths of context for context groups and non-context - # groups. - # If no {} found in @command: add it to @command - # - # Input: - # \@transfer_files = array of filenames to transfer - # \@return_files = array of filenames to return - # \@template_names = array of names to copy to - # \@template_contents = array of contents to write - # @command = command template - # Output: - # \%replacecount, \%len, @command - my $transfer_files = shift; - my $return_files = shift; - my $template_names = shift; - my $template_contents = shift; - my @command = @_; - my (%replacecount,%len); - my $sum = 0; - while($sum == 0) { - # Count how many times each replacement string is used - my @cmd = @command; - my $contextlen = 0; - my $noncontextlen = 0; - my $contextgroups = 0; - for my $c (@cmd) { - while($c =~ s/ \257<( (?: [^\257]*|[\257][^<>] )*?)\257> /\000/xs) { - # %replacecount = { "perlexpr" => number of times seen } - # e.g { "s/a/b/" => 2 } - $replacecount{$1}++; - $sum++; - } - # Measure the length of the context around the {= perl expr =} - # Use that {=...=} has been replaced with \000 above - # So there is no need to deal with \257< - while($c =~ s/ (\S*\000\S*) //xs) { - my $w = $1; - $w =~ tr/\000//d; # Remove all \000's - $contextlen += length($w); - $contextgroups++; - } - # All {= perl expr =} have been removed: The rest is non-context - $noncontextlen += length $c; - } - for(@$transfer_files, @$return_files, - @$template_names, @$template_contents, - @opt::filter, - $opt::tagstring, $opt::workdir, $opt::results, $opt::retries) { - # Options that can contain replacement strings - defined($_) or next; - my $t = $_; - while($t =~ s/ \257<( (?: [^\257]*|[\257][^<>] )* )\257> //xs) { - # %replacecount = { "perlexpr" => number of times seen } - # e.g { "$_++" => 2 } - # But for tagstring we just need to mark it as seen - $replacecount{$1} ||= 1; - } - } - if($opt::bar) { - # If the command does not contain {} force it to be computed - # as it is being used by --bar - $replacecount{""} ||= 1; - } - - $len{'context'} = 0+$contextlen; - $len{'noncontext'} = $noncontextlen; - $len{'contextgroups'} = $contextgroups; - $len{'noncontextgroups'} = @cmd-$contextgroups; - ::debug("length", "@command Context: ", $len{'context'}, - " Non: ", $len{'noncontext'}, " Ctxgrp: ", $len{'contextgroups'}, - " NonCtxGrp: ", $len{'noncontextgroups'}, "\n"); - if($sum == 0) { - if(not @command) { - # Default command = {} - @command = ("\257<\257>"); - } elsif(($opt::pipe or $opt::pipepart) - and not $opt::fifo and not $opt::cat) { - # With --pipe / --pipe-part you can have no replacement - last; - } else { - # Append {} to the command if there are no {...}'s and no {=...=} - push @command, ("\257<\257>"); - } - } - } - return(\%replacecount,\%len,@command); -} - -sub get($) { - my $self = shift; - if(@{$self->{'unget'}}) { - my $cmd_line = shift @{$self->{'unget'}}; - return ($cmd_line); - } else { - if($opt::sqlworker) { - # Get the sequence number from the SQL table - $self->set_seq($SQL::next_seq); - # Get the command from the SQL table - $self->{'command'} = $SQL::command_ref; - my @command; - # Recompute replace counts based on the read command - ($self->{'replacecount'}, - $self->{'len'}, @command) = - replacement_counts_and_lengths($self->{'transfer_files'}, - $self->{'return_files'}, - $self->{'template_name'}, - $self->{'template_contents'}, - @$SQL::command_ref); - if("@command" =~ /^[^ \t\n=]*\257new($self->seq(), - $self->{'command'}, - $self->{'arg_queue'}, - $self->{'context_replace'}, - $self->{'max_number_of_args'}, - $self->{'transfer_files'}, - $self->{'return_files'}, - $self->{'template_names'}, - $self->{'template_contents'}, - $self->{'replacecount'}, - $self->{'len'}, - ); - $cmd_line->populate(); - ::debug("run","cmd_line->number_of_args ", - $cmd_line->number_of_args(), "\n"); - if(not $Global::no_more_input and ($opt::pipe or $opt::pipepart)) { - if($cmd_line->replaced() eq "") { - # Empty command - pipe requires a command - ::error("--pipe/--pipepart must have a command to pipe into ". - "(e.g. 'cat')."); - ::wait_and_exit(255); - } - } elsif($cmd_line->number_of_args() == 0) { - # We did not get more args - maybe at EOF string? - return undef; - } - $self->set_seq($self->seq()+1); - return $cmd_line; - } -} - -sub unget($) { - my $self = shift; - unshift @{$self->{'unget'}}, @_; -} - -sub empty($) { - my $self = shift; - my $empty = (not @{$self->{'unget'}}) && - $self->{'arg_queue'}->empty(); - ::debug("run", "CommandLineQueue->empty $empty"); - return $empty; -} - -sub seq($) { - my $self = shift; - return $self->{'seq'}; -} - -sub set_seq($$) { - my $self = shift; - $self->{'seq'} = shift; -} - -sub quote_args($) { - my $self = shift; - # If there is not command emulate |bash - return $self->{'command'}; -} - - -package Limits::Command; - -# Maximal command line length (for -m and -X) -sub max_length($) { - # Find the max_length of a command line and cache it - # Returns: - # number of chars on the longest command line allowed - if(not $Limits::Command::line_max_len) { - # Disk cache of max command line length - my $len_cache = $Global::cache_dir . "/tmp/sshlogin/" . ::hostname() . - "/linelen"; - my $cached_limit; - local $/ = undef; - if(open(my $fh, "<", $len_cache)) { - $cached_limit = <$fh>; - $cached_limit || ::warning("Invalid content in $len_cache"); - close $fh; - } - if(not $cached_limit) { - $cached_limit = real_max_length(); - # If $HOME is write protected: Do not fail - my $dir = ::dirname($len_cache); - -d $dir or eval { File::Path::mkpath($dir); }; - open(my $fh, ">", $len_cache.$$); - print $fh $cached_limit; - close $fh; - rename $len_cache.$$, $len_cache || ::die_bug("rename cache file"); - } - $Limits::Command::line_max_len = tmux_length($cached_limit); - } - return int($Limits::Command::line_max_len); -} - -sub real_max_length() { - # Find the max_length of a command line - # Returns: - # The maximal command line length with 1 byte arguments - # return find_max(" c"); - return find_max("c"); -} - -sub find_max($) { - my $string = shift; - # This is slow on Cygwin, so give Cygwin users a warning - if($^O eq "cygwin" or $^O eq "msys") { - ::warning("Finding the maximal command line length. ". - "This may take up to 1 minute.") - } - # Use an upper bound of 100 MB if the shell allows for infinite - # long lengths - my $upper = 100_000_000; - my $lower; - # 1000 is supported everywhere, so the search can start anywhere 1..999 - # 324 makes the search much faster on Cygwin, so let us use that - my $len = 324; - do { - if($len > $upper) { return $len }; - $lower = $len; - $len *= 4; - ::debug("init", "Maxlen: $lower<$len<$upper(".($upper-$lower)."): "); - } while (is_acceptable_command_line_length($len,$string)); - # Then search for the actual max length between - # last successful length ($len/16) and upper bound - return binary_find_max(int($len/16),$len,$string); -} - - -# Prototype forwarding -sub binary_find_max($$$); -sub binary_find_max($$$) { - # Given a lower and upper bound find the max (length or args) of a - # command line - # Returns: - # number of chars on the longest command line allowed - my ($lower, $upper, $string) = (@_); - if($lower == $upper - or $lower == $upper-1 - or $lower/$upper > 0.99) { - # $lower is +- 1 or within 1%: Don't search more - return $lower; - } - # Unevenly split binary search which is faster for Microsoft Windows. - # Guessing too high is cheap. Guessing too low is expensive. - my $split = ($^O eq "cygwin" or $^O eq "msys") ? 0.93 : 0.5; - my $middle = int (($upper-$lower)*$split + $lower); - ::debug("init", "Maxlen: $lower<$middle<$upper(".($upper-$lower)."): "); - if (is_acceptable_command_line_length($middle,$string)) { - return binary_find_max($middle,$upper,$string); - } else { - return binary_find_max($lower,$middle,$string); - } -} - -{ - my $prg; - - sub is_acceptable_command_line_length($$) { - # Test if a command line of this length can run - # in the current environment - # If the string is " x" it tests how many args are allowed - # Returns: - # 0 if the command line length is too long - # 1 otherwise - my $len = shift; - my $string = shift; - if($Global::parallel_env) { - $len += length $Global::parallel_env; - } - # Force using non-built-in command - $prg ||= ::which("echo"); - my $l = length ::qqx("$prg ".${string}x(($len-1-length $prg)/length $string)); - if($l < $len/2) { - # The command returned OK, but did not output $len chars - # => this failed (Centos3 does this craziness) - return 0 - } - ::debug("init", "$len=$?\n"); - return not $?; - } -} - -sub tmux_length($) { - # If $opt::tmux set, find the limit for tmux - # tmux 1.8 has a 2kB limit - # tmux 1.9 has a 16kB limit - # tmux 2.0 has a 16kB limit - # tmux 2.1 has a 16kB limit - # tmux 2.2 has a 16kB limit - # Input: - # $len = maximal command line length - # Returns: - # $tmux_len = maximal length runable in tmux - local $/ = "\n"; - my $len = shift; - if($opt::tmux) { - $ENV{'PARALLEL_TMUX'} ||= "tmux"; - if(not ::which($ENV{'PARALLEL_TMUX'})) { - ::error($ENV{'PARALLEL_TMUX'}." not found in \$PATH."); - ::wait_and_exit(255); - } - my @out; - for my $l (1, 2020, 16320, 30000, $len) { - my $tmpfile = ::tmpname("tms"); - my $qtmp = ::Q($tmpfile); - my $tmuxcmd = $ENV{'PARALLEL_TMUX'}. - " -S $qtmp new-session -d -n echo $l". - ("t"x$l). " && echo $l; rm -f $qtmp"; - push @out, ::qqx($tmuxcmd); - ::rm($tmpfile); - } - ::debug("tmux","tmux-out ",@out); - chomp @out; - # The arguments is given 3 times on the command line - # and the tmux wrapping is around 30 chars - # (29 for tmux1.9, 33 for tmux1.8) - my $tmux_len = ::max(@out); - $len = ::min($len,int($tmux_len/4-33)); - ::debug("tmux","tmux-length ",$len); - } - return $len; -} - - -package RecordQueue; - -sub new($) { - my $class = shift; - my $fhs = shift; - my $colsep = shift; - my @unget = (); - my $arg_sub_queue; - if($opt::sqlworker) { - # Open SQL table - $arg_sub_queue = SQLRecordQueue->new(); - } elsif(defined $colsep) { - # Open one file with colsep or CSV - $arg_sub_queue = RecordColQueue->new($fhs); - } else { - # Open one or more files if multiple -a - $arg_sub_queue = MultifileQueue->new($fhs); - } - return bless { - 'unget' => \@unget, - 'arg_number' => 0, - 'arg_sub_queue' => $arg_sub_queue, - }, ref($class) || $class; -} - -sub get($) { - # Returns: - # reference to array of Arg-objects - my $self = shift; - if(@{$self->{'unget'}}) { - $self->{'arg_number'}++; - # Flush cached computed replacements in Arg-objects - # To fix: parallel --bar echo {%} ::: a b c ::: d e f - my $ret = shift @{$self->{'unget'}}; - if($ret) { - map { $_->flush_cache() } @$ret; - } - return $ret; - } - my $ret = $self->{'arg_sub_queue'}->get(); - if($ret) { - if(grep { index($_->orig(),"\0") > 0 } @$ret) { - # Allow for \0 in position 0 because GNU Parallel uses "\0noarg" - # to mean no-string - ::warning("A NUL character in the input was replaced with \\0.", - "NUL cannot be passed through in the argument list.", - "Did you mean to use the --null option?"); - for(grep { index($_->orig(),"\0") > 0 } @$ret) { - # Replace \0 with \\0 - my $a = $_->orig(); - $a =~ s/\0/\\0/g; - $_->set_orig($a); - } - } - if(defined $Global::max_number_of_args - and $Global::max_number_of_args == 0) { - ::debug("run", "Read 1 but return 0 args\n"); - # \0noarg => nothing (not the empty string) - map { $_->set_orig("\0noarg"); } @$ret; - } - # Flush cached computed replacements in Arg-objects - # To fix: parallel --bar echo {%} ::: a b c ::: d e f - map { $_->flush_cache() } @$ret; - } - return $ret; -} - -sub unget($) { - my $self = shift; - ::debug("run", "RecordQueue-unget\n"); - $self->{'arg_number'} -= @_; - unshift @{$self->{'unget'}}, @_; -} - -sub empty($) { - my $self = shift; - my $empty = (not @{$self->{'unget'}}) && - $self->{'arg_sub_queue'}->empty(); - ::debug("run", "RecordQueue->empty $empty"); - return $empty; -} - -sub flush_cache($) { - my $self = shift; - for my $record (@{$self->{'unget'}}) { - for my $arg (@$record) { - $arg->flush_cache(); - } - } - $self->{'arg_sub_queue'}->flush_cache(); -} - -sub arg_number($) { - my $self = shift; - return $self->{'arg_number'}; -} - - -package RecordColQueue; - -sub new($) { - my $class = shift; - my $fhs = shift; - my @unget = (); - my $arg_sub_queue = MultifileQueue->new($fhs); - return bless { - 'unget' => \@unget, - 'arg_sub_queue' => $arg_sub_queue, - }, ref($class) || $class; -} - -sub get($) { - # Returns: - # reference to array of Arg-objects - my $self = shift; - if(@{$self->{'unget'}}) { - return shift @{$self->{'unget'}}; - } - if($self->{'arg_sub_queue'}->empty()) { - return undef; - } - my $in_record = $self->{'arg_sub_queue'}->get(); - if(defined $in_record) { - my @out_record = (); - for my $arg (@$in_record) { - ::debug("run", "RecordColQueue::arg $arg\n"); - my $line = $arg->orig(); - ::debug("run", "line='$line'\n"); - if($line ne "") { - if($opt::csv) { - # Parse CSV and put it into a record - chomp $line; - if(not $Global::csv->parse($line)) { - die "CSV has unexpected format: ^$line^"; - } - for($Global::csv->fields()) { - push @out_record, Arg->new($_); - } - } else { - # Split --colsep into record - for my $s (split /$opt::colsep/o, $line, -1) { - push @out_record, Arg->new($s); - } - } - } else { - push @out_record, Arg->new(""); - } - } - return \@out_record; - } else { - return undef; - } -} - -sub unget($) { - my $self = shift; - ::debug("run", "RecordColQueue-unget '@_'\n"); - unshift @{$self->{'unget'}}, @_; -} - -sub empty($) { - my $self = shift; - my $empty = (not @{$self->{'unget'}}) && - $self->{'arg_sub_queue'}->empty(); - ::debug("run", "RecordColQueue->empty $empty"); - return $empty; -} - -sub flush_cache($) { - my $self = shift; - for my $arg (@{$self->{'unget'}}) { - $arg->flush_cache(); - } - $self->{'arg_sub_queue'}->flush_cache(); -} - - -package SQLRecordQueue; - -sub new($) { - my $class = shift; - my @unget = (); - return bless { - 'unget' => \@unget, - }, ref($class) || $class; -} - -sub get($) { - # Returns: - # reference to array of Arg-objects - my $self = shift; - if(@{$self->{'unget'}}) { - return shift @{$self->{'unget'}}; - } - return $Global::sql->get_record(); -} - -sub unget($) { - my $self = shift; - ::debug("run", "SQLRecordQueue-unget '@_'\n"); - unshift @{$self->{'unget'}}, @_; -} - -sub empty($) { - my $self = shift; - if(@{$self->{'unget'}}) { return 0; } - my $get = $self->get(); - if(defined $get) { - $self->unget($get); - } - my $empty = not $get; - ::debug("run", "SQLRecordQueue->empty $empty"); - return $empty; -} - -sub flush_cache($) { - my $self = shift; - for my $record (@{$self->{'unget'}}) { - for my $arg (@$record) { - $arg->flush_cache(); - } - } -} - - -package MultifileQueue; - -@Global::unget_argv=(); - -sub new($$) { - my $class = shift; - my $fhs = shift; - for my $fh (@$fhs) { - if(-t $fh and -t ($Global::status_fd || *STDERR)) { - ::warning( - "Input is read from the terminal. You are either an expert", - "(in which case: YOU ARE AWESOME!) or maybe you forgot", - "::: or :::: or -a or to pipe data into parallel. If so", - "consider going through the tutorial: man parallel_tutorial", - "Press CTRL-D to exit."); - } - } - return bless { - 'unget' => \@Global::unget_argv, - 'fhs' => $fhs, - 'arg_matrix' => undef, - }, ref($class) || $class; -} - -sub get($) { - my $self = shift; - if($opt::link) { - return $self->link_get(); - } else { - return $self->nest_get(); - } -} - -sub unget($) { - my $self = shift; - ::debug("run", "MultifileQueue-unget '@_'\n"); - unshift @{$self->{'unget'}}, @_; -} - -sub empty($) { - my $self = shift; - my $empty = (not @Global::unget_argv) && - not @{$self->{'unget'}}; - for my $fh (@{$self->{'fhs'}}) { - $empty &&= eof($fh); - } - ::debug("run", "MultifileQueue->empty $empty "); - return $empty; -} - -sub flush_cache($) { - my $self = shift; - for my $record (@{$self->{'unget'}}, @{$self->{'arg_matrix'}}) { - for my $arg (@$record) { - $arg->flush_cache(); - } - } -} - -sub link_get($) { - my $self = shift; - if(@{$self->{'unget'}}) { - return shift @{$self->{'unget'}}; - } - my @record = (); - my $prepend; - my $empty = 1; - for my $i (0..$#{$self->{'fhs'}}) { - my $fh = $self->{'fhs'}[$i]; - my $arg = read_arg_from_fh($fh); - if(defined $arg) { - # Record $arg for recycling at end of file - push @{$self->{'arg_matrix'}[$i]}, $arg; - push @record, $arg; - $empty = 0; - } else { - ::debug("run", "EOA "); - # End of file: Recycle arguments - push @{$self->{'arg_matrix'}[$i]}, shift @{$self->{'arg_matrix'}[$i]}; - # return last @{$args->{'args'}{$fh}}; - push @record, @{$self->{'arg_matrix'}[$i]}[-1]; - } - } - if($empty) { - return undef; - } else { - return \@record; - } -} - -sub nest_get($) { - my $self = shift; - if(@{$self->{'unget'}}) { - return shift @{$self->{'unget'}}; - } - my @record = (); - my $prepend; - my $empty = 1; - my $no_of_inputsources = $#{$self->{'fhs'}} + 1; - if(not $self->{'arg_matrix'}) { - # Initialize @arg_matrix with one arg from each file - # read one line from each file - my @first_arg_set; - my $all_empty = 1; - for (my $fhno = 0; $fhno < $no_of_inputsources ; $fhno++) { - my $arg = read_arg_from_fh($self->{'fhs'}[$fhno]); - if(defined $arg) { - $all_empty = 0; - } - $self->{'arg_matrix'}[$fhno][0] = $arg || Arg->new(""); - push @first_arg_set, $self->{'arg_matrix'}[$fhno][0]; - } - if($all_empty) { - # All filehandles were at eof or eof-string - return undef; - } - return [@first_arg_set]; - } - - # Treat the case with one input source special. For multiple - # input sources we need to remember all previously read values to - # generate all combinations. But for one input source we can - # forget the value after first use. - if($no_of_inputsources == 1) { - my $arg = read_arg_from_fh($self->{'fhs'}[0]); - if(defined($arg)) { - return [$arg]; - } - return undef; - } - for (my $fhno = $no_of_inputsources - 1; $fhno >= 0; $fhno--) { - if(eof($self->{'fhs'}[$fhno])) { - next; - } else { - # read one - my $arg = read_arg_from_fh($self->{'fhs'}[$fhno]); - defined($arg) || next; # If we just read an EOF string: Treat this as EOF - my $len = $#{$self->{'arg_matrix'}[$fhno]} + 1; - $self->{'arg_matrix'}[$fhno][$len] = $arg; - # make all new combinations - my @combarg = (); - for (my $fhn = 0; $fhn < $no_of_inputsources; $fhn++) { - push(@combarg, [0, $#{$self->{'arg_matrix'}[$fhn]}], - # Is input source --link'ed to the next? - $opt::linkinputsource[$fhn+1]); - } - # Find only combinations with this new entry - $combarg[2*$fhno] = [$len,$len]; - # map combinations - # [ 1, 3, 7 ], [ 2, 4, 1 ] - # => - # [ m[0][1], m[1][3], m[2][7] ], [ m[0][2], m[1][4], m[2][1] ] - my @mapped; - for my $c (expand_combinations(@combarg)) { - my @a; - for my $n (0 .. $no_of_inputsources - 1 ) { - push @a, $self->{'arg_matrix'}[$n][$$c[$n]]; - } - push @mapped, \@a; - } - # append the mapped to the ungotten arguments - push @{$self->{'unget'}}, @mapped; - # get the first - if(@mapped) { - return shift @{$self->{'unget'}}; - } - } - } - # all are eof or at EOF string; return from the unget queue - return shift @{$self->{'unget'}}; -} - -{ - my $cr_count = 0; - my $nl_count = 0; - my $dos_crnl_determined; - sub read_arg_from_fh($) { - # Read one Arg from filehandle - # Returns: - # Arg-object with one read line - # undef if end of file - my $fh = shift; - my $prepend; - my $arg; - my $half_record = 0; - do {{ - # This makes 10% faster - if(not defined ($arg = <$fh>)) { - if(defined $prepend) { - return Arg->new($prepend); - } else { - return undef; - } - } - if(not $dos_crnl_determined and not defined $opt::d) { - # Warn if input has CR-NL and -d is not set - if($arg =~ /\r$/) { - $cr_count++; - } else { - $nl_count++; - } - if($cr_count == 3 or $nl_count == 3) { - $dos_crnl_determined = 1; - if($nl_count == 0 and $cr_count == 3) { - ::warning('The first three values end in CR-NL. '. - 'Consider using -d "\r\n"'); - } - } - } - if($opt::csv) { - # We need to read a full CSV line. - if(($arg =~ y/"/"/) % 2 ) { - # The number of " on the line is uneven: - # If we were in a half_record => we have a full record now - # If we were outside a half_record => - # we are in a half record now - $half_record = not $half_record; - } - if($half_record) { - # CSV half-record with quoting: - # col1,"col2 2""x3"" board newline <-this one - # cont",col3 - $prepend .= $arg; - redo; - } else { - # Now we have a full CSV record - } - } - # Remove delimiter - chomp $arg; - if($Global::end_of_file_string and - $arg eq $Global::end_of_file_string) { - # Ignore the rest of input file - close $fh; - ::debug("run", "EOF-string ($arg) met\n"); - if(defined $prepend) { - return Arg->new($prepend); - } else { - return undef; - } - } - if(defined $prepend) { - $arg = $prepend.$arg; # For line continuation - undef $prepend; - } - if($Global::ignore_empty) { - if($arg =~ /^\s*$/) { - redo; # Try the next line - } - } - if($Global::max_lines) { - if($arg =~ /\s$/) { - # Trailing space => continued on next line - $prepend = $arg; - redo; - } - } - }} while (1 == 0); # Dummy loop {{}} for redo - if(defined $arg) { - return Arg->new($arg); - } else { - ::die_bug("multiread arg undefined"); - } - } -} - -# Prototype forwarding -sub expand_combinations(@); -sub expand_combinations(@) { - # Input: - # ([xmin,xmax], [ymin,ymax], ...) - # Returns: ([x,y,...],[x,y,...]) - # where xmin <= x <= xmax and ymin <= y <= ymax - my $minmax_ref = shift; - my $link = shift; # This is linked to the next input source - my $xmin = $$minmax_ref[0]; - my $xmax = $$minmax_ref[1]; - my @p; - if(@_) { - my @rest = expand_combinations(@_); - if($link) { - # Linked to next col with --link/:::+/::::+ - # TODO BUG does not wrap values if not same number of vals - push(@p, map { [$$_[0], @$_] } - grep { $xmin <= $$_[0] and $$_[0] <= $xmax } @rest); - } else { - # If there are more columns: Compute those recursively - for(my $x = $xmin; $x <= $xmax; $x++) { - push @p, map { [$x, @$_] } @rest; - } - } - } else { - for(my $x = $xmin; $x <= $xmax; $x++) { - push @p, [$x]; - } - } - return @p; -} - - -package Arg; - -sub new($) { - my $class = shift; - my $orig = shift; - my @hostgroups; - if($opt::hostgroups) { - if($orig =~ s:@(.+)::) { - # We found hostgroups on the arg - @hostgroups = split(/\+|,/, $1); - if(not grep { defined $Global::hostgroups{$_} } @hostgroups) { - # This hostgroup is not defined using -S - # Add it - ::warning("Adding hostgroups: @hostgroups"); - # Add sshlogin - for(grep { not defined $Global::hostgroups{$_} } @hostgroups) { - my $sshlogin = SSHLogin->new($_); - my $sshlogin_string = $sshlogin->string(); - $Global::host{$sshlogin_string} = $sshlogin; - $Global::hostgroups{$sshlogin_string} = 1; - } - } - } else { - # No hostgroup on the arg => any hostgroup - @hostgroups = (keys %Global::hostgroups); - } - } - return bless { - 'orig' => $orig, - 'hostgroups' => \@hostgroups, - }, ref($class) || $class; -} - -sub Q($) { - # Q alias for ::shell_quote_scalar - my $ret = ::Q($_[0]); - no warnings 'redefine'; - *Q = \&::Q; - return $ret; -} - -sub pQ($) { - # pQ alias for ::perl_quote_scalar - my $ret = ::pQ($_[0]); - no warnings 'redefine'; - *pQ = \&::pQ; - return $ret; -} - -sub hash($) { - $Global::use{"DBI"} ||= eval "use B; 1;"; - B::hash(@_); -} - -sub total_jobs() { - return $Global::JobQueue->total_jobs(); -} - -{ - my %perleval; - my $job; - sub skip() { - # shorthand for $job->skip(); - $job->skip(); - } - sub slot() { - # shorthand for $job->slot(); - $job->slot(); - } - sub seq() { - # shorthand for $job->seq(); - $job->seq(); - } - sub uq() { - # Do not quote this arg - $Global::unquote_arg = 1; - } - sub yyyy_mm_dd_hh_mm_ss(@) { - # ISO8601 2038-01-19T03:14:08 - ::strftime("%Y-%m-%dT%H:%M:%S", localtime(shift || time())); - } - sub yyyy_mm_dd_hh_mm(@) { - # ISO8601 2038-01-19T03:14 - ::strftime("%Y-%m-%dT%H:%M", localtime(shift || time())); - } - sub yyyy_mm_dd(@) { - # ISO8601 2038-01-19 - ::strftime("%Y-%m-%d", localtime(shift || time())); - } - sub hh_mm_ss(@) { - # ISO8601 03:14:08 - ::strftime("%H:%M:%S", localtime(shift || time())); - } - sub hh_mm(@) { - # ISO8601 03:14 - ::strftime("%H:%M", localtime(shift || time())); - } - sub yyyymmddhhmmss(@) { - # ISO8601 20380119 + ISO8601 031408 - ::strftime("%Y%m%d%H%M%S", localtime(shift || time())); - } - sub yyyymmddhhmm(@) { - # ISO8601 20380119 + ISO8601 0314 - ::strftime("%Y%m%d%H%M", localtime(shift || time())); - } - sub yyyymmdd(@) { - # ISO8601 20380119 - ::strftime("%Y%m%d", localtime(shift || time())); - } - sub hhmmss(@) { - # ISO8601 031408 - ::strftime("%H%M%S", localtime(shift || time())); - } - sub hhmm(@) { - # ISO8601 0314 - ::strftime("%H%M", localtime(shift || time())); - } - - sub replace($$$$) { - # Calculates the corresponding value for a given perl expression - # Returns: - # The calculated string (quoted if asked for) - my $self = shift; - my $perlexpr = shift; # E.g. $_=$_ or s/.gz// - my $quote = shift; # should the string be quoted? - # This is actually a CommandLine-object, - # but it looks nice to be able to say {= $job->slot() =} - $job = shift; - # Positional replace treated as normal replace - $perlexpr =~ s/^(-?\d+)? *//; - if(not $Global::cache_replacement_eval - or - not $self->{'cache'}{$perlexpr}) { - # Only compute the value once - # Use $_ as the variable to change - local $_; - if($Global::trim eq "n") { - $_ = $self->{'orig'}; - } else { - # Trim the input - $_ = trim_of($self->{'orig'}); - } - ::debug("replace", "eval ", $perlexpr, " ", $_, "\n"); - if(not $perleval{$perlexpr}) { - # Make an anonymous function of the $perlexpr - # And more importantly: Compile it only once - if($perleval{$perlexpr} = - eval('sub { no strict; no warnings; my $job = shift; '. - $perlexpr.' }')) { - # All is good - } else { - # The eval failed. Maybe $perlexpr is invalid perl? - ::error("Cannot use $perlexpr: $@"); - ::wait_and_exit(255); - } - } - # Execute the function - $perleval{$perlexpr}->($job); - $self->{'cache'}{$perlexpr} = $_; - if($Global::unquote_arg) { - # uq() was called in perlexpr - $self->{'cache'}{'unquote'}{$perlexpr} = 1; - # Reset for next perlexpr - $Global::unquote_arg = 0; - } - } - # Return the value quoted if needed - if($self->{'cache'}{'unquote'}{$perlexpr}) { - return($self->{'cache'}{$perlexpr}); - } else { - return($quote ? Q($self->{'cache'}{$perlexpr}) - : $self->{'cache'}{$perlexpr}); - } - } -} - -sub flush_cache($) { - # Flush cache of computed values - my $self = shift; - $self->{'cache'} = undef; -} - -sub orig($) { - my $self = shift; - return $self->{'orig'}; -} - -sub set_orig($$) { - my $self = shift; - $self->{'orig'} = shift; -} - -sub trim_of($) { - # Removes white space as specifed by --trim: - # n = nothing - # l = start - # r = end - # lr|rl = both - # Returns: - # string with white space removed as needed - my @strings = map { defined $_ ? $_ : "" } (@_); - my $arg; - if($Global::trim eq "n") { - # skip - } elsif($Global::trim eq "l") { - for my $arg (@strings) { $arg =~ s/^\s+//; } - } elsif($Global::trim eq "r") { - for my $arg (@strings) { $arg =~ s/\s+$//; } - } elsif($Global::trim eq "rl" or $Global::trim eq "lr") { - for my $arg (@strings) { $arg =~ s/^\s+//; $arg =~ s/\s+$//; } - } else { - ::error("--trim must be one of: r l rl lr."); - ::wait_and_exit(255); - } - return wantarray ? @strings : "@strings"; -} - - -package TimeoutQueue; - -sub new($) { - my $class = shift; - my $delta_time = shift; - my ($pct); - if($delta_time =~ /(\d+(\.\d+)?)%/) { - # Timeout in percent - $pct = $1/100; - $delta_time = 1_000_000; - } - $delta_time = ::multiply_time_units($delta_time); - - return bless { - 'queue' => [], - 'delta_time' => $delta_time, - 'pct' => $pct, - 'remedian_idx' => 0, - 'remedian_arr' => [], - 'remedian' => undef, - }, ref($class) || $class; -} - -sub delta_time($) { - my $self = shift; - return $self->{'delta_time'}; -} - -sub set_delta_time($$) { - my $self = shift; - $self->{'delta_time'} = shift; -} - -sub remedian($) { - my $self = shift; - return $self->{'remedian'}; -} - -sub set_remedian($$) { - # Set median of the last 999^3 (=997002999) values using Remedian - # - # Rousseeuw, Peter J., and Gilbert W. Bassett Jr. "The remedian: A - # robust averaging method for large data sets." Journal of the - # American Statistical Association 85.409 (1990): 97-104. - my $self = shift; - my $val = shift; - my $i = $self->{'remedian_idx'}++; - my $rref = $self->{'remedian_arr'}; - $rref->[0][$i%999] = $val; - $rref->[1][$i/999%999] = (sort @{$rref->[0]})[$#{$rref->[0]}/2]; - $rref->[2][$i/999/999%999] = (sort @{$rref->[1]})[$#{$rref->[1]}/2]; - $self->{'remedian'} = (sort @{$rref->[2]})[$#{$rref->[2]}/2]; -} - -sub update_median_runtime($) { - # Update delta_time based on runtime of finished job if timeout is - # a percentage - my $self = shift; - my $runtime = shift; - if($self->{'pct'}) { - $self->set_remedian($runtime); - $self->{'delta_time'} = $self->{'pct'} * $self->remedian(); - ::debug("run", "Timeout: $self->{'delta_time'}s "); - } -} - -sub process_timeouts($) { - # Check if there was a timeout - my $self = shift; - # $self->{'queue'} is sorted by start time - while (@{$self->{'queue'}}) { - my $job = $self->{'queue'}[0]; - if($job->endtime()) { - # Job already finished. No need to timeout the job - # This could be because of --keep-order - shift @{$self->{'queue'}}; - } elsif($job->is_timedout($self->{'delta_time'})) { - # Need to shift off queue before kill - # because kill calls usleep that calls process_timeouts - shift @{$self->{'queue'}}; - ::warning("This job was killed because it timed out:", - $job->replaced()); - $job->kill(); - } else { - # Because they are sorted by start time the rest are later - last; - } - } -} - -sub insert($) { - my $self = shift; - my $in = shift; - push @{$self->{'queue'}}, $in; -} - - -package SQL; - -sub new($) { - my $class = shift; - my $dburl = shift; - $Global::use{"DBI"} ||= eval "use DBI; 1;"; - # +DBURL = append to this DBURL - my $append = $dburl=~s/^\+//; - my %options = parse_dburl(get_alias($dburl)); - my %driveralias = ("sqlite" => "SQLite", - "sqlite3" => "SQLite", - "pg" => "Pg", - "postgres" => "Pg", - "postgresql" => "Pg", - "csv" => "CSV", - "oracle" => "Oracle", - "ora" => "Oracle"); - my $driver = $driveralias{$options{'databasedriver'}} || - $options{'databasedriver'}; - my $database = $options{'database'}; - my $host = $options{'host'} ? ";host=".$options{'host'} : ""; - my $port = $options{'port'} ? ";port=".$options{'port'} : ""; - my $dsn = "DBI:$driver:dbname=$database$host$port"; - my $userid = $options{'user'}; - my $password = $options{'password'};; - if(not grep /$driver/, DBI->available_drivers) { - ::error("$driver not supported. Are you missing a perl DBD::$driver module?"); - ::wait_and_exit(255); - } - my $dbh; - if($driver eq "CSV") { - # CSV does not use normal dsn - if(-d $database) { - $dbh = DBI->connect("dbi:CSV:", "", "", { f_dir => "$database", }) - or die $DBI::errstr; - } else { - ::error("$database is not a directory."); - ::wait_and_exit(255); - } - } else { - $dbh = DBI->connect($dsn, $userid, $password, - { RaiseError => 1, AutoInactiveDestroy => 1 }) - or die $DBI::errstr; - } - $dbh->{'PrintWarn'} = $Global::debug || 0; - $dbh->{'PrintError'} = $Global::debug || 0; - $dbh->{'RaiseError'} = 1; - $dbh->{'ShowErrorStatement'} = 1; - $dbh->{'HandleError'} = sub {}; - if(not defined $options{'table'}) { - ::error("The DBURL ($dburl) must contain a table."); - ::wait_and_exit(255); - } - - return bless { - 'dbh' => $dbh, - 'driver' => $driver, - 'max_number_of_args' => undef, - 'table' => $options{'table'}, - 'append' => $append, - }, ref($class) || $class; -} - -# Prototype forwarding -sub get_alias($); -sub get_alias($) { - my $alias = shift; - $alias =~ s/^(sql:)*//; # Accept aliases prepended with sql: - if ($alias !~ /^:/) { - return $alias; - } - - # Find the alias - my $path; - if (-l $0) { - ($path) = readlink($0) =~ m|^(.*)/|; - } else { - ($path) = $0 =~ m|^(.*)/|; - } - - my @deprecated = ("$ENV{HOME}/.dburl.aliases", - "$path/dburl.aliases", "$path/dburl.aliases.dist"); - for (@deprecated) { - if(-r $_) { - ::warning("$_ is deprecated. ". - "Use .sql/aliases instead (read man sql)."); - } - } - my @urlalias=(); - check_permissions("$ENV{HOME}/.sql/aliases"); - check_permissions("$ENV{HOME}/.dburl.aliases"); - my @search = ("$ENV{HOME}/.sql/aliases", - "$ENV{HOME}/.dburl.aliases", "/etc/sql/aliases", - "$path/dburl.aliases", "$path/dburl.aliases.dist"); - for my $alias_file (@search) { - # local $/ needed if -0 set - local $/ = "\n"; - if(-r $alias_file) { - open(my $in, "<", $alias_file) || die; - push @urlalias, <$in>; - close $in; - } - } - my ($alias_part,$rest) = $alias=~/(:\w*)(.*)/; - # If we saw this before: we have an alias loop - if(grep {$_ eq $alias_part } @Private::seen_aliases) { - ::error("$alias_part is a cyclic alias."); - exit -1; - } else { - push @Private::seen_aliases, $alias_part; - } - - my $dburl; - for (@urlalias) { - /^$alias_part\s+(\S+.*)/ and do { $dburl = $1; last; } - } - - if($dburl) { - return get_alias($dburl.$rest); - } else { - ::error("$alias is not defined in @search"); - exit(-1); - } -} - -sub check_permissions($) { - my $file = shift; - - if(-e $file) { - if(not -o $file) { - my $username = (getpwuid($<))[0]; - ::warning("$file should be owned by $username: ". - "chown $username $file"); - } - my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, - $atime,$mtime,$ctime,$blksize,$blocks) = stat($file); - if($mode & 077) { - my $username = (getpwuid($<))[0]; - ::warning("$file should be only be readable by $username: ". - "chmod 600 $file"); - } - } -} - -sub parse_dburl($) { - my $url = shift; - my %options = (); - # sql:mysql://[[user][:password]@][host][:port]/[database[/table][?query]] - - if($url=~m!^(?:sql:)? # You can prefix with 'sql:' - ((?:oracle|ora|mysql|pg|postgres|postgresql)(?:s|ssl|)| - (?:sqlite|sqlite2|sqlite3|csv)):// # Databasedriver ($1) - (?: - ([^:@/][^:@]*|) # Username ($2) - (?: - :([^@]*) # Password ($3) - )? - @)? - ([^:/]*)? # Hostname ($4) - (?: - : - ([^/]*)? # Port ($5) - )? - (?: - / - ([^/?]*)? # Database ($6) - )? - (?: - / - ([^?]*)? # Table ($7) - )? - (?: - \? - (.*)? # Query ($8) - )? - $!ix) { - $options{databasedriver} = ::undef_if_empty(lc(uri_unescape($1))); - $options{user} = ::undef_if_empty(uri_unescape($2)); - $options{password} = ::undef_if_empty(uri_unescape($3)); - $options{host} = ::undef_if_empty(uri_unescape($4)); - $options{port} = ::undef_if_empty(uri_unescape($5)); - $options{database} = ::undef_if_empty(uri_unescape($6)); - $options{table} = ::undef_if_empty(uri_unescape($7)); - $options{query} = ::undef_if_empty(uri_unescape($8)); - ::debug("sql", "dburl $url\n"); - ::debug("sql", "databasedriver ", $options{databasedriver}, - " user ", $options{user}, - " password ", $options{password}, " host ", $options{host}, - " port ", $options{port}, " database ", $options{database}, - " table ", $options{table}, " query ", $options{query}, "\n"); - } else { - ::error("$url is not a valid DBURL"); - exit 255; - } - return %options; -} - -sub uri_unescape($) { - # Copied from http://cpansearch.perl.org/src/GAAS/URI-1.55/URI/Escape.pm - # to avoid depending on URI::Escape - # This section is (C) Gisle Aas. - # Note from RFC1630: "Sequences which start with a percent sign - # but are not followed by two hexadecimal characters are reserved - # for future extension" - my $str = shift; - if (@_ && wantarray) { - # not executed for the common case of a single argument - my @str = ($str, @_); # need to copy - foreach (@str) { - s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg; - } - return @str; - } - $str =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg if defined $str; - $str; -} - -sub run($) { - my $self = shift; - my $stmt = shift; - if($self->{'driver'} eq "CSV") { - $stmt=~ s/;$//; - if($stmt eq "BEGIN" or - $stmt eq "COMMIT") { - return undef; - } - } - my @retval; - my $dbh = $self->{'dbh'}; - ::debug("sql","$opt::sqlmaster$opt::sqlworker run $stmt\n"); - # Execute with the rest of the args - if any - my $rv; - my $sth; - my $lockretry = 0; - while($lockretry < 10) { - $sth = $dbh->prepare($stmt); - if($sth - and - eval { $rv = $sth->execute(@_) }) { - last; - } else { - if($@ =~ /no such table|Table .* doesn.t exist|relation ".*" does not exist/ - or - $DBI::errstr =~ /no such table|Table .* doesn.t exist|relation ".*" does not exist/) { - # This is fine: - # It is just a worker that reported back too late - - # another worker had finished the job first - # and the table was then dropped - $rv = $sth = 0; - last; - } - if($DBI::errstr =~ /locked/) { - ::debug("sql", "Lock retry: $lockretry"); - $lockretry++; - ::usleep(rand()*300); - } elsif(not $sth) { - # Try again - $lockretry++; - } else { - ::error($DBI::errstr); - ::wait_and_exit(255); - } - } - } - if($lockretry >= 10) { - ::die_bug("retry > 10: $DBI::errstr"); - } - if($rv < 0 and $DBI::errstr){ - ::error($DBI::errstr); - ::wait_and_exit(255); - } - return $sth; -} - -sub get($) { - my $self = shift; - my $sth = $self->run(@_); - my @retval; - # If $sth = 0 it means the table was dropped by another process - while($sth) { - my @row = $sth->fetchrow_array(); - @row or last; - push @retval, \@row; - } - return \@retval; -} - -sub table($) { - my $self = shift; - return $self->{'table'}; -} - -sub append($) { - my $self = shift; - return $self->{'append'}; -} - -sub update($) { - my $self = shift; - my $stmt = shift; - my $table = $self->table(); - $self->run("UPDATE $table $stmt",@_); -} - -sub output($) { - my $self = shift; - my $commandline = shift; - - $self->update("SET Stdout = ?, Stderr = ? WHERE Seq = ". - $commandline->seq(), - join("",@{$commandline->{'output'}{1}}), - join("",@{$commandline->{'output'}{2}})); -} - -sub max_number_of_args($) { - # Maximal number of args for this table - my $self = shift; - if(not $self->{'max_number_of_args'}) { - # Read the number of args from the SQL table - my $table = $self->table(); - my $v = $self->get("SELECT * FROM $table LIMIT 1;"); - my @reserved_columns = qw(Seq Host Starttime JobRuntime Send - Receive Exitval _Signal Command Stdout Stderr); - if(not $v) { - ::error("$table contains no records"); - } - # Count the number of Vx columns - $self->{'max_number_of_args'} = $#{$v->[0]} - $#reserved_columns; - } - return $self->{'max_number_of_args'}; -} - -sub set_max_number_of_args($$) { - my $self = shift; - $self->{'max_number_of_args'} = shift; -} - -sub create_table($) { - my $self = shift; - if($self->append()) { return; } - my $max_number_of_args = shift; - $self->set_max_number_of_args($max_number_of_args); - my $table = $self->table(); - $self->run(qq(DROP TABLE IF EXISTS $table;)); - # BIGINT and TEXT are not supported in these databases or are too small - my %vartype = ( - "Oracle" => { "BIGINT" => "NUMBER(19,0)", - "TEXT" => "CLOB", }, - "mysql" => { "TEXT" => "BLOB", }, - "CSV" => { "BIGINT" => "INT", - "FLOAT" => "REAL", }, - ); - my $BIGINT = $vartype{$self->{'driver'}}{"BIGINT"} || "BIGINT"; - my $TEXT = $vartype{$self->{'driver'}}{"TEXT"} || "TEXT"; - my $FLOAT = $vartype{$self->{'driver'}}{"FLOAT"} || "FLOAT(44)"; - my $v_def = join "", map { "V$_ $TEXT," } (1..$self->max_number_of_args()); - $self->run(qq{CREATE TABLE $table - (Seq $BIGINT, - Host $TEXT, - Starttime $FLOAT, - JobRuntime $FLOAT, - Send $BIGINT, - Receive $BIGINT, - Exitval $BIGINT, - _Signal $BIGINT, - Command $TEXT,}. - $v_def. - qq{Stdout $TEXT, - Stderr $TEXT);}); -} - -sub insert_records($) { - my $self = shift; - my $seq = shift; - my $command_ref = shift; - my $record_ref = shift; - my $table = $self->table(); - # For SQL encode the command with \257 space as split points - my $command = join("\257 ",@$command_ref); - my @v_cols = map { ", V$_" } (1..$self->max_number_of_args()); - # Two extra value due to $seq, Exitval, Send - my $v_vals = join ",", map { "?" } (1..$self->max_number_of_args()+4); - $self->run("INSERT INTO $table (Seq,Command,Exitval,Send @v_cols) ". - "VALUES ($v_vals);", $seq, $command, -1000, - 0, @$record_ref[1..$#$record_ref]); -} - - -sub get_record($) { - my $self = shift; - my @retval; - my $table = $self->table(); - my @v_cols = map { ", V$_" } (1..$self->max_number_of_args()); - my $rand = "Reserved-".$$.rand(); - my $v; - my $more_pending; - - do { - if($self->{'driver'} eq "CSV") { - # Sub SELECT is not supported in CSV - # So to minimize the race condition below select a job at random - my $r = $self->get("SELECT Seq, Command @v_cols FROM $table ". - "WHERE Exitval = -1000 LIMIT 100;"); - $v = [ sort { rand() > 0.5 } @$r ]; - } else { - # Avoid race condition where multiple workers get the same job - # by setting Stdout to a unique string - # (SELECT * FROM (...) AS dummy) is needed due to sillyness in MySQL - $self->update("SET Stdout = ?,Exitval = ? ". - "WHERE Seq = (". - " SELECT * FROM (". - " SELECT min(Seq) FROM $table WHERE Exitval = -1000". - " ) AS dummy". - ") AND Exitval = -1000;", $rand, -1210); - # If a parallel worker overwrote the unique string this will get nothing - $v = $self->get("SELECT Seq, Command @v_cols FROM $table ". - "WHERE Stdout = ?;", $rand); - } - if($v->[0]) { - my $val_ref = $v->[0]; - # Mark record as taken - my $seq = shift @$val_ref; - # Save the sequence number to use when running the job - $SQL::next_seq = $seq; - $self->update("SET Exitval = ? WHERE Seq = ".$seq, -1220); - # Command is encoded with '\257 space' as splitting char - my @command = split /\257 /, shift @$val_ref; - $SQL::command_ref = \@command; - for (@$val_ref) { - push @retval, Arg->new($_); - } - } else { - # If the record was updated by another job in parallel, - # then we may not be done, so see if there are more jobs pending - $more_pending = - $self->get("SELECT Seq FROM $table WHERE Exitval = ?;", -1210); - } - } while (not $v->[0] and $more_pending->[0]); - - if(@retval) { - return \@retval; - } else { - return undef; - } -} - -sub total_jobs($) { - my $self = shift; - my $table = $self->table(); - my $v = $self->get("SELECT count(*) FROM $table;"); - if($v->[0]) { - return $v->[0]->[0]; - } else { - ::die_bug("SQL::total_jobs"); - } -} - -sub max_seq($) { - my $self = shift; - my $table = $self->table(); - my $v = $self->get("SELECT max(Seq) FROM $table;"); - if($v->[0]) { - return $v->[0]->[0]; - } else { - ::die_bug("SQL::max_seq"); - } -} - -sub finished($) { - # Check if there are any jobs left in the SQL table that do not - # have a "real" exitval - my $self = shift; - if($opt::wait or $Global::start_sqlworker) { - my $table = $self->table(); - my $rv = $self->get("select Seq,Exitval from $table ". - "where Exitval <= -1000 limit 1"); - return not $rv->[0]; - } else { - return 1; - } -} - -package Semaphore; - -# This package provides a counting semaphore -# -# If a process dies without releasing the semaphore the next process -# that needs that entry will clean up dead semaphores -# -# The semaphores are stored in $PARALLEL_HOME/semaphores/id- Each -# file in $PARALLEL_HOME/semaphores/id-/ is the process ID of the -# process holding the entry. If the process dies, the entry can be -# taken by another process. - -sub new($) { - my $class = shift; - my $id = shift; - my $count = shift; - $id =~ s/([^-_a-z0-9])/unpack("H*",$1)/ige; # Convert non-word chars to hex - $id = "id-".$id; # To distinguish it from a process id - my $parallel_locks = $Global::cache_dir . "/semaphores"; - -d $parallel_locks or ::mkdir_or_die($parallel_locks); - my $lockdir = "$parallel_locks/$id"; - my $lockfile = $lockdir.".lock"; - if(-d $parallel_locks and -w $parallel_locks - and -r $parallel_locks and -x $parallel_locks) { - # skip - } else { - ::error("Semaphoredir must be writable: '$parallel_locks'"); - ::wait_and_exit(255); - } - - if($count < 1) { ::die_bug("semaphore-count: $count"); } - return bless { - 'lockfile' => $lockfile, - 'lockfh' => Symbol::gensym(), - 'lockdir' => $lockdir, - 'id' => $id, - 'idfile' => $lockdir."/".$id, - 'pid' => $$, - 'pidfile' => $lockdir."/".$$.'@'.::hostname(), - 'count' => $count + 1 # nlinks returns a link for the 'id-' as well - }, ref($class) || $class; -} - -sub remove_dead_locks($) { - my $self = shift; - my $lockdir = $self->{'lockdir'}; - - for my $d (glob "$lockdir/*") { - $d =~ m:$lockdir/([0-9]+)\@([-\._a-z0-9]+)$:o or next; - my ($pid, $host) = ($1, $2); - if($host eq ::hostname()) { - if(kill 0, $pid) { - ::debug("sem", "Alive: $pid $d\n"); - } else { - ::debug("sem", "Dead: $d\n"); - ::rm($d); - } - } - } -} - -sub acquire($) { - my $self = shift; - my $sleep = 1; # 1 ms - my $start_time = time; - while(1) { - # Can we get a lock? - $self->atomic_link_if_count_less_than() and last; - $self->remove_dead_locks(); - # Retry slower and slower up to 1 second - $sleep = ($sleep < 1000) ? ($sleep * 1.1) : ($sleep); - # Random to avoid every sleeping job waking up at the same time - ::usleep(rand()*$sleep); - if($opt::semaphoretimeout) { - if($opt::semaphoretimeout > 0 - and - time - $start_time > $opt::semaphoretimeout) { - # Timeout: Take the semaphore anyway - ::warning("Semaphore timed out. Stealing the semaphore."); - if(not -e $self->{'idfile'}) { - open (my $fh, ">", $self->{'idfile'}) or - ::die_bug("timeout_write_idfile: $self->{'idfile'}"); - close $fh; - } - link $self->{'idfile'}, $self->{'pidfile'}; - last; - } - if($opt::semaphoretimeout < 0 - and - time - $start_time > -$opt::semaphoretimeout) { - # Timeout: Exit - ::warning("Semaphore timed out. Exiting."); - exit(1); - last; - } - } - } - ::debug("sem", "acquired $self->{'pid'}\n"); -} - -sub release($) { - my $self = shift; - ::rm($self->{'pidfile'}); - if($self->nlinks() == 1) { - # This is the last link, so atomic cleanup - $self->lock(); - if($self->nlinks() == 1) { - ::rm($self->{'idfile'}); - rmdir $self->{'lockdir'}; - } - $self->unlock(); - } - ::debug("run", "released $self->{'pid'}\n"); -} - -sub pid_change($) { - # This should do what release()+acquire() would do without having - # to re-acquire the semaphore - my $self = shift; - - my $old_pidfile = $self->{'pidfile'}; - $self->{'pid'} = $$; - $self->{'pidfile'} = $self->{'lockdir'}."/".$$.'@'.::hostname(); - my $retval = link $self->{'idfile'}, $self->{'pidfile'}; - ::debug("sem","link($self->{'idfile'},$self->{'pidfile'})=$retval\n"); - ::rm($old_pidfile); -} - -sub atomic_link_if_count_less_than($) { - # Link $file1 to $file2 if nlinks to $file1 < $count - my $self = shift; - my $retval = 0; - $self->lock(); - my $nlinks = $self->nlinks(); - ::debug("sem","$nlinks<$self->{'count'} "); - if($nlinks < $self->{'count'}) { - -d $self->{'lockdir'} or ::mkdir_or_die($self->{'lockdir'}); - if(not -e $self->{'idfile'}) { - open (my $fh, ">", $self->{'idfile'}) or - ::die_bug("write_idfile: $self->{'idfile'}"); - close $fh; - } - $retval = link $self->{'idfile'}, $self->{'pidfile'}; - ::debug("sem","link($self->{'idfile'},$self->{'pidfile'})=$retval\n"); - } - $self->unlock(); - ::debug("sem", "atomic $retval"); - return $retval; -} - -sub nlinks($) { - my $self = shift; - if(-e $self->{'idfile'}) { - return (stat(_))[3]; - } else { - return 0; - } -} - -sub lock($) { - my $self = shift; - my $sleep = 100; # 100 ms - my $total_sleep = 0; - $Global::use{"Fcntl"} ||= eval "use Fcntl qw(:DEFAULT :flock); 1;"; - my $locked = 0; - while(not $locked) { - if(tell($self->{'lockfh'}) == -1) { - # File not open - open($self->{'lockfh'}, ">", $self->{'lockfile'}) - or ::debug("run", "Cannot open $self->{'lockfile'}"); - } - if($self->{'lockfh'}) { - # File is open - chmod 0666, $self->{'lockfile'}; # assuming you want it a+rw - if(flock($self->{'lockfh'}, LOCK_EX()|LOCK_NB())) { - # The file is locked: No need to retry - $locked = 1; - last; - } else { - if ($! =~ m/Function not implemented/) { - ::warning("flock: $!", - "Will wait for a random while."); - ::usleep(rand(5000)); - # File cannot be locked: No need to retry - $locked = 2; - last; - } - } - } - # Locking failed in first round - # Sleep and try again - $sleep = ($sleep < 1000) ? ($sleep * 1.1) : ($sleep); - # Random to avoid every sleeping job waking up at the same time - ::usleep(rand()*$sleep); - $total_sleep += $sleep; - if($opt::semaphoretimeout) { - if($opt::semaphoretimeout > 0 - and - $total_sleep/1000 > $opt::semaphoretimeout) { - # Timeout: Take the semaphore anyway - ::warning("Semaphore timed out. Taking the semaphore."); - $locked = 3; - last; - } - if($opt::semaphoretimeout < 0 - and - $total_sleep/1000 > -$opt::semaphoretimeout) { - # Timeout: Exit - ::warning("Semaphore timed out. Exiting."); - $locked = 4; - last; - } - } else { - if($total_sleep/1000 > 30) { - ::warning("Semaphore stuck for 30 seconds. ". - "Consider using --semaphoretimeout."); - } - } - } - ::debug("run", "locked $self->{'lockfile'}"); -} - -sub unlock($) { - my $self = shift; - ::rm($self->{'lockfile'}); - close $self->{'lockfh'}; - ::debug("run", "unlocked\n"); -} - -# Keep perl -w happy - -$opt::x = $Semaphore::timeout = $Semaphore::wait = -$Job::file_descriptor_warning_printed = $Global::envdef = @Arg::arg = -$Global::max_slot_number = $opt::session; - -package main; - -sub main() { - save_stdin_stdout_stderr(); - save_original_signal_handler(); - parse_options(); - ::debug("init", "Open file descriptors: ", join(" ",keys %Global::fh), "\n"); - my $number_of_args; - if($Global::max_number_of_args) { - $number_of_args = $Global::max_number_of_args; - } elsif ($opt::X or $opt::m or $opt::xargs) { - $number_of_args = undef; - } else { - $number_of_args = 1; - } - - my @command = @ARGV; - my @input_source_fh; - if($opt::pipepart) { - if($opt::tee) { - @input_source_fh = map { open_or_exit($_) } @opt::a; - # Remove the first: It will be the file piped. - shift @input_source_fh; - if(not @input_source_fh and not $opt::pipe) { - @input_source_fh = (*STDIN); - } - } else { - # -a is used for data - not for command line args - @input_source_fh = map { open_or_exit($_) } "/dev/null"; - } - } else { - @input_source_fh = map { open_or_exit($_) } @opt::a; - if(not @input_source_fh and not $opt::pipe) { - @input_source_fh = (*STDIN); - } - } - - if($opt::skip_first_line) { - # Skip the first line for the first file handle - my $fh = $input_source_fh[0]; - <$fh>; - } - - set_input_source_header(\@command,\@input_source_fh); - if($opt::filter_hosts and (@opt::sshlogin or @opt::sshloginfile)) { - # Parallel check all hosts are up. Remove hosts that are down - filter_hosts(); - } - if($opt::sqlmaster and $opt::sqlworker) { - # Start a real --sqlworker in the background later - $Global::start_sqlworker = 1; - $opt::sqlworker = undef; - } - - $Global::start_time = ::now(); - if($opt::nonall or $opt::onall) { - onall(\@input_source_fh,@command); - wait_and_exit(min(undef_as_zero($Global::exitstatus),254)); - } - - $Global::JobQueue = JobQueue->new( - \@command, \@input_source_fh, $Global::ContextReplace, - $number_of_args, \@Global::transfer_files, \@Global::ret_files, - \@Global::template_names, \@Global::template_contents - ); - - if($opt::sqlmaster) { - # Create SQL table to hold joblog + output - # Figure out how many arguments are in a job - # (It is affected by --colsep, -N, $number_source_fh) - my $record_queue = $Global::JobQueue->{'commandlinequeue'}{'arg_queue'}; - my $record = $record_queue->get(); - my $no_of_values = $number_of_args * (1+$#{$record}); - $record_queue->unget($record); - $Global::sql->create_table($no_of_values); - if($opt::sqlworker) { - # Start a real --sqlworker in the background later - $Global::start_sqlworker = 1; - $opt::sqlworker = undef; - } - } - - if($opt::pipepart) { - pipepart_setup(); - } elsif($opt::pipe) { - if($opt::tee) { - pipe_tee_setup(); - } elsif($opt::shard or $opt::bin) { - pipe_shard_setup(); - } elsif($opt::groupby) { - pipe_group_by_setup(); - } - } - - if($opt::eta or $opt::bar or $opt::shuf or $Global::halt_pct) { - # Count the number of jobs or shuffle all jobs - # before starting any. - # Must be done after ungetting any --pipepart jobs. - $Global::JobQueue->total_jobs(); - } - # Compute $Global::max_jobs_running - # Must be done after ungetting any --pipepart jobs. - max_jobs_running(); - init_run_jobs(); - my $sem; - if($Global::semaphore) { - $sem = acquire_semaphore(); - } - $SIG{TERM} = $Global::original_sig{TERM}; - $SIG{HUP} = \&start_no_new_jobs; - - if($opt::tee or $opt::shard or $opt::bin) { - # All jobs must be running in parallel for --tee/--shard/--bin - while(start_more_jobs()) {} - $Global::start_no_new_jobs = 1; - if(not $Global::JobQueue->empty()) { - if($opt::tee) { - ::error("--tee requires --jobs to be higher. Try --jobs 0."); - } elsif($opt::bin) { - ::error("--bin requires --jobs to be higher than the number of", - "arguments. Increase --jobs."); - } elsif($opt::shard) { - ::error("--shard requires --jobs to be higher than the number of", - "arguments. Increase --jobs."); - } else { - ::die_bug("--bin/--shard/--tee should not get here"); - } - ::wait_and_exit(255); - } - } elsif($opt::pipe and not $opt::pipepart and not $opt::semaphore) { - # Fill all jobslots - while(start_more_jobs()) {} - spreadstdin(); - } else { - # Reap the finished jobs and start more - while(reapers() + start_more_jobs()) {} - } - ::debug("init", "Start draining\n"); - drain_job_queue(@command); - ::debug("init", "Done draining\n"); - reapers(); - ::debug("init", "Done reaping\n"); - if($Global::semaphore) { $sem->release(); } - cleanup(); - ::debug("init", "Halt\n"); - halt(); -} - -main(); diff --git a/ci3/bin/redis-cli b/ci3/bin/redis-cli deleted file mode 100755 index 98303f61cb004b5c6eeca9782ea0fc1f857b6fa7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2253984 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%0D>r{w~aLfqjtGPZyc&6}8bNF-= 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%0DM%z> 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

f62R!c2A}eXuX^r8pprw6>~}&)XRH9 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

N55gux$IyLMC; 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%0DDxErh4b?N9W;FX&zyPN z4I0Kkd$Y)5*T?aiw3;n=)d^Q~f92^|nP7Ybf_5Hxrq0Vs7XHvx#bN<=Zn_*V3b;L( zHPX`OV~`NxNPi&I=1+z@LTW~esEd>wrX-YSb)ePGjhlDk~A(g)o zb-c$_jZ-mW8di4tl^%(YJ$J^m%)vZm@zwEfssaI@>Itr3$T$VBIcvUj?#ISW7h^8n zS2s%Bx2@q9c3$0vEPb|KS=)j?uirZ_bFk55^t=S%o<1YfE-u(t{f8jk=979MKslN&Go5$ zF=6DQ_V(vc1GOb_Gd(*~sFd?|(H5Jyo%=-RbS6*A#f9o8hV7?6WE*_>*0FwmZ){HT ziG3PLZ|GWkwgQdSvSn!NR2%2BbL~(*^J@>|v!Hf=KD*QwMtfD^_p_xZg7~Qa`yco% ztSt+RZm!pl>-F&`eLSL%CHlZ!RIgDV=;Lz=)V)^ipBmxTXtZ%I^P(LQt}Xqstk~vG zPI_0DOAZ*_8kZ!kLB7I-vN6P=zz1DxPsP%w+CM2Wq9wl&BzR{=qS~L)iKm`C{cA== zwLh{mPlI_{J7ZAHzapO}nkavN#vw6(MnOydFdqIjW8YYMSr?n{-5CYs>C1NfHqiil zTtypy2OVrRP^nHp0;+%@xX>_xmD}Xxz!Ns*Fo4HsB<|f~;rP&N`eBLFB#%dq>kD;) zZBS^(GrwaTF8SAswotELMi(~dsaldy(&(h`5!Y}+7YpvIViv1-VdQ1qQ1&ZtzO{&3 zFhe*w-Bx>u?4rak_6uVgh_~Jo14GO4ZFt<;c;`d66p2V|x_BwffSv$EbDC)qUV%H< ztPLAn0dg*00h6rZD|mfv4w2Z9OI!~dM)m3M;CFW@XSDGXagJvV=WtCDUP_YDkzYFg z9rMT_Y30w{CzW4EezvMO2Lt{cx2l~Tzf^68dqn3if&FS)uGtH^qsqD2ZWy?0s8H}f zWX2*-)l^Z9tr4pCuxh%{Rvf&g8fhTpBpX!5yF=BQ*gS@dB>Wl3_v~4^LA{`6dh1y6 zfT`fJ&B`@7!{44e{KIV@MA2U6C%mOUUDD%D(~=ngJxlnDRxr;&E2*IQc zO0uHQ7YB+C_xOmfq|1fHHPQ6PRLH%ht8rj{i^Q&x^;h=M72hET)ICS=Sf=F?Nw&;} zAUar4X{q*2jD^q@lC)+SNo$lOtyx4j|Gh<>7ixckgnbPyMPjYSt2ZSON#`ukx0wG< zWM$*vA39#6x;G8h(ikZWp-lNAnIm-u9KL^<_m-zbGQH7{;eDhzctVKuS*{I zdAO9nx`~Wg6c2>}E%8!Bkq#8^et<>sCz75)-b^nYVpvp4dInZn8?;LLOS&It4n&XM z(y%~}j(-X&ghTsNMM$u~ouvO)y$r@k+GgdJRY>YKI4G(6ZU7Tb{c&G$@QTVe`8KS3 znMrUfpXWPiQI^sz1si$J3wDhH)xg|Ks4S$1s;t&j7LzZM%i!L+N$aFzlbOrQre&7# z)ZWp{W(JE?hd1;fHQ){E&KEobGoZ)mtvRzGb=&@xc?MHr?#oPq2zC-3a}3jlx~p;R zI}ylF3-VKu_fmd}&Bj%1HVl1cFJORS^{*_@%MaV0wingjO`6WQCE_9MYQ;_IK6El| z7C%yZIK91q4$-gE`HK0IO3)Vk)4{TE_89=F++xbEBPQKWEL850A>6CrbUxa6KD_{# zn;8gHQxc4}yN68f$|P?_UNxKS;CS}0@E1*~LjkiZKj;hP-EMZ}m(skvYyQh*Cuh)) z63Q-9i&*_d+LfQmPcfsW?rPJnc-k_<@KC`Ubn||U1A$QUmhev-7Ti;=dg`v+<4ozo z-A-^9Ds&nJGoP4qEHgX7rh!7)rUi^Yl9|u&jOZ0JiXP#G;oot86tK_&Q@?mi<|Il3 z|0Z9sGlsb*-mvf00UUsJM{ac&F--}q=$nH%B)1ftP2&2mzbh6U%EBGn?n z1^_Y_gPB6GCBJ5SJ(s*@VrnZC8$x^YZ|3wSHr7Dx%>SoXKiO(6I9Qf;gX;S*E2dwW zk0>@eogBC$FH;0HQ*{?p-NvW&Ynm#)jU4V$TVq#hWViqQ5vubzQ)i7Ro5^BppDo5}nNa<`SC*CRuZ+lq(2 zfjFZ;hF%&;f7rElyo1e3Z8e{rYKQUJx%LP?^J@?0vq1hvzU>lix|gb=y`V$QJ^vS9 z4l?rqlP{=8`ku%C0{$Q7znTBn`CrFBqi(6=NM&=fJQHoa76eJSFQ-a5al5hv^4Qkp zlD=H_7}J`66t5sr>yVOLV(E{&)=tskod281$d>#llJJW&POSD%@5IyoJiRm{UhPlq z%+nz}y*%TXYX8D~o(|{fl^FwK{-^?;j)JwUm~mh%-O?r2lHZkgug>UBI=9|KJvHHn zEUX6y+`Ox;wc4;%JUF<|J8tUX^bRQU1xUb{q-Fe~ylqSHE^`c0viyum?>cE;aHW3u z{$P)sex!{9v<3l~^Rxfbe ztDX!F8EUE7do+~5$GQ}>lS-!5?yU!@XnBa?3SNX8@IW_2cpSB~O~KS9h2uYGkkO^S zt9L6%K-bmtUZYICy~Jo!UnB!q^XFb5iHa>yF~_6%;wrJo9u2%So6Ol+f6`CHMZgN<#rq40TF2+f*TEIkcBYwPbRi+HIR`a7Bn$SxB2$PVn;4!jvsB$-GGY z^I*Bynsl5;Kog_H0vgRmh$!MOAfJ4Qq|@=<;`7w?R_9y6SnL^AjFzIvH%>a)8zEs7 zyBNbSQ(jStf?f1Z*g&rBXnJFrMbpeLr~{~85Ipj;+}?FZGi4k5=3_NhcZ+e!wT9do z-8jk8?P#yA;!~Lm$S<_j!y^_Q8L^ zy71H;|98Pb^H6R0{qnED76$#oz}1|4Bf17o=~^NH6=E^Z-QMO10lsU`XOkbPt>Uv& zZ9m8Rh0wfLNRA|yZVCR*298Us*wOCc<@Nukm$l)`B)5+mPk}n@v5K+E<;~!l#0*1$ zc5i(!Zxh}ndw8>-ePamI?q$9|iZcG`eQCGu+RQpYB$-5k>QWAPcUaJ zjGf~(?i)j!oXWO!C~$rvHTT3!W__WAH_H$3bJzh)VSTbe0{zPNuFMMt0-{6`?#J(I z3g+>tWBVW27olU=v=?$)?dWlMz{D7VQ&Flg@P-;$!P+gXBu^x> z5X=!hH2W3e#mnYpCpBElTNBTZ!~FE7VZm_Qji!^W{^3j&PF4&W$G4gJktV?%6z3bf zDBw%_)33Eq{#89en-O>PVZhuLP5O0xgYQ{uEc4&&stwbYW|n3NKl7r!s{2Y`ZPen$ z`Zd^b$FXAHn~GA!IFcoVA|8zTYFT9cT)6RY@H0EZj4$qA!LIeYpc|t`MRG3nM$<9~ z8RHMRI^_?4BAn?2FErDdlGA=}0qZ>~HK35HxF~C3ka90V)n?29xCmK$Q_8!pS#9g) zE5^d&!g5Jb986$LfC(x*gh|}R1&5P>lCpA zKSkp5#yiRKaRX-LC*5&`!#$%WS$^7JJ)Jf<<{dYoyn5J-&Tcia6~|9@%EwjB$Y-9a zaE3qKDNohtDOIB?i=9X`%N&-i3z9)kJ@|2&+jbzo%&i8Ql;P9(UC&Tm{cuchF>!_p&E`l_(BVZyl;Wa%x< zyj?)XlZynKLn(t=!tiQKkDb=)LMA#dqZ_X6^tiG}jnsBpHZP>jYj^$x#5ZTX!2$n7 z>&Jo-gtFjk>NXU*y}j1v;>huN8$ZgSr}dS~$(C2U7tpxO{OVSQ&8VfPmiEugssE{W zJLq)?c z9(k&Y#?VdSF*ZL~-dn#5j-*<-#%A^&6~?{5jPxfBVShHcH{NCGWvb%r4kS67EuKLV zGVFm!PK0im4S&0>OxbNYwlbS|jUS+{xs*xx7tNFa zblPE-${re0*;fw8p|UdKG=86D3;43J5|e9uL4-F7*`&F)0?#N|{)N}kC5|I|A?@@# zi#@4CG0YP3JNq-ad$A$kX=uGqIU-wv-)!y7_Z<^-G)b_k(oMvdVRSgb6*#2)2TF{Z zrQs9fr^5!bPnK?WB9BkzJtj^gygu(Bd^D}98>Z97ZuN`jQFUJJ&oOgs-aiENRVd$Vih#rV*@a^KMXc1&*aNSI#+E7fc{9N|_dC;@2IQ!#p#1W+HJJ%UpkB8FA648cD{ONLuA?sFjOA5_ z-b7kZ^j5+zA%hhaj6wx?~Fu=IHFr4{t%9G{aasTao zg{y8{K>JB@r1W6ojOgr9`%ifZ4xI#BzXJj;Z(o#_T(m0&n@&X6Y(06QeK{=Fw*M(W z?dfP>t?qahbr>gxL;2xVMk9bkTkR2XuizP^0=QP)A-W^f*ahwlaj)&g0d=&okf$s? zOe|&?`ZE{fXTL%{j(?JLyR7xHr?4N&bNa7|J=I-5C>`TI84wVUj##2A;jwhEPb?aL z!CCz&T98qG@7dMvehK%Gl$09{8wHe!*5eEXPx+^_X=AtL-)oZHGm7zq*fZsxjkCg! z$hb8{uy?27rM8D2=6?7YJ+<7}W43#WGd?NO&%;o6_kLba**x!PO`VxFri@AYjifxe zK-0tpkLTy-#ZHNx%FJoLnqJTHMR|$7S@2KZ{giiqNMczVUU`c1h|iNRZMQIejVLAd zSJ#fK`#!VFQt?MK|hY=-H_BE}3Rc2dVESblml0jC~n;#8TMIg!>g>z@qG2o;lr zbJSCQPJQU;-{x^%Y*8!iW~OGx%P^!8ujafulSd*t*kVoVxPS&?UP@ZFLTQ2(p%kS_?O`Skc*ti{-idg# zYQ|60s=6<=BbD6}u>oqwa2lcd@WU(nRz*q>VZOUcaw5MQxYo_`98p*7co(p}-P|V; zdEC%1gq4*Yomr+{v@~7A7X=wdC-w}Penyl~y)r4q$-sVEKzT>M68as z^(S+;KY}q9MY@&Bg&PU=RbZ=~Z00xGSjqfp;WEeRhY{iYpNM~$UPLm_k{o*qWYcs+ z8&8J-&eJ?3xK3l{0S#q==D`>vhU2&Ea}V}jnL!i?zWd2;F+pSFlm4v9CLR{+D1|}N z|1Azs#ug{iAMY5)o%xec2FjJ+m(KRJiT=yS_yY+9z6l>*<96s?6r12Ux-rSHS=uGw>q6S~ja`;B))9|B5A(rX z2wsSOuE_$2{?%KuAr(%Gm(M2KH$)}OAP%^L0L%E0rVs>vgJ7kH&HCP9td>Q(oQf@t zEq7V(n&(~Hu`0le%t)UiY`-whyx<~648NauR`ve0~i z9@y7XNG_~=kK#{29w1!>){z@__k=YPAy0JCmuvwLzebv!aQ0ES0UySx+yfBRfX%qD zWv-A8;yS$dnsG{(4#yY9;yUc;1o@c*0PRYQzGr}3fb5s*5KtDve?Xj}-8FfIb)KQh z5PMZNf;nJ^7P5e(eFhR|5J@JRLGdosRqX#cQ2#xA4-ci2B%teV$XTH11)Ul9fEZ^kzWwq2*UogQeLP7;K*edHuCdJ zwEa*EPirY889CTCIX%algwOfHBls>%`!fyO-+@AQC6sWQ4C<~(p<^4CqP0{Jz`MOK zWRsV15rRi=;CP8N6=6n}P?QF2HSWK_#C97m3hS>{8;ng-Y*Gs`Y>7(#4kn8o?7WpG+4-dpEOt6TMtF1wOJPf`? z2v!&<73C!M@H`8|ApZOH1#kN$A38z}$>$olx+TfyC7$&QXju85Z_f#wV8orr0`h=% zInmiKgw8US+oZsujG|-s^pFZny5J_!fek0I4a}ZSV};gLr!!-t{vAT#LxKPisEcV~ z652C5oo`rmI+xYciTdnSNc-GRsUF}8=bd|l&h04Z=NAReltdR|}`}SJv0j(1@m0UTMtaPG9@UAuS8z#&v%9UUd!SWFtzD0G#dvD38-~cA6`lW2=7aBkaRi-_lv?g(4acdz z$`%^+|43xD3zn3e;$fDNuKO&8=NlN2C*dcpQ^ady{J*T4-Ty0H8|wd+qow0i1Kv7Ip}wfWmBQNqBOuM>mn0}_Or^xC^t3wz*3;S@ zhBFCNWCdVtOxw|glrYb=Sb`vZ`x@4_^GV;nW}HKLlu-!b)PW@tI3^<-paXRs${Y0E zF&dEj4t-gN$U@sNrn2x#1lE5?-;YV{4NI8kYu}mJ%t@tiFf`=#>F2q<*D zd9XTZ^?4gyKOA~Z!=L3koUu9Qvz`X62;2`Z6eeZw{%iwQV#(S_c1CIssv)#&S#^M| zZ`K#wy~i%G-1j(f2!ylDB6V^JU62I$5dEl+hUhm(t|56CCaSFxh0M-{tJnSH+>%X_ zAL^do5HbTM$!!@MN2u;cexT3mTup6ra7TyU4!ar7U#A;4$(18S?9U|ICN#MT%jv-; z{Z>xNwha>YXCqYSvL9?eS{7D<2uZwWR~<&||I+scN~wn=1ngEvz!&g{GKn?>Eqb2H z;Q+?MC%HoDbjxiYi9^#ZGfH9XO;{q8`d|;~(ni9LCASB^M#ST18~4rh(~6%l?rZ30 zI)0km zL{{bA`teQQmzdShcVpF0iQR{!f&)z8D*~zaQSaCiVd;{Ot$X=a{7=XK9Q+@L|I_e) z2G&puPzq>)U5475fIwkC=0EVf>Z#1Xs_RH1+VlV)1kzhm?j z#`>7VmM*zy+*^QBNOVfPPv}=KBO#2Ej@OW%_%6b5@yoYFk8e(c>-)m5FLz+eojT;n z%4{@QG0v$z0tdgajnFGl#+u~o;8a}zvE_)qT`}I_Er~-!Qf2Hv{7=xz%Yjy~?I+Mm3Aa)eY-Iylc~=<=y?W$kwDJSDvOCbq&uHb7Kr1`ZiWsSh zQd9;d)jAoNvZq3+9$DqE9kLW9LZ1o5i=_7jZg`0-F2Z^UNY2M@iF&x`{6-mL7Mrmq z#a?;wJ{^{*Hye=*2z%fix9bw=1esc-TQp)9KdD|_Us{-v~FZZ$i$4Dp*nJ?hA zUWN;M;x};REe`n)s6$^R$Nwxb*ULGD$;J3Ew7G4_N;qB&uI8M9T&H>&Ijes3Xg0^w z%|*Q6u9#g&NGp2~9F7A+;y4n4NBvg`YOuaWA}=E4WtSPBvh)k*JFCejDUp$z4pEV(>RW6a*L%Jf}f|Aam&L|!)2^SNVH^Z z5w#T2b!ZEY7|hE~#fm1rg|90B=Y~vvi;iN3@YG4r6lS-9A{Pbf^gzIgK^4vYNV+l_H(ewfbW$q$`}`&@=z2 zQv}V!DgH=Vz8M%=x|>D35WNQ)G;##&inPjj;FGfEb3>Rs@w zbNa*dlJ4N&82g5m2re9p9^M8DAa}lz&N;&=$E4@vaL&1uLlZC_a~$XNq#Q6g%9+MF z?T~|u8MyFtc)J~L`F=)cLB~;$NJ&vphxFQmL5Bgg6@fH;v`tUq(-L?-oggb3lHMVT zkfl>Z!D1wiAmOyVu9JiaEy!Y_!1lUM(_YRZqEJyM>fgx11ryaxdWWf^GamK$1Gfkz z5%k@)Tc>X@(Ki)ZnRZikM@$EWXc%0b_s;O%sFQOR5@XKBiy)&s?Xr2Jgf7TV_9A{T zr}$Fm6noHQD5rpgjBVnUqUBt>2~IJ5*kQ~v%px)?0vv-Zvah1XXf*+cVe}U0I+Rn$ zIw@oyQNZ|d^q?zsMi!3y=@k3hMP#@EE~SeLaR!U% zJVa=_ju?q}*t14A1noxrmIIkTJ?+u?Qx3qR z+e3yiBeE0`9OVFi!V}(aK!#Jf3jAq5_>;~$#AEsZC;oKMDbDew2io!Tr#!UpqxB`% zu$9z+rRdN2>*rB2aXXzwAt%_9IEPj~!3A|DC>uBXC={*EpKx>;nt`^@?Qb`LUsr%` za((L(k5FeWMMC?`CxU({zd~qFegv0D4Lk)H;2G$^Px3?5^!!kg^NHjDD`oDA?FaCB ziLbC=7qL*6U_Q%SRW^R&5^2f4$!LL3SvrTk6wgn7H~vbZ3jB>$lW22+!+20O!s{cy zTZd9WufQdW^*8;9ezk-W-<@bp7t8UWdUH-PyNa(RIK`wBX~ZuvPWrfvM`eW>ZvCsJ z24&?HYANUl`XS16D0jkey_&nA9aK8cx2@EBxC$l2(IC%L~BA05q#HBo_*Y;Dq zgsxo5pLL3t>HP@GeJP*l{!6qgpH@GVJmfd7NQw_x*E1pr-v-q{^@F^PR~@z(Wk4GIz3M0V65r% z=+l)AS)p@*K3)C6PtVyz&l>DKm)77TVBYh;peJbtTccCte?#|k7SEu8S9CXL(K|v# zou->OODD?zk|rPv_nD}U(i)zwXod`@io7pw|5ATTDnc;%C^(2#cDN(f5!T`O#c}v; zT-T@;O}&r%Yh6LLcF{h1mdCM*j0jo&eQH?9hd-&VvKWo+fhF#Ir^t;j&rN}5vk!~d zC9?QKqFj+{#ghcod{Ur-@gT9+v)^9KP+0bV6CP@o^ciiae3{mzV4<4^b3U5FhKO9m z!7G7=?s+a_U*&U+^@DugZFM#bKUY{`KOz4EvN8u2nS0H3T?5@y5NboHauKlq=(}6t z(amp?!qyTxzwl3xOgq9;cOL@}dl%hX3g=TEtdlq+TA4-lYy-l=+x@Jn?l{ z`>e>bq90_ToH9SYa(Vsx3e0eLmI5jRQx>Mhdi~~V-5PhV*)N*ceG~Gw{Pq?7G5z1q zzf%p)Z6;dv_lDhuSR|`|Zv}U!7(Cch#aqp>TOUHeyRXwZoS? z6?_?S0ywY~KE+RTuN5j{3Yo<5*`o^HthY$ zN;`lEM-EEddDzm``&(dBbh{Wy243>*vCcohZXsg(BV#p0ASgGzd*ZiK#GZwuJoW4+DHf{r4pOm-oT=?Ix@bdyfso1|w=b_N~`?Vi#Hd zy1@75`wNb}i@Sj$J?o3;`?Z^E@0RCJ*`DLMU=h7NdBL&rkp6tnG`tV%T!5IkrNNs8 zsKfRdeY0#KUGg<-n<+939LgJ2$bmH%l3<*FRRLR0V3b!LAXS*cPylE!f`ZBPA^>!A zAjXTgHr`US829l#Ovx~dA<`bsuOPrhdc5ammtu4&4k$zg)Pz$MQy-+gBZ6koIBO|- z3lj@)oDz7i@YXMj4r7(c9je9%{6TzE?8Et{$_WoLsauVQyx(GX%ogyZrQT~G^-7efbIpO;=#wj|bK?;J8Nn9y4g*~w&#KN%XeoRN6IRKm@b4sVv( zb2$M88-)NAtHbTi6SQdGH?9?0fEk&#< zvF#k|;}tZO8+0t}n7-sq`dAJ)g|WQ{91vhE3rWhL$gFu7(DNZV+uK*k-o0RN_D)rD zYK#3o#gR!lud2~%J3 zAua&G4D4;YNWX=GJyEc4SQ|vkJ$P2MrAlV?j`*=k5}Hfu;ZjUSyLu&7FH8vfrME+k zt9MxK>ZK{v2a050e*&}Q?^e5|q;zVnfFCQmQ0fzI&hXiByh z=OL$!=S)Z*3cwK2l8XR$KXRZSxNu884Xc zSJT*Kt(-Tm!0R_3QXhqv%aavt$f5wAg<1H)Ge2#}dbXY4CP#;)5iEt-myQT?xn3yO%FEK@_~H~)JN4Vns-4*{N??KdFstk^`srZH5l2%;+9)g1lk2mrQT743F+ zawyoCNMm!NEMj2tbmtcgm%qfE%ab0C@X_eTx&{wO;3j-g&Qi3JK9KiasP6jw5BbWV zeQb@zFt3CYK|@pHmoOlu}MXPCi!!2$JlsY&%F4B7+?NetE*RvPhi7p3t1&5YWZ#uw$&kX6T! zrRZ5|3HV@3++T5_-H&E;St4{kc!|o@W2ykGC5oc}N`65c^#}hC!_h#(kuHDncxPX+ zjUn}ELr87V)_+Hkvg`6Fd-29Dhmfr;IL=1wpVp-jSJ`S=x~@H(K$T!Gw)qoF5eFb) z9%U({eaJsamfMZ=$AQfh2;S%vWtLQIrM9Fxas2Pjo4{ddVSs0dG6og=a)F1Pq01MQ zW9#KV=0CKh-&`+2%o>GJWGQ+WHSq?zVkBV=!d^Uqra(*4pQu66)j!_&JrL8&3K4dT zbb!jxyhG&>cR&ttHn>OqIJle4?@a$N?Ho9-4 zWo-A=_#5NS$6u2>6MtjfR{Vt|0yYAZdP4YE?^@p^l3HE6JBFX1 zo-o}qyM)|WtS(3~@X~e0k&Jz`VG4@lqest#E}5TFk)Md(@Rjqeaz(yOzb#$HVtwx3 zaoU+%$;dEz9NNI5u!e15>YJ<3l%>mq)FKI*__>6BRxEfK3H`&*Wf$u|aq|h}rXEwp z6UZv{#lZb0bo6nux|%Xpmn4Jn{c8*LGHcxGas5$SeS6wle_=z3+~5sefTHPkET%nDRm~8kbcs_n|2C?wpEu8D%PK=w->utK^2C z?LUf~2s2uu9Fro)zKn6^u&f-VIh7}>@VQ-OBP~>@!!~6RE*k}RiOqQOGJ%>TwoO`TqthlQ!r_i54=+7YZXAt}hEC+>{aA!|Ps!=P@`tVh5e8 zJ%qDTPdjb#2A%;Sd_r>u|6D~Uz(VYKy8WWbs+XP-tXj|Scs}RTXYZHNrbV+hb}MFmvR&JY zFVYqs!s%%Uj9p2N?CgNXK<+O*Bc88)>iQk+;vo zYh!mFmqS*;yPia?=3uKeZ6LPFs70DUqV~myWPB{)sbhwTpoajC*d>{|9s!i0da_+Z z_6~*Voo>67F^O~#9vdEIslbNZyI_5WTnj%M5br6lFfBGtL`~=>8J{EIrKPYGw2;p} zH};!AA|38hE`|LB!F}NEm8eL;-dWx>to8Yb*Z>%w6N@wu1^3F}zJ)KKHCpDw5WEpb z(y`Z)YNsH~Id6!@wYCS35v-WPwJsy-5f}rCObT@3`byZL&C{j!a^p&e@ig?vkn{DL zG9X`gA99^|xGWc~V&Q~6Q}6o=NWu$AN5t|J`0Is4U5Q9(E>Z2I6Ly$lwK0Jnn4*_a z9w+5deohL!=)sDDtu6iy!^vSKchEpr=1fA#aVXh_OCDu=m3I6C|D$r7@1vxX2u86O z6rbxPf~md>CnQjhb{kYs6}-u!F*J4+iVw*~ag4<}N=&XHZO~FUo}R!v-wH=TuV-0`HsR58=&S)9 zI&i3SjSNs(;GEo5XgPqu3sFmkvr{<5p`D=Md=*A#>X*)8Sr+dl^h{zuA&`+*B(SDH z50i0ZHQXcP1K}HM*`JYCOTrL4m@TsuzQ=FXT7ILN{WsZk6EevHu~sg@t5a0tIc;2+Q9nc-6N;o%88m|dbBS@nxnpcX{< z@GY$M{K)_IHPa^j)prL6_Pu~Vem)bpzxzG?zLoAPA@|WRtD>y+PT@lo#2=hWG6==n z!seAv(6&Bq#d<9JnViFhS{9a~4H;txr%|x>a1|cHDb9s;|1$il|IYeLyb+!AOC)gY z!y265r8f9+5Iyv=F9|KTgvuT9e-j26#;8+Vk8(Zy<#4%|nmnJ1ZbDIt_f668>a=9g z3|iay?xvlaM5pgbPz+T1MuS2KMTK~=LHlkZHpnNjN!Lkxuk|HXnn|plf!G5&|1J0` z5F3cik)q#)Y;z?U7Hp|2CEI?4 zz3oE^Lbi}5bC{*jj0}lzv@*Uc6!!0`Mv3w9P}mPTQp=1Ra6;T$g|f6tI95sMh%@-B zsVSIU$B2Z^FumH=`L*>rXzO7XS36l;GwLm_*ig_Fdv&|7W{c~`-gTs)4}&UvvCuE# za1nPg3{to?TFLG@N@RD}ug0S47*zEaY@!x|pxukIfogX3GP8OYjBR?oKu79biMV40 zCEQg==@j*@^v0#Lyz6oTg>CC#V>>gTua}xUz41vplDb|Tf26dH=PZZ72PA`;?gkOW z^H}`~K>=n3%*%anFteem?Ky|PKFeJ^+ge^f1Y=5v5ePmT*KR~{Ha1}S&GO~ixA2zj zk3U8IH;c!K`=WScbOw;p%J8^jM~m@z7Q7TqSd(NZ1}q7cIQm%vhm{H>j^jp;wbYx4ax!*7*tAMQCbI{%Y2&`SC3^ewE~vwqw=}IHzQJU9X5kc%=~M;uJ=52J#G0We5iWqux0v`((Su1qvN+VkKaWbTRwhq!lc_HpGWb~ zn(;8ZA~HPO|7jEttr-u~*GGnjMIT4;(3xfAq>xg}@d=dwhH94m^ixv{$B_Xho z_ow{JJ zQ1zp?CH5?RzFE8pBUtUGQWBkSPPu30be?aXd%daordj@e+j~*|-kSOQBd@ibzmvUl zOhptAtr-uCUTK7f=H$`){+j*k$)m^r30fkQNB6%Q#b;~A=XWa`;j`KKYsI=K9$GUV zcDxuF9{M~2>q>NgcB|c=RsKRFJTy0d&3x)toWF(=EfLLMYPTIvM)BF2`PG2u8sW3q z`D^0Z=={~1@zDL5NbsO`Td_RKA6hdW+CCi#9+Yk`y%FUPtr-tTo@#`L=H!`c?)w$b zL)=WXG?r(Yqvrv@^H)1>^x2b5($nmGdgS%!cxlb!rTps9~t z^{U>F;X~DPla{Pk;d`C?YV>>kHv8po{oDV~_nPz1==b_$mw{gC30dd+Gc04labnSMAp6X?(fp{=8PbKd=1*%zPW{2S2I(g5nsdzi8F`;?qAy$}b+3$v0_q|4ZxL|5DtDU&u9Ryyl$lnqovWEZn~XMCVupdVOuG@R`ysz@g@5B;{qfhzCE4PTOk;?ea zeutG`i!C@~)|vcO;)vN6?e#^C@X(w*@#v%|e`wYE{ffdycxaA41mO_n56mqBBe&J? zhesB+7!L#G3R5z95+?ZplSMc3ABr$L`eczJR~YGL;|YUe2LvaK96f$agZV;65t=V5 z4AjJ_=Zy1CHD`4CUz;=F>G)TlGvMhMezH$7Z%_bOc68kQym3zfa8bWJ3qRc<{#=`u zCm0_F%(*AU2i^P+@j*>Q@j>ECDny`u?de=Z1Z-mh7Lp0Fkf}DrW=YpV$4478T+if`#zI zrajCch*(n|W*)=H3C5~mc|+pp5(re`v~6)%^55e?6%@)a+0$kwE8CN^<`7kd(s=1&GMFWgbFHaE1&rkMS&%`4e==&`jrU@ zP%+EzS4NOq83}K+8ZQ$8D!}b17;IM#1{+^n`GHn8I~YHh<5Qe;J|0|;hXdE>cu?kj z7%%3LGg^rD{`dg8Guo6U;c!C;9Q^)qq}9Zb*&(eT5#mGQ$7v`U@JA~J7&1Aem53vE z#$DpbOZ*&pHht@B>07(#TW1kRKASkQC7-;?f~Cew%#!WmV+njW;3(FAS?~H)Z1vyP z{ma_*>!8QjQ&g91hi1*DI)U*msh1fg@aA9xfsTzOU)*Js~a&#z%d zy@tfBt0*Wb#s7>qTbAkpn%9{(d)hj@S?TUhdgx;>M<#`2B_XP02BN@T8ZRLPEFxN} z4l8mIXK;(Q$YnB2x{AX92I82~a#5MCA2`}?-~eTwb12{_!aE_HDc#n4_H=M4HtjW^ zNe?6Fua8x}0u(`BS%^WfL3@`_dqe6U!qXEZA|R)`i3wsYH^j~ijkW%F3|*|M#s|VH2A<0yYMh8xN(K%s@plwF@sLv-kVU!H z>y}u9SjTZ{3%7b!%1R94%b7gg6=$+3qeK#M$(4VpH{r5aF55Q4M6y}zpdi<@@`{kJ`wyhQ~#g8C~{2|aw+Sgo?$gd-bd<;qCHj>DbgA#f7{NlI-T_VpD z+eqYwoSxPvzc?+)F1GtcaMO7bTDOZW0g=43RGb}`CpP=b$F)T(rc>3YIeI1<$x}xd z^VF-_Nb1coB%gOC&7duJi%aS&J5EwZnYc}LM66y01fdcYi)%~NpmsaG4kYnlJ*q>r za|uZ0OQtVDBy!&+B#B@5`{4KuqTysw&T`x-6c01>J6kBwD^}0-?_7pKP)NAItbM{E zK0u&gd~NNcKT~A&ueE`Hq12Qd8r~O?C24iu;t+WlSe1|a$7fUP@RRTjU1}Zzi{>Fp zsV25Lu<9-=&OXq>OL!IDA)LLd0!#7a9u}+6=OJXIF6B#NJ=Ae1swq&%C5ju_+mDI| z{HT~osF+Hq7)7YKkWg_JTh2x)X;@fKgPKN4x&BeH+Md)00ZkKi3?LG?sk1|v%(@na zWqMvN&(aZt%cS#MA}PhrfI{K8J`RD|oAH9n?6%P34SBZjE#5`w0=4jE&G_W;^bpXl z5E?ZuCD0}Hh5J@Xy()$Jl$ErLC3Qr)L!@P4S+5U(kOds(VEI2W&{d$1043;a=CVq7 zf`uML>nv8R&vz!{T5?{oi-OjB&Ze-)JjQ)701JY+LO;dZw=4FGtMi0?cy;BMSm_!G zmA!lI^n5Y7TW#h0TKtV2d3}X=Yyc0m-oo)#DGU8Ibq!twv|J9p8y^-{+KdSR?57{l zL>#CmA~9>upK9x1us{S}lMbMJWDz4HY;+QF|8%qGT3MNI(1xIxDASqB=bknVV**-= zC?0ZvPk~1n#CB>&y&HxVXDY&x?uD7hc`m|{o^YgoYTSlxAKnw@S(h#ezL?4v`FR+x z4)IO>zL8n4;hkEF@(5yE-5=q-ny~L72Cvn<4)2W299`JV(Y3bnpGz`I)LC`X8d@-P zDJEm-{uu9f?GS1gIQ@LqLlg^|F^b^NGb#4-RBtU|w3aYhOBk(1bQ}0L?unqNeWf*o zQM>q3@|NK_;vikcoo^Rk>6a#KtTSDdCqARgv5Kk77-8xWQDKk-Qx-`G=t7-k&QmYP z!l~X3;&o?8>K_uR4{99ut<(YCM>*Kx;?DP%Ne?Lltd`{j$_2{Mee^YRneTo-_;!dG zNtu^XE#_TN?YooM*Wa%v_Lb8lo=%0g=zYMXneq5rzaKDylg&i_c(lgQZC)71dOX_3 zp+6&uM|;_|&~=uCtPksfjb#dp9h8fH#s0VcF3`U;p8K0{xsw7iyV`GXwlDQq3ofC_ z)g7xnC&wC;;TMcdGN=ic4C{6te#```q?x#ENx|JlgXMuf#NCt^$P5YIXep$);L4Dj z+-DlVL@bL|B41I*Wg2m5vJ{r!(ss<^T}H)+O^LS@-j83}9f=(c-r<8$DaPHyu5>ER zOTzYsbx+Xl&!yUtcbt+=oBcQ#DF93_b|r`q4T{!I8@PyWJHwA)fR5F|Lo zx3btz5`m|xrmkR^0X$uO_$Jz{rM^n5Sxzug+?_KmHb}kzJs9V2;;tbBd-1sXj&-$l zh`rsOV{aGW4#{?wMHGi!5>?*4Mrn&?H&)m_vfOtr^6(BkZ=?LD;2eHALskYYASa8s z^LOksF1n&885PjC@_tPnLa0}6(&sBZbAV1R^4;giiZ=zs_$dz1kNIhsY+LWfZbKh6 z^V&7|NJEay^uO`DK!q~=UVD*eLf{99H=Vm+-wE9sc^T z34EOk8cctZwPK-wd2-1-b`$AYRH8<+Bz60DLs zlys*^f(BP>_9Qt)Pqge=Q5iRViAyoM#6l_pqrl4POC-NRV5igeA=c%8M1@)B<{T-9 zhIKhwhi&c*vV%jz>WSPodagsH`=(%CCA|)2rk+mqN+`K{eeBuCM*Emmq?Xw>%vwoyrUfKrVB7wU-s7}>f1K9u zkDJ^X-s7?EzTV?)-JP&xZiFr$vx?L(vCdn9CHsUfXsD~Gzy;J+8`Q^Pe1SVQ-pfzL zP_YXtc0G9=+Xjnw0G>!xU?)PLZlIbvE?%9R80ag|jXKw=j!S7=HeH>YNC}ZvppA=9V{Z_B->s6 zWchBz{a|~I?OU_b*j})Z(7e^%1!zuy9i}_%1(u>BWRk_f*OAc-`w2P~n_cQ{Fe}Y# z3q0?satZu!ii6HnE#ySX%ncbE)tiC_uyWXdI+yN?@$PBo5c4`2U{J+|m~fcH-VIAIAto@gHI0SU4dw>2lY*S=KA?oI$qq_( zDcH4O+6~&svSA}b&$^w|JVr@sJ624l`~LcIsE_vYY=P|&Orc<2_~>n=`OlUb~2b$*#c{(dDEHS%*@g)+ znQg@hr`W4)n`|~fMz~S-#$JM3MtNB8bfj(zGWu|a=-n5otQHv#0>EnT1qnUbbThj< zPJq9blWlk#Fg7To%(BfVTSk0U9yOJv^Kev;AQ49>FA`3|Fp&siCgT>~y;_ya&-(hcz?_W|7(Pc5z(VasY&a zWXbz=9b1?3Y}J;9WChQ|p+_~=4VQFdl(d6bqOla_gMO#@-fwK#_}xitHEihEG=>%) zeRrTGn*a6nIhEtMZ^0tn7V$9&Ph<_x!;CbuPI?DFX-l)IBP`wd#deyEKDKzzMmCwd zpTb>ltec2cV&@Q)I8GTv4w}FumnTm6tE8QoTYL-6XI1_x z$uW7w*T;g|x>;4&BfeXVbY%DwD@gl}C?)UT6Lh@`*!qXFRb4)KK333h%If zQI16#-JZ9o2GiA(CbH*UI}P?cQBD@U*aI14(JO_qEHAaxZYzOB??)R$-rd7*(Nox> zhdr!C>+q_{BsC>IfZ?KSH0ziD88<)U8t}CgR^mqx!aiIOgn^RLx+nxW2KF~2dDSDh zm7ryPc=^7prWSEO?A z0elw-u~IJC5VcXO*nsFebOK`E66B{M-;P0s>gVB}L#)TXR7=rciK-na#Lw%eT+N6z z_$0g~)cbJ+_#-MEdxI)f_$DD+`^V@ok(=$+67l10!qW`1! z$KLs|NT=5#PoRtIH#l4y`t zp*cgry(WXhV%N%J3sH?Z^!RIuLu<^Ti4Q2_z{a<^t!t_LtR3pCHgAIbI@$K2dt>E` z^*Tew5fYtq3_3sWJc{`F`9zv8AH{WhJU`!0L&Kl%x`|=Ww+XlD`QAdx&-XlMh(RwP z#q*7)r1|H&V{hZ<`#Y-f^SOWbOFZ9s4tl<^L|W+cO?0Y5x*}7W;1cVl@z{BRnH691 z=;cna3G-H?Wl`;h3HsBs{^X7Qz@|U_+erE8|G^nz(2Gdd62p}{PEOA8q~J}ALY5+% z1-4gUB64@7MfuJSWdcxg+~m1}He6)vE0291#qi`uwY#R_$s3%Wck7HMUjN|I6lrR3 zpND!+iIy+ZW8OqG_@?4BVtl-90ufFXoyxXDvL%0qJ}=NhJT`A6^t7uZ>X=Z;yR%(h zaYB-9yTx0FI(Yzb;bQ!du*&1c98lH9Jq>?j+@tXq>!A3Hg;4y(N+_C2O3f=yN?S`4 zgQ)bbr!Cc{p4b49Bz*c+W_zhPGfooU;}TmYOkMJeO$id0#m4TWp1H+INkY@h#u+7S zp%D9VMO0>huuNOctea6>onlm7ZK#2s+=X48K>If`+oQQ8WG8i@PSq(UH7}O7r+|jc zIMsu_AxQ~#bxJ#wjWc@Jo3T?RiQDGRsXOX~h_Kbnd=%@=6AWuPzQ(m(&;TLovP@%k8Efu<&3>J{N|zgp1HI zEyMG2I|#s1yxx^V(fbHcf>(g8A^qz4Go9zK!VW|GLy};ZNJ{oc3U>Knc?Mv~Cvi7Q zi1D1Sj5kAV9v_ED!4SToGN;r|m&D{W+8=Vz?$;j(uV)X51h1ZPVR-#AQ^)IvNExqh zafTSQ7HJ4xQ=wU0i!50gK|2$g0_?;9VBbVJ2CTTcF<=ii25dkN0l&KKyu6Kpubr32Of9->m@fyb&Vo)cf4e2r zK^FKcO*zf-*DFzu@tO+BT~@HG=6d;DeO16x)I5JZI-KyDNHj$GYwO{!JF`RZ+I3JQ zc)dXm!|MlWI$qyK%6R<;XF&f!+5oSYAq#k=U5L%%^*NMdyxs{}IWoNF4oJsLyl#Z7 z92s68%_6+6A{wH2Z9RA`xF`g#uMUg^ud%sdc)b;s39nO;GG3jWAqKgSHo)tl0gTt2 zQ{mNts*Km3V?0;%r9{4lLFcpZb3`D-?3h(W`VhTt^~_SbLw5MCEk}=&>mn3oyiSC;+>pgKgV%xQ5?*DZA$N6gnc#S_N z1h2WhBEjp@Az^r}>8ay&KT^i)7n~smeTlRIUKb(@czw17c-?`bjMo(qmm|RIoD{`f!g(@VaMk7+zhdO#Jm?q>R@z&JcqJA#H%yt=$=~w2|mk^1uMp zX1pGOxEujqEBX^&HFOlkYb%lmUQQ0d>#l@I@H!+t46n;kneci)QpW2%&Jcsy!y3btHpr60iLfnGwC@e_Ymc)DuUSMxkiSOv zOZ|uYrEa19QuEI`*?y`2J0lYO93B*gpDC!!`VUgZPc~;j|3MmpAJ%`mFuya?_itKd zuh)N2n*r3;))+v|$deyi2|rcns2)Gj{Zjv%`=ug~CrkQ<@VlQZk>EA&+%UYph{~-0 zAZ5HR<_zdRNE_gFFtQ*|?rg#Qya+`ZuM;nf2(JVC5ME`XA;|Bdcs=d$8s9qvueqHf z!RykLFuc~p>3H3bl=1onXF&f!+5oQ$kp;YJEx_vz6lJ`wxF90D&N-9tx{PRu;tD70IyryGhVA&fY$-2&3HWm@ih|tCz0@~ zp`$2XTa*6NBLuIz+C_rbA!mo-bvY`t{)3e9I*&7;{~!&)E9*bVQi=}(uBX(0P?F)A ze_ms_Hmm=1C%k464MF}I)qj2w{ioYW^`F>C@N?K2hMy^@%=!;f#!oh9K>tA+f*;m@ zOw8{nINB-oAJk?5wM}abpyu?Su7sZ|bX1R@s62Vv%abK%gz&qc+C+laygp%geG!#e z|3S)lUCbHKe~>o7>tJL-p0u_AuZvKW@j5XzBD@alLU@&lh9JL-;`Ox0YkYhNUULnR z;C1PlVR)^n8^(CukCgHH1!qA2LD~SX3y}r9vQNTO*q3&oDC2d-fQaxqr!(Po8PO2M zYb(-!I)&i%;hJF)oIl=^7=~9DDzpBBl<}Iz8PI=_Ho)uFYQ}3;3;62*)MmUMf%qDU z{?n20s-dGOUR#s?(;)<}yN*YK*CE}*@VXq8S^q)Gc%8=?(0`DI;Fa|sWGThzfa@vs zACzRc=J#t1*Jkyf_Jr3gq9Mp%qx#P;qW_prs{d3)f}g`(!|*c&m0ABm%J|9V4Cp^d zL-51;&r#-g#Vz1>7os);sBPcI0BTPEX-oL2LPz!ZiOQ3wy*ybG8^Z5?IvfdJ^SXrL z^+i->{Rb)Ibuni^|3TUSuY-{Vd6JwfoJwC`grbbsiAfRRb)bpxDiaMseiy~-X^+?V zm=L_?{uBvbmv#=rYt11YultcQUccZB=s!ps;B_IgfY+N^fY%);%6MJTGa|gsF%n*v z5e-qiwj%w<5Q5i-4@QF5JsrdF>Oy7Ke~>a>(>MeA57GvB-FkrWI=2OQ9e~=5*CP;L zBhi0qV}Mr;9Yyiln)IKV4jlhMBL6wN_C{pT0ae@=7=_0S#SFYNy+Bf-z%wqf|0 zg37G_AZ7eya|ZMuq#^iW{pWk;cN<#3?=D1b22k5G8Uv^~{pT3rrwSd_<0mRlp7!!& zNmU5H`$>xguX(00yuOIatp6Zoye{Sp=s!ps;B_#vAWuHg0=zClQO4^;OGJ1bc$Dxe z6AeLr7scyokJtDkA$ZOGE)u*hHHP7}W{-~7{YV+FUvLKWAEXWNx)52w>$5Gu>kbrU zysn6g2(NQ~CcG{q8lre@Mf%TAA$WawS0s4dQ`?Tc3;X@gx=@+*AEb=eG|qtjgR}u& zw|>ibB`4&kvM&ulZN}>nh_8|8KR*&)HFOlkYirVf4u;@$*Um`rI;1)bugg)H^&g~+ z*Lj=){Re3XURnP^7S8_wuBX(0P?F)A->xxSo7H~~5MHy0h9G~9>Oa4T{Gz_mVqB840NExq-IRp9+(gt`Pj4a5LJ6kY6FG5kq z>%=w@;dS8mgjbnp2=coqUQc_x#`{9>n)_)acwPE)7+z~W(eb(;DdY7E&Vc@dv;kfh zA`5ucT7cIbD9U(UQP(aKd2-HP!s{}kA&S>lr2l*ug4c(4M1t2nKZfDeh03h|AZ5I! zaR&4sqz&-8bvxs=ss(r*fZB}LBM@IB(SLRmUNv+S#cONQe|Cl7b=S5?@H*r`7+#m7 zGV4D`8L#s=1Nslr5WKSfgDjl?X^H-Wk_^}UtA+f*;m@HZ#AITcZD?pHD;Z`tZg`@VaMr7+zhd%=!;f#%mg9 zK>tD70Iyp&FkZ7-fY$-2&3HWm@ih|t=VQXFhK{0mZB6>mju5==Dvt!OL%t2e>vB|P z{Rb)IbslFx|3MmpSJr=!h4Vjv>nYcNP?F)AzppV|o7I1|6JE23h9G~9>Oa4T{`2pX z>Ob#Bf}g`*hv8=mDzpBBl<||z8PI=_hTw7os);sBL9q05zxoY$g0u zp`&{IMCHlTUY;!362k9(DvboMd0&R%^+i->{Rb)Ibuni^|3TUSuY-{Vd2(6{@VW>^ z8Ltzyi10daGvQSx8iM>Tir3Q~ukjy+;5GN1NbtJ!voO5YyshJPKT^i)7n}k82WbPm zE<_gadQ%JVx&uWSuPgRMgx5JA5MGxN4N<(dBK_z65WGJ8RwQ`c^KlqnU8v0Z4^qZ! z8fQTNLD~SXTh}mN=e7W^15lgsdIaKYB>K;LgjWq6Me*92^q-9(c-{48BzPUNJq)kQ zQJM80q>R^joB{m@X$W3f|3Mbc{{XJ1)PGQt;hMj*FM%~jY#lwcxxDbrl2zGKS&ur*_;9W2Wbd?SpRvQ`Q3&V@Vg69n*r4JtHuCoPXAd? z_^Coi_4tX(lc&8rS@LcOzx(O6Nbs7sISj8aqB840NExq-IRp9+(gt`Pj4a5LPqYB9 zi%^vDI`Q*}@H()J@G27xL4Fs->uHbI_;n$8&3z>jye|D946il+)bY9>DdY7E&Vc@d zv;kfhA`5tZwgq_IfufAp6`w?e*EuDG*JVUQ6tAsF|9K|_uMe+`1h0GE3&X1mm0ABm z%6LuV4Cp^d8{l>8ON`f5Ex_vl)MmUMf%qDU{t2`KS=BG`Bo>r&c~{>03zE^mtMgtC;_xzR@?P zuC5MoQs30&%Wq`cd$J{e3+h@HPQso}sxLM>)OpEfhvGC=$=aC4&kMh=<+!uOlW8G(YYumazk5-bpj1oybq}A~#ntXg6mJ>Npg= zD~F=@5ugNp&0JQQWa(=+7>=wnd8|cS-JR{0*AtTSiW8ELtZU;r+aV?x)^-OuzHe*l z>Xgi6@v$T_lk*h2tvXNGv#Tq=l*G1N%Nw`!teu`KZb_=Gd|zAjMh8RS`~GC3!v9S;wyls3qaF@ z%LR}k)^oW8AU5HAQH~3jk^pcgT*M^ZgN8+!U7ciB$1z&%>bM4I&Fq5PR`n);HM0i| zCUr(SjVHpYIw{Lr;zz39MWFRS|LA4rW0k$Y7mkz?PB(_)w3Kn`^5gV4;S@aLBMF=$ zPjO}er%oNGn+T_g^*D|HSIGRF`*bAuTv`&wUu*uZ<8wb!#^)ED0sRMQI6m8;AI9g_ zO^xx{EPr*NEm>W)1Q^zy2FC>V2*awM#rF5J*gx^OX1w0`D&e)5Xo~Vzu>I4VzfO9k z3I6JRDiZu|UmJ$s;iydf^*p4E-z3fugL)$k$L|Q_Fn-V27$JVelZ@YGgx@{?iUhyU zyiEArfcjDVlGt*Z<9FH0Ch%MKcqI7kwk8a}^HG`bdmB>5?_|ypgQg%2$L~VqFn%v9 zj}X5bA7lKEBmDLUM{PO(-TNZp*Gx1;`S0nE-%tM01b$(eh0ol_?3yKD1J|W{3br% z1b%b>5($2n{woZ>HGkIeyC118|8fT8U!>vqZG(Pv`4=K|MEG@}E#P-E^dIeMaMYIL z_r_;g{w12C_&xpcJL#Dw@augv68vs|B@DmAQJLjmq`Lge8IXUGhU0exa&-Ck?FjKJ z9%1}`LipYDd?fgN=4qCHQ9p{`)*%1>y$Sr5{V5Xsc6&Jtzw=R<k_?_@#7=G72 zsN?q~q`Lge8IXUGhU0e~a&-9@B6LLfZI7Ce9}-#q1xIZ;eg{6m@-NX8#qa5l-^9n8 zz;EsYk>Gdf^I`a{xnIZcex$nm%NdY=k%r^94f@gLUx?5V;n#t-NdASfU3(fFwdMG| zaXHJsL{k*Mr$2rt{jCZ7dKX86-|f$Y;deMHv;2!xmw!0}@-NbG{Ek45F8{t7A%2CT z%fE!*J %-)H{9@-ON~@!J~Y-#<5j-!c&ie!D##hTr+9%BEj#3r^4{Nc9D+X zmyqi6FK0mhMH-IZb;!}>Ux?5V;kP|%lKe~fy#XAx<@g=Al;vNdDT?3IAHRuzY68Ex zg^}QQ>EmJet?}yk-H%k4e>ns4FVb-Qwn0C-{0k8}BK$hg7Vx`-?Z4otEywST53&49 zG)3`y`r~)fgH7PqyC4$$ZvR^teutwn%fCo<`Ij>w{~`^??+E1R^6#?|;#U+fewVTR z_fL`F_n8M+{zd&Lep`e5dw&!7ExRWY{C4|G7=GuYGRwb6b@`VwApar_$L~Vq=<+W_ z=!p35#`%n2+SRD_2S;r=|J}Qoi_q(x2O*E>5B{BD0R48OxsndM)ky8O!-kbjYe<97sdboqCAg!mO6UH)bJuZRS{ z&lIx!i~3Rgwg&mvTaVufve+YCA>mljL`fWxCOFkWZ_Wae$KxE*x0lP}gi{=ly+5Q} zFDs`0DS(bF@-}3YXbBJK08WbG18(zB2LSGUD^dpV)tn)EPezKfX|YI@ab{^jtpTTC z-0khQLzW_PaVT~;#5c0IVU=9^U5ry5X>pWlCP(Q`lT%d43-%fg*3~&uk2!7cySvE? zexS;-cX4u+1(YDuQzbtI@5=+t3Xwo6RqvJU*;HOjVw&}ZG2A4SM6bHws zN!=@ZSo#*AuEBu=VzP*r=_rluGq=^DoFNx&^=O=5K_$=;-}CIY^v%IzdtSz?D34t? z6XQ=?`ezz^u9sbET4&koGs~sE7};BD^nTOMQn;HdA2V7CSLwgnSPGZwzhd08aiGcM zz5;(^-C6kC*4@*gTqqYEad)zdZ!)&Z;%jZ-?wUH6dJj$pT@-$!Gj2G%6^(Bk-gS~2 zU!3el*~xA^f3h17`fs>wHI~8>JWnI?Zl@B;9J5QEV-6>L0^gb4AomhIH$J>hPnVk}zyI0Rm56|6#T)fpRqQ58y#~fwlhEG_He4^CtB(oK6j{CDldGl4 z(iCZ`bj_5q>E{K`-#OIzCEAVm^8Ak`jlPen`F!OlFV0N*p;|NnXQs+yZ=x3jSE>B-u)r^ zl>4=pZmFsh6<`d73#cz;c&z`%QrsviasM7!#~;+oE=1Nl;2DTh>YliU&RmwqZoiE` zNuBoe(#DhRV3c6jWuxy*{XFeE^eq;gVD8t+sl@(# zD_);%lG$^yCVpsRC@k?z&}QI=r~uEp0wm$+-EkaO#``S9+?M!Czcr(re&q~9YRZGz=crct> zJp=Ov&ifUc=-jHM=r-g!M0ek7wS;z@ZxtUC)h=~%ve~JOA6Dh??!u|d@!3_b)GtP; zrn8)>ySzKAyjA9z8#0dA)tlAK$YaJ z;p3MzeEhP8k6+gC@ynW6w-tAB{L-nI&UFdXxoa_Z7{meZdJqsd%q8N6N#c84jZ#cq zM+;L|N$lgR%yUPJ%yWg03z|BjYR6`3*=>6)-ZSu?P+fJI)%o$Gl+1JO>V!mU1$}q4 ztG8OI5$aWSrBKJzr8+;Idi9si@~+PbR0tFfl%!(lX6AC=^LU$fKwa#}6KTnc^MYzg zI5Vq#4v&BM(!fCKqGe!QXaWCaE9nT9VvLk`PU;6b=uxVdbOkY0;py|HdQkq zb1qL*TOayvR_+=I>;pW#sW?55FD^df-@AO@OU27A^9cVByPCo7R9 zzRoM$=ozOCn@B^;GtSSC^D=h(rtvT-L{)%Zy$9p}P9Fa^)A--cBLMl}3)gUd4n3{! zXFXpVHin1l8`4`mQWLnX90LgG_UyI84EEyjI;D>gr4PE4DG*&w7(M6H5%J0eOCYpZ zmX=yx{}CJ_E~5_B?Ah%w46_}=WAK%r6MfugeC{gx+_JQ+=yP{_lu2ucr%ZE*DF)x) z2q;B&(hP%fs`GaUgA=6j6E0sv{1Ax9atg5UFovFzQBp8``{G`Z=B~%5F8n>cqZ6_? z><}N|o4dsO+QCBovmfL}Vke;peRc;@{_GcVhUlGz)TwyPkkRB)&FUV;OshQh3gq(_ zAGk#S#E?S$Gp?m*g3f)j!np7BC0ga~>N>ma;OsTB+U=t+dKoal4v`%20)6gYG?UCQ zTZ;ZgpGt{KcBlX1{nlf=Ir-fya$ zsT&-^g-3SxuaZ;O8#jZcjuyBezWPdD##VK1-5Q;*t{u->l}%~ z)%gN{e1E`wN$dclwGSaii@&}rkXQeG0^=_Ci!dkD|P<*_5yvx zjMVw-0JK0OrUz0UF|nK>dbdT2LHE&Aoxk4aujc2kE0ImrotHH&8dFph|kGMY#MydhyE%Qns3Pqq!Zx44&K=5S(Bv8b6>xYbg$8(5IV zpgmJ?4Mz>EJihF3aa1XC${z48dh!0Eb)uKH10yMy5Ja@65tN-o>;_8{W zY{=DLVO^8Bx)#FKb^gZuwQd%Tx1FFN$X}Ts@8x_m8aDc##^=I3=u|q{P|hjh=&3D* z`T7vI6kd!U{`mk;PfjGAzyO}fHx_r6l*e{KH9YFqTE;k@3Hso%_fO^-qCECz{4i=a z%FH(k4-s6}GG8YVfu7s%SLNGKzbZ015g*e_c4ACB@V+1(eN!dlrC~^a;7I-6xZS&_ z%3A}UDsQ35jL+@r6x}-oT$A*YN-H59?P>?Pp{Gr=c>jzdd0_U1g2pE%3-_(~+s1tr z{>HdR;;+el0sgXtbOI8ROG$2nF=u246{3_~O=Ne#h;8q8HIXzJ<2}Rgk;2rAI+Db@ zC^uS(I}5|UpQzwUnL`~S(-v@Xk<@E6xCBwuj^2ZKSC!4`jCgpQI7^+Bh(XuSq3GQ} z)*Y=yM=^&{mp09&lokspC&})0R%HS^Gz=9hO^l#HEP@z)H<(E?kE27fz=xn899c#aFfB z^R!V%!(~u5w2`>M>fQJxap{5fscG1~~+mkR)SK z(dZmDH{ zVR5OCl%N=tElZ&jKOCidOr!CIWhG0t9kUdX9OEcG&|X@wx2g^-BxWu|mMZzp0*vjt zIuN9!JH#PJ>9_HY(&OoH7-5`j|ivC(cgrXlwC|d9? zy}fX`D8wEyFS$h9aT_tpqt_5a{1*miJ^nK`g4N|^)cFn|8+_6+?2iVtW+@^ov_pBw zOo>=cdGsor%1top@#}Dj=jo;_AhC=yk5WxTecx~!1)v`bCvIKP!ri0rL6fXqhM?X~ zB_Ww)7_)B)7sb4=#)Q&NlumN~KlaW)Jc{c2|GTguizaRmkf20UT{To9Nr{pO*o58aOZor<=L+VVBXn|g-7 zZ0Dg57p6(NUO~IctLilG}rBJqcf!Q|74@u)V4b>ztyBjB^ zYGCJh-UYEXvKQ}xgq#3jaB-+#LaLGOH$pFNMqpC-yy1E#;Y^hdbJ3JXL92Vyr##&1 zwf@a(-R#wW%U4$aHC;-t_9U%&^*@WXKapuV&Uw*ELtd%y_+C{o8xF5#(CQzQb#x7} zHJ4VGDC^?HY5}b-P}X%5t3_CKRQS+zkhB;pr57srO%aL^9jbCH;>NpGR2DaF`g~ri(6~uE-u}dDa^cc()d&>RG4arFL};v1ee^| zBDkoi3xdlEx|c&nJqgP)Y5_a6lqy)aUI>yKsK4T<+>MHA}dth*2IdpOrk|w~MYlkFQ}~ zz8~`V8s_C25FTIi_WvxbQufQ9#H}wXS+-LjJw*9NrUik2-C;4;o%T^S(vhIzr+7H zQdVva^|!yJ{`R+YhQGzFik=VU(Bq-2=;=^Tg(WEqL1?S4qcPc_!g3B%5e~uZhd&m5 zZfLhFMrsLHvpmGv=!%;x5>f~JOm@r5*(vm?wkIzz93AclaeA}5LLg#LBrBe>2njr_K|`U<<*B? z?9ql^ny2lfRbmHoJ6@Zo?GdX(yQtdGZZ5Hk9J(NfZvTB>7hUWZ@e~D_xNu?>jARH7 zr{Tm7?MAsM;>>oV;*lnesz7BUE#{LW1ryvcT;PC`?Cq!JL?g z2oMcC_gSw81AfWMRXcuGlaK8|+j)8pc9&aRK&xed2jE;E`~&>EL&`zfQgP zJdE?~rrG;$;Nj(=$D2OD2D(3l^gQ3Y!lQ<`xu&7>deVSlGM)g@#DN~|El6KqH@83A z?#rM)KAsdshV@<~1?zu1&3r34p-(Zc(sw*=zgjzwGh(Q*HCRM{DGf#~+9r?v^;(jaA0W58_!| zAxGJA+~XOdDiLyQwX zNMc3n8Bz~7AtOxBic-CSxd3=h7u*SGhSoW8xDy;hi~6BA++Uql&#g&~w5E3ur_jqD zh&ZKSJn$uoPkaYJe3})BPa`QlT~G1pa*9tEP)kCG@3L@93N@W9g#4_5F70D#I-~TV z7$+Edb)+RF;0}-wZ)i!#AwJUVPP?y*x~(*f5@|`((2^3mxDcW!vv5nIoDho;^@pSD znl+HhRsz%Tv}Gt_6sPBw6y7JCKAe`)p)9l{8Y-f8k;Lu7WeK;7#G_5W!lq!sPtU{q z3pAMUqvMlZ+C3NyoCW{n6SPiVM9)Ljy1JIH+uQFc-FGg@itN*v=`Mbh2sGF47+KhmwN3an>N{3RJx&4KGUI_Hnw2 zHJ!LZGzsh?iaR{s=P06fltICrki(|v~b38xRIrF0$5Tig%Ne_2eI;!=g@E&c>TM<41`=yo&|x0Z2M+*-!jjy8X+ z?LyP>^{1xe8$k0GTkZqWNZ~4zm{02u0e)qyMzjf@;((7T({2qjd`F*qW zZ20X1*%9J5RfgX&lZfA9s#c8O$a|qt%Wv*P1HUC6X?|;`nD|XY4ita;!ZN?{?9kHf zux5VK;bDG{%{m)?3%`xDc>I!Hu|6>n z%HQz_>)|u`j>nBOvdsFCzT?3|FUIe9V1T0^bxqIcJ04^7UVTSvz0AEuuRfr^M;m}| zaqutW_8Dy z3JqvEC>JF#3L{#!K9mL0VBBTEKq{Mhi&TCneQ4Z8>=RBOPD|-{+@<*v9?wEgz4TJ_ z(i0tP1~`?f0sI*c$l;5`S3Ld(HB7HY@T(;}($a%QT6#ea<2e@%?LrR^*Cs*_zZYy7 zYvB2cJZYYb?=tbcXSm>bD=hQ8mK|F9I#@H$``}@oS3*+D^Bn*0j(Dz>;(6dL#B%{v zGsZKTS(Njfdb5G&v5GX$)e}uT+mQps=WlaEJRiUUUL#Ep!kT$bL{jE?A0)Os&x60} zi01|=p6?n%JeN~7V?0Mc&jq6mJo|5y=DFcE6VKVmfq3o@%kjAnJGAsYA&+DXkUg#m7Ylg*mCJhkv@wt4If#=mjqrWc z%<~L*nCD@T;PN~#4R*wHy%f(I-NbX_4Kbe237%_I1JC=jrFqU6XX1Goav+{HSmwEm z9a{QqSToPd;bERO=UP7l&*J%>U1of4U>@;XK-G#p zABq0)<<#K@e#Z`y=C^vZiC;T%;Qrr_Li`@U0{8!5&HN@JDf7Dzl3SkN!Hp4qTOL0# zAQ%2fY3Rd`r#_#asSv-5s9G_8qo3cRTm!$0Go<-#9A)BHMGnMo4lKvttJ$HYr@@-} z^}@sara)rL^INtr!mmXp{;s-__-z;%m#P(uztJCm8*ec1+w&S}ev3w!_^tn; z;CDT&(7)_}{)IL3yA2+pe<7je`EA`D;kQkO-+?)-f2mqAexsk?)NBL4W7DMhtsZXT z*Nz-m|6U!|zgS@X3v1>#5lMyqg@l&pckr$Vzl}2d&b*%WFI6kXZ}jt9be)0U#aBx6 z+n8(OS49r2e_@6GWe4;xteIagJVO6^&W7Kz9T9%_$nd*r5bIxOjNfxZ|Nh9p@6OAm z`Axmi#BUXHVEqd#^e;Q0e__r12H+9;7ZO@N{;u5~;kQ z-=3+`{1y!{@mqhH;CDT&(7)_}{)IL3yA2+pe<7je`E7ka!f%5NzXPvj{Y%w~@f-d8 zrluSC9h)M}Z*{hbUpsPO{o60Bf3d*&7uL*gB9aRI3kfaH@8EYM{H~MXcjh&$f2mqA zexsk?q8}RgU3{@Lzm3t9%*f7t>33v1@r3y;vh%GvN+Ru|#7UWVUQX{>+y z#`rxq^zYRMes}hf<~MbaiQg*Z!1@%r#BY5s!S8xlp?}!{{R?a6cN;uH|3X5a%8i zKpSTzH2uE0b(7#%uUdv5fMcF78%^M2dVFjvy;8wkg!p;$uS4_ZV7}OaSFrx2YK8gT zKvOGu@H1wcn)4q_ZS!|+H_eW9CF(#^V)e}n+|SNx6fm?O4d&MwTqM_XySF`C^| z@wZuhrTEKpW3}R^P78(iQg5-f%ttA zmib-44*dQN*39opc$nY0*Pji)yWfcLyGVv#$ECz?230E@A7lJRFTa))1HXe3rTHyS zGw~Zt5d7|iWq#|}f%zX`&HNsOhxvUI68Gf$d*|`D&ubBWt7P~ca|!WVOx23<8~yq; z_hJLTCFe==TYII6-!$Yv@wYE5^Bd0&%>Mvu<~JQ4=JyyRt~|emYa{&D$nd)`nfR^g z9^?0%7#}F_W8imnyfnX-D@^=OMGmZgVTJx>2lOwjnco@k2>lBQeKNnD_pg?|D)`l_ zCIH_v?_X{2&HA^Sf!|pF%Eq0%|55)cy?00btFdv?d{w8K__8Ah;_F*mh_3@!;PHQ0 zGhc~F%6#pE#66j>&hPJo{~U>rm~rgP`&To25?_m`TCw|k^xxl$E;Pi)#a*QNZA>xo zt0D*DHwTvE-gepe+CzYW$HzvsmLz1m^mcW3(z zvcDfoy~M=bcAAn_kr?3O#|FC9$1Mo1vldd`&e%HPn;a8R6_fR77n?%)$#oy?U zzl{k7etRC1=C>%>#Bcpk!S8xl=J!Q*VEiA}%@?zeN9Prk(XKRV&PItbg@C?O$z+@2G!uxK*03+@2=BRv-uB>q%ISj|{smM>patgQ^wgD>nWg#pC~RhWI$>8)<&alT7>u zzZU%Ng=K#0*n#nXSTnx|;bDH?gv32LK6cJ;pWjFLMUUys`5n`h_${Vt#rTc>`+IH| z1HUEzk>rX9vdrVa@!e!^8X@gT$5RxA55rzqn(cIll`n#Ba^N zWBi^I;{)aG-A{Z!V)Y?uel6#l_??Ozh~L{_ncsYNVEiA}%+%U3ae&k25Oj~e(r{4Z&KbL}R6S0D%0zpz68vIF`T*39opc!d6igg!a` zcHY0*{iNVmuiAt6?auu9mE(KjH-oAb)(^4%)&I1AHRHSP7Q+X24DSan{!*H+#yAsS zDsmvca$uRStJ#6^e^@hLUU-1-v$|e4}C-Y zCQ-G*@iE44^zz&IwSnKB|CHvp$YSER{&T_adRXT7MRs8PAJ)w8Hh7rd<&d~1-`_ir zzpcNH@LMm#@4&;vZvj;+#&7hG52XIb!0*`4r1`Br*3CknxU9zS81Q{QJ941-`|YP8 zeh*-Q$Nyo?{3aqP^ScicSDxR&3nTot$nZN8sqy{`RV&7C^z&PE$iVO7Po(*6{LaL$ ziX2$~!V3M%4(MN4GrwMVg#PVzD!-lgugV@3{Nndl!1v7iSE~-P{{4r6-&p_Zf7-uF zJlIkHYS2DuzRKH7d<8!ieC>s0zUtV4@qbt|UkBk~zTSkym5+~oeiez2)iUvMOf&IS zOx23T$LNobxnCOMW66io{MLSB;x`RBP<-qQ%lyW(1LOa&W`5J*VSbN6;>z<|xFEvs zA{l-c28rLAy)k~ziSg6&0|tIq@0RA*a@fT0ROCSX-UiG3=CcFi|FC9$XTZb!4m;lM zEcjho5#hH=hTrY~Bz{}|9^?0%;J5a31HXqqkmfhH#l-Ilxa(!SA8B9{Nndl!1v7iS7Y!r0{WM#73MeAzxto{uU3B2QU7Y^ zc4@v+n@xOGAqR?&55O{CQ`mv=e^@hL0eG0NNk>lQtMl>GwR0lzu}CI99@!PV?%uGxlNkiqM(W2`u7FD>tUJS7ukXFe^@iW+u&h-mqX&793MO9xAlPt zzXdY<4*ZDtEud<}_>KPid+LVF_XL#~^V}=BsmkS~xQjA5&%Gz4ZlmLNBB*W z;dlEs;zX z-F;t#U%L#yj`xV)460W6{vM0J(Hnm)?;7|W^qMrk<-1J$2GxVv5BK&5^@H?iS_${Vt#rTc>@qyer1HUC}rTMMhVd6IpIk5hP75bMQ z(7&)|e$(L*`WF)VWPUsEUlraX_{Hz9FjM-O_pcVd!}@oPf!|pF>VMk5I{0=+{i~jT zk>;zY!NgbntAelfu*}zs?7;XxteLNE@GxJ?A#qRUtMmR<>rW!_v5`I&aHhY%8u%9R zRY28>#mDH6kExpt@p0^*rTMMiX5!b594J11`==1U2e827|FC9$6Ookp-3N&)&+p*7 zBK$VU@H?}X_+3QRit!u${1$CA@Vj`GG{24Sn)p?b1M!;!%klSWc3}J;*37RL9_BaY z?`Okr*~AFH^)mdf!skyg{{Kph-*aMop!y91zdK)+<~Oz8#BUXHAbuZ!Wqzly1LOa& zW_|Y*NERFs#YxiMt}TmTyNmF=l@CbTl9{J-};vXzw2Rz z{$&UBFRYp0ZSV;F3kiL)e(1b^)q1Po7r(y(zGvRQ8n}-2FI6kdZ>)dyKkZ)y)^^mt zTK%FlUzWE_d`(3T6d!McWxn#+f$@J>GhZ{{VZMg_^;Et(?_Vt)7m1J6GVyWyUx=@k z7YuyG#!sVn{IvE}Lwr2^yfnYLwI+U7AP3_2Nm%B00Xs1M4{PRkB|OaU-1p9g-`zJy z_+2E!uVXdwn?cnI$Hy4I(aW#p&jx-6{a%{i@;6QV27f2`-3!b7*0BTQ|FC9$55mLz zz6ptY^8LN@_}gbpgx@L|e#g`hzr|Fo7{Af4Pjgop_$_%>n%~;jO#G%H2a3OaVVU1} zc3}J;*354@Jk0MgNL+b-3q28jYh?IcxRUs-c_zm1IWay^{<4AJ)lWMqGW@Rk1M%CiB*yPKaeuFV-oWq9$EEpAtugUig&c_A2Vj}sDeS=bKdhPG06fg^ zq}R`e-?d7FUsZ1|8UsyB0iAXB+FC_HI{C3{I8a!0+i{D=X-!t!D&0Nm60N z>hPn|eC4h%@wEau5MNKia(rCC4vhc9n)zA@5A!v5^{ISy-oM(N6N!(hGV#&z6!Dco z)e7?!8~=~u@&Df%;^UxSN%LF&2NSPHRnapxRqep8p3_^m z{tAZ_i&!^IPGr!y5 zVSblG;+}ke?>zptUKQcDUWVU+Rm5)rRV&7C^p6juE->&rwoIDe>ff07wIc_Lzu*2M z#P0zt@c2KhncqYtWq$WT;>z z+BeELxAy*cRXgU<+Q)jd_f-GClJ8M<+m&g|OPvNNWWj_GF+Ge-z zN>u|1x$34*ZEDw%1*-n1s=H6f;S0T$M|_EH{UX)>oxSubH&k-w!RALMJ1)yZK7og8 zgU>!fJlB>G&qLbV+Z~r#HrlBW?Nc@9JJpfD8+$q);g=8_Xn%KLj??beJ=QkWf1u5+ zd2DU&{ztw2KUJ(BDE`me6#r4AzY${=UftGL3HY4t%IlQlrmwnsG}~qVFXCM}`yKv% zNKtvjQLzx4{Ku`nS^nc)e0TbfclC|*AGi6g_aBe*UG6{L&3C~Vy;tASTCdBL*OQPo zPur*X-?Dr40sTGNfPs11N3`ljSt@eR)AowhtnTO%I9%F?v^qw&^%$+$`uz8OxmuN6 z#9mimtX&CAOLFU3-Ic%{$&?x8q69{zQchGn(xg!psBEOopz=fMvr%epc%N|ka9T=- zvSux7zJ#yCkIF+ga&Lf+h&$~67$SbKY(ZgVQy4wq(g z=IaxC9L?7z_Bz`9v7+r7?pQmq|MAJgwTT0cH@_ELG|#~ETR)fPIVoV`c{Xw&p6`QY zo(tKbrB8r0^E?+G=GhBLeR4eRoaYxW?TF{uQao>en0Rg}j`2*4Is8%1bL~S0o)6zI z&2#RrOgyhZ4#e}5u*~xUc3}Jq*39!tc$nw8kl6A(@4loXo~KIj+&+hRPNizb;&b%# z-14A-=PRa5^IW{Z#PgnMg6FNU%=21yXzA-<%{=dehk0HJ$t}-weBX|EE|=nY;4g{i z0;*<==ji7-^#KFVWABsZxw^u{vmH5b|8HuD=L1;a{vWKF=R_oBp7%k5%kw<=qK5b^u8;vszJomg)n&+YiOgz_5 z5cC^Rb6c<@pG`65R3g z!8y*P;H_~Xp!1ycgs?XJgi!xeYJa?t>2un=l?Q#s2ZXlLD;2HOf(L~2PCOrccrx)^ zU1;RlhR0H#fX8VIJt4%CsmdLWigBov%dw_k-@L$0?aCWivnc+?cvscL6pNDcfx|zD z5>(bYDqiGGZB|ExUo3U;O;`MFU47&6*XFwsf8%^t<8L=#(r`Q#`@!(4=~r5mH}F&x z&;Rx-{p0lNRo_PoHyojn9<2m|yF1p>o^Wu@xJU8BJ;#oxw z#B&ZT^L#Zs@ct*PnP)FN%ySAPxIE8gtsU{)hWAs?Tt7cMfq1TU$9O&`o{ualH1PaZ zzBJECcbj;gjU0&Q`(T;pLU!Q&Pgpb0bKzm0y^!GYJiqvLM?AMk@w|OJ@!T>Z#`8JB zbL}_-&xiA*dCn~|@w@^#5YJD-GS3Uxf%iXQ%{;G!hk2e02`if=hnuKcwQ&P^JO;?&$(317|+qqbCP1< zd3dHY&x<@Jp4&16&rPt*^KN$F{ZCjk&&S|lp0`1Q%kzBoKRV*MMvCV#KPH}wshTmK zqo3#88x1^{TuVH=wIj-SulCLv%@0%S7CcwuRYJ@UlZG5fZ}f#_p5xhp_dj9HJg39M zJRci(D$kwI&sF#lc-C_QrfdEkjNGPDKu^e_@6GWe4;xteM{#@Cf}22`$g>(rsaWYgQNrAWjOwm?e^D^jyTh z6QlV&w_nTp_p%tj=LEmC=>~oer%3agn`7d41#)2h3oG<5JD`7I&HS!}N9bQjXnB5j zzZ>DVT83Z80M@@$t(bm@e*Iv%#=!5OOQiWNzuv@e@M6L5URa@j*#Z3vYv%VLJVO6M zLd)~pr#`~(G8uly^k@A`)r#>O{ru*p8Tc(pmgcv1kcrxy;`AvsM z=wC=^d43DuiSWBfhTnx(vHtB7!f%xfzuT{1{o5WJ5?d1l34__$FZ|=1wepes|*1xbq|FQ%6 z7uL-0N_d3+g@l&pclX8!zvVLgI)1?Vm#P)h57Dn5EU5;52PH}KTYim+-=IVAyBAjI zUv@zM!kYO#2#?UekkIn{_IWeHZ^jcReo)%!_`{e>S^rYCV*Exwzqu&}eoGRi`K?Ve z@tcMmSpUKb{mTyMUsyB0>F@~s3kfaHZ{ceZeyj0rLkB~^r;eX3yoB{{LX6*YLO+yW zY~XkGdD8q^t}^jE6*;i}g%$dj9nin9W`1YDBlIsMv^>8{*M|8mws!#UY2V-3p3M5U zdyL<6g5TOc27V96OY@t1g^Ax4$bt1QtkA#gfc}Lw^Scrrp?@Kv<@w$HYM9>|{x;g_ zA0eL_e;pUG{-tWg^h5OP2TN}Qzk}kW`7KX1@f)-We)qx({mTyMUsyB02jLO=7ZO^Y z-#&ki@Vi1rKaA)TevFBZ*h{0epr~q`qvuc_ngoV% z)7aSWF8uRx!Sen;``rc2zpW3&hj<$lZ74p(JI;y^@s6|ML%iB9mt(CD-{Ey>K75Dw z{h2lOwjnco@k2>lBQ zEzj@L--Y=to-L(+yRiN}9OL(#;J4Ol;P>#qrTNWGH1WFvIk5hP75bMQ(7&)|epkXH z^e-f|Jioi2iSS!2!>_|a{AN(KV)`Na^@F9|e&YKygAPgaTYjF2-{4n*-@ULx|FQ%6 z7uL-0L3o7zg@l&px6jiNey7UtJLZ^`e*Z?*it!u${N^4t@LTdPX?|OS=`j;Khzp!S0XTT%$FC?@)ze|@y_?;lb@Ag*WwokC7(+3TYFUS8+!hhh8$S`!V3M%4(MN4Gr#HZ2>lBQeKNnDKYuHH zjQPdfXSUPLU2=sRethY6DZZb|&)=3FB5v0;61SZ^e^a!-)81};#??Mb&&zC6{hzjt z((~22x&L@K-vy)eemzEN{dy>` zB_XX#+o||B+sEj(zN0l;-#l#>t$OvW{vIu>e;yuEVb!f?Vfy?mYo4}StoF-MwSGDO zeP6CtC5oU-0>8Ao0=FkoMoQpk6br0wy#T-vxP&HNBj!s$R|`o4b_aN{zR?c@b*U#LwTd zoH!J`P3Y3|a*n#RyrD;%_bJ-W;f}S_`yQV>T$|pHp1)OnWk2=#+v?_OA;f7t>33v1^0AUs0)+h;!Wi9se&!c*pPd=M zOFw7*yDP@;IiVk__Me8|)jOs6wHz|>I~6&w{)H9#mmSc*ux5T|z$5f8B(yxgOUs#G z^fAti-`$_G{@oGd_nhFjzVS5t9&V84H}_vAepes|*1xbq|FQ%67uL-0N_d3+g@itt z-?iwotNyo>wcs6-l!?kDypZ2gp+ zoesYvG@9@m@?$h%HjO6SO`{2;X*3~+MiZPgn&8MsFS~DkRj+=^>v)?Pef@ok|1B$W z20&F=1M;+wXkDR_6y%<#?G3GGbyupg5?tDcw2oYCJ@Tt;y*k@p$@#vd6q&dJV=<60 zt&0-4qboAc>drZEv7xfE5|qFw8|8_#ah!%Kgq^x^TB;59ju-p*-?B&c<9#S8pN;Ql zVOqpI48KGM5Ux7j-X5Is54&aZ z90@UQn}O}%F69TX%$`Z;@!;`x?B`gMYpcq0#&3zc78@)<>o;HzllCVw zLvB66srtXQmg2~L&iLG_QcGs7s*iKp?@_Pm#Z@jX0G3H%D1js$f8e3o;M$LguRYs{ zuW91_^2~O%a%-;RiMq;m-vzGB+7XU59$Wl~Do-3v*-+;?8gpr`4i4LQy7N`&2@Ja% zxk-Kxp(ZHK3;(9hAD2w^!F=4^VfnnYzE2Jyvd4a;DQF>NpUf`}|%7?PZ3zXr8-Oozp2J&f4um=^2q#0CjD0#NO zE;qbKICVHFB||~8o@!2=9B!e;aX}l#1#Ksf3w}cH<=dS2wE!-hqR+`Zs%UevcwBHt zzGLmg9>*u=YZH6XxM1;zhVks@-j(LJ<)0>gCm;vn*A2`34rYgzo(*f}cM?3zZ#udJ zC*Myx=XXI_gx}f3Z|goee%J2d_P;g8Z&dSJv)jP${yJ%XGxnSKU4|TpUk#S|En^41 ze+O&kcR4)F?~Jd`hTl40gx_4^cQE3qO#E%#Mf@gH^W^&%|#sa$x-nEA;OsazOvWn)yvZ5}|(~q2={M{>%u!7S_Lrr!xGOHL(7r z>c#ktUVe+W8~A;0qcp!QADZ}`fE-x=!V3M%4(MN4GryDI5&HLwv*CBa^a#Ig7_JDm z?vUem?KalG8)E!MxBh+K!0-Myr1{O*W8!xia$x-nEA%frpnqY_{4R$_=wC=^`S@FR zUxeRU;&(9OsZ9KBeV6qwRWBBQqc{GxY&GzE#cR_17Vk3gyJx-NcPp&WzwChig*EfL z4<4a^A))2@jh_#ztPKYMxBA*yVpwdTeHK&Z!&UV{R=Df z?;3JI|H7L2O+gZ&e<7je`OUv4!f!e8yB6_OhTpPxSpQP>V*Exgzr}AG_9mxvYN?Pi5k7>qgeURJ~aI zjo$d%vcbUb6|1EAEq>R;@19o#zguC2{$&UBFRYp0eeekV3kfaHZ~VjvzuByR5l?0K z9gNyw{Fka1<2QQw&3MDW@7*s;^IKDA;x`#Nu>OS=`uC6Ifc}Lw^P7SsLjOWS%k!Im zTZCWCumb&ycq+qh*=wwSsd_PfqnF>}^#*>Qdr6w#mbXp(PCyQA_**wN!f!3{I~eg)CjPd*%KDe87mL5q8-H6?8~DBAd1-!& zH=6j}^PJ#!E3DAJ?127-HS@a<9-)6Bq2>9Fzd6D$y%8HMLp+t?ckrKC|5Ej0{6;Uo z8GkbHd-w08`K@`w#BVZkVEqd#^lvpepnqY_{H7p@(7%w-^8DtHj__Mf{H{ejmEpH+ z73*KBUX0)9<+u111HaEbBh7EidK14BkOS*qSfPK}0sRYW=64c2LjS&VHvBFa72$U_ z>)$`g@w@hA*1yYR{6@F_{iA{3{ZC2ro3Yl!?=s}T`WIH{Uv@zM!kYPA4v)~kkkIn+ zw@!`lo6Gta@l+=Mw!Xyrm#PGum5INt&#?Zb z>c!%3^v2(oTFI6wbZ}jq;@sxqzyLIB%t8G!nyR~nWac=GX@v3&rqqUFqXzyVvjb!sw z8kx1hnimAW#1Q7iNJb8xNrsX^@%<3xyFgT@VQ1s+lBdJ zC*pgJ!?lU{UgLYg3Cm71-;lpbn(v0^Onhe}2jaUwEc4xm9a?%ateNj&@G#$rtIme+ zDc5z#w}tqwdrFS)1xtwU6$@f~6LV3|_v|N5!}oKQ()y#N+N3`wAP4UM!7|^2*@6Ba zteNjg@G#%$kl?D0nUyeUk(g)6QF#XQpZqAIKW1ZYreN!`6Fm3yVv3{--1>Oj0}!xJ zJAcpG#l-WT3gS67e^1oT-&6A&gZ|k6h%~<$&j^02h5lHE9Jv1n%lwwH1N}c(Gr!B> zVSZ;og3I$;H<0+vEl~C1jy?ztZnJ1k`o+e1r`i`0x2aUE*!L8o|9c883&Z>>m~qan z9pg#o0x8MmG=bbFYG7I|baPs;>M6mgr5o$ymWOfuJwwtzg=J2+vqMX7fK|1xFom2a zaOL9JIhNp!00udE@x6iCEpZ31-4c8as@tO-2>vLP>xMYa^>(DETt~rjt~u<`(rM#mFgm$M@g0kvPG*^1HCq>n#Y->YU7;A zs_w&*exs^E^{SQaj*5Yt_}QrtBc8y;3Bf z90-2=q&S8lTml`he?cnJp@db*uvEf=Bz9=*5Em@?I7;y7NRRWYY*|o%6qu{;+2mhX zEZ)G{J*6nZiX!MVJrF8l1E%iqP^H{{e-hH+H2u*IOQ*?iVTZkPc#g3l^*%jUA!Wv^_uvf%G^{ z%9i;V*aZjijNM^{d8PgWT#2D5f)3)QT{p_TcTGUXGYWj& zv8dwo&nWQdI2+vNuj^TwWAW;bJ86G+U{*WmIfP%dG)9h5X_^VJ3Ha=Js{L}Ao zk^+fe7Wc)uN^oL|y*$k4r+3RdnE6p=}VFm!y;;l{ZqCm=KHgADwRkoBn(%O`UEtFXg-h~f?!hyBv@GkSdw_a3AywNF{9@iw z%Ek8+HBc}?!wqIw1@ghPrK(m?5UNeDyhwGF^;93PBX844Ew3)Q>3$xgDTfbLD7LFP zTS~7`>u#cv5sRMObPaE=pf$_uSZ)v1P1R;4HSGo-h8bhUe6e2bub~?_?!335D&xT+ zCV<7z*IT%SXPAb*0BrCti$&Dfh9WTZm4aQ+uG24orGOE4w2T~D`Z3tUj+t@aJam!8 z(H|`U6Tmtu9;Ur2Kl1%j^$)R?`e=meLY!%5QBvvshZV=n3mpCF-iy0FuGOJn8yLjw zwF&+<+u_(dh;Q*Uzjz9%q7@vl$~}4PqYSVJO6v zxivD2F{Yojb;l^zh^o9q3(sYI2~yCPk?7-5H23LTn)`GL&3$?^&3!tA=03fO<~~ir zzPBXZ0UZiB)hgV@g%n1&gRhj*dOdT^1hz^JZN z6msrufu~n@t^^X=T!E4}O4y&Pp#)00q1uTSjETi_Q7F@n-B4pH2Pp^i5T#RzTrm_o zOQX1{YCW4Ut5^G*=i~e|^~X~1*OhilXg*^TpX+g#=#0k)0v>~*T@tJ`XLFv&6LUlN0-xiE&tt@Avnx;vKD}Ki zufANQD==Qny^WkxEL1rG8B*GmfGdttW^y%@Kq&y7h~^NO6pz{^SWzYcdL7L{-YXL+ zv-)!BR3g_4#h&03v(|?AME|!v`0q!;d={?5)x*<+$h6zZG{<&=W!2=s3FA{qBA=a-ld+fnfpe0t(sxRjhnodvjIJ0774iamk!s%g|O z`kq4Ju*U?~G(wFqdoQ-2%b#*9EOVX54lTt6>sWJLH<;XtBaX^_l#})aDc9cyzi&r- zO#Jd^xZ_l8EgiolZZwi%Gw%No&bSF-GI)a|VO>Q&o+;e4As0$)Zf)@Dd4!{#%5Iv> zHy_kDn7Ko3z0QYHJb_-fj0_C;jk@WdHnnT|ZS1;twkI$*n}YUz0dY*5_?TS9IG{;W zSs)hS>B*$2b(Jfyh0ZWiZ>g-tf`#>W(@k7fIXO@m&Ti{5o3YwXdamTr z%OHX$7gX#hRS<6;%Eq7kY<)zsH9vW>l$9SJ~Y9LyI+x4A6?|N)_b)PSRCh^>ecU@>a~98)o#V&bZ4?0<}mYW=HQTf>gPt4@0E<<{SDR>6Ap z`D8rSI=8lgmfV32{A~5T3ewix&#lHHq*K){2LdZ@^N-adolAezxd)aUuR6(tWB#k+ z=pnY|JVqN!;$0dqxb!6-^G;7Y$+}UCHntnc&;b!BI2bZ6>xV9l)4TNwXCtLU=c0wI zbw00Jm(W`9>JxQ5ipWZJ{6VOWOYF!#W8oJoiu}CNK z<^)lr=bd)s>%xUghNU{bLJq3qKT*dQcx%pl+DLWe1(*I9ePfh?gk>PvZlKv8m`k>= z&}1fxbt~E$rKb~^%Jykz23hM8UbQ|$Yr+4NM=Du|MXKb}5N}kaSDh+S5M2{s$+LkR zRLNFUat&|InNJ((YV(4kFPX?Yz3L>(EV6y29LD+|s`YG2pm91nkwvr}g%rcOts7{S zNG7j!J~@MP#uHkp+;yVAQCAk`YeMb&jxZ(z(uQ>&l__dqiq~~XaT|JX&5?> zWEn`d8`i-vkZfP6g;A{UqOnmr8oa26r65#8>(*y@)jFTng8%n$DrZlKAi8ArCS(A?^Z^*yvSN>8URmF?rs46+E2Y?jbk@NQHr)Mm0nb=+nb zHG1BuA_WEU39wYhSI7|wkhC#p4Q=BXP78?P6Je;Gk!AQ`nM$^=l*8m%>kz9%f(^6D z4>XF{KJF|gYkiDYtxISvxN&Zzj*nxJE_Gk3BO1v3DkPxLvkaCTk2=Y7B0$pSoM&h& z#~xZh?0K3Ec!agqqbbn>h3jgzuR3eUT9@#ubq%dmpp)~{cu~m9&Shz033u_((hFhN z8uD;pi8NgDP?Vu0ULZyziC*glG8G?{b|-O>=C$6RM(22rGlR?s{ySO?H7v2}bJ9B9 z=Z&y+ujXoVOem_abm3(;8%2 z17(YWsPXHN@j6v6tyQfh7}@zs)#ui$)(7jM!acgH!DAiXfE@QA$34ig5ji$`^cg{q zbz0C9n1&o&Evj{Ri>iCuRO_g=VEKa)e8yuD_-NNb0f(sn5nN4IV%;5BLdVQaAID6O zFzn5DgrCFd`ANtW<6vWO+@q2ey)ZRrEFJjRRJ{Zr9C_I3o>xalWj(HyI6d@19xr!{&5Gm-mUIOIEm!QcIo4(}@(W%~sd>F`GL;jwEhTunFf;US#zHGM^x4Mlh3 za$hFKyHx#rk3QFeD^rVOa9g)~w7Ix0e^e~=*Q4KoQ#5IU zs*kNkrbQytB9A_H1uiwjcY{ZBP4(!bYH(GHJytN_DpvJT;2}_jiXuC(u#(O@=g3RQ zbdRb(v5dA`KU6hdQuUHXkN!k8oNnuXJQ^=~^x zS_{069^u3fW9(<YOL6oN)k5`(u^;jcCl3@*KG<(16+jD5P#q#ChfmW^s!6B<;hHU_ zWR!kxIjZ5J1NsZ)4LhZVc5mflcg(!2mQv+cQ?+n@7tvZ_BmJG(f_%%$X|b#URSfMu zz6N>B;Jv)G*Jt>@nJa_~RYTXP+Ro&*R_nU>y#EX z=MzUI4ZhJ0kMIb-Opm4X=bafCy!KjOAam02fE$2OLqw<*M(`GjHy-^Ow>~i39Ou;$ zn{MktueOTL71|XwOt-#}jB2fSYYTWuG*7p_mQKA2^`|Z6CH-|e0bYFp8SZRub9w?4C+OPpD*>cguM_^P>}d^FUtE8Ke73eLZ9g{nVTgMfty zjy~+eHE6qtS7REylv^QM=d?Y@YLCbY?Q(b{vT77rHF|PBa#SXvMCdDOZA}3@1fjOo z%SnGP<QoZQWr;3g`mLGSP;|2zmO{Qs58>%0AK3s~>x4Sj6 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

0C;{*alU(*Gfp)uaC}X8PaxNK`*hQ@=(~ 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

EFS2DTZp!6+;yq$n40aX`9#_ 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%0D`XAL>!b#5#7~gI7zon?Gv^AWr3b9I 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 M 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 z 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 diff --git a/ci3/bootstrap.sh b/ci3/bootstrap.sh deleted file mode 100755 index 4d81f9f5b39..00000000000 --- a/ci3/bootstrap.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash -source $(git rev-parse --show-toplevel)/ci3/source_bootstrap - -hash=$(cache_content_hash ^ci3) - -function test_cmds { - for f in tests/*; do - echo "$hash ./ci3/$f" - done - echo "$hash ./ci3/semver test" -} - -function test { - echo_header "ci3 tests" - test_cmds | filter_test_cmds | parallelize -} - -case "$cmd" in - "") - test - ;; - *) - default_cmd_handler "$@" - ;; -esac diff --git a/ci3/bootstrap_ec2 b/ci3/bootstrap_ec2 deleted file mode 100755 index b7189462525..00000000000 --- a/ci3/bootstrap_ec2 +++ /dev/null @@ -1,479 +0,0 @@ -#!/usr/bin/env bash -# Unified bootstrap script for launching CI builds on EC2 instances. -# Supports both SSH and SSM transport modes, controlled by CI_USE_SSH. -# CI_USE_SSH=1 → SSH into instance (requires key pair) -# CI_USE_SSH=0 → SSM RunShellScript (no SSH) -# Both modes use instance profiles for AWS credentials. - -NO_CD=1 source $(git rev-parse --show-toplevel)/ci3/source -source $ci3/source_refname - -cmd=${1:?usage: $(basename "$0") ''} -arch=${ARCH:-amd64} -NO_TERMINATE=${NO_TERMINATE:-0} -use_ssh=${CI_USE_SSH:-0} -status_job_id=${JOB_ID:-bootstrap} - -# We're always "in CI" if we're running on a remote instance. -export CI=1 -# Enable build instance key features (disk logging, benchmark uploads) by default. -# Set to 0 in ci3-external.yml for external contributors who don't have SSH access. -export CI_USE_BUILD_INSTANCE_KEY=${CI_USE_BUILD_INSTANCE_KEY:-1} - -# Pre-generate a log ID and print the dashboard link. -export CI_LOG_ID=$(date +%s%3N)$((100 + RANDOM % 900)) -echo "CI booting..." | redis_setexz "$CI_LOG_ID" 300 -log_url="http://ci.aztec-labs.com/$CI_LOG_ID" -echo -e "CI Log: ${yellow}$log_url${reset}" -if [ -n "${CI_DASHBOARD:-}" ]; then - echo "CI dashboard: http://ci.aztec-labs.com/section/$CI_DASHBOARD" -fi - -# Post pending GitHub commit status so the CI log link is visible on the PR. -post_github_status pending "ci/${status_job_id}" "$log_url" "${cmd:0:140}" 2>/dev/null || true - -if [ "$arch" == "arm64" ]; then - cores=64 - export AWS_SHUTDOWN_TIME=${AWS_SHUTDOWN_TIME_ARM:-${AWS_SHUTDOWN_TIME:-60}} -else - cores=192,128,64 - if [ "${CI_FULL:-0}" -eq 1 ]; then - export AWS_SHUTDOWN_TIME=${AWS_SHUTDOWN_TIME:-75} - else - export AWS_SHUTDOWN_TIME=${AWS_SHUTDOWN_TIME:-60} - fi -fi - -# Allow override. -cores=${CPUS:-$cores} - -# Trap function to terminate our running instance when the script exits. -function cleanup { - if [ -d "${state_dir:-}" ]; then - aws_terminate_instance "$state_dir" >/dev/null 2>&1 - fi -} -# It's important that cleanup writes to /dev/null. -# We run this from parallel, which closes stdout/stderr during its cleanup. -# This causes cleanup to die silently, probably from a SIGPIPE. -trap 'cleanup >/dev/null' SIGINT SIGTERM EXIT - -# Verify that the commit exists on the remote. -current_commit=$(git rev-parse HEAD) -if [[ "$(git fetch origin --negotiate-only --negotiation-tip="$current_commit")" != *"$current_commit"* ]]; then - echo "Commit $current_commit is not pushed, exiting." - exit 1 -fi - -# Our instance_name acts as a uniqueness key for the instance: an existing instance -# with the same name is terminated first, reaping orphans left when a GA run is -# cancelled (e.g. by a new push) on the same ref. It's repo-scoped so -# aztec-packages{,-private} don't reap each other, and stable across re-runs within a -# repo so cleanup still works. Derived by aws_instance_name, shared with ci.sh's -# connect/kill helpers so the launch and connect names never drift. -instance_name=$(aws_instance_name "$REF_NAME" "$arch" "${INSTANCE_POSTFIX:-}") - -# Merge-train PRs are intentionally not cancelled per-commit (see ci3.yml concurrency), so -# several distinct commits build concurrently. Append the commit sha so each gets its own -# instance and they don't reap each other. ci.sh's connect helpers match the Name tag by -# substring, so the sha-free base from aws_instance_name still resolves these. -if [[ "$REF_NAME" == merge-train/* ]]; then - instance_name="${instance_name}_${current_commit:0:9}" -fi - -state_dir=$(mktemp -d /tmp/aws_request_instance.XXXXXX) - -if semver check "$REF_NAME"; then - # Override the public key that aws will load into ~/.ssh/authorized_keys on the launched instance. - # This requires the restricted key only available in release environments. - key_name="super-build-instance" -else - key_name="build-instance" -fi - -docker_hostname=$(echo -n "$instance_name" | tr '_' '-' | cut -c 1-63) - -if [ "$use_ssh" -eq 1 ]; then - echo_header "request build instance (SSH)" -else - echo_header "request build instance (SSM)" -fi - -# Terminate any existing instances with the same name. -existing_instance=$(aws ec2 describe-instances \ - --region us-east-2 \ - --filters "Name=tag:Name,Values=$instance_name" "Name=instance-state-name,Values=running" \ - --query "Reservations[].Instances[?State.Name!='terminated'].InstanceId[]" \ - --output text) -if [ -n "$existing_instance" ]; then - for i in $existing_instance; do - echo "Terminating existing instance: $i" - aws ec2 --region us-east-2 terminate-instances --instance-ids "$i" >/dev/null 2>&1 - done -fi - -# Request new instance. -# SSH mode needs a key pair; SSM mode uses instance profile (no key pair). -if [ "$use_ssh" -eq 1 ]; then - KEY_NAME=$key_name aws_request_instance "$instance_name" "$cores" "$arch" "$state_dir" -else - KEY_NAME= aws_request_instance "$instance_name" "$cores" "$arch" "$state_dir" -fi -ip=$(cat "$state_dir/ip") -sir="" -if [[ -f "$state_dir/sir" ]]; then - sir=$(cat "$state_dir/sir") -fi -iid=$(cat "$state_dir/iid" | tr -d '\n\r' | xargs) -export EC2_INSTANCE_TYPE=$(cat "$state_dir/instance_type" 2>/dev/null || echo "unknown") -export EC2_SPOT=$(cat "$state_dir/spot" 2>/dev/null || echo "unknown") - -# --- Credential encoding --- -# Encode GCP and network env files as base64 for transfer to the instance. -export GOOGLE_APPLICATION_CREDENTIALS=${GOOGLE_APPLICATION_CREDENTIALS:-/tmp/gcp-key.json} -export NETWORK_ENV_FILE=${NETWORK_ENV_FILE:-/tmp/network.env} -if [ -f "${GOOGLE_APPLICATION_CREDENTIALS}" ]; then - export GCP_PRIVATE_NPM_DEPLOY_B64=$(cat "${GOOGLE_APPLICATION_CREDENTIALS}" | base64 -w 0) -fi -if [ -f "${NETWORK_ENV_FILE}" ]; then - export NETWORK_ENV_FILE_B64=$(cat "${NETWORK_ENV_FILE}" | base64 -w 0) -fi - -# SSH-specific: encode SSH keys for transfer to the instance. -# AWS credentials are handled by instance profiles on all paths. -if [ "$use_ssh" -eq 1 ]; then - # Only pass SSH keys if disk logging is enabled (internal CI only). - if [ "${CI_USE_BUILD_INSTANCE_KEY:-1}" -eq 1 ]; then - if [ -f ~/.ssh/build_instance_key ]; then - export BUILD_INSTANCE_KEY_B64=$(cat ~/.ssh/build_instance_key | base64 -w 0) - fi - if [ -f "$ci3/aws/build_instance_ssh_config" ]; then - export BUILD_INSTANCE_SSH_CONFIG_B64=$(cat "$ci3/aws/build_instance_ssh_config" | base64 -w 0) - fi - fi -fi - -# --- Determine run command --- -# If stdout is connected to a terminal (SSH interactive), drop into a shell on failure. -# Otherwise (CI mode, always for SSM), pipe logs through timestamps and cache. -if [ "$use_ssh" -eq 1 ] && [ -t 1 ]; then - ssh_args="-t" - if [ "$NO_TERMINATE" -eq 0 ]; then - run_cmd="run || exec zsh" - else - run_cmd="run; exec zsh" - fi -else - run_cmd="PARENT_LOG_ID=\$ci_log_id run 2>&1 | ci3/add_timestamps | DUP=1 ci3/cache_log 'CI run' \$ci_log_id" -fi - -# --- Container script --- -# This runs inside the devbox Docker container. -container_script=$( - cat </dev/null || true; sleep 30; done & - - function run { - echo "env: REF_NAME=\$REF_NAME COMMIT_HASH=\$COMMIT_HASH CURRENT_VERSION=\$CURRENT_VERSION TARGET_BRANCH=\$TARGET_BRANCH" - if semver check "\$REF_NAME"; then - echo "Performing a release because \$REF_NAME is a semver." - fi - - # Background monitors. cache_log and add_timestamps both close inherited pipeline - # fds internally, so these long-running processes won't block the outer pipeline. - mpstat 2 2>&1 | cache_log cpufile & - vmstat -w -S M 2 2>&1 | add_timestamps | cache_log memfile & - bash -c "while true; do pstree -agl; echo; echo; sleep 10; done" 2>&1 | add_timestamps | cache_log processes & - - set +e - set -x - $cmd - local code=\${PIPESTATUS[0]} - set +x - - sudo dmesg 2>&1 | cache_log 'dmesg' - - return \$code - } - export -f run - - set +e - $run_cmd - code=\${PIPESTATUS[0]} - - case \$code in - 155) ;; - 0) log_ci_run \$ci_log_id PASSED ;; - *) - log_ci_run \$ci_log_id FAILED - merge_train_failure_slack_notify \$ci_log_id - release_canary_slack_notify \$ci_log_id - ;; - esac - exit \$code -EOF -) - -# CI_SSM_MODE: 1 when using SSM, 0 when using SSH. -ci_ssm_mode=$((1 - use_ssh)) - -# --- Host script --- -# This runs on the EC2 host (either via SSH or SSM). -host_script=$( - cat </dev/null || true - -sudo sysctl fs.inotify.max_user_watches=1048576 &>/dev/null -sudo sysctl fs.inotify.max_user_instances=1048576 &>/dev/null - -# DNS caching. CI's massively parallel jobs resolve the same handful of hosts (S3, -# Docker Hub, npm, cargo, github) thousands of times. By default every lookup — from -# the devbox container and nested docker-in-docker alike — goes straight to the VPC -# resolver, which AWS caps at ~1024 pps per ENI for link-local services; over that, -# packets are silently dropped and surface as "could not resolve host". -# Route container lookups through the host's (caching) systemd-resolved instead, by -# exposing its stub on the instance's primary private IP — the one address reachable -# from both the devbox container and nested dind — and pointing containers at it via -# --dns on docker run below. priv_ip is cleared (and --dns omitted, leaving DNS -# unchanged) if any step fails, so this can only help, never break resolution. -priv_ip=\$(ip -4 route get 169.254.169.253 2>/dev/null | awk '{for(i=1;i<=NF;i++) if(\$i=="src"){print \$(i+1); exit}}' || true) -if [ -n "\$priv_ip" ] && systemctl is-active --quiet systemd-resolved \ - && echo "DNSStubListenerExtra=\$priv_ip" | sudo tee -a /etc/systemd/resolved.conf >/dev/null \ - && sudo systemctl restart systemd-resolved; then - # Only route containers to the cache once it's actually listening on priv_ip. - for _ in 1 2 3 4 5; do - sudo ss -lnu "sport = :53" 2>/dev/null | grep -qF "\$priv_ip:53" && { dns_ready=1; break; } - sleep 0.5 - done - if [ "\${dns_ready:-0}" = 1 ]; then - echo "HOST: DNS cache active on \$priv_ip (systemd-resolved)." - else - echo "HOST: DNS cache failed to bind \$priv_ip; using default resolver." - priv_ip= - fi -else - echo "HOST: DNS cache not configured; using default resolver." - priv_ip= -fi - -# Pin host processes to top CPU cores to keep benchmark cores clean. -# CPU layout: physical cores 0..N/2-1, hyperthreads N/2..N-1. -# OS gets top 8 physical cores + their hyperthread siblings. -# Skip dockerd so containers it spawns can use all CPUs for build/test. -total_cpus=\$(nproc) -total_physical=\$((total_cpus / 2)) -os_start=\$((total_physical - 8)) -os_ht_start=\$((total_cpus - 8)) -os_cpu_list="\$os_start-\$((total_physical - 1)),\$os_ht_start-\$((total_cpus - 1))" -for pid in \$(ps -eo pid= 2>/dev/null); do - comm=\$(cat /proc/\$pid/comm 2>/dev/null) || continue - [ "\$comm" = "dockerd" ] && continue - sudo taskset -apc "\$os_cpu_list" \$pid &>/dev/null || true -done -echo "Host processes pinned to OS CPUs: \$os_cpu_list" - -echo "HOST: fetching EC2 metadata token..." -aws_token=\$(curl -sX PUT http://169.254.169.254/latest/api/token -H 'X-aws-ec2-metadata-token-ttl-seconds: 21600') -echo "HOST: metadata token acquired." - -GOOGLE_APPLICATION_CREDENTIALS=\${GOOGLE_APPLICATION_CREDENTIALS:-/tmp/gcp-key.json} -NETWORK_ENV_FILE=\${NETWORK_ENV_FILE:-/tmp/network.env} - -# Decode GCP service account key if provided (base64-encoded by the caller). -echo "HOST: decoding credentials..." -if [ -n "${GCP_PRIVATE_NPM_DEPLOY_B64:-}" ]; then - echo "${GCP_PRIVATE_NPM_DEPLOY_B64:-}" | base64 -d > "\${GOOGLE_APPLICATION_CREDENTIALS}" -fi - -# Decode network env file if provided. -if [ -n "${NETWORK_ENV_FILE_B64:-}" ]; then - echo "${NETWORK_ENV_FILE_B64:-}" | base64 -d > "\${NETWORK_ENV_FILE}" -fi - -# Decode SSH keys if provided (SSH mode only, empty in SSM mode). -if [ -n "${BUILD_INSTANCE_KEY_B64:-}" ]; then - mkdir -p \$HOME/.ssh - echo "${BUILD_INSTANCE_KEY_B64:-}" | base64 -d > \$HOME/.ssh/build_instance_key - chmod 600 \$HOME/.ssh/build_instance_key -fi -if [ -n "${BUILD_INSTANCE_SSH_CONFIG_B64:-}" ]; then - mkdir -p \$HOME/.ssh - echo "${BUILD_INSTANCE_SSH_CONFIG_B64:-}" | base64 -d > \$HOME/.ssh/build_instance_ssh_config -fi - -start_build() { - echo "HOST: preparing devbox (uid/gid, docker run)..." - local_uid=\$(id -u) - local_gid=\$(id -g) - - docker run --privileged --rm \${docker_args:-} \${priv_ip:+--dns \$priv_ip} \ - --name aztec_build \ - --hostname $docker_hostname \ - -v bootstrap_ci_local_docker:/var/lib/docker \ - -v bootstrap_ci_repo:/home/aztec-dev/aztec-packages \ - -v \$HOME/.ssh:/home/aztec-dev/.ssh:ro \ - -v \$HOME/.bb-crs:/home/aztec-dev/.bb-crs \ - -v /dev/kmsg:/dev/kmsg \ - -v /tmp:/tmp \ - -e GOOGLE_APPLICATION_CREDENTIALS="\${GOOGLE_APPLICATION_CREDENTIALS}" \ - -e NETWORK_ENV_FILE="\${NETWORK_ENV_FILE}" \ - -e CI=1 \ - -e CI_SSM_MODE=$ci_ssm_mode \ - -e CI_LOG_ID=${CI_LOG_ID:-} \ - -e CI_LOGS_S3_LOCATION=${CI_LOGS_S3_LOCATION:-s3://aztec-ci-artifacts/logs} \ - -e RUN_ID=${RUN_ID:-} \ - -e JOB_ID=${JOB_ID:-} \ - -e REF_NAME=${REF_NAME:-} \ - -e TARGET_BRANCH=${TARGET_BRANCH:-} \ - -e CI_DASHBOARD=${CI_DASHBOARD:-} \ - -e PARENT_LOG_ID=${PARENT_LOG_ID:-} \ - -e NO_CACHE=${NO_CACHE:-} \ - -e NO_FAIL_FAST=${NO_FAIL_FAST:-} \ - -e CI_USE_BUILD_INSTANCE_KEY=${CI_USE_BUILD_INSTANCE_KEY:-1} \ - -e CI_REDIS='ci-redis-tiered.lzka0i.ng.0001.use2.cache.amazonaws.com' \ - -e SSH_CONNECTION=' ' \ - -e LOCAL_USER_ID=\$local_uid \ - -e LOCAL_GROUP_ID=\$local_gid \ - -e GCP_PROJECT_ID=${GCP_PROJECT_ID:-} \ - -e DOCKERHUB_PASSWORD=${DOCKERHUB_PASSWORD:-} \ - -e DOCKERHUB_USERNAME=${DOCKERHUB_USERNAME:-} \ - -e R2_ACCESS_KEY_ID=${R2_ACCESS_KEY_ID:-} \ - -e R2_SECRET_ACCESS_KEY=${R2_SECRET_ACCESS_KEY:-} \ - -e BUILD_SYSTEM_DEBUG=${BUILD_SYSTEM_DEBUG:-} \ - -e GITHUB_TOKEN=${GITHUB_TOKEN:-} \ - -e GITHUB_REPOSITORY=${GITHUB_REPOSITORY:-aztecprotocol/aztec-packages} \ - -e PR_NUMBER=${PR_NUMBER:-} \ - -e PR_HEAD_REF=${PR_HEAD_REF:-} \ - -e PR_BASE_REF=${PR_BASE_REF:-} \ - -e CHONK_INPUTS_STATE_DIR=${CHONK_INPUTS_STATE_DIR:-} \ - -e NETLIFY_SITE_ID=${NETLIFY_SITE_ID:-} \ - -e NETLIFY_AUTH_TOKEN=${NETLIFY_AUTH_TOKEN:-} \ - -e CARGO_REGISTRY_TOKEN=${CARGO_REGISTRY_TOKEN:-} \ - -e SLACK_BOT_TOKEN=${SLACK_BOT_TOKEN:-} \ - -e AZTEC_FOUNDATION_CI_SLACK_BOT_TOKEN=${AZTEC_FOUNDATION_CI_SLACK_BOT_TOKEN:-} \ - -e SOCKET_SECURITY_API_TOKEN=${SOCKET_SECURITY_API_TOKEN:-} \ - -e AWS_TOKEN=\$aws_token \ - -e NAMESPACE=${NAMESPACE:-} \ - -e NETWORK=${NETWORK:-} \ - -e GITHUB_ACTOR=${GITHUB_ACTOR:-} \ - -e EC2_INSTANCE_TYPE=${EC2_INSTANCE_TYPE:-unknown} \ - -e EC2_SPOT=${EC2_SPOT:-unknown} \ - -e BENCH_UPLOAD=${BENCH_UPLOAD:-} \ - -e AZTEC_TOOLCHAIN_DEFAULT_MAJOR_VERSION=${AZTEC_TOOLCHAIN_DEFAULT_MAJOR_VERSION:-} \ - -e DRY_RUN=${DRY_RUN:-} \ - -e PRIVATE_RELEASE=${PRIVATE_RELEASE:-} \ - -e INTERNAL_DOCKER_REGISTRY=${INTERNAL_DOCKER_REGISTRY:-} \ - -e SKIP_COMPAT_E2E=${SKIP_COMPAT_E2E:-} \ - --pids-limit=65536 \ - --shm-size=2g \ - --ulimit nofile=1048576:1048576 \ - aztecprotocol/devbox:3.2 bash -c $(printf '%q' "$container_script") -} - -# If stdout is a tty, run in foreground, otherwise run in background and handle spot termination notices. -if [ -t 1 ]; then - docker_args+=' -ti' - start_build -else - echo "HOST: starting devbox container..." - start_build & - build_pid=\$! - echo "HOST: devbox container launched (pid=\$build_pid). Monitoring for spot termination..." - - # While the docker container is running, check for spot termination notices. - while kill -0 \$build_pid &>/dev/null; do - # The check for the file allows for testing spot termination logic. - if [ -f /tmp/spot_term ] || curl -fs -H 'X-aws-ec2-metadata-token: '\$aws_token http://169.254.169.254/latest/meta-data/spot/termination-time &>/dev/null; then - echo 'Spot will be terminated! Exiting early.' - docker kill aztec_build &>/dev/null || true - exit 155 - fi - sleep 5 - done - - # Returns exit code from docker run. - wait \$build_pid -fi -EOF -) - -# --- Transport: send host_script to the instance --- -if [ "$use_ssh" -eq 1 ]; then - echo_header "connect via SSH" - function run_ssh { - ssh ${ssh_args:-} -F $ci3/aws/build_instance_ssh_config ubuntu@$ip "$host_script" - } - - set +e - # If in terminal run in foreground. - # If not, run in background so we can handle the signals in a timely fashion, and wait for it to finish. - if [ -t 1 ]; then - run_ssh - else - echo "Stdout is not a tty, running in background..." - run_ssh & - wait $! - fi - exit_code=$? - set -e - echo "SSH exited with code: $exit_code" -else - echo_header "invoke SSM command" - # Send host script via SSM and poll until completion. - # Run as ubuntu to match SSH behavior (SSM defaults to root). - set +e - SSM_RUN_AS=ubuntu SSM_POLL_TIMEOUT=${SSM_POLL_TIMEOUT:-$((AWS_SHUTDOWN_TIME * 60 + 600))} \ - ssm_send_command "$iid" "$host_script" - exit_code=$? - set -e -fi - -# If we were spot evicted, try again using on-demand. -if [ "$exit_code" -eq 155 ]; then - echo "Spot was evicted. Retrying with on-demand instance." - NO_SPOT=1 exec "$0" "$@" -else - # Print CI log URL at the end for easy access (avoid scrolling up). - echo -e "CI Log: ${yellow}$log_url${reset}" - # Post final GitHub commit status. - if [ "$exit_code" -eq 0 ]; then - post_github_status success "ci/${status_job_id}" "$log_url" "${cmd:0:140}" 2>/dev/null || true - else - post_github_status failure "ci/${status_job_id}" "$log_url" "${cmd:0:140}" 2>/dev/null || true - fi - exit "$exit_code" -fi diff --git a/ci3/cache_content_hash b/ci3/cache_content_hash deleted file mode 100755 index 89e49e0f60c..00000000000 --- a/ci3/cache_content_hash +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env bash -# Calculate the content hash for an artifact that controls rebuilds. -# Takes a list of rebuild pattern files (regex files that match patterns from repo root) or directories. -# The REPO_PATH env var can be used to point the script at the root of the git repository where the patterns -# are rooted, and where the git commands will be executed, which can be used to compute the hash in a sub repo. -# Two special test-only environment variables can control the cache. -# These are used when iterating on build infrastructure: -# - AZTEC_CACHE_NO_SCRIPTS: Don't include any shell files (namely bootstrap.sh) in cache patterns. -# - AZTEC_CACHE_COMMIT: Compute the content hash for artifacts from an earlier commit. -NO_CD=1 source ${root:-$(git rev-parse --show-toplevel)}/ci3/source - -if [[ "$#" = 0 ]]; then - echo "Error: No arguments provided." - exit 1 -fi - -AZTEC_CACHE_COMMIT=${AZTEC_CACHE_COMMIT:-HEAD} -PLATFORM_TAG="${PLATFORM_TAG:-${OSTYPE:-unknown}-$(uname -m)}" - -rebuild_patterns=() -for arg in "$@"; do - if [[ -f "$arg" ]]; then - # If the file is a rebuild patterns file, read it and add its contents to the patterns. - rebuild_patterns+=$(cat "$arg") - rebuild_patterns+=$'\n' - elif [[ "$arg" == ^* ]]; then - # Add as pattern literally - rebuild_patterns+="$arg"$'\n' - else - # Ensure the folder exists, and add folder as pattern relative to repo root - if [[ ! -d "$arg" ]]; then - echo_stderr "Error: Directory '$arg' does not exist." - exit 1 - fi - arg=$(realpath --relative-to="$(git rev-parse --show-toplevel)" "$arg") - rebuild_patterns+="$arg"$'\n' - fi -done - -GREP_PATTERN=$(echo "$rebuild_patterns" | grep -v '^$') -# Concatenate patterns with '|' and double escape backslashes for AWK filter empty lines -AWK_PATTERN=$(echo "$rebuild_patterns" | grep -v '^$' | sed 's/\\/\\\\/g' | tr '\n' '|' | sed 's/|$//') - -# use git repo root because that is where our patterns are focused -cd "${REPO_PATH:-$(git rev-parse --show-toplevel)}" - -diff="$({ - git diff --name-only - git diff --staged --name-only - git ls-files --others --exclude-standard -} | grep -E "$GREP_PATTERN" || true)" - -# Check for uncommitted files/changes. -# Only use this check if AZTEC_CACHE_COMMIT is the default, otherwise the user is being explicit about using git history. -if [ -n "$diff" ] && [ "$AZTEC_CACHE_COMMIT" == "HEAD" ]; then - # In CI we shouldn't be changing files during the run, so a 'disabled-cache' content hash here - # means something is wrong — fail loudly instead of silently disabling the cache. - if [ "$CI" -eq 1 ]; then - echo_stderr "ERROR: Noticed changes to rebuild patterns during CI run: $diff" - exit 1 - fi - # Signal to cache_upload and cache_download to not touch this file. - echo "disabled-cache" - exit 0 -fi - -# Calculate a content hash for matched files -# Use git ls-tree and AWK to filter files matching the rebuild patterns and extract their hashes -# Sort the hashes and compute the content hash -CONTENT_HASH="$( - git ls-tree -r "$AZTEC_CACHE_COMMIT" \ - | awk -v pattern="($AWK_PATTERN)" '$4 ~ pattern {print $3}' \ - | sort \ - | git hash-object --stdin \ - | cut -c1-16 -)" - -# Check if file list was empty by comparing against the result of -# 'echo '' | git hash-object --stdin | cut -c1-16' -EMPTY_HASH="8b137891791fe969" -if [[ "$CONTENT_HASH" == "$EMPTY_HASH" ]]; then - echo "No files matched the rebuild patterns $rebuild_patterns." - echo "Awk pattern expanded: $AWK_PATTERN." - exit 1 -fi - -# important: include architecture in content hash because we target x86_64 and arm64 -echo $(hash_str $CONTENT_HASH-$PLATFORM_TAG) diff --git a/ci3/cache_delete b/ci3/cache_delete deleted file mode 100755 index 8d326513dad..00000000000 --- a/ci3/cache_delete +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash -[ -n "${BUILD_SYSTEM_DEBUG:-}" ] && set -x -set -eu - -# Ensure at least one argument is provided -if [ "$#" -lt 1 ]; then - echo "Usage: $0 " >&2 - exit 1 -fi - -# Name of the artifact to delete -name="$1" - -s3_path="s3://aztec-ci-artifacts/build-cache/$name" -if aws ${S3_BUILD_CACHE_AWS_PARAMS:-} s3 ls "$s3_path" >/dev/null 2>&1; then - aws ${S3_BUILD_CACHE_AWS_PARAMS:-} s3 rm "$s3_path" >&2 -else - echo "File not found, skipping delete: $name" >&2 -fi diff --git a/ci3/cache_download b/ci3/cache_download deleted file mode 100755 index d3cca549feb..00000000000 --- a/ci3/cache_download +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env bash -# DO NOT output to stdout. This script is used in contexts where stdout is processed. -NO_CD=1 source ${root:-$(git rev-parse --show-toplevel)}/ci3/source - -if [ "$#" -lt 1 ]; then - echo_stderr "Usage: $0 " - exit 1 -fi - -if [[ "$1" == *"disabled-cache"* ]]; then - echo_stderr "Not using cache for $1 due to uncommitted changes/files." - exit 1 -fi -if [ "${NO_CACHE:-0}" -eq 1 ]; then - # Skip download if NO_CACHE is 1 - echo_stderr "Not using cache for $1 because NO_CACHE=1." - exit 1 -fi -# Get the tar.gz file name from the argument -tar_file="$1" -out_dir="${2:-.}" - -function extract_tar { - if [[ "$tar_file" == *.zst ]]; then - tar --use-compress-program="zstd -d" -x -C "$out_dir" &>/dev/null - else - tar -xzf - -C "$out_dir" &>/dev/null - fi -} - -mkdir -p "$out_dir" - -if [ "$CI" -eq 1 ]; then - endpoint="http://aztec-ci-artifacts.s3.amazonaws.com/build-cache" -else - endpoint="https://build-cache.aztec-labs.com" -fi - -# Downloads the artifact from remote to stdout. -function download_from_remote { - if [[ -n "${S3_BUILD_CACHE_AWS_PARAMS:-}" ]]; then - # Use AWS CLI with custom params (e.g., custom endpoint) - # NOTE: This is NOT currently used, but allows for using minio or other S3-compatible storage for tests. - s3_uri="s3://aztec-ci-artifacts/build-cache/$tar_file" - aws $S3_BUILD_CACHE_AWS_PARAMS s3 cp "$s3_uri" "-" 2>/dev/null - elif [[ -n "${CACHE_SSH_HOST:-}" ]]; then - # Run remote download on remote host via SSH jump and pipe back - ssh "$CACHE_SSH_HOST" "curl -s -f \"$endpoint/$tar_file\"" - else - # Default to download from remote via curl - curl -s -f "$endpoint/$tar_file" - fi -} - -# Local cache: if CACHE_LOCAL_DIR is set, check local cache first, -# and on miss, download from remote into local cache before extracting. -# If the cache directory cannot be created, skip local caching and fall through. -if [[ -n "${CACHE_LOCAL_DIR:-}" ]] && ! mkdir -p "$CACHE_LOCAL_DIR" 2>/dev/null; then - echo_stderr "Warning: Cannot create local cache dir $CACHE_LOCAL_DIR, skipping local cache." - CACHE_LOCAL_DIR="" -fi - -if [[ -n "${CACHE_LOCAL_DIR:-}" ]]; then - local_cache_file="$CACHE_LOCAL_DIR/$tar_file" - - if [[ -f "$local_cache_file" ]]; then - echo_stderr "Local cache hit for $tar_file." - extract_tar < "$local_cache_file" - echo_stderr "Cache extraction of $tar_file from local cache complete in ${SECONDS}s." - exit 0 - fi - - echo_stderr "Local cache miss for $tar_file, downloading from remote." - if ! download_from_remote > "$local_cache_file"; then - rm -f "$local_cache_file" - echo_stderr "Cache download of $tar_file failed." - exit 1 - fi - - extract_tar < "$local_cache_file" - echo_stderr "Cache download and extraction of $tar_file complete in ${SECONDS}s." - exit 0 -fi - -if ! download_from_remote | extract_tar; then - echo_stderr "Cache download of $tar_file failed." - exit 1 -fi - -echo_stderr "Cache download and extraction of $tar_file complete in ${SECONDS}s." diff --git a/ci3/cache_exists b/ci3/cache_exists deleted file mode 100755 index a2fce6ae470..00000000000 --- a/ci3/cache_exists +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash -# Check if a cache artifact exists in S3 without downloading it. -# DO NOT output to stdout. This script is used in contexts where stdout is processed. -# Exit 0 if exists, exit 1 if not found or cache disabled. - -NO_CD=1 source ${root:-$(git rev-parse --show-toplevel)}/ci3/source - -if [ "$#" -lt 1 ]; then - echo_stderr "Usage: $0 " - exit 1 -fi - -if [[ "$1" == *"disabled-cache"* ]]; then - exit 1 -fi - -if [ "${NO_CACHE:-0}" -eq 1 ]; then - exit 1 -fi - -tar_file="$1" - -# Check if artifact exists in S3 -if [[ -n "${S3_BUILD_CACHE_AWS_PARAMS:-}" ]]; then - # Use AWS CLI to check object existence - if aws $S3_BUILD_CACHE_AWS_PARAMS s3api head-object \ - --bucket aztec-ci-artifacts \ - --key "build-cache/$tar_file" &>/dev/null; then - exit 0 - else - exit 1 - fi -else - # Use HTTP HEAD request to check existence - S3_ENDPOINT="http://aztec-ci-artifacts.s3.amazonaws.com" - if curl -s -I -f "$S3_ENDPOINT/build-cache/$tar_file" &>/dev/null; then - exit 0 - else - exit 1 - fi -fi diff --git a/ci3/cache_load_image b/ci3/cache_load_image deleted file mode 100755 index 690e1397bdf..00000000000 --- a/ci3/cache_load_image +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash -NO_CD=1 source ${root:-$(git rev-parse --show-toplevel)}/ci3/source - -image=$1 -image_file=${image//[^[:alnum:]]/_}-$(arch) - -if ! cache_download $image_file.zst; then - docker pull $image - docker save $image > $image_file - cache_upload $image_file.zst $image_file - rm $image_file -else - docker load < $image_file - rm $image_file -fi diff --git a/ci3/cache_local.test.sh b/ci3/cache_local.test.sh deleted file mode 100755 index ce919ffe676..00000000000 --- a/ci3/cache_local.test.sh +++ /dev/null @@ -1,230 +0,0 @@ -#!/usr/bin/env bash -# Test script for local cache functionality in cache_download and cache_upload. -set -euo pipefail - -script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -test_root="/tmp/cache-local-test-$$" -passed=0 -failed=0 - -cleanup() { - rm -rf "$test_root" -} -trap cleanup EXIT - -log() { echo -e "\033[1m$1\033[0m"; } -pass() { echo -e " \033[32m✓ $1\033[0m"; ((++passed)); } -fail() { echo -e " \033[31m✗ $1\033[0m"; ((++failed)); } - -setup() { - log "Setting up test environment in $test_root" - mkdir -p "$test_root"/{local-cache,extract,source} - - # Create some test content to tar up. - echo "hello world" > "$test_root/source/file1.txt" - echo "foo bar" > "$test_root/source/file2.txt" - - # Create a test tar.gz from that content. - tar -czf "$test_root/test-artifact.tar.gz" -C "$test_root/source" . -} - -test_download_without_local_cache() { - log "\nTest 1: cache_download without CACHE_LOCAL_DIR (baseline)" - - # Without CACHE_LOCAL_DIR, and without S3 access, cache_download should fail. - # This just confirms that the env var being unset means no local cache logic runs. - unset CACHE_LOCAL_DIR 2>/dev/null || true - if NO_CACHE=1 "$script_dir/cache_download" "some-file.tar.gz" "$test_root/extract" 2>/dev/null; then - fail "Should have exited with error when NO_CACHE=1" - else - pass "cache_download respects NO_CACHE=1 without local cache" - fi -} - -test_download_local_cache_hit() { - log "\nTest 2: cache_download with local cache hit" - - export CACHE_LOCAL_DIR="$test_root/local-cache" - rm -rf "$test_root/extract" - mkdir -p "$test_root/extract" - - # Place the artifact in the local cache. - cp "$test_root/test-artifact.tar.gz" "$CACHE_LOCAL_DIR/test-artifact.tar.gz" - - # cache_download should find it in local cache and extract. - if "$script_dir/cache_download" "test-artifact.tar.gz" "$test_root/extract" 2>/dev/null; then - if [[ -f "$test_root/extract/file1.txt" ]] && [[ -f "$test_root/extract/file2.txt" ]]; then - pass "Local cache hit extracted files correctly" - else - fail "Local cache hit did not extract files" - fi - else - fail "cache_download failed on local cache hit" - fi - - # Verify stderr mentions local cache hit. - local stderr_output - rm -rf "$test_root/extract" - mkdir -p "$test_root/extract" - stderr_output=$("$script_dir/cache_download" "test-artifact.tar.gz" "$test_root/extract" 2>&1 >/dev/null) || true - if echo "$stderr_output" | grep -q "Local cache hit"; then - pass "Reported local cache hit in stderr" - else - fail "Did not report local cache hit (got: $stderr_output)" - fi -} - -test_download_local_cache_miss() { - log "\nTest 3: cache_download with local cache miss (no remote)" - - export CACHE_LOCAL_DIR="$test_root/local-cache-empty" - mkdir -p "$CACHE_LOCAL_DIR" - rm -rf "$test_root/extract" - mkdir -p "$test_root/extract" - - # With an empty local cache and no S3 access, download should fail. - local stderr_output - stderr_output=$("$script_dir/cache_download" "nonexistent.tar.gz" "$test_root/extract" 2>&1 >/dev/null) || true - if echo "$stderr_output" | grep -q "Local cache miss"; then - pass "Reported local cache miss in stderr" - else - fail "Did not report local cache miss (got: $stderr_output)" - fi -} - -test_upload_saves_to_local_cache() { - log "\nTest 4: cache_upload saves to local cache" - - export CACHE_LOCAL_DIR="$test_root/local-cache-upload" - mkdir -p "$CACHE_LOCAL_DIR" - - # cache_upload requires CI=1 or S3_FORCE_UPLOAD, and AWS credentials. - # We set S3_FORCE_UPLOAD but expect S3 upload to fail (no credentials) - that's OK, - # we just want to verify the local cache copy happens. - local stderr_output - stderr_output=$(S3_FORCE_UPLOAD=1 "$script_dir/cache_upload" "test-upload.tar.gz" "$test_root/source/file1.txt" "$test_root/source/file2.txt" 2>&1 >/dev/null) || true - - if [[ -f "$CACHE_LOCAL_DIR/test-upload.tar.gz" ]]; then - pass "cache_upload saved artifact to local cache" - else - fail "cache_upload did not save artifact to local cache" - fi - - if echo "$stderr_output" | grep -q "Saved test-upload.tar.gz to local cache"; then - pass "Reported saving to local cache in stderr" - else - fail "Did not report saving to local cache (got: $stderr_output)" - fi -} - -test_upload_without_local_cache() { - log "\nTest 5: cache_upload without CACHE_LOCAL_DIR" - - unset CACHE_LOCAL_DIR 2>/dev/null || true - - # Without CACHE_LOCAL_DIR, upload should not create any local cache files. - local stderr_output - stderr_output=$(S3_FORCE_UPLOAD=1 "$script_dir/cache_upload" "test-no-local.tar.gz" "$test_root/source/file1.txt" 2>&1 >/dev/null) || true - - if echo "$stderr_output" | grep -q "local cache"; then - fail "Should not mention local cache when CACHE_LOCAL_DIR is unset" - else - pass "No local cache activity when CACHE_LOCAL_DIR is unset" - fi -} - -test_roundtrip() { - log "\nTest 6: Upload then download roundtrip via local cache" - - export CACHE_LOCAL_DIR="$test_root/local-cache-roundtrip" - mkdir -p "$CACHE_LOCAL_DIR" - - # Upload: creates the tar and saves to local cache. - S3_FORCE_UPLOAD=1 "$script_dir/cache_upload" "roundtrip.tar.gz" "$test_root/source/file1.txt" "$test_root/source/file2.txt" 2>/dev/null || true - - # Download: should find it in local cache and extract. - rm -rf "$test_root/extract" - mkdir -p "$test_root/extract" - if "$script_dir/cache_download" "roundtrip.tar.gz" "$test_root/extract" 2>/dev/null; then - if [[ -f "$test_root/extract/$test_root/source/file1.txt" ]] || [[ -f "$test_root/extract/file1.txt" ]]; then - pass "Roundtrip: upload then download via local cache works" - else - # The tar preserves full paths, so check with the full path structure. - if tar -tzf "$CACHE_LOCAL_DIR/roundtrip.tar.gz" | grep -q "file1.txt"; then - pass "Roundtrip: artifact in local cache contains expected files" - else - fail "Roundtrip: extracted files not found" - fi - fi - else - fail "Roundtrip: cache_download failed" - fi -} - -test_disabled_cache_skips_local() { - log "\nTest 7: disabled-cache key skips local cache" - - export CACHE_LOCAL_DIR="$test_root/local-cache" - - local stderr_output - stderr_output=$("$script_dir/cache_download" "disabled-cache-foo.tar.gz" "$test_root/extract" 2>&1 >/dev/null) || true - if echo "$stderr_output" | grep -q "uncommitted changes"; then - pass "disabled-cache still triggers early exit before local cache" - else - fail "disabled-cache did not trigger early exit (got: $stderr_output)" - fi -} - -test_inaccessible_cache_dir_falls_through() { - log "\nTest 8: Inaccessible CACHE_LOCAL_DIR falls through gracefully" - - # Use a path we definitely can't create (root-owned directory). - export CACHE_LOCAL_DIR="/proc/fake-cache-dir" - - local stderr_output - stderr_output=$("$script_dir/cache_download" "test-artifact.tar.gz" "$test_root/extract" 2>&1 >/dev/null) || true - if echo "$stderr_output" | grep -q "Cannot create local cache dir"; then - pass "Download warns about inaccessible cache dir" - else - fail "Download did not warn about inaccessible dir (got: $stderr_output)" - fi - # Should NOT see "Local cache hit" or "Local cache miss" since it fell through. - if echo "$stderr_output" | grep -q "Local cache"; then - fail "Download should not attempt local cache when dir is inaccessible" - else - pass "Download skipped local cache logic when dir is inaccessible" - fi - - # Test upload too. - stderr_output=$(S3_FORCE_UPLOAD=1 "$script_dir/cache_upload" "test-upload-fallthrough.tar.gz" "$test_root/source/file1.txt" 2>&1 >/dev/null) || true - if echo "$stderr_output" | grep -q "Cannot create local cache dir"; then - pass "Upload warns about inaccessible cache dir" - else - fail "Upload did not warn about inaccessible dir (got: $stderr_output)" - fi -} - -main() { - log "=== Local Cache Test Suite ===\n" - - setup - - test_download_without_local_cache - test_download_local_cache_hit - test_download_local_cache_miss - test_upload_saves_to_local_cache - test_upload_without_local_cache - test_roundtrip - test_disabled_cache_skips_local - test_inaccessible_cache_dir_falls_through - - log "\n=== Results ===" - echo -e "\033[32mPassed: $passed\033[0m" - echo -e "\033[31mFailed: $failed\033[0m" - - if [[ $failed -gt 0 ]]; then - exit 1 - fi -} - -main "$@" diff --git a/ci3/cache_log b/ci3/cache_log deleted file mode 100755 index c3ae2201440..00000000000 --- a/ci3/cache_log +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env bash -# Runs the given command. -# If a redis cache is available, redirect input into a cache log. -# If DUP=1 then we duplicate input also to stdout. -NO_CD=1 source $(git rev-parse --show-toplevel)/ci3/source - -name=$1 - -key=${2:-$(uuid)} -dup=${DUP:-0} -outfile=/tmp/$key -touch $outfile -trap 'rm -f $outfile' EXIT -if [ -n "${PARENT_LOG_ID:-}" ]; then - echo -e "Parent Log: $(ci_term_link $PARENT_LOG_ID)" >>$outfile -else - echo -e "Parent Log: none" >>$outfile -fi -echo -e "Started: $(date)" >>$outfile -echo -e "Completed: in-progress" >>$outfile -echo -e "Took: in-progress\n" >>$outfile - -function finalize_log { - # Use sed to update the header. - sed -i "3c Completed: $(date)" "$outfile" - sed -i "4c Took: ${SECONDS}s" "$outfile" -} - -function publish_log { - cat $outfile | redis_setexz $key $CI_REDIS_EXPIRE -} - -function publish_log_final { - finalize_log - # Synchronous: this is the run's top-level log and the instance goes away right after. - cat $outfile | CACHE_PERSIST_SYNC=1 cache_persistent $key $CI_REDIS_EXPIRE -} - -function live_publish_log { - while [ -f $outfile ]; do - if [ $(( $(date +%s) - $(stat -c %Y "$outfile") )) -le 5 ]; then - publish_log - fi - for i in {1..5}; do - [ ! -f $outfile ] && break - sleep 1 - done - done -} - -if [ "$CI_REDIS_AVAILABLE" -eq 1 ]; then - log_str="$name log id: ${yellow}$DASHBOARD_URL/$key${reset}" - echo_stderr -e "$log_str" - # Close inherited pipeline FDs to prevent blocking parent pipelines. - # When cache_log runs inside a pipeline, fd 1/2 may reference the parent's pipe. - # Holding them open prevents the parent pipeline from seeing EOF. - exec 2>/dev/null - if [ "$dup" -ne 1 ]; then - exec >/dev/null - fi - live_publish_log >&- 2>&- & - if [ "$dup" -eq 1 ]; then - redact | tee -a $outfile - # Print a second line if the log is long enough. - # This is to avoid having to scroll up to see the log id. - if [ "$(cat $outfile | wc -l)" -ge 16 ]; then - echo_stderr -e "$log_str" - fi - else - redact | cat >>$outfile - fi - publish_log_final -else - if [ "$dup" -eq 1 ]; then - redact | cat - else - cat >/dev/null - fi -fi diff --git a/ci3/cache_ls b/ci3/cache_ls deleted file mode 100755 index 5b027669c19..00000000000 --- a/ci3/cache_ls +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash -set -eu -o pipefail -[ "${BUILD_SYSTEM_DEBUG:-}" = 1 ] && set -x - -# List files inside a tar.gz on S3. For debugging. -if [ "$#" -lt 1 ]; then - echo "Usage: $0 " >&2 - exit 1 -fi - -# Get the tar.gz file name from the argument -TAR_FILE="$1" - -# Extract endpoint URL if S3_BUILD_CACHE_AWS_PARAMS is set -if [[ -n "${S3_BUILD_CACHE_AWS_PARAMS:-}" ]]; then - aws $S3_BUILD_CACHE_AWS_PARAMS s3 cp "s3://aztec-ci-artifacts/build-cache/$TAR_FILE" "-" | tar -tzf - -else - # Default to AWS S3 URL if no custom endpoint is set - S3_ENDPOINT="http://aztec-ci-artifacts.s3.amazonaws.com" - # Attempt to download and extract the cache file - curl -s -f "$S3_ENDPOINT/build-cache/$TAR_FILE" | tar -tzf - -fi diff --git a/ci3/cache_upload b/ci3/cache_upload deleted file mode 100755 index 21a206d7c93..00000000000 --- a/ci3/cache_upload +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env bash -# DO NOT output to stdout. This script is used in contexts where stdout is processed. -NO_CD=1 source ${root:-$(git rev-parse --show-toplevel)}/ci3/source - -if [ "$#" -lt 2 ]; then - echo_stderr "Usage: $0 " - exit 1 -fi - -if [[ "$1" == *"disabled-cache"* ]]; then - echo_stderr "Skipping upload of $1 due to uncommitted changes/files." - exit 0 -fi - -# Name, intended to have .tar.gz ending -name="$1" -# Now $@ = our binary path args -shift 1 - -if [[ -z "${S3_FORCE_UPLOAD:-}" && "${CI:-0}" -eq 0 ]]; then - echo_stderr "Skipping upload because CI=0 and S3_FORCE_UPLOAD not set." - exit 0 -fi - -if [[ "${NO_CACHE_UPLOAD:-0}" -eq 1 ]]; then - echo_stderr "Skipping upload because NO_CACHE_UPLOAD=1." - exit 0 -fi - -# In SSM/instance-profile mode, AWS CLI falls back to IMDS for credentials. -if [[ "${CI_SSM_MODE:-0}" -eq 0 ]]; then - if [[ -z "${AWS_ACCESS_KEY_ID:-}" || -z "${AWS_SECRET_ACCESS_KEY:-}" ]] && ! aws configure get aws_access_key_id &>/dev/null; then - echo_stderr "Skipping upload, no AWS credentials found." - exit 0 - fi -fi - -if [ -z "${S3_FORCE_UPLOAD:-}" ] && \ - aws ${S3_BUILD_CACHE_AWS_PARAMS:-} s3 ls "s3://aztec-ci-artifacts/build-cache/$name" &>/dev/null; then - echo_stderr "Skipping upload, already exists: $name" - exit 0 -fi - -out_tar=$(mktemp) -trap 'rm -f $out_tar' EXIT - -if [[ "$name" == *.zst ]]; then - tar --use-compress-program="zstd -T0" -cf $out_tar "$@" -else - tar -czf $out_tar "$@" -fi - -# Save to local cache if enabled. -if [[ -n "${CACHE_LOCAL_DIR:-}" ]]; then - if mkdir -p "$CACHE_LOCAL_DIR" 2>/dev/null; then - cp "$out_tar" "$CACHE_LOCAL_DIR/$name" - echo_stderr "Saved $name to local cache." - else - echo_stderr "Warning: Cannot create local cache dir $CACHE_LOCAL_DIR, skipping local cache." - fi -fi - -# Pipe tar directly to AWS S3 cp -if aws ${S3_BUILD_CACHE_AWS_PARAMS:-} s3 cp $out_tar "s3://aztec-ci-artifacts/build-cache/$name" &>/dev/null; then - echo_stderr "Cache upload of $name complete in ${SECONDS}s." -else - echo_stderr "Cache upload of $name failed." -fi diff --git a/ci3/check_port b/ci3/check_port deleted file mode 100755 index a637a8e7840..00000000000 --- a/ci3/check_port +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash -# Check if a port is free. If taken, print the process tree of the holder. -# Usage: check_port -# Exit code: 0 if free, 1 if taken. -set -eu - -port="${1:?Usage: check_port }" - -pid=$(lsof -ti :"$port" 2>/dev/null | head -1 || true) -if [ -z "$pid" ]; then - exit 0 -fi - -echo "Port $port is taken by PID $pid:" >&2 -# Show the command line of the process. -ps -p "$pid" -o pid,ppid,user,args --no-headers >&2 || true -# Show the process tree rooted at this PID. -pstree -apls "$pid" >&2 2>/dev/null || pstree -p "$pid" >&2 2>/dev/null || true -exit 1 diff --git a/ci3/clean_remote_tags b/ci3/clean_remote_tags deleted file mode 100755 index 5bd1608a248..00000000000 --- a/ci3/clean_remote_tags +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash -# Set your GitHub repository info and token if needed -GH_OWNER="aztecprotocol" -GH_REPO="aztec-packages" - -# Fetch all releases with pagination -page=1 -per_page=100 -all_releases="" - -while true; do - page_releases=$(curl -s "https://api.github.com/repos/$GH_OWNER/$GH_REPO/releases?per_page=$per_page&page=$page" | jq -r '.[].tag_name') - - # Break if no more releases are found - if [ -z "$page_releases" ]; then - break - fi - - all_releases="$all_releases $page_releases" - page=$((page + 1)) -done -# echo "${all_releases[@]}" - -# Create an associative array for fast lookup -declare -A release_tags -for tag in $all_releases; do - release_tags["$tag"]=1 -done - -# List remote tags and delete those not found in releases -for tag in $(git ls-remote --tags origin | awk '{print $2}' | sed 's|refs/tags/||'); do - if [ -z "${release_tags[$tag]}" ]; then - echo "Deleting remote tag: $tag" - git push --delete origin tag "$tag" - fi -done diff --git a/ci3/color_prefix b/ci3/color_prefix deleted file mode 100755 index 9141fa4ee5f..00000000000 --- a/ci3/color_prefix +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env bash -# Runs a command and prefixes each output line with a colored job name. -# Replicates the Makefile's run_command color prefixing functionality. -# -# Usage: color_prefix -# -# Example: -# color_prefix "test-foo" "echo hello world" -# color_prefix "build-bar" "./build.sh" - -set -euo pipefail - -if [ $# -lt 2 ]; then - echo "Usage: color_prefix " >&2 - exit 1 -fi - -job_name="$1" -shift -command="$@" - -cmd_pid="" -awk_pid="" - -# Signal handler - kill the command, let EOF propagate through awk, then exit. -function on_signal { - set +e - if [ -n "$cmd_pid" ]; then - kill -TERM "$cmd_pid" 2>/dev/null - wait $cmd_pid 2>/dev/null - cmd_pid= - fi - if [ -n "$awk_pid" ]; then - wait $awk_pid 2>/dev/null - awk_pid= - fi - exit 130 -} -trap on_signal SIGTERM SIGINT - -# Compute color from job name hash. -# Picks a color between 20 and 231 (avoiding very dark/light colors) -function compute_color { - local name="$1" - local hash=$(printf '%s' "$name" | cksum | cut -d' ' -f1) - echo $(( (hash % 212) + 20 )) -} - -color=$(compute_color "$job_name") - -# Named pipe so we can track both PIDs independently: -# kill cmd → EOF propagates through fifo → awk drains and exits. -prefix_fifo=$(mktemp -u) -mkfifo "$prefix_fifo" -trap 'rm -f $prefix_fifo' EXIT - -# Awk prefixer reads from fifo, writes to our stdout. -awk -v c="$color" -v n="$job_name" \ - '{printf "\033[38;5;%sm[%s]\033[0m %s\n", c, n, $0; fflush()}' \ - < "$prefix_fifo" & -awk_pid=$! - -# Command writes into the fifo. -stdbuf -oL -eL bash -c "$command" > "$prefix_fifo" 2>&1 & -cmd_pid=$! - -# Wait for command and capture exit code. -wait $cmd_pid 2>/dev/null -code=$? -cmd_pid= - -# Command exited; EOF propagates through fifo. Wait for awk to drain. -wait $awk_pid 2>/dev/null -awk_pid= - -exit $code diff --git a/ci3/cpuset_count b/ci3/cpuset_count deleted file mode 100755 index 3258da527a6..00000000000 --- a/ci3/cpuset_count +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash -# Given a valid cpuset string, count the number of CPUs. - -count=0 -IFS=',' read -ra parts <<< "$1" -for part in "${parts[@]}"; do - if [[ $part == *"-"* ]]; then - start=${part%-*} - end=${part#*-} - count=$((count + end - start + 1)) - else - count=$((count + 1)) - fi -done -echo "$count" diff --git a/ci3/dashboard/Dockerfile b/ci3/dashboard/Dockerfile deleted file mode 100644 index 980eff34bab..00000000000 --- a/ci3/dashboard/Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -FROM python:3.12 - -# Install aws cli. -RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-$(uname -m).zip" -o "awscliv2.zip" && \ - unzip awscliv2.zip && \ - ./aws/install --bin-dir /usr/local/bin --install-dir /usr/local/aws-cli --update && \ - rm -rf aws awscliv2.zip - -# Install redis cli. -RUN apt update && apt install -y \ - jq \ - netcat-openbsd \ - redis-tools \ - && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* - -WORKDIR /app -COPY requirements.txt requirements.txt -RUN pip install --no-cache-dir -r requirements.txt gunicorn - -# Install ci-metrics dependencies (ci-metrics runs as subprocess) -COPY ci-metrics/requirements.txt ci-metrics/requirements.txt -RUN pip install --no-cache-dir -r ci-metrics/requirements.txt - -RUN git config --global --add safe.directory /aztec-packages -COPY . . -EXPOSE 8080 8081 -CMD ["gunicorn", "-w", "4", "--threads", "12", "-b", "0.0.0.0:8080", "rk:app"] diff --git a/ci3/dashboard/chonk-breakdowns/README.md b/ci3/dashboard/chonk-breakdowns/README.md deleted file mode 100644 index a6ff27c1ffb..00000000000 --- a/ci3/dashboard/chonk-breakdowns/README.md +++ /dev/null @@ -1,60 +0,0 @@ -# Chonk Breakdowns - Barretenberg Benchmark Viewer - -This viewer displays hierarchical timing breakdowns for Barretenberg's Chonk (IVC) proving system. It shows where time is spent during proof generation, with drill-down capabilities to explore component performance. - -## What This Shows - -The breakdown viewer visualizes: -- **Hierarchical component timings** from Barretenberg's `--bench_out_hierarchical` flag -- **Time spent in each proving phase**: ChonkAccumulate, ChonkProve, OinkProver, etc. -- **Nested operations**: Click on components to drill down into child operations -- **Percentage distribution**: See which operations dominate proving time - -Data comes from CI benchmark runs and is stored in `/logs-disk/bench/bb-breakdown/`. - -## Local Testing - -### Quick Start - -1. **Install dependencies (first time only):** - ```bash - cd rkapp - python3 -m venv venv - venv/bin/pip install -r requirements.txt - ``` - -2. **Fetch sample test data:** - ```bash - mkdir -p /tmp/rkapp-test-data/bench/bb-breakdown - # Fetch all breakdowns for a specific SHA (e.g., f4decd6) - scp -i ~/.ssh/build_instance_key \ - "ubuntu@ci.aztec-labs.com:/logs-disk/bench/bb-breakdown/*-f4decd6*.log.gz" \ - /tmp/rkapp-test-data/bench/bb-breakdown/ - ``` - -3. **Start the server:** - ```bash - cd rkapp - ./chonk-breakdowns/run-local.sh - ``` - -4. **Test in browser:** - - Open: http://localhost:8080/chonk-breakdowns - - Enter credentials when prompted - - The SHA field will auto-populate with the latest commit from `aztec-packages` `next` branch - - Select Runtime: `native` or `wasm` - - The Flow dropdown will dynamically show flows available for the selected runtime and SHA - - Click "Load Breakdown" - -## What the Script Does - -- Sets `LOGS_DISK_PATH=/tmp/rkapp-test-data` to use local test data -- Sets `REDIS_TIMEOUT=0.1` for fast failover to disk -- Runs rk.py directly (no mocking needed) - -## Environment Variables - -- `LOGS_DISK_PATH` - Path to logs directory (default: `/logs-disk`) -- `REDIS_HOST` - Redis hostname (default for local: `localhost`) -- `REDIS_PORT` - Redis port (default for local: `9999` - non-existent) -- `REDIS_TIMEOUT` - Connection timeout in seconds (default: `0.1`) diff --git a/ci3/dashboard/chonk-breakdowns/breakdown-viewer.html b/ci3/dashboard/chonk-breakdowns/breakdown-viewer.html deleted file mode 100644 index 31d88dce684..00000000000 --- a/ci3/dashboard/chonk-breakdowns/breakdown-viewer.html +++ /dev/null @@ -1,855 +0,0 @@ - - - - - - Barretenberg Benchmark Breakdown Viewer - - - -

-

🔍 Barretenberg Benchmark Breakdown Viewer

-

Visualize hierarchical benchmark data showing time spent in each component

- - -
-

Step 1: Enter Commit SHA

-
- - -
-
- -
-
- - - - -
- - - -
-
-
- - - - - diff --git a/ci3/dashboard/chonk-breakdowns/run-local.sh b/ci3/dashboard/chonk-breakdowns/run-local.sh deleted file mode 100755 index 5e8176f1944..00000000000 --- a/ci3/dashboard/chonk-breakdowns/run-local.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env bash -# Run rk.py locally for testing -# This script sets up the environment to use local test data - -set -e - -echo "============================================================" -echo "Starting rkapp locally for testing" -echo "============================================================" -echo "" - -# Check if venv exists -if [ ! -d "venv" ]; then - echo "Creating virtual environment..." - python3 -m venv venv - echo "Installing dependencies..." - venv/bin/pip install -q -r requirements.txt - echo "" -fi - -# Set environment variables for local testing -# Use a dummy Redis host to skip Redis and go straight to disk -export REDIS_HOST="${REDIS_HOST:-localhost}" -export REDIS_PORT="${REDIS_PORT:-9999}" -export REDIS_TIMEOUT="0.1" # Very short timeout to fail fast -# Point to /tmp for test data (not committed to git) -export LOGS_DISK_PATH="/tmp/rkapp-test-data" - -echo "Test data location: $LOGS_DISK_PATH" -echo "" - -# Check if test data exists -if [ ! -d "$LOGS_DISK_PATH/bench/bb-breakdown" ]; then - echo "⚠️ No test data found at: $LOGS_DISK_PATH/bench/bb-breakdown/" - echo " (Will fall back to Redis if available)" -else - echo "Available test data:" - for f in $LOGS_DISK_PATH/bench/bb-breakdown/*.log.gz; do - if [ -f "$f" ]; then - basename "$f" | sed -E 's/^([^-]+)-(.+)-([^-]+)\.log\.gz$/ Runtime: \1, Flow: \2, SHA: \3/' - fi - done -fi -echo "" - -echo "Starting Flask server..." -echo "Server will be available at: http://localhost:8080" -echo "Chonk breakdowns viewer: http://localhost:8080/chonk-breakdowns" -echo "" -echo "Note: You'll need Redis access or use test data that exists locally" -echo " Test data is in test-data/bench/bb-breakdown/" -echo "" -echo "Press Ctrl+C to stop" -echo "============================================================" -echo "" - -# Run with modified LOGS_DISK_PATH if we want to use test-data -venv/bin/python3 rk.py diff --git a/ci3/dashboard/ci-metrics/Dockerfile b/ci3/dashboard/ci-metrics/Dockerfile deleted file mode 100644 index 4013545da66..00000000000 --- a/ci3/dashboard/ci-metrics/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -FROM python:3.12 - -RUN apt update && apt install -y jq redis-tools && rm -rf /var/lib/apt/lists/* - -WORKDIR /app -COPY requirements.txt requirements.txt -RUN pip install --no-cache-dir -r requirements.txt gunicorn -RUN git config --global --add safe.directory /aztec-packages -COPY . . -EXPOSE 8081 -CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:8081", "app:app"] diff --git a/ci3/dashboard/ci-metrics/app.py b/ci3/dashboard/ci-metrics/app.py deleted file mode 100644 index e2925d9ae3b..00000000000 --- a/ci3/dashboard/ci-metrics/app.py +++ /dev/null @@ -1,1149 +0,0 @@ -from flask import Flask, request, Response, redirect -from flask_compress import Compress -from flask_httpauth import HTTPBasicAuth -from datetime import datetime, timedelta -import json -import os -import re -import redis -import time -import threading -from pathlib import Path - -import db -import metrics -import github_data -import billing.aws as billing_aws -from billing import ( - get_billing_files_in_range, - aggregate_billing_weekly, aggregate_billing_monthly, - serve_billing_dashboard, -) - -REDIS_HOST = os.getenv('REDIS_HOST', 'localhost') -REDIS_PORT = int(os.getenv('REDIS_PORT', '6379')) -LOGS_DISK_PATH = os.getenv('LOGS_DISK_PATH', '/logs-disk') -DASHBOARD_PASSWORD = os.getenv('DASHBOARD_PASSWORD', 'password') - -r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, decode_responses=False) - -app = Flask(__name__) -Compress(app) -auth = HTTPBasicAuth() - - -@auth.verify_password -def verify_password(username, password): - return password == DASHBOARD_PASSWORD - - -def _init(): - """Initialize SQLite, warm caches, and start background threads.""" - try: - db.get_db() - metrics.start_test_listener(r) - metrics.start_phase_listener(r) - metrics.start_ci_run_sync(r) - github_data.start_merge_queue_poller() - github_data.start_pr_dirs_worker() - print("[ci-metrics] Background threads started") - except Exception as e: - print(f"[ci-metrics] Warning: startup failed: {e}") - # Warm billing caches so first request isn't slow - try: - from billing.gcp import _ensure_cached as _warm_gcp - _warm_gcp() - print("[ci-metrics] GCP billing cache warmed") - except Exception as e: - print(f"[ci-metrics] GCP billing warmup failed: {e}") - try: - from billing.aws import _ensure_cached as _warm_aws - _warm_aws() - print("[ci-metrics] AWS costs cache warmed") - except Exception as e: - print(f"[ci-metrics] AWS costs warmup failed: {e}") - -threading.Thread(target=_init, daemon=True, name='metrics-init').start() - - -# ---- Helpers ---- - -def _aggregate_dates(by_date_list, granularity, sum_fields, avg_fields=None): - """Aggregate a list of {date, ...} dicts by weekly/monthly granularity.""" - if granularity == 'daily' or not by_date_list: - return by_date_list - - buckets = {} - for entry in by_date_list: - d = datetime.strptime(entry['date'], '%Y-%m-%d') - if granularity == 'weekly': - key = (d - timedelta(days=d.weekday())).strftime('%Y-%m-%d') - else: # monthly - key = d.strftime('%Y-%m') + '-01' - - if key not in buckets: - buckets[key] = {'date': key} - for f in sum_fields: - buckets[key][f] = 0 - if avg_fields: - for f in avg_fields: - buckets[key][f'_avg_sum_{f}'] = 0 - buckets[key][f'_avg_cnt_{f}'] = 0 - - for f in sum_fields: - buckets[key][f] += entry.get(f) or 0 - if avg_fields: - for f in avg_fields: - val = entry.get(f) - if val is not None: - buckets[key][f'_avg_sum_{f}'] += val - buckets[key][f'_avg_cnt_{f}'] += 1 - - result = [] - for key in sorted(buckets): - b = buckets[key] - out = {'date': b['date']} - for f in sum_fields: - out[f] = round(b[f], 2) if isinstance(b[f], float) else b[f] - if avg_fields: - for f in avg_fields: - cnt = b[f'_avg_cnt_{f}'] - out[f] = round(b[f'_avg_sum_{f}'] / cnt, 1) if cnt else None - result.append(out) - - return result - - -def _json(data): - return Response(json.dumps(data), mimetype='application/json') - - -_TEN_DAYS = 10 * 24 * 3600 - - -def _cache_ttl(date_to: str) -> int: - """Return 10-day TTL for historical ranges (date_to < today), else 5 min.""" - try: - if datetime.strptime(date_to, '%Y-%m-%d').date() < datetime.now().date(): - return _TEN_DAYS - except ValueError: - pass - return 300 - - -# ---- Author mapping: git display name → GitHub username ---- - -_author_map = {} -_author_map_ts = 0 - - -def _get_author_map() -> dict: - """Build git display name → GitHub username mapping from ci_runs + pr_authors.""" - global _author_map, _author_map_ts - now = time.time() - if now - _author_map_ts < 3600 and _author_map: - return _author_map - rows = db.query(''' - SELECT cr.author as git_name, pa.author as github_user, COUNT(*) as c - FROM ci_runs cr - JOIN pr_authors pa ON cr.pr_number = pa.pr_number - WHERE cr.author IS NOT NULL AND cr.author != '' - AND pa.author IS NOT NULL AND pa.author != '' - GROUP BY cr.author, pa.author - ''') - name_to_gh = {} - for row in rows: - gn = row['git_name'] - gh = row['github_user'] - if gn not in name_to_gh: - name_to_gh[gn] = {} - name_to_gh[gn][gh] = name_to_gh[gn].get(gh, 0) + row['c'] - result = {} - for gn, gh_counts in name_to_gh.items(): - best = max(gh_counts, key=gh_counts.get) - result[gn] = best - result[best] = best # identity mapping for usernames used as commit_author - _author_map = result - _author_map_ts = now - return result - - -def _normalize_authors(authors_str: str) -> str: - """Normalize comma-separated git names to deduplicated GitHub usernames.""" - if not authors_str: - return '' - amap = _get_author_map() - seen = set() - result = [] - for name in authors_str.split(','): - name = name.strip() - if not name: - continue - gh = amap.get(name, name) - if gh not in seen: - seen.add(gh) - result.append(gh) - return ','.join(result) - - -# ---- Namespace billing ---- - -@app.route('/namespace-billing') -@auth.login_required -def namespace_billing(): - html = serve_billing_dashboard() - if html: - return html - return "Billing dashboard not found", 404 - - -@app.route('/api/billing/data') -@auth.login_required -def billing_data(): - date_from_str = request.args.get('from') - date_to_str = request.args.get('to') - granularity = request.args.get('granularity', 'daily') - - if not date_from_str or not date_to_str: - return _json({'error': 'from and to date params required (YYYY-MM-DD)'}), 400 - try: - date_from = datetime.strptime(date_from_str, '%Y-%m-%d') - date_to = datetime.strptime(date_to_str, '%Y-%m-%d') - except ValueError: - return _json({'error': 'Invalid date format, use YYYY-MM-DD'}), 400 - - daily_data = get_billing_files_in_range(date_from, date_to) - - # Filter out namespaces costing less than $1 total across the range - ns_totals = {} - for entry in daily_data: - for ns, ns_data in entry.get('namespaces', {}).items(): - ns_totals[ns] = ns_totals.get(ns, 0) + ns_data.get('total', 0) - cheap_ns = {ns for ns, total in ns_totals.items() if total < 1.0} - if cheap_ns: - for entry in daily_data: - entry['namespaces'] = {ns: d for ns, d in entry.get('namespaces', {}).items() - if ns not in cheap_ns} - - if granularity == 'weekly': - result = aggregate_billing_weekly(daily_data) - elif granularity == 'monthly': - result = aggregate_billing_monthly(daily_data) - else: - result = daily_data - - return _json(result) - - -# ---- CI runs ---- - -@app.route('/api/ci/runs') -@auth.login_required -def api_ci_runs(): - date_from = request.args.get('from', '') - date_to = request.args.get('to', '') - status_filter = request.args.get('status', '') - author = request.args.get('author', '') - dashboard = request.args.get('dashboard', '') - limit = min(int(request.args.get('limit', 100)), 1000) - offset = int(request.args.get('offset', 0)) - - ts_from = int(datetime.strptime(date_from, '%Y-%m-%d').timestamp() * 1000) if date_from else None - ts_to = int((datetime.strptime(date_to, '%Y-%m-%d') + timedelta(days=1)).timestamp() * 1000) if date_to else None - - runs = metrics.get_ci_runs(ts_from, ts_to) - - if status_filter: - runs = [run for run in runs if run.get('status') == status_filter] - if author: - runs = [run for run in runs if run.get('author') == author] - if dashboard: - runs = [run for run in runs if run.get('dashboard') == dashboard] - - runs.sort(key=lambda x: x.get('timestamp', 0), reverse=True) - runs = runs[offset:offset + limit] - - return _json(runs) - - -@app.route('/api/ci/stats') -@auth.login_required -def api_ci_stats(): - ts_from = int((datetime.now() - timedelta(days=7)).timestamp() * 1000) - runs = metrics.get_ci_runs(ts_from) - - total = len(runs) - passed = sum(1 for run in runs if run.get('status') == 'PASSED') - failed = sum(1 for run in runs if run.get('status') == 'FAILED') - costs = [run['cost_usd'] for run in runs if run.get('cost_usd') is not None] - durations = [] - for run in runs: - complete = run.get('complete') - ts = run.get('timestamp') - if complete and ts: - durations.append((complete - ts) / 60000.0) - - return _json({ - 'total_runs': total, - 'passed': passed, - 'failed': failed, - 'total_cost': round(sum(costs), 2) if costs else None, - 'avg_duration_mins': round(sum(durations) / len(durations), 1) if durations else None, - }) - - -# ---- Cost endpoints ---- - -@app.route('/api/costs/overview') -@auth.login_required -def api_costs_overview(): - date_from = request.args.get('from', (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d')) - date_to = request.args.get('to', datetime.now().strftime('%Y-%m-%d')) - granularity = request.args.get('granularity', 'daily') - result = billing_aws.get_costs_overview(date_from, date_to) - if granularity != 'daily' and result.get('by_date'): - buckets = {} - for entry in result['by_date']: - d = datetime.strptime(entry['date'], '%Y-%m-%d') - if granularity == 'weekly': - key = (d - timedelta(days=d.weekday())).strftime('%Y-%m-%d') - else: - key = d.strftime('%Y-%m') + '-01' - if key not in buckets: - buckets[key] = {'date': key, 'aws': {}, 'gcp': {}, 'aws_total': 0, 'gcp_total': 0} - for cat, amt in entry.get('aws', {}).items(): - buckets[key]['aws'][cat] = buckets[key]['aws'].get(cat, 0) + amt - for cat, amt in entry.get('gcp', {}).items(): - buckets[key]['gcp'][cat] = buckets[key]['gcp'].get(cat, 0) + amt - buckets[key]['aws_total'] += entry.get('aws_total', 0) - buckets[key]['gcp_total'] += entry.get('gcp_total', 0) - result['by_date'] = sorted(buckets.values(), key=lambda x: x['date']) - result['period'] = {'from': date_from, 'to': date_to} - return _json(result) - - -@app.route('/api/costs/details') -@auth.login_required -def api_costs_details(): - """Per-resource (USAGE_TYPE) cost breakdown.""" - date_from = request.args.get('from', (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d')) - date_to = request.args.get('to', datetime.now().strftime('%Y-%m-%d')) - - rows = billing_aws.get_aws_cost_details(date_from, date_to) - - usage_map = {} - for row in rows: - ut = row['usage_type'] - if ut not in usage_map: - usage_map[ut] = { - 'usage_type': ut, - 'service': row['service'], - 'category': row['category'], - 'total': 0, - 'by_date': {}, - 'is_ri': 'HeavyUsage' in ut, - } - usage_map[ut]['total'] += row['amount_usd'] - d = row['date'] - usage_map[ut]['by_date'][d] = usage_map[ut]['by_date'].get(d, 0) + row['amount_usd'] - - items = sorted(usage_map.values(), key=lambda x: -x['total']) - for item in items: - item['total'] = round(item['total'], 2) - item['by_date'] = {d: round(v, 4) for d, v in sorted(item['by_date'].items())} - - all_dates = sorted({row['date'] for row in rows}) - ri_items = [i for i in items if i['is_ri']] - ri_total = round(sum(i['total'] for i in ri_items), 2) - - return _json({ - 'items': items, - 'dates': all_dates, - 'ri_total': ri_total, - 'grand_total': round(sum(i['total'] for i in items), 2), - }) - - -@app.route('/api/costs/attribution') -@auth.login_required -def api_costs_attribution(): - """CI cost attribution by user, branch, instance.""" - date_from = request.args.get('from', (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d')) - date_to = request.args.get('to', datetime.now().strftime('%Y-%m-%d')) - ts_from = int(datetime.strptime(date_from, '%Y-%m-%d').timestamp() * 1000) - ts_to = int((datetime.strptime(date_to, '%Y-%m-%d') + timedelta(days=1)).timestamp() * 1000) - - runs = metrics.get_ci_runs(ts_from, ts_to) - runs_with_cost = [run for run in runs if run.get('cost_usd') is not None] - - # Enrich merge queue runs with PR author from GitHub - pr_numbers = {run.get('pr_number') for run in runs_with_cost if run.get('pr_number')} - pr_authors = github_data.batch_get_pr_authors(pr_numbers) - - granularity = request.args.get('granularity', 'daily') - - instances = [] - by_user = {} - by_branch = {} - by_type = {} - by_date_type = {} - - for run in runs_with_cost: - info = billing_aws.decode_branch_info(run) - cost = run['cost_usd'] - date = metrics._ts_to_date(run.get('timestamp', 0)) - - author = info['author'] - prn = info['pr_number'] - if prn and int(prn) in pr_authors: - author = pr_authors[int(prn)]['author'] - # Attribute nightly / release runs to a special 'release' actor - if info['type'] in ('nightly', 'releases'): - author = 'release' - - inst_type = run.get('instance_type', 'unknown') - vcpus = run.get('instance_vcpus') - if inst_type == 'unknown' and vcpus: - inst_type = f'{vcpus}vcpu' - - instances.append({ - 'instance_name': info['instance_name'], - 'date': date, - 'cost_usd': cost, - 'author': author, - 'branch': info['branch'], - 'pr_number': prn, - 'type': info['type'], - 'instance_type': inst_type, - 'spot': run.get('spot', False), - 'job_id': run.get('job_id', ''), - 'duration_mins': round((run.get('complete', 0) - run.get('timestamp', 0)) / 60000, 1) if run.get('complete') else None, - }) - - if author not in by_user: - by_user[author] = {'aws_cost': 0, 'gcp_cost': 0, 'runs': 0, 'by_date': {}} - by_user[author]['aws_cost'] += cost - by_user[author]['runs'] += 1 - by_user[author]['by_date'][date] = by_user[author]['by_date'].get(date, 0) + cost - - branch_key = info['branch'] or info['type'] - if branch_key not in by_branch: - by_branch[branch_key] = {'cost': 0, 'runs': 0, 'type': info['type'], 'author': author} - by_branch[branch_key]['cost'] += cost - by_branch[branch_key]['runs'] += 1 - - rt = info['type'] - if rt not in by_type: - by_type[rt] = {'cost': 0, 'runs': 0} - by_type[rt]['cost'] += cost - by_type[rt]['runs'] += 1 - - if date not in by_date_type: - by_date_type[date] = {} - by_date_type[date][rt] = by_date_type[date].get(rt, 0) + cost - - # GCP costs — reported as total, no namespace→user heuristic - gcp_total = 0 - try: - from billing.gcp import get_billing_files_in_range as get_gcp_billing - gcp_data = get_gcp_billing( - datetime.strptime(date_from, '%Y-%m-%d'), - datetime.strptime(date_to, '%Y-%m-%d'), - ) - for entry in gcp_data: - for ns, ns_data in entry.get('namespaces', {}).items(): - gcp_total += ns_data.get('total', 0) - except Exception as e: - print(f"[attribution] GKE billing error: {e}") - - # Sort and format - user_list = [{'author': a, 'aws_cost': round(v['aws_cost'], 2), 'gcp_cost': round(v['gcp_cost'], 2), - 'total_cost': round(v['aws_cost'] + v['gcp_cost'], 2), 'runs': v['runs'], - 'by_date': {d: round(c, 2) for d, c in sorted(v['by_date'].items())}} - for a, v in sorted(by_user.items(), key=lambda x: -(x[1]['aws_cost'] + x[1]['gcp_cost']))] - - branch_list = [{'branch': b, 'cost': round(v['cost'], 2), 'runs': v['runs'], - 'type': v['type'], 'author': v['author']} - for b, v in sorted(by_branch.items(), key=lambda x: -x[1]['cost'])[:100]] - - type_list = [{'type': t, 'cost': round(v['cost'], 2), 'runs': v['runs']} - for t, v in sorted(by_type.items(), key=lambda x: -x[1]['cost'])] - - instances.sort(key=lambda x: -(x['cost_usd'] or 0)) - - all_types = sorted(by_type.keys()) - # Pre-compute runs-per-date to avoid O(dates × instances) - runs_per_date = {} - for inst in instances: - runs_per_date[inst['date']] = runs_per_date.get(inst['date'], 0) + 1 - by_date_list = [] - for date in sorted(by_date_type): - entry = {'date': date, 'total': 0, 'runs': runs_per_date.get(date, 0)} - for rt in all_types: - entry[rt] = round(by_date_type[date].get(rt, 0), 2) - entry['total'] += by_date_type[date].get(rt, 0) - entry['total'] = round(entry['total'], 2) - by_date_list.append(entry) - - by_date_list = _aggregate_dates(by_date_list, granularity, - sum_fields=['total', 'runs'] + all_types) - - total_aws = sum(u['aws_cost'] for u in user_list) - - return _json({ - 'by_user': user_list, - 'by_branch': branch_list, - 'by_type': type_list, - 'by_date': by_date_list, - 'run_types': all_types, - 'instances': instances[:500], - 'period': {'from': date_from, 'to': date_to}, - 'totals': {'aws': round(total_aws, 2), 'gcp': round(gcp_total, 2), - 'gcp_unattributed': round(gcp_total, 2), - 'combined': round(total_aws + gcp_total, 2)}, - }) - - -@app.route('/api/costs/runners') -@auth.login_required -def api_costs_runners(): - date_from = request.args.get('from', (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d')) - date_to = request.args.get('to', datetime.now().strftime('%Y-%m-%d')) - granularity = request.args.get('granularity', 'daily') - dashboard = request.args.get('dashboard', '') - ts_from = int(datetime.strptime(date_from, '%Y-%m-%d').timestamp() * 1000) - ts_to = int((datetime.strptime(date_to, '%Y-%m-%d') + timedelta(days=1)).timestamp() * 1000) - - runs = metrics.get_ci_runs(ts_from, ts_to) - runs_with_cost = [run for run in runs if run.get('cost_usd') is not None] - if dashboard: - runs_with_cost = [run for run in runs_with_cost if run.get('dashboard') == dashboard] - - by_date_map = {} - for run in runs_with_cost: - date = metrics._ts_to_date(run.get('timestamp', 0)) - if date not in by_date_map: - by_date_map[date] = {'spot_cost': 0, 'ondemand_cost': 0, 'total': 0} - cost = run['cost_usd'] - if run.get('spot'): - by_date_map[date]['spot_cost'] += cost - else: - by_date_map[date]['ondemand_cost'] += cost - by_date_map[date]['total'] += cost - - by_date = [{'date': date, 'spot_cost': round(d['spot_cost'], 2), - 'ondemand_cost': round(d['ondemand_cost'], 2), 'total': round(d['total'], 2), - 'spot_pct': round(100.0 * d['spot_cost'] / max(d['total'], 0.01), 1)} - for date, d in sorted(by_date_map.items())] - - by_date = _aggregate_dates(by_date, granularity, - sum_fields=['spot_cost', 'ondemand_cost', 'total']) - for d in by_date: - d['spot_pct'] = round(100.0 * d['spot_cost'] / max(d['total'], 0.01), 1) - - by_instance_map = {} - for run in runs_with_cost: - inst = run.get('instance_type', 'unknown') - if inst not in by_instance_map: - by_instance_map[inst] = {'cost': 0, 'runs': 0} - by_instance_map[inst]['cost'] += run['cost_usd'] - by_instance_map[inst]['runs'] += 1 - by_instance = [{'instance_type': k, 'cost': round(v['cost'], 2), 'runs': v['runs']} - for k, v in sorted(by_instance_map.items(), key=lambda x: -x[1]['cost'])] - - by_dash_map = {} - for run in runs_with_cost: - dash = run.get('dashboard', 'unknown') - if dash not in by_dash_map: - by_dash_map[dash] = {'cost': 0, 'runs': 0} - by_dash_map[dash]['cost'] += run['cost_usd'] - by_dash_map[dash]['runs'] += 1 - by_dashboard = [{'dashboard': k, 'cost': round(v['cost'], 2), 'runs': v['runs']} - for k, v in sorted(by_dash_map.items(), key=lambda x: -x[1]['cost'])] - - total_cost = sum(run['cost_usd'] for run in runs_with_cost) - spot_cost = sum(run['cost_usd'] for run in runs_with_cost if run.get('spot')) - - return _json({ - 'by_date': by_date, - 'by_instance_type': by_instance, - 'by_dashboard': by_dashboard, - 'period': {'from': date_from, 'to': date_to}, - 'summary': { - 'total_cost': round(total_cost, 2), - 'spot_pct': round(100.0 * spot_cost / max(total_cost, 0.01), 1), - 'avg_cost_per_run': round(total_cost / max(len(runs_with_cost), 1), 2), - 'total_runs': len(runs_with_cost), - }, - }) - - -# ---- CI Performance ---- - -@app.route('/api/ci/performance') -@auth.login_required -def api_ci_performance(): - date_from = request.args.get('from', (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d')) - date_to = request.args.get('to', datetime.now().strftime('%Y-%m-%d')) - dashboard = request.args.get('dashboard', '') - granularity = request.args.get('granularity', 'daily') - _ck = f'perf:{date_from}:{date_to}:{dashboard}:{granularity}' - if cached := db.cache_get(_ck): - return _json(cached) - _t0 = time.perf_counter() - ts_from = int(datetime.strptime(date_from, '%Y-%m-%d').timestamp() * 1000) - ts_to = int((datetime.strptime(date_to, '%Y-%m-%d') + timedelta(days=1)).timestamp() * 1000) - - runs = metrics.get_ci_runs(ts_from, ts_to) - runs = [run for run in runs if run.get('status') in ('PASSED', 'FAILED')] - if dashboard: - runs = [run for run in runs if run.get('dashboard') == dashboard] - _t1 = time.perf_counter() - - by_date_map = {} - for run in runs: - date = metrics._ts_to_date(run.get('timestamp', 0)) - if date not in by_date_map: - by_date_map[date] = {'total': 0, 'passed': 0, 'failed': 0, 'durations': []} - by_date_map[date]['total'] += 1 - if run.get('status') == 'PASSED': - by_date_map[date]['passed'] += 1 - else: - by_date_map[date]['failed'] += 1 - complete = run.get('complete') - ts = run.get('timestamp') - if complete and ts: - by_date_map[date]['durations'].append((complete - ts) / 60000.0) - - by_date = [] - for date in sorted(by_date_map): - d = by_date_map[date] - durs = sorted(d['durations']) - by_date.append({ - 'date': date, - 'total': d['total'], - 'passed': d['passed'], - 'failed': d['failed'], - 'pass_rate': round(100.0 * d['passed'] / max(d['total'], 1), 1), - 'failure_rate': round(100.0 * d['failed'] / max(d['total'], 1), 1), - 'avg_duration_mins': round(sum(durs) / len(durs), 1) if durs else None, - 'p50_duration_mins': round(durs[len(durs) // 2], 1) if durs else None, - 'p95_duration_mins': round(durs[int(len(durs) * 0.95)], 1) if durs else None, - 'max_duration_mins': round(max(durs), 1) if durs else None, - }) - - _t2 = time.perf_counter() - # Merge test outcome counts from test_daily_stats before aggregation - ds_conditions = ['date >= ?', 'date <= ?'] - ds_params = [date_from, date_to] - if dashboard: - ds_conditions.append('dashboard = ?') - ds_params.append(dashboard) - ds_where = 'WHERE ' + ' AND '.join(ds_conditions) - - daily_test_counts = db.query(f''' - SELECT date, SUM(passed) as passed, SUM(failed) as failed, SUM(flaked) as flaked - FROM test_daily_stats {ds_where} - GROUP BY date - ''', ds_params) - daily_test_map = {r['date']: r for r in daily_test_counts} - for d in by_date: - tc = daily_test_map.get(d['date'], {}) - d['flake_count'] = tc.get('flaked', 0) or 0 - d['test_failure_count'] = tc.get('failed', 0) or 0 - d['test_success_count'] = tc.get('passed', 0) or 0 - - by_date = _aggregate_dates(by_date, granularity, - sum_fields=['total', 'passed', 'failed', - 'flake_count', 'test_failure_count', 'test_success_count'], - avg_fields=['avg_duration_mins', 'p50_duration_mins', - 'p95_duration_mins', 'max_duration_mins']) - for d in by_date: - d['pass_rate'] = round(100.0 * d['passed'] / max(d['total'], 1), 1) - d['failure_rate'] = round(100.0 * d['failed'] / max(d['total'], 1), 1) - - # Duration by dashboard (pipeline) — from pre-aggregated ci_run_daily_stats - dbd_rows = db.query(''' - SELECT date, dashboard, run_count, passed, failed, - sum_duration, min_duration, max_duration, p50_duration, p95_duration - FROM ci_run_daily_stats - WHERE date >= ? AND date <= ? - ORDER BY date - ''', (date_from, date_to)) - - dbd_map = {} # {dashboard: [{date, avg_duration_mins, ...}]} - for r in dbd_rows: - dbd_map.setdefault(r['dashboard'], []).append({ - 'date': r['date'], - 'avg_duration_mins': round(r['sum_duration'] / max(r['run_count'], 1), 1), - 'total_duration_mins': round(r['sum_duration'], 1), - 'p50_duration_mins': r['p50_duration'], - 'p95_duration_mins': r['p95_duration'], - 'count': r['run_count'], - }) - - duration_by_dashboard = {} - for db_name, entries in dbd_map.items(): - duration_by_dashboard[db_name] = _aggregate_dates( - entries, granularity, - sum_fields=['count', 'total_duration_mins'], - avg_fields=['avg_duration_mins', 'p50_duration_mins', 'p95_duration_mins']) - - _t3 = time.perf_counter() - # Top flakes/failures (with affected authors — filter out empty/NULL) - _author_concat = "GROUP_CONCAT(DISTINCT CASE WHEN commit_author IS NOT NULL AND commit_author != '' THEN commit_author END)" - if dashboard: - top_flakes = db.query(f''' - SELECT test_cmd, COUNT(*) as count, dashboard, - {_author_concat} as authors - FROM test_events WHERE status='flaked' AND dashboard = ? - AND timestamp >= ? AND timestamp <= ? - GROUP BY test_cmd ORDER BY count DESC LIMIT 20 - ''', (dashboard, date_from, date_to + 'T23:59:59')) - top_failures = db.query(f''' - SELECT test_cmd, COUNT(*) as count, dashboard, - {_author_concat} as authors - FROM test_events WHERE status='failed' AND dashboard = ? - AND timestamp >= ? AND timestamp <= ? - GROUP BY test_cmd ORDER BY count DESC LIMIT 20 - ''', (dashboard, date_from, date_to + 'T23:59:59')) - else: - top_flakes = db.query(f''' - SELECT test_cmd, COUNT(*) as count, dashboard, - {_author_concat} as authors - FROM test_events WHERE status='flaked' AND timestamp >= ? AND timestamp <= ? - GROUP BY test_cmd ORDER BY count DESC LIMIT 20 - ''', (date_from, date_to + 'T23:59:59')) - top_failures = db.query(f''' - SELECT test_cmd, COUNT(*) as count, dashboard, - {_author_concat} as authors - FROM test_events WHERE status='failed' AND timestamp >= ? AND timestamp <= ? - GROUP BY test_cmd ORDER BY count DESC LIMIT 20 - ''', (date_from, date_to + 'T23:59:59')) - - # Normalize git display names → GitHub usernames - for row in top_flakes: - row['authors'] = _normalize_authors(row.get('authors', '')) - for row in top_failures: - row['authors'] = _normalize_authors(row.get('authors', '')) - - # Summary - total = len(runs) - passed = sum(1 for run in runs if run.get('status') == 'PASSED') - failed = total - passed - durations = [] - for run in runs: - complete = run.get('complete') - ts = run.get('timestamp') - if complete and ts: - durations.append((complete - ts) / 60000.0) - - # Test outcome summary from test_daily_stats - ds_summary = db.query(f''' - SELECT SUM(passed) as passed, SUM(failed) as failed, SUM(flaked) as flaked - FROM test_daily_stats {ds_where} - ''', ds_params) - ds_s = ds_summary[0] if ds_summary else {} - fc = ds_s.get('flaked', 0) or 0 - tfc = ds_s.get('failed', 0) or 0 - tpc = ds_s.get('passed', 0) or 0 - tc = fc + tfc + tpc - - _t4 = time.perf_counter() - _result = { - 'by_date': by_date, - 'duration_by_dashboard': duration_by_dashboard, - 'top_flakes': top_flakes, - 'top_failures': top_failures, - 'period': {'from': date_from, 'to': date_to}, - 'summary': { - 'total_runs': total, - 'pass_rate': round(100.0 * passed / max(total, 1), 1), - 'failure_rate': round(100.0 * failed / max(total, 1), 1), - 'avg_duration_mins': round(sum(durations) / len(durations), 1) if durations else None, - 'flake_rate': round(100.0 * fc / max(tc, 1), 1) if tc else 0, - 'total_flakes': fc, - 'total_test_failures': tfc, - 'total_test_successes': tpc, - }, - } - print(f"[perf] ci_performance {date_from}..{date_to} | get_ci_runs={_t1-_t0:.3f}s db_queries={_t2-_t1:.3f}s agg={_t3-_t2:.3f}s top_flakes={_t4-_t3:.3f}s total={_t4-_t0:.3f}s", flush=True) - db.cache_set(_ck, _result, _cache_ttl(date_to)) - return _json(_result) - - -# ---- GitHub integration ---- - -@app.route('/api/deployments/speed') -@auth.login_required -def api_deploy_speed(): - date_from = request.args.get('from', (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d')) - date_to = request.args.get('to', datetime.now().strftime('%Y-%m-%d')) - workflow = request.args.get('workflow', '') - granularity = request.args.get('granularity', 'daily') - result = github_data.get_deployment_speed(date_from, date_to, workflow) - if granularity != 'daily' and result.get('by_date'): - result['by_date'] = _aggregate_dates( - result['by_date'], granularity, - sum_fields=['count', 'success', 'failure'], - avg_fields=['median_mins', 'p95_mins']) - return _json(result) - - -@app.route('/api/branches/lag') -@auth.login_required -def api_branch_lag(): - date_from = request.args.get('from', (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d')) - date_to = request.args.get('to', datetime.now().strftime('%Y-%m-%d')) - return _json(github_data.get_branch_lag(date_from, date_to)) - - -@app.route('/api/prs/metrics') -@auth.login_required -def api_pr_metrics(): - date_from = request.args.get('from', (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d')) - date_to = request.args.get('to', datetime.now().strftime('%Y-%m-%d')) - author = request.args.get('author', '') - _ck = f'pr_metrics:{date_from}:{date_to}:{author}' - if cached := db.cache_get(_ck): - return _json(cached) - _t0 = time.perf_counter() - ts_from = int(datetime.strptime(date_from, '%Y-%m-%d').timestamp() * 1000) - ts_to = int((datetime.strptime(date_to, '%Y-%m-%d') + timedelta(days=1)).timestamp() * 1000) - ci_runs = metrics.get_ci_runs(ts_from, ts_to) - _t1 = time.perf_counter() - _result = github_data.get_pr_metrics(date_from, date_to, author, ci_runs) - _t2 = time.perf_counter() - print(f"[perf] pr_metrics {date_from}..{date_to} | get_ci_runs={_t1-_t0:.3f}s get_pr_metrics={_t2-_t1:.3f}s total={_t2-_t0:.3f}s", flush=True) - db.cache_set(_ck, _result, _cache_ttl(date_to)) - return _json(_result) - - -@app.route('/api/merge-queue/stats') -@auth.login_required -def api_merge_queue_stats(): - date_from = request.args.get('from', (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d')) - date_to = request.args.get('to', datetime.now().strftime('%Y-%m-%d')) - _ck = f'mq_stats:{date_from}:{date_to}' - if cached := db.cache_get(_ck): - return _json(cached) - _t0 = time.perf_counter() - _result = github_data.get_merge_queue_stats(date_from, date_to) - _t1 = time.perf_counter() - print(f"[perf] merge_queue_stats {date_from}..{date_to} | get_merge_queue_stats={_t1-_t0:.3f}s total={_t1-_t0:.3f}s", flush=True) - db.cache_set(_ck, _result, _cache_ttl(date_to)) - return _json(_result) - - -@app.route('/api/test-history/') -@auth.login_required -def api_test_history(test_hash): - """Test event history by hash — SQLite backing for Redis history_ lists.""" - branch = request.args.get('branch', '') - limit = min(int(request.args.get('limit', 1000)), 5000) - rows = metrics.get_test_history(test_hash, branch, limit) - return _json(rows) - - -@app.route('/api/ci/runs/pr/') -@auth.login_required -def api_ci_runs_for_pr(pr_number): - limit = min(int(request.args.get('limit', 100)), 500) - return _json(metrics.get_ci_runs_for_pr(pr_number, limit)) - - -@app.route('/api/ci/flakes-by-command') -@auth.login_required -def api_flakes_by_command(): - date_from = request.args.get('from', (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d')) - date_to = request.args.get('to', datetime.now().strftime('%Y-%m-%d')) - dashboard = request.args.get('dashboard', '') - _ck = f'flakes:{date_from}:{date_to}:{dashboard}' - _t0 = time.perf_counter() - metrics.sync_failed_tests_to_sqlite(r) - _t1 = time.perf_counter() - if cached := db.cache_get(_ck): - return _json(cached) - _result = metrics.get_flakes_by_command(date_from, date_to, dashboard) - _t2 = time.perf_counter() - print(f"[perf] flakes_by_command {date_from}..{date_to} | sync={_t1-_t0:.3f}s get_flakes={_t2-_t1:.3f}s total={_t2-_t0:.3f}s", flush=True) - db.cache_set(_ck, _result, _cache_ttl(date_to)) - return _json(_result) - - -# ---- CI Phase timing ---- - -@app.route('/api/ci/phases') -@auth.login_required -def api_ci_phases(): - """CI phase timing breakdown: avg time per phase, by date, and per run.""" - date_from = request.args.get('from', (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d')) - date_to = request.args.get('to', datetime.now().strftime('%Y-%m-%d')) - dashboard = request.args.get('dashboard', '') - run_id = request.args.get('run_id', '') - _ck = f'phases:{date_from}:{date_to}:{dashboard}:{run_id}' - if cached := db.cache_get(_ck): - return _json(cached) - _t0 = time.perf_counter() - _result = metrics.get_phases(date_from, date_to, dashboard, run_id) - _t1 = time.perf_counter() - print(f"[perf] ci_phases {date_from}..{date_to} | get_phases={_t1-_t0:.3f}s total={_t1-_t0:.3f}s", flush=True) - db.cache_set(_ck, _result, _cache_ttl(date_to)) - return _json(_result) - - -# ---- Test timings ---- - -@app.route('/api/tests/timings') -@auth.login_required -def api_test_timings(): - """Test timing statistics: duration by test command, with trends.""" - date_from = request.args.get('from', (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d')) - date_to = request.args.get('to', datetime.now().strftime('%Y-%m-%d')) - dashboard = request.args.get('dashboard', '') - status = request.args.get('status', '') # filter to specific status - test_cmd = request.args.get('test_cmd', '') # filter to specific test - _ck = f'timings:{date_from}:{date_to}:{dashboard}:{status}:{test_cmd}' - _ttl = _cache_ttl(date_to) - if cached := db.cache_get(_ck): - return _json(cached) - _t0 = time.perf_counter() - - # Base WHERE for test_daily_stats - ds_conds = ['date >= ?', 'date <= ?'] - ds_params = [date_from, date_to] - if dashboard: - ds_conds.append('dashboard = ?') - ds_params.append(dashboard) - if test_cmd: - ds_conds.append('test_cmd = ?') - ds_params.append(test_cmd) - ds_where = 'WHERE ' + ' AND '.join(ds_conds) - - if not status: - # Fast path: push GROUP BY into SQL — returns N_tests + N_dates rows, not N_tests*N_dates rows - by_test_rows = db.query(f''' - SELECT test_cmd, MAX(dashboard) as dashboard, - SUM(passed) as passed, SUM(failed) as failed, SUM(flaked) as flaked, - SUM(total_secs) as total_secs, SUM(count_timed) as count_timed, - MIN(min_secs) as min_secs, MAX(max_secs) as max_secs - FROM test_daily_stats {ds_where} - GROUP BY test_cmd - ORDER BY SUM(passed)+SUM(failed)+SUM(flaked) DESC LIMIT 500 - ''', ds_params) - _t1 = time.perf_counter() - - by_date_rows = db.query(f''' - SELECT date, - SUM(passed) as passed, SUM(failed) as failed, SUM(flaked) as flaked, - SUM(total_secs) as total_secs, SUM(count_timed) as count_timed - FROM test_daily_stats {ds_where} - GROUP BY date ORDER BY date - ''', ds_params) - _t2 = time.perf_counter() - - by_test = [] - for t in by_test_rows: - count = (t['passed'] or 0) + (t['failed'] or 0) + (t['flaked'] or 0) - avg_secs = round(t['total_secs'] / t['count_timed'], 1) if t['count_timed'] else None - by_test.append({ - 'test_cmd': t['test_cmd'], 'dashboard': t['dashboard'], 'count': count, - 'passed': t['passed'] or 0, 'failed': t['failed'] or 0, 'flaked': t['flaked'] or 0, - 'pass_rate': round(100.0 * (t['passed'] or 0) / max(count, 1), 1), - 'avg_secs': avg_secs, 'min_secs': t['min_secs'], 'max_secs': t['max_secs'], - 'total_time_secs': round(t['total_secs'] or 0, 0), - }) - - by_date = [] - for d in by_date_rows: - count = (d['passed'] or 0) + (d['failed'] or 0) + (d['flaked'] or 0) - avg_secs = round(d['total_secs'] / d['count_timed'], 1) if d['count_timed'] else None - by_date.append({ - 'date': d['date'], 'passed': d['passed'] or 0, - 'failed': d['failed'] or 0, 'flaked': d['flaked'] or 0, - 'count': count, 'avg_secs': avg_secs, - }) - - total_passed = sum(d['passed'] for d in by_date) - total_failed = sum(d['failed'] for d in by_date) - total_flaked = sum(d['flaked'] for d in by_date) - total_secs_all = sum(d['total_secs'] or 0 for d in by_date_rows) - count_timed_all = sum(d['count_timed'] or 0 for d in by_date_rows) - else: - # Slow fallback: status filter requires scanning test_events - te_conds = ['duration_secs IS NOT NULL', 'duration_secs > 0', - 'timestamp >= ?', "timestamp < ? || 'T23:59:59'"] - te_params = [date_from, date_to] - if dashboard: - te_conds.append('dashboard = ?') - te_params.append(dashboard) - te_conds.append('status = ?') - te_params.append(status) - if test_cmd: - te_conds.append('test_cmd = ?') - te_params.append(test_cmd) - te_where = 'WHERE ' + ' AND '.join(te_conds) - - raw = db.query(f''' - SELECT test_cmd, dashboard, - COUNT(*) as count, - ROUND(AVG(duration_secs),1) as avg_secs, - ROUND(MIN(duration_secs),1) as min_secs, - ROUND(MAX(duration_secs),1) as max_secs, - SUM(duration_secs) as total_secs, - substr(timestamp,1,10) as date - FROM test_events {te_where} - GROUP BY test_cmd - ORDER BY count DESC LIMIT 200 - ''', te_params) - _t1 = time.perf_counter() - by_test = [dict(r, pass_rate=0, passed=0, failed=r['count'] if status=='failed' else 0, - flaked=r['count'] if status=='flaked' else 0, - total_time_secs=round(r['total_secs'] or 0, 0)) for r in raw] - - by_date_raw = db.query(f''' - SELECT substr(timestamp,1,10) as date, COUNT(*) as count - FROM test_events {te_where} - GROUP BY substr(timestamp,1,10) ORDER BY date - ''', te_params) - by_date = [{'date': r['date'], 'count': r['count'], 'passed': 0, - 'failed': r['count'] if status=='failed' else 0, - 'flaked': r['count'] if status=='flaked' else 0} for r in by_date_raw] - - total_passed = 0 - total_failed = sum(r['count'] for r in by_date) if status == 'failed' else 0 - total_flaked = sum(r['count'] for r in by_date) if status == 'flaked' else 0 - total_secs_all = sum(r.get('total_secs') or 0 for r in raw) - count_timed_all = sum(r['count'] for r in raw) - _t2 = time.perf_counter() - - # Slowest individual runs — uses idx_test_events_duration index - sl_conds = ['duration_secs IS NOT NULL', 'duration_secs > 0', - 'timestamp >= ?', "timestamp <= ? || 'T23:59:59'"] - sl_params = [date_from, date_to] - if dashboard: - sl_conds.append('dashboard = ?') - sl_params.append(dashboard) - if test_cmd: - sl_conds.append('test_cmd = ?') - sl_params.append(test_cmd) - sl_where = 'WHERE ' + ' AND '.join(sl_conds) - slowest = db.query(f''' - SELECT test_cmd, status, duration_secs, dashboard, - substr(timestamp,1,10) as date, commit_author, log_url - FROM test_events {sl_where} - ORDER BY duration_secs DESC LIMIT 50 - ''', sl_params) - _t3 = time.perf_counter() - - print(f"[perf] test_timings {date_from}..{date_to} | by_test={_t1-_t0:.3f}s by_date={_t2-_t1:.3f}s slowest={_t3-_t2:.3f}s total={_t3-_t0:.3f}s", flush=True) - _result = { - 'by_test': by_test, - 'by_date': by_date, - 'slowest': slowest, - 'period': {'from': date_from, 'to': date_to}, - 'summary': { - 'total_runs': total_passed + total_failed + total_flaked, - 'avg_duration_secs': round(total_secs_all / count_timed_all, 1) if count_timed_all > 0 else None, - 'max_duration_secs': slowest[0]['duration_secs'] if slowest else None, - 'total_compute_secs': round(total_secs_all, 0), - 'passed': total_passed, - 'failed': total_failed, - 'flaked': total_flaked, - }, - } - db.cache_set(_ck, _result, _ttl) - return _json(_result) - - -# ---- Dashboard views ---- - -@app.route('/ci-health') -@auth.login_required -def ci_health(): - return redirect('/ci-insights') - - -@app.route('/ci-insights') -@auth.login_required -def ci_insights(): - path = Path(__file__).parent / 'views' / 'ci-insights.html' - if path.exists(): - return path.read_text() - return "Dashboard not found", 404 - - -@app.route('/cost-overview') -@auth.login_required -def cost_overview(): - path = Path(__file__).parent / 'views' / 'cost-overview.html' - if path.exists(): - return path.read_text() - return "Dashboard not found", 404 - - -@app.route('/test-timings') -@auth.login_required -def test_timings(): - path = Path(__file__).parent / 'views' / 'test-timings.html' - if path.exists(): - return path.read_text() - return "Dashboard not found", 404 - - -@app.route('/ci-health-report') -@auth.login_required -def ci_health_report(): - path = Path(__file__).parent / 'views' / 'ci-health-report.html' - if path.exists(): - return path.read_text() - return "Report not found", 404 - - -@app.route('/commits') -@auth.login_required -def commits_page(): - path = Path(__file__).parent / 'views' / 'commits.html' - return path.read_text() - - -@app.route('/api/commits') -@auth.login_required -def api_commits(): - branch = request.args.get('branch', 'next') - page = max(1, int(request.args.get('page', 1))) - per_page = min(int(request.args.get('per_page', 50)), 100) - return _json(github_data.get_recent_commits(branch, page, per_page)) - - -@app.route('/flake-prs') -@auth.login_required -def flake_prs(): - path = Path(__file__).parent / 'views' / 'flake-prs.html' - if path.exists(): - return path.read_text() - return "Page not found", 404 - - -@app.route('/api/flake-prs') -@auth.login_required -def api_flake_prs(): - rows = db.query(''' - SELECT pa.pr_number, pa.author, pa.title, pa.branch, - pa.additions, pa.deletions, pa.fetched_at, - MIN(cr.timestamp_ms) as first_seen_ms - FROM pr_authors pa - LEFT JOIN ci_runs cr ON cr.pr_number = pa.pr_number - WHERE ( - pa.title LIKE '%flake%' OR pa.title LIKE '%deflake%' - OR pa.branch LIKE '%flake%' OR pa.branch LIKE '%deflake%' - ) - GROUP BY pa.pr_number - ORDER BY pa.pr_number DESC - LIMIT 200 - ''') - return _json([dict(r) for r in rows]) - - -if __name__ == '__main__': - app.run(host='0.0.0.0', port=8081) diff --git a/ci3/dashboard/ci-metrics/billing/__init__.py b/ci3/dashboard/ci-metrics/billing/__init__.py deleted file mode 100644 index e097751047c..00000000000 --- a/ci3/dashboard/ci-metrics/billing/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -"""Billing package: GKE namespace billing and AWS cost data.""" - -from billing.gcp import ( - get_billing_files_in_range, - aggregate_billing_weekly, - aggregate_billing_monthly, - serve_billing_dashboard, -) -from billing.aws import ( - get_costs_overview, - get_aws_cost_details, - decode_branch_info, - decode_instance_name, -) diff --git a/ci3/dashboard/ci-metrics/billing/aws.py b/ci3/dashboard/ci-metrics/billing/aws.py deleted file mode 100644 index 4dc9061b37d..00000000000 --- a/ci3/dashboard/ci-metrics/billing/aws.py +++ /dev/null @@ -1,382 +0,0 @@ -"""AWS Cost Explorer fetch with in-memory cache. - -Fetches on first request, caches for 6 hours. No SQLite, no background threads. -""" -import threading -import time -from datetime import datetime, timedelta, timezone - -SERVICE_CATEGORY_MAP = { - # Compute - 'Amazon Elastic Compute Cloud - Compute': 'ec2', - 'EC2 - Other': 'ec2', # EBS volumes, snapshots, NAT gateways, data transfer - 'Amazon Elastic Container Service': 'ecs', - 'Amazon Elastic Kubernetes Service': 'eks', - 'Amazon EC2 Container Registry (ECR)': 'ecr', - 'AWS Lambda': 'lambda', - 'Amazon Lightsail': 'lightsail', - # Storage - 'Amazon Simple Storage Service': 's3', - 'Amazon Elastic File System': 'efs', - 'Amazon Elastic Block Store': 'ebs', - 'Amazon ElastiCache': 'elasticache', - 'Amazon Relational Database Service': 'rds', - 'Amazon DynamoDB': 'dynamodb', - 'AWS Backup': 'backup', - # Networking - 'Amazon CloudFront': 'cloudfront', - 'CloudFront Flat-Rate Plans': 'cloudfront', - 'Amazon Virtual Private Cloud': 'vpc', - 'Elastic Load Balancing': 'elb', - 'Amazon Elastic Load Balancing': 'elb', - 'Amazon Route 53': 'route53', - 'Amazon API Gateway': 'apigateway', - 'AWS Data Transfer': 'data_transfer', - 'AWS Global Accelerator': 'global_accelerator', - # Monitoring & Security - 'AmazonCloudWatch': 'cloudwatch', - 'AWS CloudTrail': 'cloudtrail', - 'AWS Secrets Manager': 'secrets', - 'AWS Key Management Service': 'kms', - 'AWS WAF': 'waf', - 'AWS Config': 'config', - 'AWS Certificate Manager': 'acm', - # CI/CD & Dev Tools - 'AWS CodeBuild': 'codebuild', - 'AWS CodePipeline': 'codepipeline', - 'AWS CloudFormation': 'cloudformation', - 'AWS Amplify': 'amplify', - # Data & Analytics - 'AWS Glue': 'glue', - # IoT - 'AWS IoT': 'iot', - 'Amazon Location Service': 'location', - # Messaging - 'Amazon Simple Notification Service': 'sns', - 'Amazon Simple Queue Service': 'sqs', - # Savings Plans / Reserved Instances - 'Savings Plans for AWS Compute usage': 'savings_plans', - # Other - 'Tax': 'tax', - 'AWS Support (Business)': 'support', - 'AWS Support (Enterprise)': 'support', - 'AWS Cost Explorer': 'cost_explorer', -} - -import re - -# One-time contract payments: annual Savings Plan upfronts and monthly Reserved Instance charges. -# These appear as large single-day spikes but are not operational spend. -_ONE_TIME_CATEGORIES = frozenset({ - 'savings_plan_1yr_annual', - 'savings_plan_3yr_annual', - 'savings_plan_1yr_annual_partial', - 'savings_plan_3yr_annual_partial', - 'reserved_instance_monthly', -}) - -_cache = {'rows': [], 'ts': 0} -_cache_lock = threading.Lock() -_detail_cache = {'rows': [], 'ts': 0} -_detail_cache_lock = threading.Lock() -_CACHE_TTL = 6 * 3600 - -# Known job postfixes from ci.sh (these become INSTANCE_POSTFIX) -_JOB_POSTFIXES = re.compile( - r'_(x[0-9]+-(?:full|fast)|a[0-9]+-(?:full|fast)|n-deploy-[0-9]+|grind-test-[a-f0-9]+)$' -) -_ARCH_SUFFIXES = ('_amd64', '_arm64', '_x86_64', '_aarch64') - - -def decode_instance_name(run: dict) -> str: - """Reconstruct the EC2 instance name from CI run metadata. - - bootstrap_ec2 naming: - merge queue: pr-{number}_{arch}[_{postfix}] - branch: {sanitized_branch}_{arch}[_{postfix}] - """ - name = run.get('name', '') - pr = run.get('pr_number') - arch = run.get('arch', 'amd64') - # Normalize arch names - if arch in ('x86_64', 'amd64'): - arch = 'amd64' - elif arch in ('aarch64', 'arm64'): - arch = 'arm64' - job = run.get('job_id', '') - - if '(queue)' in name and pr: - base = f'pr-{pr}_{arch}' - elif pr: - base = f'pr-{pr}_{arch}' - else: - # Replicate: echo -n "$REF_NAME" | head -c 50 | tr -c 'a-zA-Z0-9-' '_' - sanitized = re.sub(r'[^a-zA-Z0-9-]', '_', name[:50]) - base = f'{sanitized}_{arch}' - if job: - return f'{base}_{job}' - return base - - -def decode_branch_info(run: dict) -> dict: - """Extract branch/PR/user context from a CI run.""" - name = run.get('name', '') - dashboard = run.get('dashboard', '') - pr = run.get('pr_number') - author = run.get('author', 'unknown') - - if '(queue)' in name or dashboard == 'next': - run_type = 'merge-queue' - branch = name.replace(' (queue)', '') - elif dashboard == 'prs': - run_type = 'pr' - branch = name - elif dashboard in ('nightly', 'releases', 'network', 'deflake'): - run_type = dashboard - branch = name - else: - run_type = 'other' - branch = name - - return { - 'type': run_type, - 'branch': branch, - 'pr_number': pr, - 'author': author, - 'instance_name': decode_instance_name(run), - } - - -def _fetch_aws_costs(date_from: str, date_to: str) -> list[dict]: - try: - import boto3 - except ImportError: - print("[rk_aws_costs] boto3 not installed, skipping") - return [] - - try: - client = boto3.client('ce', region_name='us-east-2') - rows = [] - next_token = None - - while True: - kwargs = dict( - TimePeriod={'Start': date_from, 'End': date_to}, - Granularity='DAILY', - Metrics=['UnblendedCost'], - GroupBy=[ - {'Type': 'DIMENSION', 'Key': 'SERVICE'}, - {'Type': 'DIMENSION', 'Key': 'USAGE_TYPE'}, - ], - ) - if next_token: - kwargs['NextPageToken'] = next_token - - response = client.get_cost_and_usage(**kwargs) - - for result in response['ResultsByTime']: - date = result['TimePeriod']['Start'] - for group in result['Groups']: - service = group['Keys'][0] - usage_type = group['Keys'][1] if len(group['Keys']) > 1 else '' - amount = float(group['Metrics']['UnblendedCost']['Amount']) - if amount == 0: - continue - category = SERVICE_CATEGORY_MAP.get(service, 'other') - # Savings plans: ComputeSP:1yrAllUpfront, ComputeSP:3yrNoUpfront, etc. - if category == 'savings_plans': - m = re.match(r'ComputeSP:(\d+yr)(\w+)', usage_type) - if m: - term = m.group(1) - payment = m.group(2) - if payment == 'NoUpfront': - category = f'savings_plan_{term}_monthly' - elif 'Upfront' in payment: - category = f'savings_plan_{term}_annual' - # EC2 reserved instances: HeavyUsage: billed monthly on 1st - elif category == 'ec2' and 'HeavyUsage:' in usage_type: - category = 'reserved_instance_monthly' - if category == 'other': - print(f"[rk_aws_costs] unmapped service: {service!r} / {usage_type!r} (${amount:.2f})") - rows.append({ - 'date': date, - 'service': service, - 'category': category, - 'amount_usd': round(amount, 4), - }) - - next_token = response.get('NextPageToken') - if not next_token: - break - - return rows - except Exception as e: - print(f"[rk_aws_costs] Error: {e}") - return [] - - -def _ensure_cached(): - now = time.time() - if _cache['rows'] and now - _cache['ts'] < _CACHE_TTL: - return - if not _cache_lock.acquire(blocking=False): - return - try: - today = datetime.now(timezone.utc).date() - rows = _fetch_aws_costs( - (today - timedelta(days=365)).isoformat(), - today.isoformat(), - ) - if rows: - _cache['rows'] = rows - _cache['ts'] = now - finally: - _cache_lock.release() - - -def get_aws_costs(date_from: str, date_to: str) -> list[dict]: - """Get AWS costs for date range. Blocks on first fetch, async refresh after.""" - if not _cache['rows']: - _ensure_cached() # block on first load so dashboard isn't empty - else: - threading.Thread(target=_ensure_cached, daemon=True).start() - return [r for r in _cache['rows'] if date_from <= r['date'] <= date_to] - - -def _fetch_aws_cost_details(date_from: str, date_to: str) -> list[dict]: - """Fetch per-resource (USAGE_TYPE) cost breakdown from AWS Cost Explorer.""" - try: - import boto3 - except ImportError: - return [] - - try: - client = boto3.client('ce', region_name='us-east-2') - rows = [] - next_token = None - - while True: - kwargs = dict( - TimePeriod={'Start': date_from, 'End': date_to}, - Granularity='DAILY', - Metrics=['UnblendedCost'], - GroupBy=[ - {'Type': 'DIMENSION', 'Key': 'SERVICE'}, - {'Type': 'DIMENSION', 'Key': 'USAGE_TYPE'}, - ], - ) - if next_token: - kwargs['NextPageToken'] = next_token - - response = client.get_cost_and_usage(**kwargs) - - for result in response['ResultsByTime']: - date = result['TimePeriod']['Start'] - for group in result['Groups']: - service = group['Keys'][0] - usage_type = group['Keys'][1] - amount = float(group['Metrics']['UnblendedCost']['Amount']) - if amount == 0: - continue - category = SERVICE_CATEGORY_MAP.get(service, 'other') - rows.append({ - 'date': date, - 'service': service, - 'usage_type': usage_type, - 'category': category, - 'amount_usd': round(amount, 4), - }) - - next_token = response.get('NextPageToken') - if not next_token: - break - - return rows - except Exception as e: - print(f"[rk_aws_costs] Detail fetch error: {e}") - return [] - - -def _ensure_detail_cached(): - now = time.time() - if _detail_cache['rows'] and now - _detail_cache['ts'] < _CACHE_TTL: - return - if not _detail_cache_lock.acquire(blocking=False): - return - try: - today = datetime.now(timezone.utc).date() - rows = _fetch_aws_cost_details( - (today - timedelta(days=365)).isoformat(), - today.isoformat(), - ) - if rows: - _detail_cache['rows'] = rows - _detail_cache['ts'] = now - finally: - _detail_cache_lock.release() - - -def get_aws_cost_details(date_from: str, date_to: str) -> list[dict]: - """Get per-resource AWS cost details. Blocks on first fetch, async refresh after.""" - if not _detail_cache['rows']: - _ensure_detail_cached() - else: - threading.Thread(target=_ensure_detail_cached, daemon=True).start() - return [r for r in _detail_cache['rows'] if date_from <= r['date'] <= date_to] - - -def get_costs_overview(date_from: str, date_to: str) -> dict: - """Combined AWS + GCP cost overview. GCP data comes from billing JSON files.""" - aws_rows = get_aws_costs(date_from, date_to) - - # GCP data from billing files (already on disk, no SQLite needed) - gcp_by_date = {} - try: - from billing.gcp import get_billing_files_in_range - billing_data = get_billing_files_in_range( - datetime.strptime(date_from, '%Y-%m-%d'), - datetime.strptime(date_to, '%Y-%m-%d'), - ) - for entry in billing_data: - d = entry['date'] - if d not in gcp_by_date: - gcp_by_date[d] = {} - for ns_data in entry.get('namespaces', {}).values(): - for cat, amt in ns_data.get('breakdown', {}).items(): - gcp_by_date[d][cat] = gcp_by_date[d].get(cat, 0) + amt - except Exception as e: - print(f"[rk_aws_costs] GCP billing read failed: {e}") - - by_date = {} - for r in aws_rows: - d = r['date'] - if d not in by_date: - by_date[d] = {'date': d, 'aws': {}, 'gcp': {}, 'aws_total': 0, 'gcp_total': 0, 'aws_one_time': 0} - cat = r['category'] - by_date[d]['aws'][cat] = by_date[d]['aws'].get(cat, 0) + r['amount_usd'] - by_date[d]['aws_total'] += r['amount_usd'] - if cat in _ONE_TIME_CATEGORIES: - by_date[d]['aws_one_time'] += r['amount_usd'] - - for d, cats in gcp_by_date.items(): - if d not in by_date: - by_date[d] = {'date': d, 'aws': {}, 'gcp': {}, 'aws_total': 0, 'gcp_total': 0, 'aws_one_time': 0} - by_date[d]['gcp'] = cats - by_date[d]['gcp_total'] = sum(cats.values()) - - sorted_dates = sorted(by_date.values(), key=lambda x: x['date']) - aws_total = sum(d['aws_total'] for d in sorted_dates) - aws_one_time = sum(d['aws_one_time'] for d in sorted_dates) - gcp_total = sum(d['gcp_total'] for d in sorted_dates) - - return { - 'by_date': sorted_dates, - 'totals': { - 'aws': round(aws_total, 2), - 'aws_operational': round(aws_total - aws_one_time, 2), - 'aws_one_time': round(aws_one_time, 2), - 'gcp': round(gcp_total, 2), - 'combined': round(aws_total + gcp_total, 2), - 'combined_operational': round(aws_total - aws_one_time + gcp_total, 2), - } - } diff --git a/ci3/dashboard/ci-metrics/billing/billing-dashboard.html b/ci3/dashboard/ci-metrics/billing/billing-dashboard.html deleted file mode 100644 index 87193ffae20..00000000000 --- a/ci3/dashboard/ci-metrics/billing/billing-dashboard.html +++ /dev/null @@ -1,415 +0,0 @@ - - - - - ACI - Namespace Billing - - - - -

namespace billing

- -
- - - - | - - - | - - - - | - - - - - - | - - - -
- -
- -
- -
-
-

cost over time

-
-
-
-

cost by namespace

-
-
-
-

cost by category

-
-
-
- - - - -
- - - - - diff --git a/ci3/dashboard/ci-metrics/billing/explore.py b/ci3/dashboard/ci-metrics/billing/explore.py deleted file mode 100644 index c591d8c847e..00000000000 --- a/ci3/dashboard/ci-metrics/billing/explore.py +++ /dev/null @@ -1,619 +0,0 @@ -#!/usr/bin/env python3 -"""CLI tool to explore GCP billing data from the Cloud Billing BigQuery export. - -Queries the actual billing export tables (not usage metering) to get real -invoice-level costs. Caches results in SQLite for fast re-queries. - -Usage: - python billing_explore.py discover # find billing export tables - python billing_explore.py fetch [--months N] # fetch & cache billing data - python billing_explore.py monthly # show monthly totals - python billing_explore.py monthly --by service # monthly by service - python billing_explore.py monthly --by sku # monthly by SKU - python billing_explore.py monthly --by project # monthly by project - python billing_explore.py daily [--month 2024-12] # daily for a month - python billing_explore.py top [--month 2024-12] # top costs for a month - python billing_explore.py compare # compare billing export vs usage metering -""" -import argparse -import os -import sqlite3 -import sys -from datetime import datetime, timedelta, timezone - -DB_PATH = os.path.join(os.getenv('LOGS_DISK_PATH', '/tmp'), 'billing_explore.db') - -SCHEMA = """ -CREATE TABLE IF NOT EXISTS gcp_billing ( - date TEXT NOT NULL, - project_id TEXT NOT NULL DEFAULT '', - service TEXT NOT NULL DEFAULT '', - sku TEXT NOT NULL DEFAULT '', - cost REAL NOT NULL DEFAULT 0, - credits REAL NOT NULL DEFAULT 0, - usage_amount REAL NOT NULL DEFAULT 0, - usage_unit TEXT NOT NULL DEFAULT '', - currency TEXT NOT NULL DEFAULT 'USD', - fetched_at TEXT NOT NULL, - PRIMARY KEY (date, project_id, service, sku) -); -CREATE INDEX IF NOT EXISTS idx_gcp_billing_date ON gcp_billing(date); -CREATE INDEX IF NOT EXISTS idx_gcp_billing_service ON gcp_billing(service); - -CREATE TABLE IF NOT EXISTS gcp_billing_meta ( - key TEXT PRIMARY KEY, - value TEXT -); -""" - - -def get_db(): - os.makedirs(os.path.dirname(DB_PATH) or '.', exist_ok=True) - conn = sqlite3.connect(DB_PATH) - conn.execute('PRAGMA busy_timeout = 5000') - conn.row_factory = sqlite3.Row - conn.executescript(SCHEMA) - return conn - - -def fmt_usd(v): - if v >= 1000: - return f'${v:,.0f}' - if v >= 1: - return f'${v:,.2f}' - return f'${v:,.4f}' - - -# ---- BigQuery Discovery ---- - -def cmd_discover(args): - """Find billing export tables in the project.""" - from google.cloud import bigquery - project = args.project - client = bigquery.Client(project=project) - - print(f'Listing datasets in project: {project}') - datasets = list(client.list_datasets()) - if not datasets: - print(' No datasets found.') - return - - for ds in datasets: - ds_id = ds.dataset_id - tables = list(client.list_tables(ds.reference)) - billing_tables = [t for t in tables if 'billing' in t.table_id.lower() or 'cost' in t.table_id.lower()] - if billing_tables: - print(f'\n Dataset: {ds_id}') - for t in billing_tables: - full = f'{project}.{ds_id}.{t.table_id}' - print(f' {full}') - # Show schema for first billing table - tbl = client.get_table(t.reference) - print(f' rows: {tbl.num_rows}, size: {tbl.num_bytes / 1e6:.1f} MB') - print(f' columns: {", ".join(f.name for f in tbl.schema[:15])}') - else: - # Check for usage metering tables too - usage_tables = [t for t in tables if 'gke_cluster' in t.table_id.lower()] - if usage_tables: - print(f'\n Dataset: {ds_id} (usage metering)') - for t in usage_tables: - print(f' {project}.{ds_id}.{t.table_id}') - - # Also try common billing export naming patterns - print(f'\n Trying common billing export table patterns...') - for ds in datasets: - for t in client.list_tables(ds.reference): - if t.table_id.startswith('gcp_billing_export'): - full = f'{project}.{ds.dataset_id}.{t.table_id}' - print(f' FOUND: {full}') - - -# ---- BigQuery Fetch ---- - -def cmd_fetch(args): - """Fetch billing data from BigQuery and cache in SQLite.""" - from google.cloud import bigquery - - table = args.table - project = args.project - months = args.months - - if not table: - print('ERROR: --table is required. Run "discover" first to find the billing export table.') - print(' e.g. --table project.dataset.gcp_billing_export_resource_v1_XXXXXX') - sys.exit(1) - - client = bigquery.Client(project=project) - end_date = datetime.now(timezone.utc).date() - start_date = end_date - timedelta(days=months * 31) - - print(f'Fetching billing data from {start_date} to {end_date}') - print(f'Table: {table}') - - # Query the billing export table - # The standard billing export has: billing_account_id, service.description, - # sku.description, usage_start_time, project.id, cost, credits, usage.amount, usage.unit - query = f""" - SELECT - DATE(usage_start_time) AS date, - COALESCE(project.id, '') AS project_id, - COALESCE(service.description, '') AS service, - COALESCE(sku.description, '') AS sku, - SUM(cost) AS cost, - SUM(IFNULL((SELECT SUM(c.amount) FROM UNNEST(credits) c), 0)) AS credits, - SUM(usage.amount) AS usage_amount, - MAX(usage.unit) AS usage_unit - FROM `{table}` - WHERE DATE(usage_start_time) BETWEEN @start_date AND @end_date - GROUP BY date, project_id, service, sku - HAVING ABS(cost) > 0.0001 OR ABS(credits) > 0.0001 - ORDER BY date, service, sku - """ - - job_config = bigquery.QueryJobConfig( - query_parameters=[ - bigquery.ScalarQueryParameter('start_date', 'DATE', start_date.isoformat()), - bigquery.ScalarQueryParameter('end_date', 'DATE', end_date.isoformat()), - ] - ) - - print('Running query...') - result = list(client.query(query, job_config=job_config).result()) - print(f'Got {len(result)} rows') - - if not result: - print('No data returned. Check table name and date range.') - return - - # Store in SQLite - db = get_db() - now = datetime.now(timezone.utc).isoformat() - - db.execute('DELETE FROM gcp_billing WHERE date >= ? AND date <= ?', - (start_date.isoformat(), end_date.isoformat())) - - for row in result: - db.execute(''' - INSERT OR REPLACE INTO gcp_billing - (date, project_id, service, sku, cost, credits, usage_amount, usage_unit, fetched_at) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) - ''', ( - row.date.isoformat() if hasattr(row.date, 'isoformat') else str(row.date), - row.project_id or '', - row.service or '', - row.sku or '', - float(row.cost or 0), - float(row.credits or 0), - float(row.usage_amount or 0), - row.usage_unit or '', - now, - )) - - db.commit() - db.execute("INSERT OR REPLACE INTO gcp_billing_meta VALUES ('last_fetch', ?)", (now,)) - db.execute("INSERT OR REPLACE INTO gcp_billing_meta VALUES ('table', ?)", (table,)) - db.commit() - - print(f'Cached {len(result)} rows in {DB_PATH}') - - # Show quick summary - rows = db.execute(''' - SELECT substr(date, 1, 7) as month, SUM(cost) as cost, SUM(credits) as credits - FROM gcp_billing GROUP BY month ORDER BY month - ''').fetchall() - print(f'\n{"Month":<10} {"Gross":>12} {"Credits":>12} {"Net":>12}') - print('-' * 48) - for r in rows: - net = r['cost'] + r['credits'] - print(f'{r["month"]:<10} {fmt_usd(r["cost"]):>12} {fmt_usd(r["credits"]):>12} {fmt_usd(net):>12}') - - -# ---- Reports ---- - -def cmd_monthly(args): - """Show monthly totals.""" - db = get_db() - group_by = args.by - - if group_by == 'service': - rows = db.execute(''' - SELECT substr(date, 1, 7) as month, service, - SUM(cost) as cost, SUM(credits) as credits - FROM gcp_billing GROUP BY month, service ORDER BY month, cost DESC - ''').fetchall() - - current_month = None - for r in rows: - if r['month'] != current_month: - current_month = r['month'] - month_total = sum(row['cost'] + row['credits'] for row in rows if row['month'] == current_month) - print(f'\n {current_month} (net: {fmt_usd(month_total)})') - print(f' {"Service":<45} {"Gross":>10} {"Credits":>10} {"Net":>10}') - print(' ' + '-' * 77) - net = r['cost'] + r['credits'] - if abs(net) >= 0.01: - print(f' {r["service"]:<45} {fmt_usd(r["cost"]):>10} {fmt_usd(r["credits"]):>10} {fmt_usd(net):>10}') - - elif group_by == 'sku': - month_filter = args.month - if not month_filter: - # Use most recent month - row = db.execute('SELECT MAX(substr(date, 1, 7)) as m FROM gcp_billing').fetchone() - month_filter = row['m'] if row else None - - if not month_filter: - print('No data.') - return - - rows = db.execute(''' - SELECT service, sku, SUM(cost) as cost, SUM(credits) as credits, - SUM(usage_amount) as usage_amount, MAX(usage_unit) as usage_unit - FROM gcp_billing WHERE substr(date, 1, 7) = ? - GROUP BY service, sku ORDER BY cost DESC - ''', (month_filter,)).fetchall() - - total = sum(r['cost'] + r['credits'] for r in rows) - print(f'\n {month_filter} (net: {fmt_usd(total)})') - print(f' {"Service":<30} {"SKU":<40} {"Net":>10} {"Usage":>15}') - print(' ' + '-' * 97) - for r in rows[:40]: - net = r['cost'] + r['credits'] - if abs(net) >= 0.01: - usage = f'{r["usage_amount"]:.1f} {r["usage_unit"]}' if r['usage_amount'] else '' - print(f' {r["service"][:29]:<30} {r["sku"][:39]:<40} {fmt_usd(net):>10} {usage:>15}') - - elif group_by == 'project': - rows = db.execute(''' - SELECT substr(date, 1, 7) as month, project_id, - SUM(cost) as cost, SUM(credits) as credits - FROM gcp_billing GROUP BY month, project_id ORDER BY month, cost DESC - ''').fetchall() - - current_month = None - for r in rows: - if r['month'] != current_month: - current_month = r['month'] - month_total = sum(row['cost'] + row['credits'] for row in rows if row['month'] == current_month) - print(f'\n {current_month} (net: {fmt_usd(month_total)})') - print(f' {"Project":<45} {"Net":>12}') - print(' ' + '-' * 59) - net = r['cost'] + r['credits'] - if abs(net) >= 0.01: - print(f' {r["project_id"]:<45} {fmt_usd(net):>12}') - - else: - # Default: just monthly totals - rows = db.execute(''' - SELECT substr(date, 1, 7) as month, - SUM(cost) as cost, SUM(credits) as credits, - COUNT(DISTINCT date) as days - FROM gcp_billing GROUP BY month ORDER BY month - ''').fetchall() - - print(f'\n {"Month":<10} {"Gross":>12} {"Credits":>12} {"Net":>12} {"Days":>6} {"Daily Avg":>12}') - print(' ' + '-' * 68) - grand_total = 0 - for r in rows: - net = r['cost'] + r['credits'] - daily = net / max(r['days'], 1) - grand_total += net - print(f' {r["month"]:<10} {fmt_usd(r["cost"]):>12} {fmt_usd(r["credits"]):>12} {fmt_usd(net):>12} {r["days"]:>6} {fmt_usd(daily):>12}') - print(' ' + '-' * 68) - print(f' {"TOTAL":<10} {"":>12} {"":>12} {fmt_usd(grand_total):>12}') - - -def cmd_daily(args): - """Show daily costs for a month.""" - db = get_db() - month = args.month - if not month: - row = db.execute('SELECT MAX(substr(date, 1, 7)) as m FROM gcp_billing').fetchone() - month = row['m'] if row else None - - if not month: - print('No data.') - return - - rows = db.execute(''' - SELECT date, SUM(cost) as cost, SUM(credits) as credits - FROM gcp_billing WHERE substr(date, 1, 7) = ? - GROUP BY date ORDER BY date - ''', (month,)).fetchall() - - total = 0 - print(f'\n {"Date":<12} {"Gross":>10} {"Credits":>10} {"Net":>10}') - print(' ' + '-' * 44) - for r in rows: - net = r['cost'] + r['credits'] - total += net - print(f' {r["date"]:<12} {fmt_usd(r["cost"]):>10} {fmt_usd(r["credits"]):>10} {fmt_usd(net):>10}') - print(' ' + '-' * 44) - print(f' {"TOTAL":<12} {"":>10} {"":>10} {fmt_usd(total):>10}') - - -def cmd_top(args): - """Show top cost items for a month.""" - db = get_db() - month = args.month - if not month: - row = db.execute('SELECT MAX(substr(date, 1, 7)) as m FROM gcp_billing').fetchone() - month = row['m'] if row else None - - if not month: - print('No data.') - return - - # Top services - services = db.execute(''' - SELECT service, SUM(cost + credits) as net, SUM(cost) as gross - FROM gcp_billing WHERE substr(date, 1, 7) = ? - GROUP BY service ORDER BY net DESC LIMIT 15 - ''', (month,)).fetchall() - - total = sum(r['net'] for r in services) - print(f'\n Top services for {month} (total: {fmt_usd(total)})') - print(f' {"Service":<45} {"Net":>12} {"% of Total":>10}') - print(' ' + '-' * 69) - for r in services: - pct = 100 * r['net'] / max(total, 0.01) - if abs(r['net']) >= 0.01: - print(f' {r["service"]:<45} {fmt_usd(r["net"]):>12} {pct:>9.1f}%') - - # Top SKUs - skus = db.execute(''' - SELECT service, sku, SUM(cost + credits) as net - FROM gcp_billing WHERE substr(date, 1, 7) = ? - GROUP BY service, sku ORDER BY net DESC LIMIT 20 - ''', (month,)).fetchall() - - print(f'\n Top SKUs for {month}') - print(f' {"Service":<25} {"SKU":<40} {"Net":>12}') - print(' ' + '-' * 79) - for r in skus: - if abs(r['net']) >= 0.01: - print(f' {r["service"][:24]:<25} {r["sku"][:39]:<40} {fmt_usd(r["net"]):>12}') - - -def cmd_compare(args): - """Compare billing export data vs usage metering estimates.""" - db = get_db() - - # Get billing export monthly totals - billing_rows = db.execute(''' - SELECT substr(date, 1, 7) as month, SUM(cost + credits) as net - FROM gcp_billing GROUP BY month ORDER BY month - ''').fetchall() - - if not billing_rows: - print('No billing export data cached. Run "fetch" first.') - return - - # Get usage metering estimates - try: - from billing import gcp as _gcp_billing - _gcp_billing._ensure_cached() - metering_data = _gcp_billing._cache.get('data', []) - except Exception as e: - print(f'Could not load usage metering data: {e}') - metering_data = [] - - metering_monthly = {} - for entry in metering_data: - month = entry['date'][:7] - day_total = sum(ns.get('total', 0) for ns in entry.get('namespaces', {}).values()) - metering_monthly[month] = metering_monthly.get(month, 0) + day_total - - print(f'\n {"Month":<10} {"Billing Export":>15} {"Usage Metering":>15} {"Ratio":>8}') - print(' ' + '-' * 50) - for r in billing_rows: - billing = r['net'] - metering = metering_monthly.get(r['month'], 0) - ratio = f'{billing / metering:.2f}x' if metering > 0 else '--' - print(f' {r["month"]:<10} {fmt_usd(billing):>15} {fmt_usd(metering):>15} {ratio:>8}') - - -def cmd_status(args): - """Show what data we have cached.""" - db = get_db() - meta = {r['key']: r['value'] for r in db.execute('SELECT * FROM gcp_billing_meta').fetchall()} - billing_count = db.execute('SELECT COUNT(*) as c FROM gcp_billing').fetchone()['c'] - billing_range = db.execute('SELECT MIN(date) as mn, MAX(date) as mx FROM gcp_billing').fetchone() - - print(f'\n Billing export cache:') - print(f' DB path: {DB_PATH}') - print(f' Table: {meta.get("table", "(not set)")}') - print(f' Last fetch: {meta.get("last_fetch", "(never)")}') - print(f' Rows: {billing_count}') - if billing_count: - print(f' Date range: {billing_range["mn"]} to {billing_range["mx"]}') - - # Also check billing export table status - try: - from google.cloud import bigquery - client = bigquery.Client(project=args.project) - table_id = 'testnet-440309.testnet440309billing.gcp_billing_export_v1_01EA8B_291C89_753ABC' - t = client.get_table(table_id) - print(f'\n BigQuery billing export:') - print(f' Table: {table_id}') - print(f' Rows: {t.num_rows}') - print(f' Modified: {t.modified}') - if t.num_rows > 0: - print(f' STATUS: Data available! Run "fetch --table {table_id}" to cache it.') - else: - print(f' STATUS: Not yet populated. GCP takes up to 24h after enabling export.') - except Exception as e: - print(f'\n BigQuery check failed: {e}') - - -def cmd_metering(args): - """Query both usage metering tables and compare with different approaches.""" - from google.cloud import bigquery - project = args.project - client = bigquery.Client(project=project) - months = args.months - - end_date = datetime.now(timezone.utc).date() - start_date = end_date - timedelta(days=months * 31) - - # Table names - usage_table = f'{project}.egress_consumption.gke_cluster_resource_usage' - consumption_table = f'{project}.egress_consumption.gke_cluster_resource_consumption' - - print(f'Date range: {start_date} to {end_date}') - - # 1. Current approach: usage table with our SKU pricing - print('\n=== Approach 1: gke_cluster_resource_usage (requests) with hardcoded SKU prices ===') - _query_metering_table(client, usage_table, start_date, end_date, 'REQUESTS') - - # 2. Consumption table with our SKU pricing - print('\n=== Approach 2: gke_cluster_resource_consumption (actual) with hardcoded SKU prices ===') - _query_metering_table(client, consumption_table, start_date, end_date, 'CONSUMPTION') - - # 3. Raw totals: what does each table report? - print('\n=== Approach 3: Raw resource totals from both tables ===') - for tname, label in [(usage_table, 'REQUESTS'), (consumption_table, 'CONSUMPTION')]: - query = f""" - SELECT - FORMAT_DATE('%Y-%m', DATE(start_time)) AS month, - resource_name, - SUM(usage.amount) AS total_amount, - usage.unit - FROM `{tname}` - WHERE DATE(start_time) BETWEEN @start AND @end - GROUP BY month, resource_name, usage.unit - ORDER BY month, resource_name - """ - job_config = bigquery.QueryJobConfig(query_parameters=[ - bigquery.ScalarQueryParameter('start', 'DATE', start_date.isoformat()), - bigquery.ScalarQueryParameter('end', 'DATE', end_date.isoformat()), - ]) - rows = list(client.query(query, job_config=job_config).result()) - print(f'\n {label} table raw resources:') - print(f' {"Month":<10} {"Resource":<20} {"Amount":>20} {"Unit":<15}') - print(' ' + '-' * 67) - for r in rows: - print(f' {r.month:<10} {r.resource_name:<20} {r.total_amount:>20,.0f} {r.unit:<15}') - - # 4. Count distinct SKUs - print('\n=== Approach 4: Distinct SKUs in usage table ===') - query = f""" - SELECT sku_id, resource_name, COUNT(*) as row_count, - SUM(usage.amount) as total_amount, usage.unit - FROM `{usage_table}` - WHERE DATE(start_time) BETWEEN @start AND @end - GROUP BY sku_id, resource_name, usage.unit - ORDER BY total_amount DESC - """ - job_config = bigquery.QueryJobConfig(query_parameters=[ - bigquery.ScalarQueryParameter('start', 'DATE', start_date.isoformat()), - bigquery.ScalarQueryParameter('end', 'DATE', end_date.isoformat()), - ]) - rows = list(client.query(query, job_config=job_config).result()) - # Import pricing to check - from billing.gcp import _SKU_PRICING - print(f' {"SKU ID":<20} {"Resource":<20} {"Rows":>10} {"Amount":>18} {"Unit":<12} {"Known?"}') - print(' ' + '-' * 90) - for r in rows: - known = 'YES' if r.sku_id in _SKU_PRICING else 'MISSING' - print(f' {r.sku_id:<20} {r.resource_name:<20} {r.row_count:>10,} {r.total_amount:>18,.0f} {r.unit:<12} {known}') - - -def _query_metering_table(client, table, start_date, end_date, label): - """Query a metering table and compute costs using our SKU pricing.""" - from google.cloud import bigquery - from billing.gcp import _SKU_PRICING, _usage_to_cost - - query = f""" - SELECT - FORMAT_DATE('%Y-%m', DATE(start_time)) AS month, - namespace, - sku_id, - resource_name, - SUM(usage.amount) AS total_usage - FROM `{table}` - WHERE DATE(start_time) BETWEEN @start AND @end - GROUP BY month, namespace, sku_id, resource_name - ORDER BY month, namespace - """ - job_config = bigquery.QueryJobConfig(query_parameters=[ - bigquery.ScalarQueryParameter('start', 'DATE', start_date.isoformat()), - bigquery.ScalarQueryParameter('end', 'DATE', end_date.isoformat()), - ]) - rows = list(client.query(query, job_config=job_config).result()) - - monthly = {} - monthly_by_cat = {} - missing_skus = set() - for r in rows: - cost, category = _usage_to_cost(r.sku_id, r.resource_name, float(r.total_usage)) - if r.sku_id not in _SKU_PRICING: - missing_skus.add(r.sku_id) - month = r.month - monthly[month] = monthly.get(month, 0) + cost - key = (month, category) - monthly_by_cat[key] = monthly_by_cat.get(key, 0) + cost - - print(f' {"Month":<10} {"Total":>12} {"compute_spot":>14} {"compute_od":>14} {"network":>10} {"storage":>10}') - print(' ' + '-' * 74) - for month in sorted(monthly.keys()): - total = monthly[month] - spot = monthly_by_cat.get((month, 'compute_spot'), 0) - od = monthly_by_cat.get((month, 'compute_ondemand'), 0) - net = monthly_by_cat.get((month, 'network'), 0) - stor = monthly_by_cat.get((month, 'storage'), 0) - print(f' {month:<10} {fmt_usd(total):>12} {fmt_usd(spot):>14} {fmt_usd(od):>14} {fmt_usd(net):>10} {fmt_usd(stor):>10}') - - if missing_skus: - print(f'\n WARNING: {len(missing_skus)} unknown SKU IDs (not priced): {", ".join(sorted(missing_skus)[:5])}...') - - -# ---- Main ---- - -def main(): - parser = argparse.ArgumentParser(description='Explore GCP billing data') - parser.add_argument('--project', default='testnet-440309', help='GCP project ID') - parser.add_argument('--table', default='', help='BigQuery billing export table') - sub = parser.add_subparsers(dest='command') - - sub.add_parser('discover', help='Find billing export tables') - - fetch_p = sub.add_parser('fetch', help='Fetch billing data from BigQuery') - fetch_p.add_argument('--months', type=int, default=6, help='How many months back to fetch') - - monthly_p = sub.add_parser('monthly', help='Monthly totals') - monthly_p.add_argument('--by', choices=['service', 'sku', 'project'], default='', help='Group by') - monthly_p.add_argument('--month', default='', help='Filter to month (YYYY-MM)') - - daily_p = sub.add_parser('daily', help='Daily costs') - daily_p.add_argument('--month', default='', help='Month to show (YYYY-MM)') - - top_p = sub.add_parser('top', help='Top cost items') - top_p.add_argument('--month', default='', help='Month to show (YYYY-MM)') - - sub.add_parser('compare', help='Compare billing export vs usage metering') - sub.add_parser('status', help='Show data status (what we have cached)') - - meter_p = sub.add_parser('metering', help='Query both metering tables directly and compare') - meter_p.add_argument('--months', type=int, default=6, help='How many months back') - - args = parser.parse_args() - - if not args.command: - parser.print_help() - sys.exit(1) - - cmds = { - 'discover': cmd_discover, - 'fetch': cmd_fetch, - 'monthly': cmd_monthly, - 'daily': cmd_daily, - 'top': cmd_top, - 'compare': cmd_compare, - 'metering': cmd_metering, - 'status': cmd_status, - } - cmds[args.command](args) - - -if __name__ == '__main__': - main() diff --git a/ci3/dashboard/ci-metrics/billing/fetch_billing.py b/ci3/dashboard/ci-metrics/billing/fetch_billing.py deleted file mode 100644 index 271a788fc6b..00000000000 --- a/ci3/dashboard/ci-metrics/billing/fetch_billing.py +++ /dev/null @@ -1,262 +0,0 @@ -#!/usr/bin/env python3 -"""Fetch namespace billing data from GKE resource consumption metering in BigQuery. - -Queries the GKE cluster resource consumption table which records CPU and memory -usage per namespace per pod. Actual GCP SKU prices (from the Cloud Billing -Catalog API) are applied to convert resource usage into dollar costs. - -Categories produced: - - compute_spot (Spot / Preemptible VM cores + RAM) - - compute_ondemand (On-demand VM cores + RAM) - -Usage: - # Fetch last 30 days - python fetch-billing.py - - # Specific range - python fetch-billing.py --from 2026-01-01 --to 2026-01-31 - - # Custom output directory - python fetch-billing.py --output-dir /tmp/billing - -Environment: - Requires Application Default Credentials or GOOGLE_APPLICATION_CREDENTIALS. - pip install google-cloud-bigquery -""" -import argparse -import json -import os -import sys -from datetime import datetime, timedelta - -from google.cloud import bigquery - -# ---- defaults ---- -DEFAULT_PROJECT = 'testnet-440309' -DEFAULT_DATASET = 'egress_consumption' -DEFAULT_TABLE_CONSUMPTION = 'gke_cluster_resource_consumption' -DEFAULT_TABLE_USAGE = 'gke_cluster_resource_usage' -DEFAULT_OUTPUT_DIR = os.path.join( - os.getenv('LOGS_DISK_PATH', '/logs-disk'), 'billing' -) - -# ---- SKU pricing ---- -# Prices sourced from GCP Cloud Billing Catalog API for us-west1. -SKU_PRICING = { - # Compute - Spot (per vCPU-hour / per GiB-hour) - 'E7FF-A0FB-FA82': {'price': 0.00497, 'resource': 'cpu', 'category': 'compute_spot'}, - '48AB-89F5-9112': {'price': 0.000668, 'resource': 'memory', 'category': 'compute_spot'}, - # Compute - On-demand T2D - 'EFE6-E23C-19CB': {'price': 0.027502, 'resource': 'cpu', 'category': 'compute_ondemand'}, - 'FB05-036A-8982': {'price': 0.003686, 'resource': 'memory', 'category': 'compute_ondemand'}, - # Compute - On-demand N2 - 'BB77-5FDA-69D9': {'price': 0.031611, 'resource': 'cpu', 'category': 'compute_ondemand'}, - '5B01-D157-A097': {'price': 0.004237, 'resource': 'memory', 'category': 'compute_ondemand'}, - # Compute - On-demand N2D - 'A03E-E620-7389': {'price': 0.027502, 'resource': 'cpu', 'category': 'compute_ondemand'}, - '5535-6D2D-4B50': {'price': 0.003686, 'resource': 'memory', 'category': 'compute_ondemand'}, - # Network Egress (per GiB) - '0C3C-6B13-B1E8': {'price': 0.02, 'resource': 'networkEgress', 'category': 'network'}, - '6B8F-E63D-832B': {'price': 0.0, 'resource': 'networkEgress', 'category': 'network'}, - '92CB-C25F-B1D1': {'price': 0.0, 'resource': 'networkEgress', 'category': 'network'}, - '984A-1F27-2D1F': {'price': 0.04, 'resource': 'networkEgress', 'category': 'network'}, - '9DE9-9092-B3BC': {'price': 0.20, 'resource': 'networkEgress', 'category': 'network'}, - 'C863-37DA-506E': {'price': 0.02, 'resource': 'networkEgress', 'category': 'network'}, - 'C8EA-1A86-3D28': {'price': 0.02, 'resource': 'networkEgress', 'category': 'network'}, - 'DE9E-AFBC-A15A': {'price': 0.01, 'resource': 'networkEgress', 'category': 'network'}, - 'DFA5-B5C6-36D6': {'price': 0.085, 'resource': 'networkEgress', 'category': 'network'}, - 'F274-1692-F213': {'price': 0.08, 'resource': 'networkEgress', 'category': 'network'}, - 'FDBC-6E3B-D4D8': {'price': 0.15, 'resource': 'networkEgress', 'category': 'network'}, - # Storage (per GiB-month) - 'D973-5D65-BAB2': {'price': 0.04, 'resource': 'storage', 'category': 'storage'}, -} - - -def usage_to_cost(sku_id: str, resource_name: str, amount: float) -> tuple[float, str]: - """Convert raw usage amount to dollar cost. Returns (cost_usd, category).""" - info = SKU_PRICING.get(sku_id) - if not info: - return 0.0, 'other' - - price = info['price'] - if resource_name == 'cpu': - return (amount / 3600.0) * price, info['category'] - elif resource_name == 'memory': - return (amount / 3600.0 / (1024 ** 3)) * price, info['category'] - elif resource_name.startswith('networkEgress'): - return (amount / (1024 ** 3)) * price, info['category'] - elif resource_name == 'storage': - gib_months = amount / (1024 ** 3) / (730 * 3600) - return gib_months * price, info['category'] - return 0.0, info['category'] - - -# ---- BigQuery query ---- - -def fetch_usage_rows( - client: bigquery.Client, - project: str, - dataset: str, - date_from: str, - date_to: str, -) -> list[dict]: - """Query both metering tables for daily usage by namespace + SKU.""" - consumption = f'{project}.{dataset}.{DEFAULT_TABLE_CONSUMPTION}' - usage = f'{project}.{dataset}.{DEFAULT_TABLE_USAGE}' - query = f""" - SELECT date, namespace, sku_id, resource_name, SUM(total_usage) AS total_usage FROM ( - SELECT DATE(start_time) AS date, namespace, sku_id, resource_name, SUM(usage.amount) AS total_usage - FROM `{consumption}` - WHERE DATE(start_time) BETWEEN @date_from AND @date_to - GROUP BY date, namespace, sku_id, resource_name - UNION ALL - SELECT DATE(start_time) AS date, namespace, sku_id, resource_name, SUM(usage.amount) AS total_usage - FROM `{usage}` - WHERE DATE(start_time) BETWEEN @date_from AND @date_to - AND resource_name IN ('networkEgress', 'storage') - GROUP BY date, namespace, sku_id, resource_name - ) - GROUP BY date, namespace, sku_id, resource_name - ORDER BY date, namespace - """ - job_config = bigquery.QueryJobConfig( - query_parameters=[ - bigquery.ScalarQueryParameter('date_from', 'DATE', date_from), - bigquery.ScalarQueryParameter('date_to', 'DATE', date_to), - ] - ) - rows = client.query(query, job_config=job_config).result() - return [dict(row) for row in rows] - - -# ---- aggregate into daily JSON ---- - -def build_daily_files(rows: list[dict]) -> tuple[dict[str, dict], set[str]]: - """Convert raw usage rows into daily billing JSON structures. - - Returns (days_dict, unknown_skus). - """ - days: dict[str, dict] = {} - unknown_skus: set[str] = set() - - for row in rows: - date_str = ( - row['date'].isoformat() - if hasattr(row['date'], 'isoformat') - else str(row['date']) - ) - ns = row['namespace'] - sku_id = row['sku_id'] - resource_name = row['resource_name'] - amount = float(row['total_usage']) - - cost, category = usage_to_cost(sku_id, resource_name, amount) - - if sku_id not in SKU_PRICING: - unknown_skus.add(sku_id) - - if cost <= 0: - continue - - if date_str not in days: - days[date_str] = {'date': date_str, 'namespaces': {}} - if ns not in days[date_str]['namespaces']: - days[date_str]['namespaces'][ns] = {'total': 0, 'breakdown': {}} - - entry = days[date_str]['namespaces'][ns] - entry['breakdown'][category] = ( - entry['breakdown'].get(category, 0) + cost - ) - entry['total'] += cost - - # Round - for day in days.values(): - for ns_data in day['namespaces'].values(): - ns_data['total'] = round(ns_data['total'], 4) - ns_data['breakdown'] = { - k: round(v, 4) for k, v in ns_data['breakdown'].items() - } - - return days, unknown_skus - - -def write_files(days: dict[str, dict], output_dir: str) -> int: - os.makedirs(output_dir, exist_ok=True) - count = 0 - for date_str, data in sorted(days.items()): - filepath = os.path.join(output_dir, f'{date_str}.json') - with open(filepath, 'w') as f: - json.dump(data, f, indent=2) - count += 1 - return count - - -# ---- CLI ---- - -def main(): - parser = argparse.ArgumentParser( - description='Fetch GKE namespace compute billing from resource consumption metering' - ) - today = datetime.utcnow().strftime('%Y-%m-%d') - default_from = (datetime.utcnow() - timedelta(days=30)).strftime('%Y-%m-%d') - - parser.add_argument('--from', dest='date_from', default=default_from, - help='Start date YYYY-MM-DD (default: 30 days ago)') - parser.add_argument('--to', dest='date_to', default=today, - help='End date YYYY-MM-DD (default: today)') - parser.add_argument('--project', default=DEFAULT_PROJECT, - help=f'GCP project ID (default: {DEFAULT_PROJECT})') - parser.add_argument('--dataset', default=DEFAULT_DATASET, - help=f'BigQuery dataset (default: {DEFAULT_DATASET})') - parser.add_argument('--output-dir', default=DEFAULT_OUTPUT_DIR, - help=f'Output directory (default: {DEFAULT_OUTPUT_DIR})') - args = parser.parse_args() - - print(f'Connecting to BigQuery ({args.project})...') - client = bigquery.Client(project=args.project) - - print(f'Fetching metering data {args.date_from} to {args.date_to}...') - print(f' consumption: {args.project}.{args.dataset}.{DEFAULT_TABLE_CONSUMPTION}') - print(f' usage: {args.project}.{args.dataset}.{DEFAULT_TABLE_USAGE}') - rows = fetch_usage_rows( - client, args.project, args.dataset, - args.date_from, args.date_to, - ) - print(f'Got {len(rows)} aggregated rows') - - if not rows: - print('No metering data found. Check that:') - print(' 1. GKE resource consumption metering is enabled') - print(' 2. The date range has data') - return - - days, unknown_skus = build_daily_files(rows) - count = write_files(days, args.output_dir) - print(f'Wrote {count} daily billing files to {args.output_dir}') - - if unknown_skus: - print(f'\nWARNING: {len(unknown_skus)} unknown SKU(s) had zero cost assigned:') - for s in sorted(unknown_skus): - print(f' {s}') - print('Add these to SKU_PRICING in fetch-billing.py with prices from') - print('the GCP Cloud Billing Catalog API.') - - # Summary - total = sum( - ns['total'] for day in days.values() - for ns in day['namespaces'].values() - ) - ns_set: set[str] = set() - cat_set: set[str] = set() - for day in days.values(): - for ns_name, ns_data in day['namespaces'].items(): - ns_set.add(ns_name) - cat_set.update(ns_data['breakdown'].keys()) - - print(f'\nTotal cost: ${total:,.2f}') - print(f'Namespaces ({len(ns_set)}): {sorted(ns_set)}') - print(f'Categories: {sorted(cat_set)}') - - -if __name__ == '__main__': - main() diff --git a/ci3/dashboard/ci-metrics/billing/gcp.py b/ci3/dashboard/ci-metrics/billing/gcp.py deleted file mode 100644 index 5254e20bbbf..00000000000 --- a/ci3/dashboard/ci-metrics/billing/gcp.py +++ /dev/null @@ -1,289 +0,0 @@ -"""Namespace billing helpers for rkapp. - -Fetches GKE namespace billing from BigQuery with in-memory cache. -Route definitions remain in rk.py; this module provides the logic. - -SKU pricing: Queries the Cloud Billing pricing export table in BigQuery -if available, otherwise falls back to hardcoded rates. To enable the -pricing export: - 1. Go to GCP Console > Billing > Billing export - 2. Enable "Detailed usage cost" and "Pricing" exports - 3. Set the dataset to the _BQ_DATASET below -""" -import threading -import time -from datetime import datetime, timedelta, timezone -from pathlib import Path - -# BigQuery defaults -_BQ_PROJECT = 'testnet-440309' -_BQ_DATASET = 'egress_consumption' -_BQ_TABLE_USAGE = 'gke_cluster_resource_usage' -_BQ_TABLE_PRICING = 'cloud_pricing_export' - -# Hardcoded fallback SKU pricing (us-west1). -# cpu: price per vCPU-hour, memory: price per GiB-hour -# network: price per GiB, storage: price per GiB-month -_HARDCODED_SKU_PRICING = { - # Compute - Spot - 'E7FF-A0FB-FA82': {'price': 0.00497, 'resource': 'cpu', 'category': 'compute_spot'}, - '48AB-89F5-9112': {'price': 0.000668, 'resource': 'memory', 'category': 'compute_spot'}, - # Compute - On-demand T2D - 'EFE6-E23C-19CB': {'price': 0.027502, 'resource': 'cpu', 'category': 'compute_ondemand'}, - 'FB05-036A-8982': {'price': 0.003686, 'resource': 'memory', 'category': 'compute_ondemand'}, - # Compute - On-demand N2 - 'BB77-5FDA-69D9': {'price': 0.031611, 'resource': 'cpu', 'category': 'compute_ondemand'}, - '5B01-D157-A097': {'price': 0.004237, 'resource': 'memory', 'category': 'compute_ondemand'}, - # Compute - On-demand N2D - 'A03E-E620-7389': {'price': 0.027502, 'resource': 'cpu', 'category': 'compute_ondemand'}, - '5535-6D2D-4B50': {'price': 0.003686, 'resource': 'memory', 'category': 'compute_ondemand'}, - # Network Egress (price per GiB) - '0C3C-6B13-B1E8': {'price': 0.02, 'resource': 'networkEgress', 'category': 'network'}, - '6B8F-E63D-832B': {'price': 0.0, 'resource': 'networkEgress', 'category': 'network'}, - '92CB-C25F-B1D1': {'price': 0.0, 'resource': 'networkEgress', 'category': 'network'}, - '984A-1F27-2D1F': {'price': 0.04, 'resource': 'networkEgress', 'category': 'network'}, - '9DE9-9092-B3BC': {'price': 0.20, 'resource': 'networkEgress', 'category': 'network'}, - 'C863-37DA-506E': {'price': 0.02, 'resource': 'networkEgress', 'category': 'network'}, - 'C8EA-1A86-3D28': {'price': 0.02, 'resource': 'networkEgress', 'category': 'network'}, - 'DE9E-AFBC-A15A': {'price': 0.01, 'resource': 'networkEgress', 'category': 'network'}, - 'DFA5-B5C6-36D6': {'price': 0.085, 'resource': 'networkEgress', 'category': 'network'}, - 'F274-1692-F213': {'price': 0.08, 'resource': 'networkEgress', 'category': 'network'}, - 'FDBC-6E3B-D4D8': {'price': 0.15, 'resource': 'networkEgress', 'category': 'network'}, - # Storage (price per GiB-month) - 'D973-5D65-BAB2': {'price': 0.04, 'resource': 'storage', 'category': 'storage'}, -} - -# Resource name to category mapping for SKUs discovered from BigQuery -_RESOURCE_CATEGORIES = { - ('cpu', True): 'compute_spot', - ('cpu', False): 'compute_ondemand', - ('memory', True): 'compute_spot', - ('memory', False): 'compute_ondemand', -} - -# Active SKU pricing — updated from BigQuery if available -_SKU_PRICING = dict(_HARDCODED_SKU_PRICING) - -# In-memory caches -_cache = {'data': [], 'ts': 0} -_cache_lock = threading.Lock() -_CACHE_TTL = 6 * 3600 # 6 hours - -_pricing_cache = {'ts': 0} -_pricing_lock = threading.Lock() -_PRICING_CACHE_TTL = 24 * 3600 # 24 hours - - -def _refresh_sku_pricing(): - """Try to fetch SKU pricing from BigQuery pricing export table.""" - global _SKU_PRICING - now = time.time() - if _pricing_cache['ts'] and now - _pricing_cache['ts'] < _PRICING_CACHE_TTL: - return - if not _pricing_lock.acquire(blocking=False): - return - try: - if _pricing_cache['ts'] and time.time() - _pricing_cache['ts'] < _PRICING_CACHE_TTL: - return - from google.cloud import bigquery - client = bigquery.Client(project=_BQ_PROJECT) - table = f'{_BQ_PROJECT}.{_BQ_DATASET}.{_BQ_TABLE_PRICING}' - - # Get the known SKU IDs we need pricing for - sku_ids = list(_HARDCODED_SKU_PRICING.keys()) - placeholders = ', '.join(f"'{s}'" for s in sku_ids) - - query = f""" - SELECT sku.id AS sku_id, - pricing.effective_price AS price, - sku.description AS description - FROM `{table}` - WHERE sku.id IN ({placeholders}) - AND service.description = 'Compute Engine' - QUALIFY ROW_NUMBER() OVER (PARTITION BY sku.id ORDER BY export_time DESC) = 1 - """ - rows = list(client.query(query).result()) - if rows: - updated = dict(_HARDCODED_SKU_PRICING) - for row in rows: - sid = row.sku_id - if sid in updated: - updated[sid] = {**updated[sid], 'price': float(row.price)} - _SKU_PRICING = updated - _pricing_cache['ts'] = time.time() - print(f"[rk_billing] Updated {len(rows)} SKU prices from BigQuery") - else: - _pricing_cache['ts'] = time.time() - print("[rk_billing] No pricing rows returned, using hardcoded rates") - except Exception as e: - # Table probably doesn't exist yet — use hardcoded rates - _pricing_cache['ts'] = time.time() - print(f"[rk_billing] SKU pricing query failed (using hardcoded): {e}") - finally: - _pricing_lock.release() - - -# ---- BigQuery fetch ---- - -def _usage_to_cost(sku_id, resource_name, amount): - info = _SKU_PRICING.get(sku_id) - if not info: - return 0.0, 'other' - price = info['price'] - if resource_name == 'cpu': - # cpu-seconds -> hours - return (amount / 3600.0) * price, info['category'] - elif resource_name == 'memory': - # byte-seconds -> GiB-hours - return (amount / 3600.0 / (1024 ** 3)) * price, info['category'] - elif resource_name.startswith('networkEgress'): - # bytes -> GiB - return (amount / (1024 ** 3)) * price, info['category'] - elif resource_name == 'storage': - # byte-seconds -> GiB-months (730 hours/month) - gib_months = amount / (1024 ** 3) / (730 * 3600) - return gib_months * price, info['category'] - return 0.0, info['category'] - - -def _fetch_from_bigquery(date_from_str, date_to_str): - """Query BigQuery for usage data, return list of daily billing entries.""" - try: - from google.cloud import bigquery - except ImportError: - print("[rk_billing] google-cloud-bigquery not installed") - return [] - - try: - client = bigquery.Client(project=_BQ_PROJECT) - # Use the usage table for all resources (actual consumption, not just requests). - # The consumption table only records resource *requests* which can be far lower - # than actual usage (e.g. prove-n-tps-real: $2.87 requests vs $138.72 actual). - usage = f'{_BQ_PROJECT}.{_BQ_DATASET}.{_BQ_TABLE_USAGE}' - query = f""" - SELECT DATE(start_time) AS date, namespace, sku_id, resource_name, - SUM(usage.amount) AS total_usage - FROM `{usage}` - WHERE DATE(start_time) BETWEEN @date_from AND @date_to - GROUP BY date, namespace, sku_id, resource_name - ORDER BY date, namespace - """ - job_config = bigquery.QueryJobConfig( - query_parameters=[ - bigquery.ScalarQueryParameter('date_from', 'DATE', date_from_str), - bigquery.ScalarQueryParameter('date_to', 'DATE', date_to_str), - ] - ) - rows = list(client.query(query, job_config=job_config).result()) - except Exception as e: - print(f"[rk_billing] BigQuery fetch failed: {e}") - return [] - - # Build daily structures - days = {} - for row in rows: - date_str = row.date.isoformat() if hasattr(row.date, 'isoformat') else str(row.date) - ns = row.namespace - cost, category = _usage_to_cost(row.sku_id, row.resource_name, float(row.total_usage)) - if cost <= 0: - continue - if date_str not in days: - days[date_str] = {'date': date_str, 'namespaces': {}} - if ns not in days[date_str]['namespaces']: - days[date_str]['namespaces'][ns] = {'total': 0, 'breakdown': {}} - entry = days[date_str]['namespaces'][ns] - entry['breakdown'][category] = entry['breakdown'].get(category, 0) + cost - entry['total'] += cost - - # Round values - for data in days.values(): - for ns_data in data['namespaces'].values(): - ns_data['total'] = round(ns_data['total'], 4) - ns_data['breakdown'] = {k: round(v, 4) for k, v in ns_data['breakdown'].items()} - - return sorted(days.values(), key=lambda x: x['date']) - - -def _ensure_cached(): - now = time.time() - if _cache['data'] and now - _cache['ts'] < _CACHE_TTL: - return - if not _cache_lock.acquire(blocking=False): - return - try: - yesterday = datetime.now(timezone.utc).date() - timedelta(days=1) - date_from = (yesterday - timedelta(days=365)).isoformat() - date_to = yesterday.isoformat() - print(f"[rk_billing] Fetching billing data from BigQuery ({date_from} to {date_to})...") - data = _fetch_from_bigquery(date_from, date_to) - if data: - _cache['data'] = data - _cache['ts'] = now - print(f"[rk_billing] Cached {len(data)} days of billing data") - finally: - _cache_lock.release() - - -# ---- Public API ---- - -def get_billing_files_in_range(date_from, date_to): - """Return billing data for dates in range. Fetches from BigQuery with in-memory cache.""" - # Refresh SKU pricing from BigQuery (async, falls back to hardcoded) - threading.Thread(target=_refresh_sku_pricing, daemon=True).start() - - if not _cache['data']: - _ensure_cached() # block on first load so dashboard isn't empty - else: - threading.Thread(target=_ensure_cached, daemon=True).start() - - # Convert datetime args to date strings for filtering - from_str = date_from.strftime('%Y-%m-%d') if hasattr(date_from, 'strftime') else str(date_from) - to_str = date_to.strftime('%Y-%m-%d') if hasattr(date_to, 'strftime') else str(date_to) - - return [e for e in _cache['data'] if from_str <= e['date'] <= to_str] - - -def _merge_ns_billing(target, ns_data): - target['total'] += ns_data.get('total', 0) - for cat, val in ns_data.get('breakdown', {}).items(): - target['breakdown'][cat] = target['breakdown'].get(cat, 0) + val - - -def aggregate_billing_weekly(daily_data): - if not daily_data: - return [] - weeks = {} - for entry in daily_data: - d = datetime.strptime(entry['date'], '%Y-%m-%d') - week_start = d - timedelta(days=d.weekday()) - week_key = week_start.strftime('%Y-%m-%d') - if week_key not in weeks: - weeks[week_key] = {'date': week_key, 'namespaces': {}} - for ns, ns_data in entry.get('namespaces', {}).items(): - if ns not in weeks[week_key]['namespaces']: - weeks[week_key]['namespaces'][ns] = {'total': 0, 'breakdown': {}} - _merge_ns_billing(weeks[week_key]['namespaces'][ns], ns_data) - return sorted(weeks.values(), key=lambda x: x['date']) - - -def aggregate_billing_monthly(daily_data): - if not daily_data: - return [] - months = {} - for entry in daily_data: - month_key = entry['date'][:7] + '-01' - if month_key not in months: - months[month_key] = {'date': month_key, 'namespaces': {}} - for ns, ns_data in entry.get('namespaces', {}).items(): - if ns not in months[month_key]['namespaces']: - months[month_key]['namespaces'][ns] = {'total': 0, 'breakdown': {}} - _merge_ns_billing(months[month_key]['namespaces'][ns], ns_data) - return sorted(months.values(), key=lambda x: x['date']) - - -def serve_billing_dashboard(): - billing_html_path = Path(__file__).parent / 'billing-dashboard.html' - if billing_html_path.exists(): - with billing_html_path.open('r') as f: - return f.read() - return None diff --git a/ci3/dashboard/ci-metrics/ci-run-seed.json.gz b/ci3/dashboard/ci-metrics/ci-run-seed.json.gz deleted file mode 100644 index a971ad10d38bf6cbc05df2f5cd5c79d93952c7ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 357714 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
M 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 -
-
aztec-packages · Jan 21 – Feb 23, 2026
-

CI Health Report

-
34-day view · spend · flake · pipeline health · actions
-
-
$6,107
AWS CI EC2 (34d · metrics.db)
-
$22,738
AWS operational total (34d · Cost Explorer, ex-tax, ex-contract)
-
$9,972
GCP compute (30d · 31 namespaces)
-
63.5%
merge queue success (712 attempts)
-
92.3%
next pipeline pass rate
-
33.6 min
next P50 · +8% in 3 weeks
-
-
source: ci.aztec-labs.com metrics.db + BigQuery · 5,136 tracked runs · Jan 21–Feb 23, 2026
-
- - -
-
§1 spend · overview
-

$6,107 AWS CI EC2 in 34 days — network is the cost hotspot at 30% of spend, 18.4% spot

-
Jan 21–Feb 23 · metrics.db cost_usd · CI pipelines only (GCP on next slide)
-
-
-
-
-
-
-
network pipeline
-
$5.03/run avg
-
523 runs · only 15.5% spot · $2,632 total = 37% of all CI spend
-
-
-
prs pipeline
-
$0.72/run avg
-
1,864 runs · 94.4% spot · $1,340 total · most cost-efficient pipeline
-
-
Network spot rate 15.5% vs 87–99% everywhere else. Spot costs ~86% less than on-demand. Network pipeline is the single largest AWS cost lever.
-
-
-
- - -
-
§1 spend · full picture
-

$16K/month total: $9K dev/testing across both clouds, $6K always-on production

-
AWS CI EC2 $6,107 (34d · metrics.db) · AWS operational $22,738 (34d · Cost Explorer, ex-tax, ex-contract) · GCP $9,972 (30d)
-
-
-
-
-
-
-
-
-
-
GCP production clusters
-
$6,379/mo
-
testnet $997 · next-net $859 · mainnet $816 · staging $792 · fisherman $334. Always-on regardless of CI activity.
-
-
$6,107 = CI run EC2 tracked in metrics.db — what we directly pay per CI job. Gap to $16,221 total EC2 bill = always-on infra (NAT, VPC, bastion, EFS) not tagged as CI runs. $22,738 = full AWS operational (Cost Explorer, 34d): EC2 $16,221 + CloudFront $1,829 + EC2-Other $2,177 + ElastiCache $637 + misc $1,874. Excluded: $17,520 annual Savings Plan commitment (one-time contract) + $6,172 tax → $46,430 total AWS bill. GCP: $2,129 CI namespaces + $6,379 prod clusters (always-on, CI-independent).
-
-
-
- - -
-
§1 spend · weekly trend
-

W04 network anomaly: $999 in one week — Feb 14 instance change cut it 91%

-
Stacked weekly CI spend by pipeline · m6a.48xlarge eliminated from network CI Feb 14, replaced by m6a.4xlarge
-
-
-
-
-
-
-
W04 network (m6a.48xlarge, on-demand)
-
$9.79/run
-
192 vCPUs, on-demand pricing. Long-failing jobs during p2p/epoch instability. $999 in one week.
-
-
-
W07 network (m6a.4xlarge)
-
$0.85/run
-
$94 total for the week. Network tests no longer run on the 192-vCPU on-demand beast.
-
-
W07 total: $988 vs W06 peak $1,947. 49% weekly reduction driven by eliminating m6a.48xlarge from network tests. kind tests also moved to spot this period.
-
-
-
- - -
-
§1 spend · spot discipline
-

Network pipeline: 15.5% spot — every other pipeline runs 87–99% spot

-
Spot instances cost ~86% less than on-demand at equivalent capacity. The gap is isolated to network.
-
-
-
-
-
-
Network at 15.5% spot: 442 of 523 runs were on-demand. At prs pipeline spot rates (94.4%) the network bill drops from $2,632 to ~$370. ~$2,200/month opportunity.
-
GCP spot migration: $1,397/month in 0%-spot namespaces (eth-mainnet, sepolia, monitoring). Ops complexity — preemption handling for archive nodes. See §4.
-
kind tests moved to spot (done). Network pipeline is the next highest-ROI action on AWS.
-
-
-
- - -
-
§2 flake · weekly pattern
-

One cluster drove every spike — p2p/epoch cleared completely in W07

-
Jan 14–Feb 23 · stacked: p2p/epoch (orange) · other flakes (blue) · hard fails (red)
-
-
-
-
-
-
-
p2p/epoch cluster (W02–W06)
-
2,034 events
-
56 distinct tests · all classified as flakes (0 hard fails) · 747 events in W06 → 2 in W07
-
-
W06 (Feb 9): 515 hard fails — highest of any week. Three high-risk PRs merged same day (see slide 8).
-
W08 (Feb 23, partial): 36 events, 0 p2p/epoch. Too early to call — partial week, low volume.
-
-
-
- - -
-
§2 flake · anatomy
-

2,034 labeled events across 56 tests — and 1,912 unlabeled failures behind them

-
The e2e-p2p-epoch-flakes cluster is tagged. Everything else is anonymous.
-
-
-
-
-
-
-
e2e-p2p-epoch-flakes · 2,034 events · 56 tests · 0 hard fails
-

Known cluster. All events classified as flakes — timing-driven, not code bugs. Cleared in W07 after Santiago PRs #20351 (mbps test fix) and #20462 (remove hardcoded 10s timeout), plus ludamad #20613 (CI parallelism). Root cause: PXE using latest (not checkpointed) block across distributed epoch transitions. Will recur.

-
-
-
Unlabeled · 1,912 events · 938 distinct tests · merge queue failures
-

These are failures in the merge queue (next pipeline). In the queue, any failure blocks a PR — whether it's a real bug, infrastructure noise, or a timing race. 938 distinct failing tests with no cluster assignment and no assigned owner. This is the long tail of test rot that grows with every feature sprint.

-
-
The labeled cluster cleared. The unlabeled tail didn't. 938 distinct failing tests is residual risk that accumulates without active maintenance.
-
-
-
- - -
-
§2 flake · signal quality
-

New flakes keep appearing — the floor doesn't clear, and 4 grinds didn't catch W04

-
0.12% overall flake rate (6,632 flakes / 5.5M test runs) — low headline, lumpy reality
-
-
-
W04: the gate failed
-
-
valid_epoch_pruned_slash: 0 events W03 → 346 events W04
-

This test passed the 4-run deflake gate and was merged. In its first full week in CI it produced 346 fail/flake events — 33% of the entire W04 spike. Similarly, tx_proposal_collector (180 events) and inactivity_slash_with_consequence (74 events) debuted in W04 with immediate instability. All passed the gate.

-
-
-
team-alpha now running 10 grinds
-

A stricter informal standard adopted by one team. Not yet universal policy. Reduces risk of introducing flaky tests but increases CI time per new test by 2.5×. Proposal: targeted deflake — instrument tests for determinism before setting a grind count. Outstanding from offsite.

-
-
-
-
W07: new regressions after p2p fix
-
-
profile_gates.test.ts (12 events) + compile.test.ts (11 events)
-

Both appeared for the first time in W07 — the same week the p2p cluster cleared. CLI-layer tests, hash-prefixed job context. Not present in W06. New regressions introduced during the W07 fix sprint, or surface area exposed once p2p noise was removed.

-
-
-
~7% of CI run failures are genuine code bugs
-

Most failures are infrastructure noise (nightly 61% quick-fail) or p2p timing (labeled cluster). Merge-queue failures often behave like flakes regardless of their root cause — they block PRs that may be perfectly correct. New failing tests appear every week; old ones aren't fully removed.

-
-
-
-
- - -
-
§2 flake · PR correlation
-

What caused the spikes — and who fixed them

-
Attribution via ci_runs × pr_authors. Santiago Palladino: 18+ fix PRs in 6 weeks.
-
-
-
spike causes
-
- -
-
W02 — Jan 13 · 2,072 flakes
-
spalladino refactors (e2e setup, archiver, test setup). Changed patterns exposing latent timing races across p2p/epoch simultaneously. Tipping point for existing instability.
-
-
-
- -
-
W04 — Jan 26 · 935 flakes
-
PhilWindle added cross-chain mbps e2e tests without pre-deflaking. valid_epoch_pruned_slash: 0→346 events. tx_proposal_collector: 180 events. inactivity_slash_with_consequence: 74 events. All absent the prior week.
-
-
-
- -
-
W06 — Feb 9 · 850 flakes + 515 hard fails (worst week)
-
Three high-risk PRs same day: #20047 peer scoring (15:27, mrzeszutko), #20241 max checkpoints→32 (11:21), #20257 hash constants (14:38, LeilaWang). Spanning p2p + epoch config + consensus hashes — the exact subsystems where all flakes live.
-
-
-
-
-
fixes that worked
-
- -
-
W03 — Jan 23 · −57% · root fix
-
spalladino: checkpointed chain tip for PXE. PXE was using latest (not checkpointed) block, causing epoch boundary races. 6 of 7 CI runs passed cleanly. Most impactful single fix of the period.
-
-
-
- -
-
W05 — Feb 3–5 · −33%
-
spalladino #20088 slasher multi-block handling. PhilWindle #20140 deflake discv5. 20+ CI runs over 2 days before clean merge.
-
-
-
- -
-
W07 — Feb 10–17 · −65% · p2p cluster cleared
-
spalladino #20351 fix mbps chain test (p2p_client 311→0 flakes). #20462 remove hardcoded 10s timeout. ludamad #20613 CI parallelism — insufficient workers caused timeout cascades misclassified as flakes.
-
-
-
-
-
Pattern: every spike triggered by new unstable tests or multi-subsystem config changes without pre-deflaking. Will recur without structural change.
-
- - -
-
§2 flake · maintenance cost
-

30 fix/deflake PRs in 34 days — nearly one per day of ongoing maintenance overhead

-
PRs with "fix/flake/deflake" in title or branch · Jan 14–Feb 23 · engineers patching unstable tests continuously
-
-
-
recent (Feb)
-
-
#20636spypsy · Feb 18
-
#20609alexghr · Feb 17
-
#20439ludamad · Feb 12
-
#20335spypsy · Feb 10
-
#20330ludamad · Feb 10
-
#19322charlielye · Feb 10
-
#20246mverzilli · Feb 6
-
#20243mverzilli · Feb 6
-
#20240mverzilli · Feb 6
-
#20215ludamad · Feb 5
-
#20160mverzilli · Feb 4
-
#20095suyash67 · Feb 4
-
#20140PhilWindle · Feb 3
-
#20131alexghr · Feb 3
-
#20119alexghr · Feb 3
-
#20115danielntmd · Feb 2
-
-
-
-
earlier (Jan)
-
-
#20090mralj · Jan 30
-
#20070mralj · Jan 30
-
#20068mralj · Jan 30
-
#20040mralj · Jan 29
-
#20024spalladino · Jan 28
-
#20004spalladino · Jan 28
-
#19952ludamad · Jan 26
-
#19910spalladino · Jan 23
-
#19782alexghr · Jan 21
-
#19767spalladino · Jan 20
-
#19705PhilWindle · Jan 19
-
#19618alexghr · Jan 15
-
#19588spalladino · Jan 15
-
#19580PhilWindle · Jan 14
-
-
-
-
-
deflake maintenance rate
-
30
-
PRs in 34 days = 0.88/day. 8 distinct contributors. spalladino (6), alexghr (5), ludamad (4), mverzilli (4), mralj (4), PhilWindle (3).
-
-
This is the hidden cost of test rot — not paid in dollars but in senior engineer hours. Each deflake PR is an interrupt to feature work.
-
-
-
- - -
-
§3 health · pass rates
-

next at 92% — nightly and network stuck at 58%, different failure modes

-
Jan 21–Feb 23 · ci_run_daily_stats · 4,870 total runs across 5 pipelines
-
-
-
-
-
-
-
nightly (573 runs)
-
58.1%
-
61% of failures complete in under 5 min — single job, crashes at startup before tests run. Environment instability, not code.
-
-
-
network (450 runs)
-
58.0%
-
9.5% quick-fail, 87.9min avg fail duration. Network runs often partially pass — a FAIL may have completed 80% of its test suite before one test times out.
-
-
Nightly failures are environment crashes (startup), not code regressions. Network failures are long-running timeouts — pass rate understates how much actually succeeds.
-
-
-
- - -
-
§3 health · failure anatomy
-

61% of nightly failures complete in under 5 minutes — infra crashes, not code

-
Quick-fail proxy: FAILED runs completing in <5 min. Heuristic — some short unit tests legitimately fail fast.
-
-
-
-
-
-
nightly: 60.8% quick-fail (avg 5.2min to failure). Single job type (next), bailing at startup. The pipeline is crashing on environment setup — likely a Docker pull, secret mount, or k8s scheduling failure — before any test code runs.
-
prs: 30.3% quick-fail (168 of 555 failures). 1 in 3 PR CI failures is infrastructure, not the engineer's code. Significant developer friction that inflates apparent failure rates.
-
next: 9.0% quick-fail — next failures are mostly genuine test failures. The highest-signal pipeline for code quality.
-
-
-
- - -
-
§3 health · build time
-

next P50 +8% in 3 weeks — P95 up 22%, tail latency accelerating

-
W05=Feb 2 · W06=Feb 9 · W07=Feb 16 · individual ci_runs PASSED records · exact percentiles
-
-
-
-
-
-
-
P95 growth (3 weeks)
-
+22%
-
45.3min (W05) → 55.1min (W07). At this rate: P95 exceeds 70min by May.
-
-
-
P50 growth (3 weeks)
-
+8%
-
31.0min (W05) → 33.6min (W07). Compounded across 1,100+ next runs/month.
-
-
W08 early data (Feb 23, 10 runs only): P50=32.9, P95=34.5. Too small a sample to conclude. No identified root cause for the growth trend.
-
-
-
- - -
-
§3 health · merge queue
-

63.5% merge queue success — W04 bottomed at 57.6%, W07 still only 59.5%

-
712 total attempts · 452 successes · 251 failures · Jan 21–Feb 23
-
-
-
-
-
-
-
34-day success rate
-
63.5%
-
1 in 3 merge attempts needs a retry. Target for a healthy queue: 85%+.
-
-
W07 (59.5%) is worse than W06 (69.6%) despite the p2p fix. Hard test failures replaced flakes as merge blockers — the queue improved in one dimension and degraded in another.
-
W04 worst week: 91 failures out of 217 attempts (57.6%). Coincided with the p2p/epoch spike and the new unstable test introductions.
-
-
-
- - -
-
§3 health · scheduling patterns
-

Tuesday 72% vs Thursday 82% — the Monday queue effect is measurable

-
Jan 21–Feb 23 · all pipelines · 4,870 runs · 9.5pp spread between worst and best weekday
-
-
-
-
-
-
Mon/Tue dip: PRs accumulate over the weekend and all enter the merge queue simultaneously Monday morning. Pipeline contention drives higher failure rates. By Thursday the queue has cleared and pass rates recover.
-
9.5pp spread (Tue 72.3% → Thu 81.8%). Thursday–Friday is measurably safer for large feature merges. Batch rollouts on Monday are higher risk.
-
-
-
- - -
-
§4 GCP · optimization
-

$1,397/month in 0%-spot namespaces — ops complexity vs savings is the open question

-
Five GCP namespaces running 100% on-demand at 86% spot discount foregone · Jan 21–Feb 20
-
-
-
-
-
-
-
Monthly savings potential
-
$1,397/mo
-
eth-mainnet + sepolia ($800) · monitoring ($599). At 86% spot discount, matching cluster-wide spot rate.
-
-
eth-mainnet and sepolia: Ethereum archive nodes, 80% memory utilization. Spot preemption means re-syncing from chain tip. Engineering cost to handle graceful preemption needs evaluation.
-
metrics + public-telemetry: Prometheus/Grafana. Spot-tolerant with PVC persistence — a platform ops task, not a code change. Lower risk than archive nodes.
-
-
-
- - -
-
§5 actions · done
-

Three improvements landed in February

-
Concrete changes with measured impact on cost and flake counts.
-
-
-
cost — instance type
-
-
m6a.48xlarge → m6a.4xlarge (Feb 14)
-

The 192-vCPU on-demand instance running network tests is gone. At W04's failure rate it cost $9.79/run and $999 in a single week. m6a.4xlarge costs $0.85/run. W07 network spend: $94 vs W04's $999. 91% per-run cost reduction. Network tests no longer run on an oversized on-demand machine. Done.

-
-
-
kind tests moved to spot
-

kind test runs moved from on-demand to spot instances. ~86% cost reduction on affected runs. Done.

-
-
-
-
flake — p2p/epoch cleared
-
-
e2e-p2p-epoch-flakes: 2,034 → ~0 events
-

The cluster that drove every spike since Jan 6 cleared in W07. Two p2p/epoch events in W07 vs 747 in W06. W08: zero p2p events so far.

-
-
-
Santiago Palladino: 18+ fix PRs (Jan–Feb)
-

#19914 checkpointed PXE chain tip (root fix), #20088 slasher multi-block, #20351 mbps chain test (311→0 flakes), #20462 remove hardcoded 10s timeout. ludamad #20613 CI parallelism fix.

-
-
-
-
open — not yet resolved
-
-
p2p cluster root cause
-

Cleared via targeted fixes, not architectural resolution. PXE epoch boundary races and test isolation issues remain. Will recur when new p2p/epoch features merge. No assigned owner for permanent fix.

-
-
-
Build time creep unresolved
-

next P50 +8%, P95 +22% in 3 weeks. No identified cause. At current rate P95 exceeds 70 min by May.

-
-
-
-
- - -
-
§5 actions · in progress
-

The deflake gate exists — but W04 proved 4 grinds isn't enough for epoch/slashing tests

-
Outstanding from offsite: reassess deflake strategy. team-alpha already at 10 grinds.
-
-
-
current state
-
-
4-run deflake gate (universal, always existed)
-

All new e2e tests must pass 4 consecutive CI runs before merging. Has been standard for some time. Cost: ~$11 in deflake CI spend over 34 days — not a cost constraint.

-
-
-
W04 evidence: the gate failed
-

valid_epoch_pruned_slash passed 4 grinds and produced 346 events in its first week. tx_proposal_collector (180 events) and inactivity_slash_with_consequence (74 events) debuted similarly. The gate is not filtering unstable epoch/slashing tests adequately.

-
-
-
team-alpha: 10 grinds
-

Informal higher standard on one team. Not yet universal. Reduces risk but increases CI wait time by 2.5× per new test.

-
-
-
-
options
-
-
Option A: raise blanket gate to 10 grinds
-

Universal. Simple to enforce. team-alpha already doing this. Tradeoff: 2.5× CI cost per new e2e test; developers wait longer before merge. Not evidence-based — fast-passing tests still get 10 runs.

- A-533 · set up hard grinding tests in spartan merge train -
-
-
Option B: targeted deflake (proposed)
-

Analyze test pass rate distribution over N runs. Flag tests above a variance threshold. Grind count becomes adaptive: stable tests get 4, timing-sensitive tests get 20+. More surgical, higher tooling cost to implement.

-
-
-
Decision needed from offsite follow-up
-

Who owns the deflake gate upgrade? What is the target flake rate threshold before a test is considered acceptable for CI merge?

-
-
-
-
- - -
-
§5 actions · open questions
-

Three questions for discussion

-
Not answered here — require team input, owner assignment, or cost-benefit analysis.
-
-
-
deflake strategy
-
-
Targeted deflake vs blanket grinds?
-

team-alpha doing 10 grinds — should this become universal? Or build adaptive tooling that sets the grind count per-test based on variance? Who owns the implementation and the policy?

- A-533 -
-
Evidence: W04 proved 4 isn't enough for epoch/slashing tests. 10 is pragmatic but not evidence-based. Targeted deflake is surgical but requires tooling investment.
-
-
-
GCP spot migration
-
-
$1,397/month savings — worth the ops complexity?
-

eth-mainnet and sepolia archive nodes: spot preemption means re-syncing from chain tip. metrics and public-telemetry: lower risk, Prometheus/Grafana with PVC persistence. What's the engineering cost to make each namespace preemption-safe?

-
-
AWS network pipeline spot migration (~$2,200/mo) is likely the easier, higher-ROI first step — no archive node complexity.
-
-
-
p2p/epoch ownership
-
-
Who owns the cluster to permanent resolution?
-

The cluster cleared through 18+ targeted fix PRs. Root cause — PXE epoch boundary races, test isolation, distributed timing — is not architecturally resolved. Next p2p feature merge will likely trigger another spike without a named owner and explicit SLO.

-
-
Without assignment: the next spike will be diagnosed reactively, same as this one. Expected recurrence: within 1–2 major p2p feature merges.
-
-
-
- - - -
1 / 19
- - - - - diff --git a/ci3/dashboard/ci-metrics/views/ci-insights.html b/ci3/dashboard/ci-metrics/views/ci-insights.html deleted file mode 100644 index bb483af6f1d..00000000000 --- a/ci3/dashboard/ci-metrics/views/ci-insights.html +++ /dev/null @@ -1,1221 +0,0 @@ - - - - - ACI - CI Insights - - - - - -

ci insights

- -
- - - - - - - - | - - - | - - - - | - -
- -
- -
-
Overview
-
Attribution
-
- - -
- - -
-
mq success rate
--
-
flakes / day
--
-
prs merged / day
--
-
avg mq duration
--
-
- -
-
-

merge queue: daily outcomes + success rate

-
-
-
-

test outcomes per day

-
-
-
-

ci run duration by pipeline (avg mins)

-
-
-
-

total ci time by pipeline (hours)

-
-
-
-

P95 build time by pipeline & phase (secs)

-
-
-
- -
-
-
top flaky tests
-
- - - -
testflakesaffected
-
-
-
-
top failing tests
-
- - - -
testfailuresaffected
-
-
-
- -
flakes by pipeline
-
- - - -
-
- - -
-
- test details - | - - - - -
-
-
-
-
-
-

avg duration by day

-
-
-
-

test run count by day

-
-
-
-

tests by duration

-
- - - - - - - - - - - - - - - - -
test commandrunsavg (s)min (s)max (s)total (h)pass %passedfailedflaked
-
-

slowest individual runs

-
- - - - - - - - - - - - - -
test commandduration (s)statusdateauthorpipelinelog
-
-
- - -
-
- -
-
-
-

ci cost by run type (time series)

-
-
-
-

cost by user (AWS + GCP)

-
-
-
-

cost by run type

-
-
-
- -
author ci profile
-
- - - -
-
- -
instances
-
- - - -
-
-
- - - - - diff --git a/ci3/dashboard/ci-metrics/views/commits.html b/ci3/dashboard/ci-metrics/views/commits.html deleted file mode 100644 index 03143adff90..00000000000 --- a/ci3/dashboard/ci-metrics/views/commits.html +++ /dev/null @@ -1,345 +0,0 @@ - - - - - Commits — aztec-packages - - - - - -

commits — next

- -
- - - - - - - - - - -
- -
Loading…
- - - - - - diff --git a/ci3/dashboard/ci-metrics/views/cost-overview.html b/ci3/dashboard/ci-metrics/views/cost-overview.html deleted file mode 100644 index da8d73e0b7f..00000000000 --- a/ci3/dashboard/ci-metrics/views/cost-overview.html +++ /dev/null @@ -1,649 +0,0 @@ - - - - - ACI - Cost Overview - - - - - -

cost overview

- -
- - - - - - - - | - - - | - - - - | - -
- -
- -
-
Overview
-
Resource Details
-
- -
-
- -
-
-

combined daily spend

-
-
-
-

service category breakdown

-
-
-
-

aws vs gcp split

-
-
-
- - - - -
-
- -
-
-
-
- - - -
-
-
- - - - - - diff --git a/ci3/dashboard/ci-metrics/views/flake-prs.html b/ci3/dashboard/ci-metrics/views/flake-prs.html deleted file mode 100644 index a962d94d675..00000000000 --- a/ci3/dashboard/ci-metrics/views/flake-prs.html +++ /dev/null @@ -1,155 +0,0 @@ - - - - - Flake PRs - - - - - -

Flake / Deflake PRs

-
loading…
- -
- -
- -
- - - - diff --git a/ci3/dashboard/ci-metrics/views/test-timings.html b/ci3/dashboard/ci-metrics/views/test-timings.html deleted file mode 100644 index 63cc54bb369..00000000000 --- a/ci3/dashboard/ci-metrics/views/test-timings.html +++ /dev/null @@ -1,7 +0,0 @@ - - -Redirecting... - - - - diff --git a/ci3/dashboard/debug.sh b/ci3/dashboard/debug.sh deleted file mode 100755 index ed7d17c58c7..00000000000 --- a/ci3/dashboard/debug.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -REDIS_HOST=localhost flask --app rk.py --debug run --port ${PORT:-8080} diff --git a/ci3/dashboard/deploy-test.sh b/ci3/dashboard/deploy-test.sh deleted file mode 100755 index a3f62229bb5..00000000000 --- a/ci3/dashboard/deploy-test.sh +++ /dev/null @@ -1,143 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# Deploy a test instance of the dashboard on ports 8082/8083 via SSM. -# Shares Redis and /logs-disk with the production instance. -# Requires: aws cli and SSM-enabled instance. - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -REGION="us-east-2" -INSTANCE_NAME="${INSTANCE_NAME:-ci-dashboard}" - -# Resolve instance ID from Name tag (override with INSTANCE_ID env var) -if [ -z "${INSTANCE_ID:-}" ]; then - INSTANCE_ID=$(aws ec2 describe-instances --region "$REGION" \ - --filters "Name=tag:Name,Values=$INSTANCE_NAME" "Name=instance-state-name,Values=running" \ - --query 'Reservations[0].Instances[0].InstanceId' --output text) - if [ "$INSTANCE_ID" = "None" ] || [ -z "$INSTANCE_ID" ]; then - echo "ERROR: Could not find running instance with Name=$INSTANCE_NAME" - echo "Set INSTANCE_ID or INSTANCE_NAME env var." - exit 1 - fi -fi -echo "Using instance: $INSTANCE_ID" - -# Stage and tar files locally -echo "Packaging dashboard files..." -STAGING=$(mktemp -d /tmp/rk-test-stage-XXXXXX) -TARBALL="$STAGING.tar.gz" -rsync -a --exclude='deploy.sh' --exclude='deploy-test.sh' "$SCRIPT_DIR"/ "$STAGING"/ -tar -czf "$TARBALL" -C "$STAGING" . -rm -rf "$STAGING" - -# Upload to S3 as transfer mechanism -echo "Uploading to S3..." -S3_TMP="s3://aztec-ci-artifacts/tmp/rk-test-deploy.tar.gz" -aws s3 cp "$TARBALL" "$S3_TMP" --region "$REGION" --quiet -rm -f "$TARBALL" - -# Encode the remote script as base64 to avoid all quoting issues. -# The script copies env vars from the prod container (rkapp) so the test -# instance connects to the same Redis, uses the same passwords, etc. -REMOTE_SCRIPT_B64=$(base64 << 'SCRIPT' -#!/bin/bash -set -euo pipefail - -cd /home/ubuntu -mkdir -p rk-test -echo "Downloading files from S3..." -aws s3 cp s3://aztec-ci-artifacts/tmp/rk-test-deploy.tar.gz /tmp/rk-test.tar.gz --region us-east-2 --quiet -tar -xzf /tmp/rk-test.tar.gz -C rk-test -rm -f /tmp/rk-test.tar.gz - -cd rk-test -echo "Building docker image..." -docker build -t rkapp-test . - -echo "Stopping old container..." -docker rm -f rkapp-test 2>/dev/null || true - -# Pull env vars from the production container -echo "Reading env vars from production container..." -PROD_ENV="" -for var in REDIS_HOST REDIS_PORT DASHBOARD_PASSWORD LOGS_DISK_PATH S3_LOGS_BUCKET S3_LOGS_PREFIX REPO_PATH; do - val=$(docker exec rkapp printenv "$var" 2>/dev/null) || true - if [ -n "$val" ]; then - PROD_ENV="$PROD_ENV -e $var=$val" - fi -done - -echo "Starting new container..." -eval docker run -d --name rkapp-test --restart unless-stopped \ - --network host \ - -e CI_METRICS_PORT=8083 \ - $PROD_ENV \ - -v /logs-disk:/logs-disk \ - rkapp-test \ - gunicorn -w 4 -b 0.0.0.0:8082 rk:app - -echo "Container started:" -docker ps --filter name=rkapp-test -SCRIPT -) - -PARAMS_FILE=$(mktemp /tmp/rk-test-params-XXXXXX) -python3 -c " -import json, sys -cmd = 'echo $REMOTE_SCRIPT_B64 | base64 -d | bash' -json.dump({'commands': [cmd]}, open(sys.argv[1], 'w')) -" "$PARAMS_FILE" - -# Send command and wait for result -echo "Deploying on instance..." -COMMAND_ID=$(aws ssm send-command --region "$REGION" \ - --instance-ids "$INSTANCE_ID" \ - --document-name "AWS-RunShellScript" \ - --timeout-seconds 300 \ - --parameters "file://$PARAMS_FILE" \ - --output text --query 'Command.CommandId') -rm -f "$PARAMS_FILE" - -echo "Command ID: $COMMAND_ID" -echo "Waiting for completion..." - -# Poll until done (5s intervals, up to 5 minutes) -for i in $(seq 1 60); do - sleep 5 - STATUS=$(aws ssm get-command-invocation --region "$REGION" \ - --command-id "$COMMAND_ID" --instance-id "$INSTANCE_ID" \ - --query 'Status' --output text 2>/dev/null) || continue - case "$STATUS" in - Success) - echo "" - echo "Deploy succeeded!" - aws ssm get-command-invocation --region "$REGION" \ - --command-id "$COMMAND_ID" --instance-id "$INSTANCE_ID" \ - --query 'StandardOutputContent' --output text - echo "" - echo "Test dashboard: http://ci.aztec-labs.com:8082" - exit 0 - ;; - Failed|TimedOut|Cancelled) - echo "" - echo "Deploy FAILED (status: $STATUS)" - echo "--- stdout ---" - aws ssm get-command-invocation --region "$REGION" \ - --command-id "$COMMAND_ID" --instance-id "$INSTANCE_ID" \ - --query 'StandardOutputContent' --output text - echo "--- stderr ---" - aws ssm get-command-invocation --region "$REGION" \ - --command-id "$COMMAND_ID" --instance-id "$INSTANCE_ID" \ - --query 'StandardErrorContent' --output text - exit 1 - ;; - InProgress|Pending|Delayed) - printf "." - ;; - esac -done - -echo "" -echo "Timed out waiting for command. Check manually:" -echo " aws ssm get-command-invocation --region $REGION --command-id $COMMAND_ID --instance-id $INSTANCE_ID" -exit 1 diff --git a/ci3/dashboard/deploy.sh b/ci3/dashboard/deploy.sh deleted file mode 100755 index 27119bc9f84..00000000000 --- a/ci3/dashboard/deploy.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" - -# Sync dashboard (rkapp) files, including ci-metrics subdirectory -rsync -avz --exclude='deploy.sh' -e "ssh -i ~/.ssh/build_instance_key" "$SCRIPT_DIR"/* ubuntu@ci.aztec-labs.com:rk - -ssh -i ~/.ssh/build_instance_key ubuntu@ci.aztec-labs.com " - cd rk - docker build -t rkapp . - sudo systemctl restart rkapp -" diff --git a/ci3/dashboard/requirements.txt b/ci3/dashboard/requirements.txt deleted file mode 100644 index 31fdc5a5e45..00000000000 --- a/ci3/dashboard/requirements.txt +++ /dev/null @@ -1,9 +0,0 @@ -flask -gunicorn -redis -boto3 -ansi2html -Flask-Compress -requests -Flask-HTTPAuth -boto3 diff --git a/ci3/dashboard/rk.py b/ci3/dashboard/rk.py deleted file mode 100644 index fb89613c749..00000000000 --- a/ci3/dashboard/rk.py +++ /dev/null @@ -1,675 +0,0 @@ -from flask import Flask, render_template_string, request, Response, redirect -from flask_compress import Compress -from flask_httpauth import HTTPBasicAuth -import boto3 -from botocore.exceptions import ClientError -import gzip -import json -import os -import re -import requests -import shlex -import subprocess -import threading -import time as _time -import uuid -from ansi2html import Ansi2HTMLConverter -from pathlib import Path - -# Import core rendering logic -from rk_core import ( - YELLOW, BLUE, GREEN, RED, PURPLE, BOLD, RESET, - hyperlink, r, get_section_data, get_list_as_string -) -S3_LOGS_BUCKET = os.getenv('S3_LOGS_BUCKET', 'aztec-ci-artifacts') -S3_LOGS_PREFIX = os.getenv('S3_LOGS_PREFIX', 'logs') - -_s3 = boto3.client('s3', region_name='us-east-2') -DASHBOARD_PASSWORD = os.getenv('DASHBOARD_PASSWORD', '') -CI_METRICS_PORT = int(os.getenv('CI_METRICS_PORT', '8081')) -CI_METRICS_URL = os.getenv('CI_METRICS_URL', f'http://localhost:{CI_METRICS_PORT}') - -app = Flask(__name__) -Compress(app) -auth = HTTPBasicAuth() - -# Start the ci-metrics server as a subprocess (once across all workers). -# Uses a file lock so only the first gunicorn worker to import this module -# actually spawns the process; the rest skip silently. -import fcntl -import signal - -_ci_metrics_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'ci-metrics') -if os.path.isdir(_ci_metrics_dir): - _lock_path = f'/tmp/ci-metrics-{CI_METRICS_PORT}.lock' - try: - _lock_fd = open(_lock_path, 'w') - fcntl.flock(_lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB) - # We hold the lock — kill stale process and spawn fresh one - try: - out = subprocess.check_output( - ['lsof', '-ti', f':{CI_METRICS_PORT}'], stderr=subprocess.DEVNULL, text=True) - for pid in out.strip().split('\n'): - if pid: - os.kill(int(pid), signal.SIGTERM) - _time.sleep(0.5) - except (subprocess.CalledProcessError, OSError): - pass - _ci_metrics_env = {**os.environ, 'CI_METRICS_PORT': str(CI_METRICS_PORT)} - subprocess.Popen( - ['gunicorn', '-w', '1', '-b', f'0.0.0.0:{CI_METRICS_PORT}', - '--timeout', '120', 'app:app'], - cwd=_ci_metrics_dir, - env=_ci_metrics_env, - ) - print(f"[rk.py] ci-metrics server started on port {CI_METRICS_PORT}") - # Hold the lock until this process exits so other workers skip - except OSError: - # Another worker already holds the lock — nothing to do - pass - -# Conditional auth decorator - only require auth if password is set -def optional_auth(f): - if DASHBOARD_PASSWORD: - return auth.login_required(f) - return f - - -def read_from_s3(key): - """Read log from S3 (fallback when Redis and disk both miss).""" - try: - prefix = key[:4] - s3_key = f"{S3_LOGS_PREFIX}/{prefix}/{key}.log.gz" - obj = _s3.get_object(Bucket=S3_LOGS_BUCKET, Key=s3_key) - return gzip.decompress(obj['Body'].read()).decode('utf-8', errors='replace') - except ClientError as e: - if e.response['Error']['Code'] != 'NoSuchKey': - print(f"S3 error reading {key}: {e}") - except Exception as e: - print(f"Error reading from S3: {e}") - return None - -def read_breakdown_from_s3(runtime, flow_name, sha): - """Read benchmark breakdown from S3.""" - breakdown_name = f"{runtime}-{flow_name}-{sha}" - s3_prefix = f"{S3_LOGS_PREFIX}/bench/bb-breakdown" - - # Exact match - try: - s3_key = f"{s3_prefix}/{breakdown_name}.log.gz" - obj = _s3.get_object(Bucket=S3_LOGS_BUCKET, Key=s3_key) - return gzip.decompress(obj['Body'].read()).decode('utf-8', errors='replace') - except ClientError as e: - if e.response['Error']['Code'] != 'NoSuchKey': - print(f"S3 error reading breakdown {breakdown_name}: {e}") - except Exception as e: - print(f"Error reading breakdown from S3: {e}") - - # Prefix search (SHA may be a prefix) - try: - list_prefix = f"{s3_prefix}/{breakdown_name}" - paginator = _s3.get_paginator('list_objects_v2') - for page in paginator.paginate(Bucket=S3_LOGS_BUCKET, Prefix=list_prefix): - for obj in page.get('Contents', []): - key = obj['Key'] - if key.endswith('.log.gz'): - try: - resp = _s3.get_object(Bucket=S3_LOGS_BUCKET, Key=key) - return gzip.decompress(resp['Body'].read()).decode('utf-8', errors='replace') - except Exception: - pass - except Exception as e: - print(f"S3 prefix search failed for breakdown {breakdown_name}: {e}") - - return None - -@auth.verify_password -def verify_password(username, password): - if username == "aztec" and password == DASHBOARD_PASSWORD: - return username - return None - -_github_status_cache = {"status": None, "ts": 0} -_github_status_lock = threading.Lock() - -def convert_to_ocs8(text): - # Replace URLs not already part of an OCS8 link using negative lookbehind. - pattern = r'(? None: - ga_status = get_github_actions_status() - return ( - f"{BOLD}{BLUE}{hyperlink('/', 'AZTEC LABS CI SYSTEM')}{RESET}: " - f"(offset: {offset}) (filter: {filter_str or 'unset'}) (filter_prop: {filter_prop or 'unset'} [status,name,author,msg]) ({ga_status})\n" - ) - -def get_github_actions_status(): - # Cache for 60 seconds - import time - now = time.time() - ga_url='https://githubstatus.com' - with _github_status_lock: - if _github_status_cache["status"] and now - _github_status_cache["ts"] < 60: - return _github_status_cache["status"] - try: - resp = requests.get("https://www.githubstatus.com/api/v2/components.json", timeout=2) - resp.raise_for_status() - data = resp.json() - for comp in data.get("components", []): - if comp.get("name", "").lower() == "actions": - status = comp.get("status", "") - if status in ("operational", "none"): - result = f"Github Actions: {GREEN}{hyperlink(ga_url, 'NOMINAL')}{RESET}" - else: - result = f"Github Actions: {RED}{hyperlink(ga_url, 'DEGRADED')}{RESET}" - _github_status_cache["status"] = result - _github_status_cache["ts"] = now - return result - except Exception: - result = f"Github Actions: {YELLOW}UNKNOWN{RESET}" - _github_status_cache["status"] = result - _github_status_cache["ts"] = now - return result - -def root() -> str: - # Show the default (no section) view with updated links - return ( - update_status(0, '', '') + - f"\n" - f"Select a filter:\n" - f"\n{YELLOW}" - f"{hyperlink('/section/next', 'next queue')}\n" - f"{hyperlink('/section/prs', 'prs')}\n" - f"{hyperlink('/section/releases', 'releases')}\n" - f"{hyperlink('/section/nightly', 'nightly')}\n" - f"{hyperlink('/section/network', 'network')}\n" - f"{hyperlink('/section/deflake', 'deflake')}\n" - f"{RESET}" - f"\n" - f"Benchmarks:\n" - f"\n{YELLOW}" - f"{hyperlink('https://aztecprotocol.github.io/benchmark-page-data/bench?branch=next', 'next')}\n" - f"{hyperlink('https://aztecprotocol.github.io/benchmark-page-data/bench?branch=prs', 'prs')}\n" - f"{hyperlink('/chonk-breakdowns', 'chonk breakdowns')}\n" - f"{RESET}" - f"\n" - f"CI Metrics:\n" - f"\n{YELLOW}" - f"{hyperlink('/cost-overview', 'cost overview (AWS + GCP)')}\n" - f"{hyperlink('/namespace-billing', 'namespace billing')}\n" - f"{hyperlink('/ci-insights', 'ci insights')}\n" - f"{RESET}" - ) - -def section_view(section: str) -> str: - offset = int(request.args.get('offset', 0)) - limit = int(request.args.get('limit', 50)) - filter_str = request.args.get('filter', default='', type=str) - filter_prop = request.args.get('filter_prop', default='', type=str) - - lines = update_status(offset, filter_str, filter_prop) - lines += "\n" - lines += f"Last {limit} ci runs on {section}:\n\n" - lines += get_section_data(section, offset, limit, filter_str, filter_prop) - return lines - -TEMPLATE = """ - - - - ACI · {{ filter_str|default('') }} - - - - -
{{ value|safe }}
- - -""" - -@app.route('/') -@optional_auth -def show_root(): - return render_template_string( - TEMPLATE, - value=ansi_to_html(root()), - filter_str='', - filter_prop='' - ) - -@app.route('/section/
') -@optional_auth -def show_section(section): - return render_template_string( - TEMPLATE, - value=ansi_to_html(section_view(section)), - filter_str=request.args.get('filter', default='', type=str), - filter_prop=request.args.get('filter_prop', default='', type=str), - follow='top' - ) - -# (not ) so branch-qualified history keys like -# `history__merge-train/spartan` route correctly. WSGI decodes %2F to / -# in PATH_INFO before Flask routes, so percent-encoding can't rescue a plain -# converter — only matches segments containing /. -@app.route('/list/') -@optional_auth -def get_list(key): - value = get_list_as_string(key) - follow = request.args.get('follow', 'top') - return render_template_string(TEMPLATE, value=ansi_to_html(value), follow=follow, filter_str='', filter_prop='') - -@app.route('/chonk-breakdowns') -@optional_auth -def chonk_breakdowns(): - """Serve the chonk breakdowns viewer page.""" - breakdown_html_path = Path('chonk-breakdowns/breakdown-viewer.html') - if breakdown_html_path.exists(): - with breakdown_html_path.open('r') as f: - return f.read() - else: - return "Breakdown viewer not found", 404 - -@app.route('/api/breakdown/flows') -@optional_auth -def list_available_flows(): - """API endpoint to list available breakdown flows from S3, filtered by runtime and SHA.""" - runtime = request.args.get('runtime') - sha = request.args.get('sha') - flows = set() - s3_prefix = f"{S3_LOGS_PREFIX}/bench/bb-breakdown/" - - try: - paginator = _s3.get_paginator('list_objects_v2') - for page in paginator.paginate(Bucket=S3_LOGS_BUCKET, Prefix=s3_prefix): - for obj in page.get('Contents', []): - filename = obj['Key'][len(s3_prefix):] - if not filename.endswith('.log.gz'): - continue - parts = filename.replace('.log.gz', '').split('-', 1) - if len(parts) == 2: - file_runtime = parts[0] - rest = parts[1] - last_dash = rest.rfind('-') - if last_dash != -1: - flow_name = rest[:last_dash] - file_sha = rest[last_dash + 1:] - - if runtime and file_runtime != runtime: - continue - if sha and not file_sha.startswith(sha): - continue - - flows.add(flow_name) - except Exception as e: - print(f"Error listing flows from S3: {e}") - - return Response(json.dumps(sorted(list(flows))), mimetype='application/json') - -@app.route('/api/breakdown///') -@optional_auth -def get_breakdown(runtime, flow_name, sha): - """API endpoint to fetch breakdown JSON.""" - breakdown_data = read_breakdown_from_s3(runtime, flow_name, sha) - if breakdown_data: - return Response(breakdown_data, mimetype='application/json') - - return Response('{"error": "Breakdown not found"}', mimetype='application/json', status=404) - - -@app.route('/grind') -@optional_auth -def trigger_grind(): - """Trigger a grind job for a flaky test.""" - from urllib.parse import urlencode as url_encode - - full_cmd = request.args.get('cmd') - commit = request.args.get('commit', 'HEAD') - run_id = request.args.get('run') # Pre-generated run_id from selection page - start = request.args.get('start') # If set, start the grind - - # Configurable options with defaults - grind_time = request.args.get('time', '20m') - cpus = request.args.get('cpus', '192') - jobs_pct = request.args.get('jobs', '200') - memsuspend_pct = request.args.get('memsuspend', '50') - - if not full_cmd: - return "Missing cmd parameter", 400 - - # If run_id is provided and already has a log, redirect to it (back-button protection) - if run_id and r.exists(run_id): - return redirect(f'/{run_id}') - - # If start not requested, show configuration page - if not start: - # Generate one run_id for all links on this page load - page_run_id = uuid.uuid4().hex[:16] - - # Helper to build option links - def make_options(param_name, options, current_value, suffix=''): - links = [] - for opt in options: - is_selected = str(opt) == str(current_value) - if is_selected: - links.append(f"{BOLD}{BLUE}{opt}{suffix}{RESET}") - else: - params = { - 'cmd': full_cmd, 'commit': commit, 'run': page_run_id, - 'time': grind_time, 'cpus': cpus, 'jobs': jobs_pct, 'memsuspend': memsuspend_pct - } - params[param_name] = opt - url = f"/grind?{url_encode(params)}" - links.append(f"{YELLOW}{hyperlink(url, f'{opt}{suffix}')}{RESET}") - return ' | '.join(links) - - time_options = make_options('time', ['5m', '10m', '20m', '30m', '1h'], grind_time) - cpus_options = make_options('cpus', ['16', '32', '64', '128', '192'], cpus) - jobs_options = make_options('jobs', ['10', '25', '50', '75', '100', '200', '400'], jobs_pct, '%') - memsuspend_options = make_options('memsuspend', ['25', '50', '75'], memsuspend_pct, '%') - - # Start grind button - start_params = { - 'cmd': full_cmd, 'commit': commit, 'run': page_run_id, - 'time': grind_time, 'cpus': cpus, 'jobs': jobs_pct, 'memsuspend': memsuspend_pct, - 'start': '1' - } - start_url = f"/grind?{url_encode(start_params)}" - start_button = f"{BOLD}{GREEN}{hyperlink(start_url, '[ Start Grind ]')}{RESET}" - - page = ( - f"{BOLD}Grind Test{RESET}\n\n" - f"Command: {full_cmd}\n" - f"Commit: {commit}\n\n" - f"Duration: {time_options}\n" - f"CPUs: {cpus_options}\n" - f"Jobs: {jobs_options}\n" - f"Memsuspend: {memsuspend_options}\n\n" - f"{start_button}\n" - ) - return render_template_string(TEMPLATE, value=ansi_to_html(page), filter_str='grind', follow='top') - - # Start requested - run the grind - # Use run_id from URL, or generate new one if not provided - if not run_id: - run_id = uuid.uuid4().hex[:16] - - # Initialize the log key so redirect doesn't show "Key not found" - r.setex(run_id, 86400, b'Starting grind...\n') - - # Start grind job in background - # Dashboard server needs local repo checkout at REPO_PATH - repo_path = os.environ.get('REPO_PATH') - if repo_path: - # Refresh the launcher checkout to current origin/next before launching. - # REPO_PATH only supplies the orchestration scripts (ci.sh/bootstrap_ec2); - # the grind target commit is checked out on the remote box. The launcher - # must stay current so grind uses the same transport (SSM) as the rest of - # CI -- a drifted checkout silently falls back to the retired SSH path and - # every instance times out waiting for SSH. - refresh = subprocess.run( - ['git', '-C', repo_path, 'fetch', '--quiet', 'origin', 'next'], - stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True - ) - if refresh.returncode == 0: - refresh = subprocess.run( - ['git', '-C', repo_path, 'checkout', '--quiet', '--force', 'origin/next'], - stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True - ) - if refresh.returncode != 0: - r.setex(run_id, 86400, - f'Failed to refresh launcher checkout at {repo_path}:\n{refresh.stdout}\n'.encode()) - return redirect(f'/{run_id}') - - subprocess.Popen( - ['bash', '-c', f'cd {repo_path} && RUN_ID={run_id} CPUS={cpus} ./ci.sh grind-test {shlex.quote(full_cmd)} {grind_time} {jobs_pct} {memsuspend_pct} {commit}'], - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - start_new_session=True - ) - - # Redirect to log view. - return redirect(f'/{run_id}') - - -# ---- Reverse proxy to ci-metrics server ---- - -_proxy_session = requests.Session() -_HOP_BY_HOP = frozenset([ - 'connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization', - 'te', 'trailers', 'transfer-encoding', 'upgrade', -]) -_STRIP_REQUEST_HEADERS = frozenset(['host']) - -def _proxy(path): - """Forward request to ci-metrics, streaming the response back. - - Passes the browser's Accept-Encoding through to ci-metrics so it - compresses directly for the browser. We stream the raw (still - compressed) bytes back without decompression. - """ - url = f'{CI_METRICS_URL}/{path.lstrip("/")}' - try: - fwd_headers = {k: v for k, v in request.headers if k.lower() not in _STRIP_REQUEST_HEADERS} - resp = _proxy_session.request( - method=request.method, - url=url, - params=request.args, - data=request.get_data(), - headers=fwd_headers, - stream=True, - timeout=180, - ) - # Stream raw bytes (skip requests auto-decompression) - headers = {k: v for k, v in resp.headers.items() if k.lower() not in _HOP_BY_HOP} - return Response(resp.raw.stream(8192), - status=resp.status_code, headers=headers) - except Exception as e: - return Response(json.dumps({'error': f'ci-metrics unavailable: {e}'}), - mimetype='application/json', status=502) - -@app.route('/namespace-billing') -@app.route('/ci-health') -@app.route('/ci-insights') -@app.route('/cost-overview') -@app.route('/test-timings') -@app.route('/ci-health-report') -@app.route('/flake-prs') -@auth.login_required -def proxy_dashboard(): - return _proxy(request.path) - - -@app.route('/api/', methods=['GET', 'POST', 'PUT', 'DELETE']) -@auth.login_required -def proxy_api(path): - return _proxy(f'/api/{path}') - -@app.route('/') -@optional_auth -def get_value(key): - # Check if raw text format is requested - raw_text = key.endswith('.txt') - if raw_text: - key = key[:-4] # Remove .txt extension - - try: - value = r.get(key) - except Exception: - value = None - if value is None: - value = read_from_s3(key) - if value is None: - value = "Key not found" - elif isinstance(value, bytes): - # Redis returns raw bytes — decompress if gzip. - try: - if value.startswith(b"\x1f\x8b"): - value = gzip.decompress(value).decode() - else: - value = value.decode() - except Exception: - value = "Failed to decompress" - - # Return raw text if .txt extension was used - if raw_text: - return Response(value, mimetype='text/plain') - - return render_template_string(TEMPLATE, value=ansi_to_html(value), filter_str='', filter_prop='') - -if __name__ == '__main__': - app.run(host='0.0.0.0', port=8080) diff --git a/ci3/dashboard/rk_cli.py b/ci3/dashboard/rk_cli.py deleted file mode 100644 index 998d4759f42..00000000000 --- a/ci3/dashboard/rk_cli.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python3 -"""Lightweight CLI for CI run viewer - minimal dependencies for fast startup.""" -import argparse -from rk_core import get_section_data, set_base_url - -def main(): - parser = argparse.ArgumentParser(description='CI Run Viewer (CLI)') - parser.add_argument('--section', '-s', type=str, required=True, - help='Section to display (next, prs, releases)') - parser.add_argument('--offset', type=int, default=0, help='Offset for pagination') - parser.add_argument('--limit', '-l', type=int, default=200, help='Number of results to fetch') - parser.add_argument('--filter', '-f', dest='filter_str', type=str, default='', - help='Filter pattern (comma-separated)') - parser.add_argument('--filter-prop', '-p', type=str, default='', - help='Property to filter on (status,name,author,msg)') - - args = parser.parse_args() - - set_base_url("http://ci.aztec-labs.com") - output = get_section_data(args.section, args.offset, args.limit, args.filter_str, args.filter_prop) - print(output, end='') - -if __name__ == '__main__': - main() diff --git a/ci3/dashboard/rk_core.py b/ci3/dashboard/rk_core.py deleted file mode 100644 index a4d64c9dd8b..00000000000 --- a/ci3/dashboard/rk_core.py +++ /dev/null @@ -1,126 +0,0 @@ -"""Core CI rendering logic - minimal dependencies for fast CLI startup.""" -import redis -import re -import os -import json -import time -from datetime import datetime -from pathlib import Path - -# Color definitions as constants. -YELLOW = "\033[38;2;250;217;121m" -BLUE = "\033[38;2;95;167;241m" -GREEN = "\033[38;2;97;214;104m" -RED = "\033[38;2;230;69;83m" -PURPLE = "\033[38;2;188;109;208m" -BOLD = "\033[1m" -RESET = "\033[0m" -LINK_OPEN = '\033]8;;' -LINK_CLOSE = '\x07' -CLEAR_EOL = "\033[K" -BASE_URL = "" # Set to full URL in CLI mode - -def set_base_url(url: str): - global BASE_URL - BASE_URL = url - -def hyperlink(link, text): - return f"{LINK_OPEN}{link}{LINK_CLOSE}{text}{LINK_OPEN}{LINK_CLOSE}" - -def ts_to_s(ts): - """Normalise a timestamp to seconds (int). Accepts ms (13-digit) or ms+suffix (16-digit).""" - ts = int(ts) - if ts > 9_999_999_999_999: - ts = ts // 1000 - return ts // 1000 - -REDIS_HOST = os.getenv('REDIS_HOST', 'localhost') -REDIS_PORT = int(os.getenv('REDIS_PORT', '6379')) -r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, decode_responses=False) - -def get_list_as_string(key, limit=None): - try: - if limit is None: - values = r.lrange(key, 0, -1) - else: - values = r.lrange(key, 0, limit - 1) - if not values: - value = "" - else: - concatenated = [] - for item in values: - concatenated.append(item.decode()) - value = "\n".join(concatenated) - except Exception as e: - value = f"Error retrieving list: {str(e)}" - return value - -def render(group: list) -> str: - # Use the first JSON object for common properties. - data = group[0] - ts = data['timestamp'] - msg = data['msg'] - name, author, complete = data['name'], data['author'], data.get('complete') - - # Generate comma-delimited job links with color reflecting each job's status. - valid_jobs = [job for job in group if 'job_id' in job] - job_links = [] - for job in sorted(valid_jobs, key=lambda j: j['job_id']): - job_status = job.get('status', '') - if job_status == 'RUNNING': - link_color = BLUE - elif job_status == 'FAILED': - link_color = RED - elif job_status == 'PASSED': - link_color = GREEN - else: - link_color = RESET - job_links.append(f"{link_color}{hyperlink(BASE_URL + '/' + str(job['timestamp']), job['job_id'])}{RESET}") - links_str = ",".join(job_links) - date_time = datetime.fromtimestamp(ts_to_s(ts)).strftime("%m-%d %H:%M:%S") - - statuses = [job.get('status') for job in group if 'status' in job] - if statuses and all(s == 'INACTIVE' for s in statuses): - return f"{date_time}: {links_str} {BOLD}{name}{RESET} {author}: {msg}{CLEAR_EOL}\n" - - current_time = int(time.time()) - durations = [] - for job in group: - # ignore inactive jobs in duration calculation - if job.get('status') == 'INACTIVE': - continue - if 'timestamp' in job: - job_start = ts_to_s(job['timestamp']) - job_complete = job.get('complete') - job_end = ts_to_s(job_complete) if job_complete is not None else current_time - durations.append(job_end - job_start) - max_duration = max(durations) if durations else 0 - duration_str = f"({max_duration // 60}m{max_duration % 60}s)" - return f"{date_time}: {links_str} {BOLD}{name}{RESET} {PURPLE}{author}{RESET}: {msg} {duration_str}{CLEAR_EOL}\n" - -def get_section_data(section: str, offset: int = 0, limit: int = 100, - filter_str: str = '', filter_prop: str = '') -> str: - """Core logic for fetching and rendering section data.""" - lua_script_path = Path(__file__).parent / 'set-filter.lua' - with lua_script_path.open('r') as f: - result = r.eval(f.read(), 1, 'ci-run-' + section, offset, limit, filter_prop, filter_str) - - lines = "" - if not result: - lines += "Nothing to see here...\n" - else: - groups = {} - for line in result: - item = json.loads(line) - run = item.get('run_id', 'unknown') - groups.setdefault(run, []).append(item) - for group in groups.values(): - group_sorted = sorted(group, key=lambda x: x.get('ts', x.get('timestamp', 0))) - lines += render(group_sorted) - - fail_lines = get_list_as_string("failed_tests_" + section, 100) - if fail_lines: - lines += "\n" - lines += f"Last 100 failed or flaked tests:\n\n" - lines += fail_lines - return lines diff --git a/ci3/dashboard/set-filter.lua b/ci3/dashboard/set-filter.lua deleted file mode 100644 index 54bdf194194..00000000000 --- a/ci3/dashboard/set-filter.lua +++ /dev/null @@ -1,60 +0,0 @@ -local key = KEYS[1] -local offset = tonumber(ARGV[1]) -local limit = tonumber(ARGV[2]) -local property = ARGV[3] -local pattern = ARGV[4] -local result = {} -local skip = 0 -local seen_runs = {} -local run_count = 0 - -local patterns = {} -for pat in string.gmatch(pattern, '([^,]+)') do - table.insert(patterns, pat) -end - -local function matches_any(str, patterns) - for _, pat in ipairs(patterns) do - if string.find(str, pat) then return true end - end - return false -end - -local allMembers = redis.call('ZREVRANGE', key, 0, -1) -for _, member in ipairs(allMembers) do - local ok, parsed = pcall(cjson.decode, member) - if ok then - local include = true - if property and pattern and parsed[property] and not matches_any(parsed[property], patterns) then - include = false - end - if include then - if skip >= offset then - local run_id = parsed["run_id"] or "unknown" - if not seen_runs[run_id] then - seen_runs[run_id] = true - run_count = run_count + 1 - if run_count > limit then break end - end - -- Patch status via string replace to avoid cjson number truncation. - if parsed["status"] == "RUNNING" then - -- Extract timestamp from raw JSON string to avoid Lua double truncation. - local ts_str = string.match(member, '"timestamp":(%d+)') - local hb_key = "hb-" .. (ts_str or "0") - if redis.call("EXISTS", hb_key) == 0 then - local patched = string.gsub(member, '"status":"RUNNING"', '"status":"INACTIVE"', 1) - table.insert(result, patched) - else - table.insert(result, member) - end - else - table.insert(result, member) - end - else - skip = skip + 1 - end - end - end -end - -return result diff --git a/ci3/denoise b/ci3/denoise deleted file mode 100755 index 17dadbeb780..00000000000 --- a/ci3/denoise +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env bash -NO_CD=1 source ${root:-$(git rev-parse --show-toplevel)}/ci3/source - -# Ensure a command is passed -if [ "$#" != 1 ]; then - echo "Usage: $0 " - exit 1 -fi - -cmd=$1 - -if [[ "${DENOISE:-0}" -eq 0 ]] || [[ "${BUILD_SYSTEM_DEBUG:-0}" -eq 1 ]]; then - set -e - exec bash -c "$cmd" - exit 0 -fi - -function cleanup { - set +e - if [ -n "${publish_pid:-}" ]; then - kill $publish_pid &>/dev/null - wait $publish_pid - publish_pid= - fi - if [ -n "${job_pid:-}" ]; then - kill $job_pid &>/dev/null - wait $job_pid - job_pid= - fi - if [ -n "${tail_pid:-}" ]; then - kill $tail_pid &>/dev/null - wait $tail_pid - tail_pid= - fi - if [ -n "${dots_pid:-}" ]; then - kill $dots_pid &>/dev/null - wait $dots_pid - dots_pid= - fi -} -trap cleanup SIGINT SIGTERM -# Remove file on exit, so we can publish even after SIGINT or SIGTERM. -trap 'cleanup; rm -f $outfile $dots_fifo' EXIT - -dots_per_line=${DENOISE_WIDTH:-64} -dot_count=0 -status="in-progress" -time="in-progress" - -# We don't want to lose color just because we're wrapping. -export FORCE_COLOR=${FORCE_COLOR:-1} - -# If stdout is connected to a terminal, print singular dots for each line. -[ -t 1 ] && realtime=1 || realtime=0 - -key=$(uuid) -url=$DASHBOARD_URL/$key -# DENOISE_TMPDIR gives a run a private output directory, so tests can scope their -# leaked-process checks to their own denoise instead of every one on the machine. -outfile=${DENOISE_TMPDIR:-/tmp}/$key -touch $outfile - -function format_log_output { - if [ -n "${PARENT_LOG_ID:-}" ]; then - echo -e "Parent Log: $(ci_term_link $PARENT_LOG_ID)" - fi - echo "Command: ${DENOISE_DISPLAY_NAME:-$cmd}" - echo "Working Dir: ${PWD:-}" - echo "Date: $(date)" - echo "Status: $status" - echo "Took: ${time}" - echo - cat $outfile -} - -function publish_log { - [ "$CI_REDIS_AVAILABLE" -eq 0 ] && return - format_log_output | redis_setexz $key $CI_REDIS_EXPIRE -} - -function publish_log_final { - [ "$CI_REDIS_AVAILABLE" -eq 0 ] && return - format_log_output | cache_persistent $key $CI_REDIS_EXPIRE -} - -function live_publish_log { - while [ -f $outfile ]; do - if [ $(( $(date +%s) - $(stat -c %Y "$outfile") )) -le 5 ]; then - publish_log - fi - sleep 5 - done -} - -if [ "$CI_REDIS_AVAILABLE" -eq 1 ]; then - live_publish_log & - publish_pid=$! - log_info="(${yellow}${url}${reset})" -fi - -# Dot logging via named pipe so we can track both PIDs. -set +e -display_cmd="${DENOISE_DISPLAY_NAME:-$cmd}" -echo -e "Executing: $display_cmd ${log_info:-}" -echo -n " 0 " -dots_fifo=$(mktemp -u) -mkfifo "$dots_fifo" - -tail --sleep-interval=0.2 -n +1 -f "$outfile" > "$dots_fifo" & -tail_pid=$! - -while IFS= read -r line; do - dot_count=$((dot_count+1)) - [ $realtime -eq 1 ] && printf "." - if [[ "$dots_per_line" -gt 0 && $((dot_count % dots_per_line)) -eq 0 ]]; then - [ $realtime -eq 0 ] && printf '%.s.' $(seq 1 "$dots_per_line") - printf "\n%4s " "$dot_count" - fi -done < "$dots_fifo" & -dots_pid=$! - -# Execute the command in background. -PARENT_LOG_ID=$key bash -c "set -euo pipefail; $cmd" 2>&1 | redact > $outfile & -job_pid=$! - -# Wait for the job to finish and get its exit status. -wait $job_pid -status=$? - -time="${SECONDS}s" -[ -n "${publish_pid:-}" ] && kill $publish_pid &>/dev/null || true -publish_log_final - -# Kill tail (source), EOF propagates through fifo, dots loop drains and exits. -kill $tail_pid &>/dev/null -wait $tail_pid 2>/dev/null -tail_pid= -wait $dots_pid 2>/dev/null -dots_pid= - -# Handle non-zero exit status -if [ "$status" -ne 0 ]; then - # Print "interrupted" on SIGINT or SIGTERM. - if [ "$status" -eq 143 ] || [ "$status" -eq 130 ]; then - echo -e ". ${yellow}interrupted${reset} ($time)" - else - # With no redis there is no log URL to point the reader at, so the output has to go - # inline or the failure is invisible. - if [ -t 1 ] || [ "${DUMP_FAIL:-0}" -eq 1 ] || [ "$CI_REDIS_AVAILABLE" -eq 0 ]; then - echo -e "\nCommand exited with status $status. Dumping output:" - cat $outfile - fi - echo -e ". ${red}failed${reset} ($time) ${log_info:-}" - if [ "${CI:-0}" == "1" ]; then - post_github_status failure "${display_cmd:0:240}" "$url" "failed ($time)" 2>/dev/null || true - fi - fi -else - # With no redis there is no log to link to, so a CI orchestrator (e.g. the GitHub runner - # driving bootstrap_ec2) would lose the output entirely, including the build's CI Log link. - if [ "$CI_REDIS_AVAILABLE" -eq 0 ] && [ "${CI:-0}" == "1" ]; then - echo -e "\nOutput (no log store available):" - cat $outfile - fi - echo -e ". ${green}done${reset} ($time)" -fi - -exit $status diff --git a/ci3/deploy_npm b/ci3/deploy_npm deleted file mode 100755 index c6ccf1ac358..00000000000 --- a/ci3/deploy_npm +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env bash -NO_CD=1 source $(git rev-parse --show-toplevel)/ci3/source - -version=$1 - -dry_run=${DRY_RUN:-0} - -# Only two npm dist-tags are used: 'latest' for stable releases and 'prerelease' for everything else. -# Pre-release versions (nightlies, RCs, etc.) are installed by exact version, not by dist-tag. -prerelease=$(semver prerelease $version) -if [ -n "$prerelease" ]; then - npm_tag="prerelease" -else - npm_tag="latest" -fi - -# Point .npmrc config to one that relies on 'NPM_TOKEN' in the environment. A private release -# (INTERNAL_NPM_REGISTRY set) has already exported NPM_CONFIG_GLOBALCONFIG — an .npmrc that scopes -# @aztec/@noir-lang to our internal GCP Artifact Registry npm repo (see bootstrap.sh::private_release) -# — so we leave it in place; a normal release uses the npmjs config. -if [ -z "${INTERNAL_NPM_REGISTRY:-}" ]; then - export NPM_CONFIG_GLOBALCONFIG="$root/ci3/npm/.npmrc" -fi - -package_name=$(jq -r '.name' package.json) - -# Pack mode: with NPM_PACK_DIR set the release build only produces the publishable tarball (same -# version stamping and workspace rewriting as a publish) and drops it into that directory. The public -# release job then publishes the collected tarballs from a GitHub runner with npm trusted publishing -# (ci3/publish_npm_release), so the build machine never holds npm credentials. -pack_dir=${NPM_PACK_DIR:-} -if [ -n "$pack_dir" ]; then - echo_header "packing $package_name" - published_version="" -else - echo_header "publishing $package_name" - published_version=$(npm show . version --tag $npm_tag 2>/dev/null | grep -vE '^@' || true) -fi - -# Check if there is already a published package equal to given version, assume this is a re-run of a deploy. -if [ "$version" == "$published_version" ]; then - echo "Tagged $npm_tag version $version is equal to published $npm_tag version $published_version." - echo "Skipping publish." - exit 0 -fi - -# Where this package lives in the tree, for the repository field release_prep_package_json stamps. -export NPM_PACKAGE_DIR -NPM_PACKAGE_DIR=$(python3 -c 'import os,sys; print(os.path.relpath(sys.argv[1], sys.argv[2]))' "$PWD" "$root" 2>/dev/null || true) - -# Create temp directory for publishing -tmpdir=$(mktemp -d) -function cleanup { - rm -rf "$tmpdir" -} -trap cleanup SIGINT SIGTERM EXIT - -# Use npm pack instead of yarn pack. yarn pack fails on portal: dependencies (e.g. bb.js) -# because its beforeWorkspacePacking hook can't resolve workspace:^ for portal links. -# npm pack just packs the files without trying to resolve workspace refs. -npm pack --pack-destination "$tmpdir" --quiet - -# Extract and prepare -cd "$tmpdir" -tar -xzf *.tgz -cd package - -# Replace workspace:^ with concrete versions. -release_prep_package_json $version - -if [ -n "$pack_dir" ]; then - mkdir -p "$pack_dir" - # The pack above already ran the package's prepack scripts inside its workspace; this extracted - # copy has no workspace, so they must not run again (same reason publish uses --ignore-scripts). - npm pack --ignore-scripts --pack-destination "$pack_dir" --quiet - exit 0 -fi - -# Publish from temp dir -if [ "$dry_run" -eq 1 ]; then - denoise "npm publish --ignore-scripts --dry-run --tag $npm_tag --access public" -else - # Check if version exists. - if npm view "$package_name@$version" version >/dev/null 2>&1; then - # Tag the existing version. - npm dist-tag add $package_name@$version $npm_tag - else - # Publish new version. - denoise "npm publish --ignore-scripts --tag $npm_tag --access public" - fi -fi diff --git a/ci3/dist_tag b/ci3/dist_tag deleted file mode 100755 index a68262cb61b..00000000000 --- a/ci3/dist_tag +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash -# Returns the release channel name from a semver tag (e.g. "nightly" from "5.0.0-nightly.20260415"). -# Used for S3 aliases (aztec-up, playground), git branch pushes (l1-contracts), and docker tags. -# NOT used for npm dist-tags — deploy_npm computes those independently. -source $(git rev-parse --show-toplevel)/ci3/source -version=$REF_NAME -if [ -n "${DIST_TAG:-}" ]; then - echo "$DIST_TAG" - return -fi -if ! semver check $version; then - echo_stderr "$version is not a valid semver." - exit 1 -fi -dist=$(semver prerelease $version | sed 's/\..*//') -if [ -n "$dist" ]; then - echo $dist -else - echo latest -fi diff --git a/ci3/do_or_dryrun b/ci3/do_or_dryrun deleted file mode 100755 index 1a4e7f651da..00000000000 --- a/ci3/do_or_dryrun +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash -if [ ${DRY_RUN:-0} = 0 ]; then - exec "$@" -else - echo "DRY: $@" -fi diff --git a/ci3/docker_has_image b/ci3/docker_has_image deleted file mode 100755 index a94c76318e8..00000000000 --- a/ci3/docker_has_image +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -IMAGE="$1" -docker image ls --format '{{.Repository}}:{{.Tag}}' | grep -q "$IMAGE" diff --git a/ci3/docker_isolate b/ci3/docker_isolate deleted file mode 100755 index bbf0880b161..00000000000 --- a/ci3/docker_isolate +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env bash -# Isolate the network stack etc using docker. -# Carefully set things so we can use our git repo mounted as-is. -NO_CD=1 source $(git rev-parse --show-toplevel)/ci3/source - -cmd=$1 -export CPUS=${CPUS:-2} -export MEM=${MEM:-$((CPUS * 4))g} -export TMPFS_SIZE=${TMPFS_SIZE:-1g} -export USE_HOME_TMP=${USE_HOME_TMP:-0} - -function cleanup { - if [ -n "${cid:-}" ]; then - docker rm -f "$cid" &>/dev/null - fi -} -trap cleanup EXIT - -trap exit SIGINT SIGTERM - -# Compute container name. -if [ -n "${NAME:-}" ]; then - name="$(echo "${NAME}" | sed 's/^[^a-zA-Z0-9]*//' | tr '/' '_')${NAME_POSTFIX:-}" - name_arg="--name $name" - # Kill any existing container with the same name. - docker rm -f $name &>/dev/null || true -fi - -# For pinning to given CPUs. -[ -n "${CPU_LIST:-}" ] && cpuset_arg="--cpuset-cpus=$CPU_LIST" - -# Env vars to inject into the container. -arg_env_vars=() -for env in ${ENV_VARS_TO_INJECT:-}; do - if [ -n "${!env:-}" ]; then - echo "$env=${!env}" - arg_env_vars+=("-e" "$env=${!env}") - fi -done - -# Tests don't usually need network access, so disable it by default. -network_arg="--net=none" -[ "${NET:-0}" -eq 1 ] && network_arg="" - -# Set up tmp mount - default to tmpfs, optionally use $HOME/tmp -if [ "${USE_HOME_TMP}" -eq 1 ]; then - # Ensure $HOME/tmp exists - mkdir -p $HOME/tmp - tmp_mount="-v$HOME/tmp:/tmp:z" -else - # Default: use tmpfs with specified size - tmp_mount="--tmpfs /tmp:exec,size=$TMPFS_SIZE" -fi - -# Launch the container in the background. -# Don't launch in the foreground or you can't process SIGINT/SIGTERM. -# Don't use & as we want to block, and be sure it starts before processing any signals. -set -x -cid=$(docker run -d \ - ${name_arg:-} \ - ${network_arg:-} \ - ${cpuset_arg:-} \ - --cpus=$CPUS \ - --memory=$MEM \ - --user $(id -u):$(id -g) \ - -v$HOME:$HOME \ - $tmp_mount \ - --workdir $PWD \ - -e HOME \ - -e VERBOSE \ - -e GIT_CONFIG_GLOBAL=$root/build-images/src/home/.gitconfig \ - -e FORCE_COLOR=true \ - -e CPUS \ - -e MEM \ - -e TMPFS_SIZE \ - -e USE_HOME_TMP \ - -e AVM \ - -e ELU_MONITOR_FILE \ - -e TEST_TIMING_FILE \ - -e RUN_ID \ - "${arg_env_vars[@]}" \ - aztecprotocol/build:3.2 \ - /bin/bash -c "$cmd") -set +x -# At this point we know it's started, and can handle SIGTERM to kill the container. - -# Output logs, but use & so we can still handle signals. -docker logs -f $cid & -wait $! -exit $(docker wait $cid) diff --git a/ci3/dump_fail b/ci3/dump_fail deleted file mode 100755 index d70bf0b959a..00000000000 --- a/ci3/dump_fail +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash -# If the given command succeeds, only stdout is output. -# If the given command fails, both stdout and stderr are output. -# As stdout is output as normal, the caller can redirect to /dev/null if they want to silence that also. -# This enables use in captures: -# my_var=$(dump_fail some_command) -# Or as a denoiser where you want no output unless the command fails: -# dump_fail some_command > /dev/null -set -uo pipefail -source $(git rev-parse --show-toplevel)/ci3/source_color - -if [ "$#" -lt 1 ]; then - echo "Usage: $0 [args...]" - exit 1 -fi - -# Need a temp to capture stderr. -stdout=$(mktemp) -stderr=$(mktemp) -trap "rm $stdout $stderr" EXIT - -bash -c "$1" >$stdout 2>$stderr & -pid=$! -trap 'kill -SIGTERM $pid &>/dev/null || true' SIGTERM -# We wait twice, as the first awakening might be due to this script receiving SIGTERM. -# In that case wait will return with 143, and the trap handler is immediately called. -# We then wait on the pid again, to get the actual status code. -wait $pid -wait $pid -status=$? - -# 0 or SIGTERM considered a success. -if [ "$status" -ne 0 ] && [ "$status" -ne 143 ]; then - { - echo -e "${red}command failed${reset}: $* (exit: $status)" - echo -e "${blue}--- stdout ---${reset}" - cat $stdout - echo -e "${blue}--- stderr ---${reset}" - cat "$stderr" - echo -e "${blue}--------------${reset}" - } >&2 -else - cat $stdout -fi - -exit $status diff --git a/ci3/echo_header b/ci3/echo_header deleted file mode 100755 index bf8c05953a0..00000000000 --- a/ci3/echo_header +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash -set -eu -source $(dirname "$0")/source_color - -echo -e "${purple}---${reset} ${blue}${bold}$@${reset} ${purple}---${reset}" diff --git a/ci3/exec_test b/ci3/exec_test deleted file mode 100755 index 31fc0e6dec3..00000000000 --- a/ci3/exec_test +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env bash -# Execute a test command with proper resource limits and logging. -# Usage: exec_test -# -# full_cmd format: [:VAR=val]... -# Output goes to stdout - caller handles redirection. -# -# Handles: -# - Variable extraction (CPUS, TIMEOUT, ISOLATE, etc.) -# - Log header (Command, Commit, Resources, etc.) -# - Test execution with taskset/docker_isolate -# - Timestamps on output -# -# Returns: exit code from test - -NO_CD=1 source $(git rev-parse --show-toplevel)/ci3/source - -full_cmd="$1" - -[ -z "$full_cmd" ] && exit 0 - -# Extract test parameters (hash_prefix, test_cmd, CPUS, TIMEOUT, MEM, etc.) -source $ci3/source_test_params - -# Compute test hash for history tracking -test_hash=$(hash_str_orig "$test_cmd") - -# Print log header -if [ -n "${PARENT_LOG_ID:-}" ]; then - echo -e "Parent Log: $(ci_term_link $PARENT_LOG_ID)" -fi -cat < >(add_timestamps) -else - [ "${ONLY_TERM_PARENT:-0}" -eq 1 ] && fg_arg="--foreground" - taskset -c $CPU_LIST timeout ${fg_arg:-} -v $TIMEOUT bash -c "$test_cmd" &> >(add_timestamps) -fi -code=$? -set -e - -# Upload ELU data if present (written by e2e tests, absent for C++/Rust/Noir). -if [ -s "$ELU_MONITOR_FILE" ]; then - echo "ELU file: $ELU_MONITOR_FILE ($(wc -l < "$ELU_MONITOR_FILE") lines, $(wc -c < "$ELU_MONITOR_FILE") bytes)" - cat "$ELU_MONITOR_FILE" | cache_log "elufile:${NAME:-unknown}" - rm -f "$ELU_MONITOR_FILE" -fi - -# Upload per-test timing data to S3 if present (written by e2e tests, absent for C++/Rust/Noir). -# Keyed by the job's CI_LOG_ID (the decimal id in a job's dashboard URL) and the individual test's -# log id (LOG_ID, passed by run_test_cmd; same id as the test's log at ci.aztec-labs.com/), -# so a test log maps directly to its timing file. -# Object lands at s3://aztec-ci-artifacts/logs/test-timings//.log.gz (gzipped JSONL). -if [ -s "$TEST_TIMING_FILE" ]; then - if [ -z "${CI_LOG_ID:-}" ] || [ -z "${LOG_ID:-}" ]; then - echo "Warning: not uploading test timings; CI_LOG_ID or LOG_ID missing (CI_LOG_ID='${CI_LOG_ID:-}' LOG_ID='${LOG_ID:-}')." - else - echo "Test timing file: $TEST_TIMING_FILE ($(wc -l <"$TEST_TIMING_FILE") lines, $(wc -c <"$TEST_TIMING_FILE") bytes)" - cat "$TEST_TIMING_FILE" | gzip | cache_s3_transfer_to test-timings "${CI_LOG_ID}/${LOG_ID}" - fi - rm -f "$TEST_TIMING_FILE" -fi - -exit $code diff --git a/ci3/fd_monitor b/ci3/fd_monitor deleted file mode 100755 index 9ba5cf925e8..00000000000 --- a/ci3/fd_monitor +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash -# Periodically logs which processes hold given pipe FD(s) open. -# Used to debug container script exit hangs caused by leaked FDs. -# Usage: fd_monitor [ ...] -# Close stderr to prevent holding inherited pipeline FDs open (see cache_log). -exec 0/dev/null -pipes=("$@") -while true; do - for pipe in "${pipes[@]}"; do - echo "=== Holders of $pipe ===" - for f in /proc/[0-9]*/fd/*; do - [ "$(readlink "$f" 2>/dev/null)" = "$pipe" ] || continue - pid=${f#/proc/}; pid=${pid%%/*} - fdnum=${f##*/} - comm=$(cat /proc/$pid/comm 2>/dev/null || echo "?") - ppid=$(awk '/^PPid:/{print $2}' /proc/$pid/status 2>/dev/null || echo "?") - cmdline=$(tr '\0' ' ' /dev/null) - echo " pid=$pid ppid=$ppid fd=$fdnum comm=$comm cmd=$cmdline" - done - done - echo - sleep 10 -done diff --git a/ci3/filter_cached_test_cmd b/ci3/filter_cached_test_cmd deleted file mode 100755 index 4ca27c290f9..00000000000 --- a/ci3/filter_cached_test_cmd +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env bash -NO_CD=1 source $(git rev-parse --show-toplevel)/ci3/source - -function process_batch { - set -euo pipefail - - # Read all lines from stdin into an array. - mapfile -t lines - - # Build array of hashes to query for. - local hashes=() - for line in "${lines[@]}"; do - hashes+=($(hash_str_orig "$line")) - done - - # Batch resolve in redis. - readarray -t results < <(redis_cli MGET ${hashes[@]}) - - # Iterate over the results and output lines whose hashes don't exist in redis. - for i in "${!results[@]}"; do - if [ -z "${results[$i]}" ] || [[ "${lines[$i]}" == *disabled-cache* ]]; then - echo "${lines[$i]}" - else - echo -e "${blue}SKIPPED${reset} ($(ci_term_link ${results[$i]})): ${lines[$i]}" >&3 - fi - done -} -export -f process_batch - -if [ "${USE_TEST_CACHE:-0}" -eq 1 ] && [ "$CI_REDIS_AVAILABLE" -eq 1 ]; then - exec 3> >(cache_log "Skipped tests") - # Process stdin in batches of 50. - parallel --pipe -N50 --keep-order process_batch - exec 3>&- - wait -else - # Noop. - cat -fi diff --git a/ci3/filter_test_cmds b/ci3/filter_test_cmds deleted file mode 100755 index 778ce22490e..00000000000 --- a/ci3/filter_test_cmds +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash -NO_CD=1 source $(git rev-parse --show-toplevel)/ci3/source - -# Inject MAKEFILE_TARGET into hash prefix if set (strip -tests suffix). -# This allows bisect_flake and grind_test to do targeted builds. -function inject_makefile_target { - if [ -n "${MAKEFILE_TARGET:-}" ]; then - local mt="${MAKEFILE_TARGET%-tests}" - sed "s/^\([^ ]*\) /\1:MAKEFILE_TARGET=$mt /" - else - cat - fi -} - -# Filters any test cmd that marked to skip in the .test_patterns.yml file. -# Then filter through cache to remove previously successfully run tests. -grep -Ev -f <(yq e '.tests[] | select(.skip == true) | .regex' $root/.test_patterns.yml) | inject_makefile_target | filter_cached_test_cmd diff --git a/ci3/get_test_entry b/ci3/get_test_entry deleted file mode 100755 index bc6e66f95e2..00000000000 --- a/ci3/get_test_entry +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash -# Returns the full test entry (as JSON) based on matching test name pattern and optional error regex. -NO_CD=1 source $(git rev-parse --show-toplevel)/ci3/source - -export test_cmd=${1:-} -log_file=${2:-} -# The labs submodule's tests run under this engine too, so its quarantine list applies to -# them as well as this repo's own. -pattern_files=.test_patterns.yml -[ -f labs/.test_patterns.yml ] && pattern_files+=" labs/.test_patterns.yml" -entries=$(for f in $pattern_files; do yq e -o=json -r 'explode(.) | .tests[] | .regex as $pattern | select(strenv(test_cmd) | test($pattern))' "$f"; done | jq -c .) - -# Iterate over each matching entry and output those that match error_regex (if present) -while IFS= read -r entry; do - error_regex=$(echo "$entry" | jq -r '.error_regex // empty') - - # If error_regex is present, check the log content. - if [ -n "$error_regex" ]; then - # Strip color codes from the log file. - # Do *not* use a pipe here. grep -q will exit immediately on first match and break the pipe. - if grep -qE "$error_regex" <(sed 's/\x1B\[[0-9;]*[a-zA-Z]//g' "$2"); then - echo "$entry" - fi - else - echo "$entry" - fi -done <<< "$entries" diff --git a/ci3/get_test_owner b/ci3/get_test_owner deleted file mode 100755 index c96731d6019..00000000000 --- a/ci3/get_test_owner +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash -# Prints a list of test owners based on matching test name pattern, and optional error regex. -NO_CD=1 source $(git rev-parse --show-toplevel)/ci3/source - -# Use get_test_entry to get matching entries, then extract unique owners -get_test_entry "$@" | jq -r '.owners[]' | sort -u diff --git a/ci3/grind_test b/ci3/grind_test deleted file mode 100755 index b4e4a111b34..00000000000 --- a/ci3/grind_test +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env bash - -# Wrapping in {} ensures bash parses the entire script before executing it. -# Avoids nerfing self when checking out different commits. -{ - -NO_CD=1 source $(git rev-parse --show-toplevel)/ci3/source - -full_cmd="$1" -timeout="${2:-10m}" -# Jobs as percentage of CPUs (default 200% = nproc * 2) -jobs_pct="${3:-200}" -# Memsuspend threshold as percentage of total memory (default 50%) -memsuspend_pct="${4:-50}" -commit_tip=$(git rev-parse HEAD) -commit="${5:-$commit_tip}" -jobs=$(($(nproc) * jobs_pct / 100)) - -# Ensure we return to the original commit on exit. -trap 'git checkout $commit_tip' EXIT - -# Extract test parameters (hash_prefix, test_cmd, CPUS, TIMEOUT, MAKEFILE_TARGET, etc.) -source $ci3/source_test_params - -function checkout { - git reset --hard $1 --quiet - denoise "git submodule update --init --recursive --depth 1 --jobs 8" - git checkout $commit_tip -- ci3 -} - -function build { - if [ -n "${MAKEFILE_TARGET:-}" ]; then - echo "Building target: $MAKEFILE_TARGET..." - make $MAKEFILE_TARGET - else - echo "Building (full bootstrap)..." - ./bootstrap.sh - fi -} - -function grind { - echo_header "grinding for $timeout" - # NAME_POSTFIX allows for grinding compose tests in parallel. - local par_cmd="NAME_POSTFIX={} CI=0 TRACK_TEST_FAIL=1 run_test_cmd '${hash_prefix}:NAME=test-{} ${test_cmd}'" - local joblog=$(mktemp) - - # Run for full timeout, logging all results (no --halt, collect all failure data) - # memsuspend_pct is the % of memory usage at which to start suspending jobs - # parallel suspends at 2*SIZE and runs 1 job at SIZE, so SIZE = mem * (100 - pct) / 200 - local mem=$(total_mem_gb) - local memsuspend_gb=$((mem * (100 - memsuspend_pct) / 200)) - awk 'BEGIN {i=1; while (1) print i++}' | timeout $timeout parallel --memsuspend ${memsuspend_gb}G --halt now,fail=10 --joblog "$joblog" -j $jobs --line-buffer "$par_cmd" || true - - # Check if any jobs failed (column 7 is exitval in joblog) - if awk 'NR>1 && $7!=0 {found=1} END {exit !found}' "$joblog"; then - rm -f "$joblog" - return 1 # Had failures - fi - rm -f "$joblog" - return 0 # No failures -} - -echo_header "grind test" -echo "Full cmd: $full_cmd" -echo "Make target: ${MAKEFILE_TARGET:-}" -echo "Grind for: $timeout" -echo "Commit: $commit" -echo "Ref name: $REF_NAME" -echo "Jobs: $jobs (${jobs_pct}% of $(nproc) CPUs)" -echo "Memsuspend: ${memsuspend_pct}% of memory" -echo - -ulimit -n $(ulimit -Hn) - -checkout $commit -build -grind - -} diff --git a/ci3/internal/augment_prompt b/ci3/internal/augment_prompt deleted file mode 100755 index e3dc524043b..00000000000 --- a/ci3/internal/augment_prompt +++ /dev/null @@ -1,2 +0,0 @@ -# Used to augment the prompt when using start_interactive and zsh. -echo "b " \ No newline at end of file diff --git a/ci3/log_ci_run b/ci3/log_ci_run deleted file mode 100755 index f4946e72a67..00000000000 --- a/ci3/log_ci_run +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env bash -NO_CD=1 source $(git rev-parse --show-toplevel)/ci3/source - -key=${1:?usage: log_ci_run [status]} -status=${2:-RUNNING} - -if [ -z "${RUN_ID:-}" ]; then - echo_stderr "No RUN_ID set in log_ci_run. This ci run won't be seen on the dashboards." - exit -fi - -# Sliding TTL on the section ZSET: refreshed on every write below, so a section -# lives as long as it sees runs and is dropped 90 days after its last one. This is -# what lets the dashboard landing page prune retired org/repo/section combinations by -# a simple EXISTS check rather than scanning timestamps. -SECTION_TTL=$((60 * 60 * 24 * 90)) - -if [ -z "${CI_DASHBOARD:-}" ]; then - # This path is deprecated. Set CI_DASHBOARD explicitly in ci.sh. - # CI runs are grouped by: - # - The 'release' group if tagged with a semver. - # - Or by the target branch of the merge queue they enter. - # - Or in the 'prs' group. - if semver check "$REF_NAME"; then - section="releases" - elif [[ "$REF_NAME" =~ ^gh-readonly-queue/ ]]; then - section="$TARGET_BRANCH" - else - section="prs" - fi -else - section="$CI_DASHBOARD" -fi - -# Scope the section by repository, so repositories sharing the dashboard don't collide -# (and one repo's runs can't evict another's under the 1000-entry cap). The dashboard -# string "//
" is what the landing page lists. -repo="${GITHUB_REPOSITORY:-}" -if [ -n "$repo" ]; then - dashboard="$repo/$section" -else - dashboard="$section" # callers with no GITHUB_REPOSITORY keep the unscoped key -fi -range_key="ci-run-$dashboard" - -if [ "$status" = "RUNNING" ]; then - msg=$(git log -1 --pretty=format:"%s") - author="$(git log -1 --pretty=format:"%an")" - name=$REF_NAME - [ "$(aws_get_meta_data instance-life-cycle)" == "spot" ] && spot=true || spot=false - instance_type=$(aws_get_meta_data instance-type 2>/dev/null || echo "unknown") - instance_vcpus=$(nproc 2>/dev/null || echo 0) - - # Extract PR number from branch name or merge queue ref - pr_number="" - if [[ "$REF_NAME" =~ [Pp][Rr]-?([0-9]+) ]]; then - pr_number="${BASH_REMATCH[1]}" - fi - - # If this is github merge queue, just keep the queue name. - if [[ "$name" =~ ^gh-readonly-queue/([^/]+)/ ]]; then - name="${BASH_REMATCH[1]} (queue)" - fi - - msg=$(pr_link "$msg") - - json=$(jq -c -j -n \ - --argjson timestamp "$key" \ - --arg run_id "${RUN_ID:-}" \ - --arg job_id "${JOB_ID:-}" \ - --arg status "$status" \ - --arg msg "$msg" \ - --arg name "$name" \ - --arg author "$author" \ - --arg arch "$(arch)" \ - --argjson spot "$spot" \ - --arg instance_type "$instance_type" \ - --argjson instance_vcpus "$instance_vcpus" \ - --arg pr_number "$pr_number" \ - --arg dashboard "$dashboard" \ - --arg repo "$repo" \ - --arg github_actor "${GITHUB_ACTOR:-}" \ - '{timestamp: $timestamp, run_id: $run_id, job_id: $job_id, status: $status, msg: $msg, name: $name, author: $author, github_actor: $github_actor, arch: $arch, spot: $spot, instance_type: $instance_type, instance_vcpus: $instance_vcpus, pr_number: $pr_number, dashboard: $dashboard, repo: $repo}') - # echo "$json" >&2 - redis_cli ZADD $range_key $key "$json" &>/dev/null - redis_cli SETEX hb-$key 60 1 &>/dev/null - - # To keep only the last 1000 entries (by score). - redis_cli ZREMRANGEBYRANK $range_key 0 -1001 &>/dev/null -else - # Retrieve the current entry. - old_json=$(redis_cli ZRANGEBYSCORE $range_key $key $key) - # Compute duration. - now=$(date +%s%3N) - json=$(echo "$old_json" | jq -c -j \ - --arg status "$status" \ - --argjson complete "$now" \ - '. * { status: $status, complete: $complete }') - # echo "$json" >&2 - # Remove the old entry. - redis_cli ZREM $range_key "$old_json" &>/dev/null - # Add the updated entry with the same key. - redis_cli ZADD $range_key $key "$json" &>/dev/null -fi - -# Refresh the section's sliding TTL and register it in the landing page's index. Done on -# both create and update so the window tracks last activity, not first. -redis_cli EXPIRE $range_key $SECTION_TTL &>/dev/null -redis_cli SADD ci-run-sections "$dashboard" &>/dev/null diff --git a/ci3/lua/set-filter.lua b/ci3/lua/set-filter.lua deleted file mode 100644 index ce5467962a1..00000000000 --- a/ci3/lua/set-filter.lua +++ /dev/null @@ -1,56 +0,0 @@ --- local key = KEYS[1] --- local offset = tonumber(ARGV[1]) --- local limit = tonumber(ARGV[2]) --- local property = ARGV[3] --- local pattern = ARGV[4] - --- if not property or property == "" or not pattern or pattern == "" then --- return redis.call('ZREVRANGE', key, offset, offset + limit) --- end - --- local allMembers = redis.call('ZREVRANGE', key, 0, -1) --- local result = {} --- for _, member in ipairs(allMembers) do --- local ok, parsed = pcall(cjson.decode, member) --- if ok and parsed[property] and string.find(parsed[property], pattern) then --- table.insert(result, member) --- if #result >= limit then break end --- end --- end --- return result - -local key = KEYS[1] -local offset = tonumber(ARGV[1]) -local limit = tonumber(ARGV[2]) -local property = ARGV[3] -local pattern = ARGV[4] -local result = {} -local skip = 0 - -local allMembers = redis.call('ZREVRANGE', key, 0, -1) -for _, member in ipairs(allMembers) do - local ok, parsed = pcall(cjson.decode, member) - if ok then - if parsed["status"] == "RUNNING" then - local ts = parsed["timestamp"] - local hb_key = "hb-" .. tostring(ts) - if redis.call("EXISTS", hb_key) == 0 then - parsed["status"] = "INACTIVE" - end - end - local include=true - if property and pattern and parsed[property] and not string.find(parsed[property], pattern) then - include=false - end - if include then - if skip >= offset then - table.insert(result, cjson.encode(parsed)) - if #result >= limit then break end - else - skip = skip + 1 - end - end - end -end - -return result diff --git a/ci3/memsuspend_limit b/ci3/memsuspend_limit deleted file mode 100755 index 1c1678faa28..00000000000 --- a/ci3/memsuspend_limit +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash -set -eu - -# This informs the --memsuspend flag for GNU parallel. -# Per the documentation: -# --memsuspend size -# If the available memory falls below 2 * size, GNU parallel will suspend some of the running jobs. -# If the available memory falls below size, only one job will be running. -# We set memsuspend value to 1/4 the total memory. -# So suspension will start when we exceed 1/2 available memory. -# Only 1 job will be running when we exceed 3/4 available memory. -# If the system has >= 256GB, we act like we have 256GB (set to 64GB). - -if [ -n "${MEMSUSPEND:-}" ]; then - echo $MEMSUSPEND - exit -fi - -mem=$(total_mem_gb) - -# Max out at one fourth of 256GB (64GB memsuspend). -echo $(( mem < 256 ? $((mem / 4)) : 64 ))G diff --git a/ci3/memusage b/ci3/memusage deleted file mode 100755 index 341d3fce241..00000000000 --- a/ci3/memusage +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env bash -# memusg -- Measure peak memory usage of a command, polling every second. -# Usage: memusage output-file COMMAND [ARGS]... -# -# Runs the process, outputting peak memory usage to 'output-file' in -# amount of megabytes. -# -# Author: Jaeho Shin -# Created: 2010-08-16 -############################################################################ -# Copyright 2010 Jaeho Shin. # -# # -# Licensed under the Apache License, Version 2.0 (the "License"); # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http://www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -set -eu - -memory_output_file="${MEMUSAGE_OUT:-&2}" - -# detect OS-specific ps command -case $(uname) in - Darwin|*BSD) function sizes { /bin/ps -o rss= -g $1; } ;; - Linux) function sizes { /bin/ps -o rss= $1; } ;; - *) echo "$(uname): unsupported OS" >&2; exit 2 ;; -esac - -# run the target command in the background in its own process group -setsid "$@" & -cmd_pid=$! -sleep 1 # wait for the command to get a process group -cmd_pgid=$(ps -o pgid= $cmd_pid) - -# start monitoring in background -( - peak=0 - while kill -0 "$cmd_pid" 2>/dev/null && sizes_out=$(sizes "$cmd_pgid"); do - sample=$(echo "$sizes_out" | awk '{sum += $1} END {print sum}') - if [[ -n "$sample" && "$sample" -gt "$peak" ]]; then - peak=$sample - fi - sleep 0.1 - done - echo "$((peak / 1024))" > "$memory_output_file" -) & -monitor_pid=$! -trap "kill -- -$cmd_pgid &>/dev/null || true" EXIT SIGINT - -# wait for command, save its exit code -wait "$cmd_pid" -exit_code=$? - -# clean up monitor -wait "$monitor_pid" - -exit "$exit_code" diff --git a/ci3/npm_install_deps b/ci3/npm_install_deps deleted file mode 100755 index 4e134bf3ee7..00000000000 --- a/ci3/npm_install_deps +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env bash -# In CI we leverage fast caching of node_modules folders using zstd. -# Note this cache pull won't restore .yarn, which means a subsequent "yarn install" may still be slow. -# This doesn't matter in CI however. -# Takes optional extra dependencies (files, directories, or patterns) to include in the cache hash. -NO_CD=1 source ${root:-$(git rev-parse --show-toplevel)}/ci3/source -# We assume that `yarn.lock` sits within the root directory of the git repository we're currently in. -# -# We cannot assume that this is equal to `$root` as we may be within a nested repository -# whereas `$root` points to the root of the aztec-packages monorepo. -REPO_PATH=$(git rev-parse --show-toplevel) - -if [ ! -f yarn.lock ]; then - echo_stderr "yarn.lock not found in $PWD." - exit 1 -fi - -yarn_lock_path=$(realpath yarn.lock --relative-to=$REPO_PATH) -package_json_path=$(realpath package.json --relative-to=$REPO_PATH) -extra_deps=("$@") - -# We only use the node module cache in CI. -# The checking for ci-started makes sure we really *are* on a CI machine (it's created in bootstrap_ec2). -if [ "$CI" -eq 1 ] && [ -f $HOME/ci-started ] && [ "${NO_CACHE:-0}" -eq 0 ]; then - nm_hash=$(cache_content_hash "^$yarn_lock_path" "^$package_json_path" "${extra_deps[@]}") - # Several targets install the same project (e.g. barretenberg/ts is installed by - # build_bb_js, build_bb_avm_sim and build_cdb). Extracting the archive again is not - # harmless: tar unlinks each entry before recreating it, so a test that the engine is - # already running out of this tree sees a module vanish (ERR_MODULE_NOT_FOUND) or has a - # file it mapped replaced underneath it (SIGBUS). Record what is installed and do - # nothing when it already matches. The marker lives inside node_modules, so wiping the - # tree invalidates it, and it is written before upload so restores carry it too. - marker=node_modules/.ci3-nm-hash - if [ "$(cat $marker 2>/dev/null)" == "$nm_hash" ]; then - echo_stderr "node_modules already installed at $nm_hash." - exit 0 - fi - if ! cache_download node-modules-$nm_hash.zst; then - denoise "retry 'find . -type d -iname node_modules -not -path \"*/node_modules/*\" | xargs rm -rf && yarn install --immutable'" - if [ -d node_modules ]; then echo "$nm_hash" >$marker; fi - cache_upload node-modules-$nm_hash.zst $(find . -type d -iname node_modules -not -path "*/node_modules/*") - else - if [ -d node_modules ]; then echo "$nm_hash" >$marker; fi - fi -else - if [ "$CI" -ne 1 ]; then - export YARN_ENABLE_IMMUTABLE_INSTALLS=false - fi - denoise "yarn install" -fi diff --git a/ci3/os b/ci3/os deleted file mode 100755 index 1319c3a8b26..00000000000 --- a/ci3/os +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -set -e -case "$(uname -s)" in - Linux) echo linux ;; - Darwin) echo macos ;; - *) echo "unknown" ;; -esac diff --git a/ci3/parallelize b/ci3/parallelize deleted file mode 100755 index 1556c3cf2f6..00000000000 --- a/ci3/parallelize +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bash -# For running test cmds that are pathed relative to the repo root. -# Commands are read from stdin. -NO_CD=1 source $(git rev-parse --show-toplevel)/ci3/source - -[ "${STRICT_SCHEDULING:-0}" -eq 1 ] && exec parallelize_strict "$@" - -cd $root - -if [ -z "${1:-}" ]; then - num_cpus=$(get_num_cpus) - jobs=$((num_cpus / 2)) -else - jobs=$(get_num_cpus_max ${1:-}) -fi - -parallel_args="-E STOP -j$jobs --memsuspend $(memsuspend_limit) --line-buffer --joblog joblog.txt" - -if [ "${NO_FAIL_FAST:-0}" -eq 0 ]; then - parallel_args+=" --halt now,fail=1" -fi - -echo "Starting parallel run with max $jobs jobs..." - -# Set up flakes file (gitignored, persists for local inspection) -export FLAKES_FILE="$root/.logged_test_flakes" -echo "" > "$FLAKES_FILE" # Create/clear from previous run - -if [ -t 1 ]; then - # If we're in a terminal default to a progress bar, and use cache_log to save output to redis. - # Dump output only on failure. - set +e - output=$(parallel $parallel_args --bar 'DUMP_FAIL=1 run_test_cmd {}' > >(DUP=1 stdbuf -oL cache_log "Test run")) - code=$? - if [ "$code" -ne 0 ]; then - echo -e "$output" - exit 1 - fi - set -e -else - # Non interactive environments (like CI) should just stream output. - # Caller can denoise if they want to. - parallel $parallel_args 'run_test_cmd {}' -fi - -slow_jobs=$(cat joblog.txt | \ - awk 'NR>1 && $4 > 300 {print | "sort -k4,4"}' | \ - awk '{print $4 ": " substr($0, index($0, $10))}') -if [ -n "$slow_jobs" ]; then - echo -e "${yellow}WARNING: The following tests exceed 5 minute runtimes. Break them up or rethink them.${reset}" - echo "$slow_jobs" -fi - -# We filter out the no-ops we use for parallel result liveness. -echo "Completed run of $(tail -n+2 joblog.txt | grep -v "''" | wc -l) tests in $SECONDS seconds." - -# Process flake log (check thresholds and post PR comment in CI) -if [ "$CI" -eq 1 ]; then - $root/scripts/process_flake_log.py "$FLAKES_FILE" --post-comment -else - $root/scripts/process_flake_log.py "$FLAKES_FILE" -fi diff --git a/ci3/parallelize_strict b/ci3/parallelize_strict deleted file mode 100755 index 7683638abd8..00000000000 --- a/ci3/parallelize_strict +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env bash -# For running test cmds that are pathed relative to the repo root. -# Commands are read from stdin. -NO_CD=1 source $(git rev-parse --show-toplevel)/ci3/source - -cd $root - -if [ -n "${BENCH_CPU_COUNT:-}" ]; then - num_cpus=$BENCH_CPU_COUNT -elif [ -z "${1:-}" ]; then - num_cpus=$(get_num_cpus) - num_cpus=$((num_cpus / 2)) -else - num_cpus=$(get_num_cpus_max ${1:-}) -fi - -echo "Starting parallel run using max $num_cpus cpus..." - -sem_sched init $num_cpus - -function cleanup { - [ -f "$output" ] && rm -f $output - kill -TERM $(jobs -p) &>/dev/null -} -trap cleanup EXIT - -output=$(mktemp) - -function run_and_release { - export CPU_LIST=$1 - trap 'sem_sched release $CPU_LIST' EXIT - trap 'kill $pid &>/dev/null' SIGTERM SIGINT - run_test_cmd "$cmd" & - local pid=$! - wait $pid -} - -function wait_for_job { - wait -n - ret=$? - if [ $ret -ne 0 ]; then - exit 1 - fi -} - -function run_tests { - while IFS= read -r cmd || [ -n "$cmd" ]; do - # Default. - local CPUS=2 - - # Extract the first token and set any variable assignments (locally). - # We're actually only using this to get the number of cpus we need. - hash_part="${cmd%% *}" - if [[ "$hash_part" == *:* ]]; then - IFS=':' read -ra parts <<< "$hash_part" - # The first element is the actual hash; remaining elements are variable assignments. - for var_assignment in "${parts[@]:1}"; do - local "$var_assignment" - done - fi - - # Acquire some cpus. - until CPU_LIST=$(sem_sched acquire $CPUS); do - # Not enough available. Wait for a job to complete and release some cpus. - wait_for_job - done - - run_and_release $CPU_LIST & - - num_jobs=$((num_jobs + 1)) - done -} - -num_jobs=0 - -# We'll handle errors explicitly. -set +e -set -o pipefail -run_tests | tee "$output" -run_exit=$? - -# Loop to monitor jobs until one fails or all finish. -while (( $(jobs -p | wc -l) > 0 )); do - wait_for_job -done - -# Propagate failure from run_tests (which runs in a subshell due to the pipe). -[ $run_exit -ne 0 ] && exit $run_exit - -function filter_long_times { - grep -E '\([0-9]+s\)$' | # Match lines ending with (number)s - sed 's/.*(\([0-9]\+\)s)$/\1 &/' | # Extract number and keep original line - awk '$1 > 300 {print $0}' | # Filter for times > 300s - sort -n | # Sort numerically by first column (time) - sed 's/^[0-9]\+ //' # Remove the extracted number used for sorting -} - -slow_jobs=$(cat "$output" | filter_long_times) -if [ -n "$slow_jobs" ]; then - echo -e "${yellow}WARNING: The following tests exceed 5 minute runtimes. Break them up or rethink them.${reset}" - echo "$slow_jobs" -fi - -echo "Completed run of $num_jobs tests in $SECONDS seconds." diff --git a/ci3/post_github_status b/ci3/post_github_status deleted file mode 100755 index a74c1c51bdb..00000000000 --- a/ci3/post_github_status +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env bash -# Post a GitHub commit status (pending/success/failure/error) with a target URL. -# No-op when not in CI (CI!=1) or when required env vars are missing. -# -# Usage: post_github_status [description] -# -# Examples: -# post_github_status pending "ci/build" "http://ci.aztec-labs.com/abc123" -# post_github_status failure "ci/x1-full" "http://ci.aztec-labs.com/abc123" "bootstrap failed" -# -# Environment: -# CI - must be 1 (otherwise no-op) -# GITHUB_TOKEN - GitHub API token -# GITHUB_REPOSITORY - owner/repo (e.g. AztecProtocol/aztec-packages) -# COMMIT_HASH - the SHA to post status on -set -euo pipefail - -# No-op outside CI. -[ "${CI:-0}" != "1" ] && exit 0 - -state=${1:?usage: post_github_status [description]} -context=${2:?usage: post_github_status [description]} -target_url=${3:?usage: post_github_status [description]} -description=${4:-} - -token="${GITHUB_TOKEN:-}" -repo="${GITHUB_REPOSITORY:-}" -sha="${COMMIT_HASH:-}" - -# Silently no-op if credentials are missing. -[[ -z "$token" || -z "$repo" || -z "$sha" ]] && exit 0 - -# Truncate to GitHub API limits. -context="${context:0:255}" -description="${description:0:140}" - -body=$(jq -n \ - --arg state "$state" \ - --arg target_url "$target_url" \ - --arg description "$description" \ - --arg context "$context" \ - '{state: $state, target_url: $target_url, description: $description, context: $context}') - -# Post asynchronously — don't block the caller. -curl -s -o /dev/null -X POST \ - -H "Authorization: token $token" \ - -H "Accept: application/vnd.github+json" \ - "https://api.github.com/repos/${repo}/statuses/${sha}" \ - -d "$body" & diff --git a/ci3/redact b/ci3/redact deleted file mode 100755 index 5019074499f..00000000000 --- a/ci3/redact +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env perl -# See redact_test for the testing of this script. -# -# We support github actions-style ::add-mask::some-value lines to add secrets on the fly. -# We do not recommend its use lightly however, instead relying on this script or not printing secrets. -# -# Change note (Adam Dec 19, 2025) -# Moved to perl when adding ::add::mask:: handling. -# This was the fastest of the redact scripts I tried crafting. -# The previous bash version was quadratic: -# ● With 40 secrets, 50k lines: - -# | Version | Time | Speedup | -# |----------|-------|-------------| -# | Bash/awk | 527ms | - | -# | Perl | 68ms | 7.7x faster | -# -# ● With 900 secrets, 50k lines: -# | Version | Time | Speedup | -# |----------|-------|------------| -# | Bash/awk | 9.84s | - | -# | Perl | 0.41s | 24x faster | - -use strict; -use warnings; - -# Enable auto-flush so logs appear immediately (crucial for CI/CD) -$| = 1; - -my @secrets = (); -my $re; - -# --- 1. Initial Secret Collection from ENV --- - -# Iterate over all environment variables -while (my ($key, $val) = each %ENV) { - # Check if the KEY name matches our sensitive keywords - if ($key =~ /(?:KEY|TOKEN|PASSWORD|SECRET|MNEMONIC)$/) { - next unless defined $val and $val ne ''; - - # NOTE: We assume anything with 6 or less characters is a poor secret and bail. - if (length($val) < 6) { - next; - } - - # Handle multi-line environment variables by splitting them - # so we mask each line independently if needed. - # Naughty example: 'PUBLISHERS_PER_VALIDATOR_KEY: 2' - for my $line (split /\n/, $val) { - # Apply the same min-length check per line to avoid redacting - # short tokens like "{" or "}" from multi-line JSON secrets. - next if $line eq '' or length($line) < 6; - push @secrets, $line; - } - } -} - -# Compile the initial regex if we found secrets -$re = build_regex(@secrets); - -# --- 2. Helper Function --- - -sub build_regex { - my @list = @_; - return undef unless @list; - - # 1. Sort by length descending (Longest Match First). - # This prevents "supersecret" becoming "***secret" if "super" is also masked. - # 2. Use 'quotemeta' to escape regex special chars (., *, ?, etc.) - # 3. Use 'u' (unique) logic or a hash if duplicate secrets are common, - # but sort handles priority well enough here. - @list = sort { length($b) <=> length($a) } @list; - - my $pattern = join("|", map { quotemeta $_ } @list); - return qr/$pattern/; -} - -# --- 3. Main Processing Loop --- - -while (my $line = ) { - - # Check for GitHub Actions syntax: ::add-mask::some-value - if ($line =~ /^::add-mask::(.*)$/) { - my $new_secret = $1; - - # Strip potential Windows carriage return - $new_secret =~ s/\r$//; - - # Skip single-character masks – they'd redact common structural - # chars like { } " across all output (e.g. from JSON key files - # that get split and masked line-by-line). - if ($new_secret ne "" and length($new_secret) > 1) { - push @secrets, $new_secret; - $re = build_regex(@secrets); - } - - # Swallow this line (do not print it) - next; - } - - # Apply redaction if we have an active regex - if (defined $re) { - $line =~ s/$re/***/g; - } - - print $line; -} diff --git a/ci3/release_prep_package_json b/ci3/release_prep_package_json deleted file mode 100755 index e50bc543f2b..00000000000 --- a/ci3/release_prep_package_json +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash -# Used before releases to write version and rewrite 'workspace:^' URLs. -set -euo pipefail -version=$1 - -tmp=$(mktemp) - -jq --arg v $version '.version = $v' package.json >$tmp && mv $tmp package.json - -# npm provenance (trusted publishing) is only accepted when repository.url names the repository the -# package was built from; the noir packages carry noir-lang/noir and most others carry nothing. -# NPM_PACKAGE_DIR is the package's directory in this tree (deploy_npm sets it before packing). -repo_url="git+https://github.com/${GITHUB_REPOSITORY:-AztecProtocol/aztec-packages}.git" -jq --arg u "$repo_url" --arg d "${NPM_PACKAGE_DIR:-}" \ - '.repository = ({type: "git", url: $u} + (if $d == "" then {} else {directory: $d} end))' package.json >$tmp && mv $tmp package.json - -# We update every category of dependency. -# While we don't strictly need to update devDependencies, 'workspace:^' is not a valid URL in npm. -for deps in dependencies devDependencies peerDependencies optionalDependencies; do - # Update each dependency @aztec-foundation package version in package.json. - for pkg in $(jq --raw-output "(.$deps // {}) | keys[] | select(contains(\"@aztec-foundation/\"))" package.json); do - jq --arg v $version ".$deps[\"$pkg\"] = \$v" package.json >$tmp - mv $tmp package.json - done - # Update each dependency @noir-lang package version in package.json to point to renamed packages and versions. - for pkg in $(jq --raw-output '(.'$deps' // {}) | keys[] | select(startswith("@noir-lang/"))' package.json); do - new_pkg="@aztec-foundation/noir-${pkg#@noir-lang/}" - jq --arg v "$version" --arg old "$pkg" --arg new "$new_pkg" '.'$deps'[$new] = $v | del(.'$deps'[$old])' package.json > tmp.json - mv tmp.json package.json - done -done - -# Stamp aztec_version into any published noir contract artifacts in artifacts/*.json. The build-time stamp in -# labs/noir-projects/noir-contracts/bootstrap.sh writes "dev"; $version here is the authoritative release version about to -# be written to package.json. We cannot stamp real version in bootstrap because there we don't have access to it. -if [ -d artifacts ]; then - for f in artifacts/*.json; do - [ -e "$f" ] || continue - jq --arg v "$version" '.aztec_version = $v' "$f" >$tmp && mv $tmp "$f" - done -fi diff --git a/ci3/retry b/ci3/retry deleted file mode 100755 index 5eeb45e2176..00000000000 --- a/ci3/retry +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env bash -set -u # not -e -[ "${BUILD_SYSTEM_DEBUG:-}" = 1 ] && set -x - -# By default retries any non-zero exit. With "-p " only failures whose (combined) output -# matches the extended regex are retried; any other failure exits immediately so genuine errors -# (e.g. a real compile error) are not masked by retries. -# The attempt count and the inter-attempt sleep are tunable via RETRY_ATTEMPTS (default 3) and -# RETRY_SLEEP (default 5s), e.g. RETRY_ATTEMPTS=30 RETRY_SLEEP=10 retries every 10s for ~5 min. -# Usage: retry [-p ] "" -pattern= -if [ "${1:-}" = "-p" ]; then - pattern=$2 - shift 2 -fi - -if [ -n "${RETRY_DISABLED:-}" ]; then - set -e - eval "$1" - exit -fi - -ATTEMPTS=${RETRY_ATTEMPTS:-3} -out= -[ -n "$pattern" ] && out=$(mktemp) -# Retries up to $ATTEMPTS times, sleeping ${RETRY_SLEEP:-5}s between attempts. -trap 'kill -SIGTERM $pid &>/dev/null || true; [ -n "$out" ] && rm -f "$out"' SIGTERM EXIT -for i in $(seq 1 $ATTEMPTS); do - if [ -n "$pattern" ]; then - # Capture combined output (while still streaming it) so we can decide whether the failure is - # retryable. pipefail makes the subshell exit with the command's code, not tee's, and the tee - # finishes before the subshell exits so the captured file is complete before we inspect it. - ( set -o pipefail; bash -c "$1" 2>&1 | tee "$out" ) & - else - bash -c "$1" & - fi - pid=$! - # First wait might be SIGTERM received by this script. - # Second should be the actual exit status. - wait $pid - wait $pid - code=$? - [ $code -eq 0 ] || [ $code -eq 143 ] && exit 0 - if [ -n "$pattern" ] && ! grep -Eq "$pattern" "$out"; then - >&2 echo "Not retrying: failure (code $code) did not match retry pattern: $pattern" - exit $code - fi - [ "$i" != "$ATTEMPTS" ] && sleep ${RETRY_SLEEP:-5} -done - ->&2 echo "Failed after $ATTEMPTS attempts: $1" -exit 1 diff --git a/ci3/run_compose_test b/ci3/run_compose_test deleted file mode 100755 index 0bfdcab5988..00000000000 --- a/ci3/run_compose_test +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -set -x -set +e - -name="$1" -exit_service="$2" -compose_dir="$3" - -name="$(echo "${name}${NAME_POSTFIX:-}" | sed 's/^[^a-zA-Z0-9]*//; s/[\/\.]/_/g')" -cd "$compose_dir" - -shutdown=0 -trap 'shutdown=1' TERM INT - -down() { - local down_flags="--remove-orphans --timeout 2" - if [ "${REMOVE_COMPOSE_VOLUMES:-0}" -eq 1 ]; then - down_flags="-v $down_flags" - fi - docker compose -p "$name" down $down_flags >/dev/null 2>&1 || true -} -trap down EXIT - -# Start clean -down - -# Image pulls can fail on transient registry errors (e.g. Docker Hub 502s); retry only those. -pull_flake='Bad Gateway|Service Unavailable|Gateway Time-?out|Internal Server Error|unexpected status from HEAD request|TLS handshake timeout|i/o timeout|connection reset|connection refused|Temporary failure in name resolution|unexpected EOF|toomanyrequests' -# A failed `up` can leave the exit service created but never started, and `docker wait` on it -# returns 0, so the failure has to end the test here. -retry -p "$pull_flake" "docker compose -p \"$name\" up -d --force-recreate" || exit 1 - -cid="$(docker ps -aq \ - --filter "label=com.docker.compose.project=$name" \ - --filter "label=com.docker.compose.service=$exit_service" \ - | head -n1)" - -# If asked to shutdown (or never found cid), cleanup and exit with signal-ish code / failure. -if [[ "$shutdown" -eq 1 ]]; then - exit 143 -fi -if [[ -z "$cid" ]]; then - exit 1 -fi - -docker logs -f "$cid" & - -# Block until the target container exits; exit status is the container's exit code. -rc=$(docker wait "$cid") - -# Give the backgrounded `docker logs -f` a moment to flush the container's final output -# (e.g. the trailing local-network logs) before the EXIT trap tears the container down. -sleep 1 - -exit "$rc" diff --git a/ci3/run_test_cmd b/ci3/run_test_cmd deleted file mode 100755 index 94105e7f2df..00000000000 --- a/ci3/run_test_cmd +++ /dev/null @@ -1,428 +0,0 @@ -#!/usr/bin/env bash -# Called by 'parallelize' to execute a given test cmd. -# This is a thin wrapper around exec_test that handles CI integration: -# - Cache checking -# - Redis publishing (started/failed/flaked events) -# - Flake detection and owner notification -NO_CD=1 source $(git rev-parse --show-toplevel)/ci3/source - -# We must enable job control to ensure our test runs in it's own process group. -# Otherwise, when parallel sends a TERM to the group, it will also kill any child process of the test. -# This can cause issues with proper cleanup (e.g. killing docker client processes as they run). -set -m - -cmd=$1 -[ -z "$cmd" ] && exit 0 - -is_merge_queue=0 -[[ "$REF_NAME" =~ ^gh-readonly-queue/ ]] && is_merge_queue=1 - -# If enabled, update the test log every 5s. -live_logging=0 -# If enabled, publish test started/failed/flaked events to Redis channel. -publish=0 -# If enabled, save logs for passed tests. -pass_log=0 -# Allow a flaking tests to pass rather than fail. -allow_flakes=0 -# If enabled, track test result in history tracker. -track_test_history=0 -# If enabled, track failed test results to fail tracker. -track_test_fail=0 -# If enabled, notify slack on flakes. -slack_notify_flake=0 - -if [ "$CI" -eq 1 ]; then - # CI overrides. - if [ "$CI_REDIS_AVAILABLE" -eq 1 ]; then - live_logging=1 - publish=1 - fi - pass_log=1 - allow_flakes=1 - track_test_fail=1 - - # Track the test in the history tracker if this is in merge queue, targeting a version branch, or - # targeting a merge-train. - if [[ "$is_merge_queue" -eq 1 || ("${TARGET_BRANCH:-}" =~ ^v[0-9]) || ("${TARGET_BRANCH:-}" == merge-train/*) ]]; then - track_test_history=1 - fi - - # Notify on flakes if in merge queue or a spartan merge-train. - # The spartan trains run their full test suite on pull_request (via ci-full-no-test-cache), - # not just merge_group, so without these cases the spartan PR's flakes are never reported. - [[ "$is_merge_queue" -eq 1 \ - || "$REF_NAME" == "merge-train/spartan" \ - || "$REF_NAME" == "merge-train/spartan-v5" ]] && slack_notify_flake=1 -fi - -# Env overrides. -live_logging=${LIVE_LOGGING:-$live_logging} -publish=${PUBLISH:-$publish} -pass_log=${PASS_LOG:-$pass_log} -allow_flakes=${ALLOW_FLAKES:-$allow_flakes} -track_test_history=${TRACK_TEST_HISTORY:-$track_test_history} -track_test_fail=${TRACK_TEST_FAIL:-$track_test_fail} -slack_notify_flake=${SLACK_NOTIFY_FLAKE:-$slack_notify_flake} - -# The "key" is used for test caching (should re-run or not). It includes all test framework arguments. -key=$(hash_str_orig "$cmd") - -# Extract the test command (without hash prefix) for display and tracking. -export test_cmd="${cmd#* }" - -# The "test hash" does not include framework arguments, only the test command itself. -# It is used for tracking test history. -test_hash=$(hash_str_orig "$test_cmd") - -# We can skip the test if it's already been successfully run. -# We actually pre-filter tests in CI runs so this is rarely hit. -# Commands marked 'disabled-cache' must never be served a cached result (mirrors filter_cached_test_cmd). -if [ "${USE_TEST_CACHE:-0}" -eq 1 ] && [[ "$cmd" != *disabled-cache* ]]; then - log_key=$(redis_cli GET $key) - if [ -n "$log_key" ]; then - log_info=" ($(ci_term_link $log_key))" - echo -e "${blue}SKIPPED${reset}${log_info:-}: $cmd" - exit 0 - fi -fi - -# Exit handler to cleanup. -function cleanup { - if [ -n "${publish_pid:-}" ]; then - kill $publish_pid &>/dev/null - fi - if [ -f ${tmp_file:-} ]; then - rm -f $tmp_file - fi -} -trap cleanup EXIT - -# Signal handler to forward SIGTERM/SIGINT to test process group. -# We use set -m so exec_test runs in its own process group (leader PID = test_pid). -# We must kill the process GROUP (-$pid) not just exec_test, otherwise children -# like docker_isolate are orphaned and Docker containers keep running. -function sig_handler { - [ -n "${test_pid:-}" ] && kill -TERM -$test_pid &>/dev/null - exit -} -trap sig_handler SIGTERM SIGINT - -function publish_log { - local expire=${1:-$CI_REDIS_EXPIRE} - cat $tmp_file 2>/dev/null | redis_setexz $log_key $expire -} - -function publish_log_final { - local expire=${1:-$CI_REDIS_EXPIRE} - cat $tmp_file 2>/dev/null | cache_persistent $log_key $expire -} - -# Finalize the current log and start a fresh one with a new unique key. -function rotate_log { - if [ "$CI_REDIS_AVAILABLE" -eq 1 ]; then - publish_log_final "$@" - fi - log_key=$(uuid) - log_info=" ($(ci_term_link $log_key))" - > $tmp_file - if [ "$live_logging" -eq 1 ]; then - kill ${publish_pid:-} &>/dev/null - live_publish_log & - publish_pid=$! - fi -} - -function live_publish_log { - # Not replacing previous trap as we run this function in the background. - trap 'kill $sleep_pid &>/dev/null; exit' SIGTERM SIGINT - # If the test takes longer than 30s, we enter a loop to publish the log every 5s. - sleep 30 & - local sleep_pid=$! - wait $sleep_pid - publish_log - echo -e "${blue}RUNNING${reset}${log_info:-}: $test_cmd" - # Close inherited stdout/stderr so this background process can't hold the outer - # pipeline's pipe fds open (which would prevent denoise/redact from seeing EOF). - exec >&- 2>&- - while [ -f $tmp_file ]; do - local stat=$(stat -c %Y "$tmp_file" || echo 0) - if [ $(( $(date +%s) - "$stat" )) -le 5 ]; then - publish_log - fi - sleep 5 & - sleep_pid=$! - wait $sleep_pid - done -} - -# Create a new log key and ci link. -log_key=$(uuid) -log_info=" ($(ci_term_link $log_key))" -grind_link=$(term_link "/grind?cmd=$(urlencode "$cmd")" "grind") -fail_links="($grind_link)" - -# Create a temporary file for the test log. -tmp_file=/tmp/$key -touch $tmp_file - -# Publish test started event to Redis channel. -if [ "$publish" -eq 1 ]; then - start_redis_data=$(jq -n \ - --arg status "started" \ - --arg test_cmd "$cmd" \ - --arg log_id "$log_key" \ - --arg log_url "$DASHBOARD_URL/$log_key" \ - --arg ref_name "${TARGET_BRANCH:-$REF_NAME}" \ - --arg commit_hash "$COMMIT_HASH" \ - --arg commit_author "$COMMIT_AUTHOR" \ - --arg commit_msg "$COMMIT_MSG" \ - --arg dashboard "${CI_DASHBOARD:-}" \ - '{status: $status, test_cmd: $test_cmd, log_id: $log_id, log_url: $log_url, ref_name: $ref_name, commit_hash: $commit_hash, commit_author: $commit_author, commit_msg: $commit_msg, dashboard: $dashboard, timestamp: now | todate}') - redis_publish "ci:test:started" "$start_redis_data" -fi - -# Start live log publishing if enabled. -if [ "$live_logging" -eq 1 ]; then - live_publish_log & - publish_pid=$! -fi - -function run_test { - # Reset timer and run the test in background (for prompt signal handling) using exec_test. - SECONDS=0 - set +e - # Pass this attempt's log id so exec_test can name the test's timing file after it - # (matching the test's log at ci.aztec-labs.com/). Tracks rotate_log on retries. - LOG_ID=$log_key $ci3/exec_test "$cmd" >> "$tmp_file" 2>&1 & - test_pid=$! - wait $test_pid - code=$? - - # If the test received a SIGTERM or SIGINT, we don't want to track or print anything, just exit. - if [ "$code" -eq 143 ] || [ "$code" -eq 130 ]; then - exit $code - fi -} - -function finalize_test { - if [ "$CI_REDIS_AVAILABLE" -eq 1 ]; then - # If the test succeeded and we're in CI, set success flag for test. This key is unique to the test. - # If the test succeeded and we're logging passes, save the test log. - # If the test failed, save the test log. - if [ $code -eq 0 ]; then - if [ "$CI" -eq 1 ]; then - redis_cli SETEX $key 604800 $log_key &>/dev/null - fi - if [ "$pass_log" -eq 1 ]; then - publish_log_final - else - # Scrub the link we optimistically (for live logging) set earlier. - log_info="" - fi - else - # Publish final log, extending lifetime of failure to 12 weeks. - publish_log_final $((60 * 60 * 24 * 7 * 12)) - fi - fi -} - -function track_test { - local list_key=$1 - local line=$(pr_link "$2") - local max_lines=${3:-1000} - - redis_cli LPUSH $list_key "$(date "+%m-%d %H:%M:%S"): $(echo -e "$line")" &>/dev/null - # Keeps only the last max_lines lines. - redis_cli LTRIM $list_key 0 $max_lines &>/dev/null -} - -function track_test_history { - track_test "history_${test_hash}${TARGET_BRANCH:+_$TARGET_BRANCH}" "$1" ${2:-} -} - -function track_test_failed { - track_test "failed_tests${CI_DASHBOARD:+_$CI_DASHBOARD}" "$1" 10000 -} - -function publish_redis { - local redis_data=$(jq -n \ - --arg status "$1" \ - --arg test_cmd "$cmd" \ - --arg log_url "http://ci.aztec-labs.com/$log_key" \ - --arg ref_name "${TARGET_BRANCH:-$REF_NAME}" \ - --arg commit_hash "$COMMIT_HASH" \ - --arg commit_author "$COMMIT_AUTHOR" \ - --arg commit_msg "$COMMIT_MSG" \ - --argjson code "$code" \ - --argjson duration "$SECONDS" \ - --arg dashboard "${CI_DASHBOARD:-}" \ - '{status: $status, test_cmd: $test_cmd, log_url: $log_url, ref_name: $ref_name, commit_hash: $commit_hash, commit_author: $commit_author, commit_msg: $commit_msg, exit_code: $code, duration_seconds: $duration, dashboard: $dashboard, timestamp: now | todate}') - redis_publish "ci:test:$1" "$redis_data" -} - -# Show PASSED and early out on success. -function pass { - finalize_test - - local line="${green}PASSED${reset}${log_info:-}: $test_cmd (${SECONDS}s)" - echo -e "$line" - - [ "$publish" -eq 1 ] && publish_redis "passed" - - if [ "$track_test_history" -eq 1 ]; then - local track_line="${green}PASSED${reset}${log_info:-} ${fail_links}: $test_cmd (${SECONDS}s) (${purple}$COMMIT_AUTHOR${reset}: $COMMIT_MSG)" - track_test_history "$track_line" - fi - - exit 0 -} - -# Post a GitHub commit status for a failed test, with the ci.aztec-labs.com log as target_url. -# No-ops silently if required env vars are absent (local runs, forks, etc.). -function post_github_failure_status { - local token="${GITHUB_TOKEN:-}" - local repo="${GITHUB_REPOSITORY:-}" - local sha="${COMMIT_HASH:-}" - [[ -z "$token" || -z "$repo" || -z "$sha" ]] && return 0 - - # Context: ci/, truncated to 255 chars (GitHub limit). - local context="ci/${test_cmd}" - context="${context:0:255}" - # Description: truncated to 140 chars (GitHub limit). - local description="${test_cmd}" - description="${description:0:140}" - - local body - body=$(jq -n \ - --arg state "failure" \ - --arg target_url "http://ci.aztec-labs.com/${log_key}" \ - --arg description "$description" \ - --arg context "$context" \ - '{state: $state, target_url: $target_url, description: $description, context: $context}') - - curl -s -o /dev/null -X POST \ - -H "Authorization: token $token" \ - -H "Accept: application/vnd.github+json" \ - "https://api.github.com/repos/${repo}/statuses/${sha}" \ - -d "$body" & -} - -# Show FAILED and exit with error code. -function fail { - finalize_test - - local line="${red}FAILED${reset}${log_info:-}: $test_cmd (${SECONDS}s) (code: $code)" - echo -e "$line" - - if [ "${DUMP_FAIL:-0}" -eq 1 ]; then - cat $tmp_file - echo -e "$line" - fi - - local track_line="${red}FAILED${reset}${log_info:-} ${fail_links}: $test_cmd (${SECONDS}s) (code: $code) (${purple}$COMMIT_AUTHOR${reset}: $COMMIT_MSG)" - [ "$track_test_history" -eq 1 ] && track_test_history "$track_line" - [ "$track_test_fail" -eq 1 ] && track_test_failed "$track_line" - [ "$publish" -eq 1 ] && publish_redis "failed" - [ "$CI" -eq 1 ] && post_github_failure_status - - exit $code -} - -# Resolve the slack channel used for flake notifications, based on the PR head -# branch. Defaults to empty (slack_notify falls back to #aztec3-ci). Cached in -# /tmp so parallel test runs don't repeatedly hit the gh API. -function flake_slack_channel { - local cache=/tmp/.flake_slack_channel - if [ -f "$cache" ]; then - cat "$cache" - return - fi - local head_branch="${PR_HEAD_REF:-$REF_NAME}" - if [[ "$head_branch" =~ ^gh-readonly-queue/.+/pr-([0-9]+)- ]] \ - && [ -n "${GITHUB_TOKEN:-}" ] && command -v gh >/dev/null 2>&1; then - head_branch=$(GH_TOKEN="$GITHUB_TOKEN" gh pr view "${BASH_REMATCH[1]}" \ - --repo AztecProtocol/aztec-packages --json headRefName -q '.headRefName' 2>/dev/null || true) - fi - local channel="" - case "$head_branch" in - merge-train/spartan | merge-train/spartan-v5) channel="#team-alpha-ci" ;; - esac - echo "$channel" > "$cache" - echo "$channel" -} - -# Show FLAKED and send slack message to test owner(s). Exit with success. -function flake { - local group_suffix="" - if [ -n "${flake_group_id:-}" ]; then - group_suffix=" group:$flake_group_id" - fi - local line="${purple}FLAKED${reset}${log_info:-}: $test_cmd (${SECONDS}s) (code: $code)${group_suffix}" - echo -e "$line" - - local track_line="${purple}FLAKED${reset}${log_info:-} ${fail_links}: $test_cmd (${SECONDS}s) (code: $code)${group_suffix} (${purple}$COMMIT_AUTHOR${reset}: $COMMIT_MSG)" - [ "$track_test_history" -eq 1 ] && track_test_history "$track_line" - [ "$track_test_fail" -eq 1 ] && track_test_failed "$track_line" - [ "$publish" -eq 1 ] && publish_redis "flaked" - - # Save flake to buffer file (for PR comment generation) - if [ -n "${FLAKES_FILE:-}" ]; then - echo "$line" >> "$FLAKES_FILE" - fi - - if [ "$slack_notify_flake" -eq 1 ]; then - # Send slack message to owners. - local slack_uids="" - for uid in $owners; do - slack_uids+="<@$uid> " - done - slack_notify "${slack_uids% }: Test flaked on *${TARGET_BRANCH:-$REF_NAME}*: \`$test_cmd\` $DASHBOARD_URL/$log_key" "$(flake_slack_channel)" - fi - - exit 0 -} - -run_test - -# Test passed. -[ $code -eq 0 ] && pass - -# If flakes are not allowed, fail the test. -[ "$allow_flakes" -eq 0 ] && fail - -# Get matching test entries -test_entries=$(get_test_entry "$test_cmd" "$tmp_file") - -# Extract owners from entries -owners=$(echo "$test_entries" | jq -r '.owners[]' | sort -u) - -# Extract flake_group_id from first matching entry -flake_group_id=$(echo "$test_entries" | jq -r '.flake_group_id // empty' | head -1) - -# If there's no owner for a failed test, we consider it a hard fail. -# Otherwise we perform a single retry. -if [ -z "$owners" ]; then - fail -else - failure_log_key=$log_key - failure_log_info=$log_info - rotate_log $((60 * 60 * 24 * 7 * 12)) - - echo -e "${yellow}RETRYING${reset}${log_info}: $test_cmd" - - run_test - - if [ $code -eq 0 ]; then - # Publish the retry's log, then point back at the failure for flake reporting. - if [ "$CI_REDIS_AVAILABLE" -eq 1 ]; then - publish_log_final - fi - log_key=$failure_log_key - log_info=$failure_log_info - flake - fi - - # Otherwise we failed twice in a row, so hard fail. - fail -fi diff --git a/ci3/sem_sched b/ci3/sem_sched deleted file mode 100755 index 83f9b7b822a..00000000000 --- a/ci3/sem_sched +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/env bash -source $(git rev-parse --show-toplevel)/ci3/source - -# Configuration -SHM_FILE="/dev/shm/cpu_alloc.shm" -SEM_BIN="./semc" -MUTEX_NAME="/cpu_alloc_mutex" -FREE_SEM="/cpu_alloc_free" -NUMA_FILE="/dev/shm/cpu_numa.shm" - -function init { - # Build semc if not already built. - if [ ! -f "$ci3/$SEM_BIN" ]; then - gcc -O3 -o "$ci3/$SEM_BIN" "$ci3/semc.c" - fi - - # Initialize shared memory and free semaphore if not already created. - n=${1:-$(nproc)} - head -c $n < /dev/zero | tr '\0' '0' > "$SHM_FILE" - $SEM_BIN --name "$FREE_SEM" --init $n - $SEM_BIN --name "$MUTEX_NAME" --init 1 - - # Write NUMA node boundaries (physical cores only) to companion file. - # Format: one line per node, each line is "start end" (inclusive). - > "$NUMA_FILE" - for node_dir in /sys/devices/system/node/node*; do - [ ! -f "$node_dir/cpulist" ] && continue - # Parse cpulist (e.g. "0-23,96-119") — take first range (physical cores). - range=$(cut -d, -f1 < "$node_dir/cpulist") - start=${range%-*} - end=${range#*-} - # Clamp to managed CPUs (0..n-1). - [ "$start" -ge "$n" ] && continue - [ "$end" -ge "$n" ] && end=$((n - 1)) - echo "$start $end" >> "$NUMA_FILE" - done -} - -# Acquire mutex to update shared memory and select free CPUs -function acquire { - local num_cpus="${1:-2}" - local timeout="${2:-0}" - - # Block until num_cpus are available globally. - $SEM_BIN --name "$FREE_SEM" --atomic --timeout $timeout -$num_cpus - - # Update the shared memory to mark the allocated CPUs. - $SEM_BIN --name "$MUTEX_NAME" -1 - data=$(cat "$SHM_FILE") - allocated_list=() - - # Try NUMA-local allocation if topology info exists. - if [ -f "$NUMA_FILE" ] && [ -s "$NUMA_FILE" ]; then - # Check if request can fit on a single node at all. - local fits_on_node=0 - while read -r ns ne; do - [ $((ne - ns + 1)) -ge "$num_cpus" ] && fits_on_node=1 && break - done < "$NUMA_FILE" - - if [ "$fits_on_node" -eq 1 ]; then - # Find a node with enough free CPUs. - while read -r node_start node_end; do - local free=0 - for ((i=node_start; i<=node_end; i++)); do - [ "${data:$i:1}" == "0" ] && free=$((free + 1)) - done - if [ "$free" -ge "$num_cpus" ]; then - for ((i=node_start; i<=node_end && ${#allocated_list[@]} < num_cpus; i++)); do - if [ "${data:$i:1}" == "0" ]; then - allocated_list+=($i) - data="${data:0:$i}1${data:$((i+1))}" - fi - done - break - fi - done < "$NUMA_FILE" - - # No single node had enough free — release semaphore and fail. - # Caller will wait for a job to finish and retry. - if [ "${#allocated_list[@]}" -lt "$num_cpus" ]; then - $SEM_BIN --name "$MUTEX_NAME" +1 - $SEM_BIN --name "$FREE_SEM" +$num_cpus - return 1 - fi - fi - fi - - # Fallback: linear scan (no NUMA file, or request exceeds any single node). - if [ "${#allocated_list[@]}" -lt "$num_cpus" ]; then - local len=${#data} - for ((i=0; i "$SHM_FILE" - cpu_list=$(IFS=, ; echo "${allocated_list[*]}") - $SEM_BIN --name "$MUTEX_NAME" +1 - echo $cpu_list -} - -function release { - local cpu_list="$1" - local num_cpus=0 - - $SEM_BIN --name "$MUTEX_NAME" -1 - data=$(cat "$SHM_FILE") - len=${#data} - for cpu in $(echo $cpu_list | tr ',' ' '); do - data="${data:0:$cpu}0${data:$((cpu+1))}" - num_cpus=$((num_cpus + 1)) - done - echo -n "$data" > "$SHM_FILE" - $SEM_BIN --name "$MUTEX_NAME" +1 - $SEM_BIN --name "$FREE_SEM" +$num_cpus -} - -cmd="${1:-}" -[ -n "$cmd" ] && shift 1 - -case "$cmd" in - init|acquire|release) - $cmd "$@" - ;; - *) - echo "Usage: sem_sched {init|acquire|release} [num_cpus]" - exit 1 - ;; -esac - -# Run command with taskset binding -# taskset -c "$cpu_list" "$@" diff --git a/ci3/semc.c b/ci3/semc.c deleted file mode 100644 index d7d03caf29d..00000000000 --- a/ci3/semc.c +++ /dev/null @@ -1,231 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // added for timeout functionality -#include - -#define MAX_NAME_LEN 255 // Maximum length for semaphore name - -volatile sig_atomic_t termination_requested = 0; - -static int verbose = 0; - -// Signal handler to set the term flag. -void handle_signal(int signum) { - termination_requested = 1; -} - -void usage(const char *prog_name) { - fprintf(stderr, "Usage: %s [-v] [--atomic] --name /sem_name [--init N] [+N|-N] [--timeout T] [--cleanup]\n", prog_name); - fprintf(stderr, " -v : Enable verbose output (to stderr)\n"); - fprintf(stderr, " --atomic : Use atomic_wait approach (with rollback on signal) for decrementing\n"); - fprintf(stderr, " --name /sem_name : Specify the semaphore name (must start with /)\n"); - fprintf(stderr, " --init N : Initialize semaphore with value N\n"); - fprintf(stderr, " +N or -N : Increment or decrement by N\n"); - fprintf(stderr, " --timeout T : Timeout in seconds for decrement operations\n"); - fprintf(stderr, " --cleanup : Remove the semaphore and exit\n"); - exit(EXIT_FAILURE); -} - -// printf-like function for -v mode. -void vprintf_verbose(const char *fmt, ...) { - if (!verbose) - return; - va_list args; - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); -} - -sem_t *init_semaphore(const char *name, int initial_value) { - sem_t *sem; - // Force reset if semaphore exists. - sem_unlink(name); // ...ignore errors... - - // Create a new semaphore with the initial value. - sem = sem_open(name, O_CREAT | O_EXCL, 0644, initial_value); - if (sem == SEM_FAILED) { - perror("sem_open (create)"); - exit(EXIT_FAILURE); - } - vprintf_verbose("Initialized semaphore %s with value %d\n", name, initial_value); - return sem; -} - -void cleanup_semaphore(const char *name) { - if (sem_unlink(name) == -1) { - perror("sem_unlink"); - exit(EXIT_FAILURE); - } - vprintf_verbose("Cleaned up semaphore %s\n", name); -} - -int get_sem_value(sem_t *sem) { - int value; - if (sem_getvalue(sem, &value) == -1) { - perror("sem_getvalue"); - exit(EXIT_FAILURE); - } - return value; -} - -// Updated atomic_wait to accept a timeout value (in seconds) -bool wait(sem_t *sem, int n, int timeout, bool atomic) { - time_t start = time(NULL); - int acquired = 0, i; - while (1) { - if (termination_requested) { - vprintf_verbose("Termination signal received during atomic_wait, rolling back.\n"); - break; - } - if (timeout > 0 && (time(NULL) - start) >= timeout) { - vprintf_verbose("Timeout exceeded in atomic_wait after %d seconds, rolling back.\n", timeout); - break; - } - for (i = 0; i < n; i++) { - if (sem_trywait(sem) == -1) - break; - acquired++; - } - if (acquired == n) { - return true; - } else { - if (atomic) { - // Rollback any acquired resources - for (i = 0; i < acquired; i++) { - sem_post(sem); - } - acquired = 0; - } - if (timeout == 0) { - break; - } - usleep(1000); // Sleep briefly before retrying - } - } - for (i = 0; i < acquired; i++) { - sem_post(sem); - } - return false; -} - -int main(int argc, char *argv[]) { - const char *sem_name = NULL; - int init_value = -1; // -1 means no initialization - int amount = 0; - int cleanup = 0; - int has_amount = 0; - int use_atomic_decrement = 0; - int timeout = -1; - - // Install signal handlers. - signal(SIGINT, handle_signal); - signal(SIGTERM, handle_signal); - - // Parse command-line arguments. - for (int i = 1; i < argc; i++) { - if (strcmp(argv[i], "-v") == 0) { - verbose = 1; - continue; - } - if (strcmp(argv[i], "--atomic") == 0) { - use_atomic_decrement = 1; - continue; - } - if (strcmp(argv[i], "--name") == 0) { - if (++i >= argc) usage(argv[0]); - sem_name = argv[i]; - if (sem_name[0] != '/') { - fprintf(stderr, "Semaphore name must start with /\n"); - exit(EXIT_FAILURE); - } - if (strlen(sem_name) > MAX_NAME_LEN) { - fprintf(stderr, "Semaphore name too long\n"); - exit(EXIT_FAILURE); - } - } else if (strcmp(argv[i], "--init") == 0) { - if (++i >= argc) usage(argv[0]); - init_value = atoi(argv[i]); - if (init_value < 0) { - fprintf(stderr, "Initial value must be non-negative\n"); - exit(EXIT_FAILURE); - } - } else if (strcmp(argv[i], "--cleanup") == 0) { - cleanup = 1; - } else if (strcmp(argv[i], "--timeout") == 0) { - if (++i >= argc) usage(argv[0]); - timeout = atoi(argv[i]); - } else if (argv[i][0] == '+' || argv[i][0] == '-') { - amount = atoi(argv[i]); - has_amount = 1; - } else { - usage(argv[0]); - } - } - - // Validate required arguments - if (!sem_name) { - fprintf(stderr, "Semaphore name required (--name)\n"); - usage(argv[0]); - } - - // Handle cleanup - if (cleanup) { - cleanup_semaphore(sem_name); - return 0; - } - - // Initialize or connect to the semaphore - sem_t *sem = (init_value >= 0) ? init_semaphore(sem_name, init_value) : sem_open(sem_name, O_CREAT); - if (sem == SEM_FAILED) { - perror("sem_open"); - exit(EXIT_FAILURE); - } - - // If no amount specified, print value and exit. - if (!has_amount) { - if (init_value < 0) { - printf("%d\n", get_sem_value(sem)); - } - sem_close(sem); - return 0; - } - - // Show current value - vprintf_verbose("Current semaphore value: %d\n", get_sem_value(sem)); - - // Perform increment or decrement - if (amount >= 0) { - for (int i = 0; i < amount; i++) { - if (sem_post(sem) == -1) { - perror("sem_post"); - exit(EXIT_FAILURE); - } - } - vprintf_verbose("Incremented by %d, new value: %d\n", amount, get_sem_value(sem)); - } else { - amount = -amount; // Make positive value for decrement - vprintf_verbose("Decrementing by %d, atomic: %d...\n", amount, use_atomic_decrement); - bool success = wait(sem, amount, timeout, use_atomic_decrement); - if (success) { - vprintf_verbose("Decremented by %d, new value: %d\n", amount, get_sem_value(sem)); - } else { - vprintf_verbose("Failed to decrement, value: %d\n", amount, get_sem_value(sem)); - exit(EXIT_FAILURE); - } - } - - // Close the semaphore handle (does not remove it) - if (sem_close(sem) == -1) { - perror("sem_close"); - exit(EXIT_FAILURE); - } - - return 0; -} diff --git a/ci3/semver b/ci3/semver deleted file mode 100755 index a9f63801df2..00000000000 --- a/ci3/semver +++ /dev/null @@ -1,365 +0,0 @@ -#!/usr/bin/env perl -use strict; -use warnings; -use feature 'say'; - -# Written in perl as it is very portable and doesn't have the python versioning fiasco. -# We allow one variant: a leading 'v' can be used (Aztec convention). -# Usage: -# semver check -# semver # valid: major, minor, patch, prerelease, buildmetadata -# semver sort [ ...] -# semver test - -my $mode = shift @ARGV or die usage(); - -if ($mode eq 'test') { - run_tests(); - exit 0; -} -elsif ($mode eq 'sort') { - die usage() unless @ARGV >= 1; - sort_versions(@ARGV); - exit 0; -} -else { - # Either "check" or extracting a component - die usage() unless @ARGV == 1; - my $version = $ARGV[0]; - - my $regex = qr/ - ^ - v? - (?0|[1-9]\d*) - \. - (?0|[1-9]\d*) - \. - (?0|[1-9]\d*) - (?:- - (? - (?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*) - (?:\. - (?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*) - )* - ) - )? - (?:\+ - (? - [0-9a-zA-Z-]+ - (?:\.[0-9a-zA-Z-]+)* - ) - )? - $ - /x; - - if ($version =~ $regex) { - if ($mode eq 'check') { - # valid semver - exit 0; - } - - my %valid = map { $_ => 1 } qw(major minor patch prerelease buildmetadata); - die "Invalid component: $mode\n" unless $valid{$mode}; - - if ($mode eq 'prerelease' && defined $+{prerelease}) { - say $+{prerelease}; - } - else { - my $value = defined $+{$mode} ? $+{$mode} : ""; - say $value; - } - exit 0; - } - else { - # invalid semver - exit 1; - } -} - -# --------------------------------------------------------------------------- -# SUBROUTINES -# --------------------------------------------------------------------------- - -sub usage { - return <<"END_USAGE"; -Usage: - $0 check - $0 # valid: major, minor, patch, prerelease, buildmetadata - $0 sort [ ...] - $0 test -END_USAGE -} - -sub parse_semver { - my ($version) = @_; - my $regex = qr/ - ^ - v? - (?0|[1-9]\d*) - \. - (?0|[1-9]\d*) - \. - (?0|[1-9]\d*) - (?:- - (? - (?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*) - (?:\. - (?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*) - )* - ) - )? - (?:\+ - (? - [0-9a-zA-Z-]+ - (?:\.[0-9a-zA-Z-]+)* - ) - )? - $ - /x; - - if ($version =~ $regex) { - return { - major => $+{major}, - minor => $+{minor}, - patch => $+{patch}, - prerelease => (defined $+{prerelease} ? [ split /\./, $+{prerelease} ] : undef), - buildmetadata => $+{buildmetadata}, - }; - } - else { - die "Invalid semver: $version\n"; - } -} - -# Sort any number of versions (ascending order) by SemVer precedence. -sub sort_versions { - my @versions = @_; - - # Parse each version once - my @parsed = map { { original => $_, parsed => parse_semver($_) } } @versions; - - # Sort by comparing their parsed data - my @sorted = sort { - compare_semver($a->{parsed}, $b->{parsed}); - } @parsed; - - # Print them in the sorted order - for my $v (@sorted) { - say $v->{original}; - } -} - -# Compare two parsed semver structures, returning -1/0/+1 -sub compare_semver { - my ($a, $b) = @_; - for my $key (qw(major minor patch)) { - my $cmp = $a->{$key} <=> $b->{$key}; - return $cmp if $cmp != 0; - } - - # If one has no prerelease, that one is considered higher precedence - if (!defined $a->{prerelease} && defined $b->{prerelease}) { - return 1; - } - if (defined $a->{prerelease} && !defined $b->{prerelease}) { - return -1; - } - if (!defined $a->{prerelease} && !defined $b->{prerelease}) { - return 0; - } - - my @a_ids = @{ $a->{prerelease} }; - my @b_ids = @{ $b->{prerelease} }; - my $len = @a_ids < @b_ids ? @a_ids : @b_ids; - - for (my $i = 0; $i < $len; $i++) { - my $c = compare_identifiers($a_ids[$i], $b_ids[$i]); - return $c if $c != 0; - } - return @a_ids <=> @b_ids; -} - -sub compare_identifiers { - my ($x, $y) = @_; - my $x_is_num = ($x =~ /^\d+$/); - my $y_is_num = ($y =~ /^\d+$/); - - if ($x_is_num && $y_is_num) { - return $x <=> $y; - } - elsif ($x_is_num && !$y_is_num) { - return -1; - } - elsif (!$x_is_num && $y_is_num) { - return 1; - } - else { - return $x cmp $y; - } -} - -# --------------------------------------------------------------------------- -# TEST SUBROUTINE -# --------------------------------------------------------------------------- -sub run_tests { - my @tests = ( - { - name => 'check valid semver 1.2.3', - cmd => [ 'check', '1.2.3' ], - expect_exit => 0, - expect_stdout => '', - }, - { - name => 'check valid semver v1.2.3', - cmd => [ 'check', 'v1.2.3' ], - expect_exit => 0, - expect_stdout => '', - }, - { - name => 'check invalid semver 1.2', - cmd => [ 'check', '1.2' ], - expect_exit => 1, - expect_stdout => '', - }, - { - name => 'check invalid semver 1.2.3.4', - cmd => [ 'check', '1.2.3.4' ], - expect_exit => 1, - expect_stdout => '', - }, - { - name => 'extract major from 1.2.3', - cmd => [ 'major', '1.2.3' ], - expect_exit => 0, - expect_stdout => "1\n", - }, - { - name => 'extract minor from v1.2.3', - cmd => [ 'minor', 'v1.2.3' ], - expect_exit => 0, - expect_stdout => "2\n", - }, - { - name => 'extract patch from 1.2.3', - cmd => [ 'patch', '1.2.3' ], - expect_exit => 0, - expect_stdout => "3\n", - }, - { - name => 'extract prerelease from 1.2.3-alpha.1', - cmd => [ 'prerelease', '1.2.3-alpha.1' ], - expect_exit => 0, - expect_stdout => "alpha.1\n", - }, - { - name => 'extract buildmetadata from 1.2.3+exp.sha', - cmd => [ 'buildmetadata', '1.2.3+exp.sha' ], - expect_exit => 0, - expect_stdout => "exp.sha\n", - }, - { - name => 'extract prerelease empty from 1.2.3 (none)', - cmd => [ 'prerelease', '1.2.3' ], - expect_exit => 0, - expect_stdout => "\n", - }, - { - name => 'extract buildmetadata empty from 1.2.3 (none)', - cmd => [ 'buildmetadata', '1.2.3' ], - expect_exit => 0, - expect_stdout => "\n", - }, - { - name => 'sort just 1.2.3', - cmd => [ 'sort', '1.2.3' ], - expect_exit => 0, - expect_stdout => "1.2.3\n", - }, - { - name => 'sort 1.2.3 and 1.2.4', - cmd => [ 'sort', '1.2.3', '1.2.4' ], - expect_exit => 0, - expect_stdout => "1.2.3\n1.2.4\n", - }, - { - name => 'sort v2.0.0 and 2.0.0 (they are equal, so keep input order)', - cmd => [ 'sort', 'v2.0.0', '2.0.0' ], - expect_exit => 0, - expect_stdout => "v2.0.0\n2.0.0\n", - }, - { - name => 'sort 1.0.0-alpha and 1.0.0 (alpha is lower)', - cmd => [ 'sort', '1.0.0-alpha', '1.0.0' ], - expect_exit => 0, - expect_stdout => "1.0.0-alpha\n1.0.0\n", - }, - { - name => 'sort multiple versions', - cmd => [ - 'sort', - 'v1.2.3', - '1.10.0-alpha', - '1.2.3-beta.1', - '1.2.3-alpha.2', - '1.2.3-alpha.1', - 'v1.2.3+build123', - '2.0.0', - 'v0.1.2', - ], - expect_exit => 0, - expect_stdout => join("\n", - 'v0.1.2', - '1.2.3-alpha.1', - '1.2.3-alpha.2', - '1.2.3-beta.1', - 'v1.2.3', - 'v1.2.3+build123', - '1.10.0-alpha', - '2.0.0', - ) . "\n", - }, - ); - - my $all_passed = 1; - my $test_count = 0; - - foreach my $test (@tests) { - $test_count++; - my $name = $test->{name}; - - # Build the command string from the array - my $cmd_str = join(' ', @{ $test->{cmd} }); - - # $^X is the current Perl executable - my $full_cmd = "$^X $0 $cmd_str"; - my $output = `$full_cmd`; - my $exit_code = $? >> 8; - - my $ok_exit = ($exit_code == $test->{expect_exit}); - my $ok_stdout = ($output eq $test->{expect_stdout}); - - if (!$ok_exit || !$ok_stdout) { - $all_passed = 0; - say "FAILED: $name"; - say " Command : $full_cmd"; - say " Expected exit : $test->{expect_exit}, got $exit_code"; - say " Expected stdout:\n---\n$test->{expect_stdout}---"; - say " Got stdout:\n---\n$output---"; - say ""; - } - else { - say "PASSED: $name"; - } - } - - say "-------------------------------------------"; - say "Test summary: $test_count tests run."; - if ($all_passed) { - say "All tests PASSED!"; - exit 0; - } - else { - say "Some tests FAILED!"; - exit 1; - } -} diff --git a/ci3/slack_notify b/ci3/slack_notify deleted file mode 100755 index f6e6f7efaa5..00000000000 --- a/ci3/slack_notify +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -NO_CD=1 source $(git rev-parse --show-toplevel)/ci3/source - -if [ -z "${SLACK_BOT_TOKEN:-}" ]; then - exit 0 -fi - -channel=${2:-"#aztec3-ci"} -data=$(jq -n --arg channel "$channel" --arg text "$1" '{channel: $channel, text: $text}') - -# Post to Slack, capturing the API response. Previously the response went to -# /dev/null, so a failed post (most commonly the bot not being a member of the -# target channel -> "not_in_channel", or an invalid channel/token) vanished with -# no trace and could not be troubleshooted. Surface any failure in the CI log. -# Best-effort: log the failure but do not fail the caller. -response=$(curl -sS -X POST https://slack.com/api/chat.postMessage \ - -H "Authorization: Bearer $SLACK_BOT_TOKEN" \ - -H "Content-type: application/json" \ - --data "$data" 2>&1) - -if [ "$(jq -r '.ok // false' <<<"$response" 2>/dev/null)" != "true" ]; then - error=$(jq -r '.error // empty' <<<"$response" 2>/dev/null) - echo "slack_notify: failed to post to Slack channel '$channel': ${error:-$response}" >&2 -fi diff --git a/ci3/source b/ci3/source deleted file mode 100644 index baddc6bc594..00000000000 --- a/ci3/source +++ /dev/null @@ -1,13 +0,0 @@ -# Usage: source $(git rev-parse --show-toplevel)/ci3/source -# This is a basis for shell scripts that use the ci3 framework. - -export root=${root:-$(git rev-parse --show-toplevel)} -export ci3="$root/ci3" - -source $ci3/source_base -source $ci3/source_options -source $ci3/source_color -source $ci3/source_stdlib -source $ci3/source_redis -source $ci3/source_refname -source $ci3/source_cache diff --git a/ci3/source_base b/ci3/source_base deleted file mode 100644 index 0c3b0a00bc1..00000000000 --- a/ci3/source_base +++ /dev/null @@ -1,53 +0,0 @@ -# Most basic setup. -# Should work on old versions of bash. - -# Enter our script directory, allowing usage of scripts from any directory. -[ -z "${NO_CD:-}" ] && cd "$(dirname $0)" - -# We export so we can use from exported functions. -export root=${root:-$(git rev-parse --show-toplevel)} -export ci3="$root/ci3" - -# Update PATH with ci3 tools. -if [[ "$PATH" != *"$ci3:" ]]; then - export PATH=$ci3:$PATH - - # We're on the github runner, we're about to launch ec2 instances with our container, but we need: - # - redis-cli for logging. - # - parallel for running commands in parallel. - if [ "${GITHUB_ACTIONS:-}" = "true" ]; then - export PATH=$ci3/bin:$PATH - fi -fi - -# We use this path to normalise tool names. -# e.g. on macos we link brews llvm clang++ binary to clang++20 to align with linux. -export AZTEC_DEV_BIN="$HOME/.local/aztec-dev-bin" -export PATH=$AZTEC_DEV_BIN:$PATH - -# On macOS, prefer GNU tools where available for better compatibility with our scripts. -if [[ "$(os)" == "macos" ]]; then - brew_prefix=$(brew --prefix) - export PATH=$brew_prefix/opt/gnu-sed/libexec/gnubin:$brew_prefix/opt/coreutils/libexec/gnubin:$brew_prefix/opt/util-linux/bin:$brew_prefix/opt/grep/libexec/gnubin:$PATH -fi - -[ -z "${CI:-}" ] && export CI=0 -[ "$CI" == "true" ] && export CI=1 - -# We are fine using foundry nightly. -export FOUNDRY_DISABLE_NIGHTLY_WARNING=1 - -# Corepack should download without prompting. -export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 - -# Silences some warnings from perl. -export LC_ALL= -export LANG=C - -# GNU parallel sets XDG_CACHE_HOME to empty string in child processes. -# This causes corepack to resolve its cache as a relative path (node/corepack/), -# placing yarn.js under the CWD. If a package.json there has "type": "module", -# Node loads yarn.js as ESM, breaking its require() calls. -# Setting COREPACK_HOME explicitly is robust: corepack checks it first, -# and parallel doesn't touch it (only XDG_* vars). -export COREPACK_HOME="${COREPACK_HOME:-${HOME}/.cache/node/corepack}" diff --git a/ci3/source_bootstrap b/ci3/source_bootstrap deleted file mode 100644 index 81da4165864..00000000000 --- a/ci3/source_bootstrap +++ /dev/null @@ -1,47 +0,0 @@ -# Source this first in all bootstrap scripts. -# Usage: source $(git rev-parse --show-toplevel)/ci3/source_bootstrap - -if (( BASH_VERSINFO[0] < 5 )); then - echo "Error: Bash 5 or newer is required." >&2 - if [ "$(uname -s)" == "Darwin" ]; then - echo "On macOS, you can install it with Homebrew: brew install bash" >&2 - fi - exit 1 -fi - -source $(git rev-parse --show-toplevel)/ci3/source - -# Silence parallel citation message. -mkdir -p $HOME/.parallel -touch $HOME/.parallel/will-cite - -cmd=${1:-} -[ -n "$cmd" ] && shift - -case "$cmd" in - release) - if ! semver check $REF_NAME; then - echo_stderr -e "${yellow}Not deploying $REF_NAME because it is not a release tag.${reset}" - exit 0 - fi - # Until releases are 100% stable, print a debug log. - # set -x - ;; -esac - -# Default clean function available to all bootstrap scripts. -function clean { - git clean -fdx - exit 0 -} - -# Default command handler available to all bootstrap scripts. -# If the bootstrap scripts command handler doesn't specifically specify, we will execute a matching function. -function default_cmd_handler { - if declare -f $cmd >/dev/null 2>&1; then - $cmd "$@" - else - echo "Unknown command: $cmd" - exit 1 - fi -} diff --git a/ci3/source_cache b/ci3/source_cache deleted file mode 100644 index ba117460438..00000000000 --- a/ci3/source_cache +++ /dev/null @@ -1,89 +0,0 @@ -# Cache system for CI logs. -# Logs are written to Redis (synchronous) and S3 (background). -# Legacy disk transfer functions (SSH to bastion) are kept but no longer used in normal flow. -# NOTE: This file depends on source_redis being sourced first. - -# Transfer log to S3 (accepts gzipped data on stdin). -# Usage: cat file.gz | cache_s3_transfer -# S3 path: /logs//.log.gz -function cache_s3_transfer { - local key="$1" - local prefix="${key:0:4}" - local location="${CI_LOGS_S3_LOCATION:-s3://aztec-ci-artifacts/logs}" - local no_scheme="${location#s3://}" - local bucket="${no_scheme%%/*}" - local base_prefix="${no_scheme#*/}" - - aws s3 ${S3_BUILD_CACHE_AWS_PARAMS:-} cp - "s3://${bucket}/${base_prefix}/${prefix}/${key}.log.gz" &>/dev/null -} - -# Transfer to S3 with explicit subfolder (accepts gzipped data on stdin). -# Usage: cat file.gz | cache_s3_transfer_to -function cache_s3_transfer_to { - local subfolder="$1" - local key="$2" - local location="${CI_LOGS_S3_LOCATION:-s3://aztec-ci-artifacts/logs}" - local no_scheme="${location#s3://}" - local bucket="${no_scheme%%/*}" - local base_prefix="${no_scheme#*/}" - - aws s3 ${S3_BUILD_CACHE_AWS_PARAMS:-} cp - "s3://${bucket}/${base_prefix}/${subfolder}/${key}.log.gz" &>/dev/null -} - -# Transfer log to bastion via SSH (accepts gzipped data on stdin) -# Usage: cat file.gz | cache_disk_transfer -function cache_disk_transfer { - local key="$1" - local prefix="${key:0:4}" - - # Transfer via SSH pipe using persistent multiplexed connection - # Use first 4 chars as subdirectory to avoid too many files in one dir - ssh -F "${ci3}/aws/build_instance_ssh_config" \ - -o ConnectTimeout=5 \ - ci-bastion.aztecprotocol.com \ - "mkdir -p /logs-disk/${prefix} && cat > /logs-disk/${prefix}/${key}.log.gz" &>/dev/null -} - -# Transfer to bastion with explicit subfolder (accepts gzipped data on stdin) -# Usage: cat file.gz | cache_disk_transfer_to -# Legacy: transfer to bastion disk via SSH. Kept for fallback but no longer called in normal flow. -function cache_disk_transfer_to { - local subfolder="$1" - local key="$2" - - ssh -F "${ci3}/aws/build_instance_ssh_config" \ - -o ConnectTimeout=5 \ - ci-bastion.aztecprotocol.com \ - "mkdir -p /logs-disk/${subfolder} && cat > /logs-disk/${subfolder}/${key}.log.gz" &>/dev/null -} - -# Persistent cache function that writes to both Redis and S3 -function cache_persistent { - local key="$1" - local expire="$2" - local tmpfile="/tmp/cache_log_$$_${key}.gz" - - cat | gzip > "$tmpfile" - - # Write to Redis (synchronous). Skipped when redis is unavailable: redis_cli would exit - # without draining, killing the cat with SIGPIPE and failing the caller under pipefail. - if [ "$CI_REDIS_AVAILABLE" -eq 1 ]; then - cat "$tmpfile" | redis_cli -x SETEX "$key" "$expire" &>/dev/null - fi - - # Write to S3 in background, or in the foreground for a log that must outlive the caller: - # the build instance's top-level log is persisted as its last act, and a background upload - # is killed with the container before it lands. - if [ "${CACHE_PERSIST_SYNC:-0}" -eq 1 ]; then - cat "$tmpfile" | cache_s3_transfer "$key" - rm -f "$tmpfile" - else - { - cat "$tmpfile" | cache_s3_transfer "$key" - rm -f "$tmpfile" - } >&- 2>&- & - fi -} - -# Export functions -export -f cache_s3_transfer cache_s3_transfer_to cache_disk_transfer cache_disk_transfer_to cache_persistent diff --git a/ci3/source_color b/ci3/source_color deleted file mode 100644 index b23b8c52c49..00000000000 --- a/ci3/source_color +++ /dev/null @@ -1,23 +0,0 @@ -# Color codes for output. - -# Quite nice colors guessed by GPT. -export yellow="\033[38;2;250;217;121m" -export orange="\033[38;2;255;165;0m" -export blue="\033[38;2;95;167;241m" -export green="\033[38;2;97;214;104m" -export red="\033[38;2;230;69;83m" -export purple="\033[38;2;188;109;208m" - -export bold="\033[1m" -export reset="\033[0m" - -# Cursor control -export hide_cursor="\033[?25l" -export show_cursor="\033[?25h" - -# OSC 8 support for links. -export link_open='\033]8;;' -export link_close='\x07' - -# We always want color. -export FORCE_COLOR=true diff --git a/ci3/source_options b/ci3/source_options deleted file mode 100644 index 21ad8ccc805..00000000000 --- a/ci3/source_options +++ /dev/null @@ -1,9 +0,0 @@ -# Set best practices for early error exit and undefined variables. -set -euo pipefail - -# Enable extended bash globs. -shopt -s extglob -shopt -s globstar - -# Error on x=$(failing_command). -shopt -s inherit_errexit diff --git a/ci3/source_redis b/ci3/source_redis deleted file mode 100644 index ec10f62b590..00000000000 --- a/ci3/source_redis +++ /dev/null @@ -1,120 +0,0 @@ -# We use a redis cache for e.g. test run tracking, denoise logs, etc. - -# Declare CI_REDIS host, allowing override. -export CI_REDIS=${CI_REDIS:-localhost} - -# If we haven't yet checked for redis availability. -if [ -z "${CI_REDIS_AVAILABLE:-}" ]; then - # Start by setting it as not available. - export CI_REDIS_AVAILABLE=0 - - if nc -z $CI_REDIS 6379 &>/dev/null; then - # Redis is found at given host, we're done. - export CI_REDIS_AVAILABLE=1 - elif [ "${GITHUB_ACTIONS:-}" != "true" ] && [ -f ~/.ssh/build_instance_key ] && [ "${CI_SSM_MODE:-0}" -eq 0 ]; then - # The bastion tunnel is for developer machines only. A GitHub runner deliberately runs - # with no log or test cache: it only orchestrates the build instance, and the instance - # reaches CI_REDIS directly from inside the VPC, so everything from "instance up" - # onwards is still logged under the same CI_LOG_ID. Keeping the runner off the bastion - # means no CI path depends on an SSH credential to produce logs. - # Open a port to the remote redis cache via ssh through the bastion. - echo_stderr "Opening port to remote redis..." - ssh -N -L 6379:ci-redis-tiered.lzka0i.ng.0001.use2.cache.amazonaws.com:6379 \ - -o ControlMaster=auto -o ControlPath=/tmp/ssh_mux_%h_%p_%r -o ControlPersist=480m \ - -F $ci3/aws/build_instance_ssh_config ci-bastion.aztecprotocol.com true && true - if nc -z $CI_REDIS 6379 &>/dev/null; then - export CI_REDIS_AVAILABLE=1 - else - echo_stderr -e "${yellow}Failed to establish connection to remote redis. Log and test cache will be disabled.${reset}" - fi - elif docker ps &> /dev/null && [ "$CI" -eq 0 ]; then - echo_stderr "Starting local redis container..." - docker network create ci-network &>/dev/null || true - docker run -d --rm --name ci-redis -p 6379:6379 --network ci-network redis:latest redis-server --save "" --appendonly no &>/dev/null || true - if nc -z $CI_REDIS 6379 &>/dev/null; then - export CI_REDIS_AVAILABLE=1 - else - echo_stderr -e "${yellow}Failed to start a local redis. Log and test cache will be disabled.${reset}" - fi - elif [ "$CI" -eq 0 ]; then - echo_stderr -e "${yellow}Cannot connect to remote redis, and no docker available for local redis. Log and test cache will be disabled.${reset}" - fi -fi - -# If we haven't yet checked for dashboard availability. -if [ -z "${DASHBOARD_URL:-}" ]; then - # By default, log links will go to the labs ci dashboard. - # We override to localhost if we're running local redis. - export DASHBOARD_URL="http://ci.aztec-labs.com" - - if command -v docker &>/dev/null && docker ps | grep -q "ci-redis"; then - if [ -z "$(docker ps -q --filter "name=^/ci-dashboard$")" ]; then - echo_stderr "Building local ci dashboard..." - (cd $ci3/dashboard && docker build -t aztecprotocol/ci-dashboard:latest .) &>/dev/null - echo_stderr "Starting local ci dashboard..." - docker network create ci-network &>/dev/null || true - docker run -d --rm --name ci-dashboard -p 4273:8080 \ - -e REDIS_HOST=ci-redis \ - -e REDIS_PORT=6379 \ - -e DASHBOARD_PORT=4273 \ - --network ci-network \ - aztecprotocol/ci-dashboard:latest &>/dev/null - fi - export DASHBOARD_URL="http://localhost:4273" - fi -fi - -# If redis isn't available, neither is the test cache. -if [ "$CI_REDIS_AVAILABLE" -eq 0 ]; then - export USE_TEST_CACHE=0 -fi - -# Will set log expiry to 2 weeks in CI, and 2 days for local runs. -if [ "${CI:-0}" -eq 1 ]; then - CI_REDIS_EXPIRE=$((60 * 60 * 24 * 14)) -else - CI_REDIS_EXPIRE=$((60 * 60 * 24 * 2)) -fi - -# Help function to avoid having to constantly specify args. -# Noop when redis is unavailable. -function redis_cli { - if [ "$CI_REDIS_AVAILABLE" -eq 1 ]; then - timeout 30 redis-cli --raw -h $CI_REDIS "$@" || true - fi -} - -# Callers pipe log data into this. When redis is unavailable the data must still be read: -# a reader that exits without draining kills the upstream writer with SIGPIPE, and under -# `set -o pipefail` that 141 propagates out and fails the caller. -function redis_setexz { - if [ "$CI_REDIS_AVAILABLE" -eq 1 ]; then - gzip | redis_cli -x SETEX $1 $2 &>/dev/null - else - cat >/dev/null - fi -} - -function redis_getz { - hex=$(redis_cli --raw GET "$1" | head -c -1 | xxd -p) - if [ -z "$hex" ]; then - echo "Key not found." - fi - if [[ "${hex:0:4}" == "1f8b" ]]; then - echo "$hex" | xxd -r -p | gunzip - else - echo "$hex" | xxd -r -p - fi -} - -# Publish a message to a Redis channel for pub/sub. -# Usage: redis_publish -function redis_publish { - local channel=$1 - local message=$2 - if [ "$CI_REDIS_AVAILABLE" -eq 1 ]; then - redis_cli PUBLISH "$channel" "$message" &>/dev/null - fi -} - -export -f redis_cli redis_setexz redis_getz redis_publish diff --git a/ci3/source_refname b/ci3/source_refname deleted file mode 100644 index 797f94fef85..00000000000 --- a/ci3/source_refname +++ /dev/null @@ -1,55 +0,0 @@ -# Set REF_NAME to be the branch name if on a branch, or the tag name if on a tag. -# If REF_NAME starts with "release/", just keep what comes after. -# Example outputs: -# - master (branch is master) -# - cl/ci_branch (a feature branch) -# - v1.2.3 (a tagged version) -# - wise-weasle (a tagged release e.g. release/wise-weasle) -# We use this for e.g: -# - Creating a unique instance name when launching spots. -# - Identifying GitHub releases to upload assets to. -# - Tagging released images. -# - Identifying a PR associated with a branch. -# This is obviously overloaded in terms of how its value is generated, and how it's used. -# So far this has been fine, so requiring more distinct variables has been unnecessary. -# In Github Actions we need to take from what GA provides to avoid any race conditions. -if [ -z "${REF_NAME:-}" ]; then - if [ -n "${GITHUB_HEAD_REF:-}" ]; then - # Use correct branch name in PRs. - REF_NAME="${GITHUB_HEAD_REF}" - elif [ -n "${GITHUB_REF:-}" ]; then - REF_NAME="${GITHUB_REF#refs/heads/}" # Extract branch name - REF_NAME="${REF_NAME#refs/tags/}" # Extract tag name - else - REF_NAME=$(git symbolic-ref --short -q HEAD || git describe --tags --exact-match 2>/dev/null || git rev-parse HEAD) - REF_NAME="${REF_NAME#heads/}" - fi - - [ -z "$REF_NAME" ] && exit 1 - - export REF_NAME -fi - -if [ -z "${CI_FULL:-}" ]; then - export CI_FULL=0 - if [ "$CI" -eq 1 ] && { semver check "$REF_NAME" || [ "$REF_NAME" == "master" ]; }; then - export CI_FULL=1 - fi -fi -export CI_SCENARIO_TEST="${CI_SCENARIO_TEST:-0}" - -if [ -z "${COMMIT_HASH:-}" ]; then - export COMMIT_HASH=$(git rev-parse HEAD) -fi - -if [ -z "${COMMIT_MSG:-}" ]; then - export COMMIT_MSG=$(git log -1 --pretty=format:"%s") -fi - -if [ -z "${COMMIT_AUTHOR:-}" ]; then - export COMMIT_AUTHOR="$(git log -1 --pretty=format:"%an")" -fi - -if [ -z "${CURRENT_VERSION:-}" ]; then - export CURRENT_VERSION=$(jq -r '."."' $root/.release-please-manifest.json) -fi diff --git a/ci3/source_stdlib b/ci3/source_stdlib deleted file mode 100644 index 5416a89a067..00000000000 --- a/ci3/source_stdlib +++ /dev/null @@ -1,76 +0,0 @@ -# A tiny stdlib of useful functions that don't warrant having their own file. - -# TODO: This should be hash_str. -# We need to find places we're using hash_str to generate content hashes, and replace with pure cache_content_hash calls. -function hash_str_orig { - set -euo pipefail - echo $1 | git hash-object --stdin | cut -c1-16 | tr -d '\n' -} - -function hash_str { - set -eu - if [[ "$*" == *"disabled-cache"* ]]; then - # We want to propagate cache being disabled so that cache_upload and cache_download can catch this - echo "disabled-cache" - else - echo "$*" | git hash-object --stdin | cut -c1-16 | tr -d '\n' - fi -} - -function echo_stderr { - echo $@ >&2 -} - -function uuid { - dd if=/dev/urandom count=8 bs=1 status=none | xxd -p -} - -function get_num_cpus { - if [ -f /sys/fs/cgroup/cpu.max ]; then - read cpu_quota cpu_period < /sys/fs/cgroup/cpu.max - if [[ "$cpu_quota" == "max" ]]; then - nproc - else - echo $((cpu_quota / cpu_period)) - fi - elif [[ "$(uname)" == "Darwin" ]]; then - sysctl -n hw.ncpu - else - nproc - fi -} - -function get_num_cpus_max { - local jobs=$(get_num_cpus) - [ -n "${1:-}" ] && [ "$1" -lt $jobs ] && jobs=$1 - echo $jobs -} - -function aws_get_meta_data { - curl -fs -H "X-aws-ec2-metadata-token: $AWS_TOKEN" http://169.254.169.254/latest/meta-data/$1 || true -} - -function urlencode { - printf '%s' "$1" | python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.stdin.read(), safe=''))" -} - -function term_link { - echo -e "${yellow}${link_open}$1${link_close}$2${link_open}${link_close}${reset}" -} - -function ci_term_link { - term_link "$DASHBOARD_URL/$1" "$1" -} - -function pr_link { - local line=$1 - if [[ $line =~ \(#([0-9]+)\) ]]; then - pr_number="${BASH_REMATCH[1]}" - link=$(term_link https://github.com/aztecprotocol/aztec-packages/pull/$pr_number $pr_number) - echo -e "${line/\(#${pr_number}\)/(#${link})}" - else - echo -e "$line" - fi -} - -export -f hash_str hash_str_orig echo_stderr uuid get_num_cpus get_num_cpus_max urlencode term_link ci_term_link pr_link diff --git a/ci3/source_test b/ci3/source_test deleted file mode 100644 index 8f75b931c21..00000000000 --- a/ci3/source_test +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash -# Script base for testing the cache with minio. -# The cache is essentially ephemeral this way. -# Any calls to cache_* functions work with hitting local minio instead of S3. -source "$(git rev-parse --show-toplevel)/ci3/source" - -TEST_PORT=9338 - -# Configure AWS parameters to use MinIO -# S3_BUILD_CACHE_AWS_PARAMS: Used by cache/download and cache/upload -export S3_BUILD_CACHE_AWS_PARAMS="--endpoint-url http://localhost:$TEST_PORT" -export AWS_SECRET_ACCESS_KEY="minioadmin" -export AWS_ACCESS_KEY_ID="minioadmin" - -function minio_cleanup() { - minio_delete_cache -} -trap minio_cleanup EXIT - -function minio_delete_cache() { - aws --endpoint http://localhost:$TEST_PORT \ - s3 rm s3://aztec-ci-artifacts --recursive --include "*" 2>&1 || true -} - -function minio_start() { - echo "Starting MinIO..." - docker start minio-test-db &>/dev/null || docker run -d --name minio-test-db \ - -p $TEST_PORT:9000 \ - -v minio-data:/data \ - quay.io/minio/minio server /data - - # Wait for MinIO to be ready - until nc -z 127.0.0.1 $TEST_PORT >/dev/null 2>&1; do - sleep 1 - done - - # Create the cache bucket - echo "Creating MinIO bucket for cache..." - aws --endpoint-url http://localhost:$TEST_PORT s3 mb s3://aztec-ci-artifacts 2>/dev/null || true -} - -# If MinIO is already running, ensure the cache is deleted -if nc -z 127.0.0.1 $TEST_PORT >/dev/null 2>&1; then - minio_delete_cache -fi - -minio_start diff --git a/ci3/source_test_params b/ci3/source_test_params deleted file mode 100644 index f24808110a7..00000000000 --- a/ci3/source_test_params +++ /dev/null @@ -1,30 +0,0 @@ -# Source this to parse a full_cmd and extract test parameters. -# Usage: full_cmd="[:VAR=val]... " source $ci3/source_test_params -# -# Requires: -# full_cmd - The full test command string -# -# Exports: -# hash_prefix - First token (hash and embedded vars) -# test_cmd - Everything after first space -# CPUS, TIMEOUT, MEM, CPU_LIST, ISOLATE, MAKEFILE_TARGET, etc. - -# Parse full_cmd: extract hash prefix and test command -hash_prefix="${full_cmd%% *}" -test_cmd="${full_cmd#* }" - -# Extract variables from hash prefix (format: hash:CPUS=4:TIMEOUT=900s:MAKEFILE_TARGET=yarn-project) -# This must happen before defaults are set so that e.g. MEM can use extracted CPUS. -if [[ "$hash_prefix" == *:* ]]; then - IFS=':' read -ra parts <<< "$hash_prefix" - for var_assignment in "${parts[@]:1}"; do - export "$var_assignment" - done -fi - -# Apply defaults for unset variables -export TIMEOUT=${TIMEOUT:-600s} -export CPU_LIST=${CPU_LIST:-"0-$(($(nproc)-1))"} -export CPUS=${CPUS:-2} -export MEM=${MEM:-$((CPUS * 4))g} -# MAKEFILE_TARGET has no default (empty means full bootstrap) diff --git a/ci3/spinner b/ci3/spinner deleted file mode 100755 index a28e7ad8630..00000000000 --- a/ci3/spinner +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env bash -# Shows a spinner while running a command. -# Usage: spinner -# In interactive mode: prints message + spinner, then "done." in green on success -# In non-interactive mode: just prints message and newline -# On failure: outputs dump_fail style error info -set -uo pipefail -source $(git rev-parse --show-toplevel)/ci3/source_color - -if [ "$#" -ne 2 ]; then - echo "Usage: $0 " - exit 1 -fi - -message="$1" -command="$2" - -# Check if we're in a terminal -if [ ! -t 1 ]; then - # Non-interactive mode: just print message and run command - echo "$message" - bash -c "set -euo pipefail; $command" - exit $? -fi - -# Interactive mode: show spinner -echo -n "$message " - -# Spinner characters (braille circle worm) -spinner_chars=(⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏) - -# Hide cursor and ensure it's restored on exit -printf "$hide_cursor" - -# Run command in background, capturing output -output=$(mktemp) -trap "printf '$show_cursor'; rm -f $output" EXIT - -bash -c "set -euo pipefail; $command" >$output 2>&1 & -pid=$! -trap 'kill -SIGTERM $pid &>/dev/null || true' SIGTERM - -# Show spinner while command runs -i=0 -while kill -0 $pid 2>/dev/null; do - i=$(( (i+1) %10 )) - printf "\r$message ${orange}${spinner_chars[$i]}${reset}" - sleep 0.1 -done - -# Wait for the command to complete and get status -# We wait twice, as the first awakening might be due to this script receiving SIGTERM. -# In that case wait will return with 143, and the trap handler is immediately called. -# We then wait on the pid again, to get the actual status code. -wait $pid -wait $pid -status=$? - -# Show cursor again -printf "$show_cursor" - -# 0 or SIGTERM considered a success. -if [ "$status" -eq 0 ] || [ "$status" -eq 143 ]; then - # Success - print done in green - printf "\r$message ${green}done.${reset}\n" -else - # Failure - clear the line and show dump_fail style output - printf "\r\033[K" - { - echo -e "${red}command failed${reset}: $command (exit: $status)" - echo -e "${blue}--- output ---${reset}" - cat $output - echo -e "${blue}--------------${reset}" - } >&2 -fi - -exit $status diff --git a/ci3/squash_args b/ci3/squash_args deleted file mode 100755 index 84356b2f5ca..00000000000 --- a/ci3/squash_args +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash -cmd=$1 -shift -squashed_args=$(printf '%q ' "$@") -exec $cmd "$squashed_args" diff --git a/ci3/ssm_send_command b/ci3/ssm_send_command deleted file mode 100755 index 558d2eadaa1..00000000000 --- a/ci3/ssm_send_command +++ /dev/null @@ -1,151 +0,0 @@ -#!/usr/bin/env bash -# ssm_send_command - Send a shell script to an EC2 instance via SSM and wait for completion. -# -# Usage: -# ssm_send_command -# ssm_send_command - < script_file -# -# Env: -# SSM_RUN_AS - User to run the command as (default: no runuser wrapper). -# SSM_POLL_TIMEOUT - Max seconds to poll for completion (default: 7200 = 2h). -# SSM_EXEC_TIMEOUT - Max seconds AWS allows the command to run (default: SSM_POLL_TIMEOUT). -# Maps to the AWS-RunShellScript executionTimeout parameter. -# SSM_NO_WAIT - If set to 1, fire-and-forget: send the command and exit 0 immediately. -# AWS_DEFAULT_REGION - Region (default: us-east-2). -# -# Behaviour: -# 1. Base64-encodes the script and sends it via AWS-RunShellScript. -# 2. Polls get-command-invocation every 5s, printing stdout/stderr as it arrives. -# 3. Exits with the remote command's exit code. - -set -euo pipefail - -iid=${1:?"usage: ssm_send_command