Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 91 additions & 29 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,56 +1,88 @@
# Publishes actor-debugger to npm when a version tag is pushed.
# Publishes the npm package in javascript/ to npm, then tags the commit and creates the GitHub
# release. Started manually (Actions -> Publish to npm -> Run workflow) on master; the version it
# publishes is whatever javascript/package.json carries on that commit.
#
# Release flow:
# npm version patch # bumps package.json, commits, and creates the v* tag
# git push --follow-tags
# 1. Bump the version in javascript/package.json in a PR (e.g. `npm version patch
# --no-git-tag-version` inside javascript/) and merge it to master.
# 2. Actions -> Publish to npm -> Run workflow (branch: master).
# The workflow refuses to run if that version is already on npm or its v* tag already exists,
# so a re-run after a failure is safe. On success it publishes, pushes the vX.Y.Z tag and
# creates a GitHub release with generated notes - never create those by hand.
#
# Setup (one-time): create a granular npm access token with read/write on this
# package (or "new packages" for the first publish) and save it as the
# repository secret NPM_TOKEN.
#
# Note: `npm publish --provenance` is not used because it requires a public
# repository. If the repo goes public, add `--provenance` to the publish step
# and `id-token: write` to permissions.
# Uses npm Trusted Publishing (OIDC) - no npm token or repository secret. The trusted publisher
# configured on npmjs.com for the actor-debugger package is:
# repository: apify/actor-debugger
# workflow: publish.yml (this file name must match exactly, including the extension)
# Provenance attestations are generated automatically by trusted publishing, and npm requires
# package.json "repository.url" to point at this exact GitHub repository.

name: Publish to npm

on:
push:
tags:
- 'v*'
workflow_dispatch:

# Never let two releases run at once (they would race on the same version/tag).
concurrency:
group: publish-npm
cancel-in-progress: false

defaults:
run:
working-directory: javascript

jobs:
check:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Refuse to publish from anything but master
if: github.ref != 'refs/heads/master'
run: |
echo "Publish to npm must be started on master, got ${GITHUB_REF}" >&2
exit 1

- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 22
node-version: 24

- name: Install dependencies
run: npm ci

- name: Syntax-check sources
run: |
node --check bin/cli.mjs
node --check lib/debug_server.mjs
- name: Read version from package.json
id: version
run: echo "version=$(node -p "require('./package.json').version")" >> "$GITHUB_OUTPUT"

- name: Verify tag matches package.json version
- name: Verify the version is not published yet
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
PKG_VERSION="$(node -p "require('./package.json').version")"
TAG_VERSION="${GITHUB_REF_NAME#v}"
if [ "$PKG_VERSION" != "$TAG_VERSION" ]; then
echo "Tag $GITHUB_REF_NAME does not match package.json version $PKG_VERSION" >&2
# Ask the registry for the published versions (fails loudly on a registry/network error
# rather than silently treating the version as new).
PUBLISHED="$(npm view actor-debugger versions --json)"
if node -e 'process.exit(JSON.parse(process.argv[1]).includes(process.argv[2]) ? 0 : 1)' "$PUBLISHED" "$VERSION"; then
echo "actor-debugger@${VERSION} is already on npm - bump the version in package.json first" >&2
exit 1
fi
if git ls-remote --exit-code --tags origin "refs/tags/v${VERSION}" > /dev/null; then
echo "Tag v${VERSION} already exists - bump the version in package.json first" >&2
exit 1
fi

- name: Syntax-check sources
run: |
node --check bin/cli.mjs
node --check lib/debug_server.mjs
node --check lib/inline_sourcemaps.mjs

- name: Smoke test (entrypoint runs and its output passes through)
run: |
mkdir -p /tmp/smoke && cd /tmp/smoke
echo 'console.log("smoke-ok")' > index.js
OUTPUT="$(node "$GITHUB_WORKSPACE/bin/cli.mjs" index.js 2>&1)"
OUTPUT="$(node "$GITHUB_WORKSPACE/javascript/bin/cli.mjs" index.js 2>&1)"
echo "$OUTPUT"
echo "$OUTPUT" | grep -q 'smoke-ok'

Expand All @@ -59,7 +91,7 @@ jobs:
mkdir -p /tmp/smoke-debug && cd /tmp/smoke-debug
echo 'setTimeout(() => console.log("done"), 15000)' > index.js
ACTOR_WEB_SERVER_PORT=4321 ACTOR_WEB_SERVER_URL=http://localhost:4321 \
node "$GITHUB_WORKSPACE/bin/cli.mjs" index.js &
node "$GITHUB_WORKSPACE/javascript/bin/cli.mjs" index.js &
CLI_PID=$!
for i in $(seq 1 20); do
sleep 0.5
Expand All @@ -75,19 +107,49 @@ jobs:
publish:
needs: check
runs-on: ubuntu-latest
environment: npm
permissions:
id-token: write # npm Trusted Publishing (OIDC)
contents: write # push the release tag and create the GitHub release
env:
VERSION: ${{ needs.check.outputs.version }}
TAG: v${{ needs.check.outputs.version }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # full history and tags, so the release notes start at the previous v* tag

- uses: actions/setup-node@v4
with:
node-version: 22
node-version: 24
registry-url: 'https://registry.npmjs.org'

# Trusted publishing needs npm >= 11.5.1; make sure the runner's npm is new enough.
- name: Update npm
run: |
npm install -g npm@latest
npm --version

- name: Install dependencies
run: npm ci

- name: Publish
- name: Publish (trusted publishing)
run: npm publish

- name: Tag the release commit
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "$TAG" -m "actor-debugger $TAG (npm)"
git push origin "$TAG"

- name: Create the GitHub release
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
GH_TOKEN: ${{ github.token }}
run: |
# Previous npm release tag (v*, not the Python py-v* tags), if any, for the notes range.
PREV="$(git describe --tags --abbrev=0 --match 'v*' "${TAG}^" 2>/dev/null || true)"
if [ -n "$PREV" ]; then
gh release create "$TAG" --title "actor-debugger $TAG (npm)" --generate-notes --notes-start-tag "$PREV" --verify-tag
else
gh release create "$TAG" --title "actor-debugger $TAG (npm)" --generate-notes --verify-tag
fi
108 changes: 81 additions & 27 deletions .github/workflows/publish_to_pypi.yml
Original file line number Diff line number Diff line change
@@ -1,53 +1,83 @@
# Publishes the Python actor-debugger package to PyPI when a py-v* tag is pushed.
# Publishes the Python package in python/ to PyPI, then tags the commit and creates the GitHub
# release. Started manually (Actions -> Publish to PyPI -> Run workflow) on master; the version it
# publishes is whatever python/pyproject.toml carries on that commit.
#
# Uses PyPI Trusted Publishing (OIDC) - no API token or secret anywhere. The trusted
# publisher on PyPI is configured as:
# Release flow:
# 1. Bump the version in python/pyproject.toml and python/src/actor_debugger/__init__.py in a
# PR and merge it to master.
# 2. Actions -> Publish to PyPI -> Run workflow (branch: master).
# The workflow refuses to run if that version is already on PyPI or its py-v* tag already
# exists, so a re-run after a failure is safe. On success it publishes, pushes the py-vX.Y.Z tag
# and creates a GitHub release with generated notes - never create those by hand.
#
# Uses PyPI Trusted Publishing (OIDC) - no API token or secret anywhere. The trusted publisher on
# PyPI is configured as:
# project: actor-debugger
# repository: apify/actor-debugger
# workflow: publish_to_pypi.yml (this file name must match exactly)
#
# Release flow:
# (bump version in python/pyproject.toml and python/src/actor_debugger/__init__.py, commit)
# git tag py-vX.Y.Z && git push --tags
#
# Can also be started manually (Actions -> Publish to PyPI -> Run workflow); a manual run
# skips the tag/version check and publishes whatever version pyproject.toml carries on the
# chosen ref (PyPI rejects re-uploads of an existing version, so re-runs are safe).

name: Publish to PyPI

on:
push:
tags:
- 'py-v*'
workflow_dispatch:

# Never let two releases run at once (they would race on the same version/tag).
concurrency:
group: publish-pypi
cancel-in-progress: false

defaults:
run:
working-directory: python

jobs:
check:
runs-on: ubuntu-latest
defaults:
run:
working-directory: python
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Refuse to publish from anything but master
if: github.ref != 'refs/heads/master'
run: |
echo "Publish to PyPI must be started on master, got ${GITHUB_REF}" >&2
exit 1

- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install package
run: pip install .
- name: Read version from pyproject.toml
id: version
run: |
VERSION="$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")"
INIT_VERSION="$(python -c "import re; print(re.search(r'__version__\s*=\s*[\"\']([^\"\']+)', open('src/actor_debugger/__init__.py').read()).group(1))")"
if [ "$VERSION" != "$INIT_VERSION" ]; then
echo "pyproject.toml version $VERSION does not match __init__.__version__ $INIT_VERSION" >&2
exit 1
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"

- name: Verify tag matches pyproject version
if: github.ref_type == 'tag'
- name: Verify the version is not published yet
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
PKG_VERSION="$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")"
TAG_VERSION="${GITHUB_REF_NAME#py-v}"
if [ "$PKG_VERSION" != "$TAG_VERSION" ]; then
echo "Tag $GITHUB_REF_NAME does not match pyproject.toml version $PKG_VERSION" >&2
# Ask PyPI for the released versions (fails loudly on a network error rather than
# silently treating the version as new).
PUBLISHED="$(curl -sSf https://pypi.org/pypi/actor-debugger/json)"
if python -c "import json,sys; sys.exit(0 if sys.argv[2] in json.loads(sys.argv[1])['releases'] else 1)" "$PUBLISHED" "$VERSION"; then
echo "actor-debugger ${VERSION} is already on PyPI - bump the version first" >&2
exit 1
fi
if git ls-remote --exit-code --tags origin "refs/tags/py-v${VERSION}" > /dev/null; then
echo "Tag py-v${VERSION} already exists - bump the version first" >&2
exit 1
fi

- name: Install package
run: pip install .

- name: Smoke test (entrypoint runs and its output passes through)
run: |
mkdir -p /tmp/smoke && cd /tmp/smoke
Expand Down Expand Up @@ -75,16 +105,21 @@ jobs:
needs: check
runs-on: ubuntu-latest
permissions:
id-token: write # required for PyPI Trusted Publishing (OIDC)
id-token: write # PyPI Trusted Publishing (OIDC)
contents: write # push the release tag and create the GitHub release
env:
VERSION: ${{ needs.check.outputs.version }}
TAG: py-v${{ needs.check.outputs.version }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # full history and tags, so the release notes start at the previous py-v* tag

- uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Build distributions
working-directory: python
run: |
pip install build
python -m build
Expand All @@ -93,3 +128,22 @@ jobs:
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: python/dist

- name: Tag the release commit
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "$TAG" -m "actor-debugger $TAG (PyPI)"
git push origin "$TAG"

- name: Create the GitHub release
env:
GH_TOKEN: ${{ github.token }}
run: |
# Previous PyPI release tag (py-v*, not the npm v* tags), if any, for the notes range.
PREV="$(git describe --tags --abbrev=0 --match 'py-v*' "${TAG}^" 2>/dev/null || true)"
if [ -n "$PREV" ]; then
gh release create "$TAG" --title "actor-debugger $TAG (PyPI)" --generate-notes --notes-start-tag "$PREV" --verify-tag
else
gh release create "$TAG" --title "actor-debugger $TAG (PyPI)" --generate-notes --verify-tag
fi
Loading
Loading