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
19 changes: 19 additions & 0 deletions .github/update-hugo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## Update Hugo to v${LATEST_VERSION}

This is an automated PR to update Hugo from v${CURRENT_VERSION} to v${LATEST_VERSION} (${RELEASE_TYPE} release).

### Changes

- Hugo version updated from ${CURRENT_VERSION} to ${LATEST_VERSION}
${GO_UPDATE_NOTE}

### Hugo release notes

https://github.com/gohugoio/hugo/releases/tag/v${LATEST_VERSION}

### Release checklist

- [ ] Merge this PR
- [ ] Run `nox -s tag -- ${LATEST_VERSION}` locally to create a signed tag
- [ ] Push the tag: `git push origin v${LATEST_VERSION}`
- [ ] Run `nox -s release -- ${LATEST_VERSION}` to create the GitHub release (or create it manually)
111 changes: 111 additions & 0 deletions .github/workflows/update-hugo.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
name: Update Hugo version

on:
schedule:
# Check for new Hugo releases every six hours
- cron: "0 */6 * * *"
workflow_dispatch:

permissions: {}

jobs:
check-and-update:
name: Check for new Hugo release
runs-on: ubuntu-latest
permissions:
contents: write # to update files and push branches
pull-requests: write # to create PR
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
- name: Check for new Hugo release, and apply updates
id: check-hugo-release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail

CURRENT_VERSION=$(grep -oP 'HUGO_VERSION = "\K[0-9.]+' setup.py)
echo "Current version: $CURRENT_VERSION"

# Find the next Hugo release after our current version
NEXT_VERSION=$(gh api repos/gohugoio/hugo/releases --paginate --jq '.[].tag_name' | \
sed 's/^v//' | sort -V | awk -v cur="$CURRENT_VERSION" 'found {print; exit} $0 == cur {found=1}')

if [ -z "$NEXT_VERSION" ]; then
echo "Already up to date (v$CURRENT_VERSION)"
echo "updated=false" >> "$GITHUB_OUTPUT"
exit 0
fi

LATEST_VERSION="$NEXT_VERSION"
echo "Next version to update to: $LATEST_VERSION"

# Check if a PR already exists for this version, and skip if it does
EXISTING_PR=$(gh pr list --search "Update Hugo to v$LATEST_VERSION" --state open --json number --jq '.[0].number // empty')
if [ -n "$EXISTING_PR" ]; then
echo "PR #$EXISTING_PR already exists for v$LATEST_VERSION"
echo "updated=false" >> "$GITHUB_OUTPUT"
exit 0
fi

# Resolve Go version from Hugo's go.mod, and find the latest patch release
GO_MOD_VERSION=$(gh api "repos/gohugoio/hugo/contents/go.mod?ref=v$LATEST_VERSION" --jq '.content' | base64 -d | grep '^go ' | awk '{print $2}')
echo "Hugo v$LATEST_VERSION uses Go $GO_MOD_VERSION"

GO_MAJOR_MINOR=$(echo "$GO_MOD_VERSION" | grep -oP '^\d+\.\d+')
GO_LATEST=$(curl -sL "https://go.dev/dl/?mode=json" | python3 -c "
import json, sys
releases = json.load(sys.stdin)
prefix = 'go${GO_MAJOR_MINOR}'
for r in releases:
if r['version'].startswith(prefix):
print(r['version'].removeprefix('go'))
break
else:
print('${GO_MOD_VERSION}')
")
echo "Latest Go toolchain: $GO_LATEST"

CURRENT_GO=$(grep -oP 'go-version: "\K[0-9.]+' .github/workflows/ci.yml | head -1)
echo "Current Go version in workflows: $CURRENT_GO"

CURRENT_MINOR=$(echo "$CURRENT_VERSION" | cut -d. -f2)
LATEST_MINOR=$(echo "$LATEST_VERSION" | cut -d. -f2)

if [ "$CURRENT_MINOR" != "$LATEST_MINOR" ]; then
RELEASE_TYPE="minor"
else
RELEASE_TYPE="patch"
fi

sed -i "s/HUGO_VERSION = \"$CURRENT_VERSION\"/HUGO_VERSION = \"$LATEST_VERSION\"/" setup.py
sed -i "s/HUGO_VERSION = \"$CURRENT_VERSION\"/HUGO_VERSION = \"$LATEST_VERSION\"/" hugo/cli.py

GO_UPDATE_NOTE=""
if [ "$CURRENT_GO" != "$GO_LATEST" ]; then
sed -i "s/go-version: \"$CURRENT_GO\"/go-version: \"$GO_LATEST\"/g" .github/workflows/ci.yml
sed -i "s/go-version: \"$CURRENT_GO\"/go-version: \"$GO_LATEST\"/g" .github/workflows/cd.yml
sed -i "s/go${CURRENT_GO}\./go${GO_LATEST}./g" .github/workflows/cd.yml
GO_UPDATE_NOTE=$'\n'"- Go toolchain updated from $CURRENT_GO to $GO_LATEST"
fi

echo "updated=true" >> "$GITHUB_OUTPUT"
echo "latest_version=$LATEST_VERSION" >> "$GITHUB_OUTPUT"
echo "current_version=$CURRENT_VERSION" >> "$GITHUB_OUTPUT"
echo "release_type=$RELEASE_TYPE" >> "$GITHUB_OUTPUT"

export CURRENT_VERSION LATEST_VERSION RELEASE_TYPE GO_UPDATE_NOTE
envsubst < .github/update-hugo.md > /tmp/pr-body.md

- name: Create pull request
if: steps.check-hugo-release.outputs.updated == 'true'
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: update-hugo-v${{ steps.check-hugo-release.outputs.latest_version }}
commit-message: "Update Hugo to v${{ steps.check-hugo-release.outputs.latest_version }}"
title: "Update Hugo to v${{ steps.check-hugo-release.outputs.latest_version }}"
body-path: /tmp/pr-body.md
130 changes: 130 additions & 0 deletions noxfile.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
from __future__ import annotations

import re
import string
import subprocess
from pathlib import Path

import nox

DIR = Path(__file__).parent.resolve()
REPO = "agriyakhetarpal/hugo-python-distributions"

nox.options.sessions = ["lint", "tests"]

Expand All @@ -29,3 +33,129 @@ def venv(session: nox.Session) -> None:
session.install(file)
session.run("hugo", "version")
session.run("hugo", "env", "--logLevel", "debug")


def _get_version(session: nox.Session) -> str:
"""Extract version from session posargs or setup.py."""
if session.posargs:
return session.posargs[0].lstrip("v")
content = (DIR / "setup.py").read_text()
match = re.search(r'HUGO_VERSION = "([0-9.]+)"', content)
if not match:
session.error("Could not determine version. Pass it as: nox -s tag -- 0.157.0")
return match.group(1)


def _get_previous_tag(current_tag: str) -> str:
result = subprocess.run(
["git", "tag", "--sort=-v:refname"],
capture_output=True,
text=True,
check=True,
)
tags = result.stdout.strip().splitlines()
for i, t in enumerate(tags):
if t == current_tag and i + 1 < len(tags):
return tags[i + 1]
result = subprocess.run(
["git", "describe", "--tags", "--abbrev=0", f"{current_tag}^"],
capture_output=True,
text=True,
check=True,
)
return result.stdout.strip()


@nox.session(default=False)
def tag(session: nox.Session) -> None:
"""Create a signed, annotated tag for a release.

Usage: nox -s tag -- 0.157.0
"""
version = _get_version(session)
tag_name = f"v{version}"
tag_message = f"hugo-python-distributions, version {version}"

result = subprocess.run(
["git", "tag", "-l", tag_name], capture_output=True, text=True, check=False
)
if tag_name in result.stdout:
session.error(f"Tag {tag_name} already exists")

branch = subprocess.run(
["git", "rev-parse", "--abbrev-ref", "HEAD"],
capture_output=True,
text=True,
check=True,
).stdout.strip()
if branch != "main":
session.warn(f"You are on branch '{branch}', not 'main'. Proceed with caution.")

session.log(f"Creating signed tag {tag_name}: {tag_message}")
session.run(
"git",
"tag",
"-s",
"-a",
tag_name,
"-m",
tag_message,
external=True,
)
session.log(f"Tag {tag_name} created. Push it with: git push origin {tag_name}")


@nox.session(default=False)
def release(session: nox.Session) -> None:
"""Create a GitHub release with formatted release notes.

Usage: nox -s release -- 0.157.0
"""
version = _get_version(session)
tag_name = f"v{version}"

previous_tag = _get_previous_tag(tag_name)
is_patch = int(version.split(".")[2]) > 0
template_name = "patch.md" if is_patch else "stable.md"
template_path = DIR / "release_notes" / template_name
template = string.Template(template_path.read_text())

commit_log = subprocess.run(
[
"git",
"log",
f"{previous_tag}...HEAD",
"--pretty=format:- %s by @%an in %H",
"--no-merges",
],
capture_output=True,
text=True,
check=True,
).stdout.strip()

if commit_log:
changes_section = f"\n## Changes that made it to this release\n\n{commit_log}\n"
else:
changes_section = ""

substitutions = {
"VERSION": version,
"PREVIOUS_TAG": previous_tag,
"CHANGES_SECTION": changes_section,
}

body = template.substitute(substitutions)

session.log(f"Creating GitHub release for {tag_name}")
session.run(
"gh",
"release",
"create",
tag_name,
"--title",
tag_name,
"--notes",
body,
external=True,
)
session.log(f"Release {tag_name} created!")
22 changes: 22 additions & 0 deletions release_notes/patch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## hugo-python-distributions (`Hugo`) v${VERSION}

This release mirrors the [official v${VERSION} patch release for Hugo](https://github.com/gohugoio/hugo/releases/tag/v${VERSION}). For the changes incorporated into Hugo with this release, please refer to the [release notes](https://github.com/gohugoio/hugo/releases/tag/v${VERSION}) for Hugo v${VERSION}.

The release can be installed and used with any of the following commands:

```bash
# either install it
pip install hugo
pipx install hugo
uv pip install hugo
uv tool install hugo

# or run it directly
pipx run hugo
uv run hugo
uvx hugo
```

on Linux (amd64, aarch64, s390x, ppc64le), macOS (amd64, arm64), and Windows (amd64, arm64, i686).
${CHANGES_SECTION}
**Full range of commits**: https://github.com/agriyakhetarpal/hugo-python-distributions/compare/${PREVIOUS_TAG}...v${VERSION}
22 changes: 22 additions & 0 deletions release_notes/stable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## hugo-python-distributions (`Hugo`) v${VERSION}

This release mirrors the [official v${VERSION} release for Hugo](https://github.com/gohugoio/hugo/releases/tag/v${VERSION}). For the changes incorporated into Hugo with this release, please refer to the [release notes](https://github.com/gohugoio/hugo/releases/tag/v${VERSION}) for Hugo v${VERSION}.

The release can be installed and used with any of the following commands:

```bash
# either install it
pip install hugo
pipx install hugo
uv pip install hugo
uv tool install hugo

# or run it directly
pipx run hugo
uv run hugo
uvx hugo
```

on Linux (amd64, aarch64, s390x, ppc64le), macOS (amd64, arm64), and Windows (amd64, arm64, i686).
${CHANGES_SECTION}
**Full range of commits**: https://github.com/agriyakhetarpal/hugo-python-distributions/compare/${PREVIOUS_TAG}...v${VERSION}
Loading