Auto-release updates #143
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Auto-release updates | |
| on: | |
| schedule: | |
| - cron: "0 */6 * * *" # every 6 hours | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| actions: write | |
| jobs: | |
| refresh: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| # Use the existing chore/changelog-refresh branch (if any) as the | |
| # comparison baseline instead of main. This prevents re-triggering the | |
| # Mintlify agent every 6 hours for releases that were already processed | |
| # but whose PR has not been merged yet. When the PR is merged or closed | |
| # (with delete-branch: true below), the branch disappears and the next | |
| # release is detected normally. | |
| - name: Use existing changelog refresh branch as baseline | |
| run: | | |
| if git ls-remote --exit-code --heads origin chore/changelog-refresh >/dev/null 2>&1; then | |
| git fetch origin chore/changelog-refresh | |
| git checkout origin/chore/changelog-refresh -- docs/changelog/ || true | |
| echo "Baseline restored from chore/changelog-refresh branch." | |
| else | |
| echo "No chore/changelog-refresh branch; using main as baseline." | |
| fi | |
| - name: Regenerate docs/changelog from GitHub releases | |
| run: node scripts/generate-changelog.js | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| CHANGELOG_CACHE_TTL_SECONDS: "0" | |
| CHANGELOG_CHANGES_FILE: /tmp/changelog-changes.json | |
| - name: Read changelog changes | |
| id: changes | |
| run: | | |
| if [ ! -f /tmp/changelog-changes.json ] || [ "$(jq length /tmp/changelog-changes.json)" -eq 0 ]; then | |
| echo "No changelog changes detected." | |
| echo "pages=" >> "$GITHUB_OUTPUT" | |
| echo "has_sdk_updates=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| PAGES=$(jq -r '[.[].page] | join(" ")' /tmp/changelog-changes.json) | |
| echo "pages=${PAGES}" >> "$GITHUB_OUTPUT" | |
| # Read ALL latest versions from the cache file (updated by generate-changelog.js). | |
| # This ensures the pre-release workflow always gets every SDK's current version, | |
| # not just the ones whose changelog page happened to change in this run. | |
| CACHE=scripts/.changelog-cache.json | |
| echo "restate_version=$(jq -r '.repos.restate.releases[0].tag_name // empty' "$CACHE" | sed 's/^v//')" >> "$GITHUB_OUTPUT" | |
| echo "ts_version=$(jq -r '.repos["sdk-typescript"].releases[0].tag_name // empty' "$CACHE" | sed 's/^v//')" >> "$GITHUB_OUTPUT" | |
| echo "python_version=$(jq -r '.repos["sdk-python"].releases[0].tag_name // empty' "$CACHE" | sed 's/^v//')" >> "$GITHUB_OUTPUT" | |
| echo "java_version=$(jq -r '.repos["sdk-java"].releases[0].tag_name // empty' "$CACHE" | sed 's/^v//')" >> "$GITHUB_OUTPUT" | |
| echo "go_version=$(jq -r '.repos["sdk-go"].releases[0].tag_name // empty' "$CACHE" | sed 's/^v//')" >> "$GITHUB_OUTPUT" | |
| echo "rust_version=$(jq -r '.repos["sdk-rust"].releases[0].tag_name // empty' "$CACHE" | sed 's/^v//')" >> "$GITHUB_OUTPUT" | |
| # Check if any non-operator SDK page changed | |
| HAS=$(jq -r '[.[] | select(.page != "operator") | .latestVersion // empty | select(. != "")] | if length > 0 then "true" else "false" end' /tmp/changelog-changes.json) | |
| echo "has_sdk_updates=${HAS}" >> "$GITHUB_OUTPUT" | |
| echo "Changes detected:" | |
| jq -r '.[] | " \(.label) \(.latestVersion) — \(.latestReleaseUrl)"' /tmp/changelog-changes.json | |
| - name: Trigger pre-release workflow | |
| if: steps.changes.outputs.has_sdk_updates == 'true' | |
| continue-on-error: true | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| ARGS="" | |
| RESTATE_V="${{ steps.changes.outputs.restate_version }}" | |
| TS_V="${{ steps.changes.outputs.ts_version }}" | |
| PYTHON_V="${{ steps.changes.outputs.python_version }}" | |
| JAVA_V="${{ steps.changes.outputs.java_version }}" | |
| GO_V="${{ steps.changes.outputs.go_version }}" | |
| RUST_V="${{ steps.changes.outputs.rust_version }}" | |
| [ -n "$RESTATE_V" ] && ARGS="$ARGS -f restateVersion=$RESTATE_V" | |
| [ -n "$TS_V" ] && ARGS="$ARGS -f sdkTypescriptVersion=$TS_V" | |
| [ -n "$PYTHON_V" ] && ARGS="$ARGS -f sdkPythonVersion=$PYTHON_V" | |
| [ -n "$JAVA_V" ] && ARGS="$ARGS -f sdkJavaVersion=$JAVA_V" | |
| [ -n "$GO_V" ] && ARGS="$ARGS -f sdkGoVersion=$GO_V" | |
| [ -n "$RUST_V" ] && ARGS="$ARGS -f sdkRustVersion=$RUST_V" | |
| echo "Dispatching pre-release workflow with:${ARGS}" | |
| gh workflow run pre-release.yml $ARGS | |
| sleep 5 | |
| RUN_URL=$(gh run list --workflow pre-release.yml --limit 1 --json url --jq '.[0].url') | |
| echo "Pre-release workflow run: ${RUN_URL}" | |
| - name: Open or update changelog refresh PR | |
| uses: peter-evans/create-pull-request@v6 | |
| with: | |
| commit-message: "chore: refresh changelog from GitHub releases" | |
| title: "chore: refresh changelog" | |
| body: | | |
| Automated changelog refresh from GitHub releases across the | |
| restatedev org. Review the rendered `docs/changelog.mdx` and merge | |
| when the output looks right. | |
| branch: chore/changelog-refresh | |
| delete-branch: true | |
| add-paths: docs/changelog | |
| reviewers: gvdongen | |
| labels: | | |
| automated | |
| changelog | |
| - name: Trigger Mintlify agent per SDK | |
| if: steps.changes.outputs.pages != '' | |
| continue-on-error: true | |
| timeout-minutes: 30 | |
| env: | |
| MINTLIFY_API_KEY: ${{ secrets.MINTLIFY_API_KEY }} | |
| MINTLIFY_PROJECT_ID: ${{ secrets.MINTLIFY_PROJECT_ID }} | |
| run: | | |
| # Phase 1: Create agent jobs for each changed SDK | |
| jq -c '.[]' /tmp/changelog-changes.json | while IFS= read -r entry; do | |
| LABEL=$(echo "$entry" | jq -r '.label') | |
| # Count new releases for this page | |
| RELEASE_COUNT=$(echo "$entry" | jq '.newReleases | length') | |
| if [ "$RELEASE_COUNT" -eq 0 ] 2>/dev/null; then | |
| echo "${LABEL}: No new releases detected, skipping Mintlify agent." | |
| continue | |
| fi | |
| # Build a list of all new releases: "- ProductTag vX.Y.Z: <url>" | |
| RELEASE_LIST=$(echo "$entry" | jq -r '.newReleases[] | "- \(.productTag) \(.tagName): \(.releaseUrl)"') | |
| PROMPT="The following ${LABEL} releases were published: | |
| ${RELEASE_LIST} | |
| Review ALL of the above release notes and update the documentation accordingly. | |
| For each user-facing change across these releases: | |
| 1. Dig into the linked PRs from the release notes to fully understand the change, its motivation, and usage. | |
| 2. Draft or update documentation pages in the /docs directory that explain what the feature does, when to use it, and how to configure it. | |
| 3. Update the /restate-plugin/src directory to reflect the latest changes in the SDK. Only do this for features that are important enough to be loaded as part of Restate's Coding Agent skills. | |
| 3. Include code examples where relevant. All code snippets MUST be placed in the /snippets directory organized by language (ts, java, kotlin, python, go, rust) and referenced from MDX docs using the CODE_LOAD syntax. Use <start_tag>/<end_tag> markers in snippet files for section extraction. See CLAUDE.md for full CODE_LOAD details. | |
| 4. Create a PR with the updated documentation. | |
| Success criteria: After reading any new or updated documentation, users understand what the feature is, whether it applies to their use case, and how to use it. | |
| Important: | |
| - Only document changes that affect end users. Skip internal refactors or dependency updates. | |
| - Match the style and structure of existing docs pages. | |
| - If no user-facing changes were introduced, do nothing. | |
| - Do not include private repository file paths, directory structures, code snippets, or any other internal implementation details in PR titles, descriptions, or commit messages. | |
| - Write a quick summary of the ${LABEL} features that shipped in the PR description." | |
| HTTP_STATUS=$(jq -n --arg prompt "$PROMPT" '{prompt: $prompt}' | \ | |
| curl -sS -o /tmp/mintlify-response.json -w "%{http_code}" \ | |
| -X POST "https://api.mintlify.com/v2/agent/${MINTLIFY_PROJECT_ID}/job" \ | |
| -H "Authorization: Bearer ${MINTLIFY_API_KEY}" \ | |
| -H "Content-Type: application/json" \ | |
| -d @-) | |
| JOB_ID=$(jq -r '.id' /tmp/mintlify-response.json) | |
| if [ "$HTTP_STATUS" -ge 200 ] && [ "$HTTP_STATUS" -lt 300 ] && \ | |
| [ -n "$JOB_ID" ] && [ "$JOB_ID" != "null" ]; then | |
| echo "${LABEL}: Created agent job ${JOB_ID} for ${RELEASE_COUNT} release(s)" | |
| echo "${JOB_ID}|${LABEL}" >> /tmp/mintlify-jobs.txt | |
| else | |
| echo "::warning::Failed to create agent job for ${LABEL} (HTTP ${HTTP_STATUS})" | |
| fi | |
| done | |
| # Phase 2: Poll all jobs until each completes or fails | |
| [ ! -f /tmp/mintlify-jobs.txt ] && exit 0 | |
| echo "Polling $(wc -l < /tmp/mintlify-jobs.txt) agent job(s)..." | |
| while [ -s /tmp/mintlify-jobs.txt ]; do | |
| sleep 30 | |
| > /tmp/mintlify-pending.txt | |
| while IFS='|' read -r JOB_ID LABEL; do | |
| POLL_RESPONSE=$(curl -sS \ | |
| "https://api.mintlify.com/v2/agent/${MINTLIFY_PROJECT_ID}/job/${JOB_ID}" \ | |
| -H "Authorization: Bearer ${MINTLIFY_API_KEY}") | |
| JOB_STATUS=$(echo "$POLL_RESPONSE" | jq -r '.status') | |
| case "$JOB_STATUS" in | |
| completed) | |
| PR_LINK=$(echo "$POLL_RESPONSE" | jq -r '.prLink // empty') | |
| if [ -n "$PR_LINK" ]; then | |
| echo "${LABEL}: PR created -- ${PR_LINK}" | |
| else | |
| echo "${LABEL}: Completed (no changes needed)" | |
| fi ;; | |
| failed|archived) | |
| echo "::warning::${LABEL} agent job ${JOB_STATUS}" ;; | |
| active) | |
| echo "${LABEL}: Still running..." | |
| echo "${JOB_ID}|${LABEL}" >> /tmp/mintlify-pending.txt ;; | |
| *) | |
| echo "::warning::${LABEL} unexpected status: ${JOB_STATUS}" ;; | |
| esac | |
| done < /tmp/mintlify-jobs.txt | |
| mv /tmp/mintlify-pending.txt /tmp/mintlify-jobs.txt | |
| done |