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
170 changes: 170 additions & 0 deletions .claude/skills/playwright-patch-release/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
---
name: playwright-patch-release
description: Prepare a patch release of Playwright Python (v1.xx.1 and later) from a release branch. Picks the next version, writes the release notes as the list of fixes in microsoft/playwright between the driver of the previous release of the branch and the driver of its tip, and saves them as a draft GitHub release, in place of an old draft. Never publishes the release.
---

# Preparing a patch release of Playwright Python

In this skill, `release-1.xx` is the release branch (for example `release-1.65`),
`v1.xx.p` is the previous release of the branch, and `v1.xx.n` is the patch release
that you prepare. For the first release of a branch, `v1.xx.0`, use the
`playwright-release` skill: its notes are the full release notes.

A patch release has no release commit. The fixes are on the branch already: a roll
moved its driver to a newer commit of `release-1.xx` of `microsoft/playwright`. The
version of the package comes from the tag, and GitHub makes the tag when a maintainer
publishes the draft. So the result of this skill is only the draft release.

Remotes: find the remote for `microsoft/playwright-python` with `git remote -v`. The
commands below call it `upstream`. In a clone of `microsoft/playwright-python`, it
is `origin`.

Never publish the release: see step 5.

## 1. The version

Fetch the branch and the tags. The previous release is the highest `v1.xx.*` tag:

```sh
git fetch upstream release-1.xx --tags
git tag --list 'v1.xx.*' --sort=-v:refname | head -1 # v1.xx.p
git merge-base --is-ancestor v1.xx.p FETCH_HEAD && echo "on release-1.xx"
git log --first-parent --format='%h %s' v1.xx.p..FETCH_HEAD # the commits of this repository
```

- The next version is `v1.xx.n`, with `n = p + 1`. A draft release has no tag, so an
old draft of `v1.xx.n` does not change the result.
- No `v1.xx.*` tag: `v1.xx.0` is not released. Stop and say so.
- The tag is not on the branch: stop and say so.
- The log is empty: there is nothing to release. Stop and say so.

## 2. The changes

The changes of the release are the commits of `microsoft/playwright` between the two
drivers: the driver of `v1.xx.p` and the driver of the tip of the branch.
`DRIVER_VERSION` names the commit of each one:

```sh
git show v1.xx.p:DRIVER_VERSION # A, the driver of the previous release
git show FETCH_HEAD:DRIVER_VERSION # B, the driver of this release
```

For a release up to 1.64, `DRIVER_VERSION` is a version of `playwright-core`, not a
commit. Its commit is the tag `v<version>` of `microsoft/playwright`.

Move `.upstream/` to B and list the commits between A and B. Both are on
`release-1.xx` of `microsoft/playwright`:

```sh
git checkout --detach FETCH_HEAD && python scripts/upstream.py checkout
git -C .upstream merge-base --is-ancestor <A> <B> && echo "A is before B"
git -C .upstream log --reverse --format='%h %s' <A>..<B>
```

A commit of an upstream release branch has the title `cherry-pick(#<pr>): <title>`,
where `#<pr>` is the pull request on `main` of `microsoft/playwright`. For each
commit, read the pull request and the issue that it fixes:

```sh
gh pr view <pr> --repo microsoft/playwright --json title,body,closingIssuesReferences
```

The patch releases of `microsoft/playwright` between A and B name the same issues
(`gh release view v1.xx.y --repo microsoft/playwright`). Use them as a check, not as
the source: the list comes from the commits.

Keep a fix only when it changes something for a user of Playwright Python: the
browsers, the driver, the protocol, the API. Leave out the fixes of the test runner,
of the Node.js tools and of the other languages, and the commits that a user cannot
see: version bumps, CI, tests, docs of other languages.

When A and B are the same commit, the release has no upstream changes. Then the
commits of step 1 are the only changes: list those that a user can see, such as a new
bundled Node.js or a change of the Docker images.

## 3. The notes

Write the notes to a file, in GitHub markdown, in this shape. It is the shape of the
earlier patch releases, for example
[v1.65.1](https://github.com/microsoft/playwright-python/releases/tag/v1.65.1):

```markdown
### Highlights

https://github.com/microsoft/playwright/issues/<n> - <title of the issue>
https://github.com/microsoft/playwright/pull/<n> - <title of the pull request>

<One short paragraph for each change that needs an explanation.>

## Browser Versions

- Chromium <new version>
```

- One line for each fix, and nothing more: no new APIs, no code samples, no
sections of the full release notes. A patch release has only fixes.
- Link the issue that the fix closes, with the title of the issue as it is. The
issue can be in `microsoft/playwright` or in `microsoft/playwright-python`. When
there is no issue, link the pull request on `main`, not the cherry-pick, with its
title without `cherry-pick(#<pr>):`.
- Order the lines by how much they matter to a user: regressions first.
- Add a paragraph only when the title does not tell a user what changed, for
example why a new Node.js matters. Write what the user gets, in one or two
sentences.
- No title line, no version, no date: GitHub shows them.

Browser Versions: list only the browsers whose version changed since `v1.xx.p`,
each with its new version. Compare the table in `README.md` at the two commits. A
roll generates the table from the driver.

```sh
git show v1.xx.p:README.md | grep 'GEN:.*-version'
git show FETCH_HEAD:README.md | grep 'GEN:.*-version'
```

The names are `Chromium`, `Mozilla Firefox` and `WebKit`. When no version changed,
leave out the `## Browser Versions` section.

## 4. Check the notes

- Each line of the list has a commit between A and B in step 2. Each of those
commits that changes something for a user of Playwright Python has a line.
- Each link opens, and its title is the title of that issue or pull request.
- The notes name a browser only when its version changed since `v1.xx.p`, with the
version of `README.md` at the tip of the branch.

## 5. The draft release

Save the notes as a draft release with the tag `v1.xx.n`, the title `v1.xx.n` and
the target `release-1.xx`. A draft of `v1.xx.n` from an earlier run has old notes:
the new draft replaces it. Never delete or change a published release.

**As the Playwright bot**, on CI. You have no token and no `gh`: you cannot save the
release yourself. Leave one `release` call in `calls.json`, and the Post step of the
bot saves the draft:

```json
{ "call": "release", "repo": "microsoft/playwright-python", "tag": "v1.xx.n", "target": "release-1.xx", "bodyFile": "release-notes.md" }
```

The call deletes the old draft of `v1.xx.n` and saves the new one. It fails when the
tag exists or when the release is published. The notes have no footer.

**As a person at a computer**, with `gh` logged in. First check that the old release
is a draft:

```sh
gh release view v1.xx.n --repo microsoft/playwright-python --json isDraft --jq .isDraft
gh release delete v1.xx.n --repo microsoft/playwright-python --yes # only when the line above prints true
gh release create v1.xx.n --repo microsoft/playwright-python --draft \
--target release-1.xx --title v1.xx.n --notes-file <notes-file>
gh release list --repo microsoft/playwright-python --limit 3 # v1.xx.n shows as Draft
```

Give the user the URL of the draft, the previous release, and the list of the
changes that you left out with the reason for each. Your work ends here. Do not
publish the release: never run `gh release create` without `--draft`, and never run
`gh release edit --draft=false`. Publishing creates the tag and starts
`.github/workflows/publish.yml`, which builds the wheels and uploads them to PyPI
after a reviewer approves the `pypi` environment. Never run that workflow, and never
approve the environment.
6 changes: 5 additions & 1 deletion .claude/skills/playwright-release/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: playwright-release
description: Prepare a Playwright Python release. Checks open bugs, adds Python-specific changes to the Python release notes in microsoft/playwright, opens the release PR, cuts the release branch, and saves a draft GitHub release. Never publishes the release.
description: Prepare a new Playwright Python release, v1.xx.0, with the full release notes. For a patch release of a release branch, use playwright-patch-release. Checks open bugs, adds Python-specific changes to the Python release notes in microsoft/playwright, opens the release PR, cuts the release branch, and saves a draft GitHub release. Never publishes the release.
---

# Preparing a Playwright Python release
Expand All @@ -10,6 +10,10 @@ In this skill, `1.xx` is the release (for example `1.64`), and `v1.xx.0` is its
`DRIVER_VERSION` (`python scripts/upstream.py checkout`). Only read from it: do not
commit or switch branches there.

This skill prepares `v1.xx.0`, the first release of a branch, with the full release
notes. A patch release, `v1.xx.1` and later, has only the list of the fixes: use the
`playwright-patch-release` skill for it.

Remotes: find the remote for `microsoft/playwright-python` with `git remote -v`.
The commands below call it `upstream`.

Expand Down
62 changes: 62 additions & 0 deletions .github/workflows/agent-patch-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Prepares a patch release of Playwright Python from a release branch: the next v1.xx.y after the
# last tag of the branch. Starts the Playwright bot with the task below. The bot starts from the
# branch and writes the release notes with the playwright-patch-release skill of main: the list of
# the fixes in microsoft/playwright between the driver of the previous release of the branch and
# the driver of its tip. The bot then saves the notes as a draft release, in place of the draft of
# an earlier run. A maintainer reviews the draft and publishes it. Start it from the Actions tab.
name: "Agent: patch release"

on:
workflow_dispatch:
inputs:
branch:
description: "Release branch to release from: release-1.xx"
required: true

permissions: {}

jobs:
start:
name: Hand the task to the bot
if: github.repository == 'microsoft/playwright-python'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
id: app-token
with:
client-id: ${{ vars.PLAYWRIGHT_APP_CLIENT_ID }}
private-key: ${{ secrets.PLAYWRIGHT_PRIVATE_KEY }}
owner: microsoft
repositories: devops.playwright.dev
permission-actions: write
- name: Start the bot
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
BRANCH: ${{ inputs.branch }}
# The skill is new on main, and a release branch can have an old copy or none. So the bot
# reads it from main. YAML folds these lines into one line.
TASK: >-
Prepare the next patch release of the ${{ inputs.branch }} branch of Playwright Python,
as the playwright-patch-release skill says.
Your checkout is the ${{ inputs.branch }} branch, not main.
Read the skill from main:
git show origin/main:.claude/skills/playwright-patch-release/SKILL.md.
The remote of the skill is origin.
Pick the next version, and write the release notes: the list of the fixes in the commits
of microsoft/playwright between the DRIVER_VERSION of the previous release of the branch
and the DRIVER_VERSION of the tip of ${{ inputs.branch }}.
Open no pull request.
End with one release call: tag is the next version, target is ${{ inputs.branch }}, and
the text is the release notes and nothing else.
When there is nothing to release, make no call, and say so in the summary.
run: |
# A patch release comes only from a release branch. The first release of a branch, v1.xx.0,
# has the full release notes: the playwright-release skill prepares it.
if [[ ! "$BRANCH" =~ ^release-[0-9]+\.[0-9]+$ ]]; then
echo "::error::branch must be release-1.xx, got '$BRANCH'"
exit 1
fi
gh workflow run task.yml --repo microsoft/devops.playwright.dev --ref main \
-f repo="$GITHUB_REPOSITORY" -f branch="$BRANCH" -f task="$TASK"
echo "Started the patch release of $BRANCH. The bot saves the release notes as a draft release." >> "$GITHUB_STEP_SUMMARY"
9 changes: 9 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,15 @@ the workflow by hand. Its `branch` input is the branch to roll, in both reposito
upstream `release-1.xx`. The PR goes to that branch. The `ref` input rolls to another
upstream branch, tag or commit.

## Releases

The `playwright-release` skill prepares `v1.xx.0`: the full release notes, the release PR
and the release branch. The `playwright-patch-release` skill prepares `v1.xx.1` and later
from `release-1.xx`: the notes are the list of the fixes in `microsoft/playwright` between
the driver of the previous release of the branch and the driver of its tip. On CI, `.github/workflows/agent-patch-release.yml` runs it: start the workflow by
hand, with the release branch as its `branch` input. Both end with a draft GitHub release.
A new run replaces the draft. Only a maintainer publishes it.

## Commits and PRs

- Never push without "push", "upload", "create PR" or "ship it" in my message, also not
Expand Down
Loading