Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions .github/workflows/auto-tag.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Auto Tag on Version Bump

on:
push:
branches: [main]
paths:
- 'library.json'
- 'library.properties'
- 'src/HttpCommon.h'

permissions:
contents: write

jobs:
check-and-tag:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # full history needed for tag comparison

- name: Extract version from library.json
id: version
run: |
VERSION=$(jq -r '.version' library.json)
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "Detected version: ${VERSION}"

- name: Verify version consistency across files
run: |
VERSION="${{ steps.version.outputs.version }}"
V_PROP=$(grep '^version=' library.properties | cut -d'=' -f2)
V_HDR=$(grep '#define ESP_ASYNC_WEB_CLIENT_VERSION' src/HttpCommon.h | sed -E 's/.*"([^"]+)".*/\1/')

MISMATCH=0
if [[ "${VERSION}" != "${V_PROP}" ]]; then
echo "::error::Version mismatch: library.json=${VERSION} vs library.properties=${V_PROP}"
MISMATCH=1
fi
if [[ "${VERSION}" != "${V_HDR}" ]]; then
echo "::error::Version mismatch: library.json=${VERSION} vs HttpCommon.h=${V_HDR}"
MISMATCH=1
fi
if [[ $MISMATCH -ne 0 ]]; then
echo "::error::Fix version mismatches before tagging. Use: scripts/sync-version.sh <version>"
exit 1
fi
echo "All 3 version sources agree: ${VERSION}"

- name: Check if tag already exists
id: tag_check
run: |
TAG="v${{ steps.version.outputs.version }}"
if git rev-parse "refs/tags/${TAG}" >/dev/null 2>&1; then
echo "exists=true" >> "$GITHUB_OUTPUT"
echo "Tag ${TAG} already exists — skipping."
else
echo "exists=false" >> "$GITHUB_OUTPUT"
echo "Tag ${TAG} does not exist — will create."
fi

- name: Create and push tag
if: steps.tag_check.outputs.exists == 'false'
run: |
TAG="v${{ steps.version.outputs.version }}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "${TAG}" -m "Release ${TAG}"
git push origin "${TAG}"
echo "::notice::Created and pushed tag ${TAG}"
106 changes: 96 additions & 10 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ jobs:

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # full history for changelog generation

- name: Set up Python
uses: actions/setup-python@v5
Expand Down Expand Up @@ -48,7 +50,7 @@ jobs:

- name: Create source archive
run: |
VERSION=$(grep '"version"' library.json | sed -E 's/.*"version" *: *"([^"]+)".*/\1/')
VERSION=$(jq -r '.version' library.json)
ARCHIVE="ESPAsyncWebClient-${VERSION}.zip"
zip -r "$ARCHIVE" src examples README.md LICENSE library.json library.properties
echo "Created $ARCHIVE"
Expand All @@ -59,16 +61,100 @@ jobs:
name: source-archive
path: ${{ env.ARCHIVE }}

- name: Extract changelog for this version
id: changelog
run: |
VERSION=$(jq -r '.version' library.json)
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"

# Extract the section for this version from CHANGELOG.md
# Matches everything between "## [X.Y.Z]" and the next "## [" heading
NOTES=""
if [[ -f CHANGELOG.md ]]; then
NOTES=$(awk -v ver="${VERSION}" '
BEGIN { found=0 }
/^## \[/ {
if (found) exit
if ($0 ~ "\\[" ver "\\]") { found=1; next }
}
found { print }
' CHANGELOG.md)
fi

if [[ -z "${NOTES}" ]]; then
echo "No CHANGELOG entry found for ${VERSION}, generating from commits..."
# Get the previous tag
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
if [[ -n "${PREV_TAG}" ]]; then
NOTES=$(git log "${PREV_TAG}..HEAD" --pretty=format:"- %s (%h)" --no-merges)
else
NOTES=$(git log --pretty=format:"- %s (%h)" --no-merges -20)
fi
fi

# Write to file to preserve multiline
echo "${NOTES}" > /tmp/release_notes.md

- name: Generate AI-style release summary
id: summary
run: |
VERSION="${{ steps.changelog.outputs.version }}"
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")

# Build a structured release body
{
echo "# ESPAsyncWebClient v${VERSION}"
echo ""

# Changelog section
echo "## What's Changed"
cat /tmp/release_notes.md
echo ""

# Auto-generated commit summary by category
if [[ -n "${PREV_TAG}" ]]; then
echo "## Commit Summary"
echo ""

# Fixes
FIXES=$(git log "${PREV_TAG}..HEAD" --pretty=format:"- %s (%h)" --no-merges --grep="^[Ff]ix" || true)
if [[ -n "${FIXES}" ]]; then
echo "### Bug Fixes"
echo "${FIXES}"
echo ""
fi

# Features
FEATURES=$(git log "${PREV_TAG}..HEAD" --pretty=format:"- %s (%h)" --no-merges --grep="^[Ff]eat" || true)
if [[ -n "${FEATURES}" ]]; then
echo "### New Features"
echo "${FEATURES}"
echo ""
fi

# Other commits
OTHER=$(git log "${PREV_TAG}..HEAD" --pretty=format:"- %s (%h)" --no-merges --invert-grep --grep="^[Ff]ix" --grep="^[Ff]eat" || true)
if [[ -n "${OTHER}" ]]; then
echo "### Other Changes"
echo "${OTHER}"
echo ""
fi

echo "**Full diff**: https://github.com/${{ github.repository }}/compare/${PREV_TAG}...v${VERSION}"
echo ""
fi

# Stats
echo "## Verification"
echo "- :white_check_mark: All examples built successfully"
echo "- :white_check_mark: Version metadata consistent across library.json, library.properties, and HttpCommon.h"
echo "- :package: Source archive attached"
} > /tmp/full_release_notes.md

- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: ${{ env.ARCHIVE }}
name: Release ${{ github.ref }}
body: |
## Changes in this release
See the Git diff for this tag or the Releases page for details.

## Verification
- Examples built successfully
- Version metadata consistent
- Source archive attached
name: "v${{ steps.changelog.outputs.version }}"
body_path: /tmp/full_release_notes.md
generate_release_notes: true
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ESPAsyncWebClient",
"version": "2.1.0",
"version": "2.1.1",
"description": "Asynchronous HTTP client library for ESP32 ",
"keywords": [
"http",
Expand Down
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=ESPAsyncWebClient
version=2.1.0
version=2.1.1
author=playmiel
maintainer=playmiel
sentence=Asynchronous HTTP client library for ESP32 microcontrollers
Expand Down
108 changes: 108 additions & 0 deletions scripts/sync-version.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#!/usr/bin/env bash
# sync-version.sh — Update version in all 3 source-of-truth files at once.
# Usage: ./scripts/sync-version.sh 2.2.0
set -euo pipefail

if [[ $# -ne 1 ]]; then
echo "Usage: $0 <version>"
echo "Example: $0 2.2.0"
exit 1
fi

NEW_VERSION="$1"

# Validate semver format
if [[ ! "${NEW_VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Error: '${NEW_VERSION}' is not a valid semver (expected X.Y.Z)"
exit 1
fi

echo "Syncing version to ${NEW_VERSION} across all files..."

# 1. library.json
if [[ -f library.json ]]; then
python3 - <<'PY' "${NEW_VERSION}"
import json
import sys

new_version = sys.argv[1]
path = "library.json"

with open(path, "r", encoding="utf-8") as f:
data = json.load(f)

if not isinstance(data, dict) or "version" not in data:
raise SystemExit("library.json missing top-level 'version' field")

data["version"] = new_version

with open(path, "w", encoding="utf-8", newline="\n") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
f.write("\n")
PY
echo " ✓ library.json"
else
echo " ✗ library.json not found (aborting)" >&2
exit 1
fi

# 2. library.properties
if [[ -f library.properties ]]; then
python3 - <<'PY' "${NEW_VERSION}"
import re
import sys

new_version = sys.argv[1]
path = "library.properties"

with open(path, "r", encoding="utf-8") as f:
text = f.read()

updated, count = re.subn(r"^version=.*$", f"version={new_version}", text, flags=re.MULTILINE)
if count == 0:
raise SystemExit("library.properties missing 'version=' line")

with open(path, "w", encoding="utf-8", newline="\n") as f:
f.write(updated)
PY
echo " ✓ library.properties"
else
echo " ✗ library.properties not found (aborting)" >&2
exit 1
fi

# 3. src/HttpCommon.h
if [[ -f src/HttpCommon.h ]]; then
python3 - <<'PY' "${NEW_VERSION}"
import re
import sys

new_version = sys.argv[1]
path = "src/HttpCommon.h"

with open(path, "r", encoding="utf-8") as f:
text = f.read()

pattern = r'#define ESP_ASYNC_WEB_CLIENT_VERSION "[^"]+"'
replacement = f'#define ESP_ASYNC_WEB_CLIENT_VERSION "{new_version}"'
updated, count = re.subn(pattern, replacement, text)
if count == 0:
raise SystemExit("src/HttpCommon.h missing ESP_ASYNC_WEB_CLIENT_VERSION define")

with open(path, "w", encoding="utf-8", newline="\n") as f:
f.write(updated)
PY
echo " ✓ src/HttpCommon.h"
else
echo " ✗ src/HttpCommon.h not found (aborting)" >&2
exit 1
fi

echo ""
echo "Done! Version is now ${NEW_VERSION} everywhere."
echo ""
echo "Next steps:"
echo " 1. Update CHANGELOG.md with your changes under [${NEW_VERSION}]"
echo " 2. Commit and push to main"
echo " 3. The auto-tag workflow will create the tag v${NEW_VERSION} automatically"
echo " 4. The release workflow will generate the release with auto-generated notes"
3 changes: 1 addition & 2 deletions scripts/verify-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ pass() { echo "[OK] $1"; }
if [[ ! -f library.json ]]; then fail "library.json missing"; fi
if [[ ! -f library.properties ]]; then fail "library.properties missing"; fi

# Extract the first occurrence of a JSON key named "version" (top-level package version)
VERSION_JSON=$(grep -m1 '"version"' library.json | sed -E 's/.*"version" *: *"([^"]+)".*/\1/')
VERSION_JSON=$(jq -r '.version' library.json)
VERSION_PROP=$(grep '^version=' library.properties | cut -d'=' -f2)

[[ -n "${VERSION_JSON}" ]] || fail "Could not extract version from library.json"
Expand Down
Loading
Loading