From 789c1dc6036619d9e85362b6c44319e72a148536 Mon Sep 17 00:00:00 2001 From: Rodrigo Delduca Date: Fri, 26 Jun 2026 14:02:22 -0300 Subject: [PATCH 1/3] feat(ci): add release-please pipeline to lint, test, and publish modules Why: The template-module repo was the scaffold for new Wippy modules but had drifted: it used the legacy packcli.yaml manifest and a publish.yml calling wippyai/action-module-release@main, which no longer exists (404). It had no lint, no tests, and no automated versioning. What: Overhaul it into a modern template driven by the wippy CLI from the runtime. Reusable workflows lint and test every pull request and, on merge to main, release-please bumps wippy.yaml's version from Conventional Commits and publishes the new immutable version to the registry. The repo is itself a working example module so its own CI is green. How: - reusable-ci.yml: download and checksum-verify the wippy binary, run wippy lint and wippy test, and a Conventional-Commit PR-title check. - reusable-release.yml: release-please (simple type, yaml extra-files updater for wippy.yaml $.version) and, in the same run, wippy publish --create. - Thin callers (ci.yml, release.yml) reference the central workflows at @v1; self-test.yml exercises them on the template itself; tag-major.yml keeps the floating v1 tag in sync. - Example module under src/ with a greeting library, a test, and a runner; wippy.yaml excludes the test namespace from the published artifact. - Remove legacy packcli.yaml and publish.yml. --- .github/CONTRIBUTING.md | 21 ++++ .github/workflows/ci.yml | 12 +++ .github/workflows/publish.yml | 24 ----- .github/workflows/release.yml | 15 +++ .github/workflows/reusable-ci.yml | 76 +++++++++++++ .github/workflows/reusable-release.yml | 75 +++++++++++++ .github/workflows/self-test.yml | 12 +++ .github/workflows/tag-major.yml | 26 +++++ .gitignore | 2 + .release-please-manifest.json | 3 + README.md | 144 ++++++++++++++++++++----- packcli.yaml | 3 - release-please-config.json | 18 ++++ src/README.md | 8 ++ src/_index.yaml | 18 +++- src/greeting.lua | 7 ++ src/test/_index.yaml | 46 ++++++++ src/test/greeting_test.lua | 12 +++ src/test/runner.lua | 61 +++++++++++ wippy.lock | 3 + wippy.yaml | 12 +++ 21 files changed, 540 insertions(+), 58 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/publish.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/reusable-ci.yml create mode 100644 .github/workflows/reusable-release.yml create mode 100644 .github/workflows/self-test.yml create mode 100644 .github/workflows/tag-major.yml create mode 100644 .release-please-manifest.json delete mode 100644 packcli.yaml create mode 100644 release-please-config.json create mode 100644 src/README.md create mode 100644 src/greeting.lua create mode 100644 src/test/_index.yaml create mode 100644 src/test/greeting_test.lua create mode 100644 src/test/runner.lua create mode 100644 wippy.lock create mode 100644 wippy.yaml diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index e69de29..48188c7 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,21 @@ +# Contributing + +Pull requests are squash-merged, so the pull request title becomes the commit on `main` and drives +the next version. The title must be a valid [Conventional Commit][conventional-commits]; CI rejects +it otherwise. + +Allowed types: `feat`, `fix`, `chore`, `refactor`, `docs`, `test`, `perf`, `build`, `ci`, `style`, +`revert`. + +- `fix:` triggers a patch release. +- `feat:` triggers a minor release. +- A `feat!:` prefix or a `BREAKING CHANGE:` footer triggers a major release. + +Before opening a pull request, run the checks locally from the module directory: + +```sh +wippy lint --level error +wippy test +``` + +[conventional-commits]: https://www.conventionalcommits.org diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f7e26b0 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,12 @@ +name: CI + +on: + pull_request: + types: [opened, edited, synchronize, reopened] + push: + branches: [main] + +jobs: + ci: + if: ${{ github.repository != 'wippyai/template-module' }} + uses: wippyai/template-module/.github/workflows/reusable-ci.yml@v1 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index e2f935e..0000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Publish in the Wippy Registry - -on: - push: - tags: - - 'v*' - -concurrency: - group: publish - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - name: Check out repository - uses: actions/checkout@v5 - - - name: Publishing - uses: wippyai/action-module-release@main - with: - directory: . - tag: ${{ github.ref_name }} - username: ${{ secrets.WIPPY_USERNAME }} - password: ${{ secrets.WIPPY_PASSWORD }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..793e5f8 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,15 @@ +name: Release + +on: + push: + branches: [main] + +permissions: + contents: write + pull-requests: write + +jobs: + release: + if: ${{ github.repository != 'wippyai/template-module' }} + uses: wippyai/template-module/.github/workflows/reusable-release.yml@v1 + secrets: inherit diff --git a/.github/workflows/reusable-ci.yml b/.github/workflows/reusable-ci.yml new file mode 100644 index 0000000..495efc9 --- /dev/null +++ b/.github/workflows/reusable-ci.yml @@ -0,0 +1,76 @@ +name: Reusable CI + +on: + workflow_call: + inputs: + wippy-version: + description: Tag of the wippyai/runtime release providing the wippy binary + type: string + default: v0.3.12a + module-dir: + description: Directory containing wippy.yaml + type: string + default: . + lint-level: + description: Minimum lint severity that fails the build (error, warning, hint) + type: string + default: error + +permissions: + contents: read + +jobs: + lint-and-test: + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v7 + + - name: Install wippy runtime + env: + GH_TOKEN: ${{ github.token }} + WIPPY_VERSION: ${{ inputs.wippy-version }} + run: | + set -euo pipefail + tmp="$(mktemp -d)" + gh release download "$WIPPY_VERSION" --repo wippyai/runtime \ + --pattern wippy-linux-amd64 --pattern SHA256SUMS --dir "$tmp" + ( cd "$tmp" && sha256sum --ignore-missing -c SHA256SUMS ) + mkdir -p "$HOME/.wippy/bin" + install -m 0755 "$tmp/wippy-linux-amd64" "$HOME/.wippy/bin/wippy" + echo "$HOME/.wippy/bin" >> "$GITHUB_PATH" + "$HOME/.wippy/bin/wippy" version + + - name: Lint + working-directory: ${{ inputs.module-dir }} + env: + LINT_LEVEL: ${{ inputs.lint-level }} + run: wippy lint --level "$LINT_LEVEL" --no-color + + - name: Test + working-directory: ${{ inputs.module-dir }} + run: wippy test --silent + + pr-title: + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + permissions: + pull-requests: read + steps: + - name: Enforce Conventional Commit pull request title + uses: amannn/action-semantic-pull-request@v6 + env: + GITHUB_TOKEN: ${{ github.token }} + with: + types: | + feat + fix + chore + refactor + docs + test + perf + build + ci + style + revert diff --git a/.github/workflows/reusable-release.yml b/.github/workflows/reusable-release.yml new file mode 100644 index 0000000..29ff498 --- /dev/null +++ b/.github/workflows/reusable-release.yml @@ -0,0 +1,75 @@ +name: Reusable Release + +on: + workflow_call: + inputs: + wippy-version: + description: Tag of the wippyai/runtime release providing the wippy binary + type: string + default: v0.3.12a + module-dir: + description: Directory containing wippy.yaml + type: string + default: . + publish: + description: Publish to the registry when a release is cut + type: boolean + default: true + secrets: + WIPPY_TOKEN: + description: Registry token used by wippy publish + required: false + WIPPY_REGISTRY: + description: Registry URL override (defaults to the wippy built-in) + required: false + +permissions: + contents: write + pull-requests: write + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Run release-please + id: release + uses: googleapis/release-please-action@v5 + with: + token: ${{ github.token }} + config-file: release-please-config.json + manifest-file: .release-please-manifest.json + + - name: Check out the released revision + if: ${{ steps.release.outputs.release_created && inputs.publish }} + uses: actions/checkout@v7 + + - name: Install wippy runtime + if: ${{ steps.release.outputs.release_created && inputs.publish }} + env: + GH_TOKEN: ${{ github.token }} + WIPPY_VERSION: ${{ inputs.wippy-version }} + run: | + set -euo pipefail + tmp="$(mktemp -d)" + gh release download "$WIPPY_VERSION" --repo wippyai/runtime \ + --pattern wippy-linux-amd64 --pattern SHA256SUMS --dir "$tmp" + ( cd "$tmp" && sha256sum --ignore-missing -c SHA256SUMS ) + mkdir -p "$HOME/.wippy/bin" + install -m 0755 "$tmp/wippy-linux-amd64" "$HOME/.wippy/bin/wippy" + echo "$HOME/.wippy/bin" >> "$GITHUB_PATH" + + - name: Publish to the Wippy registry + if: ${{ steps.release.outputs.release_created && inputs.publish }} + working-directory: ${{ inputs.module-dir }} + env: + WIPPY_TOKEN: ${{ secrets.WIPPY_TOKEN }} + WIPPY_REGISTRY: ${{ secrets.WIPPY_REGISTRY }} + VERSION: ${{ steps.release.outputs.version }} + run: | + set -euo pipefail + if [ -z "${WIPPY_TOKEN:-}" ]; then + echo "WIPPY_TOKEN secret is required to publish" >&2 + exit 1 + fi + [ -z "${WIPPY_REGISTRY:-}" ] && unset WIPPY_REGISTRY + wippy publish --version "$VERSION" --create diff --git a/.github/workflows/self-test.yml b/.github/workflows/self-test.yml new file mode 100644 index 0000000..19a3fdd --- /dev/null +++ b/.github/workflows/self-test.yml @@ -0,0 +1,12 @@ +name: Self Test + +on: + pull_request: + types: [opened, edited, synchronize, reopened] + push: + branches: [main] + +jobs: + ci: + if: ${{ github.repository == 'wippyai/template-module' }} + uses: ./.github/workflows/reusable-ci.yml diff --git a/.github/workflows/tag-major.yml b/.github/workflows/tag-major.yml new file mode 100644 index 0000000..54fa597 --- /dev/null +++ b/.github/workflows/tag-major.yml @@ -0,0 +1,26 @@ +name: Update major tag + +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + +permissions: + contents: write + +jobs: + move-major: + if: ${{ github.repository == 'wippyai/template-module' }} + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v7 + + - name: Move the major tag to this release + env: + TAG: ${{ github.ref_name }} + run: | + set -euo pipefail + major="${TAG%%.*}" + git tag -f "$major" + git push -f origin "refs/tags/$major" diff --git a/.gitignore b/.gitignore index dc9ef66..9e4011e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ /runtime /dist /build +/.wippy +*.wapp # Dependency directories /node_modules diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..e18ee07 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.0.0" +} diff --git a/README.md b/README.md index 7dcc9c1..c8927b0 100644 --- a/README.md +++ b/README.md @@ -6,51 +6,137 @@

-

{Package Name}

+

Wippy Module Template

-[![Latest Release](https://img.shields.io/github/v/release/{organization}/{repo}?style=flat-square)][releases-page] -[![License](https://img.shields.io/github/license/{organization}/{repo}?style=flat-square)](LICENSE) +[![License](https://img.shields.io/github/license/wippyai/template-module?style=flat-square)](LICENSE) [![Documentation](https://img.shields.io/badge/Wippy-Documentation-brightgreen.svg?style=flat-square)][wippy-documentation]
-{description} +A template for building, testing, and publishing a [Wippy][wippy] module. It ships GitHub Actions +that lint and test the module on every pull request, then automatically version and publish it to +the [Wippy registry][modules-registry] on every merge to `main`, driven by +[Conventional Commits][conventional-commits]. ---- +The repository is itself a working example module (a `greeting` library plus a test) so its own CI +is green out of the box. Replace the example with your code. -## TODO +## What you get -### Prepare the repository +- Lint and test on every pull request, using the `wippy` CLI from the [runtime][runtime]. +- The pull request title is enforced to be a valid Conventional Commit. +- Automated semantic versioning and registry publishing via [release-please][release-please]. +- A single source of truth: the heavy lifting lives in reusable workflows hosted here; modules + call them by reference, so the pipeline is updated in one place for every module. -- Replace all `{repo}` with the actual repository name. -- Replace all `{organization}` with the actual organization name (e.g., `wippyai`). -- Replace all `{vendor}` with the actual module namespace (e.g., `wippy`). -- Replace all `{package}` with the actual package name (e.g., `llm`). -- Replace all `{Package Name}` with the actual human-readable package name. -- Replace the `{description}` with the actual package short description. -- Fill the `.github/CODEOWNERS` with the actual owners. - - Configure access to the repository for the owners in https://github.com/{organization}/{repo}/settings/access -- Check that the `LICENSE` file is present and contains the correct license information. -- Customize the `CONTRIBUTING.md` file with the actual contribution guidelines or remove it if not needed. -- Fill the blocks below with the actual information or remove them if not needed. +## Versioning -### Register the module in the Wippy registry +The version lives in `wippy.yaml` and is bumped automatically from the Conventional Commit +messages merged since the last release: -The repository uses the [Github Action](https://github.com/wippyai/action-module-release) -to automatically register the module in the [Wippy registry][modules-registry] on every release. -To trigger the registration, you need to create a new tag in the format `vX.Y.Z` (e.g., `v1.0.0`). +| Commit | Bump | Example | +| --- | --- | --- | +| `fix:` | patch | `1.2.3` to `1.2.4` | +| `feat:` | minor | `1.2.3` to `1.3.0` | +| `feat!:` or a `BREAKING CHANGE:` footer | major | `1.2.3` to `2.0.0` | +| `chore:`, `docs:`, `refactor:`, `test:`, `ci:`, ... | none on their own | no release | -Before you can use the action, you need [to set up](https://github.com/{organization}/{repo}/settings/secrets/actions) the following repository secrets. +Because merges use squash, the pull request title becomes the commit message, which is why a valid +Conventional Commit title is required. -- `WIPPY_USERNAME` - the UUID of the user in the Wippy registry. -- `WIPPY_PASSWORD` - the password for the user in the Wippy registry. +Pre-1.0 note: by default a breaking change on a `0.x` module bumps to `1.0.0`. To remain in `0.x`, +add `"bump-minor-pre-major": true` to `release-please-config.json`. -Log in [Wippy registry][modules-registry] and use [PackCli tool][packcli] to register the new module. +## How a release happens ---- +1. Merge pull requests to `main` as usual. +2. release-please opens and maintains a release pull request that bumps `version` in `wippy.yaml`, + updates `CHANGELOG.md`, and updates `.release-please-manifest.json`. +3. Merge the release pull request. release-please creates the git tag and GitHub release, and the + same workflow run then executes `wippy publish`, pushing the new immutable version to the + registry. (Publishing happens in the same run, so it does not depend on the tag event.) +## Use this template for a new module + +1. Click "Use this template". +2. In `wippy.yaml`, set `organization`, `module`, and `description`. Keep `version` at `0.0.0` and + keep `.release-please-manifest.json` at `0.0.0`; release-please takes over from there. +3. Rename the namespace in `src/_index.yaml` and `src/test/_index.yaml`, and update the `exclude` + entry pattern in `wippy.yaml` so it matches your test namespace (it keeps test-only entries out + of the published artifact). +4. Set `package-name` in `release-please-config.json`. +5. Replace the example `greeting` library and `src/test` with your own entries and tests. Update + `src/README.md`, which is the registry-facing README referenced by the `ns.definition` entry. +6. Add a `WIPPY_TOKEN` repository secret (a registry token). Optionally add `WIPPY_REGISTRY` to + target a non-default registry. +7. `.github/workflows/ci.yml` and `release.yml` already call the central reusable workflows at + `wippyai/template-module@v1`. You may delete `self-test.yml` and the two `reusable-*.yml` + files; they only run inside this template repository. + +## Migrate an existing module + +Add the following to your module repository and open a pull request: + +- `wippy.yaml` with `organization`, `module`, `version`, the `ns.definition` README reference, and + any `exclude` patterns for test-only entries. +- `release-please-config.json` (set `package-name` to your module) and + `.release-please-manifest.json`, seeding the manifest with the module's current published version. +- `.github/workflows/ci.yml` and `.github/workflows/release.yml` copied from this template (they + reference `wippyai/template-module@v1`). +- A `WIPPY_TOKEN` repository secret. + +## Notes + +- The release pull request is opened by `GITHUB_TOKEN`, so `pull_request` workflows do not run on + it. Do not make the CI or PR-title checks required status checks on the release pull request, or + it will be unmergeable; otherwise use a PAT for release-please. +- CI downloads the `wippy` binary from public `wippyai/runtime` releases using the repository + `GITHUB_TOKEN`. + +## Maintaining this template + +The reusable workflows are consumed at `wippyai/template-module@v1`. Releasing the template means +pushing a `vX.Y.Z` tag on `main`; `tag-major.yml` then moves the floating `v1` tag to it, so +consumers pinned to `@v1` pick up the change. Create the first `v1` line with: + +```sh +git tag v1.0.0 && git push origin v1.0.0 +``` + +## Local development + +Install the `wippy` CLI by downloading a release binary from [runtime releases][runtime-releases], +then from the module directory: + +```sh +wippy lint --level error +wippy test +wippy publish --dry-run --version 0.1.0 +``` + +## Layout + +``` +. +├── .github/workflows/ +│ ├── ci.yml # calls the reusable CI on pull requests and pushes +│ ├── release.yml # calls the reusable release on pushes to main +│ ├── reusable-ci.yml # lint, test, and PR-title check (reusable) +│ ├── reusable-release.yml # release-please and publish (reusable) +│ ├── self-test.yml # runs the reusable CI against this template only +│ └── tag-major.yml # moves the v1 tag when a vX.Y.Z tag is pushed (template only) +├── release-please-config.json +├── .release-please-manifest.json +├── wippy.yaml # module manifest (organization, module, version) +├── wippy.lock # dependency lock +└── src/ # module source and tests +``` + +[wippy]: https://wippy.ai [wippy-documentation]: https://docs.wippy.ai -[releases-page]: https://github.com/{organization}/{repo}/releases -[packcli]: https://github.com/wippyai/wippy-releases/releases [modules-registry]: https://modules.wippy.ai +[conventional-commits]: https://www.conventionalcommits.org +[release-please]: https://github.com/googleapis/release-please +[runtime]: https://github.com/wippyai/runtime +[runtime-releases]: https://github.com/wippyai/runtime/releases diff --git a/packcli.yaml b/packcli.yaml deleted file mode 100644 index 74701ea..0000000 --- a/packcli.yaml +++ /dev/null @@ -1,3 +0,0 @@ -name: {vendor}/{package} -dir: src -description: {description} diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..171639e --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "release-type": "simple", + "packages": { + ".": { + "package-name": "example", + "changelog-path": "CHANGELOG.md", + "include-component-in-tag": false, + "extra-files": [ + { + "type": "yaml", + "path": "wippy.yaml", + "jsonpath": "$.version" + } + ] + } + } +} diff --git a/src/README.md b/src/README.md new file mode 100644 index 0000000..48feae6 --- /dev/null +++ b/src/README.md @@ -0,0 +1,8 @@ +# Example Module + +This is the registry-facing README for the published Wippy module. It is referenced by the +`ns.definition` entry in `src/_index.yaml` (`readme: file://README.md`) and rendered on the +module page in the Wippy registry. + +Replace this text with documentation for your module: what it provides, the entries it exposes, +and how to depend on it from another Wippy application. diff --git a/src/_index.yaml b/src/_index.yaml index 8b33e64..b45d414 100644 --- a/src/_index.yaml +++ b/src/_index.yaml @@ -1,2 +1,16 @@ -version: "1.0.0" -namespace: {vendor}.{package} +version: "1.0" +namespace: wippy.example + +entries: + - name: definition + kind: ns.definition + readme: file://README.md + meta: + title: Example Module + description: Example Wippy module scaffolded from the template + + - name: greeting + kind: library.lua + meta: + comment: Example greeting library shipped by the module + source: file://greeting.lua diff --git a/src/greeting.lua b/src/greeting.lua new file mode 100644 index 0000000..ddecff9 --- /dev/null +++ b/src/greeting.lua @@ -0,0 +1,7 @@ +local greeting = {} + +function greeting.greet(name) + return "Hello, " .. tostring(name) .. "!" +end + +return greeting diff --git a/src/test/_index.yaml b/src/test/_index.yaml new file mode 100644 index 0000000..0251aac --- /dev/null +++ b/src/test/_index.yaml @@ -0,0 +1,46 @@ +version: "1.0" +namespace: wippy.example.test + +entries: + - name: processes + kind: process.host + meta: + comment: Process host used to run the test entrypoint + host: + max_processes: 1000 + workers: 8 + lifecycle: + auto_start: true + + - name: terminal + kind: terminal.host + meta: + comment: Terminal host used to exec the test runner + lifecycle: + auto_start: true + + - name: runner + kind: process.lua + meta: + comment: Discovers and runs every entry with meta.type=test + command: + name: test + short: Run module tests + use_case: test + source: file://runner.lua + method: main + modules: + - io + - registry + - funcs + + - name: greeting + kind: function.lua + meta: + type: test + suite: greeting + description: greeting.greet returns a formatted greeting + source: file://greeting_test.lua + method: main + imports: + greeting: wippy.example:greeting diff --git a/src/test/greeting_test.lua b/src/test/greeting_test.lua new file mode 100644 index 0000000..a4f7ed8 --- /dev/null +++ b/src/test/greeting_test.lua @@ -0,0 +1,12 @@ +local greeting = require("greeting") + +local function main() + local result = greeting.greet("Wippy") + if result ~= "Hello, Wippy!" then + error("expected 'Hello, Wippy!', got '" .. tostring(result) .. "'") + end + + return true +end + +return { main = main } diff --git a/src/test/runner.lua b/src/test/runner.lua new file mode 100644 index 0000000..b4d7f98 --- /dev/null +++ b/src/test/runner.lua @@ -0,0 +1,61 @@ +local io = require("io") +local registry = require("registry") +local funcs = require("funcs") + +local function run_one(id) + local ok, result, err = pcall(function() + return funcs.call(id) + end) + + if not ok then + return false, tostring(result) + end + + if err then + return false, tostring(err) + end + + if result == false then + return false, "test returned false" + end + + return true, nil +end + +local function main() + local entries, err = registry.find({ ["meta.type"] = "test" }) + if err then + io.print("registry error: " .. tostring(err)) + return 1 + end + + if not entries or #entries == 0 then + io.print("no tests found") + return 0 + end + + local passed = 0 + local failed = 0 + + for _, entry in ipairs(entries) do + local ok, message = run_one(entry.id) + if ok then + passed = passed + 1 + io.print("PASS " .. entry.id) + else + failed = failed + 1 + io.print("FAIL " .. entry.id .. ": " .. tostring(message)) + end + end + + io.print("") + io.print(tostring(passed) .. " passed, " .. tostring(failed) .. " failed") + + if failed > 0 then + return 1 + end + + return 0 +end + +return { main = main } diff --git a/wippy.lock b/wippy.lock new file mode 100644 index 0000000..0f48e24 --- /dev/null +++ b/wippy.lock @@ -0,0 +1,3 @@ +directories: + modules: .wippy + src: ./src diff --git a/wippy.yaml b/wippy.yaml new file mode 100644 index 0000000..3f1b9e3 --- /dev/null +++ b/wippy.yaml @@ -0,0 +1,12 @@ +organization: wippy +module: example +version: 0.0.0 +description: Example Wippy module scaffolded from the template +license: MPL-2.0 +repository: https://github.com/wippyai/template-module +homepage: https://wippy.ai +keywords: + - example + - template +exclude: + - wippy.example.test:* From 3f117dab43f96383c3a80f829e9dd86c19b25922 Mon Sep 17 00:00:00 2001 From: Rodrigo Delduca Date: Fri, 26 Jun 2026 14:10:17 -0300 Subject: [PATCH 2/3] fix(ci): let release-please use RELEASE_PLEASE_TOKEN to open the release PR Why: The wippyai org blocks GitHub Actions from creating pull requests, so release-please cannot open the release PR with the default GITHUB_TOKEN (verified end-to-end on a throwaway consumer repo). What: Accept an optional RELEASE_PLEASE_TOKEN secret and pass it to release-please, falling back to GITHUB_TOKEN when absent. Document the requirement. --- .github/workflows/reusable-release.yml | 5 ++++- README.md | 17 +++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/.github/workflows/reusable-release.yml b/.github/workflows/reusable-release.yml index 29ff498..313c592 100644 --- a/.github/workflows/reusable-release.yml +++ b/.github/workflows/reusable-release.yml @@ -16,6 +16,9 @@ on: type: boolean default: true secrets: + RELEASE_PLEASE_TOKEN: + description: Token (PAT or GitHub App) for release-please to open the release PR + required: false WIPPY_TOKEN: description: Registry token used by wippy publish required: false @@ -35,7 +38,7 @@ jobs: id: release uses: googleapis/release-please-action@v5 with: - token: ${{ github.token }} + token: ${{ secrets.RELEASE_PLEASE_TOKEN || github.token }} config-file: release-please-config.json manifest-file: .release-please-manifest.json diff --git a/README.md b/README.md index c8927b0..a09cc39 100644 --- a/README.md +++ b/README.md @@ -68,8 +68,8 @@ add `"bump-minor-pre-major": true` to `release-please-config.json`. 4. Set `package-name` in `release-please-config.json`. 5. Replace the example `greeting` library and `src/test` with your own entries and tests. Update `src/README.md`, which is the registry-facing README referenced by the `ns.definition` entry. -6. Add a `WIPPY_TOKEN` repository secret (a registry token). Optionally add `WIPPY_REGISTRY` to - target a non-default registry. +6. Add a `WIPPY_TOKEN` repository secret (a registry token) and a `RELEASE_PLEASE_TOKEN` secret + (see Notes). Optionally add `WIPPY_REGISTRY` to target a non-default registry. 7. `.github/workflows/ci.yml` and `release.yml` already call the central reusable workflows at `wippyai/template-module@v1`. You may delete `self-test.yml` and the two `reusable-*.yml` files; they only run inside this template repository. @@ -84,13 +84,18 @@ Add the following to your module repository and open a pull request: `.release-please-manifest.json`, seeding the manifest with the module's current published version. - `.github/workflows/ci.yml` and `.github/workflows/release.yml` copied from this template (they reference `wippyai/template-module@v1`). -- A `WIPPY_TOKEN` repository secret. +- `WIPPY_TOKEN` and `RELEASE_PLEASE_TOKEN` repository secrets (see Notes). ## Notes -- The release pull request is opened by `GITHUB_TOKEN`, so `pull_request` workflows do not run on - it. Do not make the CI or PR-title checks required status checks on the release pull request, or - it will be unmergeable; otherwise use a PAT for release-please. +- The `wippyai` organization blocks GitHub Actions from creating pull requests, so release-please + cannot open the release pull request with the default `GITHUB_TOKEN`. Add a `RELEASE_PLEASE_TOKEN` + secret (a PAT, or a GitHub App token via `actions/create-github-app-token`) with `contents` and + `pull-requests` write; the reusable release workflow uses it and falls back to `GITHUB_TOKEN` + otherwise. Alternatively, enable "Allow GitHub Actions to create and approve pull requests". +- The release pull request is opened by a token, so `pull_request` workflows do not run on it. Do + not make the CI or PR-title checks required status checks on the release pull request, or it will + be unmergeable. - CI downloads the `wippy` binary from public `wippyai/runtime` releases using the repository `GITHUB_TOKEN`. From aa994913c601397e3e8a205e15e826b47ecd56ec Mon Sep 17 00:00:00 2001 From: Rodrigo Delduca Date: Fri, 26 Jun 2026 14:17:50 -0300 Subject: [PATCH 3/3] feat(ci): mint a GitHub App token for release-please Why: The wippyai org blocks Actions from creating PRs, so release-please needs a non-default credential. A GitHub App is the most secure, non-expiring option. What: When RELEASE_PLEASE_APP_ID/RELEASE_PLEASE_APP_PRIVATE_KEY secrets are present, mint a short-lived token with actions/create-github-app-token and use it for release-please; fall back to RELEASE_PLEASE_TOKEN, then GITHUB_TOKEN. Document the App setup. --- .github/workflows/reusable-release.yml | 30 ++++++++++++++++++++++++-- README.md | 25 ++++++++++++--------- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/.github/workflows/reusable-release.yml b/.github/workflows/reusable-release.yml index 313c592..36d3eab 100644 --- a/.github/workflows/reusable-release.yml +++ b/.github/workflows/reusable-release.yml @@ -16,8 +16,14 @@ on: type: boolean default: true secrets: + RELEASE_PLEASE_APP_ID: + description: GitHub App ID for release-please (preferred over RELEASE_PLEASE_TOKEN) + required: false + RELEASE_PLEASE_APP_PRIVATE_KEY: + description: GitHub App private key paired with RELEASE_PLEASE_APP_ID + required: false RELEASE_PLEASE_TOKEN: - description: Token (PAT or GitHub App) for release-please to open the release PR + description: PAT fallback for release-please when no GitHub App is configured required: false WIPPY_TOKEN: description: Registry token used by wippy publish @@ -34,11 +40,31 @@ jobs: release: runs-on: ubuntu-latest steps: + - name: Detect GitHub App configuration + id: cfg + env: + APP_ID: ${{ secrets.RELEASE_PLEASE_APP_ID }} + run: | + set -euo pipefail + if [ -n "${APP_ID:-}" ]; then + echo "use_app=true" >> "$GITHUB_OUTPUT" + else + echo "use_app=false" >> "$GITHUB_OUTPUT" + fi + + - name: Mint GitHub App token + id: app-token + if: ${{ steps.cfg.outputs.use_app == 'true' }} + uses: actions/create-github-app-token@v3 + with: + app-id: ${{ secrets.RELEASE_PLEASE_APP_ID }} + private-key: ${{ secrets.RELEASE_PLEASE_APP_PRIVATE_KEY }} + - name: Run release-please id: release uses: googleapis/release-please-action@v5 with: - token: ${{ secrets.RELEASE_PLEASE_TOKEN || github.token }} + token: ${{ steps.app-token.outputs.token || secrets.RELEASE_PLEASE_TOKEN || github.token }} config-file: release-please-config.json manifest-file: .release-please-manifest.json diff --git a/README.md b/README.md index a09cc39..74724c4 100644 --- a/README.md +++ b/README.md @@ -68,8 +68,9 @@ add `"bump-minor-pre-major": true` to `release-please-config.json`. 4. Set `package-name` in `release-please-config.json`. 5. Replace the example `greeting` library and `src/test` with your own entries and tests. Update `src/README.md`, which is the registry-facing README referenced by the `ns.definition` entry. -6. Add a `WIPPY_TOKEN` repository secret (a registry token) and a `RELEASE_PLEASE_TOKEN` secret - (see Notes). Optionally add `WIPPY_REGISTRY` to target a non-default registry. +6. Add a `WIPPY_TOKEN` repository secret (a registry token) and the release-please App secrets + `RELEASE_PLEASE_APP_ID` / `RELEASE_PLEASE_APP_PRIVATE_KEY` (or a `RELEASE_PLEASE_TOKEN` PAT) — + see Notes. Optionally add `WIPPY_REGISTRY` to target a non-default registry. 7. `.github/workflows/ci.yml` and `release.yml` already call the central reusable workflows at `wippyai/template-module@v1`. You may delete `self-test.yml` and the two `reusable-*.yml` files; they only run inside this template repository. @@ -84,18 +85,22 @@ Add the following to your module repository and open a pull request: `.release-please-manifest.json`, seeding the manifest with the module's current published version. - `.github/workflows/ci.yml` and `.github/workflows/release.yml` copied from this template (they reference `wippyai/template-module@v1`). -- `WIPPY_TOKEN` and `RELEASE_PLEASE_TOKEN` repository secrets (see Notes). +- `WIPPY_TOKEN` and the release-please App secrets `RELEASE_PLEASE_APP_ID` / + `RELEASE_PLEASE_APP_PRIVATE_KEY` (or a `RELEASE_PLEASE_TOKEN` PAT) — see Notes. ## Notes - The `wippyai` organization blocks GitHub Actions from creating pull requests, so release-please - cannot open the release pull request with the default `GITHUB_TOKEN`. Add a `RELEASE_PLEASE_TOKEN` - secret (a PAT, or a GitHub App token via `actions/create-github-app-token`) with `contents` and - `pull-requests` write; the reusable release workflow uses it and falls back to `GITHUB_TOKEN` - otherwise. Alternatively, enable "Allow GitHub Actions to create and approve pull requests". -- The release pull request is opened by a token, so `pull_request` workflows do not run on it. Do - not make the CI or PR-title checks required status checks on the release pull request, or it will - be unmergeable. + cannot open the release pull request with the default `GITHUB_TOKEN`. Configure a GitHub App + (recommended): create an App with `contents: write` and `pull requests: write`, install it on the + repository, and set `RELEASE_PLEASE_APP_ID` and `RELEASE_PLEASE_APP_PRIVATE_KEY` as + org/repository secrets. The reusable release workflow mints a short-lived token from the App. A + `RELEASE_PLEASE_TOKEN` PAT is supported as a fallback, and it falls back to `GITHUB_TOKEN` if + neither is set (which only works if the org enables "Allow GitHub Actions to create and approve + pull requests"). +- A release pull request opened with a GitHub App or PAT token triggers `pull_request` workflows; + one opened with `GITHUB_TOKEN` does not. Configure the App or PAT if you want the CI and PR-title + checks to run on the release pull request. - CI downloads the `wippy` binary from public `wippyai/runtime` releases using the repository `GITHUB_TOKEN`.