Skip to content

fix(ci): persist fullsend version and validate before use - #90

Merged
ralphbean merged 3 commits into
mainfrom
ci/resilient-openshell-setup
Jul 10, 2026
Merged

fix(ci): persist fullsend version and validate before use#90
ralphbean merged 3 commits into
mainfrom
ci/resilient-openshell-setup

Conversation

@ralphbean

Copy link
Copy Markdown
Member

Summary

  • Persist FULLSEND_VERSION to $GITHUB_ENV after the "Install fullsend" step resolves it, so downstream steps reuse the same value instead of each making independent gh release view API calls
  • Validate FULLSEND_VERSION, OPENSHELL_VERSION, and OPENSHELL_SHA are non-empty before proceeding — fail fast with a clear error instead of silently producing an invalid Docker image reference (supervisor:) that crash-loops the gateway

Context

PR #89's functional-tests job failed because gh release view returned an HTML error page during the "Configure OpenShell gateway" step. The version resolved to empty, producing ghcr.io/nvidia/openshell/supervisor: which Docker rejected. The gateway crash-looped for 30s until the job timed out. Meanwhile, the separate "Install OpenShell CLI" step's own gh release view call succeeded — so the CLI installed fine but the gateway config was already broken.

Test plan

  • actionlint passes
  • CI passes on this PR (functional-tests should work as before when the API is healthy)

🤖 Generated with Claude Code

@ralphbean
ralphbean requested a review from a team as a code owner July 9, 2026 20:02
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Fix CI by persisting Fullsend version and validating resolved tags

🐞 Bug fix ⚙️ Configuration changes 🕐 10-20 Minutes

Grey Divider

AI Description

• Persist resolved FULLSEND_VERSION to GITHUB_ENV so all steps reuse one value.
• Fail fast if FULLSEND_VERSION / OPENSHELL_VERSION / OPENSHELL_SHA resolve empty.
• Remove redundant per-step gh release view fallbacks to reduce API flakiness impact.
Diagram

graph TD
  A["Install fullsend"] --> B["Resolve FULLSEND_VERSION"] --> C["Persist to $GITHUB_ENV"] --> D["Fetch openshell-version.sh"] --> E["Validate OPENSHELL_VERSION/SHA"] --> F["Configure gateway + install CLI"]
  B --> G{{"GitHub API (gh)"}}
  D --> G
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use step outputs instead of $GITHUB_ENV
  • ➕ More explicit data flow between steps
  • ➕ Avoids accidental env overrides in later steps
  • ➖ More YAML wiring (ids + outputs) for multiple downstream consumers
  • ➖ Still requires validation and failure handling
2. Add retry/backoff around gh API calls
  • ➕ Better resilience to transient GitHub API/edge failures
  • ➕ Can reduce false-negative CI failures without changing semantics
  • ➖ Longer CI runtime when the API is unhealthy
  • ➖ Needs careful timeouts to avoid masking real failures
3. Pin versions (no 'latest' resolution)
  • ➕ Eliminates GitHub API dependency during CI runs
  • ➕ Maximizes reproducibility
  • ➖ Requires manual bumps when new releases are desired
  • ➖ Less flexible for testing newest releases

Recommendation: The chosen approach (resolve once, persist, and validate) is the best low-friction fix for eliminating inconsistent version resolution across steps and preventing invalid image refs. If flakiness persists, the next incremental improvement would be adding a small retry/backoff wrapper around the remaining gh calls.

Files changed (1) +12 / -5

Bug fix (1) +12 / -5
functional-tests.ymlPersist FULLSEND_VERSION and validate resolved OpenShell metadata +12/-5

Persist FULLSEND_VERSION and validate resolved OpenShell metadata

• Writes FULLSEND_VERSION to $GITHUB_ENV after resolution and fails early if it is empty. Removes redundant per-step gh release view fallbacks and adds validation that OPENSHELL_VERSION and OPENSHELL_SHA were successfully extracted before exporting them.

.github/workflows/functional-tests.yml

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 9, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 8:03 PM UTC · Completed 8:10 PM UTC
Commit: 2bb9473 · View workflow run →

@qodo-code-review

qodo-code-review Bot commented Jul 9, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 55 rules
✅ Skills: 4 invoked
  code-review
  code-implementation
  pr-review
  docs-review

Grey Divider


Remediation recommended

1. Protected workflow file modified ✓ Resolved 📜 Skill insight § Compliance
Description
This PR modifies a protected governance/infrastructure path
(.github/workflows/functional-tests.yml), which requires explicit human review and must not be
auto-approved. Ensure the required approval process is followed for workflow changes.
Code

.github/workflows/functional-tests.yml[R161-165]

+          if [ -z "$FULLSEND_VERSION" ]; then
+            echo "::error::Failed to resolve fullsend version"
+            exit 1
+          fi
+          echo "FULLSEND_VERSION=${FULLSEND_VERSION}" >> "$GITHUB_ENV"
Relevance

⭐⭐ Medium

Similar “protected-path change requires human review” suggestion exists but outcome undetermined (PR
#29/#59).

PR-#29
PR-#59

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 1538392 requires raising a finding whenever protected governance/infrastructure
paths (including .github/workflows/) are modified. The diff shows changes to
.github/workflows/functional-tests.yml.

.github/workflows/functional-tests.yml[161-165]
Skill: pr-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A file under a protected path (`.github/workflows/`) was modified, which requires explicit human review/approval per governance rules.

## Issue Context
The PR description provides context, but there is no explicit linked authorization artifact shown in the change itself (e.g., issue/ADR link) for protected-path modifications.

## Fix Focus Areas
- .github/workflows/functional-tests.yml[161-165]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

2. ::error:: interpolates FULLSEND_VERSION ✓ Resolved 📜 Skill insight ⛨ Security
Description
The workflow emits a GitHub Actions workflow command (::error::) that interpolates
${FULLSEND_VERSION} without sanitizing it, enabling workflow-command injection via ::, newlines,
or control characters. This violates the requirement to sanitize every interpolated value in
workflow commands individually.
Code

.github/workflows/functional-tests.yml[R182-184]

+          if [ -z "$OPENSHELL_VERSION" ] || [ -z "$OPENSHELL_SHA" ]; then
+            echo "::error::Failed to resolve OpenShell version from fullsend tag ${FULLSEND_VERSION}"
+            exit 1
Relevance

⭐ Low

Team previously rejected sanitizing GitHub Actions workflow commands interpolation (rejected
suggestion on ::debug:: in PR #37).

PR-#37
PR-#29

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 1538382 requires sanitizing all variables interpolated into GitHub Actions workflow
commands. The added echo "::error::...${FULLSEND_VERSION}" interpolates ${FULLSEND_VERSION} with
no sanitization, creating a command-injection vector.

.github/workflows/functional-tests.yml[182-184]
Skill: pr-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A `::error::` workflow command includes an unsanitized interpolation of `${FULLSEND_VERSION}`. GitHub Actions workflow commands require sanitizing each interpolated value to prevent injection via `::`, `%0A/%0D`, ANSI escapes, or control characters.

## Issue Context
The message `::error::Failed to resolve OpenShell version from fullsend tag ${FULLSEND_VERSION}` directly embeds `${FULLSEND_VERSION}`.

## Fix Focus Areas
- .github/workflows/functional-tests.yml[182-184]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread .github/workflows/functional-tests.yml Outdated
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review

Verdict: comment — one medium finding on commit format (unchanged from prior review); no code-level blockers. The code is identical to the prior review (945f946); the branch was rebased only.

This PR correctly fixes a CI resilience gap where independent gh release view calls in downstream steps could fail transiently, producing empty version strings and invalid Docker image references. The fix persists FULLSEND_VERSION to $GITHUB_ENV after resolution and adds fail-fast validation for FULLSEND_VERSION, OPENSHELL_VERSION, and OPENSHELL_SHA.

What changed since the prior review

The branch was rebased onto the latest main (945f9464bf27d1). No code changes. All three commits retain the same content — only the committer timestamps and parent chain changed. All prior findings remain as-is.

Correctness

The change is logically sound:

  • Variable propagation — writing FULLSEND_VERSION to $GITHUB_ENV via heredoc delimiter syntax in the "Install fullsend" step correctly makes it available to all subsequent steps. All three affected steps share the same if guard (steps.changes.outputs.relevant != 'false'), ensuring downstream steps never run without the install step.
  • Empty-check placement — the FULLSEND_VERSION guard fires after the gh release view fallback, catching both empty output and command failure. The OPENSHELL_VERSION/OPENSHELL_SHA guards fire after eval and before GITHUB_ENV writes.
  • FULLSEND_TAG removal — repository-wide search confirms the variable appeared only in this file, and all occurrences are removed. No stale references remain.
  • Pre-set version path — when FULLSEND_VERSION is pre-set to a non-empty, non-"latest" value, the flow correctly preserves it and persists it to GITHUB_ENV.

Security

No critical, high, or medium security findings. The workflow's security model (pull_request_target with separate gate job, permissions: {} at top level, persist-credentials: false) is unchanged. The sanitization pipeline for the ::error:: workflow command covers literal newlines (tr -d '\n\r'), URL-encoded newlines (%0[aAdD]), and workflow command delimiters (::).

Intent & Scope

Every modified line traces to the stated goal of persisting the version and adding validation, plus hardening changes prompted by prior reviews. The lack of a linked issue is acceptable for a reactive CI fix prompted by a specific failure (PR #89).


Findings

# Severity Category Description
1 medium commit-format PR title and all three commits use fix(ci):, a forbidden type+scope combination. Per COMMITS.md, fix(ci) is misleading because CI changes are not user-visible bug fixes. Use ci(functional-tests): ... instead. COMMITS.md instructs reviewers to flag this as a required change. This finding was raised in the prior review and has not been addressed.
2 low github-env-injection OPENSHELL_VERSION and OPENSHELL_SHA GITHUB_ENV writes still use single-line echo syntax (pre-existing, not a regression). For consistency with the new FULLSEND_VERSION heredoc pattern, consider applying the same delimiter syntax. Risk is negligible — values originate from grep-filtered content of a same-org repo script.
Previous run

Review

Verdict: comment — one medium finding on commit format (unchanged from prior review); no code-level blockers. The new commit (945f946) addresses the prior review's remaining low-severity workflow-command-injection finding.

This PR correctly fixes a CI resilience gap where independent gh release view calls in downstream steps could fail transiently, producing empty version strings and invalid Docker image references. The fix persists FULLSEND_VERSION to $GITHUB_ENV after resolution and adds fail-fast validation for FULLSEND_VERSION, OPENSHELL_VERSION, and OPENSHELL_SHA.

What changed since the prior review

The new commit (945f946) addresses the prior review's low-severity finding #2 (workflow-command-injection):

  • URL-encoded newline stripping added — the SAFE_VERSION sanitization now includes sed -e 's/%0[aAdD]//g' to strip %0A/%0D sequences before interpolation into the ::error:: workflow command. The character class [aAdD] covers all case variants without needing the i flag.

The prior review's finding #3 (single-line OPENSHELL GITHUB_ENV writes) remains a pre-existing pattern, not a regression.

Correctness

The change is logically sound:

  • Variable propagation — writing FULLSEND_VERSION to $GITHUB_ENV via heredoc delimiter syntax in the "Install fullsend" step correctly makes it available to all subsequent steps. All three affected steps share the same if guard (steps.changes.outputs.relevant != 'false'), ensuring downstream steps never run without the install step.
  • Empty-check placement — the FULLSEND_VERSION guard fires after the gh release view fallback, catching both empty output and command failure. The OPENSHELL_VERSION/OPENSHELL_SHA guards fire after eval and before GITHUB_ENV writes.
  • FULLSEND_TAG removal — repository-wide search confirms the variable appeared only in this file, and all occurrences are removed. No stale references remain.
  • Pre-set version path — when FULLSEND_VERSION is pre-set to a non-empty, non-"latest" value, the flow correctly preserves it and persists it to GITHUB_ENV.

Security

No critical, high, or medium security findings. The workflow's security model (pull_request_target with separate gate job, permissions: {} at top level, persist-credentials: false) is unchanged. The sanitization pipeline for the ::error:: workflow command now covers literal newlines (tr -d '\n\r'), URL-encoded newlines (%0[aAdD]), and workflow command delimiters (::).\n\n### Intent & Scope

Every modified line traces to the stated goal of persisting the version and adding validation, plus hardening changes prompted by the prior review. The lack of a linked issue is acceptable for a reactive CI fix prompted by a specific failure (PR #89).


Findings

# Severity Category Description
1 medium commit-format PR title and all three commits use fix(ci):, a forbidden type+scope combination. Per COMMITS.md, fix(ci) is misleading because CI changes are not user-visible bug fixes. Use ci(functional-tests): ... instead. COMMITS.md instructs reviewers to flag this as a required change. This finding was raised in the prior review and has not been addressed.
2 low github-env-injection OPENSHELL_VERSION and OPENSHELL_SHA GITHUB_ENV writes still use single-line echo syntax (pre-existing, not a regression). For consistency with the new FULLSEND_VERSION heredoc pattern, consider applying the same delimiter syntax. Risk is negligible — values originate from grep-filtered content of a same-org repo script.
Previous run

Review

Verdict: comment — one medium finding on commit format (unchanged from prior review); no code-level blockers. The new commit (043298e) addresses both low-severity findings from the prior review.

This PR correctly fixes a CI resilience gap where independent gh release view calls in downstream steps could fail transiently, producing empty version strings and invalid Docker image references. The fix persists FULLSEND_VERSION to $GITHUB_ENV after resolution and adds fail-fast validation for FULLSEND_VERSION, OPENSHELL_VERSION, and OPENSHELL_SHA.

What changed since the prior review

The new commit (043298e) addresses both low-severity findings from the prior review (SHA 2bb9473, app-verified):

  • GITHUB_ENV write hardenedFULLSEND_VERSION now uses the GitHub-recommended heredoc delimiter syntax (<<GHEOF) instead of single-line echo, preventing theoretical newline-based environment variable injection.
  • Workflow command sanitization added — the ::error:: message in "Configure OpenShell gateway" now sanitizes FULLSEND_VERSION via tr -d '\n\r' | sed 's/::/: :/g' before interpolation, mitigating workflow command injection via literal newlines and :: sequences.

Both improvements are defense-in-depth hardening. The prior review's medium finding about commit format remains unaddressed.

Correctness

The change is logically sound:

  • Variable propagation — writing FULLSEND_VERSION to $GITHUB_ENV in the "Install fullsend" step correctly makes it available to all subsequent steps in the same job. All three affected steps share the same if guard, so there is no scenario where a downstream step runs while the install step was skipped.
  • Empty-check placement — the FULLSEND_VERSION guard correctly fires after the gh release view fallback, catching both empty output and command failure. The OPENSHELL_VERSION/OPENSHELL_SHA guards are correctly placed after eval and before GITHUB_ENV writes.
  • FULLSEND_TAG removal — repository-wide search confirms the variable appeared only in this file, and all occurrences are removed. No stale references remain.
  • Pre-set version path — when FULLSEND_VERSION is pre-set to a non-empty, non-"latest" value, the flow correctly preserves it and persists it to GITHUB_ENV.

Security

No critical, high, or medium security findings. The workflow's security model (pull_request_target with separate gate job, permissions: {} at top level, persist-credentials: false) is unchanged. The new hardening measures are improvements over the prior state. Two defense-in-depth observations are noted as low findings below.

Intent & Scope

Every modified line traces to the stated goal of persisting the version and adding validation, plus the hardening changes prompted by the prior review. The lack of a linked issue is acceptable for a reactive CI fix prompted by a specific failure (PR #89).


Findings

# Severity Category Description
1 medium commit-format PR title and both commits use fix(ci):, a forbidden type+scope combination. Per COMMITS.md, fix(ci) is misleading because CI changes are not user-visible bug fixes. Use ci(functional-tests): ... instead. COMMITS.md instructs reviewers to flag this as a required change. This finding was raised in the prior review and has not been addressed.
2 low workflow-command-injection The new sanitization for the ::error:: message strips literal \n/\r and :: sequences but does not neutralize URL-encoded newlines (%0A, %0D) which the Actions runner interprets as literal newlines. Risk is very low — FULLSEND_VERSION originates from gh release view against a same-org repo, and git tags cannot contain %0A through normal git operations. For completeness: sed -e 's/%0[aAdD]//gi'.
3 low github-env-injection OPENSHELL_VERSION and OPENSHELL_SHA GITHUB_ENV writes still use single-line echo syntax (pre-existing, not a regression). For consistency with the new FULLSEND_VERSION heredoc pattern, consider applying the same delimiter syntax. Risk is negligible — values originate from grep-filtered content of a same-org repo script.
Previous run

Review

Verdict: comment — one medium finding on commit format; no code-level blockers.

This PR correctly addresses a CI resilience gap where gh release view failures in downstream steps could silently produce invalid Docker image references. The fix persists FULLSEND_VERSION to $GITHUB_ENV after resolution and adds fail-fast validation for all three critical variables (FULLSEND_VERSION, OPENSHELL_VERSION, OPENSHELL_SHA).

Correctness

The change is logically sound:

  • Variable propagation works correctly — $GITHUB_ENV writes in the "Install fullsend" step make FULLSEND_VERSION available to all subsequent steps in the same job. All three affected steps share the same if guard (steps.changes.outputs.relevant != 'false'), so there is no scenario where a downstream step runs while "Install fullsend" was skipped.
  • Empty-check placement is correct — the guard only fires when the initial value was "latest" (or unset) and gh release view returned empty, which is exactly the failure case from PR ci(e2e-auth): trust fullsend-ai-coder[bot] for functional tests #89. Pre-set non-empty values pass through as intended.
  • FULLSEND_TAG removal is clean — repository-wide search confirms the variable only appeared in this file, and all three occurrences are removed by this diff. No stale references remain.

Security

No high or medium security findings. The workflow's security model (pull_request_target with separate gate job, permissions: {} at top level, persist-credentials: false) is unchanged. Two defense-in-depth observations on pre-existing patterns are noted as low findings below.

Intent & Scope

The change is appropriately scoped — every modified line traces to the stated goal of persisting the version and adding validation. No scope creep. The lack of a linked issue is acceptable for a reactive CI fix prompted by a specific failure (PR #89).

Style & Conventions

The new ::error:: messages, [ -z ] guards, and exit 1 patterns are consistent with existing idioms in the workflow (e.g., the Podman socket check at line 226).

Documentation

No documentation staleness found. FULLSEND_TAG was never referenced in documentation files.


Findings

# Severity Category Description
1 medium commit-format PR title fix(ci): ... uses a forbidden type+scope combination. Per COMMITS.md, fix(ci) is misleading because CI changes are not user-visible bug fixes. Use ci(functional-tests): persist fullsend version and validate before use instead. COMMITS.md instructs reviewers to flag this as a required change.
2 low workflow-command-injection ${FULLSEND_VERSION} is interpolated into ::error:: workflow commands (lines 162, 183). If the value contained %0A followed by another workflow command, it could inject additional commands. Risk is very low — the value originates from gh release view against a same-org repo, and git tag names cannot contain newlines. Not a regression (follows pre-existing patterns). For defense-in-depth, sanitize before interpolation or use $GITHUB_STEP_SUMMARY.
3 low github-env-injection echo "FULLSEND_VERSION=${FULLSEND_VERSION}" >> "$GITHUB_ENV" (line 163) uses the single-line syntax. If the value contained newlines, it could inject additional env vars. Risk is negligible (git tags cannot contain newlines) and the pattern matches pre-existing OPENSHELL_VERSION/OPENSHELL_SHA writes. For defense-in-depth, the heredoc delimiter syntax is the GitHub-recommended safe pattern.
Previous run

Review

Verdict: comment — one medium finding on commit format (unchanged from prior review); no code-level blockers. The new commit (945f946) addresses the prior review's remaining low-severity workflow-command-injection finding.

This PR correctly fixes a CI resilience gap where independent gh release view calls in downstream steps could fail transiently, producing empty version strings and invalid Docker image references. The fix persists FULLSEND_VERSION to $GITHUB_ENV after resolution and adds fail-fast validation for FULLSEND_VERSION, OPENSHELL_VERSION, and OPENSHELL_SHA.

What changed since the prior review

The new commit (945f946) addresses the prior review's low-severity finding #2 (workflow-command-injection):

  • URL-encoded newline stripping added — the SAFE_VERSION sanitization now includes sed -e 's/%0[aAdD]//g' to strip %0A/%0D sequences before interpolation into the ::error:: workflow command. The character class [aAdD] covers all case variants without needing the i flag.

The prior review's finding #3 (single-line OPENSHELL GITHUB_ENV writes) remains a pre-existing pattern, not a regression.

Correctness

The change is logically sound:

  • Variable propagation — writing FULLSEND_VERSION to $GITHUB_ENV via heredoc delimiter syntax in the "Install fullsend" step correctly makes it available to all subsequent steps. All three affected steps share the same if guard (steps.changes.outputs.relevant != 'false'), ensuring downstream steps never run without the install step.
  • Empty-check placement — the FULLSEND_VERSION guard fires after the gh release view fallback, catching both empty output and command failure. The OPENSHELL_VERSION/OPENSHELL_SHA guards fire after eval and before GITHUB_ENV writes.
  • FULLSEND_TAG removal — repository-wide search confirms the variable appeared only in this file, and all occurrences are removed. No stale references remain.
  • Pre-set version path — when FULLSEND_VERSION is pre-set to a non-empty, non-"latest" value, the flow correctly preserves it and persists it to GITHUB_ENV.

Security

No critical, high, or medium security findings. The workflow's security model (pull_request_target with separate gate job, permissions: {} at top level, persist-credentials: false) is unchanged. The sanitization pipeline for the ::error:: workflow command now covers literal newlines (tr -d '\n\r'), URL-encoded newlines (%0[aAdD]), and workflow command delimiters (::).

Intent & Scope

Every modified line traces to the stated goal of persisting the version and adding validation, plus hardening changes prompted by the prior review. The lack of a linked issue is acceptable for a reactive CI fix prompted by a specific failure (PR #89).


Findings

# Severity Category Description
1 medium commit-format PR title and all three commits use fix(ci):, a forbidden type+scope combination. Per COMMITS.md, fix(ci) is misleading because CI changes are not user-visible bug fixes. Use ci(functional-tests): ... instead. COMMITS.md instructs reviewers to flag this as a required change. This finding was raised in the prior review and has not been addressed.
2 low github-env-injection OPENSHELL_VERSION and OPENSHELL_SHA GITHUB_ENV writes still use single-line echo syntax (pre-existing, not a regression). For consistency with the new FULLSEND_VERSION heredoc pattern, consider applying the same delimiter syntax. Risk is negligible — values originate from grep-filtered content of a same-org repo script.
Previous run (2)

Review

Verdict: comment — one medium finding on commit format (unchanged from prior review); no code-level blockers. The new commit (043298e) addresses both low-severity findings from the prior review.

This PR correctly fixes a CI resilience gap where independent gh release view calls in downstream steps could fail transiently, producing empty version strings and invalid Docker image references. The fix persists FULLSEND_VERSION to $GITHUB_ENV after resolution and adds fail-fast validation for FULLSEND_VERSION, OPENSHELL_VERSION, and OPENSHELL_SHA.

What changed since the prior review

The new commit (043298e) addresses both low-severity findings from the prior review (SHA 2bb9473, app-verified):

  • GITHUB_ENV write hardenedFULLSEND_VERSION now uses the GitHub-recommended heredoc delimiter syntax (<<GHEOF) instead of single-line echo, preventing theoretical newline-based environment variable injection.
  • Workflow command sanitization added — the ::error:: message in "Configure OpenShell gateway" now sanitizes FULLSEND_VERSION via tr -d '\n\r' | sed 's/::/: :/g' before interpolation, mitigating workflow command injection via literal newlines and :: sequences.

Both improvements are defense-in-depth hardening. The prior review's medium finding about commit format remains unaddressed.

Correctness

The change is logically sound:

  • Variable propagation — writing FULLSEND_VERSION to $GITHUB_ENV in the "Install fullsend" step correctly makes it available to all subsequent steps in the same job. All three affected steps share the same if guard, so there is no scenario where a downstream step runs while the install step was skipped.
  • Empty-check placement — the FULLSEND_VERSION guard correctly fires after the gh release view fallback, catching both empty output and command failure. The OPENSHELL_VERSION/OPENSHELL_SHA guards are correctly placed after eval and before GITHUB_ENV writes.
  • FULLSEND_TAG removal — repository-wide search confirms the variable appeared only in this file, and all occurrences are removed. No stale references remain.
  • Pre-set version path — when FULLSEND_VERSION is pre-set to a non-empty, non-"latest" value, the flow correctly preserves it and persists it to GITHUB_ENV.

Security

No critical, high, or medium security findings. The workflow's security model (pull_request_target with separate gate job, permissions: {} at top level, persist-credentials: false) is unchanged. The new hardening measures are improvements over the prior state. Two defense-in-depth observations are noted as low findings below.

Intent & Scope

Every modified line traces to the stated goal of persisting the version and adding validation, plus the hardening changes prompted by the prior review. The lack of a linked issue is acceptable for a reactive CI fix prompted by a specific failure (PR #89).


Findings

# Severity Category Description
1 medium commit-format PR title and both commits use fix(ci):, a forbidden type+scope combination. Per COMMITS.md, fix(ci) is misleading because CI changes are not user-visible bug fixes. Use ci(functional-tests): ... instead. COMMITS.md instructs reviewers to flag this as a required change. This finding was raised in the prior review and has not been addressed.
2 low workflow-command-injection The new sanitization for the ::error:: message strips literal \n/\r and :: sequences but does not neutralize URL-encoded newlines (%0A, %0D) which the Actions runner interprets as literal newlines. Risk is very low — FULLSEND_VERSION originates from gh release view against a same-org repo, and git tags cannot contain %0A through normal git operations. For completeness: sed -e 's/%0[aAdD]//gi'.
3 low github-env-injection OPENSHELL_VERSION and OPENSHELL_SHA GITHUB_ENV writes still use single-line echo syntax (pre-existing, not a regression). For consistency with the new FULLSEND_VERSION heredoc pattern, consider applying the same delimiter syntax. Risk is negligible — values originate from grep-filtered content of a same-org repo script.
Previous run

Review

Verdict: comment — one medium finding on commit format; no code-level blockers.

This PR correctly addresses a CI resilience gap where gh release view failures in downstream steps could silently produce invalid Docker image references. The fix persists FULLSEND_VERSION to $GITHUB_ENV after resolution and adds fail-fast validation for all three critical variables (FULLSEND_VERSION, OPENSHELL_VERSION, OPENSHELL_SHA).

Correctness

The change is logically sound:

  • Variable propagation works correctly — $GITHUB_ENV writes in the "Install fullsend" step make FULLSEND_VERSION available to all subsequent steps in the same job. All three affected steps share the same if guard (steps.changes.outputs.relevant != 'false'), so there is no scenario where a downstream step runs while "Install fullsend" was skipped.
  • Empty-check placement is correct — the guard only fires when the initial value was "latest" (or unset) and gh release view returned empty, which is exactly the failure case from PR ci(e2e-auth): trust fullsend-ai-coder[bot] for functional tests #89. Pre-set non-empty values pass through as intended.
  • FULLSEND_TAG removal is clean — repository-wide search confirms the variable only appeared in this file, and all three occurrences are removed by this diff. No stale references remain.

Security

No high or medium security findings. The workflow's security model (pull_request_target with separate gate job, permissions: {} at top level, persist-credentials: false) is unchanged. Two defense-in-depth observations on pre-existing patterns are noted as low findings below.

Intent & Scope

The change is appropriately scoped — every modified line traces to the stated goal of persisting the version and adding validation. No scope creep. The lack of a linked issue is acceptable for a reactive CI fix prompted by a specific failure (PR #89).

Style & Conventions

The new ::error:: messages, [ -z ] guards, and exit 1 patterns are consistent with existing idioms in the workflow (e.g., the Podman socket check at line 226).

Documentation

No documentation staleness found. FULLSEND_TAG was never referenced in documentation files.


Findings

# Severity Category Description
1 medium commit-format PR title fix(ci): ... uses a forbidden type+scope combination. Per COMMITS.md, fix(ci) is misleading because CI changes are not user-visible bug fixes. Use ci(functional-tests): persist fullsend version and validate before use instead. COMMITS.md instructs reviewers to flag this as a required change.
2 low workflow-command-injection ${FULLSEND_VERSION} is interpolated into ::error:: workflow commands (lines 162, 183). If the value contained %0A followed by another workflow command, it could inject additional commands. Risk is very low — the value originates from gh release view against a same-org repo, and git tag names cannot contain newlines. Not a regression (follows pre-existing patterns). For defense-in-depth, sanitize before interpolation or use $GITHUB_STEP_SUMMARY.
3 low github-env-injection echo "FULLSEND_VERSION=${FULLSEND_VERSION}" >> "$GITHUB_ENV" (line 163) uses the single-line syntax. If the value contained newlines, it could inject additional env vars. Risk is negligible (git tags cannot contain newlines) and the pattern matches pre-existing OPENSHELL_VERSION/OPENSHELL_SHA writes. For defense-in-depth, the heredoc delimiter syntax is the GitHub-recommended safe pattern.
Previous run (3)

Review

Verdict: comment — one medium finding on commit format (unchanged from prior review); no code-level blockers. The new commit (043298e) addresses both low-severity findings from the prior review.

This PR correctly fixes a CI resilience gap where independent gh release view calls in downstream steps could fail transiently, producing empty version strings and invalid Docker image references. The fix persists FULLSEND_VERSION to $GITHUB_ENV after resolution and adds fail-fast validation for FULLSEND_VERSION, OPENSHELL_VERSION, and OPENSHELL_SHA.

What changed since the prior review

The new commit (043298e) addresses both low-severity findings from the prior review (SHA 2bb9473, app-verified):

  • GITHUB_ENV write hardenedFULLSEND_VERSION now uses the GitHub-recommended heredoc delimiter syntax (<<GHEOF) instead of single-line echo, preventing theoretical newline-based environment variable injection.
  • Workflow command sanitization added — the ::error:: message in "Configure OpenShell gateway" now sanitizes FULLSEND_VERSION via tr -d '\n\r' | sed 's/::/: :/g' before interpolation, mitigating workflow command injection via literal newlines and :: sequences.

Both improvements are defense-in-depth hardening. The prior review's medium finding about commit format remains unaddressed.

Correctness

The change is logically sound:

  • Variable propagation — writing FULLSEND_VERSION to $GITHUB_ENV in the "Install fullsend" step correctly makes it available to all subsequent steps in the same job. All three affected steps share the same if guard, so there is no scenario where a downstream step runs while the install step was skipped.
  • Empty-check placement — the FULLSEND_VERSION guard correctly fires after the gh release view fallback, catching both empty output and command failure. The OPENSHELL_VERSION/OPENSHELL_SHA guards are correctly placed after eval and before GITHUB_ENV writes.
  • FULLSEND_TAG removal — repository-wide search confirms the variable appeared only in this file, and all occurrences are removed. No stale references remain.
  • Pre-set version path — when FULLSEND_VERSION is pre-set to a non-empty, non-"latest" value, the flow correctly preserves it and persists it to GITHUB_ENV.

Security

No critical, high, or medium security findings. The workflow's security model (pull_request_target with separate gate job, permissions: {} at top level, persist-credentials: false) is unchanged. The new hardening measures are improvements over the prior state. Two defense-in-depth observations are noted as low findings below.

Intent & Scope

Every modified line traces to the stated goal of persisting the version and adding validation, plus the hardening changes prompted by the prior review. The lack of a linked issue is acceptable for a reactive CI fix prompted by a specific failure (PR #89).


Findings

# Severity Category Description
1 medium commit-format PR title and both commits use fix(ci):, a forbidden type+scope combination. Per COMMITS.md, fix(ci) is misleading because CI changes are not user-visible bug fixes. Use ci(functional-tests): ... instead. COMMITS.md instructs reviewers to flag this as a required change. This finding was raised in the prior review and has not been addressed.
2 low workflow-command-injection The new sanitization for the ::error:: message strips literal \n/\r and :: sequences but does not neutralize URL-encoded newlines (%0A, %0D) which the Actions runner interprets as literal newlines. Risk is very low — FULLSEND_VERSION originates from gh release view against a same-org repo, and git tags cannot contain %0A through normal git operations. For completeness: sed -e 's/%0[aAdD]//gi'.
3 low github-env-injection OPENSHELL_VERSION and OPENSHELL_SHA GITHUB_ENV writes still use single-line echo syntax (pre-existing, not a regression). For consistency with the new FULLSEND_VERSION heredoc pattern, consider applying the same delimiter syntax. Risk is negligible — values originate from grep-filtered content of a same-org repo script.
Previous run (4)

Review

Verdict: comment — one medium finding on commit format; no code-level blockers.

This PR correctly addresses a CI resilience gap where gh release view failures in downstream steps could silently produce invalid Docker image references. The fix persists FULLSEND_VERSION to $GITHUB_ENV after resolution and adds fail-fast validation for all three critical variables (FULLSEND_VERSION, OPENSHELL_VERSION, OPENSHELL_SHA).

Correctness

The change is logically sound:

  • Variable propagation works correctly — $GITHUB_ENV writes in the "Install fullsend" step make FULLSEND_VERSION available to all subsequent steps in the same job. All three affected steps share the same if guard (steps.changes.outputs.relevant != 'false'), so there is no scenario where a downstream step runs while "Install fullsend" was skipped.
  • Empty-check placement is correct — the guard only fires when the initial value was "latest" (or unset) and gh release view returned empty, which is exactly the failure case from PR ci(e2e-auth): trust fullsend-ai-coder[bot] for functional tests #89. Pre-set non-empty values pass through as intended.
  • FULLSEND_TAG removal is clean — repository-wide search confirms the variable only appeared in this file, and all three occurrences are removed by this diff. No stale references remain.

Security

No high or medium security findings. The workflow's security model (pull_request_target with separate gate job, permissions: {} at top level, persist-credentials: false) is unchanged. Two defense-in-depth observations on pre-existing patterns are noted as low findings below.

Intent & Scope

The change is appropriately scoped — every modified line traces to the stated goal of persisting the version and adding validation. No scope creep. The lack of a linked issue is acceptable for a reactive CI fix prompted by a specific failure (PR #89).

Style & Conventions

The new ::error:: messages, [ -z ] guards, and exit 1 patterns are consistent with existing idioms in the workflow (e.g., the Podman socket check at line 226).

Documentation

No documentation staleness found. FULLSEND_TAG was never referenced in documentation files.


Findings

# Severity Category Description
1 medium commit-format PR title fix(ci): ... uses a forbidden type+scope combination. Per COMMITS.md, fix(ci) is misleading because CI changes are not user-visible bug fixes. Use ci(functional-tests): persist fullsend version and validate before use instead. COMMITS.md instructs reviewers to flag this as a required change.
2 low workflow-command-injection ${FULLSEND_VERSION} is interpolated into ::error:: workflow commands (lines 162, 183). If the value contained %0A followed by another workflow command, it could inject additional commands. Risk is very low — the value originates from gh release view against a same-org repo, and git tag names cannot contain newlines. Not a regression (follows pre-existing patterns). For defense-in-depth, sanitize before interpolation or use $GITHUB_STEP_SUMMARY.
3 low github-env-injection echo "FULLSEND_VERSION=${FULLSEND_VERSION}" >> "$GITHUB_ENV" (line 163) uses the single-line syntax. If the value contained newlines, it could inject additional env vars. Risk is negligible (git tags cannot contain newlines) and the pattern matches pre-existing OPENSHELL_VERSION/OPENSHELL_SHA writes. For defense-in-depth, the heredoc delimiter syntax is the GitHub-recommended safe pattern.

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added the requires-manual-review Review requires human judgment label Jul 9, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 9, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 8:17 PM UTC · Completed 8:25 PM UTC
Commit: 043298e · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment and removed requires-manual-review Review requires human judgment labels Jul 9, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 9, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:27 PM UTC · Completed 9:40 PM UTC
Commit: 945f946 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment and removed requires-manual-review Review requires human judgment labels Jul 9, 2026
@ralphbean
ralphbean added this pull request to the merge queue Jul 10, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jul 10, 2026
@ralphbean
ralphbean added this pull request to the merge queue Jul 10, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jul 10, 2026
@rh-hemartin

Copy link
Copy Markdown
Member

See #96 for a potential fix to the merge queue problems.

The "Install fullsend" step resolved FULLSEND_VERSION as a local shell
variable but never wrote it to $GITHUB_ENV. Subsequent steps
("Configure OpenShell gateway", "Install OpenShell CLI") each made
independent gh release view calls as a fallback. When any of those
calls hit a transient GitHub API error, the version resolved to empty,
producing an invalid Docker image reference (supervisor:) that
crash-looped the gateway.

- Persist FULLSEND_VERSION to $GITHUB_ENV after resolving it once
- Validate FULLSEND_VERSION is non-empty before proceeding
- Validate OPENSHELL_VERSION and OPENSHELL_SHA are non-empty
- Remove redundant gh release view fallbacks from downstream steps

Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
Use heredoc delimiter syntax for FULLSEND_VERSION GITHUB_ENV write to
prevent theoretical newline-based env var injection. Sanitize version
string before interpolation into ::error:: workflow commands.

Signed-off-by: Ralph Bean <rbean@redhat.com>
Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
Strip %0A/%0D sequences from FULLSEND_VERSION before interpolation
into workflow ::error:: commands, as a defense-in-depth measure.

Assisted-by: Claude claude-opus-4-6 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
@ralphbean
ralphbean force-pushed the ci/resilient-openshell-setup branch from 945f946 to 4bf27d1 Compare July 10, 2026 15:13
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 10, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 3:14 PM UTC · Completed 3:21 PM UTC
Commit: 4bf27d1 · View workflow run →

@ralphbean
ralphbean added this pull request to the merge queue Jul 10, 2026

@fullsend-ai-review fullsend-ai-review Bot 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.

See the review comment for full details.

Comment thread .github/workflows/functional-tests.yml
@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment and removed requires-manual-review Review requires human judgment labels Jul 10, 2026
Merged via the queue into main with commit 8fb64a2 Jul 10, 2026
12 checks passed
@ralphbean
ralphbean deleted the ci/resilient-openshell-setup branch July 10, 2026 15:23
@fullsend-ai-retro

fullsend-ai-retro Bot commented Jul 10, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ✅ Success · Started 3:25 PM UTC · Completed 3:32 PM UTC
Commit: 4bf27d1 · View workflow run →

@fullsend-ai-retro

Copy link
Copy Markdown

PR #90 was a human-authored CI fix (3 commits by ralphbean with Claude assistance) that persisted FULLSEND_VERSION to GITHUB_ENV and added fail-fast validation in the functional-tests workflow. The fullsend-ai-review agent ran 4 times across pushes and a rebase, producing high-quality security-focused feedback that the author acted on. Three improvement opportunities emerged: (1) the agent repeatedly re-raised a pre-existing finding the author explicitly declined as out-of-scope, creating noise; (2) a tension between COMMITS.md calling commit-format violations 'required changes' and the verdict logic treating a single medium finding as non-blocking comment-only, allowing the forbidden fix(ci): prefix to be merged unaddressed; (3) the 4th review on a rebase-only push ran the full sub-agent pipeline despite detecting no code changes, wasting tokens. Additional evidence for existing open issues: #45 — this PR demonstrates the same verdict-consistency pattern; the review agent's medium finding said commit-format violations are 'required changes' per COMMITS.md but chose comment-only verdict, and the PR merged with the violation intact.

Proposals filed

ralphbean added a commit that referenced this pull request Aug 10, 2026
The trusted-decline mechanism from 5393b60 never activated: the
pulls/comments endpoint doesn't populate performed_via_github_app, so
the app-identity gate never matched and DECLINED_FINDINGS stayed
empty. Switch the gate to user.login/user.type, which the endpoint
actually returns (verified against this repo's own review comments).
Filed fullsend-ai/fullsend#6045 to expose the harness slug as a
FULLSEND_SLUG env var so this can stop hardcoding the app's identity.

Also, since exact file/line matching plus a file-level
changed-since-prior guard couldn't survive the line drift a declined
finding's location goes through across rounds (the motivating PR #90
case this feature targets), switch decline matching to file + category
+ presence of the actual declined code, dropping the round-boundary
check entirely — a decline now expires exactly when its code changes,
not when some unrelated line in the file does.

Smaller fixes bundled in:
- exclude the PR author from qualifying as a decliner of their own
  findings
- most recent qualifying reply in a thread wins, so a maintainer can
  reverse an earlier decline
- fall back to original_line/original_start_line when GitHub nulls
  line on outdated comments
- reword the author_association fallback to match what
  check-e2e-authorization.sh actually documents (defense-in-depth for
  membership-visibility edge cases, not an API bug)
- project the review-comments fetch to needed fields and cache
  collaborator-permission lookups per login

Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

requires-manual-review Review requires human judgment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants