name: Regenerate
on:
repository_dispatch:
types: [openapi-updated]
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false
jobs:
generate:
name: Regenerate from OpenAPI
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: main
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: Setup Java (for openapi-generator)
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- name: Install OpenAPI Generator
run: npm install -g @openapitools/openapi-generator-cli
- name: Fetch latest OpenAPI spec
run: |
curl -f --retry 5 --retry-delay 2 --retry-all-errors --max-time 60 -o openapi.yaml https://zernio.com/openapi.json
echo "Fetched OpenAPI spec"
- name: Generate SDK
run: |
openapi-generator-cli generate \
-i openapi.yaml \
-g rust \
-o . \
--skip-validate-spec \
--additional-properties=packageName=zernio,library=reqwest \
--git-user-id=zernio-dev \
--git-repo-id=zernio-rust
- name: Patch README install instructions
run: |
python3 -c "
import re
with open('README.md', 'r') as f:
content = f.read()
install_section = '''## Installation
Add the crate to your project:
\`\`\`bash
cargo add zernio
\`\`\`
Or add it manually to your \`Cargo.toml\`:
\`\`\`toml
[dependencies]
zernio = \"*\"
\`\`\`'''
import textwrap
install_section = textwrap.dedent(install_section)
content = re.sub(r'## Installation.*?(?=\n## )', install_section + '\n\n', content, flags=re.DOTALL)
with open('README.md', 'w') as f:
f.write(content)
"
- name: Use rustls-tls instead of native-tls
run: |
sed -i 's/default = \["native-tls"\]/default = ["rustls-tls"]/' Cargo.toml
- name: Format code
run: cargo fmt || true
- name: Build
run: cargo build
- name: Run tests
run: cargo test
- name: Check for changes
id: changes
run: |
git add -A
if git diff --staged --quiet; then
echo "has_changes=false" >> $GITHUB_OUTPUT
else
echo "has_changes=true" >> $GITHUB_OUTPUT
fi
- name: Get next version
if: steps.changes.outputs.has_changes == 'true'
id: version
run: |
# Base = max(highest tag, highest published crate). git describe alone
# gets stuck when a run publishes but dies before tagging (crates.io
# rejects the recomputed version forever after; zernio-ruby hit this
# 2026-08-12). Checking the registry self-heals any publish/tag
# divergence, including the tag race between concurrent dispatches.
TAG_VERSION=$(git tag -l 'v*' | sed 's/^v//' | sort -V | tail -1)
ZERNIO_PUBLISHED=$(curl -sf --retry 3 https://index.crates.io/ze/rn/zernio | jq -r '.vers' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -1 || echo "0.0.0")
LATE_PUBLISHED=$(curl -sf --retry 3 https://index.crates.io/la/te/late | jq -r '.vers' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -1 || echo "0.0.0")
BASE=$(printf '%s\n' "${TAG_VERSION:-0.0.0}" "${ZERNIO_PUBLISHED:-0.0.0}" "${LATE_PUBLISHED:-0.0.0}" | sort -V | tail -1)
CARGO_VERSION=$(echo "$BASE" | awk -F. '{printf "%d.%d.%d", $1, $2, $3+1}')
echo "new_version=v$CARGO_VERSION" >> $GITHUB_OUTPUT
echo "cargo_version=$CARGO_VERSION" >> $GITHUB_OUTPUT
- name: Update Cargo.toml version
if: steps.changes.outputs.has_changes == 'true'
run: |
sed -i "s/^version = .*/version = \"${{ steps.version.outputs.cargo_version }}\"/" Cargo.toml || true
- name: Commit and push
if: steps.changes.outputs.has_changes == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add -A
git commit -m "chore: regenerate from OpenAPI spec
- Auto-generated SDK updates
- Version: ${{ steps.version.outputs.new_version }}"
# Retry with rebase to survive concurrent dispatches that land
# back-to-back pushes to main. -X theirs: the replayed commit is a
# full regeneration from a fresher spec fetch, so on conflicting
# hunks the regenerated content wins instead of aborting the run.
for attempt in 1 2 3; do
if git push; then break; fi
echo "Push attempt $attempt failed; rebasing and retrying..."
git pull --rebase -X theirs origin main
sleep $((attempt * 5))
done
- name: Build
if: steps.changes.outputs.has_changes == 'true'
run: cargo build --release
- name: Publish as zernio to crates.io
if: steps.changes.outputs.has_changes == 'true'
run: cargo publish --token ${{ secrets.CRATES_IO_TOKEN }}
- name: Publish as late to crates.io
if: steps.changes.outputs.has_changes == 'true'
run: |
sed -i 's/^name = "zernio"/name = "late"/' Cargo.toml
cargo publish --allow-dirty --token ${{ secrets.CRATES_IO_TOKEN }}
- name: Restore Cargo.toml package name
if: steps.changes.outputs.has_changes == 'true'
run: |
sed -i 's/^name = "late"/name = "zernio"/' Cargo.toml
- name: Create GitHub Release
if: steps.changes.outputs.has_changes == 'true'
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.version.outputs.new_version }}
name: ${{ steps.version.outputs.new_version }}
generate_release_notes: true
body: |
## Auto-generated SDK Update
Available as both `zernio` and `late` on crates.io:
```toml
[dependencies]
zernio = "${{ steps.version.outputs.cargo_version }}"
# or
late = "${{ steps.version.outputs.cargo_version }}"
```
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}