Delegate Promptkit releases to ReleaseKit
This commit is contained in:
@@ -59,40 +59,43 @@ exist.
|
|||||||
## Maintainer Validation
|
## Maintainer Validation
|
||||||
|
|
||||||
For ordinary work, select validation proportionate to the change. Do not repeat
|
For ordinary work, select validation proportionate to the change. Do not repeat
|
||||||
successful checks for unchanged content merely to create a commit. The full
|
successful checks for unchanged content merely to create a commit. During
|
||||||
release-candidate checks have one executable entry point from the repository root:
|
development, run relevant Go tests and vet, format changed Go files, and execute
|
||||||
|
the maintained examples when they are affected:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./scripts/release-check.sh
|
go test ./...
|
||||||
|
go vet ./...
|
||||||
|
gofmt -w path/to/changed.go
|
||||||
|
python3 scripts/check-examples.py
|
||||||
|
git diff --check
|
||||||
```
|
```
|
||||||
|
|
||||||
The [script](../scripts/release-check.sh) owns the executable checks:
|
Release candidates use `./scripts/release.sh VERSION`, which runs the selected
|
||||||
race-enabled tests, vet, the three offline consumer examples with semantic output
|
profile checks and publishes the tag. The [release procedure](release.md)
|
||||||
assertions, tracked Go formatting, local Markdown links, module/workspace
|
describes its requirements and recovery. For an optional diagnostic of a
|
||||||
hygiene, whitespace, and the credential-pattern scan. It does not publish,
|
committed candidate without publication, run `./scripts/releasekit.sh check
|
||||||
change files, validate release-note structure, or retire roadmaps. It requires Go,
|
VERSION`; this is not a required pre-publication step.
|
||||||
Git, a POSIX shell, and Python 3. Tests and examples require no provider
|
|
||||||
credentials or live inference calls.
|
|
||||||
|
|
||||||
Run the consumer example checks independently with
|
The standalone `python3 scripts/check-examples.py` command checks semantic
|
||||||
`python3 scripts/check-examples.py`.
|
expectations for the three offline consumer examples. The focused
|
||||||
|
`python3 scripts/test-release.py` command exercises the release tooling against
|
||||||
|
disposable local repositories; use it when changing that tooling, not as a
|
||||||
|
recurring release gate. Tests and examples require no provider credentials or
|
||||||
|
live inference calls.
|
||||||
|
|
||||||
The script can run on intended uncommitted changes during development.
|
Ordinary validation can run on intended uncommitted work. Release validation
|
||||||
Formatting and documentation checks use tracked files; stage intended new
|
uses committed candidate contents and the selected local Go installation.
|
||||||
files before final validation so those checks include them. The release
|
|
||||||
procedure separately enforces a clean, committed candidate.
|
|
||||||
|
|
||||||
### Change Review
|
### Change Review
|
||||||
|
|
||||||
Inspect `git status --short --untracked-files=all` and the complete diff before
|
Inspect `git status --short --untracked-files=all` and the complete diff before
|
||||||
accepting a change. Reject unintended files, credentials, private data,
|
accepting a change. Reject unintended files, credentials, private data,
|
||||||
generated binaries, test output, downloaded assets, and template residue.
|
generated binaries, test output, downloaded assets, and template residue.
|
||||||
The automated credential scan does not replace review of the actual change.
|
|
||||||
|
|
||||||
For releases, follow the [release procedure](release.md). Its publication
|
For releases, follow the [release procedure](release.md). Do not repeat its
|
||||||
command runs the full local checks once on the final committed candidate and
|
candidate checks as a separate sign-off. Roadmap retirement remains preparation
|
||||||
automates release-note structure and Git checks. Do not repeat ordinary change
|
housekeeping.
|
||||||
review as a release sign-off. Roadmap retirement remains preparation housekeeping.
|
|
||||||
|
|
||||||
Follow the [agent commit policy](policy/agents.md#git-commit-policy) for local
|
Follow the [agent commit policy](policy/agents.md#git-commit-policy) for local
|
||||||
commits. Ordinary commits may leave unrelated work in the workspace; preserve
|
commits. Ordinary commits may leave unrelated work in the workspace; preserve
|
||||||
|
|||||||
@@ -222,20 +222,26 @@ active throughout those operations. Commit the module update before validating
|
|||||||
the final candidate, and describe a raised minimum Go requirement and its
|
the final candidate, and describe a raised minimum Go requirement and its
|
||||||
compatibility impact in the release note under the versioning policy above.
|
compatibility impact in the release note under the versioning policy above.
|
||||||
|
|
||||||
The existing publication script requires Summary, Compatibility, Upgrade, and
|
Use a title containing `Promptkit` and the exact version, followed by a short
|
||||||
Changes headings beneath a title of `# Promptkit vMAJOR.MINOR.PATCH`. State
|
summary and any relevant compatibility impact or upgrade instructions. The
|
||||||
“No action required” or “None” where appropriate. This retained local structure
|
note is committed with the candidate and supplies the annotated tag message.
|
||||||
matches the automated release-note guard; changing it is separate work.
|
No fixed set of section headings is required.
|
||||||
|
|
||||||
The [publication script](../../scripts/release.sh) runs the
|
The [publication command](../../scripts/release.sh) delegates to the released
|
||||||
[local checks](../../scripts/release-check.sh) once for the final committed
|
ReleaseKit tool selected in `go.mod`. The committed
|
||||||
candidate: race-enabled tests, vet, three offline consumer examples, tracked
|
[profile](../../releasekit.json) selects the race-enabled Go suite, full vet,
|
||||||
Go formatting, Markdown links, module/workspace hygiene, whitespace, and a
|
committed Go formatting, local Markdown links, workspace and filesystem
|
||||||
credential-pattern scan. These protect concurrency, assembled library use,
|
replacement inspection, and the offline semantic checks for the three
|
||||||
source distribution, and documentation integrity. A separate ordinary test run
|
maintained consumer examples. These are the selected checks for the final
|
||||||
or standalone build is not an additional release gate. Candidate, tag, and
|
candidate; a separate ordinary test run or standalone build is not an
|
||||||
synchronized-main checks remain automated. No CI or module-availability wait
|
additional release gate. The upstream tool owns candidate, remote, and tag
|
||||||
is required. Licensing footer URLs are preparation work, not a network gate.
|
publication checks. It returns native status 0 for confirmed publication, 1
|
||||||
|
for a known validation or publication failure, 2 for invalid input or profile,
|
||||||
|
and 3 when the push outcome is unknown. For status 3, resolve the exact
|
||||||
|
captured publication endpoint before retrying; the repository-specific
|
||||||
|
[procedure](../release.md#failure-recovery) links to the upstream recovery
|
||||||
|
instructions. Do not wait for CI or Go module availability. Licensing footer
|
||||||
|
URLs are preparation work, not a network gate.
|
||||||
|
|
||||||
## Catalog Profile Compatibility
|
## Catalog Profile Compatibility
|
||||||
|
|
||||||
|
|||||||
@@ -58,17 +58,24 @@ and repository hygiene.
|
|||||||
Introducing hosted CI later would supplement, not silently redefine, this
|
Introducing hosted CI later would supplement, not silently redefine, this
|
||||||
documented validation model.
|
documented validation model.
|
||||||
|
|
||||||
Full release-candidate validation includes race-enabled package tests, explicit
|
The selected ReleaseKit profile runs the race-enabled package suite and full
|
||||||
static analysis, and execution with automated semantic checks of the three maintained
|
vet, checks committed Go formatting, local Markdown links, and module
|
||||||
offline consumer examples. The test run builds the packages; the examples
|
distribution hygiene, and executes semantic assertions for the three
|
||||||
also exercise ordinary compilation. Separate ordinary tests and a standalone
|
maintained offline consumer examples. ReleaseKit owns this shared validation
|
||||||
build are not additional release gates. Ordinary tests remain useful during
|
machinery and publication workflow. The test run builds the packages; the
|
||||||
development. The preparation example protects assembled preparation and
|
examples also exercise ordinary compilation. Separate ordinary tests and a
|
||||||
|
standalone build are not additional release gates. Ordinary tests remain useful
|
||||||
|
during development. The preparation example protects assembled preparation and
|
||||||
inspection behavior. The execution example separately protects assembled
|
inspection behavior. The execution example separately protects assembled
|
||||||
`Run`, injected-client, validation, usage, and result behavior. The observation
|
`Run`, injected-client, validation, usage, and result behavior. The observation
|
||||||
example protects assembled built-in retries, structured proposal and assessment
|
example protects assembled built-in retries, structured proposal and assessment
|
||||||
runs, and application-owned interpretation of metadata.
|
runs, and application-owned interpretation of metadata.
|
||||||
|
|
||||||
|
The standalone `python3 scripts/test-release.py` command exercises Promptkit's
|
||||||
|
ReleaseKit wiring against disposable local repositories. It is focused
|
||||||
|
development validation when release tooling changes, not part of the default
|
||||||
|
Go test suite or a recurring release check.
|
||||||
|
|
||||||
Tests in the default suite must be deterministic, offline, and independent of
|
Tests in the default suite must be deterministic, offline, and independent of
|
||||||
real credentials. They must not invoke paid APIs, use live network
|
real credentials. They must not invoke paid APIs, use live network
|
||||||
dependencies, or depend on mutable external services. Tests that require live
|
dependencies, or depend on mutable external services. Tests that require live
|
||||||
|
|||||||
150
docs/release.md
150
docs/release.md
@@ -1,18 +1,19 @@
|
|||||||
# Release Procedure
|
# Release Procedure
|
||||||
|
|
||||||
This runbook implements the [release policy](policy/release.md). A successful
|
This runbook implements the [release policy](policy/release.md). Promptkit
|
||||||
push of the selected annotated tag to origin completes the release. Never wait
|
publishes source-only Go module tags from `main` to `origin`. A successful push
|
||||||
for CI to finish or succeed.
|
of the selected annotated tag, after its source commit is available on `main`,
|
||||||
|
completes the release. Do not wait for CI or Go module availability.
|
||||||
|
|
||||||
## Prepare The Candidate
|
## Prepare The Candidate
|
||||||
|
|
||||||
Required tools are Git, Go with race-detector support, POSIX shell, and Python
|
Required tools are Git, Go with race-detector support, POSIX shell, and Python
|
||||||
3. Publication requires push access to origin. Work from the repository root.
|
3. Work from the repository root with push access to `origin`.
|
||||||
|
|
||||||
Use the latest Go toolchain available in the development environment for
|
Use the latest Go installation available locally for the release tool, the
|
||||||
release preparation and validation. Make that installation the active `go`
|
candidate validation, and the maintained examples. Make it the active `go`
|
||||||
command, then advance PromptKit's minimum Go requirement to that series' first
|
command and record its version. Set Promptkit's consumer minimum to the initial
|
||||||
stable release before committing the candidate:
|
stable release of that Go series before committing the candidate:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
export GOTOOLCHAIN=local
|
export GOTOOLCHAIN=local
|
||||||
@@ -23,42 +24,36 @@ release_go_series=${release_go_series%.*}
|
|||||||
go mod edit "-go=${release_go_series}.0"
|
go mod edit "-go=${release_go_series}.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
For example, building with Go 1.26.8 normally records `go 1.26.0`, not
|
For example, Go 1.26.8 normally results in `go 1.26.0`. Keep a higher patch
|
||||||
`go 1.26.8`. If a dependency or a specific correctness fix requires a higher
|
minimum if a dependency or correctness fix requires it. Prepare module caches
|
||||||
patch minimum, use that minimum instead of reducing it to `.0`.
|
before validation; this may update `go.sum` and belongs before the candidate
|
||||||
|
commit:
|
||||||
|
|
||||||
Include the module change in the candidate commit. Describe a raised minimum
|
```sh
|
||||||
Go requirement in the release note and select the release version under the
|
GOTOOLCHAIN=local go mod download
|
||||||
[compatibility policy](policy/release.md#versioning-and-compatibility).
|
|
||||||
Keep this toolchain active through validation and publication. Toolchain
|
|
||||||
selection and the module update are preparation, not mutations of an already
|
|
||||||
validated candidate.
|
|
||||||
|
|
||||||
Select an unused version and create `docs/releases/<version>.md` using the
|
|
||||||
[required structure](policy/release.md#release-documentation). Describe
|
|
||||||
compatibility impact and consumer actions and update affected canonical docs.
|
|
||||||
For catalog updates, record selected catalog versions and any consumer action,
|
|
||||||
linking to the catalog policy rather than repeating its contracts.
|
|
||||||
|
|
||||||
Follow the [OSS policy](policy/oss.md) when preparing licensing and attribution
|
|
||||||
changes. Each new note must end with the following navigation, replacing
|
|
||||||
`vMAJOR.MINOR.PATCH` with the selected release tag:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
[COPYRIGHT](https://gitea.maximumdirect.net/eric/promptkit/src/tag/vMAJOR.MINOR.PATCH/COPYRIGHT) | [LICENSE](https://gitea.maximumdirect.net/eric/promptkit/src/tag/vMAJOR.MINOR.PATCH/LICENSE) | [ATTRIBUTION](https://gitea.maximumdirect.net/eric/promptkit/src/tag/vMAJOR.MINOR.PATCH/docs/oss/attribution.md)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Check these file paths against the candidate; a future tag need not resolve
|
Choose an unused stable version and create `docs/releases/<version>.md` with a
|
||||||
remotely during preparation. The annotated tag derives from this same note,
|
title containing `Promptkit` and that exact version, a short summary, and any
|
||||||
including its footer. Promptkit publishes no separate hosted release body or
|
relevant compatibility impact or upgrade instructions. No fixed section
|
||||||
download page. If such a channel is deliberately introduced, reuse the note
|
headings are required. Describe a raised Go minimum and its compatibility
|
||||||
and preserve its footer. No extra network checks, artifacts, or approval gates
|
impact under the [versioning policy](policy/release.md#versioning-and-compatibility).
|
||||||
are introduced, and published notes are not retrofitted.
|
End each new note with the licensing footer below, using the selected version:
|
||||||
|
|
||||||
Retire completed roadmaps, preserving planned or deferred work and repairing
|
```markdown
|
||||||
links. This is housekeeping, not a separate approval gate. Review changes
|
[COPYRIGHT](https://gitea.maximumdirect.net/eric/promptkit/src/tag/vX.Y.Z/COPYRIGHT) | [LICENSE](https://gitea.maximumdirect.net/eric/promptkit/src/tag/vX.Y.Z/LICENSE) | [ATTRIBUTION](https://gitea.maximumdirect.net/eric/promptkit/src/tag/vX.Y.Z/docs/oss/attribution.md)
|
||||||
through the ordinary development workflow, commit them, and push main to
|
```
|
||||||
origin. No repeated release-specific diff review is required.
|
|
||||||
|
Check the footer paths against the candidate; the future tag need not resolve
|
||||||
|
remotely during preparation. Follow the [OSS policy](policy/oss.md) for these
|
||||||
|
links. Promptkit publishes no binaries, hosted download page, or other
|
||||||
|
asynchronous deliverables.
|
||||||
|
|
||||||
|
Retire completed roadmaps while preserving deferred work and repairing incoming
|
||||||
|
links. Review the intended changes through the ordinary development workflow,
|
||||||
|
commit the candidate on `main`, and push `main` to `origin` before publication.
|
||||||
|
Preparation may update module metadata; the release bootstrap and validation
|
||||||
|
preserve the committed candidate.
|
||||||
|
|
||||||
## Validate And Publish
|
## Validate And Publish
|
||||||
|
|
||||||
@@ -68,34 +63,29 @@ For an optional local diagnostic of a committed candidate, run:
|
|||||||
./scripts/releasekit.sh check vMAJOR.MINOR.PATCH
|
./scripts/releasekit.sh check vMAJOR.MINOR.PATCH
|
||||||
```
|
```
|
||||||
|
|
||||||
The candidate must be committed on `main`, use a stable version, and include
|
This is not a required step before publication. The upstream
|
||||||
its version-matched release note. See the upstream
|
[candidate-check contract](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.2.0/docs/consumers/check.md)
|
||||||
[candidate check contract](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.2.0/docs/consumers/check.md)
|
describes its requirements.
|
||||||
for its requirements. This diagnostic is optional and is not required before
|
|
||||||
publication.
|
|
||||||
|
|
||||||
Run the following with the chosen unused version (replace the placeholder):
|
Run the single publication command with the chosen version:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./scripts/release.sh vMAJOR.MINOR.PATCH
|
./scripts/release.sh vMAJOR.MINOR.PATCH
|
||||||
```
|
```
|
||||||
|
|
||||||
The [publication script](../scripts/release.sh) checks a clean, synchronized
|
The familiar wrapper delegates to the native ReleaseKit executable selected in
|
||||||
main candidate, the version and unused tag, and the release note's title and
|
`go.mod`. The committed [profile](../releasekit.json) selects the race-enabled
|
||||||
required headings. It runs the [local checks](../scripts/release-check.sh)
|
Go suite, full vet, committed Go formatting, local Markdown links, workspace
|
||||||
once, rechecks candidate identity and publication state, generates the
|
and filesystem replacement inspection, and semantic checks for the three
|
||||||
annotation from the committed note plus the validated commit, and pushes only
|
maintained offline consumer examples. ReleaseKit also verifies the committed
|
||||||
the selected annotated tag.
|
candidate and publication state, then publishes only the selected tag. Its
|
||||||
|
checks run once for this candidate; do not repeat them as a separate sign-off.
|
||||||
|
|
||||||
There is no separate tag-message authoring, example-output inspection,
|
The native command returns status 0 for confirmed publication, 1 for a known
|
||||||
metadata inspection, or human sign-off. Compatibility and release-note content
|
validation or publication failure, 2 for invalid input or profile, and 3 when
|
||||||
remain ordinary editorial responsibilities. Development validation is useful
|
the push outcome remains unknown. Successful publication needs no remote
|
||||||
but does not require a second full run before invoking the publication script.
|
read-back, CI wait, or installation check. Provider probes and inference calls
|
||||||
|
are not part of validation.
|
||||||
The script stops successfully after the tag push. CI completion, CI success,
|
|
||||||
remote read-back, and Go module availability are not release completion gates.
|
|
||||||
Do not wait or poll for them. Provider probes and inference calls are never
|
|
||||||
part of release validation.
|
|
||||||
|
|
||||||
## Catalog Profile Compatibility
|
## Catalog Profile Compatibility
|
||||||
|
|
||||||
@@ -104,28 +94,32 @@ catalog compatibility boundary. This heading preserves older incoming links.
|
|||||||
|
|
||||||
## Failure Recovery
|
## Failure Recovery
|
||||||
|
|
||||||
If local checks fail, fix the candidate, commit and push changes, then rerun
|
For status 1, use the diagnostic to distinguish a validation failure from a
|
||||||
the publication command. It never automatically removes a tag.
|
known publication conflict. Correct and commit candidate defects, push `main`,
|
||||||
|
and rerun publication with an unused version when appropriate. Status 2 means
|
||||||
|
the invocation or profile must be corrected.
|
||||||
|
|
||||||
If a push fails or its result is ambiguous, set `RELEASE_VERSION` to the
|
For status 3, do not retry or remove the local tag until the remote outcome is
|
||||||
attempted version and inspect origin before retrying:
|
resolved. ReleaseKit reports the attempted version, tag object, and recovery
|
||||||
|
state, including the captured publication endpoint when available. Use that
|
||||||
|
endpoint—not merely the current `origin` URL—to query the exact tag; follow the
|
||||||
|
upstream
|
||||||
|
[failure-recovery procedure](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.2.0/docs/release.md#resolve-failures):
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git ls-remote --tags origin "refs/tags/$RELEASE_VERSION" "refs/tags/$RELEASE_VERSION^{}"
|
releasekit_endpoint='COPY_CAPTURED_ENDPOINT'
|
||||||
git rev-parse "refs/tags/$RELEASE_VERSION" "refs/tags/$RELEASE_VERSION^{}"
|
releasekit_version='vX.Y.Z'
|
||||||
|
test "$(git ls-remote --get-url -- "$releasekit_endpoint")" = "$releasekit_endpoint" &&
|
||||||
|
git ls-remote --refs -- "$releasekit_endpoint" "refs/tags/$releasekit_version"
|
||||||
```
|
```
|
||||||
|
|
||||||
Matching remote tag and peeled commit values confirm publication, even if the
|
Compare the returned object ID with the reported tag object. An exact match
|
||||||
push command reported an error. If the remote query fails, publication status
|
confirms publication. A missing ref confirms absence only when the endpoint
|
||||||
is still unknown. Do not delete or recreate the local tag until absence on
|
identity check and query both succeed. If either check fails, the outcome
|
||||||
origin is confirmed. Once confirmed unpublished, an unsuccessful attempt's
|
remains unknown; correct the transport configuration and resolve it before
|
||||||
local tag can be removed and the publication command rerun. Never overwrite
|
retrying. Only after authoritative absence is confirmed may an unpublished
|
||||||
a published tag.
|
local tag be removed before correcting the candidate or retrying. Never move,
|
||||||
|
overwrite, or delete a published tag.
|
||||||
CI or module-resolution problems may be investigated separately; temporary
|
|
||||||
service, network, or authentication failures do not require another release.
|
|
||||||
Only an actual defect requiring changes to published contents warrants a new
|
|
||||||
appropriate version. See the [recovery policy](policy/release.md#immutability-and-failure-recovery).
|
|
||||||
|
|
||||||
## Policy Changes
|
## Policy Changes
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ Status: Stage 1's tool selection and dependency preparation are complete in
|
|||||||
requirement. Stage 2's example checker and shared validation profile are
|
requirement. Stage 2's example checker and shared validation profile are
|
||||||
complete in `0d163f8`. Stage 3's native tool bootstrap is complete in
|
complete in `0d163f8`. Stage 3's native tool bootstrap is complete in
|
||||||
`d594085`. Stage 4's successful-publication fixture is complete in `51b0729`.
|
`d594085`. Stage 4's successful-publication fixture is complete in `51b0729`.
|
||||||
Stage 5's failure-propagation fixture is complete. Resume adoption at Stage 6
|
Stage 5's failure-propagation fixture is complete in `05082c6`. Stage 6's
|
||||||
|
publication cutover and documentation are complete. Resume adoption at Stage 7
|
||||||
after this revision.
|
after this revision.
|
||||||
|
|
||||||
## Baseline And Working Rules
|
## Baseline And Working Rules
|
||||||
|
|||||||
@@ -1,126 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# Run local checks without publishing or changing files.
|
|
||||||
set -eu
|
|
||||||
cd "$(dirname "$0")/.."
|
|
||||||
|
|
||||||
go test -race -vet=off ./...
|
|
||||||
go vet ./...
|
|
||||||
python3 ./scripts/check-examples.py
|
|
||||||
|
|
||||||
unformatted=$(
|
|
||||||
git ls-files '*.go' |
|
|
||||||
while IFS= read -r go_file
|
|
||||||
do
|
|
||||||
gofmt -l "$go_file"
|
|
||||||
done
|
|
||||||
)
|
|
||||||
test -z "$unformatted"
|
|
||||||
|
|
||||||
python3 - <<'PY'
|
|
||||||
from pathlib import Path
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
from urllib.parse import unquote
|
|
||||||
|
|
||||||
root = Path.cwd().resolve()
|
|
||||||
markdown_files = [
|
|
||||||
root / name
|
|
||||||
for name in subprocess.check_output(
|
|
||||||
["git", "ls-files", "*.md"], text=True
|
|
||||||
).splitlines()
|
|
||||||
]
|
|
||||||
link_pattern = re.compile(r"!?\[[^]]*\]\(([^)]+)\)")
|
|
||||||
heading_pattern = re.compile(r"^#{1,6}\s+(.+?)\s*#*\s*$")
|
|
||||||
scheme_pattern = re.compile(r"^[a-z][a-z0-9+.-]*:", re.IGNORECASE)
|
|
||||||
|
|
||||||
|
|
||||||
def markdown_lines(path):
|
|
||||||
in_fence = False
|
|
||||||
fence = ""
|
|
||||||
for line in path.read_text(encoding="utf-8").splitlines():
|
|
||||||
stripped = line.lstrip()
|
|
||||||
marker = stripped[:3]
|
|
||||||
if marker in {"```", "~~~"}:
|
|
||||||
if not in_fence:
|
|
||||||
in_fence = True
|
|
||||||
fence = marker
|
|
||||||
elif marker == fence:
|
|
||||||
in_fence = False
|
|
||||||
fence = ""
|
|
||||||
continue
|
|
||||||
if not in_fence:
|
|
||||||
yield line
|
|
||||||
|
|
||||||
|
|
||||||
anchor_cache = {}
|
|
||||||
|
|
||||||
|
|
||||||
def anchors(path):
|
|
||||||
if path in anchor_cache:
|
|
||||||
return anchor_cache[path]
|
|
||||||
found = set()
|
|
||||||
counts = {}
|
|
||||||
for line in markdown_lines(path):
|
|
||||||
match = heading_pattern.match(line)
|
|
||||||
if not match:
|
|
||||||
continue
|
|
||||||
heading = re.sub(r"<[^>]+>", "", match.group(1)).replace("`", "")
|
|
||||||
base = re.sub(r"[^\w\- ]", "", heading.lower()).replace(" ", "-")
|
|
||||||
count = counts.get(base, 0)
|
|
||||||
counts[base] = count + 1
|
|
||||||
found.add(base if count == 0 else f"{base}-{count}")
|
|
||||||
anchor_cache[path] = found
|
|
||||||
return found
|
|
||||||
|
|
||||||
|
|
||||||
failures = []
|
|
||||||
for source in markdown_files:
|
|
||||||
text = "\n".join(markdown_lines(source))
|
|
||||||
for match in link_pattern.finditer(text):
|
|
||||||
target = match.group(1).strip()
|
|
||||||
if target.startswith("<") and target.endswith(">"):
|
|
||||||
target = target[1:-1]
|
|
||||||
if scheme_pattern.match(target) or target.startswith("//"):
|
|
||||||
continue
|
|
||||||
path_text, separator, fragment = target.partition("#")
|
|
||||||
destination = source if not path_text else source.parent / unquote(path_text)
|
|
||||||
try:
|
|
||||||
destination = destination.resolve()
|
|
||||||
destination.relative_to(root)
|
|
||||||
except ValueError:
|
|
||||||
failures.append(f"{source.relative_to(root)}: escapes repository: {target}")
|
|
||||||
continue
|
|
||||||
if not destination.exists():
|
|
||||||
failures.append(f"{source.relative_to(root)}: missing target: {target}")
|
|
||||||
continue
|
|
||||||
if separator and destination.suffix.lower() == ".md":
|
|
||||||
fragment = unquote(fragment).lower()
|
|
||||||
if fragment not in anchors(destination):
|
|
||||||
failures.append(f"{source.relative_to(root)}: missing anchor: {target}")
|
|
||||||
|
|
||||||
if failures:
|
|
||||||
print("\n".join(failures), file=sys.stderr)
|
|
||||||
raise SystemExit(1)
|
|
||||||
PY
|
|
||||||
|
|
||||||
case "$(go env GOWORK)" in
|
|
||||||
''|off) ;;
|
|
||||||
*) printf '%s\n' 'an active Go workspace is not allowed' >&2; exit 1 ;;
|
|
||||||
esac
|
|
||||||
test -z "$(git ls-files go.work go.work.sum)"
|
|
||||||
test ! -e vendor
|
|
||||||
if grep -Eq '^[[:space:]]*replace([[:space:]]|\()' go.mod
|
|
||||||
then
|
|
||||||
printf '%s\n' 'go.mod contains a replacement' >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
git diff --check
|
|
||||||
git diff --cached --check
|
|
||||||
credential_pattern='-----BEGIN ([A-Z0-9]+ )?PRIV''ATE KEY-----|AKI''A[0-9A-Z]{16}|gh[pousr]_[A-Za-z0-9]{36,}|sk-[A-Za-z0-9]{32,}'
|
|
||||||
if git grep -nEI -e "$credential_pattern" -- .
|
|
||||||
then
|
|
||||||
printf '%s\n' 'possible credential found' >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
@@ -1,57 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Validate one committed candidate and publish only its selected annotated tag.
|
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
|
exec ./scripts/releasekit.sh publish "$@"
|
||||||
release_version=${1:?usage: scripts/release.sh vMAJOR.MINOR.PATCH}
|
|
||||||
printf '%s\n' "$release_version" |
|
|
||||||
grep -Eq '^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$'
|
|
||||||
release_commit=$(git rev-parse --verify 'HEAD^{commit}')
|
|
||||||
|
|
||||||
check_candidate() {
|
|
||||||
test "$(git branch --show-current)" = main
|
|
||||||
test -z "$(git status --porcelain)"
|
|
||||||
test "$(git rev-parse HEAD)" = "$release_commit"
|
|
||||||
git fetch origin main
|
|
||||||
test "$(git rev-parse refs/remotes/origin/main)" = "$release_commit"
|
|
||||||
if git show-ref --verify --quiet "refs/tags/$release_version"; then
|
|
||||||
printf '%s\n' "local tag already exists: $release_version" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
remote_refs=$(git ls-remote --tags origin "refs/tags/$release_version" "refs/tags/$release_version^{}")
|
|
||||||
test -z "$remote_refs"
|
|
||||||
}
|
|
||||||
|
|
||||||
check_candidate
|
|
||||||
python3 - "$release_version" <<'PY'
|
|
||||||
from pathlib import Path
|
|
||||||
import sys
|
|
||||||
|
|
||||||
version = sys.argv[1]
|
|
||||||
note = Path("docs/releases") / (version + ".md")
|
|
||||||
lines = note.read_text(encoding="utf-8").splitlines()
|
|
||||||
assert lines and lines[0] == "# Promptkit " + version, f"wrong release title: {note}"
|
|
||||||
headings = set()
|
|
||||||
fence = None
|
|
||||||
for line in lines:
|
|
||||||
marker = line.lstrip()[:3]
|
|
||||||
if marker in {"```", "~~~"}:
|
|
||||||
if fence is None:
|
|
||||||
fence = marker
|
|
||||||
elif fence == marker:
|
|
||||||
fence = None
|
|
||||||
elif fence is None:
|
|
||||||
headings.add(line)
|
|
||||||
for section in ("Summary", "Compatibility", "Upgrade", "Changes"):
|
|
||||||
assert "## " + section in headings, f"missing {section}: {note}"
|
|
||||||
PY
|
|
||||||
|
|
||||||
./scripts/release-check.sh
|
|
||||||
check_candidate
|
|
||||||
release_note=$(git show "$release_commit:docs/releases/$release_version.md")
|
|
||||||
printf '%s\n\nValidated commit: %s\n' "$release_note" "$release_commit" |
|
|
||||||
git tag --annotate --cleanup=verbatim "$release_version" --file - "$release_commit"
|
|
||||||
test "$(git cat-file -t "refs/tags/$release_version")" = tag
|
|
||||||
test "$(git rev-parse "refs/tags/$release_version^{commit}")" = "$release_commit"
|
|
||||||
git push origin "refs/tags/$release_version:refs/tags/$release_version"
|
|
||||||
printf '%s\n' "Released $release_version. No CI or module-availability wait is required."
|
|
||||||
|
|||||||
@@ -179,6 +179,7 @@ def prepare_repository(repository, remote, environment, module_bytes, version):
|
|||||||
|
|
||||||
scripts = repository / "scripts"
|
scripts = repository / "scripts"
|
||||||
scripts.mkdir(parents=True, exist_ok=True)
|
scripts.mkdir(parents=True, exist_ok=True)
|
||||||
|
shutil.copy2(REPOSITORY_ROOT / "scripts/release.sh", scripts / "release.sh")
|
||||||
shutil.copy2(REPOSITORY_ROOT / "scripts/releasekit.sh", scripts / "releasekit.sh")
|
shutil.copy2(REPOSITORY_ROOT / "scripts/releasekit.sh", scripts / "releasekit.sh")
|
||||||
write_file(
|
write_file(
|
||||||
scripts / "check-examples.py",
|
scripts / "check-examples.py",
|
||||||
@@ -215,9 +216,9 @@ def prepare_repository(repository, remote, environment, module_bytes, version):
|
|||||||
|
|
||||||
|
|
||||||
def verify_publication(repository, remote, temporary_root, environment, module_bytes, selected_go):
|
def verify_publication(repository, remote, temporary_root, environment, module_bytes, selected_go):
|
||||||
bootstrap = repository / "scripts/releasekit.sh"
|
publication = repository / "scripts/release.sh"
|
||||||
run(
|
run(
|
||||||
[str(bootstrap), "publish", RELEASE_VERSION],
|
[str(publication), RELEASE_VERSION],
|
||||||
cwd=temporary_root,
|
cwd=temporary_root,
|
||||||
environment=environment,
|
environment=environment,
|
||||||
)
|
)
|
||||||
@@ -324,7 +325,7 @@ def verify_validation_failure(
|
|||||||
)
|
)
|
||||||
|
|
||||||
result = execute(
|
result = execute(
|
||||||
[str(repository / "scripts/releasekit.sh"), "publish", FAILED_RELEASE_VERSION],
|
[str(repository / "scripts/release.sh"), FAILED_RELEASE_VERSION],
|
||||||
cwd=temporary_root,
|
cwd=temporary_root,
|
||||||
environment=scenario_environment,
|
environment=scenario_environment,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user