Skip to content

build(deps): bump toml from 0.9.12+spec-1.1.0 to 1.1.0+spec-1.1.0 in /rust #28

build(deps): bump toml from 0.9.12+spec-1.1.0 to 1.1.0+spec-1.1.0 in /rust

build(deps): bump toml from 0.9.12+spec-1.1.0 to 1.1.0+spec-1.1.0 in /rust #28

Workflow file for this run

name: Release Draft
on:
pull_request_target:
types: [closed]
branches: [main]
concurrency:
group: release-draft
cancel-in-progress: false
permissions:
contents: write
pull-requests: write
jobs:
analyze-and-draft:
if: |
github.event.pull_request.merged == true &&
!contains(github.event.pull_request.labels.*.name, 'breaking-change-analyzed')
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Ensure labels exist
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh label create "breaking-change-analyzed" --color "0E8A16" --description "PR has been analyzed for breaking changes" 2>/dev/null || true
gh label create "breaking-change" --color "D93F0B" --description "PR contains breaking changes" 2>/dev/null || true
- name: Run Claude Code Analysis
id: claude
timeout-minutes: 10
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
github_token: ${{ secrets.GITHUB_TOKEN }}
show_full_output: true
prompt: |
Analyze PR #${{ github.event.pull_request.number }} for breaking changes.
PR Title: ${{ github.event.pull_request.title }}
PR Body:
${{ github.event.pull_request.body }}
Analyze the merged changes and determine if this PR contains breaking changes.
Breaking changes for tstring-structured-data include:
1. Template String Parsing Changes - Changes to how JSON, TOML, or YAML template strings are parsed or validated
2. Generated Output Changes - Changes to serialized output, normalization, or emitted structures
3. Python API Changes - Changes to public Python APIs, function signatures, or import paths
4. Rust Bindings Changes - Changes to the bindings API, exposed behavior, or compatibility expectations
5. Default Behavior Changes - Changes to default parsing, coercion, or error behavior
6. Python or Runtime Support Changes - Dropping Python versions or changing supported runtime assumptions
If breaking changes are found, format them EXACTLY like this CHANGELOG.md format:
### Category Name
* Description of breaking change - Detailed explanation (#${{ github.event.pull_request.number }})
Rules:
- Each category should be a ### heading
- Each item starts with "* " followed by a brief title, then " - " and detailed explanation
- Include PR number at the end as (#NUMBER)
- If code examples help, include them in markdown code blocks
- Only include categories that have actual breaking changes
- Return empty string for breaking_changes_content if no breaking changes
claude_args: |
--model claude-opus-4-5-20251101
--max-turns 50
--json-schema '{"type":"object","properties":{"has_breaking_changes":{"type":"boolean","description":"Whether this PR contains breaking changes"},"breaking_changes_content":{"type":"string","description":"Formatted breaking changes section content (without ## Breaking Changes header), or empty string if none"},"reasoning":{"type":"string","description":"Brief explanation of why this is or is not a breaking change"}},"required":["has_breaking_changes","breaking_changes_content","reasoning"]}'
- name: Parse Claude output
id: parse
env:
CLAUDE_OUTPUT: ${{ steps.claude.outputs.structured_output }}
run: |
HAS_BC=$(echo "$CLAUDE_OUTPUT" | jq -r '.has_breaking_changes // false')
BC_CONTENT=$(echo "$CLAUDE_OUTPUT" | jq -r '.breaking_changes_content // ""')
REASONING=$(echo "$CLAUDE_OUTPUT" | jq -r '.reasoning // ""')
echo "has_breaking_changes=$HAS_BC" >> $GITHUB_OUTPUT
DELIMITER="EOF_$(date +%s%N)"
{
echo "breaking_changes_content<<$DELIMITER"
printf '%s\n' "$BC_CONTENT"
echo "$DELIMITER"
} >> $GITHUB_OUTPUT
{
echo "reasoning<<$DELIMITER"
printf '%s\n' "$REASONING"
echo "$DELIMITER"
} >> $GITHUB_OUTPUT
- name: Add breaking-change-analyzed label
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr edit ${{ github.event.pull_request.number }} --add-label "breaking-change-analyzed"
- name: Add breaking-change label if applicable
if: steps.parse.outputs.has_breaking_changes == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr edit ${{ github.event.pull_request.number }} --add-label "breaking-change"
- name: Post analysis result to PR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HAS_BC: ${{ steps.parse.outputs.has_breaking_changes }}
BC_CONTENT: ${{ steps.parse.outputs.breaking_changes_content }}
REASONING: ${{ steps.parse.outputs.reasoning }}
run: |
TMPFILE=$(mktemp)
trap 'rm -f "$TMPFILE"' EXIT
{
printf '## Breaking Change Analysis\n\n'
if [ "$HAS_BC" = "true" ]; then
printf 'Result: Breaking changes detected\n\n'
printf 'Reasoning: %s\n\n' "$REASONING"
printf '### Content for Release Notes\n\n%s\n\n' "$BC_CONTENT"
else
printf 'Result: No breaking changes detected\n\n'
printf 'Reasoning: %s\n\n' "$REASONING"
fi
printf -- '---\n'
printf '*This analysis was performed by Claude Code Action*\n'
} > "$TMPFILE"
gh pr comment ${{ github.event.pull_request.number }} --body-file "$TMPFILE"
- name: Calculate version and update draft release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HAS_BC: ${{ steps.parse.outputs.has_breaking_changes }}
BC_CONTENT: ${{ steps.parse.outputs.breaking_changes_content }}
run: |
set -euo pipefail
LATEST_TAG_RAW=$(gh release list --limit 1 --exclude-drafts --json tagName --jq '.[0].tagName // "0.0.0"')
LATEST_TAG="${LATEST_TAG_RAW#v}"
LATEST_TAG="${LATEST_TAG#V}"
echo "Latest published tag: $LATEST_TAG_RAW (parsed as: $LATEST_TAG)"
MAJOR=$(echo "$LATEST_TAG" | cut -d. -f1)
MINOR=$(echo "$LATEST_TAG" | cut -d. -f2)
PATCH=$(echo "$LATEST_TAG" | cut -d. -f3)
if ! [[ "$MAJOR" =~ ^[0-9]+$ ]] || ! [[ "$MINOR" =~ ^[0-9]+$ ]] || ! [[ "$PATCH" =~ ^[0-9]+$ ]]; then
echo "Warning: Could not parse version from tag '$LATEST_TAG_RAW', using 0.0.0"
MAJOR=0
MINOR=0
PATCH=0
fi
DRAFT_TAG_RAW=$(gh release list --json tagName,isDraft --jq '[.[] | select(.isDraft == true)] | .[0].tagName // ""')
DRAFT_TAG="${DRAFT_TAG_RAW#v}"
DRAFT_TAG="${DRAFT_TAG#V}"
BC_SOURCE_TAG_RAW="$DRAFT_TAG_RAW"
if [ -n "$DRAFT_TAG" ]; then
echo "Existing draft: $DRAFT_TAG_RAW"
DRAFT_PATCH=$(echo "$DRAFT_TAG" | cut -d. -f3)
if [ "$HAS_BC" = "true" ] && [ "$DRAFT_PATCH" != "0" ]; then
echo "Upgrading from patch to minor release due to breaking changes"
NEW_MINOR=$((MINOR + 1))
NEXT_VERSION="${MAJOR}.${NEW_MINOR}.0"
OLD_DRAFT_TAG="$DRAFT_TAG_RAW"
DRAFT_TAG_RAW=""
DRAFT_TAG=""
else
NEXT_VERSION="$DRAFT_TAG"
OLD_DRAFT_TAG=""
fi
else
OLD_DRAFT_TAG=""
BC_SOURCE_TAG_RAW=""
if [ "$HAS_BC" = "true" ]; then
NEW_MINOR=$((MINOR + 1))
NEXT_VERSION="${MAJOR}.${NEW_MINOR}.0"
else
NEW_PATCH=$((PATCH + 1))
NEXT_VERSION="${MAJOR}.${MINOR}.${NEW_PATCH}"
fi
fi
echo "Next version: $NEXT_VERSION"
if [ -n "$LATEST_TAG" ] && [ "$LATEST_TAG" != "0.0.0" ]; then
GENERATED_NOTES=$(gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
/repos/${{ github.repository }}/releases/generate-notes \
-f tag_name="$NEXT_VERSION" \
-f previous_tag_name="$LATEST_TAG_RAW" \
--jq '.body')
else
GENERATED_NOTES=$(gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
/repos/${{ github.repository }}/releases/generate-notes \
-f tag_name="$NEXT_VERSION" \
--jq '.body')
fi
EXISTING_BC=""
if [ -n "$BC_SOURCE_TAG_RAW" ]; then
EXISTING_BODY=$(gh release view "$BC_SOURCE_TAG_RAW" --json body --jq '.body // ""')
if echo "$EXISTING_BODY" | grep -q '^## Breaking Changes$'; then
EXISTING_BC=$(echo "$EXISTING_BODY" | awk '
/^## Breaking Changes$/ { found=1; next }
found && /^## / { exit }
found { print }
')
fi
fi
FINAL_BC=""
if [ -n "$BC_CONTENT" ] && [ -n "$EXISTING_BC" ]; then
MERGED_BC=$(printf '%s\n\n%s' "$EXISTING_BC" "$BC_CONTENT" | awk '
BEGIN { current_section = ""; in_fence = 0; }
/^```/ || /^~~~/ { in_fence = !in_fence; }
/^### / && !in_fence {
current_section = $0;
if (!(current_section in sections)) {
order[++order_count] = current_section;
}
next;
}
/^[[:space:]]*$/ && !in_fence { next; }
current_section != "" {
if (sections[current_section] != "") {
sections[current_section] = sections[current_section] "\n" $0;
} else {
sections[current_section] = $0;
}
}
END {
for (i = 1; i <= order_count; i++) {
section = order[i];
if (i > 1) print "";
print section;
print sections[section];
}
}
')
FINAL_BC=$(printf '## Breaking Changes\n\n%s' "$MERGED_BC")
elif [ -n "$BC_CONTENT" ]; then
FINAL_BC=$(printf '## Breaking Changes\n\n%s' "$BC_CONTENT")
elif [ -n "$EXISTING_BC" ]; then
FINAL_BC=$(printf '## Breaking Changes\n\n%s' "$EXISTING_BC")
fi
if [ -n "$FINAL_BC" ]; then
RELEASE_BODY=$(printf '%s\n\n%s' "$FINAL_BC" "$GENERATED_NOTES")
else
RELEASE_BODY="$GENERATED_NOTES"
fi
if [ -n "$DRAFT_TAG_RAW" ] && [ "$DRAFT_TAG" = "$NEXT_VERSION" ]; then
echo "$RELEASE_BODY" | gh release edit "$DRAFT_TAG_RAW" \
--title "$NEXT_VERSION" \
--notes-file -
else
if echo "$RELEASE_BODY" | gh release create "$NEXT_VERSION" \
--title "$NEXT_VERSION" \
--notes-file - \
--draft; then
if [ -n "$OLD_DRAFT_TAG" ]; then
gh release delete "$OLD_DRAFT_TAG" --yes 2>/dev/null || true
fi
else
echo "Failed to create new draft release"
exit 1
fi
fi