Initial skill library: 9 skills for delegate briefs
WordPress plugin rebrand/conventions/remote-CLI patterns, Gitea release workflow, bastille jail provisioning, remote shell quoting safety, server fleet map, delegate brief writing, and verification discipline -- all derived from real incidents this session, plus two skills adapted (MIT license, attributed) from obra/superpowers and andrej-karpathy-skills.
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
---
|
||||
name: gitea-release-workflow
|
||||
description: Use when tagging a release for a plugin/package on a self-hosted Gitea instance, especially when the release needs to be downloadable by an external system (an auto-updater, a CI job, etc.).
|
||||
---
|
||||
|
||||
# Gitea Release Workflow
|
||||
|
||||
## Creating a release
|
||||
|
||||
```bash
|
||||
curl -s -X POST -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
|
||||
-d '{"tag_name": "v1.0.0", "name": "v1.0.0", "body": "<changelog>", "draft": false, "prerelease": false}' \
|
||||
"$HOST/api/v1/repos/$OWNER/$REPO/releases"
|
||||
```
|
||||
|
||||
Verify it landed:
|
||||
```bash
|
||||
curl -s -H "Authorization: token $TOKEN" "$HOST/api/v1/repos/$OWNER/$REPO/releases/latest"
|
||||
```
|
||||
Note: on at least one real self-hosted instance, `/releases/latest` 404s
|
||||
for unauthenticated requests even on a public repo — always send the
|
||||
token, don't assume public-repo endpoints are auth-free.
|
||||
|
||||
## KNOWN GOTCHA: release-asset downloads can be unreachable
|
||||
|
||||
**Confirmed live on a real self-hosted Gitea instance**: uploading a zip
|
||||
as a release asset and then trying to fetch it via the asset's own
|
||||
`browser_download_url` field returned an **internal address**
|
||||
(`http://localhost:3000/...`) instead of the instance's real public
|
||||
hostname — a `ROOT_URL` misconfiguration server-side. This makes the
|
||||
uploaded asset **completely unreachable from outside the Gitea server
|
||||
itself**, even though the release object and its metadata (version,
|
||||
changelog) are fine.
|
||||
|
||||
**Symptom**: `update-check`-style API calls that only read release
|
||||
metadata work fine; the actual **download** step 404s or times out.
|
||||
|
||||
**Workaround that reliably works**: don't rely on the release-asset
|
||||
upload/download mechanism at all for the actual file bytes. Instead use
|
||||
Gitea's **repo-archive endpoint**, which zips the tagged git tree
|
||||
on-the-fly and is served from the same working API path pattern as
|
||||
everything else:
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: token $TOKEN" -o output.zip \
|
||||
"$HOST/api/v1/repos/$OWNER/$REPO/archive/$TAG.zip"
|
||||
```
|
||||
|
||||
This means: the release object still gets created (for its version/
|
||||
changelog metadata, which downstream consumers do need), but you do
|
||||
**not** need to upload a zip as a release asset at all — the actual
|
||||
distributable content is whatever's in the tagged git tree, fetched via
|
||||
`/archive/{tag}.zip` at request time.
|
||||
|
||||
**Before trusting the asset-download path on a NEW Gitea instance**: test
|
||||
it once (`curl` the `browser_download_url` directly) rather than
|
||||
assuming it works — if it does work on a given instance, the asset-upload
|
||||
approach is simpler and fine to use; the archive-endpoint workaround is
|
||||
only needed on instances that have this specific misconfiguration. Don't
|
||||
apply the workaround blindly without confirming the actual failure mode
|
||||
exists on the instance you're using.
|
||||
|
||||
## Archive-endpoint folder-naming gotcha
|
||||
|
||||
The `/archive/{tag}.zip` endpoint names the zip's top-level folder based
|
||||
on the repo (exact naming varies by Gitea version — sometimes the repo
|
||||
name, sometimes lowercased). If the repo's own files live in a
|
||||
subdirectory rather than at repo root, the resulting zip gets a
|
||||
**double-nested** top-level folder. Keep single-purpose repos (one
|
||||
plugin/package per repo) flat at the root to avoid this — see the
|
||||
`wordpress-plugin-rebrand` skill's flatten-to-root step.
|
||||
|
||||
## Forking into a new org
|
||||
|
||||
```bash
|
||||
curl -s -X POST -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
|
||||
-d '{"organization": "TargetOrg", "name": "new-repo-name"}' \
|
||||
"$HOST/api/v1/repos/$SOURCE_OWNER/$SOURCE_REPO/forks"
|
||||
```
|
||||
The `name` field renames the fork on creation — you don't have to accept
|
||||
the source repo's own name. Fork inherits the **source repo's full git
|
||||
history**; if you're about to do a wholesale rebrand and don't need that
|
||||
history preserved, a `git init` + single clean commit + `git push --force`
|
||||
is often cleaner than carrying an unrelated-looking history forward — but
|
||||
only force-push a repo that's genuinely fresh with nothing else depending
|
||||
on its prior state.
|
||||
|
||||
## Verify a release actually works end-to-end
|
||||
|
||||
Don't stop at "the release object exists" — actually fetch the archive
|
||||
and inspect it:
|
||||
```bash
|
||||
curl -s -H "Authorization: token $TOKEN" -o /tmp/verify.zip \
|
||||
"$HOST/api/v1/repos/$OWNER/$REPO/archive/$TAG.zip"
|
||||
unzip -l /tmp/verify.zip | head -20
|
||||
```
|
||||
Confirm: single non-double-nested top-level folder, expected files
|
||||
present, no truncation.
|
||||
Reference in New Issue
Block a user