Skip to content

Commit db49264

Browse files
committed
Resolve conflicts: scalar-generated into scalar-next
2 parents 2deefe3 + 099aa9f commit db49264

8 files changed

Lines changed: 156 additions & 16 deletions

File tree

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
name: Release PR Version
2+
3+
on:
4+
# `edited` is not one of the default pull_request types, but it is the point of this
5+
# workflow: retitling the release PR is how a maintainer picks an exact version. The
6+
# other types keep the consistency check attached to the PR as it evolves.
7+
pull_request:
8+
types: [opened, reopened, edited, synchronize]
9+
10+
# Read-only by default; only the job that pushes the Release-As commit widens this.
11+
permissions:
12+
contents: read
13+
14+
jobs:
15+
version-consistency:
16+
# Also the check name the release PR's footer tells maintainers to wait for.
17+
name: Release PR version
18+
# Only the platform's release PR carries a version in its title; every other pull request
19+
# skips this job, which GitHub reports as neutral. The release may be presented from
20+
# scalar-next itself or from the release-please branch rendered off it, so both heads
21+
# count as the release PR.
22+
if: >-
23+
${{ github.event.pull_request.base.ref == 'main'
24+
&& (github.event.pull_request.head.ref == 'scalar-next'
25+
|| startsWith(github.event.pull_request.head.ref, 'release-please--branches--scalar-next--')) }}
26+
runs-on: ubuntu-latest
27+
steps:
28+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
29+
with:
30+
# The version committed on the PR head is what merging would actually release.
31+
ref: ${{ github.event.pull_request.head.sha }}
32+
# Fails while the title version and the committed version disagree — the window between
33+
# a maintainer's retitle and the platform re-rendering the PR from it. Without this a
34+
# merge in that window would tag a release whose own files self-report the old version,
35+
# and a title release-please cannot parse would silently cut no release at all.
36+
- name: Compare the title version with the committed version
37+
env:
38+
# The title is human input, so it is bound through the environment (never
39+
# interpolated into the script) and matched against a strict semver pattern
40+
# before it is read.
41+
PR_TITLE: ${{ github.event.pull_request.title }}
42+
run: |
43+
pattern='^release: ((0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?)$'
44+
if [[ ! "$PR_TITLE" =~ $pattern ]]; then
45+
echo "::error::Release PR title must be \"release: X.Y.Z\" with a full semver version, got: $PR_TITLE"
46+
exit 1
47+
fi
48+
title_version="${BASH_REMATCH[1]}"
49+
manifest_version="$(jq -r '.["."]' .release-please-manifest.json)"
50+
if [[ "$title_version" != "$manifest_version" ]]; then
51+
echo "::error::Release PR title says $title_version but the pull request is versioned $manifest_version. Wait for the release PR to be re-rendered at $title_version before merging."
52+
exit 1
53+
fi
54+
echo "Release PR title and committed version agree on $title_version."
55+
56+
apply-title-version:
57+
name: Apply edited release version
58+
# A human retitle of the open release PR is bridged into the canonical explicit-version
59+
# mechanism (a Release-As commit on scalar-next, which the platform re-renders the
60+
# release PR from). `changes.title` is only set when the title itself changed, and bot
61+
# senders are ignored so the platform's own retitles cannot bounce back into another
62+
# commit.
63+
if: >-
64+
${{ github.event.action == 'edited'
65+
&& github.event.changes.title != null
66+
&& github.event.sender.type != 'Bot'
67+
&& github.event.pull_request.state == 'open'
68+
&& github.event.pull_request.base.ref == 'main'
69+
&& (github.event.pull_request.head.ref == 'scalar-next'
70+
|| startsWith(github.event.pull_request.head.ref, 'release-please--branches--scalar-next--')) }}
71+
# One bridge run at a time per pull request, newest retitle wins. Two retitles in quick
72+
# succession (a version typo corrected seconds later) would otherwise start two runs that
73+
# both read the version committed on the PR head, both get past the no-op guard, and both
74+
# push — and since each fetches scalar-next immediately before committing, the second
75+
# push fast-forwards instead of failing. release-please honours the newest Release-As
76+
# footer, so the release would land on whichever run happened to push last, not on the
77+
# title the maintainer actually left behind.
78+
concurrency:
79+
group: release-title-edit-${{ github.event.pull_request.number }}
80+
cancel-in-progress: true
81+
runs-on: ubuntu-latest
82+
permissions:
83+
contents: write
84+
steps:
85+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
86+
with:
87+
# The PR head, so the version the pull request currently carries can be read before
88+
# deciding whether anything needs to change.
89+
ref: ${{ github.event.pull_request.head.sha }}
90+
- name: Push a Release-As commit for the edited version
91+
env:
92+
PR_TITLE: ${{ github.event.pull_request.title }}
93+
run: |
94+
set -euo pipefail
95+
pattern='^release: ((0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?)$'
96+
if [[ ! "$PR_TITLE" =~ $pattern ]]; then
97+
echo "::error::Release PR title must be \"release: X.Y.Z\" with a full semver version, got: $PR_TITLE"
98+
exit 1
99+
fi
100+
# Only ever a validated semver from here on, so it is safe in a commit message.
101+
version="${BASH_REMATCH[1]}"
102+
manifest_version="$(jq -r '.["."]' .release-please-manifest.json)"
103+
# The re-rendered PR is retitled to the version it now carries; stopping here keeps
104+
# that from producing an endless chain of Release-As commits.
105+
if [[ "$version" == "$manifest_version" ]]; then
106+
echo "This release PR already carries $version; nothing to do."
107+
exit 0
108+
fi
109+
git config user.name "github-actions[bot]"
110+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
111+
git fetch origin scalar-next
112+
git checkout -B scalar-next origin/scalar-next
113+
git commit --allow-empty -m "chore: release $version" -m "Release-As: $version"
114+
# Plain (non-force) push: losing a race against a regeneration push fails loudly
115+
# rather than discarding it, and the retitle can simply be repeated.
116+
git push origin scalar-next
117+
echo "Pushed Release-As: $version to scalar-next. The release PR will be re-rendered at that version."

VERSIONING.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,26 @@ Release PRs are opened by the Scalar platform from `scalar-next` against the def
2525
branch — so the PR diff shows the full pending release — and are versioned from
2626
[Conventional Commits](https://www.conventionalcommits.org). Merging a release PR tags the
2727
release, publishes it, and syncs the version bump and changelog back to `scalar-next`.
28-
Pre-1.0, breaking changes bump the minor version; to cut `1.0.0` (or any explicit
29-
version), push an empty commit with a `Release-As` footer to `scalar-next`:
28+
Pre-1.0, breaking changes bump the minor version.
29+
30+
### Choosing an exact version
31+
32+
To release a specific version — `1.0.0`, a hotfix number, anything the commit history would
33+
not have picked — **edit the release PR title** to the version you want:
34+
35+
```text
36+
release: 1.0.0
37+
```
38+
39+
The `Release PR version` check turns red as soon as you save, because the version in the
40+
title no longer matches the version committed in the PR. The Scalar platform then re-renders
41+
the release PR at your version (changelog, manifest, and every version-bearing file), the
42+
title comes back as `release: 1.0.0`, and the check turns green. **Wait for it to be green
43+
before merging** — merging in between would tag a release whose own files still carry the
44+
old version.
45+
46+
The git-native equivalent, if you would rather not touch the PR: push an empty commit with a
47+
`Release-As` footer to `scalar-next`. This is exactly what the title edit does for you.
3048

3149
```sh
3250
git commit --allow-empty -m "chore: release 1.0.0" -m "Release-As: 1.0.0"

openapi.augmented.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7550,7 +7550,7 @@
75507550
{
75517551
"label": "Ruby",
75527552
"lang": "Ruby",
7553-
"source": "require \"json\"\nrequire \"scalar-sdk\"\n\nclient = ScalarApi::Client.new(\n bearer_auth: ENV[\"BEARER_AUTH\"],\n)\n\nresponse = client.namespace(\"namespace\").list\nputs response.inspect"
7553+
"source": "require \"json\"\nrequire \"scalar-sdk\"\n\nclient = ScalarApi::Client.new(\n bearer_auth: ENV[\"BEARER_AUTH\"],\n)\n\nresponse = client.namespaces.list\nputs response.inspect"
75547554
},
75557555
{
75567556
"lang": "Shell",

pyproject.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
[project]
22
name = "scalar-sdk"
3+
<<<<<<< HEAD
34
version = "0.3.1" # x-release-please-version
45
description = "API for managing Scalar platform resources.\n\n## TypeScript SDK\n\nFor TypeScript, we provide a SDK that makes using our API even easier.\n\n### Install\n\n```bash\nnpm add @scalar/sdk\n```\n\n### Get a Scalar API key\n\nCreate an API key in your Scalar account:\n\n- Dashboard: https://dashboard.scalar.com/account\n- Store it in `.env`, for example:\n\n```bash\nSCALAR_API_KEY=your_personal_token\n```\n\n### Exchange your API key for an access token\n\nThe personal token is not an access token. Exchange it first with `postv1AuthExchange`.\n\nIf you use the personal token directly for authenticated API calls, the API returns `401 Invalid authentication token`.\n\n```ts\nimport { Scalar } from '@scalar/sdk'\n\nconst scalar = new Scalar()\n\nconst exchange = await scalar.auth.postv1AuthExchange({\n personalToken: process.env.SCALAR_API_KEY!,\n})\n\nconst accessToken = exchange.accessToken\n```\n\n### Use the access token\n\nConstruct a second client with bearer auth. Use this authenticated client for API calls.\n\n```ts\nimport { Scalar } from '@scalar/sdk'\n\nconst scalar = new Scalar()\n\nconst exchange = await scalar.auth.postv1AuthExchange({\n personalToken: process.env.SCALAR_API_KEY!,\n})\n\nconst authedScalar = new Scalar({\n bearerAuth: exchange.accessToken,\n})\n```\n\n### Notes\n\n- The exchange request itself can be made from a client constructed with no arguments (`new Scalar()`).\n- The exchanged access token is valid for 12 hours.\n- Timestamps are Unix seconds.\n\n### Read more\n\n- [@scalar/sdk on npm](https://www.npmjs.com/package/@scalar/sdk)"
6+
=======
7+
version = "0.1.0" # x-release-please-version
8+
description = "API for managing Scalar platform resources."
9+
readme = "README.md"
10+
>>>>>>> 099aa9f8b3261e0ef995755200c397cfae9a0a80
511
license = "Apache-2.0"
612
requires-python = ">= 3.9"
713
dependencies = [
@@ -13,6 +19,8 @@ dependencies = [
1319
"sniffio",
1420
]
1521

22+
[project.optional-dependencies]
23+
aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9"]
1624

1725
[build-system]
1826
requires = ["hatchling>=1.18.0"]

release-please-config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"include-component-in-tag": false,
1313
"pull-request-title-pattern": "release: ${version}",
1414
"pull-request-header": "Automated Release PR",
15-
"pull-request-footer": "The semver version number is based on included commit messages.\n\nBrought to you by [Scalar](https://scalar.com)",
15+
"pull-request-footer": "The semver version number is based on included commit messages. To release a specific version,\nedit this pull request title to `release: 1.2.3` — Scalar re-creates this pull request at that\nversion, so wait for the `Release PR version` check to pass before merging.\n\nBrought to you by [Scalar](https://scalar.com)",
1616
"changelog-sections": [
1717
{
1818
"type": "feat",

scalar-sdk.manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "ScalarApi",
33
"slug": "scalarApi",
4-
"generatorVersion": "0.19.6",
4+
"generatorVersion": "0.20.1",
55
"servers": [
66
"https://access.scalar.com"
77
],

src/_base_client.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -478,12 +478,6 @@ def _make_status_error(
478478
) -> _exceptions.APIStatusError:
479479
raise NotImplementedError()
480480

481-
def _auth_headers(
482-
self,
483-
security: SecurityOptions, # noqa: ARG002
484-
) -> dict[str, str]:
485-
return {}
486-
487481
def _auth_query(
488482
self,
489483
security: SecurityOptions, # noqa: ARG002
@@ -511,7 +505,9 @@ def _build_headers(
511505
retries_taken: int = 0,
512506
) -> httpx.Headers:
513507
custom_headers = options.headers or {}
514-
headers_dict = _merge_mappings({**self._auth_headers(options.security), **self.default_headers}, custom_headers)
508+
# Auth headers are folded into `default_headers` (matching Stainless), so
509+
# merging `default_headers` here already applies the configured credentials.
510+
headers_dict = _merge_mappings(self.default_headers, custom_headers)
515511
self._validate_headers(headers_dict, custom_headers, params, cookies)
516512

517513
# headers are case-insensitive while dictionaries are not.
@@ -754,6 +750,7 @@ def default_headers(self) -> dict[str, str | Omit]:
754750
"Content-Type": "application/json",
755751
"User-Agent": self.user_agent,
756752
**self.platform_headers(),
753+
**self.auth_headers,
757754
**self._custom_headers,
758755
}
759756

src/_client.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,9 @@ def with_streaming_response(self) -> ScalarWithStreamedResponse:
182182
def qs(self) -> Querystring:
183183
return Querystring(array_format="comma")
184184

185+
@property
185186
@override
186-
def _auth_headers(self, security: dict[str, bool]) -> dict[str, str]:
187-
_ = security
187+
def auth_headers(self) -> dict[str, str]:
188188
return {
189189
**self._bearer_auth_header_auth,
190190
}
@@ -429,9 +429,9 @@ def with_streaming_response(self) -> AsyncScalarWithStreamedResponse:
429429
def qs(self) -> Querystring:
430430
return Querystring(array_format="comma")
431431

432+
@property
432433
@override
433-
def _auth_headers(self, security: dict[str, bool]) -> dict[str, str]:
434-
_ = security
434+
def auth_headers(self) -> dict[str, str]:
435435
return {
436436
**self._bearer_auth_header_auth,
437437
}

0 commit comments

Comments
 (0)