Skip to content

chore: releases and versioning - #62

Merged
csoceanu merged 5 commits into
mainfrom
chore/release-process
Aug 17, 2026
Merged

chore: releases and versioning#62
csoceanu merged 5 commits into
mainfrom
chore/release-process

Conversation

@Benkapner

Copy link
Copy Markdown
Collaborator

Summary

Adds a release process so users can pin to stable versions instead of @main.

285 commits with zero releases means every adopter is consuming @main on an action that holds contents:write and pushes commits to their branches. This is the highest-value gap in the repo.

Changes

  • Release workflow (.github/workflows/release.yml): triggered on semver tag push (v*.*.*), runs the full test suite, creates a GitHub Release with notes extracted from the matching CHANGELOG section, and force-updates the moving major tag (v0 -> v0.x.y)
  • CHANGELOG.md: seeded with a 0.1.0 entry summarizing current capability (Keep a Changelog format)
  • pyproject.toml: version set to 0.1.0 as the single source of truth
  • RELEASING.md: rewritten to document the automated tag-push flow
  • README: workflow example pins to @v0 instead of @main; new "Versioning" section explaining @v0 vs @v0.1.0 vs @main

No runtime changes

No file under src/ is touched. Nothing about a run changes.

Test plan

  • Push v0.1.0 tag on a fork: produces a Release and a moving v0 tag
  • No @main reference remains in user-facing setup instructions
  • uv run pytest -v passes (429 tests)
  • uv run ruff check src/ tests/ and uv run ruff format --check src/ tests/ clean

Dependencies

Depends on PR #53 and PR #61 being merged first (this branch is based on #61's tip).

@Benkapner
Benkapner requested a review from csoceanu August 16, 2026 11:41
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 16, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 11:42 AM UTC · Completed 11:59 AM UTC

Commit: e52dae5 · View workflow run →

@Benkapner Benkapner self-assigned this Aug 16, 2026
@Benkapner
Benkapner force-pushed the chore/release-process branch from e52dae5 to 8f710e8 Compare August 16, 2026 11:45
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 16, 2026

Copy link
Copy Markdown

Review

Findings

High

  • [protected-path] .github/workflows/release.yml — This PR modifies files under the protected path .github/, which contains governance and infrastructure files requiring human approval. The PR has no linked issue providing explicit authorization for these changes.
    Remediation: Link an issue documenting the authorization for modifying .github/ workflow files. Human review and approval is required for all protected-path changes.

Medium

  • [file-naming-convention] .github/workflows/release.yml — Workflow file uses .yml extension, but all existing workflow files in this repository use .yaml extension (ci.yaml, fullsend.yaml). This breaks the established naming convention.
    Remediation: Rename to .github/workflows/release.yaml to match the codebase convention.

Low

  • [edge-case] .github/workflows/release.yml:47 — The static heredoc delimiter CHANGELOG_EOF used for multiline GITHUB_OUTPUT could be prematurely terminated if CHANGELOG.md contains a line that is exactly CHANGELOG_EOF. While CHANGELOG.md is maintainer-controlled content (limiting risk), GitHub's recommended practice is to use a random delimiter for multiline outputs.
    Remediation: Use a dynamic delimiter: delimiter=$(uuidgen) or delimiter=$(openssl rand -hex 16).

  • [workflow-duplication] .github/workflows/release.yml:12 — The release workflow's test job duplicates the exact same steps as ci.yaml (checkout, setup-uv, uv python install, uv sync, ruff check, ruff format, pytest with coverage). If test commands change, both workflows must be updated independently.
    Remediation: Consider creating a reusable workflow that both ci.yaml and release.yml call, or rely on branch protection requiring CI to pass before tagging.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run

Review

Findings

High

  • [injection] .github/workflows/release.yml:54${{ steps.changelog.outputs.body }} is interpolated directly into a run: shell block via --notes "${{ steps.changelog.outputs.body }}". The changelog content is sourced from CHANGELOG.md, which can be modified by any contributor with merge access. A crafted entry containing backticks, $(...), or other shell expansion syntax would execute arbitrary commands during the release workflow.
    Remediation: Pass the changelog body via an environment variable — add RELEASE_BODY: ${{ steps.changelog.outputs.body }} to the env: block and use --notes "$RELEASE_BODY" in the shell command.

  • [protected-path] .github/workflows/release.yml — This PR modifies a file under the protected path .github/. The PR has no linked issue to authorize changes to governance and infrastructure files. Human approval is required for protected-path changes.

Medium

  • [injection] .github/workflows/release.yml:52${{ steps.version.outputs.tag }} is interpolated directly into the gh release create command and --title argument in the run: block. A crafted tag name matching v*.*.* (e.g., v1.0.0-$(whoami)) would execute shell metacharacters. See also: [injection] finding at line 58.
    Remediation: Pass the tag via an environment variable — add RELEASE_TAG: ${{ steps.version.outputs.tag }} to env: and use "$RELEASE_TAG" in the shell command.

  • [injection] .github/workflows/release.yml:58 — Same shell injection pattern in the "Update major version tag" step. ${{ steps.version.outputs.tag }} is interpolated directly into the run: block at the echo command.
    Remediation: Use environment variable indirection as described above.

  • [stale-reference] demo/index.html:307 — The demo workflow example still references redhat-community-ai-tools/code-to-docs@main, which contradicts the new versioning guidance. The README now recommends @v0 and labels @main as unstable.
    Remediation: Update to use @v0.

  • [stale-reference] demo/review-feature.html:392 — Same issue: demo workflow example still uses @main.
    Remediation: Update to use @v0.

  • [file-naming] .github/workflows/release.yml — Workflow file uses .yml extension, but existing workflows in this repository use .yaml (ci.yaml, fullsend.yaml). Inconsistent file naming convention.
    Remediation: Rename to .github/workflows/release.yaml.

Low

  • [injection] .github/workflows/release.yml:39 — In the "Extract changelog section" step, the version string is used inside an awk regex pattern without escaping. Dots in semver versions (e.g., 0.1.0) act as single-character wildcards in the regex, potentially matching unintended changelog sections.
    Remediation: Use environment variable indirection and escape regex metacharacters, or use fixed-string matching.

  • [scope-alignment] pyproject.toml — The version bump from 0.0.0 to 0.1.0 is for a Docker-based GitHub Action, not a PyPI package. While RELEASING.md documents keeping it in sync with git tags, the relationship between pyproject.toml version and the git tag version could be clarified.
    Remediation: Add a brief note clarifying this is informational and kept in sync with git tags.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (2)

Review

Findings

High

  • [protected-path] .github/workflows/release.yml — PR modifies files under the protected .github/ path. No linked issue provides authorization for this change. Human approval is required for all protected-path changes regardless of context.
    Remediation: Link an issue authorizing this infrastructure change, or obtain explicit maintainer approval for the .github/ path modification.

Medium

  • [GitHub Actions script injection] .github/workflows/release.yml:51 — The gh release create step interpolates steps.version.outputs.tag directly into shell arguments via ${{ }} expression syntax (lines 51–52 and 57). The Actions runner expands ${{ }} before bash parses the command, so a tag containing shell metacharacters would be interpreted as code. While only users with push access can create tags, defense-in-depth requires passing values through environment variables. The same pattern appears in the "Update major version tag" step (line 57).
    Remediation: Use env: TAG: ${{ steps.version.outputs.tag }} and reference as "$TAG" in the run script. Apply the same pattern to all steps that interpolate step outputs.

  • [GitHub Actions script injection] .github/workflows/release.yml:53 — The --notes "${{ steps.changelog.outputs.body }}" expression interpolates CHANGELOG.md content directly into a shell command. Markdown commonly contains backticks, $(...), and other shell metacharacters that will break parsing or execute unintended commands.
    Remediation: Use env: NOTES: ${{ steps.changelog.outputs.body }} and reference as --notes "$NOTES".

  • [stale-reference] demo/index.html:307 — The demo page still references redhat-community-ai-tools/code-to-docs@main in its workflow example. The PR updates the README from @main to @v0 but does not update the demo files. Users following the demo will pin to the unstable @main ref.

  • [stale-reference] demo/review-feature.html:392 — Same issue — this demo page also references @main instead of @v0.

Low

  • [regex-correctness] .github/workflows/release.yml:40 — The awk command interpolates the version string into a regex without escaping dots (. matches any character in awk regex). Unlikely to cause problems with standard semver headings, but technically incorrect.

Labels: PR adds a GitHub Actions release workflow and modifies documentation files (README, RELEASING.md, CHANGELOG.md)


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (3)

Review

Reason: stale-head

The review agent reviewed commit e52dae5409c68d8f82d3b8345d149fb639e028b0 but the PR HEAD is now 8f710e8f7d87e70ddc5ba153a403a114ac757f12. This review was discarded to avoid approving unreviewed code.

@fullsend-ai-review

Copy link
Copy Markdown

/fs-review

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 16, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 12:01 PM UTC · Completed 12:16 PM UTC

Commit: 8f710e8 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added github_actions Pull requests that update GitHub Actions code documentation Improvements or additions to documentation labels Aug 16, 2026
Benkapner and others added 3 commits August 16, 2026 17:22
Phase 1: Releases and versioning.

285 commits with zero releases means users must consume @main, which is
an unbounded trust grant for an action with contents:write. Add a release
workflow triggered on semver tag push that runs the full test suite,
creates a GitHub Release with notes extracted from CHANGELOG.md, and
force-updates the moving major tag (v0 -> v0.x.y). Seed CHANGELOG.md
with a 0.1.0 entry summarising current capability. Set version in
pyproject.toml as the single source of truth. Rewrite RELEASING.md to
document the automated tag-push flow.
Phase 1: Releases and versioning.

uses: code-to-docs@main on an action that pushes commits to user
branches is an unbounded trust grant. Pin the README workflow example
to @v0 (the moving major tag from the release workflow) and add a
Versioning section explaining the pinning options.
PR #53 (post-generation validation) is not yet merged, so it should
not be listed as a v0.1.0 feature. Will be added to the changelog
when the PR is merged.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@csoceanu
csoceanu force-pushed the chore/release-process branch from 8f710e8 to f1f029e Compare August 16, 2026 14:23
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 16, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:24 PM UTC · Completed 2:39 PM UTC

Commit: f1f029e · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

Use env: variables instead of ${{ }} interpolation in shell commands
to prevent script injection from tag names or changelog content
containing shell metacharacters. Also escape dots in awk regex for
version matching.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 16, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:43 PM UTC · Completed 2:58 PM UTC

Commit: b39241a · View workflow run →

@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.

section="Release ${version}"
fi
# Use a delimiter for multiline output
echo "body<<CHANGELOG_EOF" >> "$GITHUB_OUTPUT"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] edge-case

The static heredoc delimiter CHANGELOG_EOF used for multiline GITHUB_OUTPUT could be prematurely terminated if CHANGELOG.md contains a line that is exactly CHANGELOG_EOF. While CHANGELOG.md is maintainer-controlled content (limiting risk), GitHub's recommended practice is to use a random delimiter for multiline outputs.

Suggested fix: Use a dynamic delimiter: delimiter=$(uuidgen) or delimiter=$(openssl rand -hex 16).

contents: write

jobs:
test:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] workflow-duplication

The release workflow's test job duplicates the exact same steps as ci.yaml. If test commands change, both workflows must be updated independently.

Suggested fix: Consider creating a reusable workflow that both ci.yaml and release.yml call, or rely on branch protection requiring CI to pass before tagging.

All existing workflow files use .yaml extension (ci.yaml, fullsend.yaml).
Updates the reference in RELEASING.md as well.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 17, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 7:24 AM UTC · Completed 7:39 AM UTC

Commit: 60d7b81 · View workflow run →

@csoceanu
csoceanu merged commit 7f1dc52 into main Aug 17, 2026
13 of 15 checks passed
@fullsend-ai-review

Copy link
Copy Markdown

Review skipped — this PR is already merged.

The /fs-review command only reviews open pull requests.

Posted by fullsend post-review check

@fullsend-ai-retro

fullsend-ai-retro Bot commented Aug 17, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ✅ Success · Started 7:40 AM UTC · Completed 7:51 AM UTC

Commit: 60d7b81 · View workflow run →

@fullsend-ai-retro

Copy link
Copy Markdown

Retro: PR #62 — chore: releases and versioning

Overview

PR #62 was a human-authored PR by Benkapner that added release infrastructure: a GitHub Actions release workflow, CHANGELOG.md, version pinning in README, and documentation updates. The review agent ran 5 times across ~20 hours, catching real security issues that the human reviewer (csoceanu) fixed. The workflow performed well overall.

Timeline

  1. Aug 16, 11:40 — PR opened with release workflow, CHANGELOG, and README changes (5 files, +142/-45)
  2. Aug 16, 11:42–11:59 — Review Run 1 on e52dae5: discarded due to stale HEAD (new commit pushed during review). Correctly self-discarded and triggered redispatch.
  3. Aug 16, 12:01–12:16 — Review Run 2 on 8f710e8: CHANGES_REQUESTED. Flagged script injection (medium), stale @main refs in demo files (medium), awk regex correctness (low), and protected-path policy (high).
  4. Aug 16, 14:23 — csoceanu committed f1f029e: removed unmerged PR fix(#52): add post-generation validation and LLM verification #53 feature from changelog (human-only finding the agent missed).
  5. Aug 16, 14:24–14:39 — Review Run 3 on f1f029e: CHANGES_REQUESTED. Re-flagged all findings; escalated changelog injection from medium to high severity.
  6. Aug 16, 14:42 — csoceanu committed b39241a: fixed all script injection via env var indirection + escaped awk regex dots.
  7. Aug 16, 14:43–14:58 — Review Run 4 on b39241a: CHANGES_REQUESTED. Correctly dropped 4 fixed findings. Added new findings: file-naming convention (medium), static heredoc delimiter (low), workflow duplication (low). Silently dropped the stale @main demo file findings (medium) despite them being unfixed.
  8. Aug 17, 07:22 — csoceanu committed 60d7b81: renamed .yml to .yaml for consistency.
  9. Aug 17, 07:24–07:39 — Review Run 5 on 60d7b81: correctly skipped (PR merged at 07:29 during this run).
  10. Aug 17, 07:29 — csoceanu approved (no comments) and merged.

What went well

  • High-value security findings: The agent caught 3 instances of GitHub Actions script injection via ${{ }} interpolation — a real security concern that the human author missed. All were fixed.
  • Zero false positives: All 9 inline review comments across 4 runs were technically valid and actionable.
  • Fix detection worked perfectly: 4 out of 4 fixed issues were correctly recognized as resolved in the subsequent review — no false re-flags.
  • Severity calibration improved over runs: The changelog injection was correctly escalated from medium to high in Run 3 as the agent provided stronger justification.
  • Graceful edge-case handling: Stale-head detection (Run 1) and merged-PR skip (Run 5) both worked correctly.

Evidence for existing open issues

  • fullsend#4956 / fullsend#1044 / agents#685 (finding persistence): The [stale-reference] findings for demo/index.html:307 and demo/review-feature.html:392 were flagged at medium severity in Runs 2 and 3, then silently dropped in Run 4 without being fixed. The demo files still contain @main references today. The drop correlates with other findings being fixed in the same commit — suggesting the agent narrowed scope to changed files and lost track of findings about unchanged files.
  • fullsend#1551 (protected-path severity for human PRs): The [protected-path] finding was flagged at high severity for modifying .github/workflows/, but this was a human-authored PR where the author intentionally modified CI workflows. The human approved and merged despite this finding.
  • fullsend#1352 (non-diff file findings): The stale @main references in demo files are findings about files not in the PR diff. They were initially flagged (correctly) but lost on subsequent re-review, illustrating that non-diff file findings are particularly vulnerable to the finding-persistence gap.

Autonomy assessment

The review agent demonstrated strong competence on this PR: zero false positives, real security findings caught, correct fix detection, appropriate severity calibration. The human reviewer agreed with all high-severity findings and acted on them. The only human-exclusive finding (changelog referencing unmerged PR #53) required project-state awareness beyond the review agent's current scope. The remaining unfixed low-severity findings (heredoc delimiter, workflow duplication) were reasonable accepts. This PR supports the case for higher review agent autonomy on GitHub Actions security patterns, though the finding-persistence gap (existing issues above) would need to be addressed first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation github_actions Pull requests that update GitHub Actions code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants