coho-cli 2026.1.0a21__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.
- coho_cli-2026.1.0a21/.github/workflows/ci.yml +198 -0
- coho_cli-2026.1.0a21/.gitignore +226 -0
- coho_cli-2026.1.0a21/CHANGELOG.md +146 -0
- coho_cli-2026.1.0a21/CONTRIBUTING.md +95 -0
- coho_cli-2026.1.0a21/LICENSE +201 -0
- coho_cli-2026.1.0a21/Makefile +30 -0
- coho_cli-2026.1.0a21/PKG-INFO +96 -0
- coho_cli-2026.1.0a21/README.md +66 -0
- coho_cli-2026.1.0a21/SECURITY.md +27 -0
- coho_cli-2026.1.0a21/docs/authentication.md +113 -0
- coho_cli-2026.1.0a21/docs/ci.md +82 -0
- coho_cli-2026.1.0a21/docs/commands/account.md +27 -0
- coho_cli-2026.1.0a21/docs/commands/asset.md +64 -0
- coho_cli-2026.1.0a21/docs/commands/auth.md +70 -0
- coho_cli-2026.1.0a21/docs/commands/branch.md +32 -0
- coho_cli-2026.1.0a21/docs/commands/context.md +33 -0
- coho_cli-2026.1.0a21/docs/commands/diff.md +47 -0
- coho_cli-2026.1.0a21/docs/commands/entry.md +89 -0
- coho_cli-2026.1.0a21/docs/commands/env.md +64 -0
- coho_cli-2026.1.0a21/docs/commands/export.md +25 -0
- coho_cli-2026.1.0a21/docs/commands/index.md +41 -0
- coho_cli-2026.1.0a21/docs/commands/invite.md +48 -0
- coho_cli-2026.1.0a21/docs/commands/key.md +44 -0
- coho_cli-2026.1.0a21/docs/commands/member.md +21 -0
- coho_cli-2026.1.0a21/docs/commands/merge.md +62 -0
- coho_cli-2026.1.0a21/docs/commands/preview.md +24 -0
- coho_cli-2026.1.0a21/docs/commands/project.md +73 -0
- coho_cli-2026.1.0a21/docs/commands/ref.md +47 -0
- coho_cli-2026.1.0a21/docs/commands/role.md +41 -0
- coho_cli-2026.1.0a21/docs/commands/tag.md +64 -0
- coho_cli-2026.1.0a21/docs/commands/tier.md +34 -0
- coho_cli-2026.1.0a21/docs/commands/token.md +24 -0
- coho_cli-2026.1.0a21/docs/commands/trait.md +35 -0
- coho_cli-2026.1.0a21/docs/commands/type.md +49 -0
- coho_cli-2026.1.0a21/docs/commands/user.md +12 -0
- coho_cli-2026.1.0a21/docs/concepts.md +120 -0
- coho_cli-2026.1.0a21/docs/configuration.md +119 -0
- coho_cli-2026.1.0a21/docs/content-model.md +146 -0
- coho_cli-2026.1.0a21/docs/errors.md +101 -0
- coho_cli-2026.1.0a21/docs/index.md +32 -0
- coho_cli-2026.1.0a21/docs/install.md +60 -0
- coho_cli-2026.1.0a21/docs/quickstart.md +170 -0
- coho_cli-2026.1.0a21/pyproject.toml +101 -0
- coho_cli-2026.1.0a21/src/coho_cli/__init__.py +8 -0
- coho_cli-2026.1.0a21/src/coho_cli/_entry_ids.py +164 -0
- coho_cli-2026.1.0a21/src/coho_cli/_io.py +55 -0
- coho_cli-2026.1.0a21/src/coho_cli/_output.py +103 -0
- coho_cli-2026.1.0a21/src/coho_cli/_slugs.py +59 -0
- coho_cli-2026.1.0a21/src/coho_cli/_state.py +205 -0
- coho_cli-2026.1.0a21/src/coho_cli/_usage.py +367 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/__init__.py +1 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/account.py +159 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/asset.py +325 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/auth.py +385 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/branch.py +74 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/context.py +119 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/diffmerge.py +159 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/entry.py +297 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/env.py +188 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/export.py +65 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/invite.py +135 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/key.py +92 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/member.py +79 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/preview.py +70 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/project.py +157 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/ref.py +81 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/role.py +95 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/tag.py +76 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/tier.py +87 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/token.py +81 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/trait.py +150 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/type_.py +159 -0
- coho_cli-2026.1.0a21/src/coho_cli/commands/user.py +50 -0
- coho_cli-2026.1.0a21/src/coho_cli/main.py +304 -0
- coho_cli-2026.1.0a21/src/coho_cli/py.typed +0 -0
- coho_cli-2026.1.0a21/tests/conftest.py +70 -0
- coho_cli-2026.1.0a21/tests/test_asset.py +166 -0
- coho_cli-2026.1.0a21/tests/test_cli.py +770 -0
- coho_cli-2026.1.0a21/tests/test_entry_ids.py +373 -0
- coho_cli-2026.1.0a21/tests/test_slug_generation.py +127 -0
- coho_cli-2026.1.0a21/tests/test_usage.py +79 -0
- coho_cli-2026.1.0a21/uv.lock +913 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
tags: ["v*"]
|
|
7
|
+
pull_request:
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: ci-${{ github.ref }}
|
|
12
|
+
cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
env:
|
|
18
|
+
FORCE_COLOR: "1"
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
test:
|
|
22
|
+
name: test · ${{ matrix.os }} · py${{ matrix.python }}
|
|
23
|
+
runs-on: ${{ matrix.os }}
|
|
24
|
+
strategy:
|
|
25
|
+
fail-fast: false
|
|
26
|
+
matrix:
|
|
27
|
+
os: [ubuntu-latest, macos-latest]
|
|
28
|
+
python: ["3.11", "3.12", "3.13"]
|
|
29
|
+
include:
|
|
30
|
+
- os: windows-latest
|
|
31
|
+
python: "3.13"
|
|
32
|
+
defaults:
|
|
33
|
+
run:
|
|
34
|
+
working-directory: coho-cli
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v7
|
|
37
|
+
with:
|
|
38
|
+
path: coho-cli
|
|
39
|
+
# Every commit and tag: the version is computed from them, and a shallow
|
|
40
|
+
# clone makes it a meaningless 0.1.dev1.
|
|
41
|
+
fetch-depth: 0
|
|
42
|
+
|
|
43
|
+
# The CLI develops against a sibling checkout of the library, which is what
|
|
44
|
+
# `[tool.uv.sources]` in pyproject.toml points at. A public repository needs no
|
|
45
|
+
# extra credential; a private one would need a PAT in `token:`.
|
|
46
|
+
- uses: actions/checkout@v7
|
|
47
|
+
with:
|
|
48
|
+
repository: coho-cms/coho-management-sdk-python
|
|
49
|
+
path: coho-management-sdk-python
|
|
50
|
+
# The library's release branch, not its default branch: this CLI needs
|
|
51
|
+
# the 2026.1 line, which lives there. Spelled out as a branch because the
|
|
52
|
+
# release tag v2026.1.0 will share its name.
|
|
53
|
+
ref: refs/heads/v2026.1.0
|
|
54
|
+
# The library's version comes from its tags. A shallow clone has none and
|
|
55
|
+
# builds 0.1.dev1, which the version range above refuses.
|
|
56
|
+
fetch-depth: 0
|
|
57
|
+
|
|
58
|
+
- name: Install uv
|
|
59
|
+
# An exact release: setup-uv stopped publishing floating major tags at v8,
|
|
60
|
+
# so `@v10` would not resolve.
|
|
61
|
+
uses: astral-sh/setup-uv@v10.2.0
|
|
62
|
+
with:
|
|
63
|
+
python-version: ${{ matrix.python }} # without this every job used the same Python
|
|
64
|
+
enable-cache: true
|
|
65
|
+
cache-dependency-glob: coho-cli/uv.lock
|
|
66
|
+
|
|
67
|
+
- name: Install the project
|
|
68
|
+
run: uv sync --locked
|
|
69
|
+
|
|
70
|
+
- name: Lint
|
|
71
|
+
run: uv run ruff check .
|
|
72
|
+
|
|
73
|
+
- name: Check formatting
|
|
74
|
+
run: uv run ruff format --check .
|
|
75
|
+
|
|
76
|
+
- name: Type check
|
|
77
|
+
run: uv run mypy
|
|
78
|
+
|
|
79
|
+
- name: Test
|
|
80
|
+
run: uv run pytest
|
|
81
|
+
|
|
82
|
+
build:
|
|
83
|
+
name: build distributions
|
|
84
|
+
needs: test
|
|
85
|
+
runs-on: ubuntu-latest
|
|
86
|
+
steps:
|
|
87
|
+
- uses: actions/checkout@v7
|
|
88
|
+
with:
|
|
89
|
+
# Every commit and tag: the version is computed from them, and a shallow
|
|
90
|
+
# clone makes it a meaningless 0.1.dev1.
|
|
91
|
+
fetch-depth: 0
|
|
92
|
+
|
|
93
|
+
- name: Install uv
|
|
94
|
+
# An exact release: setup-uv stopped publishing floating major tags at v8,
|
|
95
|
+
# so `@v10` would not resolve.
|
|
96
|
+
uses: astral-sh/setup-uv@v10.2.0
|
|
97
|
+
with:
|
|
98
|
+
enable-cache: true
|
|
99
|
+
cache-dependency-glob: uv.lock
|
|
100
|
+
|
|
101
|
+
- name: Refuse a dev-version tag
|
|
102
|
+
# Dev versions come from commits after a release (2026.1.0a2.dev3), never
|
|
103
|
+
# from tags. A tag like v2026.1.0.dev1 also makes every later build fail,
|
|
104
|
+
# because setuptools-scm cannot number a dev build of a dev build.
|
|
105
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
106
|
+
run: |
|
|
107
|
+
if [[ "$GITHUB_REF_NAME" =~ [.-]?dev[0-9]*$ ]]; then
|
|
108
|
+
echo "::error::Don't tag dev versions: every commit after a release already builds one."
|
|
109
|
+
echo "Delete this tag (git push --delete origin $GITHUB_REF_NAME), or every later build fails."
|
|
110
|
+
exit 1
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
# Build metadata comes from [project], never from [tool.uv.sources], so the
|
|
114
|
+
# wheel depends on the published coho-management-sdk even though development does not.
|
|
115
|
+
- name: Build the wheel and the sdist
|
|
116
|
+
run: uv build --out-dir dist
|
|
117
|
+
|
|
118
|
+
- name: Refuse a tag that is not a version in its standard form
|
|
119
|
+
# The version comes from the tag, but normalized to PEP 440: `v2026.1.0-rc.1`
|
|
120
|
+
# builds 2026.1.0rc1. A tag that does not match its own build would mislead
|
|
121
|
+
# anyone comparing the two, so the wheel's filename is the check.
|
|
122
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
123
|
+
run: |
|
|
124
|
+
expected="dist/coho_cli-${GITHUB_REF_NAME#v}-py3-none-any.whl"
|
|
125
|
+
if [ ! -f "$expected" ]; then
|
|
126
|
+
echo "::error::tag $GITHUB_REF_NAME is not a version in PEP 440's standard form."
|
|
127
|
+
echo "Use forms like v2026.1.0, v2026.1.0a1, v2026.1.0b1 or v2026.1.0rc1. Built instead:"
|
|
128
|
+
ls -1 dist
|
|
129
|
+
exit 1
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
- name: Check the packaging metadata
|
|
133
|
+
run: uvx twine check --strict dist/*.whl dist/*.tar.gz
|
|
134
|
+
|
|
135
|
+
- name: Upload the distributions
|
|
136
|
+
uses: actions/upload-artifact@v7
|
|
137
|
+
with:
|
|
138
|
+
name: coho-cli-dist
|
|
139
|
+
path: |
|
|
140
|
+
dist/*.whl
|
|
141
|
+
dist/*.tar.gz
|
|
142
|
+
if-no-files-found: error
|
|
143
|
+
retention-days: 90
|
|
144
|
+
|
|
145
|
+
release:
|
|
146
|
+
name: attach to the GitHub release
|
|
147
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
148
|
+
needs: build
|
|
149
|
+
runs-on: ubuntu-latest
|
|
150
|
+
permissions:
|
|
151
|
+
contents: write
|
|
152
|
+
steps:
|
|
153
|
+
- name: Download the distributions
|
|
154
|
+
uses: actions/download-artifact@v8
|
|
155
|
+
with:
|
|
156
|
+
name: coho-cli-dist
|
|
157
|
+
path: dist
|
|
158
|
+
|
|
159
|
+
- name: Create or update the release
|
|
160
|
+
env:
|
|
161
|
+
GH_TOKEN: ${{ github.token }}
|
|
162
|
+
run: |
|
|
163
|
+
# An alpha, beta or release candidate is a pre-release on GitHub too, so
|
|
164
|
+
# it never shows as the latest release.
|
|
165
|
+
prerelease=""
|
|
166
|
+
if [[ "$GITHUB_REF_NAME" =~ (a|b|rc)[0-9]+$ ]]; then
|
|
167
|
+
prerelease="--prerelease"
|
|
168
|
+
fi
|
|
169
|
+
if gh release view "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
|
|
170
|
+
gh release upload "$GITHUB_REF_NAME" dist/*.whl dist/*.tar.gz --repo "$GITHUB_REPOSITORY" --clobber
|
|
171
|
+
else
|
|
172
|
+
gh release create "$GITHUB_REF_NAME" dist/*.whl dist/*.tar.gz \
|
|
173
|
+
--repo "$GITHUB_REPOSITORY" \
|
|
174
|
+
--title "coho-cli $GITHUB_REF_NAME" \
|
|
175
|
+
--generate-notes \
|
|
176
|
+
--verify-tag $prerelease
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
publish-pypi:
|
|
180
|
+
name: publish to PyPI
|
|
181
|
+
# Skipped unless the repository variable PUBLISH_TO_PYPI is "true". See CONTRIBUTING.md.
|
|
182
|
+
if: startsWith(github.ref, 'refs/tags/v') && vars.PUBLISH_TO_PYPI == 'true'
|
|
183
|
+
needs: build
|
|
184
|
+
runs-on: ubuntu-latest
|
|
185
|
+
environment:
|
|
186
|
+
name: pypi
|
|
187
|
+
url: https://pypi.org/project/coho-cli/
|
|
188
|
+
permissions:
|
|
189
|
+
id-token: write
|
|
190
|
+
steps:
|
|
191
|
+
- name: Download the distributions
|
|
192
|
+
uses: actions/download-artifact@v8
|
|
193
|
+
with:
|
|
194
|
+
name: coho-cli-dist
|
|
195
|
+
path: dist
|
|
196
|
+
|
|
197
|
+
- name: Publish
|
|
198
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
# .idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
# Temporary file for partial code execution
|
|
204
|
+
tempCodeRunnerFile.py
|
|
205
|
+
|
|
206
|
+
# Ruff stuff:
|
|
207
|
+
.ruff_cache/
|
|
208
|
+
|
|
209
|
+
# PyPI configuration file
|
|
210
|
+
.pypirc
|
|
211
|
+
|
|
212
|
+
# Marimo
|
|
213
|
+
marimo/_static/
|
|
214
|
+
marimo/_lsp/
|
|
215
|
+
__marimo__/
|
|
216
|
+
|
|
217
|
+
# Streamlit
|
|
218
|
+
.streamlit/secrets.toml
|
|
219
|
+
|
|
220
|
+
# coho-cli
|
|
221
|
+
.venv/
|
|
222
|
+
.mypy_cache/
|
|
223
|
+
.ruff_cache/
|
|
224
|
+
.pytest_cache/
|
|
225
|
+
*.ndjson
|
|
226
|
+
*.tar
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `coho-cli`. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project uses
|
|
5
|
+
[semantic versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `coho tier move ID --ord N` (coho-data doc 11, M6-15): moves a tier in the pipeline, and
|
|
12
|
+
the tiers it passes shift by one. Needs coho-management-sdk with `tiers.move`.
|
|
13
|
+
- `coho trait list`, `get`, `put` and `delete` (coho-data doc 05 §10): traits, named groups
|
|
14
|
+
of fields that types take by naming them in `traits`. `trait list` shows the types that
|
|
15
|
+
take each. A refused trait or type change now prints what it reaches, `reaches: page,
|
|
16
|
+
servicePage, and 12 entries`, and a trait in use is refused with the types that take it.
|
|
17
|
+
- `coho type get` shows the type it extends, the traits it takes, and whether it is
|
|
18
|
+
abstract (coho-data doc 05 §9).
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- `coho tier create --ord N` inserts: a position another tier holds moves that tier and the
|
|
23
|
+
ones after it along, where it was refused with `TIER_ORDER_TAKEN`, and anything past the
|
|
24
|
+
end is the end (coho-data doc 11, M6-15). `coho tier delete` closes the gap it leaves.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- An `@slug` in a field a type inherits, or takes from a trait, is written as the id it
|
|
29
|
+
names: `coho entry create -t photoGallery --set author=@tom-okafor` stored the text
|
|
30
|
+
`@tom-okafor`, because the slug was looked up only in the fields the type declares itself.
|
|
31
|
+
- Component types (coho-data doc 40 §14.10): `coho type put` takes a definition with
|
|
32
|
+
`"kind": "component"`, and `type list`, `get` and `put` show each type's `KIND`. A
|
|
33
|
+
repeater takes its items as a JSON list, `--set 'citations=[{…}]'`, and the server
|
|
34
|
+
assigns each item's `_id` and, where the repeater holds one component, its `_type`.
|
|
35
|
+
Needs coho-management-sdk with `ContentType.kind` and a server with component types.
|
|
36
|
+
|
|
37
|
+
- `coho project usage [PROJECT]` and `coho account usage`: metered usage (coho-data doc
|
|
38
|
+
41), as a table or, with `-o json`, the report exactly as the server sent it. A
|
|
39
|
+
project's shows delivery by key with requests per distinct URL (the client that is not
|
|
40
|
+
caching), management by people and project tokens and by class, content, history and
|
|
41
|
+
assets; anyone who can read the project may, project tokens included. The account's
|
|
42
|
+
sums every project and lists each one's totals, for admins: a member's `FORBIDDEN`
|
|
43
|
+
says to use `coho project usage` instead. Both take `--grain hour|day`, `--from`,
|
|
44
|
+
`--to`, `--meter` (repeatable) and `--periods` for a line per hour or day. Needs
|
|
45
|
+
coho-management-sdk with `usage()` and a server with the usage routes.
|
|
46
|
+
|
|
47
|
+
- `coho preview entries` ends the table with `more: --offset N` when another page
|
|
48
|
+
follows, as `entry list` does. It has no `--all`, because each live page resolves the
|
|
49
|
+
whole ref.
|
|
50
|
+
- `@SLUG` wherever a command takes an entry id: `entry get`, `put`, `delete`, `history`,
|
|
51
|
+
`references` and `preview entry`. The slug is looked up on the same ref with one
|
|
52
|
+
request (the BFF's live `preview/{ref}/entries-by-slug/{slug}`), then the command runs
|
|
53
|
+
as if the id had been typed; a plain id is used as given. A slug nothing has is
|
|
54
|
+
`ENTRY_NOT_FOUND` (exit 5); one that names a type or an asset, or `@` before something
|
|
55
|
+
that is not a slug, is a usage error. `entry delete @SLUG` asks about both the slug and
|
|
56
|
+
the id it found. `entry list` and `entry get` show the slug from `_slug` while the
|
|
57
|
+
server's entry responses leave out `slug`.
|
|
58
|
+
- `@SLUG` in a reference field's `--set` value, on `entry create` and `entry put`:
|
|
59
|
+
`--set author=@priya-shah --set 'tags=["@traffic"]'` writes the ids those slugs name
|
|
60
|
+
on the same ref. Only fields the content type declares as references are looked up, so
|
|
61
|
+
text keeps a leading `@`; the type is read only when a value starts with `@`.
|
|
62
|
+
- `@SLUG` in an asset field's `--set` value too: `--set image=@culvert` writes the id of
|
|
63
|
+
the asset with that slug, from one listing of the ref's assets. A slug no asset has
|
|
64
|
+
is `ASSET_NOT_FOUND` (exit 5).
|
|
65
|
+
- `coho ref switch NAME`: makes a branch, tag or environment of the current project the
|
|
66
|
+
current ref, after checking it exists, keeping the account and project. It and
|
|
67
|
+
`coho branch create` warn when `--ref` or `COHO_REF` still overrides the saved ref.
|
|
68
|
+
- `coho asset`: files on the current ref (coho-data doc 40). `asset upload FILE…` hashes
|
|
69
|
+
each file, sends it straight to object storage on a presigned URL and writes it as an
|
|
70
|
+
asset, building the slug from the filename (`Hero Shot.jpg` → `hero-shot`) unless
|
|
71
|
+
`--slug` is given for a single file; `--title`, `--alt`, `--focal x,y` and `--tag` set
|
|
72
|
+
its metadata. Bytes the project already holds are not sent again. Also `asset list`,
|
|
73
|
+
`get`, `url` (prints the original, or a resized image URL with `--width`, `--height`,
|
|
74
|
+
`--fit`, `--format`, `--quality`, centring fills on the focal point), `put` and
|
|
75
|
+
`delete`, each taking an id or a slug. Upload warns on stderr that asset URLs are
|
|
76
|
+
public to anyone holding them. Needs the `coho-management-sdk` that adds `ref.assets`,
|
|
77
|
+
and a server with the asset routes.
|
|
78
|
+
- On PyPI: `uv tool install coho-cli` or `pipx install coho-cli`.
|
|
79
|
+
- `coho login` finishes setting up an identity that has no organization yet: it opens the
|
|
80
|
+
BFF's sign-in in the browser, which asks for the organization's name, and waits until the
|
|
81
|
+
account exists. That sign-in asks for the password once more, because the CLI's own sign-in
|
|
82
|
+
never reaches the BFF. `coho login --token` with such an identity says where to go instead.
|
|
83
|
+
- An account saved with `coho use` that is not one of yours any more — deleted, or
|
|
84
|
+
remembered from before an environment was reset — gives way to your only account, which
|
|
85
|
+
is saved in its place (with the stale project and ref cleared) and named in a one-time
|
|
86
|
+
warning. With several accounts it asks which. Costs one `/me` per command while an
|
|
87
|
+
account is saved. `--account` and `COHO_ACCOUNT` are used as given.
|
|
88
|
+
- `coho type put` and `coho entry create` build the slug from the name when none is given:
|
|
89
|
+
camelCase for a type (`Blog post` → `blogPost`), kebab-case for an entry (`Hello, World!` →
|
|
90
|
+
`hello-world`). An explicit slug is sent unchanged.
|
|
91
|
+
- `coho tag create --validate` has the server check every entry at the tag point before
|
|
92
|
+
cutting the tag. If any fails, it is refused with `TAG_CONTENT_INVALID` and nothing is
|
|
93
|
+
tagged: the CLI prints how many entries fail, then each listed one's slug, type, id and
|
|
94
|
+
field errors, says how many went unlisted past the server's 100, and exits 1. With
|
|
95
|
+
`-o json` the problem document goes to stdout. Without the flag, tagging is unchanged.
|
|
96
|
+
Needs the `coho-management-sdk` that adds `tags.create(validate=…)`, and a server that
|
|
97
|
+
accepts `validate`.
|
|
98
|
+
|
|
99
|
+
### Changed
|
|
100
|
+
|
|
101
|
+
- `coho branch create` makes the new branch the current ref, as `coho project create`
|
|
102
|
+
makes the new project current; `--no-use` keeps the current ref. When `--ref` or
|
|
103
|
+
`COHO_REF` is set, it warns that the override still wins over the saved ref.
|
|
104
|
+
- `coho signup` opens the BFF's sign-up page in the browser, as `coho login` opens the
|
|
105
|
+
sign-in page, and makes no API call itself. Sign-up stays an interactive session;
|
|
106
|
+
before release that page verifies a captcha.
|
|
107
|
+
- `coho login` uses `http://localhost:8765/callback`, matching the CLI's Cognito app
|
|
108
|
+
client, and answers on both loopback addresses.
|
|
109
|
+
|
|
110
|
+
- The library moved to its own repository and distribution,
|
|
111
|
+
[coho-management-sdk-python](https://github.com/coho-cms/coho-management-sdk-python), along with the API
|
|
112
|
+
contracts and the library reference. This repository is now the command alone, and
|
|
113
|
+
depends on `coho-management-sdk` as an ordinary package. Development uses a sibling checkout;
|
|
114
|
+
see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
115
|
+
|
|
116
|
+
### Fixed
|
|
117
|
+
|
|
118
|
+
- `coho preview entries` crashed with `AttributeError: 'str' object has no attribute
|
|
119
|
+
'get'`, in table and JSON output both. It read the body as a list of entries, but
|
|
120
|
+
delivery answers with an `EntryGraph`: it now lists `roots` in order, looked up in
|
|
121
|
+
`entries`.
|
|
122
|
+
- `coho signup`, `coho invite lookup` and `coho invite accept` failed with
|
|
123
|
+
`NOT_LOGGED_IN` for anyone without a login, who are exactly the people they are for.
|
|
124
|
+
- Errors printed their code twice, as in `error NOT_LOGGED_IN: NOT_LOGGED_IN: …`.
|
|
125
|
+
- URLs printed by `--no-browser` were wrapped at the terminal's width, so a copied URL
|
|
126
|
+
came out broken.
|
|
127
|
+
- `coho login` followed a successful sign-in with a bare `UNAUTHENTICATED` when the
|
|
128
|
+
identity had no Coho account yet. It now says so, and what to run.
|
|
129
|
+
|
|
130
|
+
## [0.1.0] — unreleased
|
|
131
|
+
|
|
132
|
+
Initial version.
|
|
133
|
+
|
|
134
|
+
- Commands: `login`, `logout`, `whoami`, `signup`, `configure`, `profile`, `use`,
|
|
135
|
+
`status`, `account`, `member`, `invite`, `user`, `project`, `role`, `ref`, `branch`,
|
|
136
|
+
`tag`, `type`, `entry`, `diff`, `merge`, `env`, `promote`, `rollback`, `tier`, `key`,
|
|
137
|
+
`export`, `preview`.
|
|
138
|
+
- A sticky context (account, project, ref) per profile, verified against the server
|
|
139
|
+
before it is saved, and overridable by flag or environment variable.
|
|
140
|
+
- `--output json` prints the contract's response verbatim, so `jq` works against the
|
|
141
|
+
documented shapes. Exit codes distinguish usage (2), authentication (3), a failed
|
|
142
|
+
precondition worth retrying (4) and not-found (5).
|
|
143
|
+
- A local project registry, because the server has no project listing yet.
|
|
144
|
+
|
|
145
|
+
[Unreleased]: https://github.com/coho-cms/coho-cli/compare/v0.1.0...HEAD
|
|
146
|
+
[0.1.0]: https://github.com/coho-cms/coho-cli/releases/tag/v0.1.0
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
|
|
5
|
+
The CLI is a thin layer over [`coho-management-sdk`](https://github.com/coho-cms/coho-management-sdk-python),
|
|
6
|
+
and develops against a sibling checkout of it:
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
git clone https://github.com/coho-cms/coho-management-sdk-python
|
|
10
|
+
git clone https://github.com/coho-cms/coho-cli
|
|
11
|
+
cd coho-cli
|
|
12
|
+
uv sync # resolves coho-management-sdk from ../coho-management-sdk-python
|
|
13
|
+
uv run coho --help
|
|
14
|
+
uv run pytest # 22 tests, no network, no server
|
|
15
|
+
uv run ruff check . && uv run ruff format --check .
|
|
16
|
+
uv run mypy # strict
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or `make check` for all of it.
|
|
20
|
+
|
|
21
|
+
### `coho` as a real command
|
|
22
|
+
|
|
23
|
+
To use your working copy as the installed `coho`, from any directory:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
uv tool install --editable . --with-editable ../coho-management-sdk-python
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The command lands in `~/.local/bin` and runs the source in both repositories, so
|
|
30
|
+
edits take effect at once. Reinstall, by adding `--force`, only when dependencies or
|
|
31
|
+
the entry point change; `uv tool uninstall coho-cli` removes it. It shares the
|
|
32
|
+
profiles and stored logins in `~/.config/coho` with `uv run coho`. `uv sync --no-sources` builds against the published
|
|
33
|
+
`coho-management-sdk` instead, which is what a user gets; see `[tool.uv.sources]` in
|
|
34
|
+
`pyproject.toml`.
|
|
35
|
+
|
|
36
|
+
## Layout
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
src/coho_cli/
|
|
40
|
+
main.py the typer app, global options, error → exit code
|
|
41
|
+
_state.py config/profile/client/context resolution for commands
|
|
42
|
+
_output.py tables, JSON, shown-once secrets
|
|
43
|
+
_io.py --file and --set parsing
|
|
44
|
+
commands/ one module per noun
|
|
45
|
+
tests/ the whole command tree against the SDK's FakeBff
|
|
46
|
+
docs/ guides and the command reference
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
⚠️ **Push the SDK first.** CI builds the CLI against the SDK's `v2026.1.0` branch on GitHub,
|
|
50
|
+
not your local copy. A CLI change that uses new SDK code fails CI with
|
|
51
|
+
`"Coho" has no attribute …` until that SDK code is pushed; then re-run the CLI's
|
|
52
|
+
workflow.
|
|
53
|
+
|
|
54
|
+
The library, the API contracts and the library reference live in
|
|
55
|
+
[coho-management-sdk-python](https://github.com/coho-cms/coho-management-sdk-python).
|
|
56
|
+
|
|
57
|
+
## Rules of the road
|
|
58
|
+
|
|
59
|
+
1. **The library owns behaviour; the CLI owns presentation.** If a command needs logic
|
|
60
|
+
that is not printing or argument parsing, it belongs in `coho_management_sdk` — open a pull
|
|
61
|
+
request there and call it from here. Resist the urge to reimplement it locally.
|
|
62
|
+
2. **Errors print their `code`.** `main.py` maps `CohoError` to an exit code and a
|
|
63
|
+
one-line message; never catch an error just to reword it.
|
|
64
|
+
3. **Secrets go to stdout alone**, with the warning on stderr, so
|
|
65
|
+
`coho key create > key.txt` captures the key and nothing else.
|
|
66
|
+
4. **`--output json` prints the contract's response**, not a reshaped version of it.
|
|
67
|
+
Every model in the SDK keeps `.raw` for exactly this.
|
|
68
|
+
5. **Tests run against `coho_management_sdk.testing.FakeBff`**, never a live server. A behaviour
|
|
69
|
+
that needs a new route belongs in the fake, which means a pull request against the
|
|
70
|
+
library.
|
|
71
|
+
|
|
72
|
+
## Releasing
|
|
73
|
+
|
|
74
|
+
The version comes from git tags (`hatch-vcs`); nothing in the source holds it.
|
|
75
|
+
Tag `v2026.1.0` to release, `v2026.1.0a1`, `b1` or `rc1` for a pre-release. Every
|
|
76
|
+
commit after a tag builds a dev version such as `2026.1.0a2.dev3`. ⚠️ Never tag a dev
|
|
77
|
+
version: CI refuses it, and every later build would fail.
|
|
78
|
+
|
|
79
|
+
1. Update the `coho-management-sdk` constraint if the CLI needs a newer library, and add
|
|
80
|
+
a `CHANGELOG.md` entry.
|
|
81
|
+
2. Commit and push, then tag: `git tag v2026.1.0a4 && git push origin v2026.1.0a4`.
|
|
82
|
+
3. CI tests, builds a wheel and an sdist, fails if the tag is not a version in its
|
|
83
|
+
standard form, uploads both as a build artifact, attaches them to a GitHub
|
|
84
|
+
Release (a pre-release for an alpha, beta or release candidate), and publishes
|
|
85
|
+
them to [PyPI](https://pypi.org/project/coho-cli/).
|
|
86
|
+
|
|
87
|
+
⚠️ **Release `coho-management-sdk` first**, and wait until it is on PyPI: a `coho-cli` that
|
|
88
|
+
depends on a library version nobody can install is not installable either. PyPI never
|
|
89
|
+
takes the same version's files twice, even after a delete, so a broken release is fixed
|
|
90
|
+
by the next tag.
|
|
91
|
+
|
|
92
|
+
The `publish-pypi` job authenticates as a [trusted publisher](https://docs.pypi.org/trusted-publishers/)
|
|
93
|
+
with a short-lived OIDC token, so no API token is stored anywhere, and deploys through
|
|
94
|
+
the `pypi` environment, which only `v*` tags can use. Setting the repository variable
|
|
95
|
+
`PUBLISH_TO_PYPI` to anything but `true` turns it off.
|