add changelog tooling
All checks were successful
Build and Deploy Staging / build (push) Successful in 44s
Build and Deploy Antora Docs / build (push) Successful in 36s
Tests / test (push) Successful in 27s
Build and Deploy Staging / deploy (push) Successful in 6s
Build and Deploy Antora Docs / deploy (push) Successful in 4s
All checks were successful
Build and Deploy Staging / build (push) Successful in 44s
Build and Deploy Antora Docs / build (push) Successful in 36s
Tests / test (push) Successful in 27s
Build and Deploy Staging / deploy (push) Successful in 6s
Build and Deploy Antora Docs / deploy (push) Successful in 4s
This commit is contained in:
parent
6ed6a8f4c3
commit
96469c0212
6 changed files with 415 additions and 3 deletions
|
|
@ -5,6 +5,7 @@
|
|||
** xref:web-portal-admin.adoc[Admin]
|
||||
** xref:web-portal-controlplanes.adoc[Control-Planes]
|
||||
** xref:web-portal-billingentity.adoc[Billing Entities]
|
||||
** xref:web-portal-changelog.adoc[Changelog]
|
||||
|
||||
* xref:web-portal-planning.adoc[]
|
||||
** xref:user-stories.adoc[]
|
||||
|
|
@ -16,4 +17,4 @@
|
|||
** xref:api.adoc[]
|
||||
|
||||
* Cloud Providers
|
||||
** xref:exoscale-osb.adoc[]
|
||||
** xref:exoscale-osb.adoc[]
|
||||
|
|
|
|||
20
docs/modules/ROOT/pages/web-portal-changelog.adoc
Normal file
20
docs/modules/ROOT/pages/web-portal-changelog.adoc
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
= Portal Changelog
|
||||
|
||||
== 2025.10.27-0
|
||||
|
||||
=== UI/UX
|
||||
* Restrict user input to more sensible ranges (link:https://servala.app.codey.ch/servala/servala-portal/pulls/251[#251])
|
||||
* Inline user info in service offering page (link:https://servala.app.codey.ch/servala/servala-portal/pulls/250[#250])
|
||||
|
||||
=== bug
|
||||
* Fix generated FQDN not being submitted (link:https://servala.app.codey.ch/servala/servala-portal/pulls/249[#249])
|
||||
|
||||
=== dependencies
|
||||
* Update dependency isort to v7 (link:https://servala.app.codey.ch/servala/servala-portal/pulls/252[#252])
|
||||
* Update dependency pillow to v12 (link:https://servala.app.codey.ch/servala/servala-portal/pulls/253[#253])
|
||||
* Lock file maintenance (link:https://servala.app.codey.ch/servala/servala-portal/pulls/255[#255])
|
||||
* Update https://github.com/astral-sh/setup-uv action to v7 (link:https://servala.app.codey.ch/servala/servala-portal/pulls/254[#254])
|
||||
* Update dependency flake8-bugbear to v25 (link:https://servala.app.codey.ch/servala/servala-portal/pulls/248[#248])
|
||||
* Update https://github.com/renovatebot/github-action action to v43.0.18 - autoclosed (link:https://servala.app.codey.ch/servala/servala-portal/pulls/239[#239])
|
||||
* Update actions/setup-node action to v6 (link:https://servala.app.codey.ch/servala/servala-portal/pulls/247[#247])
|
||||
|
||||
116
hack/README.md
Normal file
116
hack/README.md
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
# Automation Scripts
|
||||
|
||||
This directory contains automation scripts for release management and changelog generation.
|
||||
|
||||
## Scripts
|
||||
|
||||
### bumpver-pre-commit-hook.sh
|
||||
|
||||
**Purpose**: Generates a changelog based on merged Pull Requests since the last release.
|
||||
|
||||
**What it does**:
|
||||
1. Queries the Forgejo API for merged pull requests since the last release tag
|
||||
2. Groups pull requests by their labels (first label if multiple labels are present)
|
||||
3. Formats the changes in AsciiDoc format with third-level headers for each label group
|
||||
4. Appends the changelog to `docs/modules/ROOT/pages/web-portal-changelog.adoc`
|
||||
5. Adds the changelog file to git staging area
|
||||
6. Saves the changelog content for the post-commit hook
|
||||
|
||||
**Note**: Pull requests without labels will be grouped under "Uncategorized".
|
||||
|
||||
**Requirements**:
|
||||
- `FORGEJO_TOKEN` environment variable must be set with a valid Forgejo API token
|
||||
- `jq` command-line JSON processor must be installed
|
||||
- `curl` must be installed
|
||||
|
||||
### bumpver-post-commit-hook.sh
|
||||
|
||||
**Purpose**: Creates a release on Forgejo after a version bump.
|
||||
|
||||
**What it does**:
|
||||
1. Gets the current version from `pyproject.toml`
|
||||
2. Reads the changelog content generated by the pre-commit hook
|
||||
3. Converts AsciiDoc format to Markdown (headers and links)
|
||||
4. Creates or updates a release on Forgejo with the Markdown-formatted changelog
|
||||
5. Cleans up temporary changelog files
|
||||
|
||||
**Note**: The script automatically converts AsciiDoc syntax to Markdown for Forgejo releases:
|
||||
- `=== Header` → `### Header`
|
||||
- `link:url[text]` → `[text](url)`
|
||||
|
||||
**Requirements**:
|
||||
- `FORGEJO_TOKEN` environment variable must be set with a valid Forgejo API token
|
||||
- `jq` command-line JSON processor must be installed
|
||||
- `curl` must be installed
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Generate a Forgejo API Token
|
||||
|
||||
1. Log in to Forgejo at https://servala.app.codey.ch
|
||||
2. Go to Settings → Applications → Generate New Token
|
||||
3. Give it a descriptive name (e.g., "bumpver-automation")
|
||||
4. Select the required permissions:
|
||||
- `repo` (Full control of repositories)
|
||||
5. Copy the generated token
|
||||
|
||||
### 2. Configure the token
|
||||
|
||||
Export the token as an environment variable:
|
||||
|
||||
```bash
|
||||
export FORGEJO_TOKEN="your-token-here"
|
||||
```
|
||||
|
||||
For permanent setup, add it to your shell profile (`~/.bashrc`, `~/.zshrc`, etc.):
|
||||
|
||||
```bash
|
||||
echo 'export FORGEJO_TOKEN="your-token-here"' >> ~/.bashrc
|
||||
```
|
||||
|
||||
### 3. Update pyproject.toml
|
||||
|
||||
Update the bumpver configuration in `pyproject.toml` to use these hooks:
|
||||
|
||||
```toml
|
||||
[tool.bumpver]
|
||||
current_version = "2025.10.27-0"
|
||||
version_pattern = "YYYY.0M.0D-INC0"
|
||||
commit_message = "bump version {old_version} -> {new_version}"
|
||||
tag_message = "{new_version}"
|
||||
tag_scope = "default"
|
||||
pre_commit_hook = "hack/bumpver-pre-commit-hook.sh"
|
||||
post_commit_hook = "hack/bumpver-post-commit-hook.sh"
|
||||
commit = true
|
||||
tag = true
|
||||
push = true
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Once configured, the hooks will run automatically when you bump the version:
|
||||
|
||||
```bash
|
||||
# Or let bumpver determine the version based on the pattern
|
||||
uvx bumpver update
|
||||
```
|
||||
|
||||
The workflow is:
|
||||
1. `bumpver` updates version files
|
||||
2. **Pre-commit hook** runs: generates changelog, updates changelog file, stages changes
|
||||
3. `bumpver` creates commit with version bump and changelog
|
||||
4. `bumpver` creates git tag
|
||||
5. **Post-commit hook** runs: creates Forgejo release
|
||||
6. `bumpver` pushes commit and tags to remote
|
||||
|
||||
## Manual execution
|
||||
|
||||
You can also run the scripts manually:
|
||||
|
||||
```bash
|
||||
# Generate changelog (run before committing)
|
||||
./hack/pre-commit-hook.sh
|
||||
|
||||
# Create release (run after committing and tagging)
|
||||
./hack/post-commit-hook.sh
|
||||
```
|
||||
141
hack/bumpver-post-commit-hook.sh
Executable file
141
hack/bumpver-post-commit-hook.sh
Executable file
|
|
@ -0,0 +1,141 @@
|
|||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Post-commit hook for bumpver to create a Forgejo release
|
||||
# This script creates a release on Forgejo using the changelog generated in the pre-commit hook
|
||||
|
||||
# Configuration
|
||||
FORGEJO_URL="https://servala.app.codey.ch"
|
||||
REPO_OWNER="servala"
|
||||
REPO_NAME="servala-portal"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Check for required environment variable
|
||||
if [ -z "${FORGEJO_TOKEN:-}" ]; then
|
||||
echo -e "${RED}Error: FORGEJO_TOKEN environment variable is not set${NC}"
|
||||
echo "Please set FORGEJO_TOKEN to your Forgejo API token"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get the current version from pyproject.toml
|
||||
CURRENT_VERSION=$(grep -E 'current_version = ".*"' pyproject.toml | head -1 | sed -E 's/current_version = "(.*)"/\1/')
|
||||
echo -e "${GREEN}Creating release for version: ${CURRENT_VERSION}${NC}"
|
||||
|
||||
# Get the latest tag (should match CURRENT_VERSION)
|
||||
LATEST_TAG=$(git tag -l --sort=-v:refname | head -1)
|
||||
if [ "$LATEST_TAG" != "$CURRENT_VERSION" ]; then
|
||||
echo -e "${YELLOW}Warning: Latest tag (${LATEST_TAG}) doesn't match current version (${CURRENT_VERSION})${NC}"
|
||||
echo -e "${YELLOW}Using current version: ${CURRENT_VERSION}${NC}"
|
||||
fi
|
||||
|
||||
# Try to read the changelog generated by the pre-commit hook
|
||||
CHANGELOG_DIR=".git/changelog"
|
||||
CHANGELOG_FILE="${CHANGELOG_DIR}/${CURRENT_VERSION}.txt"
|
||||
|
||||
if [ -f "$CHANGELOG_FILE" ]; then
|
||||
CHANGELOG_CONTENT=$(cat "$CHANGELOG_FILE")
|
||||
echo -e "${GREEN}Found changelog content from pre-commit hook${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}Warning: Changelog file not found at ${CHANGELOG_FILE}${NC}"
|
||||
echo -e "${YELLOW}Generating changelog from tag information${NC}"
|
||||
|
||||
# Fallback: use git log to get commits since previous tag
|
||||
PREVIOUS_TAG=$(git tag -l --sort=-v:refname | head -2 | tail -1)
|
||||
if [ -n "$PREVIOUS_TAG" ]; then
|
||||
CHANGELOG_CONTENT=$(git log --pretty=format:"* %s" "${PREVIOUS_TAG}..${LATEST_TAG}")
|
||||
else
|
||||
CHANGELOG_CONTENT="* Initial release"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Convert AsciiDoc to Markdown
|
||||
# 1. Convert === headers to ### (third-level headers)
|
||||
# 2. Convert link:url[text] to [text](url)
|
||||
CHANGELOG_MARKDOWN=$(echo "$CHANGELOG_CONTENT" | sed -E 's/^=== /### /g' | sed -E 's/link:([^[]+)\[([^]]+)\]/[\2](\1)/g')
|
||||
|
||||
# Create release body in Markdown format
|
||||
RELEASE_BODY="## Release ${CURRENT_VERSION}
|
||||
|
||||
${CHANGELOG_MARKDOWN}
|
||||
"
|
||||
|
||||
# Check if release already exists
|
||||
API_URL="${FORGEJO_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases/tags/${CURRENT_VERSION}"
|
||||
EXISTING_RELEASE=$(curl -s -H "Authorization: token ${FORGEJO_TOKEN}" "${API_URL}")
|
||||
|
||||
# Check if we got a release back (not an error)
|
||||
if echo "$EXISTING_RELEASE" | jq -e '.id' > /dev/null 2>&1; then
|
||||
echo -e "${YELLOW}Release ${CURRENT_VERSION} already exists${NC}"
|
||||
RELEASE_ID=$(echo "$EXISTING_RELEASE" | jq -r '.id')
|
||||
echo -e "${GREEN}Updating existing release (ID: ${RELEASE_ID})${NC}"
|
||||
|
||||
# Update the existing release
|
||||
UPDATE_URL="${FORGEJO_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases/${RELEASE_ID}"
|
||||
|
||||
RESPONSE=$(curl -s -X PATCH \
|
||||
-H "Authorization: token ${FORGEJO_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$(jq -n \
|
||||
--arg tag "${CURRENT_VERSION}" \
|
||||
--arg name "Release ${CURRENT_VERSION}" \
|
||||
--arg body "${RELEASE_BODY}" \
|
||||
'{
|
||||
tag_name: $tag,
|
||||
name: $name,
|
||||
body: $body
|
||||
}')" \
|
||||
"${UPDATE_URL}")
|
||||
|
||||
if echo "$RESPONSE" | jq -e '.id' > /dev/null 2>&1; then
|
||||
RELEASE_URL=$(echo "$RESPONSE" | jq -r '.html_url')
|
||||
echo -e "${GREEN}Release updated successfully!${NC}"
|
||||
echo -e "${GREEN}Release URL: ${RELEASE_URL}${NC}"
|
||||
else
|
||||
echo -e "${RED}Error updating release${NC}"
|
||||
echo "$RESPONSE" | jq .
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo -e "${GREEN}Creating new release${NC}"
|
||||
|
||||
# Create new release
|
||||
CREATE_URL="${FORGEJO_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases"
|
||||
|
||||
RESPONSE=$(curl -s -X POST \
|
||||
-H "Authorization: token ${FORGEJO_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$(jq -n \
|
||||
--arg tag "${CURRENT_VERSION}" \
|
||||
--arg name "Release ${CURRENT_VERSION}" \
|
||||
--arg body "${RELEASE_BODY}" \
|
||||
'{
|
||||
tag_name: $tag,
|
||||
name: $name,
|
||||
body: $body,
|
||||
draft: false,
|
||||
prerelease: false
|
||||
}')" \
|
||||
"${CREATE_URL}")
|
||||
|
||||
if echo "$RESPONSE" | jq -e '.id' > /dev/null 2>&1; then
|
||||
RELEASE_URL=$(echo "$RESPONSE" | jq -r '.html_url')
|
||||
echo -e "${GREEN}Release created successfully!${NC}"
|
||||
echo -e "${GREEN}Release URL: ${RELEASE_URL}${NC}"
|
||||
else
|
||||
echo -e "${RED}Error creating release${NC}"
|
||||
echo "$RESPONSE" | jq .
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Clean up the changelog file
|
||||
if [ -f "$CHANGELOG_FILE" ]; then
|
||||
rm -f "$CHANGELOG_FILE"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
134
hack/bumpver-pre-commit-hook.sh
Executable file
134
hack/bumpver-pre-commit-hook.sh
Executable file
|
|
@ -0,0 +1,134 @@
|
|||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Pre-commit hook for bumpver to generate changelog from Forgejo merge requests
|
||||
# This script queries the Forgejo API for merged pull requests since the last release
|
||||
# and appends them to the changelog file in AsciiDoc format
|
||||
|
||||
# Configuration
|
||||
FORGEJO_URL="https://servala.app.codey.ch"
|
||||
REPO_OWNER="servala"
|
||||
REPO_NAME="servala-portal"
|
||||
CHANGELOG_FILE="docs/modules/ROOT/pages/web-portal-changelog.adoc"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Check for required environment variable
|
||||
if [ -z "${FORGEJO_TOKEN:-}" ]; then
|
||||
echo -e "${RED}Error: FORGEJO_TOKEN environment variable is not set${NC}"
|
||||
echo "Please set FORGEJO_TOKEN to your Forgejo API token"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get the new version from pyproject.toml (bumpver updates this before running the hook)
|
||||
NEW_VERSION=$(grep -E 'current_version = ".*"' pyproject.toml | head -1 | sed -E 's/current_version = "(.*)"/\1/')
|
||||
echo -e "${GREEN}Generating changelog for version: ${NEW_VERSION}${NC}"
|
||||
|
||||
# Get the previous tag
|
||||
PREVIOUS_TAG=$(git tag -l --sort=-v:refname | head -2 | tail -1)
|
||||
if [ -z "$PREVIOUS_TAG" ]; then
|
||||
echo -e "${YELLOW}Warning: No previous tag found, using all merge requests${NC}"
|
||||
# Get date of first commit
|
||||
SINCE_DATE=$(git log --reverse --format=%aI | head -1)
|
||||
else
|
||||
echo -e "${GREEN}Previous version: ${PREVIOUS_TAG}${NC}"
|
||||
# Get the date of the previous tag
|
||||
SINCE_DATE=$(git log -1 --format=%aI "${PREVIOUS_TAG}")
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}Fetching merge requests since ${SINCE_DATE}${NC}"
|
||||
|
||||
# Query Forgejo API for closed/merged pull requests
|
||||
# Forgejo API returns pull requests sorted by updated time
|
||||
API_URL="${FORGEJO_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/pulls?state=closed&sort=updated&limit=100"
|
||||
|
||||
RESPONSE=$(curl -s -H "Authorization: token ${FORGEJO_TOKEN}" "${API_URL}")
|
||||
|
||||
# Check if curl was successful
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${RED}Error: Failed to fetch pull requests from Forgejo API${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Filter merged PRs since the previous tag and group by labels
|
||||
# Using jq to parse JSON, filter by merge date, and group by labels
|
||||
CHANGELOG_ENTRIES=$(echo "${RESPONSE}" | jq -r --arg since "${SINCE_DATE}" '
|
||||
# Filter merged PRs since the last tag
|
||||
[.[] | select(.merged_at != null and .merged_at > $since)] |
|
||||
sort_by(.merged_at) | reverse |
|
||||
|
||||
# Group by primary label (first label if multiple, or "Uncategorized")
|
||||
group_by(
|
||||
if (.labels | length) > 0 then
|
||||
.labels[0].name
|
||||
else
|
||||
"Uncategorized"
|
||||
end
|
||||
) |
|
||||
|
||||
# Format each group
|
||||
map(
|
||||
# Group header
|
||||
"=== " + (
|
||||
if .[0].labels | length > 0 then
|
||||
.[0].labels[0].name
|
||||
else
|
||||
"Uncategorized"
|
||||
end
|
||||
) + "\n" +
|
||||
|
||||
# List items in this group
|
||||
(map("* " + .title + " (link:" + .html_url + "[#" + (.number | tostring) + "])") | join("\n"))
|
||||
) |
|
||||
join("\n\n")
|
||||
')
|
||||
|
||||
if [ -z "$CHANGELOG_ENTRIES" ]; then
|
||||
echo -e "${YELLOW}Warning: No merged pull requests found since ${PREVIOUS_TAG}${NC}"
|
||||
CHANGELOG_ENTRIES="=== Uncategorized\n\n* No changes recorded"
|
||||
fi
|
||||
|
||||
# Create changelog section
|
||||
CHANGELOG_SECTION="
|
||||
== ${NEW_VERSION}
|
||||
|
||||
${CHANGELOG_ENTRIES}
|
||||
"
|
||||
|
||||
# Check if changelog file exists
|
||||
if [ ! -f "$CHANGELOG_FILE" ]; then
|
||||
echo -e "${RED}Error: Changelog file ${CHANGELOG_FILE} not found${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create temporary file with new changelog entry at the top
|
||||
TMP_FILE=$(mktemp)
|
||||
{
|
||||
# Keep the title
|
||||
head -1 "$CHANGELOG_FILE"
|
||||
# Add the new changelog section
|
||||
echo "$CHANGELOG_SECTION"
|
||||
# Add the rest of the file (skip the title)
|
||||
tail -n +2 "$CHANGELOG_FILE"
|
||||
} > "$TMP_FILE"
|
||||
|
||||
# Replace the original file
|
||||
mv "$TMP_FILE" "$CHANGELOG_FILE"
|
||||
chmod 0644 "$CHANGELOG_FILE"
|
||||
|
||||
# Add the changelog file to git
|
||||
git add "$CHANGELOG_FILE"
|
||||
|
||||
echo -e "${GREEN}Changelog updated successfully${NC}"
|
||||
echo -e "${GREEN}Added ${CHANGELOG_FILE} to git staging area${NC}"
|
||||
|
||||
# Save changelog for post-commit hook
|
||||
CHANGELOG_DIR=".git/changelog"
|
||||
mkdir -p "$CHANGELOG_DIR"
|
||||
echo "$CHANGELOG_ENTRIES" > "${CHANGELOG_DIR}/${NEW_VERSION}.txt"
|
||||
|
||||
exit 0
|
||||
|
|
@ -66,8 +66,8 @@ version_pattern = "YYYY.0M.0D-INC0"
|
|||
commit_message = "bump version {old_version} -> {new_version}"
|
||||
tag_message = "{new_version}"
|
||||
tag_scope = "default"
|
||||
pre_commit_hook = ""
|
||||
post_commit_hook = ""
|
||||
pre_commit_hook = "hack/bumpver-pre-commit-hook.sh"
|
||||
post_commit_hook = "hack/bumpver-post-commit-hook.sh"
|
||||
commit = true
|
||||
tag = true
|
||||
push = true
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue