Skip to content

Generate release summary #44

Generate release summary

Generate release summary #44

name: "Generate release summary"
on:
workflow_dispatch:
inputs:
scope:
description: "Comma-separated repos (owner/repo) OR 'org:armbian'"
required: false
default: "org:armbian"
tz:
description: "Timezone (IANA)"
required: false
default: "Europe/Ljubljana"
period:
description: "Digest period (weekly by default). Choose 'monthly' or 'quarterly' for larger windows."
required: false
default: "weekly"
publish_release:
description: "Publish a GitHub release (true/false)."
required: false
default: "true"
schedule:
- cron: "5 6 * * MON" # weekly, Mondays 06:05 UTC
permissions:
contents: read
jobs:
pr-digest:
name: "Get PR's"
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.AI_MODELS }}
SCOPE: ${{ inputs.scope || 'armbian/build,armbian/configng' }}
TZ: ${{ inputs.tz || 'Europe/Ljubljana' }}
PERIOD: ${{ inputs.period || 'weekly' }}
RELEASE_ENABLED: ${{ inputs.publish_release || 'true' }}
steps:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.10'
- name: Install OpenAI SDK
run: pip install 'openai>=1.0.0'
- name: Ensure dependencies
run: |
sudo apt-get update
sudo apt-get install -y jq
- name: Compute time window (UTC) from period
id: when
shell: bash
run: |
set -euo pipefail
export PERIOD="${PERIOD:-weekly}"
UNTIL_UTC="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
if [[ "$PERIOD" == "monthly" ]]; then
SINCE_UTC="$(date -u -d '1 month ago' +%Y-%m-%dT%H:%M:%SZ)"
LABEL="Monthly digest"
elif [[ "$PERIOD" == "quarterly" ]]; then
SINCE_UTC="$(date -u -d '3 months ago' +%Y-%m-%dT%H:%M:%SZ)"
LABEL="Quarterly digest"
else
SINCE_UTC="$(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ)"
LABEL="Weekly digest"
fi
echo "UNTIL_UTC=$UNTIL_UTC" >> "$GITHUB_ENV"
echo "SINCE_UTC=$SINCE_UTC" >> "$GITHUB_ENV"
echo "LABEL=$LABEL" >> "$GITHUB_ENV"
- name: Prepare workspace
run: mkdir -p out
- name: Write helper script
shell: bash
run: |
cat > pr_fetch.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
scope="${1:?}" # "org:armbian" or "owner/repo,owner/repo2"
since="${2:?}" # ISO UTC
until="${3:?}" # ISO UTC
outfile="${4:?}" # path
collect() {
local q="$1"
gh api -X GET search/issues \
-f q="$q" \
-f sort="updated" -f order="desc" \
-f per_page=100 --paginate \
-q '.items[] | {number: .number, repo: (.repository_url | sub("^.*/repos/";""))}'
}
tmp="$(mktemp)"
if [[ "$scope" == org:* ]]; then
org="${scope#org:}"
q="org:${org} is:pr is:merged merged:${since}..${until} archived:false"
collect "$q" > "$tmp"
else
IFS=',' read -r -a repos <<< "$scope"
: > "$tmp"
for r in "${repos[@]}"; do
r_trim="$(echo "$r" | xargs)"
q="repo:${r_trim} is:pr is:merged merged:${since}..${until}"
collect "$q" >> "$tmp"
done
fi
# De-dup and enrich, then output TSV sorted alphabetically by title (case-insensitive)
jq -s 'unique_by(.repo + "|" + (.number|tostring))' "$tmp" | jq -c '.[]' | \
while read -r row; do
repo=$(echo "$row" | jq -r .repo)
num=$(echo "$row" | jq -r .number)
pr=$(gh api "repos/${repo}/pulls/${num}")
title=$(jq -r .title <<<"$pr")
author=$(jq -r .user.login <<<"$pr")
pr_url=$(jq -r .html_url <<<"$pr")
# Skip bots and worker accounts
if [[ "$author" == "github-actions[bot]" || "$author" == "dependabot[bot]" || "$author" == "armbianworker" || "$author" == "armbianworker[bot]" ]]; then
continue
fi
# Output tab-separated: title, author, repo, pr_number, pr_url
printf "%s\t%s\t%s\t%s\t%s\n" "$title" "$author" "$repo" "$num" "$pr_url"
done | LC_ALL=C sort -f -t $'\t' -k1,1 > "$outfile"
EOF
chmod +x pr_fetch.sh
- name: Fetch merged PRs for selected period
run: ./pr_fetch.sh "$SCOPE" "$SINCE_UTC" "$UNTIL_UTC" out/pr-digest.tsv
- name: "Write Markdown digest"
shell: bash
run: |
echo "" >> summary.md
echo "## " >> summary.md
if [[ ! -s out/pr-digest.tsv ]]; then
echo "_No merged PRs in this period._" >> summary.md
else
while IFS=$'\t' read -r title author repo num pr_url; do
echo "* ${title}. by @${author} in [${repo}#${num}](${pr_url})" >> summary.md
done < out/pr-digest.tsv
fi
echo "# " >> summary.md
echo "<a href='https://blog.armbian.com/#/portal/signup' target='_blank'>
<img src='https://img.shields.io/badge/Subscribe-blog.armbian.com-red?style=for-the-badge&logo=rss' alt='Subscribe to Blog'/></a>" >> summary.md
echo "<p><br>Stay up to date with the latest Armbian news, development highlights, and tips — delivered straight to your inbox." >> summary.md
- name: Upload raw data (artifacts)
uses: actions/upload-artifact@v4
with:
name: pr-digest
path: out/
if-no-files-found: warn
- name: "Checkout OS repository to get version"
uses: actions/checkout@v5
with:
repository: armbian/os
fetch-depth: 0
clean: false
path: os
- name: "Read version from nightly or stable based on period"
shell: bash
run: |
set -euo pipefail
if [[ "${PERIOD:-weekly}" == "monthly" || "${PERIOD:-weekly}" == "quarterly" ]]; then
FILE="os/stable.json"
else
FILE="os/nightly.json"
fi
if [[ ! -f "$FILE" ]]; then
echo "::error file=$FILE::Version file not found"
exit 1
fi
VERSION=$(jq -r '.version' "$FILE")
echo "VERSION_OVERRIDE=${VERSION}" >> "$GITHUB_ENV"
- name: "Write a short journalistic intro with AI"
if: ${{ env.PERIOD == 'weekly' }}
run: |
set -euo pipefail
python3 <<'PY'
import os
from openai import OpenAI
token = os.environ["GITHUB_TOKEN"]
label = os.environ["LABEL"]
with open("summary.md", "r", encoding="utf-8") as f:
content = f.read().strip()
client = OpenAI(base_url="https://models.github.ai/inference", api_key=token)
prompt = (
"Read the following Markdown changelog and write a short journalistic intro paragraph (5–7 sentences) "
"that summarizes the main activity. Be concise, professional, and factual. Output only the intro.\n\n"
f"{content}"
)
resp = client.chat.completions.create(
model="openai/gpt-4.1",
messages=[
{"role": "system", "content": "You are a " + label + "digest editor."},
{"role": "user", "content": prompt},
],
temperature=0.3,
top_p=1.0,
)
intro = resp.choices[0].message.content.strip()
with open("summary.md", "w", encoding="utf-8") as f:
f.write(intro + "\n\n" + content)
print("Prepended AI intro:")
print(intro)
PY
cat summary.md >> "$GITHUB_STEP_SUMMARY"
- uses: ncipollo/release-action@v1
if: ${{ env.RELEASE_ENABLED == 'true' }}
with:
owner: 'armbian'
repo: 'build'
tag: "v${{ env.VERSION_OVERRIDE }}"
name: "${{ env.LABEL }}"
generateReleaseNotes: "false"
prerelease: "false"
makeLatest: "true"
bodyFile: "summary.md"
allowUpdates: "true"
skipIfReleaseExists: "true"
token: ${{ secrets.RELEASE_TOKEN }}