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
2 changes: 1 addition & 1 deletion .changeset/remove-built-in-hosted-provider.md
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
---
"@pymodel/pythinker-code": major
"@pymodel/pythinker-code": minor
Comment thread
coderabbitai[bot] marked this conversation as resolved.
---

Remove the built-in hosted provider and its sign-in, usage, feedback, model aliases, and SDK methods; configure a supported provider with its own API key or OAuth instead.
42 changes: 41 additions & 1 deletion .github/workflows/release.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -643,8 +643,48 @@ jobs:
RELEASE_TAG: ${{ needs.release.outputs.pythinker_release_tag }}
run: node apps/pythinker-code/scripts/native/produce-manifest.mjs dist-native-release "$RELEASE_TAG"

# Unlike the desktop release, which uploads into a draft and refuses to
# touch a published one, this release is already live: changesets creates
# it when it publishes to npm. `--clobber` therefore replaced the assets
# of a shipped version on any re-run of this job — and a re-run after a
# source change would put different bytes behind a version number users
# already have. A version has to identify one exact build forever.
#
# These assets are one set, not a bag of files: manifest.json pins a
# sha256 for every zip, and install.sh / install.ps1 verify against it.
# So the rule is all or nothing. A release that already carries the whole
# set is finished and is left alone; an empty one gets everything. A
# partial set stops the job, because filling in the gaps would pair zips
# from one build with checksums from another, and a rebuild is not
# guaranteed to be byte-identical even at the same commit. Sorting that
# out is a decision for a person: either what is published is the
# release, or it needs a new patch version.
- name: Upload assets to GitHub Release
shell: bash
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ needs.release.outputs.pythinker_release_tag }}
run: gh release upload "$RELEASE_TAG" dist-native-release/* --clobber
run: |
set -euo pipefail
existing="$(gh release view "$RELEASE_TAG" --json assets --jq '.assets[].name')"
present=()
absent=()
for path in dist-native-release/*; do
name="$(basename "$path")"
if printf '%s\n' "$existing" | grep -qxF "$name"; then
present+=("$name")
else
absent+=("$path")
fi
done
if [ "${#absent[@]}" -eq 0 ]; then
echo "::notice::All ${#present[@]} assets are already on ${RELEASE_TAG}; nothing to upload."
exit 0
fi
if [ "${#present[@]}" -gt 0 ]; then
echo "::error::${RELEASE_TAG} already carries ${#present[@]} of these assets: ${present[*]}"
echo "::error::Adding the rest would mix two builds behind one version - manifest.json pins a sha256 per zip."
echo "::error::Resolve by hand: keep what is published, or cut the next patch and release that instead."
exit 1
fi
gh release upload "$RELEASE_TAG" "${absent[@]}"
40 changes: 33 additions & 7 deletions CONTRIBUTING.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -82,21 +82,47 @@ This repo uses [changesets](https://github.com/changesets/changesets) to manage

### Bump levels

| Level | Use for | Example |
| --- | --- | --- |
| `patch` | A fix, or a small addition to something that already exists | `2.1.2` → `2.1.3` |
| `minor` | A capability a user could not reach before | `2.1.3` → `2.2.0` |
| `major` | A break: something that worked stops working, or works differently | `2.2.0` → `3.0.0` |
Pythinker Code follows semantic versioning. The commit type you would have written decides the level:

| Commit type | Level | Use for | Example |
| --- | --- | --- | --- |
| `fix`, `perf`, `security` | `patch` | A fix, a speed-up, or a small addition to something that already exists | `1.2.0` → `1.2.1` |
| `feat` | `minor` | A capability a user could not reach before | `1.2.8` → `1.3.0` |
| a break | `major` | Something that worked stops working, or works differently | `1.x` → `2.0.0` |

Each number counts on its own and none of them roll over at nine. Ten fixes on top of `1.2.0` land on `1.2.10`, not `1.3.0`; the minor moves only when a feature ships, and the major only when something breaks:

```text
1.2.9 + fix → 1.2.10
1.9.9 + fix → 1.9.10
1.9.9 + feature → 1.10.0
1.x + break → 2.0.0
```

Prefer one changeset per pull request. A pull request that needs several is usually carrying several separate releases, and once they are versioned together the changelog can no longer say which change each entry came from.

A `major` needs a maintainer's sign-off: the `changeset-policy` workflow fails a pull request that adds a major changeset, or edits an existing one up to `major`, unless it carries the `breaking-change-approved` label. A pinned install keeps working, but every consumer who upgrades has to deal with the break, and an npm publish cannot be taken back — so it is a decision, never a side effect of a large branch.
A `major` needs a maintainer's sign-off: the `changeset-policy` workflow fails a pull request that adds a major changeset, or edits an existing one up to `major`, unless it carries the `breaking-change-approved` label. A pinned install keeps working, but every consumer who upgrades has to deal with the break, and an npm publish cannot be taken back — so it is a decision, never a side effect of a large branch. Nothing else can reach a major: no amount of counting gets there on its own.

### A released version is one exact build, forever

Once a version exists, everything published under it is frozen. This matters most for the desktop app and the native CLI bundles, where a version number is what an updater resolves to a specific set of bytes it has already advertised.

Never do any of these:

- Overwrite the assets on a published release
- Move a tag that has shipped
- Rebuild binaries and publish them under a version that already exists
- Replace `latest.yml` / `latest-mac.yml` / `manifest.json` for a version whose binaries are already out

To correct a bad build, cut the next patch instead: fix the problem, bump, build from that exact commit, sign and notarize, and publish fresh artifacts under the new number. The updater then advertises the new version, and anyone who already installed the old one keeps a build that still matches what it claims to be.

CI enforces this rather than trusting the rule. The desktop release uploads into a draft and refuses to touch a release that is already published. The native CLI job treats its assets as one set — `manifest.json` pins a sha256 for every zip — so it uploads all of them or none: a release that already has the full set is left alone, and a partial set stops the job rather than pairing zips from one build with checksums from another.

### Release cadence

Changesets keeps a `ci: release packages` pull request open on `main` and rewrites it as changesets land. Merging it cuts exactly one release, so how often it is merged is what decides the version sequence:

- Merged per change, versions follow each change: `2.1.2`, `2.1.3`, `2.1.4`, `2.2.0`.
- Merged per change, versions follow each change: `1.2.1`, `1.2.2`, `1.2.3`, `1.3.0`.
- Left to accumulate, a backlog collapses into one bump and the numbers in between never exist.

The repository variable `AUTO_MERGE_RELEASE_PR` chooses between the two. Set to `true`, the release pull request merges itself once its required checks pass, giving one release per change. Unset or `false`, a maintainer merges it when a release is wanted.
Expand Down
Loading