Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
f2b94e1
ci: add optional GPT summaries before release publication
zengchuang-hw Nov 2, 2026
dca6546
feat: generate bounded release overviews through the GPT API
zengchuang-hw Nov 2, 2026
947177c
docs: clarify metadata redaction for GPT release summaries
zengchuang-hw Nov 2, 2026
a0a136e
fix: omit recognizable private metadata from GPT release inputs
zengchuang-hw Nov 2, 2026
98a02c5
ci: configure the approved GPT release notes provider
zengchuang-hw Nov 2, 2026
c92a97e
feat: allow the approved Yibu endpoint for release overviews
zengchuang-hw Nov 2, 2026
cc7cc15
fix: identify release notes requests to compatible providers
zengchuang-hw Nov 2, 2026
7b9dd09
ci: use DeepSeek for AI release summaries
zengchuang-hw Nov 2, 2026
2b4825d
feat: use official DeepSeek for release overviews
zengchuang-hw Nov 2, 2026
4661e2b
fix: read the configured DeepSeek release notes secret
zengchuang-hw Nov 2, 2026
75591c3
docs: describe bounded AI release overviews
zengchuang-hw Nov 2, 2026
54b39cd
fix: keep AI release summaries concise within the output budget
zengchuang-hw Nov 2, 2026
af5ede3
docs: explain the source coverage notice
zengchuang-hw Nov 2, 2026
14e5153
fix: describe partial release metadata without chronology claims
zengchuang-hw Nov 2, 2026
b7ee0fa
fix: derive release overview coverage from source metadata
zengchuang-hw Nov 2, 2026
d4dfee5
docs: describe generated release link validation
zengchuang-hw Nov 3, 2026
ddb97dc
fix: bind release overview links to validated source commits
zengchuang-hw Nov 3, 2026
214f8dc
fix: keep release overview URL scanning linear
zengchuang-hw Nov 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 72 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,59 @@ jobs:
if-no-files-found: error
retention-days: 7

publish:
notes:
needs: [plan, build]
if: needs.plan.outputs.release == 'true' && needs.build.result == 'success'
runs-on: ubuntu-latest
timeout-minutes: 5
continue-on-error: true
permissions:
contents: read
pull-requests: none
outputs:
available: ${{ steps.notes.outputs.available }}
steps:
- uses: actions/checkout@v4
with:
ref: ${{ needs.plan.outputs.commit }}
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Summarize the validated source with the configured AI API
id: notes
env:
OPENAI_API_KEY: ${{ secrets.DS_API_KEY }}
OPENAI_MODEL: ${{ vars.OPENAI_RELEASE_NOTES_MODEL || 'deepseek-flash' }}
OPENAI_BASE_URL: ${{ vars.OPENAI_BASE_URL || 'https://api.deepseek.com' }}
GH_REPO: ${{ github.repository }}
RELEASE_TAG: ${{ needs.plan.outputs.tag }}
RELEASE_COMMIT: ${{ needs.plan.outputs.commit }}
PREVIOUS_TAG: ${{ needs.plan.outputs.previous_tag }}
shell: bash
run: |
python tools/generate_release_notes.py --tag "$RELEASE_TAG" \
--commit "$RELEASE_COMMIT" --previous-tag "$PREVIOUS_TAG" \
--model "$OPENAI_MODEL" --base-url "$OPENAI_BASE_URL" \
--output "$RUNNER_TEMP/release-notes/ai-notes.md"
if [[ -s "$RUNNER_TEMP/release-notes/ai-notes.md" ]]; then
echo "available=true" >> "$GITHUB_OUTPUT"
else
echo "available=false" >> "$GITHUB_OUTPUT"
fi
- uses: actions/upload-artifact@v4
if: steps.notes.outputs.available == 'true'
with:
name: release-notes
path: ${{ runner.temp }}/release-notes/ai-notes.md
if-no-files-found: error
retention-days: 7

publish:
needs: [plan, build, notes]
if: >-
always() && !cancelled() &&
needs.plan.outputs.release == 'true' && needs.build.result == 'success' &&
(github.event_name == 'schedule' || !inputs.dry_run)
runs-on: ubuntu-latest
Expand Down Expand Up @@ -156,6 +206,13 @@ jobs:
with:
name: release-distributions
path: dist
- uses: actions/download-artifact@v4
id: download_notes
if: needs.notes.outputs.available == 'true'
continue-on-error: true
with:
name: release-notes
path: notes
- name: Verify downloaded checksums
working-directory: dist
run: sha256sum --check SHA256SUMS
Expand All @@ -177,16 +234,27 @@ jobs:
[[ "$latest" == "$PREVIOUS_TAG" ]]
- name: Create the remote tag, upload packages, then publish
shell: bash
env:
AI_NOTES_DOWNLOADED: ${{ steps.download_notes.outcome }}
run: |
gh api --method POST "repos/$GH_REPO/git/refs" \
-f ref="refs/tags/$RELEASE_TAG" -f sha="$RELEASE_COMMIT" > /dev/null
notes="Source: https://github.com/$GH_REPO/tree/$RELEASE_COMMIT"
notes+=$'\n\nThese notes describe software changes. Release builds and scaffold checks do not establish campaign PASS, model quality or service performance. Any such claim requires its acceptance issue and complete frozen evidence.'
notes_file="$RUNNER_TEMP/release-body.md"
{
printf 'Source: https://github.com/%s/tree/%s\n' "$GH_REPO" "$RELEASE_COMMIT"
printf '\nThese notes describe software changes. Release builds and scaffold checks do not establish campaign PASS, model quality or service performance. Any such claim requires its acceptance issue and complete frozen evidence.\n'
if [[ "${AI_NOTES_DOWNLOADED:-}" == success && -f notes/ai-notes.md && ! -L notes/ai-notes.md ]] &&
[[ "$(wc -c < notes/ai-notes.md)" -gt 0 && "$(wc -c < notes/ai-notes.md)" -le 20000 ]]; then
printf '\n## AI-generated summary\n\n'
cat notes/ai-notes.md
printf '\n'
fi
} > "$notes_file"
options=()
if [[ -n "$PREVIOUS_TAG" ]]; then options+=(--notes-start-tag "$PREVIOUS_TAG"); fi
gh release create "$RELEASE_TAG" dist/*.whl dist/*.tar.gz dist/*.zip dist/SHA256SUMS \
--verify-tag --draft --title "InferRSI $RELEASE_TAG" \
--generate-notes --notes "$notes" "${options[@]}"
--generate-notes --notes-file "$notes_file" "${options[@]}"
assets=$(gh release view "$RELEASE_TAG" --json assets --jq '.assets[].name')
for file in dist/*.whl dist/*.tar.gz dist/*.zip dist/SHA256SUMS; do
grep -Fxq "$(basename "$file")" <<< "$assets"
Expand Down
84 changes: 78 additions & 6 deletions docs/releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Release. It must not create code commits, edit repository files, create/update/
delete code branches, create/approve/merge PRs or enable auto-merge. Configuration
development and review are contributor work; they are not bot capabilities.

Plan/build jobs have Contents read access. Only the publisher receives Contents
Plan/build/notes jobs have Contents read access. Only the publisher receives Contents
write; Pull requests permission is explicitly none. The publisher validates its
tag/SHA inputs and never checks out or executes repository code. Regression
tests execute its actual shell commands against a recording GitHub mock that
Expand Down Expand Up @@ -64,7 +64,12 @@ configuration does not silently change their access or existing branch rules.
Its generated plugin metadata, bundled runtime and tag must have the same
version. Verify every bundled runtime hash and exercise the extracted
launcher/knowledge reads outside the checkout with isolated Python.
4. A separate write job verifies the artifacts, source ancestry and release
4. After a successful build, an optional read-only notes job calls the AI API
with bounded change metadata for exactly the planned revision range. Its
Markdown summary is transferred as a separate artifact. Missing credentials,
API/validation errors and unavailable notes artifacts fall back to the
existing GitHub-generated notes.
5. A separate write job verifies the artifacts, source ancestry and release
state again. It creates the remote tag only after validation, creates an
internal draft, uploads wheel/sdist/plugin ZIP/checksums and automatically publishes
after confirming the attachments and tag identity. It does not execute
Expand All @@ -79,6 +84,69 @@ software changes; CPU/package checks do not establish campaign `PASS`, model
quality or service performance. Such claims retain the acceptance and evidence
requirements in [contributing](contributing.md#evidence-and-claims).

## AI release summaries

To enable AI summaries, add the Actions repository secret `DS_API_KEY` under
**Settings → Secrets and variables → Actions**. The key is injected only into
the notes step as the helper's internal `OPENAI_API_KEY` environment variable.
The workflow does not read or fall back to the old `OPENAI_API_KEY` repository
secret. Do not put the key in workflow source, command arguments, PR text or
logs. API access and usage are billed by the selected provider for that key;
GitHub Copilot access is not required.

The default API model is `deepseek-flash`, documented by DeepSeek as
[DeepSeek-V4.1-Flash-1017](https://api-docs.deepseek.com/quick_start/pricing/)
when this configuration was added. This is a provider-managed alias, not a
version pin; DeepSeek may upgrade its target. Set the optional Actions repository variable
`OPENAI_RELEASE_NOTES_MODEL` to another model available to your API account and
compatible with the Responses API. Set `OPENAI_BASE_URL` to select the approved
HTTPS provider: `https://api.deepseek.com` (the fallback),
`https://api.openai.com/v1` or `https://yibuapi.com/v1`. This repository uses
DeepSeek's official API and sends the configured key only to its `/responses`
endpoint. The existing `OPENAI_*` setting names also apply to DeepSeek; use a key
issued by the selected provider. The configured API model ID is sent unchanged;
the helper does not substitute a model when it is unavailable. Trailing slashes
are normalized; other hosts,
HTTP, userinfo, query strings and fragments are rejected before sending a key.
Redirects remain disabled. Additional providers require a reviewed allowlist
change. Without the secret, the workflow skips AI generation and continues ordinary
publication.

The [notes helper](../tools/generate_release_notes.py) sends bounded commit
subjects, source links and file-change statistics from the previous successful
tag through the planned commit. It omits author metadata, commit bodies and raw
patches. Recognizable email/IP literals, URLs, personal paths and common
credential forms in selected metadata are replaced with labeled placeholders
before transmission; generated canonical commit links remain available. This
targeted redaction supplements the repository's privacy rules for source
metadata. Inputs are treated as source data, not executable instructions.
The prompt asks for a concise English overview of software features, fixes and
compatibility changes, with at most six bullets, 250 words of prose and one
supplied commit link per bullet. It must not invent performance/quality or campaign PASS
claims. The response is labeled **AI-generated summary** and prepended to the
original GitHub changelog, preserving its PR links and source/evidence notice.
When source metadata is truncated, the helper appends a fixed coverage notice
from the source flags. The model is asked to leave coverage statements to the helper.
Model output is read as a file and never executed as shell code.
Explicit URLs and Markdown links in model output must be simple inline links to
canonical commit URLs present in the supplied source. Images, reference links,
bare URL/email autolinks and unsupported or unverified link forms cause fallback.
This gate validates generated link syntax; GitHub's own reference and custom
autolink rendering remains repository policy.

The helper makes one request using the
[Responses API](https://api-docs.deepseek.com/guides/responses_api/), with
bounded input/output (at most 3,200 output tokens), a timeout, no tools and
`store: false`. DeepSeek requests
set reasoning effort to `none` for the short summary. DeepSeek ignores `store`
and documents stateless Responses compatibility; this setting does
not establish zero data retention. Missing credentials, failed/incomplete/refused
or unusable responses leave no summary. The optional notes job or artifact may
fail without skipping an otherwise valid publication; failed package checks and
workflow cancellation still prevent publication. The AI job has no repository
write permission, and the publisher receives no API key. There is no AI code
commit, merge, account installation or update to an existing public Release.

Each Release contains a Python wheel, source distribution, the versioned Codex
plugin ZIP and `SHA256SUMS` covering all three packages. Extract the ZIP and
follow the [Codex plugin installation guide](codex-plugin.md#install-from-the-release-zip).
Expand All @@ -92,13 +160,17 @@ wrapper is packaged; the downloaded ZIP is the only Codex plugin release asset.

Merge the configuration into the default branch. GitHub Actions and the listed
actions must be allowed by organization policy, and the publisher needs
`contents: write`. No extra secret, PAT, GitHub App or repository-wide write
default is needed. The workflow guards the canonical repository and default
branch; copies must deliberately update that repository guard.
`contents: write`. Ordinary releases need no extra secret, PAT, GitHub App or
repository-wide write default. AI summaries additionally use the optional
`DS_API_KEY` secret described above. The workflow guards the canonical
repository and default branch; copies must deliberately update that repository
guard.

Under **Actions → Release bot → Run workflow**, select the default branch and
leave **dry_run** checked. It plans and builds the next release without creating
a remote tag or Release; unchanged trees skip here too. Clearing **dry_run**
a remote tag or Release; unchanged trees skip here too. With `DS_API_KEY`
configured, a changed-source dry run also makes the AI request and uploads the
summary artifact for inspection, consuming API usage. Clearing **dry_run**
runs the same automatic publication immediately. It takes no version/tag input.

```bash
Expand Down
Loading
Loading