Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86ebe976ba | ||
|
|
efd5d52198 | ||
|
|
f8ab3f965e |
No files matched your search
@@ -0,0 +1,3 @@
|
||||
${PullRequestTitle} (#${PullRequestIndex})
|
||||
|
||||
${PullRequestDescription}
|
||||
@@ -0,0 +1,30 @@
|
||||
# A pull request's title becomes its commit on main (squash merges, .gitea/default_merge_message), so it must be a
|
||||
# conventional commit: the release workflow versions releases and writes the changelog from them. Required to merge
|
||||
# (branch protection, dhkatz/infra modules/app-repo).
|
||||
name: Pull request
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited, reopened, synchronize]
|
||||
|
||||
jobs:
|
||||
title:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Conventional commit title
|
||||
env:
|
||||
TITLE: ${{ github.event.pull_request.title }}
|
||||
BRANCH: ${{ github.head_ref }}
|
||||
AUTHOR: ${{ github.event.pull_request.user.login }}
|
||||
run: |
|
||||
types="feat|fix|perf|refactor|docs|test|build|ci|chore|style|revert"
|
||||
conventional="^($types)(\([a-z0-9][a-z0-9./_-]*\))?!?: [^ ]"
|
||||
if ! [[ "$TITLE" =~ $conventional ]]; then
|
||||
echo "::error::The title becomes the commit on main, so it must be a conventional commit: type(scope): summary, e.g. \"feat(api): room bans\" or \"fix: crash on an empty catalog\" (! before the colon for a breaking change). Types: ${types//|/, }."
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$TITLE" =~ ^chore\(release\) ]] && [[ "$BRANCH" != release || "$AUTHOR" != release-bot ]]; then
|
||||
echo "::error::chore(release) is release-bot's: merging its release pull request is what tags a release."
|
||||
exit 1
|
||||
fi
|
||||
echo "OK: $TITLE"
|
||||
@@ -0,0 +1,86 @@
|
||||
# Releases. release-bot keeps one pull request open from the `release` branch, "chore(release): vX.Y.Z": the next
|
||||
# version and its CHANGELOG.md entry, which git-cliff (cliff.toml) works out from the conventional commits on main
|
||||
# since the last tag. Merging it makes release-bot tag vX.Y.Z on main and publish the release notes; the tag builds the
|
||||
# image (ci.yml) and Komodo deploys it to prod (dhkatz/infra). Only release-bot may create v* tags. It acts with its
|
||||
# own token (the org secret RELEASE_TOKEN), not this workflow's: Gitea starts no workflows for what that one does, so
|
||||
# the release pull request's checks and the tag's build would never run.
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
concurrency:
|
||||
group: release
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CLIFF_VERSION: 2.14.2
|
||||
CLIFF_SHA512: 95b0c55ab3e351ca7c0285fbbde8f1e8c42906466a173e6884f5c7ca814523bda44cbd4ab26b0aa28cace4c02c5ee6b553e9742bd9d7df47a73b9ef58ea429a9
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install git-cliff
|
||||
run: |
|
||||
archive="git-cliff-$CLIFF_VERSION-x86_64-unknown-linux-musl.tar.gz"
|
||||
curl -fsSLO "https://github.com/orhun/git-cliff/releases/download/v$CLIFF_VERSION/$archive"
|
||||
echo "$CLIFF_SHA512 $archive" | sha512sum -c -
|
||||
mkdir -p "$HOME/.local/bin"
|
||||
tar -xzf "$archive" --strip-components=1 -C "$HOME/.local/bin" "git-cliff-$CLIFF_VERSION/git-cliff"
|
||||
rm "$archive"
|
||||
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Tag a merged release, or update the release pull request
|
||||
env:
|
||||
TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
api="$GITHUB_SERVER_URL/api/v1/repos/$GITHUB_REPOSITORY"
|
||||
host="${GITHUB_SERVER_URL#https://}"
|
||||
git config user.name release-bot
|
||||
git config user.email "release-bot@${host#git.}"
|
||||
basic=$(printf 'release-bot:%s' "$TOKEN" | base64 -w0)
|
||||
push() { git -c http.extraHeader="Authorization: Basic $basic" push "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git" "$@"; }
|
||||
gitea() { curl -fsS -H "Authorization: token $TOKEN" -H "Content-Type: application/json" "$@"; }
|
||||
|
||||
# The release pull request was merged: tag its commit and publish the release.
|
||||
if [[ "$(git log -1 --format=%s)" =~ ^chore\(release\):\ (v[0-9]+\.[0-9]+\.[0-9]+) ]]; then
|
||||
tag="${BASH_REMATCH[1]}"
|
||||
if git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then echo "$tag exists already"; exit 0; fi
|
||||
git tag "$tag"
|
||||
push "refs/tags/$tag"
|
||||
jq -n --arg tag "$tag" --arg body "$(git cliff --latest --strip all)" '{tag_name: $tag, name: $tag, body: $body}' |
|
||||
gitea -o /dev/null -X POST "$api/releases" -d @-
|
||||
echo "Released $tag"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Otherwise: anything to release since the last tag? (Nothing printed when no commit counts.)
|
||||
last=$(git describe --tags --abbrev=0 --match 'v[0-9]*' 2>/dev/null || true)
|
||||
next=$(git cliff --bumped-version 2>/dev/null || true)
|
||||
next="v${next#v}"
|
||||
if [ "$next" = v ] || [ "$next" = "$last" ]; then echo "Nothing to release since ${last:-the start}"; exit 0; fi
|
||||
|
||||
notes=$(git cliff --unreleased --bump --strip all)
|
||||
git switch -C release
|
||||
git cliff --bump --output CHANGELOG.md
|
||||
git add CHANGELOG.md
|
||||
git commit -q -m "chore(release): $next"
|
||||
push --force HEAD:refs/heads/release
|
||||
|
||||
title="chore(release): $next"
|
||||
body=$(printf 'Merging this releases **%s**: release-bot tags it on main, CI builds its image, and Komodo deploys it to prod. It updates itself as pull requests are merged.\n\n%s\n' "$next" "$notes")
|
||||
payload=$(jq -n --arg title "$title" --arg body "$body" '{title: $title, body: $body}')
|
||||
open=$(gitea "$api/pulls?state=open&limit=50" | jq -r '[.[] | select(.head.ref == "release")][0].number // empty')
|
||||
if [ -n "$open" ]; then
|
||||
gitea -o /dev/null -X PATCH "$api/pulls/$open" -d "$payload"
|
||||
echo "Updated the release pull request #$open: $title"
|
||||
else
|
||||
jq '. + {head: "release", base: "main"}' <<<"$payload" | gitea -o /dev/null -X POST "$api/pulls" -d @-
|
||||
echo "Opened the release pull request: $title"
|
||||
fi
|
||||
@@ -0,0 +1,44 @@
|
||||
# Changelog
|
||||
|
||||
Written by release-bot's release pull requests, from the titles of the pull requests merged since the last release.
|
||||
|
||||
## 0.2.0 (2026-11-03)
|
||||
|
||||
### Features
|
||||
|
||||
- **web:** Download link to t5de.dev in the header (#2)
|
||||
|
||||
|
||||
## 0.1.0 (2026-11-02)
|
||||
|
||||
### Badges
|
||||
|
||||
- The proxy asks which IMVU accounts show the T5 badge
|
||||
|
||||
### CI
|
||||
|
||||
- Test against a Postgres service (embedded Postgres won't run as root)
|
||||
|
||||
### Design
|
||||
|
||||
- T5 products in the client's shop and inventory
|
||||
- Everywhere the T5 badge appears
|
||||
|
||||
### Hub
|
||||
|
||||
- Accounts, IMVU linking by tagline, proxy sign-in, presence, console
|
||||
|
||||
### Presence
|
||||
|
||||
- Count an account as soon as it's linked; accept an empty outfit
|
||||
|
||||
### Products
|
||||
|
||||
- Images need https addresses; lowering a rating waits for review; library shows creators
|
||||
- Carry the IMVU parent's image and categories
|
||||
|
||||
### Website
|
||||
|
||||
- Next.js app for accounts, linking, who's online, device sign-in and the console
|
||||
- Products, library, catalogs and their console page
|
||||
|
||||
@@ -10,6 +10,13 @@ T5DE's hub (see README.md and docs/design.md). Related repositories, usually che
|
||||
in our own words are fine.
|
||||
- Test with a throwaway IMVU account, never a real one.
|
||||
- Commits: author "dhkatz <dev@t5de.dev>"; never the user's real name or email.
|
||||
- Changes reach main only through pull requests, squash-merged: the PR's title becomes the commit on main, so it must
|
||||
be a conventional commit (`feat(api): …`, `fix(web): …`, `docs: …`; `!` before the colon for a breaking change),
|
||||
checked by `.gitea/workflows/pull-request.yml`. Commits on the branch can be anything. Push a branch and open the
|
||||
pull request on git.t5de.dev (web UI or API); the user merges.
|
||||
- Releases: release-bot keeps a `chore(release): vX.Y.Z` pull request open (`release.yml`, git-cliff from those
|
||||
titles); merging it tags vX.Y.Z, which builds the image and deploys prod. Never tag by hand (v* tags are
|
||||
release-bot's).
|
||||
- IMVU session keys never leave the user's machine; the hub never asks for them.
|
||||
|
||||
## Notes
|
||||
|
||||
@@ -24,6 +24,9 @@ export async function Header() {
|
||||
<NavLink href="/catalogs">Catalogs</NavLink>
|
||||
</nav>
|
||||
<div className="ml-auto flex items-center gap-2 text-sm">
|
||||
<a href="https://t5de.dev/download" className={buttonVariants({ variant: "ghost" })}>
|
||||
Download
|
||||
</a>
|
||||
{me ? (
|
||||
<ProfileMenu username={me.username} picture={me.picture} console={isModerator(me)} />
|
||||
) : (
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
# git-cliff (https://git-cliff.org), for the release workflow (.gitea/workflows/release.yml): the next version and
|
||||
# CHANGELOG.md, from the conventional commits on main (one per pull request, titled by it) since the last vX.Y.Z tag.
|
||||
# feat → minor, fix/perf/revert → patch, a breaking change (`!` or BREAKING CHANGE:) → minor while 0.x, major after.
|
||||
|
||||
[changelog]
|
||||
header = """
|
||||
# Changelog
|
||||
|
||||
Written by release-bot's release pull requests, from the titles of the pull requests merged since the last release.
|
||||
"""
|
||||
body = """
|
||||
{% if commits | length > 0 %}
|
||||
{% if version %}## {{ version | trim_start_matches(pat="v") }} ({{ timestamp | date(format="%Y-%m-%d") }}){% else %}## Unreleased{% endif %}
|
||||
{% for group, commits in commits | group_by(attribute="group") %}
|
||||
### {{ group | striptags | trim }}
|
||||
{% for commit in commits %}
|
||||
- {% if commit.scope %}**{{ commit.scope }}:** {% endif %}{% if commit.breaking %}**Breaking:** {% endif %}{{ commit.message | upper_first }}{% endfor %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
"""
|
||||
trim = true
|
||||
|
||||
[git]
|
||||
conventional_commits = true
|
||||
filter_unconventional = true
|
||||
protect_breaking_commits = true
|
||||
commit_parsers = [
|
||||
{ message = "^chore\\(release\\)", skip = true },
|
||||
{ message = "^feat", group = "<!-- 0 -->Features" },
|
||||
{ message = "^fix", group = "<!-- 1 -->Fixes" },
|
||||
{ message = "^perf", group = "<!-- 2 -->Performance" },
|
||||
{ message = "^revert", group = "<!-- 3 -->Reverts" },
|
||||
{ message = "^refactor", group = "<!-- 4 -->Refactoring" },
|
||||
{ message = "^docs", group = "<!-- 5 -->Documentation" },
|
||||
{ message = "^(build|ci|chore|style|test)", skip = true },
|
||||
]
|
||||
tag_pattern = "v[0-9]+\\.[0-9]+\\.[0-9]+"
|
||||
sort_commits = "oldest"
|
||||
|
||||
[bump]
|
||||
features_always_bump_minor = true
|
||||
breaking_always_bump_major = false
|
||||
initial_tag = "v0.1.0"
|
||||
# Matched against the commit's type: these alone don't make a release.
|
||||
no_increment_regex = "^(chore|ci|docs|test|style|build|refactor)$"
|
||||
Reference in new issue
Block a user