forked from Solo-Web-Works/Projects-Portfolio
Add design spec: GitHub Actions release-zip workflow
Single workflow file at .github/workflows/release.yml that triggers on v* tag pushes, assembles projects-portfolio-v<version>.zip from an allowlist of runtime files, uploads as a workflow artifact, and attaches to a matching GitHub release.
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
# Release Workflow Design
|
||||
|
||||
- **Date:** 2026-08-11
|
||||
- **Plugin:** Projects Portfolio (`projects-wp`)
|
||||
- **Status:** Approved design, awaiting implementation plan
|
||||
|
||||
## 1. Goals
|
||||
|
||||
- A single tag push (`v*`) automatically produces a release-ready zip named `projects-portfolio-v<version>.zip`.
|
||||
- The zip contains exactly the files WordPress needs to install and use the plugin: `projects-portfolio.php` at the root of `projects-portfolio/`, with `admin/`, `assets/`, `includes/`, `languages/`, `templates/` subdirectories plus `README.md` and `LICENSE`.
|
||||
- The same zip is uploaded as a workflow artifact AND attached to a GitHub release matching the tag.
|
||||
- README gets a short "Building a release" section explaining how the workflow runs and what it produces.
|
||||
|
||||
## 2. Non-goals
|
||||
|
||||
- Publishing to WordPress.org — out of scope (different credentials, different approval workflow).
|
||||
- Cross-repo publishing or PAT-based auth.
|
||||
- Signing, cosign, or checksums for the zip.
|
||||
- Auto-bumping `PROJECTS_PORTFOLIO_VERSION` or the plugin header before a tag is cut. The workflow assumes the tag is created *after* the version has been bumped in-tree.
|
||||
- Backporting tags. The workflow only acts on the tag push, not on retags of older SHAs.
|
||||
|
||||
## 3. Architecture
|
||||
|
||||
A single GitHub Actions workflow file at `.github/workflows/release.yml` runs on every `v*` tag push. It performs four sequential steps inside a single job:
|
||||
|
||||
1. **Checkout** — `actions/checkout@v4` at the tag's commit.
|
||||
2. **Build zip** — bash script copies allowlisted runtime files into a staging directory `dist/staging/projects-portfolio/`, then runs `zip -r` from the staging root to produce `dist/projects-portfolio-v<version>.zip`.
|
||||
3. **Upload artifact** — `actions/upload-artifact@v4` with name `projects-portfolio-v<tag>` and path `dist/projects-portfolio-*.zip`. Fails if no file matches.
|
||||
4. **Create release** — `softprops/action-gh-release@v2` with the tag, generated release notes, and the zip file. Replaces the asset if a release with the same tag already exists (default `action-gh-release@v2` behavior).
|
||||
|
||||
The workflow uses the runner-provided `GITHUB_TOKEN` with `contents: write` permission. No additional secrets are needed.
|
||||
|
||||
### 3.1 Why a staging directory
|
||||
|
||||
WordPress's "Upload Plugin" zip-install expects `<plugin-slug>/<plugin-file>.php` at the zip root. A direct `zip -r` of the working tree would put everything at the zip root — no enclosing folder — which would fail to install. The staging directory ensures the zip's internal layout has `projects-portfolio/` at the top with `projects-portfolio.php` inside it.
|
||||
|
||||
### 3.2 Files added
|
||||
|
||||
- `.github/workflows/release.yml` — the workflow.
|
||||
- `README.md` — modified, with a new "Building a release" section after the existing "Connect Your Gitea Repo" section.
|
||||
|
||||
No PHP files are touched. The PHPUnit suite is unaffected (no test bootstrap or production code changes).
|
||||
|
||||
## 4. Trigger & permissions
|
||||
|
||||
```yaml
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
```
|
||||
|
||||
`workflow_dispatch` is intentionally omitted. To re-run for an existing tag, use the GitHub UI's "Re-run all jobs" button.
|
||||
|
||||
## 5. Zip filename
|
||||
|
||||
`projects-portfolio-v<version>.zip`, where `<version>` is the tag name with the leading `v` stripped. Examples:
|
||||
|
||||
- Tag `v1.1.0` → `projects-portfolio-v1.1.0.zip`
|
||||
- Tag `v2.0.0-rc1` → `projects-portfolio-v2.0.0-rc1.zip`
|
||||
|
||||
## 6. Allowlisted zip contents
|
||||
|
||||
The build step copies these paths from the working tree into `dist/staging/projects-portfolio/`:
|
||||
|
||||
```
|
||||
projects-portfolio.php
|
||||
README.md
|
||||
LICENSE
|
||||
admin/
|
||||
assets/
|
||||
includes/
|
||||
languages/
|
||||
templates/
|
||||
```
|
||||
|
||||
The bundled `includes/plugin-update-checker/` library IS included — it's a runtime dependency used by `projects-portfolio.php`.
|
||||
|
||||
## 7. Excluded paths
|
||||
|
||||
The build step does **not** copy any of these. Most are already not tracked in git; the explicit list prevents accidental inclusion if any are added later:
|
||||
|
||||
- `composer.json`, `composer.lock` — dev-only.
|
||||
- `vendor/` — Composer dev dependencies.
|
||||
- `tests/`, `phpunit.xml`, `phpunit.xml.dist`, `.phpunit.result.cache` — test infrastructure.
|
||||
- `plans/`, `specs/` — design artifacts (not tracked, but listed for safety).
|
||||
- `.github/` — the workflow file itself (would otherwise be included by the catch-all `cp -r`).
|
||||
- `.gitignore`, `.vscode/`, `.claude/` — editor / project config (not tracked except `.gitignore`).
|
||||
|
||||
If the build script can't find a tracked source path it expects (e.g. `projects-portfolio.php` renamed), `set -e` fails the step.
|
||||
|
||||
## 8. Build step (verbatim)
|
||||
|
||||
```bash
|
||||
set -euo pipefail
|
||||
VERSION="${GITHUB_REF_NAME#v}"
|
||||
STAGE="dist/staging"
|
||||
OUT="dist/projects-portfolio-v${VERSION}.zip"
|
||||
|
||||
rm -rf dist
|
||||
mkdir -p "$STAGE/projects-portfolio"
|
||||
|
||||
cp projects-portfolio.php "$STAGE/projects-portfolio/"
|
||||
cp README.md "$STAGE/projects-portfolio/"
|
||||
cp LICENSE "$STAGE/projects-portfolio/"
|
||||
cp -r admin/ "$STAGE/projects-portfolio/"
|
||||
cp -r assets/ "$STAGE/projects-portfolio/"
|
||||
cp -r includes/ "$STAGE/projects-portfolio/"
|
||||
cp -r languages/ "$STAGE/projects-portfolio/"
|
||||
cp -r templates/ "$STAGE/projects-portfolio/"
|
||||
|
||||
( cd "$STAGE" && zip -r "../../$OUT" projects-portfolio )
|
||||
|
||||
echo "Built $OUT"
|
||||
unzip -l "$OUT"
|
||||
```
|
||||
|
||||
`unzip -l` prints the table of contents to the action log so the run is self-documenting.
|
||||
|
||||
## 9. README addition
|
||||
|
||||
Add a "Building a Release" section after the existing "Connect Your Gitea Repo" section:
|
||||
|
||||
```markdown
|
||||
## Building a Release
|
||||
|
||||
Releases are automated via GitHub Actions. To cut a new release:
|
||||
|
||||
1. Bump `PROJECTS_PORTFOLIO_VERSION` and the plugin header `Version:` in `projects-portfolio.php`.
|
||||
2. Commit and push to `main`.
|
||||
3. Tag the release commit: `git tag -a v1.1.0 -m "v1.1.0 — short summary"`.
|
||||
4. Push the tag: `git push origin v1.1.0`.
|
||||
|
||||
The `.github/workflows/release.yml` workflow runs and:
|
||||
|
||||
- Builds `projects-portfolio-v<version>.zip` containing only the runtime files (`projects-portfolio.php`, `admin/`, `assets/`, `includes/`, `languages/`, `templates/`, `README.md`, `LICENSE`).
|
||||
- Uploads the zip as a workflow artifact.
|
||||
- Creates (or updates) the matching GitHub Release with the zip attached.
|
||||
|
||||
Dev-only paths (`composer.json`, `composer.lock`, `vendor/`, `tests/`, `plans/`, `specs/`, `.github/`, `.gitignore`, `.vscode/`, `.claude/`) are intentionally excluded.
|
||||
```
|
||||
|
||||
## 10. Error handling
|
||||
|
||||
| Failure | Behavior |
|
||||
|---|---|
|
||||
| Tag doesn't match `v*` | Workflow doesn't run. |
|
||||
| `cp -r` finds a missing source path | `set -e` fails the step; no zip, no release. |
|
||||
| `zip` command fails | `set -e` fails the step; no artifact upload, no release. |
|
||||
| Artifact upload finds no file | `if-no-files-found: error` fails the step. |
|
||||
| Release already exists for tag | `softprops/action-gh-release@v2` replaces the asset and updates notes (default behavior). |
|
||||
| Missing `contents: write` permission | Job fails at checkout or release step with a clear permissions error. |
|
||||
|
||||
## 11. Rollout
|
||||
|
||||
1. Implement on a feature branch.
|
||||
2. Merge into `main` (the workflow file is tracked and applies to all future tags).
|
||||
3. To produce the first automated release: bump `PROJECTS_PORTFOLIO_VERSION` and the plugin header from `1.1.0` to `1.1.1`, commit, tag `v1.1.1`, push the tag.
|
||||
4. Verify on the GitHub Actions run page that: artifact uploads, zip contents look right (via the `unzip -l` log line), GitHub release is created with the zip attached.
|
||||
|
||||
## 12. New translatable strings
|
||||
|
||||
None. The workflow file and README change introduce no user-facing strings.
|
||||
Reference in New Issue
Block a user