Looking for a step-by-step author guide? See the MultiworldGG-Index docs site — "I want the easiest setup" or "I want to write my own
pyproject.toml".This README is the workflow reference (inputs, outputs, version-pinning rules).
Reusable GitHub workflow for per-world MultiworldGG repos. When you want to release a world this action:
- Reads
worlds/<apworld>/archipelago.jsonfrom your repo at the release tag's checked-out source - Builds a pip-installable wheel (
<dist>-<world_version>-py3-none-any.whl) - Uploads the wheel as an asset on the GitHub release
The matching MultiworldGG-Index PR is opened separately by the
Oliver-Multiworld-Squirrel GitHub App when it sees this workflow's
workflow_run.completed event. Karen's review checks fire automatically once
that PR is open. None of that lives in this action.
In your per-world repo, create .github/workflows/make_pyproject.yml:
name: Create and Release Python Package
on:
release:
types: [published]
workflow_dispatch: {}
permissions:
contents: write # for `gh release upload`
jobs:
publish:
uses: MultiworldGG/gen-pymod-release/.github/workflows/build.yml@v3
# No `with:` needed for single-world repos.
# No `secrets:` — no Oliver secrets needed.For repos that ship a single pip-installable Python package at the repo root
(a pyproject.toml plus a top-level package directory — no worlds/<apworld>/
shape), use the sibling build-wheel.yml workflow. It rewrites
pyproject.toml:[project].version in the runner to the input version, builds
the wheel, tags v<version>, creates a GitHub Release, and uploads the wheel
as an asset. The committed pyproject.toml is not modified — the workflow
input is the single source of truth.
In your client repo, create .github/workflows/release.yml:
name: Release wheel
on:
workflow_dispatch:
inputs:
version:
description: "Version to release (e.g. 0.1.1). Tag will be v<version>."
required: true
type: string
dry-run:
description: "Build only -- skip tag, release, and asset upload."
required: false
type: boolean
default: false
permissions:
contents: write
jobs:
build-and-release:
uses: MultiworldGG/gen-pymod-release/.github/workflows/build-wheel.yml@v3
with:
version: ${{ inputs.version }}
dry-run: ${{ inputs.dry-run }}Inputs accepted by build-wheel.yml:
| name | required | default | notes |
|---|---|---|---|
version |
yes | — | Becomes both the tag (v<version>) and the wheel's metadata version. |
source-ref |
github.sha |
Caller-repo ref to build from. | |
python-version |
"3.13" |
Build interpreter. Pure-Python wheels are py3-none-any regardless. |
|
dry-run |
false |
Build the wheel, skip tag/release/upload. |
Release tag prefix <apworld>-<world_version>: The action requires the release event and
parses the apworld from the tag. Tag your release as e.g. generic-1.2.3
to publish worlds/generic/ at 1.2.3. This is the recommended path
for repos that ship multiple worlds out of one repo (such as
TheLX5/Archipelago).
The Oliver-Multiworld-Squirrel GitHub App must also be installed on your repo
(read-only) so it can see the workflow_run.completed event and open the
Index PR on your behalf. Install it from
https://github.com/apps/oliver-multiworld-squirrel.
A .apworld is the zipped-folder format end-users drop into custom_worlds/.
It is built by Launcher.py "Build APWorlds" and is separate from the
pip-installable wheel that Oliver consumes for the Index PR.
Add a second job to your make_pyproject.yml to produce both artifacts from
a single release:
name: Create and Release Python Package
on:
release:
types: [published]
workflow_dispatch: {}
permissions:
contents: write
jobs:
publish-wheel:
uses: MultiworldGG/gen-pymod-release/.github/workflows/build.yml@v3
publish-apworld:
uses: MultiworldGG/gen-pymod-release/.github/workflows/build-apworld.yml@v3
with:
game: "Your Game Name"publish-wheel is what Oliver consumes (the wheel URL gets a #sha256= pin in
the Index PR). publish-apworld is for users who install .apworld files
directly into custom_worlds/. Drop either job if you do not need it.
| name | required | default | notes |
|---|---|---|---|
game |
yes | — | The world's display name — the value of World.game in your Python class (e.g. "Clique"). Must match exactly; the Launcher looks worlds up by display name. |
mwgg-ref |
"main" |
Ref of MultiworldGG/MultiworldGG to check out as the Launcher host. Pin to a release tag (e.g. "0.6.1") for reproducibility. Always resolves against the canonical monorepo, not your fork. |
|
apworld-source-ref |
release tag, else github.sha |
Caller-repo ref to check out for the world source. Same resolution order as build.yml's source-ref. |
|
dry-run |
false |
Build the .apworld but skip gh release upload. |
A single <slug>.apworld file attached to the GitHub release as an asset. The
slug is inferred from the release tag prefix (<slug>-<version>). The upload
uses --clobber — unlike the wheel, the .apworld is not pinned by a SHA256
fragment on the Index side, so overwriting on workflow re-runs is safe.
https://github.com/<owner>/<repo>/releases/download/<release_tag>/<slug>.apworld
Your world's source must live at worlds/<apworld>/ in your repo.
archipelago.json is required at worlds/<apworld>/archipelago.json and must
include world_version.
If you ship worlds/<apworld>/pyproject.toml, it is used as-is — version,
authors, and description are injected from archipelago.json only when
missing. Otherwise a minimal default is generated.
| name | required | default | notes |
|---|---|---|---|
source-ref |
release tag, else github.sha |
What to check out from your repo. | |
dry-run |
false |
Shape + build the wheel but skip the release-asset upload. |
A single .whl file attached to the GitHub release as an asset. Its URL has
the form:
https://github.com/<owner>/<repo>/releases/download/<release_tag>/<dist>-<world_version>-py3-none-any.whl
This URL is what the Index manifest's module_location will pin to. Pip can
install it directly: pip install <url>.
The release tag is the immutability boundary. GitHub does not silently allow re-publishing a release tag at a different SHA — you must manually delete and re-create the release to do so.
The action uploads without --clobber: re-running the workflow on a release
that already has a .whl asset will fail (gh release upload refuses to
overwrite). This is deliberate — the asset bytes are pinned by a
#sha256=<hex> fragment on the consumer side once Oliver opens the Index PR,
and a silent overwrite would invalidate that pin without warning. To fix a
transient build failure on an existing release, either
gh release delete-asset <release_tag> <asset> first, or delete and recreate
the entire release. Both are explicit human actions.
To publish a new build, bump world_version in archipelago.json, tag a
new release, and let the workflow run.
Pin to a major-version tag (@v3); patch updates fast-forward v3. Breaking
changes cut a new major. Pin to a SHA (@<full-sha>) for full reproducibility.
.github/workflows/
build.yml per-world reusable workflow (workflow_call) -- worlds/<apworld>/ layout
build-apworld.yml per-world reusable workflow (workflow_call) -- builds .apworld via Launcher.py
build-wheel.yml pure-Python reusable workflow (workflow_call) -- flat-layout setuptools repos
scripts/
shape_orphan.py build the temp orphan tree from caller's worlds/<apworld>/
templates/
pyproject.toml.j2 fallback per-world pyproject (only used when caller has none)
README.md.j2 auto-generated README that ships inside the wheel
MIT (see LICENSE).