ci: cap parallel-ctest-containers concurrency to fix self-hosted NP/LR test failures - #2

Merged
igorls merged 2 commits into
mainfrom
ci/throttle-ctest-concurrency
Jun 7, 2026
Merged

ci: cap parallel-ctest-containers concurrency to fix self-hosted NP/LR test failures#2
igorls merged 2 commits into
mainfrom
ci/throttle-ctest-concurrency

Conversation

@igorls

Copy link
Copy Markdown
Member

Problem

parallel-ctest-containers launched a docker container for every discovered test at once (Promise.all over all of them). The nonparallelizable_tests / long_running_tests suites are heavy multi-nodeos integration tests, so on a self-hosted runner this starved CPU/RAM/ports and made them fail en masse — even trivial tests like get_account_test failed (starvation, not real bugs; they pass on ENF's sized runners and the code is the live-Jungle4-validated 1.2.x harvest).

Fix

Replace the unbounded fan-out with a bounded worker pool: at most N test containers run concurrently (N = CTEST_CONTAINER_CONCURRENCY env, default 4). results[i] stays aligned with tests[i], so the failure-log extraction is unchanged. Rebuilt dist/index.mjs via ncc; verified it loads + runs.

Validation (dispatched run on this branch, 12c/32G self-hosted runner)

  • NP/LR Tests: 12/12 pass — previously 0/16 (all failed under the old fan-out).
  • Platform-cache, all builds, all package jobs (incl. the distutils-fixed ubuntu24/26): green.
  • Final: 41/43 green. The only real failure is wasm_config_part1_unit_test_eos-vm-oc (Tests (asserton)) — a pre-existing, isolated OC unit-test issue consistent across every run, unrelated to this change (tracked separately).

Trade-off: capping concurrency lengthens the NP/LR wall-clock (heavy suites run 4-at-a-time). Tunable per-tier via the env var without a rebuild if needed.

The action launched one docker container per test ALL AT ONCE (Promise.all
over every discovered test). For the nonparallelizable_tests / long_running_tests
suites those are heavy multi-nodeos integration tests, so on a modest self-hosted
runner they starve CPU/RAM/ports and fail en masse (even trivial tests like
get_account_test fail — starvation, not real bugs; they pass on ENF's sized
runners). Replace the unbounded fan-out with a bounded worker pool: at most N
containers run concurrently (N = CTEST_CONTAINER_CONCURRENCY env, default 4).
results[i] stays aligned with tests[i] so failure-log extraction is unchanged.
Rebuilt dist/index.mjs via ncc; verified it loads + runs.
CopilotAI review requested due to automatic review settings June 7, 2026 19:00

@gemini-code-assistgemini-code-assistBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces bounded concurrency for running test containers in parallel, replacing the previous behavior of launching all containers at once. This prevents resource starvation on self-hosted runners by capping the concurrent executions to a default of 4 or a value specified by the CTEST_CONTAINER_CONCURRENCY environment variable. The review feedback suggests refactoring the unconventional for loop in the worker function to a more idiomatic while loop to improve code readability.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +48 to +55
for(let i = next_test++; i < tests.length; i = next_test++) {
const t = tests[i];
// Clear any orphaned container of this name before reusing it (see note above).
child_process.spawnSync("docker", ["rm", "-f", t.name], {stdio:"ignore"});
results[i] = await new Promise(resolve => {
child_process.spawn("docker", ["run", "--security-opt", "seccomp=unconfined", "-e", "GITHUB_ACTIONS=True", "--name", t.name, "--init", "baseimage", "bash", "-c", `cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`], {stdio:"inherit"}).on('close', code => resolve(code));
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The for loop with next_test++ in both the initialization and the increment expression is highly unconventional and can be difficult to read and reason about. Using a standard while loop is much more idiomatic and improves code readability.

while(next_test<tests.length){consti=next_test++;constt=tests[i];// Clear any orphaned container of this name before reusing it (see note above).child_process.spawnSync("docker",["rm","-f",t.name],{stdio:"ignore"});results[i]=awaitnewPromise(resolve=>{child_process.spawn("docker",["run","--security-opt","seccomp=unconfined","-e","GITHUB_ACTIONS=True","--name",t.name,"--init","baseimage","bash","-c",`cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`],{stdio:"inherit"}).on('close',code=>resolve(code));});}

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Caps the parallel-ctest-containers GitHub Action’s test-container execution fan-out by introducing a bounded worker pool, preventing self-hosted runners from being overwhelmed when running heavy NP/LR integration suites.

Changes:

  • Replace unbounded Promise.all container launches with a bounded worker-pool runner.
  • Add CTEST_CONTAINER_CONCURRENCY (default 4) to control maximum concurrent test containers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +42 to +43
const max_concurrency = Math.max(1, parseInt(process.env.CTEST_CONTAINER_CONCURRENCY, 10) || 4);
console.log(`Running ${tests.length} '${tests_label}' test(s), up to ${max_concurrency} container(s) at a time`);
Comment on lines +52 to +54
results[i] = await new Promise(resolve => {
child_process.spawn("docker", ["run", "--security-opt", "seccomp=unconfined", "-e", "GITHUB_ACTIONS=True", "--name", t.name, "--init", "baseimage", "bash", "-c", `cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`], {stdio:"inherit"}).on('close', code => resolve(code));
});
- use a plain while loop instead of the unconventional for(;;) with
next_test++ in both clauses (readability; Gemini)
- parse CTEST_CONTAINER_CONCURRENCY with an explicit NaN check so a
configured 0 clamps to 1 instead of silently falling back to 4 (Copilot)
- add an 'error' handler to the docker spawn so a spawn failure resolves
the worker as failed instead of hanging the pool (Copilot)
Rebuilt dist/index.mjs; behaviour on the happy path is unchanged.
@igorls

Copy link
Copy Markdown
MemberAuthor

Thanks for the review — all three points addressed in 70aad08:

  • while loop (Gemini): replaced the for(let i = next_test++; …; i = next_test++) with a plain while (next_test < tests.length) { const i = next_test++; … }.
  • CTEST_CONTAINER_CONCURRENCY parsing (Copilot): now Number.isNaN(parsed) ? 4 : Math.max(1, parsed), so a configured 0 clamps to 1 instead of silently falling back to 4.
  • spawn error handler (Copilot): the worker's Promise now resolves to a non-zero code on the error event, so a docker-spawn failure marks the test failed instead of hanging the pool.

Happy-path behaviour is unchanged (the validated run had concurrency 4, NP/LR 12/12). Rebuilt dist/index.mjs via ncc and verified it loads + runs.

@igorls
igorls merged commit 3887dfe into mainJun 7, 2026
1 check passed
@igorls
igorls deleted the ci/throttle-ctest-concurrency branch June 7, 2026 22:59
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@igorls
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

ci: cap parallel-ctest-containers concurrency to fix self-hosted NP/LR test failures - #2

Merged
igorls merged 2 commits into
mainfrom
ci/throttle-ctest-concurrency
Jun 7, 2026
Merged

ci: cap parallel-ctest-containers concurrency to fix self-hosted NP/LR test failures#2
igorls merged 2 commits into
mainfrom
ci/throttle-ctest-concurrency

Conversation

@igorls

Copy link
Copy Markdown
Member

Problem

parallel-ctest-containers launched a docker container for every discovered test at once (Promise.all over all of them). The nonparallelizable_tests / long_running_tests suites are heavy multi-nodeos integration tests, so on a self-hosted runner this starved CPU/RAM/ports and made them fail en masse — even trivial tests like get_account_test failed (starvation, not real bugs; they pass on ENF's sized runners and the code is the live-Jungle4-validated 1.2.x harvest).

Fix

Replace the unbounded fan-out with a bounded worker pool: at most N test containers run concurrently (N = CTEST_CONTAINER_CONCURRENCY env, default 4). results[i] stays aligned with tests[i], so the failure-log extraction is unchanged. Rebuilt dist/index.mjs via ncc; verified it loads + runs.

Validation (dispatched run on this branch, 12c/32G self-hosted runner)

  • NP/LR Tests: 12/12 pass — previously 0/16 (all failed under the old fan-out).
  • Platform-cache, all builds, all package jobs (incl. the distutils-fixed ubuntu24/26): green.
  • Final: 41/43 green. The only real failure is wasm_config_part1_unit_test_eos-vm-oc (Tests (asserton)) — a pre-existing, isolated OC unit-test issue consistent across every run, unrelated to this change (tracked separately).

Trade-off: capping concurrency lengthens the NP/LR wall-clock (heavy suites run 4-at-a-time). Tunable per-tier via the env var without a rebuild if needed.

The action launched one docker container per test ALL AT ONCE (Promise.all
over every discovered test). For the nonparallelizable_tests / long_running_tests
suites those are heavy multi-nodeos integration tests, so on a modest self-hosted
runner they starve CPU/RAM/ports and fail en masse (even trivial tests like
get_account_test fail — starvation, not real bugs; they pass on ENF's sized
runners). Replace the unbounded fan-out with a bounded worker pool: at most N
containers run concurrently (N = CTEST_CONTAINER_CONCURRENCY env, default 4).
results[i] stays aligned with tests[i] so failure-log extraction is unchanged.
Rebuilt dist/index.mjs via ncc; verified it loads + runs.
CopilotAI review requested due to automatic review settings June 7, 2026 19:00

@gemini-code-assistgemini-code-assistBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces bounded concurrency for running test containers in parallel, replacing the previous behavior of launching all containers at once. This prevents resource starvation on self-hosted runners by capping the concurrent executions to a default of 4 or a value specified by the CTEST_CONTAINER_CONCURRENCY environment variable. The review feedback suggests refactoring the unconventional for loop in the worker function to a more idiomatic while loop to improve code readability.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +48 to +55
for(let i = next_test++; i < tests.length; i = next_test++) {
const t = tests[i];
// Clear any orphaned container of this name before reusing it (see note above).
child_process.spawnSync("docker", ["rm", "-f", t.name], {stdio:"ignore"});
results[i] = await new Promise(resolve => {
child_process.spawn("docker", ["run", "--security-opt", "seccomp=unconfined", "-e", "GITHUB_ACTIONS=True", "--name", t.name, "--init", "baseimage", "bash", "-c", `cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`], {stdio:"inherit"}).on('close', code => resolve(code));
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The for loop with next_test++ in both the initialization and the increment expression is highly unconventional and can be difficult to read and reason about. Using a standard while loop is much more idiomatic and improves code readability.

while(next_test<tests.length){consti=next_test++;constt=tests[i];// Clear any orphaned container of this name before reusing it (see note above).child_process.spawnSync("docker",["rm","-f",t.name],{stdio:"ignore"});results[i]=awaitnewPromise(resolve=>{child_process.spawn("docker",["run","--security-opt","seccomp=unconfined","-e","GITHUB_ACTIONS=True","--name",t.name,"--init","baseimage","bash","-c",`cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`],{stdio:"inherit"}).on('close',code=>resolve(code));});}

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Caps the parallel-ctest-containers GitHub Action’s test-container execution fan-out by introducing a bounded worker pool, preventing self-hosted runners from being overwhelmed when running heavy NP/LR integration suites.

Changes:

  • Replace unbounded Promise.all container launches with a bounded worker-pool runner.
  • Add CTEST_CONTAINER_CONCURRENCY (default 4) to control maximum concurrent test containers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +42 to +43
const max_concurrency = Math.max(1, parseInt(process.env.CTEST_CONTAINER_CONCURRENCY, 10) || 4);
console.log(`Running ${tests.length} '${tests_label}' test(s), up to ${max_concurrency} container(s) at a time`);
Comment on lines +52 to +54
results[i] = await new Promise(resolve => {
child_process.spawn("docker", ["run", "--security-opt", "seccomp=unconfined", "-e", "GITHUB_ACTIONS=True", "--name", t.name, "--init", "baseimage", "bash", "-c", `cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`], {stdio:"inherit"}).on('close', code => resolve(code));
});
- use a plain while loop instead of the unconventional for(;;) with
next_test++ in both clauses (readability; Gemini)
- parse CTEST_CONTAINER_CONCURRENCY with an explicit NaN check so a
configured 0 clamps to 1 instead of silently falling back to 4 (Copilot)
- add an 'error' handler to the docker spawn so a spawn failure resolves
the worker as failed instead of hanging the pool (Copilot)
Rebuilt dist/index.mjs; behaviour on the happy path is unchanged.
@igorls

Copy link
Copy Markdown
MemberAuthor

Thanks for the review — all three points addressed in 70aad08:

  • while loop (Gemini): replaced the for(let i = next_test++; …; i = next_test++) with a plain while (next_test < tests.length) { const i = next_test++; … }.
  • CTEST_CONTAINER_CONCURRENCY parsing (Copilot): now Number.isNaN(parsed) ? 4 : Math.max(1, parsed), so a configured 0 clamps to 1 instead of silently falling back to 4.
  • spawn error handler (Copilot): the worker's Promise now resolves to a non-zero code on the error event, so a docker-spawn failure marks the test failed instead of hanging the pool.

Happy-path behaviour is unchanged (the validated run had concurrency 4, NP/LR 12/12). Rebuilt dist/index.mjs via ncc and verified it loads + runs.

@igorls
igorls merged commit 3887dfe into mainJun 7, 2026
1 check passed
@igorls
igorls deleted the ci/throttle-ctest-concurrency branch June 7, 2026 22:59
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@igorls
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ci: cap parallel-ctest-containers concurrency to fix self-hosted NP/LR test failures - #2

Merged
igorls merged 2 commits into
mainfrom
ci/throttle-ctest-concurrency
Jun 7, 2026
Merged

ci: cap parallel-ctest-containers concurrency to fix self-hosted NP/LR test failures#2
igorls merged 2 commits into
mainfrom
ci/throttle-ctest-concurrency

Conversation

@igorls

Copy link
Copy Markdown
Member

Problem

parallel-ctest-containers launched a docker container for every discovered test at once (Promise.all over all of them). The nonparallelizable_tests / long_running_tests suites are heavy multi-nodeos integration tests, so on a self-hosted runner this starved CPU/RAM/ports and made them fail en masse — even trivial tests like get_account_test failed (starvation, not real bugs; they pass on ENF's sized runners and the code is the live-Jungle4-validated 1.2.x harvest).

Fix

Replace the unbounded fan-out with a bounded worker pool: at most N test containers run concurrently (N = CTEST_CONTAINER_CONCURRENCY env, default 4). results[i] stays aligned with tests[i], so the failure-log extraction is unchanged. Rebuilt dist/index.mjs via ncc; verified it loads + runs.

Validation (dispatched run on this branch, 12c/32G self-hosted runner)

  • NP/LR Tests: 12/12 pass — previously 0/16 (all failed under the old fan-out).
  • Platform-cache, all builds, all package jobs (incl. the distutils-fixed ubuntu24/26): green.
  • Final: 41/43 green. The only real failure is wasm_config_part1_unit_test_eos-vm-oc (Tests (asserton)) — a pre-existing, isolated OC unit-test issue consistent across every run, unrelated to this change (tracked separately).

Trade-off: capping concurrency lengthens the NP/LR wall-clock (heavy suites run 4-at-a-time). Tunable per-tier via the env var without a rebuild if needed.

The action launched one docker container per test ALL AT ONCE (Promise.all
over every discovered test). For the nonparallelizable_tests / long_running_tests
suites those are heavy multi-nodeos integration tests, so on a modest self-hosted
runner they starve CPU/RAM/ports and fail en masse (even trivial tests like
get_account_test fail — starvation, not real bugs; they pass on ENF's sized
runners). Replace the unbounded fan-out with a bounded worker pool: at most N
containers run concurrently (N = CTEST_CONTAINER_CONCURRENCY env, default 4).
results[i] stays aligned with tests[i] so failure-log extraction is unchanged.
Rebuilt dist/index.mjs via ncc; verified it loads + runs.
CopilotAI review requested due to automatic review settings June 7, 2026 19:00

@gemini-code-assistgemini-code-assistBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces bounded concurrency for running test containers in parallel, replacing the previous behavior of launching all containers at once. This prevents resource starvation on self-hosted runners by capping the concurrent executions to a default of 4 or a value specified by the CTEST_CONTAINER_CONCURRENCY environment variable. The review feedback suggests refactoring the unconventional for loop in the worker function to a more idiomatic while loop to improve code readability.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +48 to +55
for(let i = next_test++; i < tests.length; i = next_test++) {
const t = tests[i];
// Clear any orphaned container of this name before reusing it (see note above).
child_process.spawnSync("docker", ["rm", "-f", t.name], {stdio:"ignore"});
results[i] = await new Promise(resolve => {
child_process.spawn("docker", ["run", "--security-opt", "seccomp=unconfined", "-e", "GITHUB_ACTIONS=True", "--name", t.name, "--init", "baseimage", "bash", "-c", `cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`], {stdio:"inherit"}).on('close', code => resolve(code));
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The for loop with next_test++ in both the initialization and the increment expression is highly unconventional and can be difficult to read and reason about. Using a standard while loop is much more idiomatic and improves code readability.

while(next_test<tests.length){consti=next_test++;constt=tests[i];// Clear any orphaned container of this name before reusing it (see note above).child_process.spawnSync("docker",["rm","-f",t.name],{stdio:"ignore"});results[i]=awaitnewPromise(resolve=>{child_process.spawn("docker",["run","--security-opt","seccomp=unconfined","-e","GITHUB_ACTIONS=True","--name",t.name,"--init","baseimage","bash","-c",`cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`],{stdio:"inherit"}).on('close',code=>resolve(code));});}

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Caps the parallel-ctest-containers GitHub Action’s test-container execution fan-out by introducing a bounded worker pool, preventing self-hosted runners from being overwhelmed when running heavy NP/LR integration suites.

Changes:

  • Replace unbounded Promise.all container launches with a bounded worker-pool runner.
  • Add CTEST_CONTAINER_CONCURRENCY (default 4) to control maximum concurrent test containers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +42 to +43
const max_concurrency = Math.max(1, parseInt(process.env.CTEST_CONTAINER_CONCURRENCY, 10) || 4);
console.log(`Running ${tests.length} '${tests_label}' test(s), up to ${max_concurrency} container(s) at a time`);
Comment on lines +52 to +54
results[i] = await new Promise(resolve => {
child_process.spawn("docker", ["run", "--security-opt", "seccomp=unconfined", "-e", "GITHUB_ACTIONS=True", "--name", t.name, "--init", "baseimage", "bash", "-c", `cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`], {stdio:"inherit"}).on('close', code => resolve(code));
});
- use a plain while loop instead of the unconventional for(;;) with
next_test++ in both clauses (readability; Gemini)
- parse CTEST_CONTAINER_CONCURRENCY with an explicit NaN check so a
configured 0 clamps to 1 instead of silently falling back to 4 (Copilot)
- add an 'error' handler to the docker spawn so a spawn failure resolves
the worker as failed instead of hanging the pool (Copilot)
Rebuilt dist/index.mjs; behaviour on the happy path is unchanged.
@igorls

Copy link
Copy Markdown
MemberAuthor

Thanks for the review — all three points addressed in 70aad08:

  • while loop (Gemini): replaced the for(let i = next_test++; …; i = next_test++) with a plain while (next_test < tests.length) { const i = next_test++; … }.
  • CTEST_CONTAINER_CONCURRENCY parsing (Copilot): now Number.isNaN(parsed) ? 4 : Math.max(1, parsed), so a configured 0 clamps to 1 instead of silently falling back to 4.
  • spawn error handler (Copilot): the worker's Promise now resolves to a non-zero code on the error event, so a docker-spawn failure marks the test failed instead of hanging the pool.

Happy-path behaviour is unchanged (the validated run had concurrency 4, NP/LR 12/12). Rebuilt dist/index.mjs via ncc and verified it loads + runs.

@igorls
igorls merged commit 3887dfe into mainJun 7, 2026
1 check passed
@igorls
igorls deleted the ci/throttle-ctest-concurrency branch June 7, 2026 22:59
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@igorls
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ci: cap parallel-ctest-containers concurrency to fix self-hosted NP/LR test failures - #2

Merged
igorls merged 2 commits into
mainfrom
ci/throttle-ctest-concurrency
Jun 7, 2026
Merged

ci: cap parallel-ctest-containers concurrency to fix self-hosted NP/LR test failures#2
igorls merged 2 commits into
mainfrom
ci/throttle-ctest-concurrency

Conversation

@igorls

Copy link
Copy Markdown
Member

Problem

parallel-ctest-containers launched a docker container for every discovered test at once (Promise.all over all of them). The nonparallelizable_tests / long_running_tests suites are heavy multi-nodeos integration tests, so on a self-hosted runner this starved CPU/RAM/ports and made them fail en masse — even trivial tests like get_account_test failed (starvation, not real bugs; they pass on ENF's sized runners and the code is the live-Jungle4-validated 1.2.x harvest).

Fix

Replace the unbounded fan-out with a bounded worker pool: at most N test containers run concurrently (N = CTEST_CONTAINER_CONCURRENCY env, default 4). results[i] stays aligned with tests[i], so the failure-log extraction is unchanged. Rebuilt dist/index.mjs via ncc; verified it loads + runs.

Validation (dispatched run on this branch, 12c/32G self-hosted runner)

  • NP/LR Tests: 12/12 pass — previously 0/16 (all failed under the old fan-out).
  • Platform-cache, all builds, all package jobs (incl. the distutils-fixed ubuntu24/26): green.
  • Final: 41/43 green. The only real failure is wasm_config_part1_unit_test_eos-vm-oc (Tests (asserton)) — a pre-existing, isolated OC unit-test issue consistent across every run, unrelated to this change (tracked separately).

Trade-off: capping concurrency lengthens the NP/LR wall-clock (heavy suites run 4-at-a-time). Tunable per-tier via the env var without a rebuild if needed.

The action launched one docker container per test ALL AT ONCE (Promise.all
over every discovered test). For the nonparallelizable_tests / long_running_tests
suites those are heavy multi-nodeos integration tests, so on a modest self-hosted
runner they starve CPU/RAM/ports and fail en masse (even trivial tests like
get_account_test fail — starvation, not real bugs; they pass on ENF's sized
runners). Replace the unbounded fan-out with a bounded worker pool: at most N
containers run concurrently (N = CTEST_CONTAINER_CONCURRENCY env, default 4).
results[i] stays aligned with tests[i] so failure-log extraction is unchanged.
Rebuilt dist/index.mjs via ncc; verified it loads + runs.
CopilotAI review requested due to automatic review settings June 7, 2026 19:00

@gemini-code-assistgemini-code-assistBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces bounded concurrency for running test containers in parallel, replacing the previous behavior of launching all containers at once. This prevents resource starvation on self-hosted runners by capping the concurrent executions to a default of 4 or a value specified by the CTEST_CONTAINER_CONCURRENCY environment variable. The review feedback suggests refactoring the unconventional for loop in the worker function to a more idiomatic while loop to improve code readability.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +48 to +55
for(let i = next_test++; i < tests.length; i = next_test++) {
const t = tests[i];
// Clear any orphaned container of this name before reusing it (see note above).
child_process.spawnSync("docker", ["rm", "-f", t.name], {stdio:"ignore"});
results[i] = await new Promise(resolve => {
child_process.spawn("docker", ["run", "--security-opt", "seccomp=unconfined", "-e", "GITHUB_ACTIONS=True", "--name", t.name, "--init", "baseimage", "bash", "-c", `cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`], {stdio:"inherit"}).on('close', code => resolve(code));
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The for loop with next_test++ in both the initialization and the increment expression is highly unconventional and can be difficult to read and reason about. Using a standard while loop is much more idiomatic and improves code readability.

while(next_test<tests.length){consti=next_test++;constt=tests[i];// Clear any orphaned container of this name before reusing it (see note above).child_process.spawnSync("docker",["rm","-f",t.name],{stdio:"ignore"});results[i]=awaitnewPromise(resolve=>{child_process.spawn("docker",["run","--security-opt","seccomp=unconfined","-e","GITHUB_ACTIONS=True","--name",t.name,"--init","baseimage","bash","-c",`cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`],{stdio:"inherit"}).on('close',code=>resolve(code));});}

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Caps the parallel-ctest-containers GitHub Action’s test-container execution fan-out by introducing a bounded worker pool, preventing self-hosted runners from being overwhelmed when running heavy NP/LR integration suites.

Changes:

  • Replace unbounded Promise.all container launches with a bounded worker-pool runner.
  • Add CTEST_CONTAINER_CONCURRENCY (default 4) to control maximum concurrent test containers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +42 to +43
const max_concurrency = Math.max(1, parseInt(process.env.CTEST_CONTAINER_CONCURRENCY, 10) || 4);
console.log(`Running ${tests.length} '${tests_label}' test(s), up to ${max_concurrency} container(s) at a time`);
Comment on lines +52 to +54
results[i] = await new Promise(resolve => {
child_process.spawn("docker", ["run", "--security-opt", "seccomp=unconfined", "-e", "GITHUB_ACTIONS=True", "--name", t.name, "--init", "baseimage", "bash", "-c", `cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`], {stdio:"inherit"}).on('close', code => resolve(code));
});
- use a plain while loop instead of the unconventional for(;;) with
next_test++ in both clauses (readability; Gemini)
- parse CTEST_CONTAINER_CONCURRENCY with an explicit NaN check so a
configured 0 clamps to 1 instead of silently falling back to 4 (Copilot)
- add an 'error' handler to the docker spawn so a spawn failure resolves
the worker as failed instead of hanging the pool (Copilot)
Rebuilt dist/index.mjs; behaviour on the happy path is unchanged.
@igorls

Copy link
Copy Markdown
MemberAuthor

Thanks for the review — all three points addressed in 70aad08:

  • while loop (Gemini): replaced the for(let i = next_test++; …; i = next_test++) with a plain while (next_test < tests.length) { const i = next_test++; … }.
  • CTEST_CONTAINER_CONCURRENCY parsing (Copilot): now Number.isNaN(parsed) ? 4 : Math.max(1, parsed), so a configured 0 clamps to 1 instead of silently falling back to 4.
  • spawn error handler (Copilot): the worker's Promise now resolves to a non-zero code on the error event, so a docker-spawn failure marks the test failed instead of hanging the pool.

Happy-path behaviour is unchanged (the validated run had concurrency 4, NP/LR 12/12). Rebuilt dist/index.mjs via ncc and verified it loads + runs.

@igorls
igorls merged commit 3887dfe into mainJun 7, 2026
1 check passed
@igorls
igorls deleted the ci/throttle-ctest-concurrency branch June 7, 2026 22:59
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@igorls
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

ci: cap parallel-ctest-containers concurrency to fix self-hosted NP/LR test failures - #2

Merged
igorls merged 2 commits into
mainfrom
ci/throttle-ctest-concurrency
Jun 7, 2026
Merged

ci: cap parallel-ctest-containers concurrency to fix self-hosted NP/LR test failures#2
igorls merged 2 commits into
mainfrom
ci/throttle-ctest-concurrency

Conversation

@igorls

Copy link
Copy Markdown
Member

Problem

parallel-ctest-containers launched a docker container for every discovered test at once (Promise.all over all of them). The nonparallelizable_tests / long_running_tests suites are heavy multi-nodeos integration tests, so on a self-hosted runner this starved CPU/RAM/ports and made them fail en masse — even trivial tests like get_account_test failed (starvation, not real bugs; they pass on ENF's sized runners and the code is the live-Jungle4-validated 1.2.x harvest).

Fix

Replace the unbounded fan-out with a bounded worker pool: at most N test containers run concurrently (N = CTEST_CONTAINER_CONCURRENCY env, default 4). results[i] stays aligned with tests[i], so the failure-log extraction is unchanged. Rebuilt dist/index.mjs via ncc; verified it loads + runs.

Validation (dispatched run on this branch, 12c/32G self-hosted runner)

  • NP/LR Tests: 12/12 pass — previously 0/16 (all failed under the old fan-out).
  • Platform-cache, all builds, all package jobs (incl. the distutils-fixed ubuntu24/26): green.
  • Final: 41/43 green. The only real failure is wasm_config_part1_unit_test_eos-vm-oc (Tests (asserton)) — a pre-existing, isolated OC unit-test issue consistent across every run, unrelated to this change (tracked separately).

Trade-off: capping concurrency lengthens the NP/LR wall-clock (heavy suites run 4-at-a-time). Tunable per-tier via the env var without a rebuild if needed.

The action launched one docker container per test ALL AT ONCE (Promise.all
over every discovered test). For the nonparallelizable_tests / long_running_tests
suites those are heavy multi-nodeos integration tests, so on a modest self-hosted
runner they starve CPU/RAM/ports and fail en masse (even trivial tests like
get_account_test fail — starvation, not real bugs; they pass on ENF's sized
runners). Replace the unbounded fan-out with a bounded worker pool: at most N
containers run concurrently (N = CTEST_CONTAINER_CONCURRENCY env, default 4).
results[i] stays aligned with tests[i] so failure-log extraction is unchanged.
Rebuilt dist/index.mjs via ncc; verified it loads + runs.
CopilotAI review requested due to automatic review settings June 7, 2026 19:00

@gemini-code-assistgemini-code-assistBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces bounded concurrency for running test containers in parallel, replacing the previous behavior of launching all containers at once. This prevents resource starvation on self-hosted runners by capping the concurrent executions to a default of 4 or a value specified by the CTEST_CONTAINER_CONCURRENCY environment variable. The review feedback suggests refactoring the unconventional for loop in the worker function to a more idiomatic while loop to improve code readability.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +48 to +55
for(let i = next_test++; i < tests.length; i = next_test++) {
const t = tests[i];
// Clear any orphaned container of this name before reusing it (see note above).
child_process.spawnSync("docker", ["rm", "-f", t.name], {stdio:"ignore"});
results[i] = await new Promise(resolve => {
child_process.spawn("docker", ["run", "--security-opt", "seccomp=unconfined", "-e", "GITHUB_ACTIONS=True", "--name", t.name, "--init", "baseimage", "bash", "-c", `cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`], {stdio:"inherit"}).on('close', code => resolve(code));
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The for loop with next_test++ in both the initialization and the increment expression is highly unconventional and can be difficult to read and reason about. Using a standard while loop is much more idiomatic and improves code readability.

while(next_test<tests.length){consti=next_test++;constt=tests[i];// Clear any orphaned container of this name before reusing it (see note above).child_process.spawnSync("docker",["rm","-f",t.name],{stdio:"ignore"});results[i]=awaitnewPromise(resolve=>{child_process.spawn("docker",["run","--security-opt","seccomp=unconfined","-e","GITHUB_ACTIONS=True","--name",t.name,"--init","baseimage","bash","-c",`cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`],{stdio:"inherit"}).on('close',code=>resolve(code));});}

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Caps the parallel-ctest-containers GitHub Action’s test-container execution fan-out by introducing a bounded worker pool, preventing self-hosted runners from being overwhelmed when running heavy NP/LR integration suites.

Changes:

  • Replace unbounded Promise.all container launches with a bounded worker-pool runner.
  • Add CTEST_CONTAINER_CONCURRENCY (default 4) to control maximum concurrent test containers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +42 to +43
const max_concurrency = Math.max(1, parseInt(process.env.CTEST_CONTAINER_CONCURRENCY, 10) || 4);
console.log(`Running ${tests.length} '${tests_label}' test(s), up to ${max_concurrency} container(s) at a time`);
Comment on lines +52 to +54
results[i] = await new Promise(resolve => {
child_process.spawn("docker", ["run", "--security-opt", "seccomp=unconfined", "-e", "GITHUB_ACTIONS=True", "--name", t.name, "--init", "baseimage", "bash", "-c", `cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`], {stdio:"inherit"}).on('close', code => resolve(code));
});
- use a plain while loop instead of the unconventional for(;;) with
next_test++ in both clauses (readability; Gemini)
- parse CTEST_CONTAINER_CONCURRENCY with an explicit NaN check so a
configured 0 clamps to 1 instead of silently falling back to 4 (Copilot)
- add an 'error' handler to the docker spawn so a spawn failure resolves
the worker as failed instead of hanging the pool (Copilot)
Rebuilt dist/index.mjs; behaviour on the happy path is unchanged.
@igorls

Copy link
Copy Markdown
MemberAuthor

Thanks for the review — all three points addressed in 70aad08:

  • while loop (Gemini): replaced the for(let i = next_test++; …; i = next_test++) with a plain while (next_test < tests.length) { const i = next_test++; … }.
  • CTEST_CONTAINER_CONCURRENCY parsing (Copilot): now Number.isNaN(parsed) ? 4 : Math.max(1, parsed), so a configured 0 clamps to 1 instead of silently falling back to 4.
  • spawn error handler (Copilot): the worker's Promise now resolves to a non-zero code on the error event, so a docker-spawn failure marks the test failed instead of hanging the pool.

Happy-path behaviour is unchanged (the validated run had concurrency 4, NP/LR 12/12). Rebuilt dist/index.mjs via ncc and verified it loads + runs.

@igorls
igorls merged commit 3887dfe into mainJun 7, 2026
1 check passed
@igorls
igorls deleted the ci/throttle-ctest-concurrency branch June 7, 2026 22:59
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@igorls
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ci: cap parallel-ctest-containers concurrency to fix self-hosted NP/LR test failures - #2

Merged
igorls merged 2 commits into
mainfrom
ci/throttle-ctest-concurrency
Jun 7, 2026
Merged

ci: cap parallel-ctest-containers concurrency to fix self-hosted NP/LR test failures#2
igorls merged 2 commits into
mainfrom
ci/throttle-ctest-concurrency

Conversation

@igorls

Copy link
Copy Markdown
Member

Problem

parallel-ctest-containers launched a docker container for every discovered test at once (Promise.all over all of them). The nonparallelizable_tests / long_running_tests suites are heavy multi-nodeos integration tests, so on a self-hosted runner this starved CPU/RAM/ports and made them fail en masse — even trivial tests like get_account_test failed (starvation, not real bugs; they pass on ENF's sized runners and the code is the live-Jungle4-validated 1.2.x harvest).

Fix

Replace the unbounded fan-out with a bounded worker pool: at most N test containers run concurrently (N = CTEST_CONTAINER_CONCURRENCY env, default 4). results[i] stays aligned with tests[i], so the failure-log extraction is unchanged. Rebuilt dist/index.mjs via ncc; verified it loads + runs.

Validation (dispatched run on this branch, 12c/32G self-hosted runner)

  • NP/LR Tests: 12/12 pass — previously 0/16 (all failed under the old fan-out).
  • Platform-cache, all builds, all package jobs (incl. the distutils-fixed ubuntu24/26): green.
  • Final: 41/43 green. The only real failure is wasm_config_part1_unit_test_eos-vm-oc (Tests (asserton)) — a pre-existing, isolated OC unit-test issue consistent across every run, unrelated to this change (tracked separately).

Trade-off: capping concurrency lengthens the NP/LR wall-clock (heavy suites run 4-at-a-time). Tunable per-tier via the env var without a rebuild if needed.

The action launched one docker container per test ALL AT ONCE (Promise.all
over every discovered test). For the nonparallelizable_tests / long_running_tests
suites those are heavy multi-nodeos integration tests, so on a modest self-hosted
runner they starve CPU/RAM/ports and fail en masse (even trivial tests like
get_account_test fail — starvation, not real bugs; they pass on ENF's sized
runners). Replace the unbounded fan-out with a bounded worker pool: at most N
containers run concurrently (N = CTEST_CONTAINER_CONCURRENCY env, default 4).
results[i] stays aligned with tests[i] so failure-log extraction is unchanged.
Rebuilt dist/index.mjs via ncc; verified it loads + runs.
CopilotAI review requested due to automatic review settings June 7, 2026 19:00

@gemini-code-assistgemini-code-assistBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces bounded concurrency for running test containers in parallel, replacing the previous behavior of launching all containers at once. This prevents resource starvation on self-hosted runners by capping the concurrent executions to a default of 4 or a value specified by the CTEST_CONTAINER_CONCURRENCY environment variable. The review feedback suggests refactoring the unconventional for loop in the worker function to a more idiomatic while loop to improve code readability.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +48 to +55
for(let i = next_test++; i < tests.length; i = next_test++) {
const t = tests[i];
// Clear any orphaned container of this name before reusing it (see note above).
child_process.spawnSync("docker", ["rm", "-f", t.name], {stdio:"ignore"});
results[i] = await new Promise(resolve => {
child_process.spawn("docker", ["run", "--security-opt", "seccomp=unconfined", "-e", "GITHUB_ACTIONS=True", "--name", t.name, "--init", "baseimage", "bash", "-c", `cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`], {stdio:"inherit"}).on('close', code => resolve(code));
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The for loop with next_test++ in both the initialization and the increment expression is highly unconventional and can be difficult to read and reason about. Using a standard while loop is much more idiomatic and improves code readability.

while(next_test<tests.length){consti=next_test++;constt=tests[i];// Clear any orphaned container of this name before reusing it (see note above).child_process.spawnSync("docker",["rm","-f",t.name],{stdio:"ignore"});results[i]=awaitnewPromise(resolve=>{child_process.spawn("docker",["run","--security-opt","seccomp=unconfined","-e","GITHUB_ACTIONS=True","--name",t.name,"--init","baseimage","bash","-c",`cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`],{stdio:"inherit"}).on('close',code=>resolve(code));});}

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Caps the parallel-ctest-containers GitHub Action’s test-container execution fan-out by introducing a bounded worker pool, preventing self-hosted runners from being overwhelmed when running heavy NP/LR integration suites.

Changes:

  • Replace unbounded Promise.all container launches with a bounded worker-pool runner.
  • Add CTEST_CONTAINER_CONCURRENCY (default 4) to control maximum concurrent test containers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +42 to +43
const max_concurrency = Math.max(1, parseInt(process.env.CTEST_CONTAINER_CONCURRENCY, 10) || 4);
console.log(`Running ${tests.length} '${tests_label}' test(s), up to ${max_concurrency} container(s) at a time`);
Comment on lines +52 to +54
results[i] = await new Promise(resolve => {
child_process.spawn("docker", ["run", "--security-opt", "seccomp=unconfined", "-e", "GITHUB_ACTIONS=True", "--name", t.name, "--init", "baseimage", "bash", "-c", `cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`], {stdio:"inherit"}).on('close', code => resolve(code));
});
- use a plain while loop instead of the unconventional for(;;) with
next_test++ in both clauses (readability; Gemini)
- parse CTEST_CONTAINER_CONCURRENCY with an explicit NaN check so a
configured 0 clamps to 1 instead of silently falling back to 4 (Copilot)
- add an 'error' handler to the docker spawn so a spawn failure resolves
the worker as failed instead of hanging the pool (Copilot)
Rebuilt dist/index.mjs; behaviour on the happy path is unchanged.
@igorls

Copy link
Copy Markdown
MemberAuthor

Thanks for the review — all three points addressed in 70aad08:

  • while loop (Gemini): replaced the for(let i = next_test++; …; i = next_test++) with a plain while (next_test < tests.length) { const i = next_test++; … }.
  • CTEST_CONTAINER_CONCURRENCY parsing (Copilot): now Number.isNaN(parsed) ? 4 : Math.max(1, parsed), so a configured 0 clamps to 1 instead of silently falling back to 4.
  • spawn error handler (Copilot): the worker's Promise now resolves to a non-zero code on the error event, so a docker-spawn failure marks the test failed instead of hanging the pool.

Happy-path behaviour is unchanged (the validated run had concurrency 4, NP/LR 12/12). Rebuilt dist/index.mjs via ncc and verified it loads + runs.

@igorls
igorls merged commit 3887dfe into mainJun 7, 2026
1 check passed
@igorls
igorls deleted the ci/throttle-ctest-concurrency branch June 7, 2026 22:59
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@igorls
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ci: cap parallel-ctest-containers concurrency to fix self-hosted NP/LR test failures - #2

Merged
igorls merged 2 commits into
mainfrom
ci/throttle-ctest-concurrency
Jun 7, 2026
Merged

ci: cap parallel-ctest-containers concurrency to fix self-hosted NP/LR test failures#2
igorls merged 2 commits into
mainfrom
ci/throttle-ctest-concurrency

Conversation

@igorls

Copy link
Copy Markdown
Member

Problem

parallel-ctest-containers launched a docker container for every discovered test at once (Promise.all over all of them). The nonparallelizable_tests / long_running_tests suites are heavy multi-nodeos integration tests, so on a self-hosted runner this starved CPU/RAM/ports and made them fail en masse — even trivial tests like get_account_test failed (starvation, not real bugs; they pass on ENF's sized runners and the code is the live-Jungle4-validated 1.2.x harvest).

Fix

Replace the unbounded fan-out with a bounded worker pool: at most N test containers run concurrently (N = CTEST_CONTAINER_CONCURRENCY env, default 4). results[i] stays aligned with tests[i], so the failure-log extraction is unchanged. Rebuilt dist/index.mjs via ncc; verified it loads + runs.

Validation (dispatched run on this branch, 12c/32G self-hosted runner)

  • NP/LR Tests: 12/12 pass — previously 0/16 (all failed under the old fan-out).
  • Platform-cache, all builds, all package jobs (incl. the distutils-fixed ubuntu24/26): green.
  • Final: 41/43 green. The only real failure is wasm_config_part1_unit_test_eos-vm-oc (Tests (asserton)) — a pre-existing, isolated OC unit-test issue consistent across every run, unrelated to this change (tracked separately).

Trade-off: capping concurrency lengthens the NP/LR wall-clock (heavy suites run 4-at-a-time). Tunable per-tier via the env var without a rebuild if needed.

The action launched one docker container per test ALL AT ONCE (Promise.all
over every discovered test). For the nonparallelizable_tests / long_running_tests
suites those are heavy multi-nodeos integration tests, so on a modest self-hosted
runner they starve CPU/RAM/ports and fail en masse (even trivial tests like
get_account_test fail — starvation, not real bugs; they pass on ENF's sized
runners). Replace the unbounded fan-out with a bounded worker pool: at most N
containers run concurrently (N = CTEST_CONTAINER_CONCURRENCY env, default 4).
results[i] stays aligned with tests[i] so failure-log extraction is unchanged.
Rebuilt dist/index.mjs via ncc; verified it loads + runs.
CopilotAI review requested due to automatic review settings June 7, 2026 19:00

@gemini-code-assistgemini-code-assistBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces bounded concurrency for running test containers in parallel, replacing the previous behavior of launching all containers at once. This prevents resource starvation on self-hosted runners by capping the concurrent executions to a default of 4 or a value specified by the CTEST_CONTAINER_CONCURRENCY environment variable. The review feedback suggests refactoring the unconventional for loop in the worker function to a more idiomatic while loop to improve code readability.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +48 to +55
for(let i = next_test++; i < tests.length; i = next_test++) {
const t = tests[i];
// Clear any orphaned container of this name before reusing it (see note above).
child_process.spawnSync("docker", ["rm", "-f", t.name], {stdio:"ignore"});
results[i] = await new Promise(resolve => {
child_process.spawn("docker", ["run", "--security-opt", "seccomp=unconfined", "-e", "GITHUB_ACTIONS=True", "--name", t.name, "--init", "baseimage", "bash", "-c", `cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`], {stdio:"inherit"}).on('close', code => resolve(code));
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The for loop with next_test++ in both the initialization and the increment expression is highly unconventional and can be difficult to read and reason about. Using a standard while loop is much more idiomatic and improves code readability.

while(next_test<tests.length){consti=next_test++;constt=tests[i];// Clear any orphaned container of this name before reusing it (see note above).child_process.spawnSync("docker",["rm","-f",t.name],{stdio:"ignore"});results[i]=awaitnewPromise(resolve=>{child_process.spawn("docker",["run","--security-opt","seccomp=unconfined","-e","GITHUB_ACTIONS=True","--name",t.name,"--init","baseimage","bash","-c",`cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`],{stdio:"inherit"}).on('close',code=>resolve(code));});}

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Caps the parallel-ctest-containers GitHub Action’s test-container execution fan-out by introducing a bounded worker pool, preventing self-hosted runners from being overwhelmed when running heavy NP/LR integration suites.

Changes:

  • Replace unbounded Promise.all container launches with a bounded worker-pool runner.
  • Add CTEST_CONTAINER_CONCURRENCY (default 4) to control maximum concurrent test containers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +42 to +43
const max_concurrency = Math.max(1, parseInt(process.env.CTEST_CONTAINER_CONCURRENCY, 10) || 4);
console.log(`Running ${tests.length} '${tests_label}' test(s), up to ${max_concurrency} container(s) at a time`);
Comment on lines +52 to +54
results[i] = await new Promise(resolve => {
child_process.spawn("docker", ["run", "--security-opt", "seccomp=unconfined", "-e", "GITHUB_ACTIONS=True", "--name", t.name, "--init", "baseimage", "bash", "-c", `cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`], {stdio:"inherit"}).on('close', code => resolve(code));
});
- use a plain while loop instead of the unconventional for(;;) with
next_test++ in both clauses (readability; Gemini)
- parse CTEST_CONTAINER_CONCURRENCY with an explicit NaN check so a
configured 0 clamps to 1 instead of silently falling back to 4 (Copilot)
- add an 'error' handler to the docker spawn so a spawn failure resolves
the worker as failed instead of hanging the pool (Copilot)
Rebuilt dist/index.mjs; behaviour on the happy path is unchanged.
@igorls

Copy link
Copy Markdown
MemberAuthor

Thanks for the review — all three points addressed in 70aad08:

  • while loop (Gemini): replaced the for(let i = next_test++; …; i = next_test++) with a plain while (next_test < tests.length) { const i = next_test++; … }.
  • CTEST_CONTAINER_CONCURRENCY parsing (Copilot): now Number.isNaN(parsed) ? 4 : Math.max(1, parsed), so a configured 0 clamps to 1 instead of silently falling back to 4.
  • spawn error handler (Copilot): the worker's Promise now resolves to a non-zero code on the error event, so a docker-spawn failure marks the test failed instead of hanging the pool.

Happy-path behaviour is unchanged (the validated run had concurrency 4, NP/LR 12/12). Rebuilt dist/index.mjs via ncc and verified it loads + runs.

@igorls
igorls merged commit 3887dfe into mainJun 7, 2026
1 check passed
@igorls
igorls deleted the ci/throttle-ctest-concurrency branch June 7, 2026 22:59
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@igorls
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

ci: cap parallel-ctest-containers concurrency to fix self-hosted NP/LR test failures - #2

Merged
igorls merged 2 commits into
mainfrom
ci/throttle-ctest-concurrency
Jun 7, 2026
Merged

ci: cap parallel-ctest-containers concurrency to fix self-hosted NP/LR test failures#2
igorls merged 2 commits into
mainfrom
ci/throttle-ctest-concurrency

Conversation

@igorls

Copy link
Copy Markdown
Member

Problem

parallel-ctest-containers launched a docker container for every discovered test at once (Promise.all over all of them). The nonparallelizable_tests / long_running_tests suites are heavy multi-nodeos integration tests, so on a self-hosted runner this starved CPU/RAM/ports and made them fail en masse — even trivial tests like get_account_test failed (starvation, not real bugs; they pass on ENF's sized runners and the code is the live-Jungle4-validated 1.2.x harvest).

Fix

Replace the unbounded fan-out with a bounded worker pool: at most N test containers run concurrently (N = CTEST_CONTAINER_CONCURRENCY env, default 4). results[i] stays aligned with tests[i], so the failure-log extraction is unchanged. Rebuilt dist/index.mjs via ncc; verified it loads + runs.

Validation (dispatched run on this branch, 12c/32G self-hosted runner)

  • NP/LR Tests: 12/12 pass — previously 0/16 (all failed under the old fan-out).
  • Platform-cache, all builds, all package jobs (incl. the distutils-fixed ubuntu24/26): green.
  • Final: 41/43 green. The only real failure is wasm_config_part1_unit_test_eos-vm-oc (Tests (asserton)) — a pre-existing, isolated OC unit-test issue consistent across every run, unrelated to this change (tracked separately).

Trade-off: capping concurrency lengthens the NP/LR wall-clock (heavy suites run 4-at-a-time). Tunable per-tier via the env var without a rebuild if needed.

The action launched one docker container per test ALL AT ONCE (Promise.all
over every discovered test). For the nonparallelizable_tests / long_running_tests
suites those are heavy multi-nodeos integration tests, so on a modest self-hosted
runner they starve CPU/RAM/ports and fail en masse (even trivial tests like
get_account_test fail — starvation, not real bugs; they pass on ENF's sized
runners). Replace the unbounded fan-out with a bounded worker pool: at most N
containers run concurrently (N = CTEST_CONTAINER_CONCURRENCY env, default 4).
results[i] stays aligned with tests[i] so failure-log extraction is unchanged.
Rebuilt dist/index.mjs via ncc; verified it loads + runs.
CopilotAI review requested due to automatic review settings June 7, 2026 19:00

@gemini-code-assistgemini-code-assistBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces bounded concurrency for running test containers in parallel, replacing the previous behavior of launching all containers at once. This prevents resource starvation on self-hosted runners by capping the concurrent executions to a default of 4 or a value specified by the CTEST_CONTAINER_CONCURRENCY environment variable. The review feedback suggests refactoring the unconventional for loop in the worker function to a more idiomatic while loop to improve code readability.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +48 to +55
for(let i = next_test++; i < tests.length; i = next_test++) {
const t = tests[i];
// Clear any orphaned container of this name before reusing it (see note above).
child_process.spawnSync("docker", ["rm", "-f", t.name], {stdio:"ignore"});
results[i] = await new Promise(resolve => {
child_process.spawn("docker", ["run", "--security-opt", "seccomp=unconfined", "-e", "GITHUB_ACTIONS=True", "--name", t.name, "--init", "baseimage", "bash", "-c", `cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`], {stdio:"inherit"}).on('close', code => resolve(code));
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The for loop with next_test++ in both the initialization and the increment expression is highly unconventional and can be difficult to read and reason about. Using a standard while loop is much more idiomatic and improves code readability.

while(next_test<tests.length){consti=next_test++;constt=tests[i];// Clear any orphaned container of this name before reusing it (see note above).child_process.spawnSync("docker",["rm","-f",t.name],{stdio:"ignore"});results[i]=awaitnewPromise(resolve=>{child_process.spawn("docker",["run","--security-opt","seccomp=unconfined","-e","GITHUB_ACTIONS=True","--name",t.name,"--init","baseimage","bash","-c",`cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`],{stdio:"inherit"}).on('close',code=>resolve(code));});}

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Caps the parallel-ctest-containers GitHub Action’s test-container execution fan-out by introducing a bounded worker pool, preventing self-hosted runners from being overwhelmed when running heavy NP/LR integration suites.

Changes:

  • Replace unbounded Promise.all container launches with a bounded worker-pool runner.
  • Add CTEST_CONTAINER_CONCURRENCY (default 4) to control maximum concurrent test containers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +42 to +43
const max_concurrency = Math.max(1, parseInt(process.env.CTEST_CONTAINER_CONCURRENCY, 10) || 4);
console.log(`Running ${tests.length} '${tests_label}' test(s), up to ${max_concurrency} container(s) at a time`);
Comment on lines +52 to +54
results[i] = await new Promise(resolve => {
child_process.spawn("docker", ["run", "--security-opt", "seccomp=unconfined", "-e", "GITHUB_ACTIONS=True", "--name", t.name, "--init", "baseimage", "bash", "-c", `cd build; ctest --output-on-failure -R '^${t.name}$' --timeout ${test_timeout}`], {stdio:"inherit"}).on('close', code => resolve(code));
});
- use a plain while loop instead of the unconventional for(;;) with
next_test++ in both clauses (readability; Gemini)
- parse CTEST_CONTAINER_CONCURRENCY with an explicit NaN check so a
configured 0 clamps to 1 instead of silently falling back to 4 (Copilot)
- add an 'error' handler to the docker spawn so a spawn failure resolves
the worker as failed instead of hanging the pool (Copilot)
Rebuilt dist/index.mjs; behaviour on the happy path is unchanged.
@igorls

Copy link
Copy Markdown
MemberAuthor

Thanks for the review — all three points addressed in 70aad08:

  • while loop (Gemini): replaced the for(let i = next_test++; …; i = next_test++) with a plain while (next_test < tests.length) { const i = next_test++; … }.
  • CTEST_CONTAINER_CONCURRENCY parsing (Copilot): now Number.isNaN(parsed) ? 4 : Math.max(1, parsed), so a configured 0 clamps to 1 instead of silently falling back to 4.
  • spawn error handler (Copilot): the worker's Promise now resolves to a non-zero code on the error event, so a docker-spawn failure marks the test failed instead of hanging the pool.

Happy-path behaviour is unchanged (the validated run had concurrency 4, NP/LR 12/12). Rebuilt dist/index.mjs via ncc and verified it loads + runs.

@igorls
igorls merged commit 3887dfe into mainJun 7, 2026
1 check passed
@igorls
igorls deleted the ci/throttle-ctest-concurrency branch June 7, 2026 22:59
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@igorls