|
| 1 | +# Maintainers Guide |
| 2 | + |
| 3 | +This document describes maintenance workflows for SDK contributors and maintainers. |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## Prerequisites |
| 8 | + |
| 9 | +The following tools must be installed before running any maintenance commands. |
| 10 | + |
| 11 | +| Tool | Purpose | Install (macOS) | |
| 12 | +|------|---------|-----------------| |
| 13 | +| `buf` | Proto code generation | `brew install bufbuild/buf/buf` | |
| 14 | +| `git` | Repository operations | `brew install git` | |
| 15 | +| `make` | Task runner | bundled with Xcode CLT | |
| 16 | +| `poetry` | Python packaging | [python-poetry.org/docs](https://python-poetry.org/docs/#installation) | |
| 17 | +| Python 3.10+ | Runtime | `brew install python` | |
| 18 | + |
| 19 | +> **macOS only**: The `fix-generated-proto-imports` step inside `make gen` uses the BSD `sed -i ""` syntax. On Linux, `sed -i ""` must be replaced with `sed -i`. All maintainers are expected to run proto generation on macOS or adapt the command in the `Makefile` accordingly. |
| 20 | +
|
| 21 | +--- |
| 22 | + |
| 23 | +## Regenerating the proto bindings |
| 24 | + |
| 25 | +The generated Python bindings in `pyinjective/proto/` are produced from `.proto` source files pulled from several upstream repositories. |
| 26 | + |
| 27 | +### Step 1 — Update version references |
| 28 | + |
| 29 | +Two files control which upstream versions are used: |
| 30 | + |
| 31 | +**`Makefile`** — controls the injective-indexer gRPC proto files: |
| 32 | + |
| 33 | +```makefile |
| 34 | +clone-injective-indexer: |
| 35 | + git clone https://github.com/InjectiveLabs/injective-indexer.git -b <tag> --depth 1 --single-branch |
| 36 | +``` |
| 37 | + |
| 38 | +Update the `-b` tag to the desired `injective-indexer` release (e.g. `v1.19.0`). |
| 39 | + |
| 40 | +**`buf.gen.yaml`** — controls all other proto sources via the `inputs:` block. Bump the relevant tags for: |
| 41 | + |
| 42 | +- `injective-core` (most common change) |
| 43 | +- `ibc-go`, `wasmd`, `cometbft`, `cosmos-sdk` (when a protocol upgrade requires it) |
| 44 | + |
| 45 | +Example `buf.gen.yaml` inputs entry to update: |
| 46 | + |
| 47 | +```yaml |
| 48 | +- git_repo: https://github.com/InjectiveLabs/injective-core |
| 49 | + tag: v1.19.0 |
| 50 | + subdir: proto |
| 51 | +``` |
| 52 | + |
| 53 | +### Step 2 — Run generation |
| 54 | + |
| 55 | +```bash |
| 56 | +make gen |
| 57 | +``` |
| 58 | + |
| 59 | +This single command runs the full pipeline: |
| 60 | + |
| 61 | +```mermaid |
| 62 | +flowchart LR |
| 63 | + bumpVersions["Bump tags in Makefile + buf.gen.yaml"] --> makeGen["make gen"] |
| 64 | + makeGen --> cloneAll["clone-all\n(injective-indexer)"] |
| 65 | + cloneAll --> copyProto["copy-proto\n(.proto → proto/exchange/)"] |
| 66 | + copyProto --> bufGen["buf generate\n(buf.gen.yaml inputs)"] |
| 67 | + bufGen --> cleanup["remove proto/\nand injective-indexer/"] |
| 68 | + cleanup --> fixImports["fix-generated-proto-imports"] |
| 69 | + fixImports --> review["git diff + pytest"] |
| 70 | +``` |
| 71 | + |
| 72 | +**What each step does:** |
| 73 | + |
| 74 | +1. `clone-all` — shallow-clones the `injective-indexer` repository at the configured tag. |
| 75 | +2. `copy-proto` — deletes the previous `pyinjective/proto/` tree, then copies all `.proto` files from the cloned indexer's `api/gen/grpc` directory into `proto/exchange/`. |
| 76 | +3. `buf generate` — runs the `buf` tool against `buf.gen.yaml`, pulling proto sources from the BSR (Buf Schema Registry) and the `git_repo` inputs, then emitting Python and gRPC stubs into `pyinjective/proto/`. |
| 77 | +4. Cleanup — removes the temporary `proto/` and `injective-indexer/` directories. |
| 78 | +5. `fix-generated-proto-imports` — rewrites bare import paths (e.g. `from cosmos`) in every generated `.py` file to their package-qualified equivalents (e.g. `from pyinjective.proto.cosmos`). This step covers the modules listed in `PROTO_MODULES` in the `Makefile` plus `google.api`. |
| 79 | + |
| 80 | +### Step 3 — Verify and commit |
| 81 | + |
| 82 | +```bash |
| 83 | +git diff pyinjective/proto/ # review the generated diff |
| 84 | +poetry run pytest -v # run the full test suite |
| 85 | +``` |
| 86 | + |
| 87 | +Commit the `Makefile`, `buf.gen.yaml`, and all updated `pyinjective/proto/**` files together in a single commit. |
| 88 | + |
| 89 | +### Troubleshooting |
| 90 | + |
| 91 | +| Problem | Fix | |
| 92 | +|---------|-----| |
| 93 | +| Stale `injective-indexer/` or `proto/` directories from a failed previous run | `make clean-all` | |
| 94 | +| `buf generate` fails with auth errors on private BSR repos | Log in with `buf registry login` | |
| 95 | +| Import errors after generation | Rerun `make fix-generated-proto-imports` manually to isolate the issue | |
| 96 | + |
| 97 | +--- |
| 98 | + |
| 99 | +## Regenerating `pyinjective/ofac.json` |
| 100 | + |
| 101 | +The `pyinjective/ofac.json` file is the local snapshot of the OFAC and restricted-wallet list used by `OfacChecker`. Its upstream source is: |
| 102 | + |
| 103 | +``` |
| 104 | +https://raw.githubusercontent.com/InjectiveLabs/injective-lists/refs/heads/master/json/wallets/ofacAndRestricted.json |
| 105 | +``` |
| 106 | + |
| 107 | +Refresh the snapshot with: |
| 108 | + |
| 109 | +```bash |
| 110 | +make gen-ofac |
| 111 | +``` |
| 112 | + |
| 113 | +This calls `poetry run python pyinjective/ofac.py`, which downloads the latest list from the URL above and overwrites `pyinjective/ofac.json`. |
| 114 | + |
| 115 | +**When to refresh:** before each release, and whenever the upstream `injective-lists` repository publishes a significant update to the wallet list. |
| 116 | + |
| 117 | +Commit the updated `pyinjective/ofac.json` together with other release preparation changes. |
| 118 | + |
| 119 | +--- |
| 120 | + |
| 121 | +## Bumping the package version |
| 122 | + |
| 123 | +The `version` field in `pyproject.toml` is the exact string that Poetry uses as the package name on PyPI when publishing. Every published release must have a unique version. |
| 124 | + |
| 125 | +```toml |
| 126 | +[tool.poetry] |
| 127 | +name = "injective-py" |
| 128 | +version = "1.15.0" # ← update this before releasing |
| 129 | +``` |
| 130 | + |
| 131 | +**Versioning conventions used in this project:** |
| 132 | + |
| 133 | +| Suffix | Meaning | Example | |
| 134 | +|--------|---------|---------| |
| 135 | +| `X.Y.Z` | Stable production release | `1.15.0` | |
| 136 | +| `X.Y.Z-rcN` | Release candidate | `1.15.0-rc1` | |
| 137 | + |
| 138 | +Keep the `pyproject.toml` version bump in the same commit as the corresponding `CHANGELOG.md` entry so both are always in sync. |
| 139 | + |
| 140 | +--- |
| 141 | + |
| 142 | +## Release workflow |
| 143 | + |
| 144 | +Publishing to PyPI is fully automated via [`.github/workflows/release.yml`](.github/workflows/release.yml). |
| 145 | + |
| 146 | +### How the workflow is triggered |
| 147 | + |
| 148 | +The workflow fires on GitHub **`release: published`** events. This means it runs only when a maintainer explicitly publishes a GitHub Release — not on plain tag pushes or branch merges. |
| 149 | + |
| 150 | +### What the workflow does |
| 151 | + |
| 152 | +1. Checks out the repository at the commit the GitHub Release points to. |
| 153 | +2. Installs Python and Poetry on `ubuntu-latest`. |
| 154 | +3. Runs `poetry publish --build`, which builds the distribution and pushes it to PyPI using the `PYPI_API_TOKEN` repository secret. |
| 155 | + |
| 156 | +> **Important:** The `pyproject.toml` version in the commit targeted by the GitHub Release is what gets published. If the version was not bumped before creating the release, the wrong version will be pushed to PyPI (and PyPI will reject a re-upload of an already-existing version). |
| 157 | +
|
| 158 | +### Operational notes |
| 159 | + |
| 160 | +- The `PYPI_API_TOKEN` secret must remain valid on the repository. Rotating it when expired is a maintainer responsibility. |
| 161 | +- The release workflow does **not** run tests. Tests run automatically on every PR and merge via `run-tests.yml` and `pre-commit.yml` — ensure the branch is green before cutting a release. |
| 162 | +- GitHub pre-releases (marked as such in the UI) still trigger `release: published`, so `-rc*` versions are published to PyPI the same way as stable ones. |
| 163 | + |
| 164 | +--- |
| 165 | + |
| 166 | +## Release checklist |
| 167 | + |
| 168 | +Follow these steps in order when cutting a new SDK release: |
| 169 | + |
| 170 | +1. **Bump proto versions** — update the `injective-indexer` tag in `Makefile` and the relevant tags in `buf.gen.yaml`. |
| 171 | +2. **Regenerate protos** — run `make gen` and verify `git diff pyinjective/proto/` looks correct. |
| 172 | +3. **Refresh OFAC list** — run `make gen-ofac`. |
| 173 | +4. **Bump package version** — update `version` in `pyproject.toml` following the `X.Y.Z` / `X.Y.Z-rcN` convention. |
| 174 | +5. **Update CHANGELOG** — add a release entry to `CHANGELOG.md` in the same commit as the version bump. |
| 175 | +6. **Run tests** — `poetry run pytest -v` must pass locally. |
| 176 | +7. **Open a PR** — get the changes reviewed and merged into the target branch. |
| 177 | +8. **Create a git tag** — tag the merge commit with the release version (e.g. `v1.15.0`). |
| 178 | +9. **Publish a GitHub Release** — point it at the tag, paste the `CHANGELOG.md` entry as release notes, and click **Publish release**. The CI workflow takes care of building and uploading the package to PyPI automatically. |
| 179 | +10. **Verify** — monitor the `Publish Python distribution to PyPI` action in the Actions tab until it completes successfully. |
0 commit comments