feat: add unified release script for all packages#142
feat: add unified release script for all packages#142ashvathsureshkumar merged 5 commits intomainfrom
Conversation
Unified release script that builds and publishes all 8 packages (3 npm, 5 PyPI) with dry-run support, selective publishing, and preflight checks.
| pkg_name=$(python3 -c " | ||
| import tomllib, pathlib | ||
| p = pathlib.Path('$pkg_dir/pyproject.toml') | ||
| d = tomllib.loads(p.read_text()) | ||
| print(d['project']['name']) | ||
| ") | ||
| local version | ||
| version=$(python3 -c " | ||
| import tomllib, pathlib | ||
| p = pathlib.Path('$pkg_dir/pyproject.toml') | ||
| d = tomllib.loads(p.read_text()) | ||
| print(d['project']['version']) | ||
| ") |
There was a problem hiding this comment.
📝 Info: release_pypi relies on tomli for Python < 3.11 but doesn't verify it's installed
The release_pypi function (lines 159-167, 170-179) uses tomllib (stdlib in 3.11+) with a fallback to tomli (third-party). The preflight checks verify python3 and build tools exist, but don't verify tomli is installed for Python < 3.11. If a developer runs this on Python 3.10 without tomli installed, both imports fail and the function returns an error. In practice most developers will be on 3.11+ or will have tomli installed, and the error message from Python would be clear enough to diagnose.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Pull request overview
Adds a unified scripts/release.sh entrypoint to build and publish all Moss npm + PyPI packages with shared preflight checks and CLI flags, consolidating release steps into one workflow.
Changes:
- Introduces
scripts/release.shwith support for--dry-run,--npm-only,--pypi-only, and selective package targeting. - Implements npm publish flow (install → optional build → publish/pack) per package.
- Implements PyPI publish flow (clean → build → publish) per package, plus a summary report.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Release npm packages | ||
| for pkg in "${do_npm[@]}"; do | ||
| if ! release_npm "$PACKAGES_DIR/$pkg"; then | ||
| fail "Failed to release $pkg" | ||
| FAILED+=("$pkg") | ||
| fi | ||
| done |
There was a problem hiding this comment.
The failure-handling logic in the release loops won’t work with set -e: if any command inside release_npm fails (install/build/publish), the script will exit immediately, so the if ! release_npm ...; then branch and the FAILED summary won’t run and remaining packages won’t be attempted. Consider either removing -e and explicitly propagating errors from release_*, or temporarily disabling -e around each package release and capturing the return code so failures can be recorded while continuing.
| # Install dependencies | ||
| if [[ -f "pnpm-lock.yaml" ]]; then | ||
| pnpm install --frozen-lockfile 2>/dev/null || pnpm install | ||
| elif [[ -f "package-lock.json" ]]; then | ||
| npm ci 2>/dev/null || npm install | ||
| else | ||
| npm install | ||
| fi |
There was a problem hiding this comment.
release_npm may call pnpm install when a pnpm-lock.yaml exists, but the preflight checks don’t verify pnpm is installed. This will fail at runtime for packages like moss-md-indexer/vitepress-plugin-moss which include a pnpm lockfile. Add a preflight check for pnpm (or fallback to npm when pnpm isn’t available).
| pkg_name=$(python3 -c " | ||
| import tomllib, pathlib | ||
| p = pathlib.Path('$pkg_dir/pyproject.toml') | ||
| d = tomllib.loads(p.read_text()) | ||
| print(d['project']['name']) | ||
| ") | ||
| local version | ||
| version=$(python3 -c " | ||
| import tomllib, pathlib | ||
| p = pathlib.Path('$pkg_dir/pyproject.toml') | ||
| d = tomllib.loads(p.read_text()) | ||
| print(d['project']['version']) | ||
| ") |
There was a problem hiding this comment.
release_pypi parses pyproject.toml using tomllib, which only exists in Python 3.11+. Several packages in this repo declare requires-python >=3.10, so this script will break on supported Python versions (e.g. 3.10). Either require Python >=3.11 in preflight or implement a fallback to tomli (or another TOML parser) when tomllib isn’t available.
| fi | ||
| else | ||
| ok "uv $(uv --version 2>&1 | awk '{print $2}')" | ||
| fi | ||
|
|
||
| if ! command -v twine &>/dev/null && ! command -v uv &>/dev/null; then | ||
| warn "twine not found — will try 'uv publish' or 'python3 -m twine'" | ||
| fi | ||
|
|
There was a problem hiding this comment.
When uv is not installed, the script falls back to python3 -m build and python3 -m twine, but preflight only checks for pip and only warns about missing twine. This will fail later unless the build and twine modules are installed in the current Python environment. Consider adding explicit preflight checks for python3 -m build and python3 -m twine availability (and fail early with clear install instructions) when uv isn’t present.
| fi | |
| else | |
| ok "uv $(uv --version 2>&1 | awk '{print $2}')" | |
| fi | |
| if ! command -v twine &>/dev/null && ! command -v uv &>/dev/null; then | |
| warn "twine not found — will try 'uv publish' or 'python3 -m twine'" | |
| fi | |
| if python3 -m build --version &>/dev/null; then | |
| ok "python build module available" | |
| else | |
| fail "Python module 'build' not available for python3; install it with: python3 -m pip install --user build twine"; missing=1 | |
| fi | |
| if python3 -m twine --version &>/dev/null; then | |
| ok "python twine module available" | |
| else | |
| fail "Python module 'twine' not available for python3; install it with: python3 -m pip install --user build twine"; missing=1 | |
| fi | |
| fi | |
| else | |
| ok "uv $(uv --version 2>&1 | awk '{print $2}')" | |
| fi |
Add workflow_dispatch publish workflows for: - elevenlabs-moss (PyPI) - moss-cli (PyPI) - moss-md-indexer (npm) - strands-agents-moss (PyPI) - vapi-moss (PyPI) Follows the same pattern as existing pipecat-moss, vercel-sdk, and vitepress-plugin-moss workflows.
- Add || return 1 to critical commands in release functions to prevent publishing after failed builds (set -e suppression in if) - Respect --npm-only/--pypi-only flags when selecting specific packages - Add tomli fallback for Python <3.11 (tomllib not available) - Add pnpm preflight check - Add build/twine module checks when uv is not available - Add permissions: contents: read to all PyPI workflow files (CodeQL)
| if [[ -f "pnpm-lock.yaml" ]]; then | ||
| { pnpm install --frozen-lockfile 2>/dev/null || pnpm install; } || return 1 |
There was a problem hiding this comment.
🟡 Preflight promises pnpm→npm fallback that release_npm never implements
The preflight check at line 61 warns: "packages with pnpm-lock.yaml will fall back to npm". However, release_npm at lines 126-127 only tries pnpm install twice when pnpm-lock.yaml is present — there is no fallback to npm install. If pnpm is not installed, both pnpm invocations fail and return 1 is triggered, causing the release to fail. A user who trusts the preflight message and skips installing pnpm will get a failed release for any package with a pnpm-lock.yaml (e.g. moss-md-indexer).
Prompt for agents
There is a mismatch between the preflight warning (scripts/release.sh:61) and the actual dependency install logic in release_npm (scripts/release.sh:126-127). The preflight says packages with pnpm-lock.yaml will fall back to npm, but the code only tries pnpm twice and then fails. Either: (1) update the install logic at lines 126-127 to fall back to npm install when pnpm is not available (e.g. check command -v pnpm and fall through to npm install), or (2) update the preflight message at line 61 to accurately say that the release will fail without pnpm.
Was this helpful? React with 👍 or 👎 to provide feedback.
When pnpm is not installed, fall back to npm install instead of failing, matching the preflight warning message.
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| python: ["3.10", "3.11", "3.12", "3.13"] |
| @@ -0,0 +1,343 @@ | |||
| #!/usr/bin/env bash | |||
There was a problem hiding this comment.
this one time script right for releasing both python and js package ? do you see this script would have repeated use case in future?
Add Python 3.14 to the test matrix in all 5 PyPI publish workflows. Add top-level permissions block to publish-pipecat-moss.yml to match the other workflows and satisfy CodeQL security scanning.
| fail-fast: false | ||
| matrix: | ||
| python: ["3.10", "3.11", "3.12", "3.13"] | ||
| python: ["3.10", "3.11", "3.12", "3.13", "3.14"] |
There was a problem hiding this comment.
🟡 Python 3.14 in CI matrix but package declares requires-python <3.14
The pipecat-moss pyproject.toml declares requires-python = ">=3.10,<3.14" (packages/pipecat-moss/pyproject.toml), but this PR adds "3.14" to the build matrix. The smoke test step runs pip install --force-reinstall <wheel>, and pip will refuse to install the wheel on Python 3.14 because the wheel's Requires-Python metadata excludes it. This matrix entry will always fail.
| python: ["3.10", "3.11", "3.12", "3.13", "3.14"] | |
| python: ["3.10", "3.11", "3.12", "3.13"] |
Was this helpful? React with 👍 or 👎 to provide feedback.
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| python: ["3.10", "3.11", "3.12", "3.13", "3.14"] |
There was a problem hiding this comment.
🟡 Python 3.14 in CI matrix but package declares requires-python <3.14
The elevenlabs-moss pyproject.toml declares requires-python = ">=3.10,<3.14" (packages/elevenlabs-moss/pyproject.toml), but the workflow matrix includes "3.14". The smoke test step runs pip install --force-reinstall <wheel>, and pip will refuse to install the wheel on Python 3.14 because the wheel's Requires-Python metadata excludes it. This matrix entry will always fail.
| python: ["3.10", "3.11", "3.12", "3.13", "3.14"] | |
| python: ["3.10", "3.11", "3.12", "3.13"] |
Was this helpful? React with 👍 or 👎 to provide feedback.
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| python: ["3.10", "3.11", "3.12", "3.13", "3.14"] |
There was a problem hiding this comment.
🟡 Python 3.14 in CI matrix but package declares requires-python <3.14
The strands-agents-moss pyproject.toml declares requires-python = ">=3.10,<3.14" (packages/strands-agents-moss/pyproject.toml), but the workflow matrix includes "3.14". The smoke test step runs pip install --force-reinstall <wheel>, and pip will refuse to install the wheel on Python 3.14 because the wheel's Requires-Python metadata excludes it. This matrix entry will always fail.
| python: ["3.10", "3.11", "3.12", "3.13", "3.14"] | |
| python: ["3.10", "3.11", "3.12", "3.13"] |
Was this helpful? React with 👍 or 👎 to provide feedback.
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| python: ["3.10", "3.11", "3.12", "3.13", "3.14"] |
There was a problem hiding this comment.
🟡 Python 3.14 in CI matrix but package declares requires-python <3.14
The vapi-moss pyproject.toml declares requires-python = ">=3.10,<3.14" (packages/vapi-moss/pyproject.toml), but the workflow matrix includes "3.14". The smoke test step runs pip install --force-reinstall <wheel>, and pip will refuse to install the wheel on Python 3.14 because the wheel's Requires-Python metadata excludes it. This matrix entry will always fail.
| python: ["3.10", "3.11", "3.12", "3.13", "3.14"] | |
| python: ["3.10", "3.11", "3.12", "3.13"] |
Was this helpful? React with 👍 or 👎 to provide feedback.
| run: | | ||
| git config user.name "github-actions" | ||
| git config user.email "github-actions@users.noreply.github.com" | ||
| git tag "moss-md-indexer-v${VERSION}" | ||
| git push origin "moss-md-indexer-v${VERSION}" |
There was a problem hiding this comment.
🟡 Missing tag existence check causes workflow failure on re-runs
Unlike all other publish workflows in this PR (e.g., publish-elevenlabs-moss.yml:102-111, publish-pipecat-moss.yml:142-151), the publish-moss-md-indexer.yml "Tag release" step does not check whether the tag already exists before creating and pushing it. If the workflow is re-dispatched for the same version (e.g., to retry a failed npm publish), git tag will fail with fatal: tag already exists and the step will error out.
| run: | | |
| git config user.name "github-actions" | |
| git config user.email "github-actions@users.noreply.github.com" | |
| git tag "moss-md-indexer-v${VERSION}" | |
| git push origin "moss-md-indexer-v${VERSION}" | |
| run: | | |
| git fetch --tags --force | |
| if git rev-parse "moss-md-indexer-v${VERSION}" >/dev/null 2>&1; then | |
| echo "Tag moss-md-indexer-v${VERSION} already exists" | |
| else | |
| git config user.name "github-actions" | |
| git config user.email "github-actions@users.noreply.github.com" | |
| git tag "moss-md-indexer-v${VERSION}" | |
| git push origin "moss-md-indexer-v${VERSION}" | |
| fi |
Was this helpful? React with 👍 or 👎 to provide feedback.
| - name: Publish to npm | ||
| working-directory: ${{ env.PKG_DIR }} | ||
| env: | ||
| NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }} | ||
| run: pnpm publish --no-git-checks --access public |
There was a problem hiding this comment.
🚩 moss-md-indexer uses pnpm publish without --skip-existing equivalent
The publish-moss-md-indexer.yml:79 step uses pnpm publish --no-git-checks --access public without any mechanism to handle the case where the version already exists on npm (unlike the Python workflows which use twine upload --skip-existing). If the workflow is re-dispatched for the same version, the npm publish step will fail with a 403 error. This is consistent with pnpm publish not having a --skip-existing flag, but combined with the missing tag existence check (reported as a bug), re-runs are fragile.
Was this helpful? React with 👍 or 👎 to provide feedback.
## Summary - Adds `scripts/release.sh` — a single script to build and publish all 8 Moss packages (3 npm, 5 PyPI) - Supports `--dry-run`, `--npm-only`, `--pypi-only`, and selective package publishing - Includes preflight checks for required tooling and auth ## Packages covered **npm:** `@moss-tools/md-indexer`, `@moss-tools/vercel-sdk`, `vitepress-plugin-moss` **PyPI:** `elevenlabs-moss`, `moss-cli`, `pipecat-moss`, `strands-agents-moss`, `vapi-moss` ## Usage ```bash ./scripts/release.sh --dry-run # verify builds ./scripts/release.sh # publish all ./scripts/release.sh vercel-sdk # publish one ./scripts/release.sh --pypi-only # publish only Python packages ``` ## Test plan - [x] Dry run completes successfully for all 8 packages - [ ] npm publish with valid auth - [ ] PyPI publish with valid auth <!-- devin-review-badge-begin --> --- <a href="https://app.devin.ai/review/usemoss/moss/pull/142" target="_blank"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1"> <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open with Devin"> </picture> </a> <!-- devin-review-badge-end -->
Summary
scripts/release.sh— a single script to build and publish all 8 Moss packages (3 npm, 5 PyPI)--dry-run,--npm-only,--pypi-only, and selective package publishingPackages covered
npm:
@moss-tools/md-indexer,@moss-tools/vercel-sdk,vitepress-plugin-mossPyPI:
elevenlabs-moss,moss-cli,pipecat-moss,strands-agents-moss,vapi-mossUsage
Test plan