Files
tracer-cloud--opensre/.github/workflows/release.yml
T
wehub-resource-sync 4b6817381b
Benchmark image — build + push to ECR (any adapter) / build + push (push) Waiting to run
CI / quality (ubuntu-latest) (push) Waiting to run
CI / test (tools-runtime) (push) Waiting to run
CI / test (e2e-general) (push) Waiting to run
CI / test (cli-runtime) (push) Waiting to run
CI / test (e2e-provider-and-openclaw) (push) Waiting to run
CI / test (integrations-and-misc) (push) Waiting to run
CI / coverage-report (push) Blocked by required conditions
CI / test-kubernetes (push) Waiting to run
CI / should-run-thorough (push) Waiting to run
CI / test-thorough (cloudwatch-demo) (push) Blocked by required conditions
CI / test-thorough (flink-ecs) (push) Blocked by required conditions
CI / test-thorough (upstream-lambda) (push) Blocked by required conditions
CI / test-thorough (prefect-ecs-fargate) (push) Blocked by required conditions
CodeQL / Analyze (python) (push) Waiting to run
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Blocked by required conditions
Release / publish-release (push) Blocked by required conditions
Release / publish-main-release (push) Blocked by required conditions
Release / prepare (push) Waiting to run
Release / verify (push) Blocked by required conditions
Release / build-python-dist (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Blocked by required conditions
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Waiting to run
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Waiting to run
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Waiting to run
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:45 +08:00

679 lines
25 KiB
YAML

name: Release
on:
push:
branches: [main]
paths-ignore:
- "docs/**"
- "tests/benchmarks/cloudopsbench/infra/**"
- "platform/deployment/install-proxy/**"
- "tests/e2e/kubernetes/helm/scripts/**"
- "tests/**"
- "**/*.md"
- "**/*.mdx"
- ".claude/**"
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. v0.1.2026.6.26 or v0.1). Ignored for the main channel."
required: false
type: string
permissions:
contents: write
actions: read
models: 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:
if: github.repository == 'Tracer-Cloud/opensre'
runs-on: ubuntu-latest
outputs:
channel: ${{ steps.meta.outputs.channel }}
tag_name: ${{ steps.meta.outputs.tag_name }}
version_name: ${{ steps.meta.outputs.version_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
year="$(date -u +%Y)"
month="$(date -u +%-m)"
day="$(date -u +%-d)"
short_sha="${GITHUB_SHA:0:7}"
main_version="0.1.${year}.${month}.${day}+main.${short_sha}"
echo "channel=main" >> "$GITHUB_OUTPUT"
echo "tag_name=main-build" >> "$GITHUB_OUTPUT"
echo "version_name=${main_version}" >> "$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)"
# Keep the v0.1 prefix so the tag makes clear the product is still
# v0.1, while the date stays useful (e.g. v0.1.2026.6.26).
tag_name="v0.1.${year}.${month}.${day}"
fi
echo "channel=release" >> "$GITHUB_OUTPUT"
echo "tag_name=${tag_name}" >> "$GITHUB_OUTPUT"
echo "version_name=${tag_name#v}" >> "$GITHUB_OUTPUT"
verify:
runs-on: ubuntu-latest
needs: prepare
if: github.event_name != 'push'
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' && needs.verify.result == 'success'
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 platform/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:
if: always() && (needs.prepare.outputs.channel == 'main' || needs.verify.result == 'success')
needs: [verify, prepare]
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-22.04
target: linux-x64
binary_name: opensre
archive_ext: tar.gz
pyinstaller_mode: onedir
- runner: ubuntu-22.04-arm
target: linux-arm64
binary_name: opensre
archive_ext: tar.gz
pyinstaller_mode: onedir
- runner: macos-15-intel
target: darwin-x64
binary_name: opensre
archive_ext: tar.gz
pyinstaller_mode: onedir
- runner: macos-latest
target: darwin-arm64
binary_name: opensre
archive_ext: tar.gz
pyinstaller_mode: onedir
- runner: windows-latest
target: windows-x64
binary_name: opensre.exe
archive_ext: zip
pyinstaller_mode: onefile
# 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 }}
VERSION_NAME: ${{ needs.prepare.outputs.version_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 binary version
shell: bash
run: python platform/packaging/sync_release_version.py --version "$VERSION_NAME"
- name: Install binary build dependencies
shell: bash
run: uv sync --frozen --extra release-binary
- name: Stage stdlib platform module for bundling
shell: bash
run: |
set -euo pipefail
# The first-party ``platform`` package shadows the stdlib ``platform``
# module. PyInstaller does not lay out the stdlib as loose ``.py`` files,
# so bundle a copy of the genuine module that platform/__init__.py can
# load from ``sys._MEIPASS`` at runtime (see _FROZEN_STDLIB_DIR).
rm -rf .stdlib_vendor
mkdir -p .stdlib_vendor
uv run python -c "import os, shutil, sysconfig; src = os.path.join(sysconfig.get_path('stdlib'), 'platform.py'); shutil.copy(src, os.path.join('.stdlib_vendor', 'platform.py')); print('Staged stdlib platform from ' + src)"
- name: Build binary
run: >-
uv run pyinstaller surfaces/cli/__main__.py
--name opensre
--${{ matrix.pyinstaller_mode }}
--clean
--noconfirm
--collect-data surfaces.cli
--collect-data config
--copy-metadata opensre
--collect-data litellm
--hidden-import tiktoken_ext
--hidden-import tiktoken_ext.openai_public
--collect-submodules integrations
--collect-submodules surfaces.interactive_shell
--collect-submodules tools
--add-data "platform:platform"
--add-data ".stdlib_vendor:_opensre_stdlib_platform"
- name: Smoke test binary (Unix)
if: runner.os != 'Windows'
shell: bash
run: |
set -uo pipefail
# Capture the exit status explicitly so a crashing binary still prints
# its stdout/stderr (under `set -e` the failed substitution aborted the
# step before the output was ever shown, hiding the real error).
# onefile builds place the executable at ./dist/<name>; onedir builds
# produce a ./dist/opensre/ directory containing the executable. A bare
# `-x` test matches the onedir directory too (dirs carry the search
# bit), so require a regular file before treating it as the binary.
if [ -f "./dist/${{ matrix.binary_name }}" ] && [ -x "./dist/${{ matrix.binary_name }}" ]; then
BINARY_PATH="./dist/${{ matrix.binary_name }}"
else
BINARY_PATH="./dist/opensre/${{ matrix.binary_name }}"
fi
set +e
VERSION_OUTPUT="$("$BINARY_PATH" --version 2>&1)"
VERSION_STATUS=$?
set -e
printf '%s\n' "$VERSION_OUTPUT"
if [ "$VERSION_STATUS" -ne 0 ]; then
printf '::error::%s --version exited with status %s\n' "${{ matrix.binary_name }}" "$VERSION_STATUS" >&2
exit "$VERSION_STATUS"
fi
case "$VERSION_OUTPUT" in
*"$VERSION_NAME"*) ;;
*)
printf 'Binary version mismatch: expected %s but saw %s\n' "$VERSION_NAME" "$VERSION_OUTPUT" >&2
exit 1
;;
esac
"$BINARY_PATH" -h >/dev/null
LITELLM_DATA="./dist/opensre/_internal/litellm/model_prices_and_context_window_backup.json"
if [ ! -f "$LITELLM_DATA" ]; then
echo "LiteLLM package data missing from Unix onedir bundle: ${LITELLM_DATA}" >&2
exit 1
fi
- name: Check Linux binary glibc compatibility
if: runner.os == 'Linux'
shell: bash
run: |
set -euo pipefail
max_glibc="$(
find dist/opensre -type f \( -name opensre -o -name '*.so' -o -name '*.so.*' \) -print0 \
| xargs -0 strings 2>/dev/null \
| grep -Eo 'GLIBC_[0-9]+\.[0-9]+(\.[0-9]+)?' \
| sort -Vu \
| tail -n 1 \
|| true
)"
echo "Max required glibc symbol: ${max_glibc:-none}"
if [ -n "$max_glibc" ] && [ "$(printf '%s\n' "$max_glibc" GLIBC_2.35 | sort -V | tail -n 1)" != "GLIBC_2.35" ]; then
echo "Linux binary requires ${max_glibc}; pin the Linux runner back to Ubuntu 22.04 or lower dependency wheel requirements." >&2
exit 1
fi
- name: Smoke test binary (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$versionOutput = & ".\dist\${{ matrix.binary_name }}" --version 2>&1 | Out-String
$versionText = $versionOutput.Trim()
Write-Host $versionText
$expectedVersion = $env:VERSION_NAME
if ($versionText -notmatch [regex]::Escape($expectedVersion)) {
throw "Binary version mismatch. Expected '$expectedVersion' but saw '$versionText'."
}
& ".\dist\${{ matrix.binary_name }}" -h | Out-Null
- name: Package binary archive (Unix)
if: runner.os != 'Windows'
shell: bash
run: |
set -euo pipefail
if [ "$RELEASE_CHANNEL" = "release" ]; then
ASSET_BASENAME="opensre_${TAG_NAME#v}_${{ matrix.target }}"
else
ASSET_BASENAME="opensre_main_${{ matrix.target }}"
fi
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 (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
if ($env:RELEASE_CHANNEL -eq "release") {
$assetBaseName = "opensre_$($env:TAG_NAME.TrimStart('v'))_${{ matrix.target }}"
} else {
$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
uses: actions/upload-artifact@v4
with:
name: ${{ env.RELEASE_CHANNEL == 'main' && 'main-' || '' }}release-${{ matrix.target }}
path: |
opensre_*_${{ matrix.target }}.${{ matrix.archive_ext }}
opensre_*_${{ 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: Resolve release context
id: release_ctx
env:
TAG_NAME: ${{ needs.prepare.outputs.tag_name }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
set -euo pipefail
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 'v0.1.[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
{
printf 'target_sha=%s\n' "$target_sha"
printf 'previous_tag=%s\n' "$previous_tag"
printf 'range_spec=%s\n' "$range_spec"
} >> "$GITHUB_OUTPUT"
- name: Create release notes
env:
TAG_NAME: ${{ needs.prepare.outputs.tag_name }}
RANGE_SPEC: ${{ steps.release_ctx.outputs.range_spec }}
PREVIOUS_TAG: ${{ steps.release_ctx.outputs.previous_tag }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
set -euo pipefail
{
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 -n 100 --pretty='- %s (%h) — %an'
echo
} > GENERATED_CHANGELOG.md
# Summarize commits into prose via GitHub Models (gpt-4o-mini).
# Falls back to raw changelog if the API is unavailable.
raw_commits="$(git log "$RANGE_SPEC" --no-merges --pretty='%s' | head -40 || true)"
if [ -n "$raw_commits" ]; then
api_body="$(jq -n --arg commits "$raw_commits" '{
model: "gpt-4o-mini",
messages: [
{
role: "system",
content: "You are writing a Discord release announcement for an open-source SRE CLI tool called opensre. Summarize the git commits into 2-4 short, punchy prose sentences — no bullet points, no headers, no markdown. Write in present tense, active voice. Focus on what users will notice: new features, fixes, performance, integrations. Be specific but concise."
},
{role: "user", content: ("Commits:\n" + $commits)}
],
max_tokens: 300,
temperature: 0.4
}')"
curl -sS --max-time 20 \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Content-Type: application/json" \
"https://models.inference.ai.azure.com/chat/completions" \
-d "$api_body" 2>/dev/null \
| jq -r '.choices[0].message.content // empty' 2>/dev/null \
| tr -d '\r' > DISCORD_NARRATIVE.md || true
if [ -s DISCORD_NARRATIVE.md ]; then
echo "LLM narrative generated ($(wc -c < DISCORD_NARRATIVE.md) bytes)."
else
echo "LLM summary unavailable; Discord will use raw changelog."
rm -f DISCORD_NARRATIVE.md
fi
fi
CB='```'
{
printf '## Install\n\n'
printf '### cURL (macOS / Linux)\n\n%sbash\ncurl -fsSL https://install.opensre.com | bash -s -- --release --version %s\n%s\n\n' "$CB" "${TAG_NAME#v}" "$CB"
printf '### Homebrew (macOS / Linux)\n\n%sbash\nbrew tap tracer-cloud/tap\nbrew install tracer-cloud/tap/opensre\n%s\n\n' "$CB" "$CB"
printf '### PowerShell (Windows)\n\n%spowershell\n$env:OPENSRE_INSTALL_CHANNEL="release"; $env:OPENSRE_VERSION="%s"; irm https://install.opensre.com | iex\n%s\n\n' "$CB" "${TAG_NAME#v}" "$CB"
printf '### Python\n\n%sbash\npipx install opensre\n%s\n\n' "$CB" "$CB"
} > RELEASE_NOTES.md
cat GENERATED_CHANGELOG.md >> RELEASE_NOTES.md
- name: Create GitHub release
id: github_release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG_NAME: ${{ needs.prepare.outputs.tag_name }}
TARGET_SHA: ${{ steps.release_ctx.outputs.target_sha }}
shell: bash
run: |
set -euo pipefail
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}"
# Every release-channel publish is the newest stable build at this
# point, so mark it as GitHub's Latest. The title keeps the full
# version (e.g. "OpenSRE 0.1.2026.6.26") so the v0.1 line is clear.
gh release create "$TAG_NAME" \
release-assets/* \
--repo "${{ github.repository }}" \
--target "$TARGET_SHA" \
--title "OpenSRE ${VERSION}" \
--notes-file RELEASE_NOTES.md \
--latest
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 }}
DISCORD_RELEASE_LOGO_EMOJI: ${{ secrets.DISCORD_RELEASE_LOGO_EMOJI }}
DISCORD_RELEASE_LOGO_URL: ${{ secrets.DISCORD_RELEASE_LOGO_URL }}
RELEASE_TAG: ${{ needs.prepare.outputs.tag_name }}
RELEASE_URL: ${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ needs.prepare.outputs.tag_name }}
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
# Prefer LLM-generated narrative; fall back to raw changelog.
if [ -f DISCORD_NARRATIVE.md ] && [ -s DISCORD_NARRATIVE.md ]; then
changelog_file="DISCORD_NARRATIVE.md"
elif [ -f GENERATED_CHANGELOG.md ]; then
changelog_file="GENERATED_CHANGELOG.md"
else
echo "No changelog available." > /tmp/discord_fallback.md
changelog_file="/tmp/discord_fallback.md"
fi
# Use an external Python script to build the JSON payload.
# This avoids jq issues with multiline LLM output containing control characters.
payload="$(CHANGELOG_FILE="$changelog_file" python3 .github/scripts/build-discord-payload.py)"
curl --fail -sS --max-time 30 -X POST -H "Content-Type: application/json" \
-d "$payload" "$DISCORD_WEBHOOK_URL_UPDATE"
- name: Sync Homebrew tap formula
if: steps.github_release.outputs.created == 'true'
continue-on-error: true
env:
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
VERSION: ${{ needs.prepare.outputs.tag_name }}
ASSET_DIR: release-assets
shell: bash
run: |
set -euo pipefail
if [ -z "${HOMEBREW_TAP_GITHUB_TOKEN:-}" ]; then
echo "HOMEBREW_TAP_GITHUB_TOKEN is not set; skipping Homebrew tap sync."
exit 0
fi
export VERSION="${VERSION#v}"
bash .github/scripts/sync-homebrew-tap-formula.sh
publish-main-release:
if: always() && needs.prepare.outputs.channel == 'main' && needs.build-binaries.result == 'success'
runs-on: ubuntu-latest
needs:
- prepare
- build-binaries
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
# The rolling `main-build` tag is force-moved to each new main commit
# below. Whenever main has advanced past a commit that touches
# .github/workflows/**, the default GITHUB_TOKEN (the
# github-actions[bot] GitHub App) is refused with "refusing to allow a
# GitHub App to create or update workflow ... without `workflows`
# permission" — and that scope cannot be granted via the permissions
# block. MAIN_BUILD_TAG_TOKEN must be a token that carries workflow
# write access (classic PAT with `repo` + `workflow`, a fine-grained
# PAT with Contents + Workflows: write, or a GitHub App token). It is
# persisted so the tag push below authenticates with it. Falls back to
# GITHUB_TOKEN so the rest of the job still runs if the secret is unset
# (the tag push will then fail as before until the secret is added).
token: ${{ secrets.MAIN_BUILD_TAG_TOKEN || secrets.GITHUB_TOKEN }}
persist-credentials: true
- 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
env:
VERSION_NAME: ${{ needs.prepare.outputs.version_name }}
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")"
CB='```'
{
printf '## Main build\n\nRolling binary build from `main`.\n\n'
printf -- '- Version: `%s`\n- Commit: `%s`\n- Built: %s\n\n' "$VERSION_NAME" "$short_sha" "$built_at"
printf '### Install\n\nmacOS / Linux:\n\n%sbash\ncurl -fsSL https://install.opensre.com | bash\n%s\n\n' "$CB" "$CB"
printf 'Equivalent explicit main channel:\n\n%sbash\ncurl -fsSL https://install.opensre.com | bash -s -- --main\n%s\n\n' "$CB" "$CB"
printf 'Windows:\n\n%spowershell\nirm https://install.opensre.com | iex\n%s\n' "$CB" "$CB"
} > MAIN_RELEASE_NOTES.md
- name: Move main build 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 "${{ needs.prepare.outputs.tag_name }}" "$GITHUB_SHA"
git push origin "refs/tags/${{ needs.prepare.outputs.tag_name }}" --force
- name: Publish rolling main release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
set -euo pipefail
tag_name="${{ needs.prepare.outputs.tag_name }}"
if gh release view "$tag_name" --repo "${{ github.repository }}" >/dev/null 2>&1; then
gh release upload "$tag_name" main-release-assets/* --repo "${{ github.repository }}" --clobber
gh release edit "$tag_name" \
--repo "${{ github.repository }}" \
--title "Main" \
--notes-file MAIN_RELEASE_NOTES.md
exit 0
fi
gh release create "$tag_name" \
main-release-assets/* \
--repo "${{ github.repository }}" \
--target "$GITHUB_SHA" \
--title "Main" \
--notes-file MAIN_RELEASE_NOTES.md \
--prerelease