Release #25
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: Release | |
| on: | |
| push: | |
| branches: [main] | |
| paths-ignore: | |
| - "docs/**" | |
| - "infra/**" | |
| - "tests/**" | |
| - "**/*.md" | |
| - "**/*.mdx" | |
| - ".claude/**" | |
| - ".github/**" | |
| schedule: | |
| - cron: "30 0 * * *" | |
| workflow_dispatch: | |
| inputs: | |
| channel: | |
| description: "Release channel to publish." | |
| required: false | |
| default: release | |
| type: choice | |
| options: | |
| - release | |
| - main | |
| tag: | |
| description: "Optional tag to release (e.g. v2026.4.5 or v0.1). Ignored for the main channel." | |
| required: false | |
| type: string | |
| permissions: | |
| contents: write | |
| actions: read | |
| concurrency: | |
| group: release-${{ github.event_name == 'push' && 'main' || (github.event_name == 'workflow_dispatch' && inputs.channel == 'main' && 'main') || 'stable' }} | |
| cancel-in-progress: true | |
| jobs: | |
| prepare: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| channel: ${{ steps.meta.outputs.channel }} | |
| tag_name: ${{ steps.meta.outputs.tag_name }} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 | |
| - name: Resolve release metadata | |
| id: meta | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| DISPATCH_CHANNEL: ${{ inputs.channel }} | |
| DISPATCH_TAG: ${{ inputs.tag }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| channel="release" | |
| if [ "$EVENT_NAME" = "push" ]; then | |
| channel="main" | |
| fi | |
| if [ "$EVENT_NAME" = "workflow_dispatch" ] && [ "$DISPATCH_CHANNEL" = "main" ]; then | |
| channel="main" | |
| fi | |
| if [ "$channel" = "main" ]; then | |
| if [ "$EVENT_NAME" = "workflow_dispatch" ] && [ -n "$DISPATCH_TAG" ]; then | |
| echo "The main channel does not accept a custom tag." >&2 | |
| exit 1 | |
| fi | |
| echo "channel=main" >> "$GITHUB_OUTPUT" | |
| echo "tag_name=nightly" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| if [ "$EVENT_NAME" = "workflow_dispatch" ] && [ -n "$DISPATCH_TAG" ]; then | |
| tag_name="$DISPATCH_TAG" | |
| else | |
| year="$(date -u +%Y)" | |
| month="$(date -u +%-m)" | |
| day="$(date -u +%-d)" | |
| tag_name="v${year}.${month}.${day}" | |
| fi | |
| echo "channel=release" >> "$GITHUB_OUTPUT" | |
| echo "tag_name=${tag_name}" >> "$GITHUB_OUTPUT" | |
| verify: | |
| runs-on: ubuntu-latest | |
| needs: prepare | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v8.1.0 | |
| with: | |
| enable-cache: true | |
| cache-dependency-glob: | | |
| pyproject.toml | |
| uv.lock | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.13" | |
| - name: Install dependencies | |
| run: uv sync --frozen --extra dev | |
| - name: Lint | |
| run: make lint | |
| - name: Type check | |
| run: make typecheck | |
| - name: CLI smoke tests | |
| run: make test-cli-smoke | |
| build-python-dist: | |
| if: needs.prepare.outputs.channel == 'release' | |
| runs-on: ubuntu-latest | |
| needs: [verify, prepare] | |
| env: | |
| TAG_NAME: ${{ needs.prepare.outputs.tag_name }} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v8.1.0 | |
| with: | |
| enable-cache: true | |
| cache-dependency-glob: | | |
| pyproject.toml | |
| uv.lock | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.13" | |
| - name: Sync release version | |
| shell: bash | |
| run: python packaging/sync_release_version.py --tag "$TAG_NAME" | |
| - name: Build Python distributions | |
| run: | | |
| uv sync --frozen --extra release-dist | |
| uv run python -m build | |
| uv run twine check dist/* | |
| - name: Verify Python distribution version | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| VERSION="${TAG_NAME#v}" | |
| test -f "dist/opensre-${VERSION}.tar.gz" | |
| test -f "dist/opensre-${VERSION}-py3-none-any.whl" | |
| - name: Upload Python distributions | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: release-python-dist | |
| path: dist/* | |
| if-no-files-found: error | |
| build-binaries: | |
| needs: [verify, prepare] | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - runner: ubuntu-latest | |
| target: linux-x64 | |
| binary_name: opensre | |
| archive_ext: tar.gz | |
| - runner: ubuntu-24.04-arm | |
| target: linux-arm64 | |
| binary_name: opensre | |
| archive_ext: tar.gz | |
| - runner: macos-15-intel | |
| target: darwin-x64 | |
| binary_name: opensre | |
| archive_ext: tar.gz | |
| - runner: macos-latest | |
| target: darwin-arm64 | |
| binary_name: opensre | |
| archive_ext: tar.gz | |
| - runner: windows-latest | |
| target: windows-x64 | |
| binary_name: opensre.exe | |
| archive_ext: zip | |
| # windows-arm64 is currently excluded from the default release matrix: | |
| # cryptography does not publish win_arm64 wheels, so dependency install | |
| # falls back to a source build that requires an OpenSSL toolchain on the | |
| # GitHub-hosted runner. | |
| runs-on: ${{ matrix.runner }} | |
| env: | |
| RELEASE_CHANNEL: ${{ needs.prepare.outputs.channel }} | |
| TAG_NAME: ${{ needs.prepare.outputs.tag_name }} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v8.1.0 | |
| with: | |
| enable-cache: true | |
| cache-dependency-glob: | | |
| pyproject.toml | |
| uv.lock | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.13" | |
| - name: Sync release version | |
| if: env.RELEASE_CHANNEL == 'release' | |
| shell: bash | |
| run: python packaging/sync_release_version.py --tag "$TAG_NAME" | |
| - name: Install binary build dependencies | |
| shell: bash | |
| run: uv sync --frozen --extra release-binary | |
| - name: Build binary | |
| run: uv run pyinstaller packaging/opensre.spec --clean --noconfirm | |
| - name: Smoke test binary | |
| if: runner.os != 'Windows' && env.RELEASE_CHANNEL == 'release' | |
| shell: bash | |
| run: | | |
| VERSION="${TAG_NAME#v}" | |
| VERSION_OUTPUT="$(./dist/${{ matrix.binary_name }} --version 2>&1)" | |
| printf '%s\n' "$VERSION_OUTPUT" | |
| case "$VERSION_OUTPUT" in | |
| *"$VERSION"*) ;; | |
| *) | |
| printf 'Binary version mismatch: expected %s but saw %s\n' "$VERSION" "$VERSION_OUTPUT" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| ./dist/${{ matrix.binary_name }} -h >/dev/null | |
| - name: Smoke test binary | |
| if: runner.os != 'Windows' && env.RELEASE_CHANNEL == 'main' | |
| shell: bash | |
| run: | | |
| VERSION_OUTPUT="$(./dist/${{ matrix.binary_name }} --version 2>&1)" | |
| printf '%s\n' "$VERSION_OUTPUT" | |
| ./dist/${{ matrix.binary_name }} -h >/dev/null | |
| - name: Smoke test binary | |
| if: runner.os == 'Windows' && env.RELEASE_CHANNEL == 'release' | |
| shell: pwsh | |
| run: | | |
| $expectedVersion = $env:TAG_NAME.TrimStart("v") | |
| $versionOutput = & ".\dist\${{ matrix.binary_name }}" --version 2>&1 | Out-String | |
| $versionText = $versionOutput.Trim() | |
| Write-Host $versionText | |
| if ($versionText -notmatch [regex]::Escape($expectedVersion)) { | |
| throw "Binary version mismatch. Expected '$expectedVersion' but saw '$versionText'." | |
| } | |
| & ".\dist\${{ matrix.binary_name }}" -h | Out-Null | |
| - name: Smoke test binary | |
| if: runner.os == 'Windows' && env.RELEASE_CHANNEL == 'main' | |
| shell: pwsh | |
| run: | | |
| $versionOutput = & ".\dist\${{ matrix.binary_name }}" --version 2>&1 | Out-String | |
| Write-Host $versionOutput.Trim() | |
| & ".\dist\${{ matrix.binary_name }}" -h | Out-Null | |
| - name: Package binary archive | |
| if: runner.os != 'Windows' && env.RELEASE_CHANNEL == 'release' | |
| shell: bash | |
| run: | | |
| VERSION="${TAG_NAME#v}" | |
| ASSET_BASENAME="opensre_${VERSION}_${{ matrix.target }}" | |
| tar -C dist -czf "${ASSET_BASENAME}.tar.gz" "${{ matrix.binary_name }}" | |
| shasum -a 256 "${ASSET_BASENAME}.tar.gz" > "${ASSET_BASENAME}.tar.gz.sha256" | |
| - name: Package binary archive | |
| if: runner.os != 'Windows' && env.RELEASE_CHANNEL == 'main' | |
| shell: bash | |
| run: | | |
| ASSET_BASENAME="opensre_main_${{ matrix.target }}" | |
| tar -C dist -czf "${ASSET_BASENAME}.tar.gz" "${{ matrix.binary_name }}" | |
| shasum -a 256 "${ASSET_BASENAME}.tar.gz" > "${ASSET_BASENAME}.tar.gz.sha256" | |
| - name: Package binary archive | |
| if: runner.os == 'Windows' && env.RELEASE_CHANNEL == 'release' | |
| shell: pwsh | |
| run: | | |
| $version = $env:TAG_NAME.TrimStart("v") | |
| $assetBaseName = "opensre_${version}_${{ matrix.target }}" | |
| Compress-Archive -Path "dist\${{ matrix.binary_name }}" -DestinationPath "${assetBaseName}.zip" | |
| $hash = (Get-FileHash -Algorithm SHA256 "${assetBaseName}.zip").Hash.ToLowerInvariant() | |
| Set-Content -Path "${assetBaseName}.zip.sha256" -Value "$hash ${assetBaseName}.zip" | |
| - name: Package binary archive | |
| if: runner.os == 'Windows' && env.RELEASE_CHANNEL == 'main' | |
| shell: pwsh | |
| run: | | |
| $assetBaseName = "opensre_main_${{ matrix.target }}" | |
| Compress-Archive -Path "dist\${{ matrix.binary_name }}" -DestinationPath "${assetBaseName}.zip" | |
| $hash = (Get-FileHash -Algorithm SHA256 "${assetBaseName}.zip").Hash.ToLowerInvariant() | |
| Set-Content -Path "${assetBaseName}.zip.sha256" -Value "$hash ${assetBaseName}.zip" | |
| - name: Upload binary archive | |
| if: env.RELEASE_CHANNEL == 'release' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: release-${{ matrix.target }} | |
| path: | | |
| opensre_*_${{ matrix.target }}.${{ matrix.archive_ext }} | |
| opensre_*_${{ matrix.target }}.${{ matrix.archive_ext }}.sha256 | |
| if-no-files-found: error | |
| - name: Upload binary archive | |
| if: env.RELEASE_CHANNEL == 'main' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: main-release-${{ matrix.target }} | |
| path: | | |
| opensre_main_${{ matrix.target }}.${{ matrix.archive_ext }} | |
| opensre_main_${{ matrix.target }}.${{ matrix.archive_ext }}.sha256 | |
| if-no-files-found: error | |
| publish-release: | |
| if: needs.prepare.outputs.channel == 'release' | |
| runs-on: ubuntu-latest | |
| needs: | |
| - prepare | |
| - build-python-dist | |
| - build-binaries | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| ref: ${{ github.event.repository.default_branch }} | |
| fetch-depth: 0 | |
| - name: Download release artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: release-* | |
| path: release-assets | |
| merge-multiple: true | |
| - name: Create release notes | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| TAG_NAME="${{ needs.prepare.outputs.tag_name }}" | |
| default_branch="${{ github.event.repository.default_branch }}" | |
| git fetch origin "$default_branch" --tags --force | |
| target_sha="$(git rev-parse "origin/$default_branch")" | |
| previous_tag="$( | |
| git tag --list 'v[0-9][0-9][0-9][0-9].[0-9]*.[0-9]*' --sort=-v:refname \ | |
| | grep -v -x "$TAG_NAME" \ | |
| | head -n 1 \ | |
| || true | |
| )" | |
| range_spec="$target_sha" | |
| if [ -n "$previous_tag" ]; then | |
| range_spec="${previous_tag}..${target_sha}" | |
| fi | |
| { | |
| echo "## Changelog" | |
| echo | |
| if [ -n "$previous_tag" ]; then | |
| echo "_Changes since ${previous_tag}_" | |
| else | |
| echo "_Changes up to ${TAG_NAME}_" | |
| fi | |
| echo | |
| git log "$range_spec" --no-merges --pretty='- %s (%h) — %an' | |
| echo | |
| } > GENERATED_CHANGELOG.md | |
| GENERATED_NOTES="$(cat GENERATED_CHANGELOG.md)" | |
| { | |
| echo "## Install" | |
| echo | |
| echo "### cURL (macOS / Linux)" | |
| echo | |
| echo '```bash' | |
| echo 'curl -fsSL https://install.opensre.com | bash' | |
| echo '```' | |
| echo | |
| echo "### cURL (macOS / Linux, latest main build)" | |
| echo | |
| echo '```bash' | |
| echo 'curl -fsSL https://install.opensre.com | bash -s -- --main' | |
| echo '```' | |
| echo | |
| echo "### Homebrew (macOS / Linux)" | |
| echo | |
| echo '```bash' | |
| echo 'brew tap tracer-cloud/tap' | |
| echo 'brew install tracer-cloud/tap/opensre' | |
| echo '```' | |
| echo | |
| echo "### PowerShell (Windows)" | |
| echo | |
| echo '```powershell' | |
| echo 'irm https://install.opensre.com | iex' | |
| echo '```' | |
| echo | |
| echo "### Python" | |
| echo | |
| echo '```bash' | |
| echo "pipx install opensre" | |
| echo '```' | |
| echo | |
| printf '%s\n' "$GENERATED_NOTES" | |
| } > RELEASE_NOTES.md | |
| - name: Create GitHub release | |
| id: github_release | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| TAG_NAME="${{ needs.prepare.outputs.tag_name }}" | |
| default_branch="${{ github.event.repository.default_branch }}" | |
| git fetch origin "$default_branch" --tags --force | |
| target_sha="$(git rev-parse "origin/$default_branch")" | |
| if gh release view "$TAG_NAME" --repo "${{ github.repository }}" >/dev/null 2>&1; then | |
| echo "created=false" >> "$GITHUB_OUTPUT" | |
| echo "Release $TAG_NAME already exists; nothing to do." | |
| exit 0 | |
| fi | |
| VERSION="${TAG_NAME#v}" | |
| latest_flag="--latest=false" | |
| release_title="Latest" | |
| case "$TAG_NAME" in | |
| v[0-9][0-9][0-9][0-9].*) ;; | |
| *) | |
| latest_flag="--latest" | |
| release_title="OpenSRE ${VERSION}" | |
| ;; | |
| esac | |
| gh release create "$TAG_NAME" \ | |
| release-assets/* \ | |
| --repo "${{ github.repository }}" \ | |
| --target "$target_sha" \ | |
| --title "$release_title" \ | |
| --notes-file RELEASE_NOTES.md \ | |
| "$latest_flag" | |
| echo "created=true" >> "$GITHUB_OUTPUT" | |
| - name: Announce on Discord | |
| if: ${{ steps.github_release.outputs.created == 'true' && !github.event.repository.fork && !github.event.repository.private }} | |
| continue-on-error: true | |
| env: | |
| DISCORD_WEBHOOK_URL_UPDATE: ${{ secrets.DISCORD_WEBHOOK_URL_UPDATE }} | |
| DISCORD_RELEASES_ROLE_ID: ${{ secrets.DISCORD_RELEASES_ROLE_ID }} | |
| RELEASE_TAG: ${{ needs.prepare.outputs.tag_name }} | |
| RELEASE_URL: ${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ needs.prepare.outputs.tag_name }} | |
| REPO_STARS: ${{ github.event.repository.stargazers_count }} | |
| REPO_FORKS: ${{ github.event.repository.forks_count }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${DISCORD_WEBHOOK_URL_UPDATE:-}" ]; then | |
| echo "DISCORD_WEBHOOK_URL_UPDATE is not set; skipping Discord announcement." | |
| exit 0 | |
| fi | |
| RELEASE_BODY="$(cat RELEASE_NOTES.md)" | |
| role_mention="" | |
| if [ -n "${DISCORD_RELEASES_ROLE_ID:-}" ]; then | |
| role_mention="<@&${DISCORD_RELEASES_ROLE_ID}>"$'\n' | |
| fi | |
| NOTES="${RELEASE_BODY:-No release notes provided.}" | |
| NOTES="$(printf '%s' "$NOTES" | tr -d '\r')" | |
| MAX_NOTES_LENGTH=1700 | |
| if [ "${#NOTES}" -gt "$MAX_NOTES_LENGTH" ]; then | |
| NOTES="${NOTES:0:$((MAX_NOTES_LENGTH-3))}..." | |
| fi | |
| payload="$(jq -n \ | |
| --arg tag "$RELEASE_TAG" \ | |
| --arg notes "$NOTES" \ | |
| --arg url "$RELEASE_URL" \ | |
| --arg stars "${REPO_STARS:-0}" \ | |
| --arg forks "${REPO_FORKS:-0}" \ | |
| --arg mention "$role_mention" \ | |
| '{ | |
| content: ( | |
| $mention | |
| + "🚀 **opensre `" + $tag + "` is live**\n" | |
| + "🔗 " + $url + "\n" | |
| + "⭐ " + $stars + " stars • 🍴 " + $forks + " forks\n\n" | |
| + "**Release Notes**\n" | |
| + $notes | |
| ) | |
| }' | |
| )" | |
| curl --fail --silent --show-error --max-time 30 \ | |
| -X POST \ | |
| -H "Content-Type: application/json" \ | |
| -d "$payload" \ | |
| "$DISCORD_WEBHOOK_URL_UPDATE" | |
| # - name: Sync Homebrew tap formula | |
| # env: | |
| # HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} | |
| # shell: bash | |
| # run: | | |
| # set -euo pipefail | |
| # | |
| # if [ -z "${HOMEBREW_TAP_GITHUB_TOKEN:-}" ]; then | |
| # echo "::error::HOMEBREW_TAP_GITHUB_TOKEN is required to sync Tracer-Cloud/homebrew-tap." | |
| # exit 1 | |
| # fi | |
| # | |
| # TAG_NAME="${{ needs.prepare.outputs.tag_name }}" | |
| # VERSION="${TAG_NAME#v}" | |
| # ASSET_DIR="release-assets" | |
| # | |
| # linux_x64_sha="$(awk '{print $1}' "${ASSET_DIR}/opensre_${VERSION}_linux-x64.tar.gz.sha256")" | |
| # linux_arm64_sha="$(awk '{print $1}' "${ASSET_DIR}/opensre_${VERSION}_linux-arm64.tar.gz.sha256")" | |
| # darwin_x64_sha="$(awk '{print $1}' "${ASSET_DIR}/opensre_${VERSION}_darwin-x64.tar.gz.sha256")" | |
| # darwin_arm64_sha="$(awk '{print $1}' "${ASSET_DIR}/opensre_${VERSION}_darwin-arm64.tar.gz.sha256")" | |
| # | |
| # tap_dir="$(mktemp -d)/homebrew-tap" | |
| # git clone "https://x-access-token:${HOMEBREW_TAP_GITHUB_TOKEN}@github.com/Tracer-Cloud/homebrew-tap.git" "$tap_dir" | |
| # | |
| # python - "$tap_dir/Formula/opensre.rb" "$VERSION" "$darwin_arm64_sha" "$darwin_x64_sha" "$linux_arm64_sha" "$linux_x64_sha" <<'PY' | |
| # from __future__ import annotations | |
| # | |
| # import re | |
| # import sys | |
| # from pathlib import Path | |
| # | |
| # formula_path = Path(sys.argv[1]) | |
| # version = sys.argv[2] | |
| # darwin_arm64_sha = sys.argv[3] | |
| # darwin_x64_sha = sys.argv[4] | |
| # linux_arm64_sha = sys.argv[5] | |
| # linux_x64_sha = sys.argv[6] | |
| # | |
| # text = formula_path.read_text() | |
| # text = re.sub(r'version "[^"]+"', f'version "{version}"', text, count=1) | |
| # replacements = { | |
| # r'(opensre_#\{version\}_darwin-arm64\.tar\.gz"\n\s*sha256 ")[^"]+(")': darwin_arm64_sha, | |
| # r'(opensre_#\{version\}_darwin-x64\.tar\.gz"\n\s*sha256 ")[^"]+(")': darwin_x64_sha, | |
| # r'(opensre_#\{version\}_linux-arm64\.tar\.gz"\n\s*sha256 ")[^"]+(")': linux_arm64_sha, | |
| # r'(opensre_#\{version\}_linux-x64\.tar\.gz"\n\s*sha256 ")[^"]+(")': linux_x64_sha, | |
| # } | |
| # for pattern, sha in replacements.items(): | |
| # text, count = re.subn(pattern, rf'\g<1>{sha}\g<2>', text, count=1) | |
| # if count != 1: | |
| # raise SystemExit(f"Failed to update checksum with pattern: {pattern}") | |
| # formula_path.write_text(text) | |
| # PY | |
| # | |
| # cd "$tap_dir" | |
| # if git diff --quiet -- Formula/opensre.rb; then | |
| # echo "Homebrew tap formula already up to date." | |
| # exit 0 | |
| # fi | |
| # | |
| # git config user.name "github-actions[bot]" | |
| # git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| # git add Formula/opensre.rb | |
| # git commit -m "chore: update opensre formula to ${VERSION}" | |
| # git push origin HEAD:main | |
| publish-main-release: | |
| if: needs.prepare.outputs.channel == 'main' | |
| runs-on: ubuntu-latest | |
| needs: | |
| - prepare | |
| - build-binaries | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 | |
| - name: Download main release artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: main-release-* | |
| path: main-release-assets | |
| merge-multiple: true | |
| - name: Create release notes | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| short_sha="$(printf '%s' "$GITHUB_SHA" | cut -c1-7)" | |
| built_at="$(date -u +"%Y-%m-%d %H:%M UTC")" | |
| { | |
| echo "## Main build" | |
| echo | |
| echo "Rolling binary build from \`main\`." | |
| echo | |
| echo "- Commit: \`${short_sha}\`" | |
| echo "- Built: ${built_at}" | |
| echo | |
| echo "### Install" | |
| echo | |
| echo "Stable:" | |
| echo | |
| echo | |
| echo '```bash' | |
| echo 'curl -fsSL https://install.opensre.com | bash' | |
| echo '```' | |
| echo | |
| echo "Main:" | |
| echo | |
| echo | |
| echo '```bash' | |
| echo 'curl -fsSL https://install.opensre.com | bash -s -- --main' | |
| echo '```' | |
| echo | |
| echo "Windows stable:" | |
| echo | |
| echo | |
| echo '```powershell' | |
| echo 'irm https://install.opensre.com | iex' | |
| echo '```' | |
| } > MAIN_RELEASE_NOTES.md | |
| - name: Move nightly tag to the latest commit | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git tag -f nightly "$GITHUB_SHA" | |
| git push origin refs/tags/nightly --force | |
| - name: Publish rolling main release | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if gh release view nightly --repo "${{ github.repository }}" >/dev/null 2>&1; then | |
| gh release upload nightly main-release-assets/* --repo "${{ github.repository }}" --clobber | |
| gh release edit nightly \ | |
| --repo "${{ github.repository }}" \ | |
| --title "Main" \ | |
| --notes-file MAIN_RELEASE_NOTES.md | |
| exit 0 | |
| fi | |
| gh release create nightly \ | |
| main-release-assets/* \ | |
| --repo "${{ github.repository }}" \ | |
| --target "$GITHUB_SHA" \ | |
| --title "Main" \ | |
| --notes-file MAIN_RELEASE_NOTES.md \ | |
| --prerelease |