labtrail 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- labtrail-0.1.0/.forgejo/workflows/ci.yml +385 -0
- labtrail-0.1.0/.github/workflows/ci.yml +398 -0
- labtrail-0.1.0/.gitignore +231 -0
- labtrail-0.1.0/LICENSE +73 -0
- labtrail-0.1.0/PKG-INFO +185 -0
- labtrail-0.1.0/README.md +95 -0
- labtrail-0.1.0/docs/conf.py +51 -0
- labtrail-0.1.0/docs/design/plan.md +2 -0
- labtrail-0.1.0/docs/design/requirements.md +2 -0
- labtrail-0.1.0/docs/index.md +60 -0
- labtrail-0.1.0/docs/methodology/author-loop.md +119 -0
- labtrail-0.1.0/docs/methodology/check-contract.md +75 -0
- labtrail-0.1.0/docs/methodology/index.md +64 -0
- labtrail-0.1.0/docs/methodology/learner-loop.md +102 -0
- labtrail-0.1.0/docs/methodology/metadata.md +101 -0
- labtrail-0.1.0/docs/methodology/tree.md +70 -0
- labtrail-0.1.0/docs/plan.md +1387 -0
- labtrail-0.1.0/docs/reference/cli.md +272 -0
- labtrail-0.1.0/docs/reference/collected-document.md +75 -0
- labtrail-0.1.0/docs/reference/ctrf.md +96 -0
- labtrail-0.1.0/docs/reference/helpers.md +158 -0
- labtrail-0.1.0/docs/reference/skills.md +101 -0
- labtrail-0.1.0/docs/requirements.md +1113 -0
- labtrail-0.1.0/docs_methodology.md +6 -0
- labtrail-0.1.0/ivpm.yaml +19 -0
- labtrail-0.1.0/pyproject.toml +60 -0
- labtrail-0.1.0/setup.cfg +4 -0
- labtrail-0.1.0/src/labtrail/__init__.py +22 -0
- labtrail-0.1.0/src/labtrail/__main__.py +6 -0
- labtrail-0.1.0/src/labtrail/_version.py +24 -0
- labtrail-0.1.0/src/labtrail/check/__init__.py +73 -0
- labtrail-0.1.0/src/labtrail/check/_assertions.py +276 -0
- labtrail-0.1.0/src/labtrail/check/_normalize.py +121 -0
- labtrail-0.1.0/src/labtrail/check/_run.py +136 -0
- labtrail-0.1.0/src/labtrail/cli.py +101 -0
- labtrail-0.1.0/src/labtrail/collect.py +157 -0
- labtrail-0.1.0/src/labtrail/commands/__init__.py +0 -0
- labtrail-0.1.0/src/labtrail/commands/_common.py +66 -0
- labtrail-0.1.0/src/labtrail/commands/assert_.py +103 -0
- labtrail-0.1.0/src/labtrail/commands/check.py +124 -0
- labtrail-0.1.0/src/labtrail/commands/collect.py +47 -0
- labtrail-0.1.0/src/labtrail/commands/doctor.py +137 -0
- labtrail-0.1.0/src/labtrail/commands/list_.py +102 -0
- labtrail-0.1.0/src/labtrail/commands/lock.py +186 -0
- labtrail-0.1.0/src/labtrail/commands/open_.py +185 -0
- labtrail-0.1.0/src/labtrail/commands/propagate.py +178 -0
- labtrail-0.1.0/src/labtrail/commands/roster.py +96 -0
- labtrail-0.1.0/src/labtrail/commands/status.py +146 -0
- labtrail-0.1.0/src/labtrail/commands/validate.py +87 -0
- labtrail-0.1.0/src/labtrail/dev/__init__.py +0 -0
- labtrail-0.1.0/src/labtrail/dev/__main__.py +71 -0
- labtrail-0.1.0/src/labtrail/dev/gen_cli_docs.py +118 -0
- labtrail-0.1.0/src/labtrail/dev/gen_skill.py +455 -0
- labtrail-0.1.0/src/labtrail/discovery.py +203 -0
- labtrail-0.1.0/src/labtrail/doctor.py +358 -0
- labtrail-0.1.0/src/labtrail/errors.py +105 -0
- labtrail-0.1.0/src/labtrail/execute.py +231 -0
- labtrail-0.1.0/src/labtrail/git.py +316 -0
- labtrail-0.1.0/src/labtrail/metadata.py +403 -0
- labtrail-0.1.0/src/labtrail/model.py +213 -0
- labtrail-0.1.0/src/labtrail/propagate.py +543 -0
- labtrail-0.1.0/src/labtrail/report/__init__.py +0 -0
- labtrail-0.1.0/src/labtrail/report/console.py +173 -0
- labtrail-0.1.0/src/labtrail/report/ctrf.py +274 -0
- labtrail-0.1.0/src/labtrail/share/skills/labtrail/SKILL.md +218 -0
- labtrail-0.1.0/src/labtrail/share/skills/labtrail-authoring/SKILL.md +295 -0
- labtrail-0.1.0/src/labtrail/share/skills/labtrail-checks/SKILL.md +302 -0
- labtrail-0.1.0/src/labtrail/share/skills/labtrail-learner/SKILL.md +173 -0
- labtrail-0.1.0/src/labtrail/share/templates/course-SKILL.md +47 -0
- labtrail-0.1.0/src/labtrail/share/templates/lab-SKILL.md +38 -0
- labtrail-0.1.0/src/labtrail/share/templates/lab.yaml +29 -0
- labtrail-0.1.0/src/labtrail/share/templates/trail.yaml +35 -0
- labtrail-0.1.0/src/labtrail/skills.py +76 -0
- labtrail-0.1.0/src/labtrail/trail.py +450 -0
- labtrail-0.1.0/src/labtrail/validate.py +307 -0
- labtrail-0.1.0/src/labtrail/verdict.py +107 -0
- labtrail-0.1.0/src/labtrail/worktree.py +176 -0
- labtrail-0.1.0/src/labtrail.egg-info/PKG-INFO +185 -0
- labtrail-0.1.0/src/labtrail.egg-info/SOURCES.txt +108 -0
- labtrail-0.1.0/src/labtrail.egg-info/dependency_links.txt +1 -0
- labtrail-0.1.0/src/labtrail.egg-info/entry_points.txt +9 -0
- labtrail-0.1.0/src/labtrail.egg-info/requires.txt +5 -0
- labtrail-0.1.0/src/labtrail.egg-info/scm_file_list.json +103 -0
- labtrail-0.1.0/src/labtrail.egg-info/scm_version.json +8 -0
- labtrail-0.1.0/src/labtrail.egg-info/top_level.txt +1 -0
- labtrail-0.1.0/tests/__init__.py +0 -0
- labtrail-0.1.0/tests/acceptance/conftest.py +72 -0
- labtrail-0.1.0/tests/acceptance/test_v1_acceptance.py +586 -0
- labtrail-0.1.0/tests/conftest.py +66 -0
- labtrail-0.1.0/tests/data/ctrf.schema.json +713 -0
- labtrail-0.1.0/tests/integration/conftest.py +72 -0
- labtrail-0.1.0/tests/integration/test_check.py +173 -0
- labtrail-0.1.0/tests/integration/test_collect.py +163 -0
- labtrail-0.1.0/tests/integration/test_learner.py +428 -0
- labtrail-0.1.0/tests/integration/test_list.py +80 -0
- labtrail-0.1.0/tests/integration/test_propagate.py +350 -0
- labtrail-0.1.0/tests/integration/test_validate.py +382 -0
- labtrail-0.1.0/tests/integration/test_worktree_sharing.py +118 -0
- labtrail-0.1.0/tests/trailkit/__init__.py +34 -0
- labtrail-0.1.0/tests/trailkit/builder.py +599 -0
- labtrail-0.1.0/tests/unit/test_check_helpers.py +325 -0
- labtrail-0.1.0/tests/unit/test_ctrf.py +223 -0
- labtrail-0.1.0/tests/unit/test_discovery.py +115 -0
- labtrail-0.1.0/tests/unit/test_doc_conventions.py +243 -0
- labtrail-0.1.0/tests/unit/test_execute.py +166 -0
- labtrail-0.1.0/tests/unit/test_git.py +125 -0
- labtrail-0.1.0/tests/unit/test_metadata.py +330 -0
- labtrail-0.1.0/tests/unit/test_packaging.py +157 -0
- labtrail-0.1.0/tests/unit/test_trail.py +189 -0
- labtrail-0.1.0/tests/unit/test_verdict.py +77 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
# fvutils/labtrail -- Forgejo Actions.
|
|
2
|
+
#
|
|
3
|
+
# COPY THIS AS A PAIR. Its counterpart is .github/workflows/ci.yml. Derived
|
|
4
|
+
# from dvkit/forgejo-ops templates/ci.forgejo.yml; see that repo's
|
|
5
|
+
# templates/README.md.
|
|
6
|
+
#
|
|
7
|
+
# The two files differ in the `authority ==` comparison, in the Forgejo runner
|
|
8
|
+
# constraints below, and in nothing else. If you are changing anything else,
|
|
9
|
+
# they are drifting.
|
|
10
|
+
#
|
|
11
|
+
# ---------------------------------------------------------------------------
|
|
12
|
+
# DECISION, 2026-08-30 (inherited from the estate template): publish jobs FAIL
|
|
13
|
+
# CLOSED on an unreachable switch.
|
|
14
|
+
#
|
|
15
|
+
# If https://dvkit.org/.well-known/release-authority.json cannot be fetched ON A
|
|
16
|
+
# TAG, the gate fails and nothing publishes. A release blocked by an outage is
|
|
17
|
+
# recoverable; a release published twice from two forges is not.
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
#
|
|
20
|
+
# FORGEJO RUNNER CONSTRAINTS -- all measured on this instance, not assumed.
|
|
21
|
+
# `runs-on: ubuntu-latest` maps to catthehacker/ubuntu:act-22.04.
|
|
22
|
+
#
|
|
23
|
+
# * Python is 3.10, and pip is 22.0.2 -- which PREDATES PEP 660, so
|
|
24
|
+
# `pip install -e .` fails outright on a pyproject-only project. Every job
|
|
25
|
+
# here opens with a venv whose pip has been upgraded, put on PATH so the
|
|
26
|
+
# steps below are textually identical to the GitHub side.
|
|
27
|
+
# * `python` unqualified DOES NOT EXIST. Use python3 (before the venv is on
|
|
28
|
+
# PATH; after it, `python` is the venv's).
|
|
29
|
+
# * NO THIRD-PARTY ACTIONS. A bare `uses:` resolves against code.forgejo.org,
|
|
30
|
+
# not github.com -- so actions/setup-python and pypa/gh-action-pypi-publish
|
|
31
|
+
# are unavailable. actions/checkout and forgejo/upload-artifact DO resolve.
|
|
32
|
+
# Publishing is plain twine, which also keeps GitHub off the critical path
|
|
33
|
+
# of a release.
|
|
34
|
+
# * 3.10 is labtrail's declared floor, so the single runner Python is the
|
|
35
|
+
# more interesting half of the GitHub matrix rather than a compromise.
|
|
36
|
+
#
|
|
37
|
+
# Trial-run any step you add before landing it:
|
|
38
|
+
# docker run --rm -v "$PWD:/w" -w /w catthehacker/ubuntu:act-22.04 bash -c '<steps>'
|
|
39
|
+
#
|
|
40
|
+
# LABTRAIL-SPECIFIC DIVERGENCE FROM THE TEMPLATE: the template stamps a version
|
|
41
|
+
# by `sed`-ing a `SUFFIX = ""` line in a version file. labtrail has no such
|
|
42
|
+
# file -- setuptools-scm derives the version from git. See "Stamp the build
|
|
43
|
+
# version" below, which diverges in the safer direction.
|
|
44
|
+
|
|
45
|
+
name: CI
|
|
46
|
+
|
|
47
|
+
on:
|
|
48
|
+
push:
|
|
49
|
+
branches: ['**']
|
|
50
|
+
tags: ['v*']
|
|
51
|
+
pull_request:
|
|
52
|
+
workflow_dispatch:
|
|
53
|
+
schedule:
|
|
54
|
+
- cron: '17 5 * * *'
|
|
55
|
+
|
|
56
|
+
jobs:
|
|
57
|
+
# ---------------------------------------------------------------- gate ----
|
|
58
|
+
gate:
|
|
59
|
+
# Release master switch. Identical to the GitHub side except for what the
|
|
60
|
+
# publish job compares the answer against. `if:` on a job cannot reference
|
|
61
|
+
# a step in its own job, so the switch is resolved here and consumed
|
|
62
|
+
# through needs.gate.outputs.authority.
|
|
63
|
+
name: Release authority
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
outputs:
|
|
66
|
+
authority: ${{ steps.resolve.outputs.authority }}
|
|
67
|
+
steps:
|
|
68
|
+
- id: resolve
|
|
69
|
+
# One line on purpose: a multi-line `python3 -c` has to start at column
|
|
70
|
+
# 0, which terminates the YAML block scalar and silently corrupts the
|
|
71
|
+
# workflow.
|
|
72
|
+
run: |
|
|
73
|
+
url=https://dvkit.org/.well-known/release-authority.json
|
|
74
|
+
if ! body=$(curl -fsS --max-time 10 "$url"); then
|
|
75
|
+
case "$GITHUB_REF" in
|
|
76
|
+
refs/tags/v*)
|
|
77
|
+
echo "::error::release-authority switch unreachable; refusing to publish"
|
|
78
|
+
exit 1 ;;
|
|
79
|
+
*)
|
|
80
|
+
echo "authority=none" >> "$GITHUB_OUTPUT"
|
|
81
|
+
echo "switch unreachable on a non-tag ref; nothing to release"
|
|
82
|
+
exit 0 ;;
|
|
83
|
+
esac
|
|
84
|
+
fi
|
|
85
|
+
auth=$(printf '%s' "$body" | python3 -c 'import json,os,sys; d=json.load(sys.stdin); r=os.environ["GITHUB_REPOSITORY"]; print(d["repos"].get(r) or d["orgs"].get(r.split("/")[0]) or d["default"])')
|
|
86
|
+
echo "release authority for $GITHUB_REPOSITORY: $auth"
|
|
87
|
+
echo "authority=$auth" >> "$GITHUB_OUTPUT"
|
|
88
|
+
|
|
89
|
+
# ---------------------------------------------------------------- test ----
|
|
90
|
+
test:
|
|
91
|
+
name: Test
|
|
92
|
+
if: github.event_name != 'schedule'
|
|
93
|
+
runs-on: ubuntu-latest
|
|
94
|
+
steps:
|
|
95
|
+
- uses: actions/checkout@v4
|
|
96
|
+
with:
|
|
97
|
+
# setuptools-scm needs the history, and so does the suite: every
|
|
98
|
+
# fixture builds a real repository.
|
|
99
|
+
fetch-depth: 0
|
|
100
|
+
- name: Python
|
|
101
|
+
run: |
|
|
102
|
+
python3 -m venv /tmp/venv
|
|
103
|
+
/tmp/venv/bin/pip install -q --upgrade pip
|
|
104
|
+
echo "/tmp/venv/bin" >> "$GITHUB_PATH"
|
|
105
|
+
- name: Install
|
|
106
|
+
run: pip install -e '.[dev]'
|
|
107
|
+
- name: doctor
|
|
108
|
+
# R6.2: doctor runs first so an environment failure is distinguishable
|
|
109
|
+
# from a content failure at a glance.
|
|
110
|
+
run: labtrail doctor
|
|
111
|
+
- name: Unit and integration tests
|
|
112
|
+
run: pytest tests/unit tests/integration -m 'not slow and not external' -q
|
|
113
|
+
- name: Acceptance tests
|
|
114
|
+
run: pytest tests/acceptance -m 'not external' -q
|
|
115
|
+
- uses: forgejo/upload-artifact@v4
|
|
116
|
+
if: always()
|
|
117
|
+
with:
|
|
118
|
+
name: ctrf
|
|
119
|
+
path: build/ctrf/
|
|
120
|
+
if-no-files-found: ignore
|
|
121
|
+
|
|
122
|
+
# --------------------------------------------------------- skill-sync ----
|
|
123
|
+
skill-sync:
|
|
124
|
+
# R3.3/D-7: the four agent skills are generated from the prose in docs/.
|
|
125
|
+
# Two copies of a convention drift, and the drift is invisible until an
|
|
126
|
+
# agent confidently follows the stale one. Regenerate and diff.
|
|
127
|
+
name: Skills match the prose
|
|
128
|
+
if: github.event_name != 'schedule'
|
|
129
|
+
runs-on: ubuntu-latest
|
|
130
|
+
steps:
|
|
131
|
+
- uses: actions/checkout@v4
|
|
132
|
+
with:
|
|
133
|
+
fetch-depth: 0
|
|
134
|
+
- name: Python
|
|
135
|
+
run: |
|
|
136
|
+
python3 -m venv /tmp/venv
|
|
137
|
+
/tmp/venv/bin/pip install -q --upgrade pip
|
|
138
|
+
echo "/tmp/venv/bin" >> "$GITHUB_PATH"
|
|
139
|
+
- run: pip install -e '.[dev]'
|
|
140
|
+
- run: labtrail-dev gen-skill
|
|
141
|
+
- run: git diff --exit-code -- src/labtrail/share/skills/
|
|
142
|
+
|
|
143
|
+
# ---------------------------------------------------- docs-conventions ----
|
|
144
|
+
docs-conventions:
|
|
145
|
+
# R2.22.1/R2.22.3: every documented start-point fully qualified, every
|
|
146
|
+
# worktree add carrying -b. Cheap grep; the failure mode is silent -- a
|
|
147
|
+
# documented command that quietly puts a learner on a published branch.
|
|
148
|
+
name: Documented git spellings
|
|
149
|
+
if: github.event_name != 'schedule'
|
|
150
|
+
runs-on: ubuntu-latest
|
|
151
|
+
steps:
|
|
152
|
+
- uses: actions/checkout@v4
|
|
153
|
+
with:
|
|
154
|
+
fetch-depth: 0
|
|
155
|
+
- name: Python
|
|
156
|
+
run: |
|
|
157
|
+
python3 -m venv /tmp/venv
|
|
158
|
+
/tmp/venv/bin/pip install -q --upgrade pip
|
|
159
|
+
echo "/tmp/venv/bin" >> "$GITHUB_PATH"
|
|
160
|
+
- run: pip install -e '.[dev]'
|
|
161
|
+
- run: pytest tests/unit/test_doc_conventions.py -q
|
|
162
|
+
|
|
163
|
+
# ---------------------------------------------------------------- docs ----
|
|
164
|
+
docs:
|
|
165
|
+
name: Documentation
|
|
166
|
+
if: github.event_name != 'schedule'
|
|
167
|
+
runs-on: ubuntu-latest
|
|
168
|
+
steps:
|
|
169
|
+
- uses: actions/checkout@v4
|
|
170
|
+
with:
|
|
171
|
+
fetch-depth: 0
|
|
172
|
+
- name: Python
|
|
173
|
+
run: |
|
|
174
|
+
python3 -m venv /tmp/venv
|
|
175
|
+
/tmp/venv/bin/pip install -q --upgrade pip
|
|
176
|
+
echo "/tmp/venv/bin" >> "$GITHUB_PATH"
|
|
177
|
+
- run: pip install -e '.[dev]' sphinx myst-parser
|
|
178
|
+
# -W so a broken reference fails the build. graphviz is *configured* in
|
|
179
|
+
# conf.py for sphinx-labtrail (M9) but no page in this repo uses a
|
|
180
|
+
# directive, so the binary is not installed here.
|
|
181
|
+
- run: sphinx-build -W -b html docs docs/_build/html
|
|
182
|
+
- uses: forgejo/upload-artifact@v4
|
|
183
|
+
with:
|
|
184
|
+
name: docs
|
|
185
|
+
path: docs/_build/html
|
|
186
|
+
|
|
187
|
+
# --------------------------------------------------------------- build ----
|
|
188
|
+
build:
|
|
189
|
+
name: Build and check the distribution
|
|
190
|
+
runs-on: ubuntu-latest
|
|
191
|
+
steps:
|
|
192
|
+
- uses: actions/checkout@v4
|
|
193
|
+
with:
|
|
194
|
+
fetch-depth: 0
|
|
195
|
+
|
|
196
|
+
- name: Check for internal identifiers in tracked files
|
|
197
|
+
# This repo is published to git.dvkit.org, so anything committed here
|
|
198
|
+
# is public. Internal host names, tailnet addresses and CGNAT-range IPs
|
|
199
|
+
# must not appear in tracked files -- if a build ever needs one, it
|
|
200
|
+
# belongs in a repository secret, which is masked in logs. This applies
|
|
201
|
+
# to documentation and comments, not just code.
|
|
202
|
+
run: |
|
|
203
|
+
pattern='perch-char\.ts\.net|\.ts\.net|a9550|100\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])\.[0-9]+\.[0-9]+'
|
|
204
|
+
if git grep -nIE "$pattern" -- . ':!.forgejo/workflows/ci.yml' ; then
|
|
205
|
+
echo "::error::internal identifier found in a tracked file (see matches above)"
|
|
206
|
+
exit 1
|
|
207
|
+
fi
|
|
208
|
+
echo "OK: no internal identifiers in tracked files"
|
|
209
|
+
|
|
210
|
+
- name: Python
|
|
211
|
+
run: |
|
|
212
|
+
python3 -m venv /tmp/venv
|
|
213
|
+
/tmp/venv/bin/pip install -q --upgrade pip
|
|
214
|
+
echo "/tmp/venv/bin" >> "$GITHUB_PATH"
|
|
215
|
+
|
|
216
|
+
- name: Stamp the build version
|
|
217
|
+
# Tag builds keep the clean number. EVERYTHING ELSE gets a PEP 440
|
|
218
|
+
# local version segment, which PyPI rejects outright -- so an
|
|
219
|
+
# accidental upload of a CI artifact fails at the tool rather than
|
|
220
|
+
# succeeding quietly.
|
|
221
|
+
#
|
|
222
|
+
# 0.1.dev4+fj.r12.g7775f37
|
|
223
|
+
# └─ WHICH FORGE. $GITHUB_RUN_ID is not unique across them:
|
|
224
|
+
# GitHub's is an 11-digit global counter, Forgejo's a
|
|
225
|
+
# per-repo index that restarts if the instance is
|
|
226
|
+
# rebuilt. The run id alone collides.
|
|
227
|
+
#
|
|
228
|
+
# DIVERGENCE FROM THE TEMPLATE: there is no version file to sed.
|
|
229
|
+
# setuptools-scm computes the public version from the tag history, and
|
|
230
|
+
# we append only the local segment. That is the safer direction --
|
|
231
|
+
# the base cannot disagree with the tag, because it is derived from it.
|
|
232
|
+
run: |
|
|
233
|
+
pip install --upgrade build twine setuptools-scm
|
|
234
|
+
case "$GITHUB_REF" in
|
|
235
|
+
refs/tags/*)
|
|
236
|
+
echo "tag build -- setuptools-scm will use the tag verbatim"
|
|
237
|
+
;;
|
|
238
|
+
*)
|
|
239
|
+
# `python -m setuptools_scm` reads THIS project's configuration,
|
|
240
|
+
# so the base agrees with what the build itself will compute.
|
|
241
|
+
# A hand-rolled get_version() call does not: without pyproject's
|
|
242
|
+
# fallback_version it answers 0.1.dev1 where the build answers
|
|
243
|
+
# 0.0.1.dev1. Everything from '+' is the local segment setuptools-scm
|
|
244
|
+
# would have added; we replace it with ours.
|
|
245
|
+
public=$(python -m setuptools_scm | sed 's/+.*//')
|
|
246
|
+
stamp="${public}+fj.r${GITHUB_RUN_ID}.g$(git rev-parse --short=7 HEAD)"
|
|
247
|
+
echo "stamping $stamp"
|
|
248
|
+
echo "SETUPTOOLS_SCM_PRETEND_VERSION=$stamp" >> "$GITHUB_ENV"
|
|
249
|
+
;;
|
|
250
|
+
esac
|
|
251
|
+
|
|
252
|
+
- name: Build sdist and wheel
|
|
253
|
+
run: |
|
|
254
|
+
rm -rf dist/
|
|
255
|
+
python -m build
|
|
256
|
+
twine check dist/*
|
|
257
|
+
ls -la dist/
|
|
258
|
+
|
|
259
|
+
- name: Smoke-test the wheel
|
|
260
|
+
# Against the built wheel in a clean venv, not the source tree: the
|
|
261
|
+
# repo suite puts src/ on sys.path, so it can never tell you whether
|
|
262
|
+
# what SHIPS works. R8.5 (the entry points) and R3.6 (the skills are
|
|
263
|
+
# inside the package) are exactly the things a packaging mistake
|
|
264
|
+
# breaks and no source-tree test would notice.
|
|
265
|
+
run: |
|
|
266
|
+
python3 -m venv /tmp/wheelenv
|
|
267
|
+
/tmp/wheelenv/bin/pip install -q --upgrade pip
|
|
268
|
+
/tmp/wheelenv/bin/pip install -q "$(ls dist/*.whl)"
|
|
269
|
+
cd /tmp # out of the source tree, so nothing shadows the wheel
|
|
270
|
+
/tmp/wheelenv/bin/labtrail --version
|
|
271
|
+
/tmp/wheelenv/bin/labtrail --help > /dev/null
|
|
272
|
+
/tmp/wheelenv/bin/labtrail-dev --help > /dev/null
|
|
273
|
+
/tmp/wheelenv/bin/python -c '
|
|
274
|
+
import os
|
|
275
|
+
from importlib.metadata import entry_points
|
|
276
|
+
eps = {e.name: e for e in entry_points(group="agent.skills")}
|
|
277
|
+
assert eps, "no agent.skills entry points in the installed wheel"
|
|
278
|
+
for name, ep in sorted(eps.items()):
|
|
279
|
+
(d,) = ep.load()()
|
|
280
|
+
assert os.path.basename(d) == name, (name, d)
|
|
281
|
+
assert os.path.isfile(os.path.join(d, "SKILL.md")), d
|
|
282
|
+
print("skill ok:", name)
|
|
283
|
+
'
|
|
284
|
+
|
|
285
|
+
- uses: forgejo/upload-artifact@v4
|
|
286
|
+
with:
|
|
287
|
+
name: dist-labtrail
|
|
288
|
+
path: dist/*
|
|
289
|
+
if-no-files-found: error
|
|
290
|
+
|
|
291
|
+
# -------------------------------------------------- forward-integration ----
|
|
292
|
+
forward-integration:
|
|
293
|
+
# R4.25: subtree propagation on a proposed change. A revision can rebase
|
|
294
|
+
# through every descendant with no conflict at all and leave one of them no
|
|
295
|
+
# longer testing anything; only re-running the invariant at each hop finds
|
|
296
|
+
# it.
|
|
297
|
+
name: Forward integration
|
|
298
|
+
if: github.event_name == 'pull_request'
|
|
299
|
+
runs-on: ubuntu-latest
|
|
300
|
+
steps:
|
|
301
|
+
- uses: actions/checkout@v4
|
|
302
|
+
with:
|
|
303
|
+
fetch-depth: 0
|
|
304
|
+
- name: Python
|
|
305
|
+
run: |
|
|
306
|
+
python3 -m venv /tmp/venv
|
|
307
|
+
/tmp/venv/bin/pip install -q --upgrade pip
|
|
308
|
+
echo "/tmp/venv/bin" >> "$GITHUB_PATH"
|
|
309
|
+
- run: pip install -e '.[dev]'
|
|
310
|
+
- run: pytest tests/integration/test_propagate.py -q
|
|
311
|
+
|
|
312
|
+
# ------------------------------------------------------------- nightly ----
|
|
313
|
+
nightly:
|
|
314
|
+
name: Nightly
|
|
315
|
+
if: github.event_name == 'schedule'
|
|
316
|
+
runs-on: ubuntu-latest
|
|
317
|
+
steps:
|
|
318
|
+
- uses: actions/checkout@v4
|
|
319
|
+
with:
|
|
320
|
+
fetch-depth: 0
|
|
321
|
+
- name: Python
|
|
322
|
+
run: |
|
|
323
|
+
python3 -m venv /tmp/venv
|
|
324
|
+
/tmp/venv/bin/pip install -q --upgrade pip
|
|
325
|
+
echo "/tmp/venv/bin" >> "$GITHUB_PATH"
|
|
326
|
+
- run: pip install -e '.[dev]'
|
|
327
|
+
- run: pytest -q -m 'slow or external or not slow'
|
|
328
|
+
|
|
329
|
+
# ------------------------------------------------------------- publish ----
|
|
330
|
+
publish:
|
|
331
|
+
name: Publish to PyPI
|
|
332
|
+
# A TAG, and the switch naming THIS forge. A push to a branch never
|
|
333
|
+
# releases anything -- that is what makes the 5-minute sync safe.
|
|
334
|
+
#
|
|
335
|
+
# SKIPS -- does not fail -- when the switch names GitHub.
|
|
336
|
+
if: startsWith(github.ref, 'refs/tags/v') && needs.gate.outputs.authority == 'forgejo'
|
|
337
|
+
needs: [gate, build, test]
|
|
338
|
+
runs-on: ubuntu-latest
|
|
339
|
+
steps:
|
|
340
|
+
- uses: actions/checkout@v4
|
|
341
|
+
with:
|
|
342
|
+
fetch-depth: 0
|
|
343
|
+
|
|
344
|
+
- uses: forgejo/download-artifact@v4
|
|
345
|
+
with:
|
|
346
|
+
name: dist-labtrail
|
|
347
|
+
path: dist
|
|
348
|
+
|
|
349
|
+
- name: Check the tag IS the version
|
|
350
|
+
# The template checks the tag against a version file and then the
|
|
351
|
+
# artifacts against that file. setuptools-scm removes the first half:
|
|
352
|
+
# the source version IS the tag, by construction, so there is no file
|
|
353
|
+
# that can disagree with it.
|
|
354
|
+
#
|
|
355
|
+
# The second half is not automatic and is what this step keeps. An
|
|
356
|
+
# artifact built from a shallow clone, from a dirty tree, or left over
|
|
357
|
+
# in dist/ from an earlier build carries a different version -- and a
|
|
358
|
+
# dirty tree in particular produces a LOCAL segment (`+d20260913`),
|
|
359
|
+
# which is precisely what must never reach PyPI.
|
|
360
|
+
run: |
|
|
361
|
+
tag="${GITHUB_REF#refs/tags/v}"
|
|
362
|
+
echo "tag=$tag"
|
|
363
|
+
for f in dist/*; do
|
|
364
|
+
case "$(basename "$f")" in
|
|
365
|
+
labtrail-${tag}-py3-none-any.whl|labtrail-${tag}.tar.gz) ;;
|
|
366
|
+
*) echo "::error::artifact $(basename "$f") is not version ${tag}"; exit 1 ;;
|
|
367
|
+
esac
|
|
368
|
+
done
|
|
369
|
+
echo "OK: the tag and every artifact agree on ${tag}"
|
|
370
|
+
|
|
371
|
+
- name: Publish
|
|
372
|
+
env:
|
|
373
|
+
TWINE_USERNAME: __token__
|
|
374
|
+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
|
|
375
|
+
run: |
|
|
376
|
+
if [ -z "$TWINE_PASSWORD" ]; then
|
|
377
|
+
echo "::error::PYPI_API_TOKEN is not visible to this repository"
|
|
378
|
+
exit 1
|
|
379
|
+
fi
|
|
380
|
+
python3 -m venv /tmp/twineenv
|
|
381
|
+
/tmp/twineenv/bin/pip install -q --upgrade pip twine
|
|
382
|
+
/tmp/twineenv/bin/twine check dist/*
|
|
383
|
+
# Idempotence net: with one switch and two possible publishers, a
|
|
384
|
+
# double-fire must be a no-op rather than a failure or a corruption.
|
|
385
|
+
/tmp/twineenv/bin/twine upload --non-interactive --skip-existing dist/*
|