From 1c91f2a12d9cde206eaf0f55f18c9ffc16ba4fc8 Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah Date: Tue, 30 Jun 2026 18:44:29 -0700 Subject: [PATCH] ci: keep project intake gh stderr separate --- .github/workflows/project-intake.yml | 42 ++++++++++++++++++++++------ tests/test_github_workflows.py | 13 +++++++++ 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/.github/workflows/project-intake.yml b/.github/workflows/project-intake.yml index d95f0de..80694d6 100644 --- a/.github/workflows/project-intake.yml +++ b/.github/workflows/project-intake.yml @@ -96,42 +96,68 @@ jobs: local operation="$1" shift local output + local error_output + local diagnostic_output + local stderr_file local status local retry_delay - if output="$("$@" 2>&1)"; then + stderr_file="$(mktemp)" + if output="$("$@" 2>"$stderr_file")"; then + error_output="$(cat "$stderr_file")" + rm -f "$stderr_file" + [[ -z "$error_output" ]] || printf '%s\n' "$error_output" >&2 [[ -z "$output" ]] || printf '%s\n' "$output" return 0 else status=$? + error_output="$(cat "$stderr_file")" + rm -f "$stderr_file" fi - if project_intake_is_auth_failure "$output"; then + diagnostic_output="$output" + if [[ -n "$error_output" ]]; then + [[ -z "$diagnostic_output" ]] || diagnostic_output+=$'\n' + diagnostic_output+="$error_output" + fi + + if project_intake_is_auth_failure "$diagnostic_output"; then echo "::error::GitHub authentication failed during Project Intake: $operation" >&2 echo "::error::Rotate BASE_PROJECT_TOKEN and rerun this workflow_dispatch for issue #${issue_number:-unknown}." >&2 echo "::error::Fix: gh auth token | gh secret set BASE_PROJECT_TOKEN --repo $GITHUB_REPOSITORY" >&2 - printf '%s\n' "$output" >&2 + printf '%s\n' "$diagnostic_output" >&2 return "$status" fi - if project_intake_is_retryable_api_failure "$output"; then - retry_delay="$(project_intake_retry_delay_seconds "$output")" + if project_intake_is_retryable_api_failure "$diagnostic_output"; then + retry_delay="$(project_intake_retry_delay_seconds "$diagnostic_output")" echo "::warning::GitHub API pressure during Project Intake: $operation" >&2 echo "::warning::Waiting ${retry_delay}s and retrying once." >&2 sleep "$retry_delay" - if output="$("$@" 2>&1)"; then + stderr_file="$(mktemp)" + if output="$("$@" 2>"$stderr_file")"; then + error_output="$(cat "$stderr_file")" + rm -f "$stderr_file" + [[ -z "$error_output" ]] || printf '%s\n' "$error_output" >&2 [[ -z "$output" ]] || printf '%s\n' "$output" return 0 else status=$? + error_output="$(cat "$stderr_file")" + rm -f "$stderr_file" + fi + diagnostic_output="$output" + if [[ -n "$error_output" ]]; then + [[ -z "$diagnostic_output" ]] || diagnostic_output+=$'\n' + diagnostic_output+="$error_output" fi echo "::error::GitHub API retry failed during Project Intake: $operation" >&2 - printf '%s\n' "$output" >&2 + printf '%s\n' "$diagnostic_output" >&2 return "$status" fi echo "::error::GitHub API command failed during Project Intake: $operation" >&2 - printf '%s\n' "$output" >&2 + printf '%s\n' "$diagnostic_output" >&2 return "$status" } diff --git a/tests/test_github_workflows.py b/tests/test_github_workflows.py index 503bf50..58eff18 100644 --- a/tests/test_github_workflows.py +++ b/tests/test_github_workflows.py @@ -99,6 +99,10 @@ def write_project_intake_mocks(tmp_path: Path) -> Path: exit 1 fi + if [[ "${PROJECT_INTAKE_WARN_ON_SUCCESS:-}" == "1" ]]; then + printf 'warning: gh emitted a non-fatal notice\\n' >&2 + fi + printf '{"state":"OPEN","url":"https://github.com/basefoundry/base/issues/1311"}\\n' ;; "project list") @@ -319,6 +323,15 @@ def test_project_intake_retries_rate_limited_operations_once(tmp_path: Path) -> assert "Synced issue #1311 into Project base." in result.stdout +def test_project_intake_keeps_success_stderr_out_of_json_stdout(tmp_path: Path) -> None: + result = run_project_intake_script(tmp_path, PROJECT_INTAKE_WARN_ON_SUCCESS="1") + + assert result.returncode == 0, result.stderr + assert "warning: gh emitted a non-fatal notice" in result.stderr + assert "warning: gh emitted a non-fatal notice" not in result.stdout + assert "Synced issue #1311 into Project base." in result.stdout + + def test_project_intake_auth_failures_do_not_retry(tmp_path: Path) -> None: result = run_project_intake_script(tmp_path, PROJECT_INTAKE_AUTH_FAIL="1")