posit-cli 0.1.0__tar.gz → 0.2.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.
Files changed (27) hide show
  1. posit_cli-0.2.0/.github/dependabot.yml +14 -0
  2. {posit_cli-0.1.0 → posit_cli-0.2.0}/.github/workflows/ci.yaml +6 -3
  3. posit_cli-0.2.0/.github/workflows/release.yaml +62 -0
  4. {posit_cli-0.1.0 → posit_cli-0.2.0}/.gitignore +3 -0
  5. {posit_cli-0.1.0 → posit_cli-0.2.0}/CLAUDE.md +3 -0
  6. posit_cli-0.2.0/CONTRIBUTING.md +91 -0
  7. {posit_cli-0.1.0 → posit_cli-0.2.0}/Justfile +16 -4
  8. {posit_cli-0.1.0 → posit_cli-0.2.0}/PKG-INFO +49 -8
  9. {posit_cli-0.1.0 → posit_cli-0.2.0}/README.md +44 -5
  10. posit_cli-0.2.0/RELEASE.md +34 -0
  11. {posit_cli-0.1.0 → posit_cli-0.2.0}/pyproject.toml +13 -3
  12. {posit_cli-0.1.0 → posit_cli-0.2.0}/skills/posit-cli/SKILL.md +17 -5
  13. posit_cli-0.2.0/src/posit_cli/_version.py +24 -0
  14. {posit_cli-0.1.0 → posit_cli-0.2.0}/src/posit_cli/connect/__init__.py +3 -0
  15. posit_cli-0.2.0/src/posit_cli/connect/version.py +68 -0
  16. {posit_cli-0.1.0 → posit_cli-0.2.0}/tests/test_rsconnect_contract.py +1 -0
  17. posit_cli-0.2.0/tests/test_version.py +97 -0
  18. posit_cli-0.2.0/uv.lock +3186 -0
  19. posit_cli-0.1.0/.github/workflows/release.yaml +0 -37
  20. posit_cli-0.1.0/RELEASE.md +0 -22
  21. posit_cli-0.1.0/uv.lock +0 -1143
  22. {posit_cli-0.1.0 → posit_cli-0.2.0}/LICENSE +0 -0
  23. {posit_cli-0.1.0 → posit_cli-0.2.0}/src/posit_cli/__init__.py +0 -0
  24. {posit_cli-0.1.0 → posit_cli-0.2.0}/src/posit_cli/__main__.py +0 -0
  25. {posit_cli-0.1.0 → posit_cli-0.2.0}/src/posit_cli/connect/api.py +0 -0
  26. {posit_cli-0.1.0 → posit_cli-0.2.0}/tests/test_api.py +0 -0
  27. {posit_cli-0.1.0 → posit_cli-0.2.0}/tests/test_cli.py +0 -0
@@ -0,0 +1,14 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: uv
4
+ directory: /
5
+ schedule:
6
+ interval: weekly
7
+ commit-message:
8
+ prefix: chore(deps)
9
+ - package-ecosystem: github-actions
10
+ directory: /
11
+ schedule:
12
+ interval: weekly
13
+ commit-message:
14
+ prefix: chore(deps)
@@ -12,7 +12,7 @@ jobs:
12
12
  runs-on: ubuntu-latest
13
13
  steps:
14
14
  - uses: actions/checkout@v7
15
- - uses: astral-sh/setup-uv@v9.0.0
15
+ - uses: astral-sh/setup-uv@v10.3.3
16
16
  - uses: extractions/setup-just@v4
17
17
  # Fail fast if uv.lock has drifted from pyproject.toml.
18
18
  - run: uv lock --locked
@@ -33,7 +33,7 @@ jobs:
33
33
  - "3.14"
34
34
  steps:
35
35
  - uses: actions/checkout@v7
36
- - uses: astral-sh/setup-uv@v9.0.0
36
+ - uses: astral-sh/setup-uv@v10.3.3
37
37
  with:
38
38
  python-version: ${{ matrix.python-version }}
39
39
  - uses: extractions/setup-just@v4
@@ -43,7 +43,10 @@ jobs:
43
43
  runs-on: ubuntu-latest
44
44
  steps:
45
45
  - uses: actions/checkout@v7
46
- - uses: astral-sh/setup-uv@v9.0.0
46
+ # hatch-vcs reads the version from the tags, so fetch them.
47
+ with:
48
+ fetch-depth: 0
49
+ - uses: astral-sh/setup-uv@v10.3.3
47
50
  - uses: extractions/setup-just@v4
48
51
  - run: just build
49
52
  - run: just smoke
@@ -0,0 +1,62 @@
1
+ name: Release
2
+ on:
3
+ push:
4
+ tags:
5
+ - "v*.*.*"
6
+ jobs:
7
+ publish:
8
+ runs-on: ubuntu-latest
9
+ environment: release
10
+ permissions:
11
+ id-token: write
12
+ steps:
13
+ - uses: actions/checkout@v7
14
+ with:
15
+ fetch-depth: 0
16
+ - uses: astral-sh/setup-uv@v10.3.3
17
+ - uses: extractions/setup-just@v4
18
+ # The tag is the only source of the version, so this only guards against a
19
+ # build that did not pick the tag up (shallow clone, dirty tree, two tags on
20
+ # one commit).
21
+ - name: assert build version matches tag
22
+ env:
23
+ TAG: ${{ github.ref_name }}
24
+ run: |
25
+ ver="$(just version)"
26
+ if [ "$TAG" != "v$ver" ]; then
27
+ echo "::error::tag '${TAG}' does not match build version 'v${ver}'"
28
+ exit 1
29
+ fi
30
+ - name: assert tag is on main
31
+ env:
32
+ TAG: ${{ github.ref_name }}
33
+ run: |
34
+ if ! git merge-base --is-ancestor HEAD origin/main; then
35
+ echo "::error::tag '${TAG}' points to a commit that is not on main"
36
+ exit 1
37
+ fi
38
+ - run: just build
39
+ - run: just smoke
40
+ - uses: actions/upload-artifact@v7
41
+ with:
42
+ name: dist
43
+ path: dist/
44
+ - uses: pypa/gh-action-pypi-publish@release/v1
45
+
46
+ # Runs only after PyPI accepts the upload, so a failed publish leaves no release.
47
+ github-release:
48
+ needs: publish
49
+ runs-on: ubuntu-latest
50
+ permissions:
51
+ contents: write
52
+ steps:
53
+ - uses: actions/checkout@v7
54
+ - uses: actions/download-artifact@v8
55
+ with:
56
+ name: dist
57
+ path: dist/
58
+ - name: create GitHub release
59
+ env:
60
+ GH_TOKEN: ${{ github.token }}
61
+ TAG: ${{ github.ref_name }}
62
+ run: gh release create "$TAG" dist/* --title "$TAG" --generate-notes
@@ -9,6 +9,9 @@ dist/
9
9
  .venv/
10
10
  venv/
11
11
 
12
+ # Written at build time by hatch-vcs
13
+ /src/posit_cli/_version.py
14
+
12
15
  # Tooling
13
16
  .pytest_cache/
14
17
  .ruff_cache/
@@ -37,4 +37,7 @@ posit-sdk) and the OAuth-release gotcha.
37
37
  `RSConnectClient`), which has no stability contract. `tests/test_rsconnect_contract.py` guards
38
38
  the surface we depend on; pin the rsconnect version and re-verify on bumps.
39
39
 
40
+ - The git tag sets the version, through `hatch-vcs`. `pyproject.toml` has no `version` field.
41
+ Do not add one.
42
+
40
43
  See `RELEASE.md` for how to cut a release to PyPI.
@@ -0,0 +1,91 @@
1
+ # Contributing
2
+
3
+ This guide covers local development for `posit-cli`. End-user installation and CLI usage are documented in [README.md](README.md).
4
+
5
+ ## Prerequisites
6
+
7
+ Install these tools before starting:
8
+
9
+ - Git
10
+ - Python 3.8 or newer
11
+ - [uv](https://docs.astral.sh/uv/)
12
+ - [just](https://github.com/casey/just)
13
+
14
+ The project uses uv for dependency management and virtual environments. The supported Python versions are 3.8 through 3.14, which are tested in CI.
15
+
16
+ ## Set Up
17
+
18
+ Clone the repository and change into its directory:
19
+
20
+ ```console
21
+ git clone https://github.com/posit-dev/posit-cli.git
22
+ cd posit-cli
23
+ ```
24
+
25
+ Sync the project, test, and lint dependencies:
26
+
27
+ ```console
28
+ just deps
29
+ ```
30
+
31
+ This runs `uv sync --all-extras`, using `uv.lock` to create or update the project environment in `.venv`. You do not need to activate `.venv` when using the `just` recipes.
32
+
33
+ To put an editable `posit` command on your `PATH`, run:
34
+
35
+ ```console
36
+ just dev
37
+ posit --help
38
+ ```
39
+
40
+ `just dev` installs the checkout as an editable uv tool. Changes to the source are therefore available through `posit` without rebuilding or reinstalling the package. If the command is not found, add the directory printed by `uv tool dir --bin` to your `PATH`, or run `uv tool update-shell`.
41
+
42
+ ## Common Commands
43
+
44
+ Run these commands from the repository root:
45
+
46
+ | Command | Purpose |
47
+ | --- | --- |
48
+ | `just deps` | Sync runtime, test, and lint dependencies from `uv.lock`. |
49
+ | `just dev` | Install the checkout as an editable uv tool. |
50
+ | `just test` | Run tests against Python 3.13. |
51
+ | `just test 3.8` | Run tests against a specific Python version. |
52
+ | `just lint` | Check formatting and run Ruff. |
53
+ | `just fmt` | Format files and apply Ruff fixes. |
54
+ | `just build` | Build the wheel and source distribution in `dist/`. |
55
+ | `just smoke` | Run `posit --help` from the latest built wheel. |
56
+ | `just install` | Install the latest built wheel as a non-editable uv tool. |
57
+ | `just uninstall` | Remove the uv-managed `posit-cli` tool. |
58
+ | `just clean` | Remove build, test, and lint artifacts. |
59
+
60
+ `just smoke` expects a wheel to exist in `dist/`, so run `just build` first unless another command has already built one.
61
+
62
+ ## Dependency Changes
63
+
64
+ Runtime dependencies and optional test/lint dependencies are declared in `pyproject.toml`. When changing them:
65
+
66
+ 1. Update `pyproject.toml`.
67
+ 2. Run `uv lock` to update `uv.lock`.
68
+ 3. Run `just deps` to sync the local environment.
69
+ 4. Include both `pyproject.toml` and `uv.lock` in the change.
70
+
71
+ CI checks that the lockfile is current with `uv lock --locked`.
72
+
73
+ ## Before Opening A Pull Request
74
+
75
+ Run the checks relevant to your change. The normal local validation sequence is:
76
+
77
+ ```console
78
+ just deps
79
+ just lint
80
+ just test
81
+ just build
82
+ just smoke
83
+ ```
84
+
85
+ CI also runs the test suite against Python 3.8, 3.9, 3.10, 3.11, 3.12, 3.13, and 3.14. Add or update tests when changing behavior, and update user-facing documentation in `README.md` when the CLI changes.
86
+
87
+ Keep commits focused and use Conventional Commit messages, such as `fix: handle expired Connect tokens` or `docs: clarify local setup`.
88
+
89
+ ## Releases
90
+
91
+ Release preparation and publishing are documented in [RELEASE.md](RELEASE.md). In particular, do not add a hard-coded version to `pyproject.toml`; release versions come from Git tags through `hatch-vcs`.
@@ -1,5 +1,9 @@
1
1
  # posit-cli task runner. Run `just --list` to see recipes.
2
2
 
3
+ # Sync project and development dependencies into the project environment
4
+ deps:
5
+ uv sync --all-extras
6
+
3
7
  # Run the test suite against a single Python version (default 3.13)
4
8
  test py="3.13":
5
9
  uv run --python {{py}} --extra test pytest tests
@@ -25,13 +29,21 @@ smoke:
25
29
  WHL=$(ls -t dist/*.whl | head -1)
26
30
  uv run --no-project --with "$WHL" posit --help
27
31
 
28
- # Install the most recently built wheel into the active environment
32
+ # Install the most recently built wheel as a standalone uv tool
29
33
  install: build
30
- uv pip install "$(ls -t dist/*.whl | head -1)"
34
+ uv tool install --force "$(ls -t dist/*.whl | head -1)"
35
+
36
+ # Install the project as an editable standalone uv tool
37
+ dev:
38
+ uv tool install --editable --force .
39
+
40
+ # Remove the installed standalone uv tool
41
+ uninstall:
42
+ uv tool uninstall posit-cli
31
43
 
32
- # Print the current version
44
+ # Print the version that a build gets from the current git state
33
45
  version:
34
- @uv version --short
46
+ @uvx hatch version
35
47
 
36
48
  # Remove build/test artifacts
37
49
  clean:
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: posit-cli
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: A single, friendly command-line interface for Posit Connect, in the spirit of gh.
5
5
  Author: Posit Software, PBC
6
6
  License-Expression: MIT
@@ -9,9 +9,11 @@ Requires-Python: >=3.8
9
9
  Requires-Dist: click>=8.0
10
10
  Requires-Dist: jq>=1.4
11
11
  Requires-Dist: keyring>=23.0
12
- Requires-Dist: rsconnect-python<2,>=1.30
12
+ Requires-Dist: rsconnect-python<2,>=1.33
13
13
  Provides-Extra: lint
14
14
  Requires-Dist: ruff>=0.6; extra == 'lint'
15
+ Provides-Extra: snowflake
16
+ Requires-Dist: rsconnect-python[snowflake]; extra == 'snowflake'
15
17
  Provides-Extra: test
16
18
  Requires-Dist: pytest>=7; extra == 'test'
17
19
  Description-Content-Type: text/markdown
@@ -30,25 +32,47 @@ This project is in early-stage development and so far only supports Posit Connec
30
32
 
31
33
  ## Installation
32
34
 
33
- `posit-cli` isn't on PyPI yet. Install the latest version straight from GitHub
34
- with [`uv`](https://docs.astral.sh/uv/):
35
+ Install [`posit-cli` from PyPI](https://pypi.org/project/posit-cli/) with
36
+ [`uv`](https://docs.astral.sh/uv/):
37
+
38
+ ```console
39
+ uv tool install posit-cli
40
+ ```
41
+
42
+ To get unreleased changes, install from GitHub instead:
35
43
 
36
44
  ```console
37
45
  uv tool install git+https://github.com/posit-dev/posit-cli.git
38
46
  ```
39
47
 
40
- If you authenticate to GitHub over SSH, use the `git+ssh://` form instead (uv
41
- requires the `git@` username):
48
+ If you authenticate to GitHub over SSH, use the `git+ssh://` form (uv requires
49
+ the `git@` username):
42
50
 
43
51
  ```console
44
52
  uv tool install git+ssh://git@github.com/posit-dev/posit-cli.git
45
53
  ```
46
54
 
47
- Either way this puts the `posit` executable on your `PATH`. To upgrade later, run
55
+ Each of these puts the `posit` executable on your `PATH`. To upgrade later, run
48
56
  `uv tool upgrade posit-cli`. See uv's
49
57
  [Git authentication docs](https://docs.astral.sh/uv/concepts/authentication/git/)
50
58
  for tokens and other hosts.
51
59
 
60
+ ### Connect on Snowflake
61
+
62
+ To deploy to Posit Connect on Snowflake, install the Snowflake extra instead:
63
+
64
+ ```console
65
+ uv tool install 'posit-cli[snowflake]'
66
+ # or: pip install 'posit-cli[snowflake]'
67
+ ```
68
+
69
+ This includes the Snowflake CLI. Before deploying, configure a
70
+ [Snowflake CLI connection](https://docs.snowflake.com/en/developer-guide/snowflake-cli/connecting/configure-connections)
71
+ with access to your Connect instance and `authenticator` set to `snowflake_jwt`
72
+ (key-pair authentication) or `oauth`. Select it with
73
+ `--snowflake-connection-name` when adding the Connect server with
74
+ `posit connect add`; see `posit connect add --help` for options.
75
+
52
76
  ## Getting started
53
77
 
54
78
  **1. Log in.** Point `posit` at your Connect server and authenticate. This runs
@@ -126,10 +150,27 @@ $ posit connect api v1/users -X GET -f page_size=5 # or force GET
126
150
  $ posit connect api v1/content -f name=my-app # POST body (creates content)
127
151
  ```
128
152
 
153
+ ## `posit connect version`
154
+
155
+ Print the Connect server version using your saved credentials:
156
+
157
+ ```console
158
+ $ posit connect version
159
+ $ posit connect version --name production
160
+ $ posit connect version --server https://connect.example.com --api-key "$CONNECT_API_KEY"
161
+ ```
162
+
163
+ The command supports `-n`/`--name`, `-s`/`--server`, `-k`/`--api-key`,
164
+ `--no-tls-verify`, and `-c`/`--cacert`, with the same connection environment
165
+ variables as `posit connect api`. It prints an empty line if the server hides
166
+ its version. Rsconnect's `CONNECT_SERVER_VERSION` environment variable overrides
167
+ the reported version.
168
+
129
169
  ## `posit connect *`
130
170
 
131
171
  `posit` wraps [`rsconnect-python`](https://github.com/posit-dev/rsconnect-python):
132
172
  it re-exposes the full `rsconnect` command set under `posit connect`, in addition to the `api` utility.
173
+ The `version` command is overridden to report the Connect server version.
133
174
 
134
175
  ## Using `posit` with Claude Code
135
176
 
@@ -12,25 +12,47 @@ This project is in early-stage development and so far only supports Posit Connec
12
12
 
13
13
  ## Installation
14
14
 
15
- `posit-cli` isn't on PyPI yet. Install the latest version straight from GitHub
16
- with [`uv`](https://docs.astral.sh/uv/):
15
+ Install [`posit-cli` from PyPI](https://pypi.org/project/posit-cli/) with
16
+ [`uv`](https://docs.astral.sh/uv/):
17
+
18
+ ```console
19
+ uv tool install posit-cli
20
+ ```
21
+
22
+ To get unreleased changes, install from GitHub instead:
17
23
 
18
24
  ```console
19
25
  uv tool install git+https://github.com/posit-dev/posit-cli.git
20
26
  ```
21
27
 
22
- If you authenticate to GitHub over SSH, use the `git+ssh://` form instead (uv
23
- requires the `git@` username):
28
+ If you authenticate to GitHub over SSH, use the `git+ssh://` form (uv requires
29
+ the `git@` username):
24
30
 
25
31
  ```console
26
32
  uv tool install git+ssh://git@github.com/posit-dev/posit-cli.git
27
33
  ```
28
34
 
29
- Either way this puts the `posit` executable on your `PATH`. To upgrade later, run
35
+ Each of these puts the `posit` executable on your `PATH`. To upgrade later, run
30
36
  `uv tool upgrade posit-cli`. See uv's
31
37
  [Git authentication docs](https://docs.astral.sh/uv/concepts/authentication/git/)
32
38
  for tokens and other hosts.
33
39
 
40
+ ### Connect on Snowflake
41
+
42
+ To deploy to Posit Connect on Snowflake, install the Snowflake extra instead:
43
+
44
+ ```console
45
+ uv tool install 'posit-cli[snowflake]'
46
+ # or: pip install 'posit-cli[snowflake]'
47
+ ```
48
+
49
+ This includes the Snowflake CLI. Before deploying, configure a
50
+ [Snowflake CLI connection](https://docs.snowflake.com/en/developer-guide/snowflake-cli/connecting/configure-connections)
51
+ with access to your Connect instance and `authenticator` set to `snowflake_jwt`
52
+ (key-pair authentication) or `oauth`. Select it with
53
+ `--snowflake-connection-name` when adding the Connect server with
54
+ `posit connect add`; see `posit connect add --help` for options.
55
+
34
56
  ## Getting started
35
57
 
36
58
  **1. Log in.** Point `posit` at your Connect server and authenticate. This runs
@@ -108,10 +130,27 @@ $ posit connect api v1/users -X GET -f page_size=5 # or force GET
108
130
  $ posit connect api v1/content -f name=my-app # POST body (creates content)
109
131
  ```
110
132
 
133
+ ## `posit connect version`
134
+
135
+ Print the Connect server version using your saved credentials:
136
+
137
+ ```console
138
+ $ posit connect version
139
+ $ posit connect version --name production
140
+ $ posit connect version --server https://connect.example.com --api-key "$CONNECT_API_KEY"
141
+ ```
142
+
143
+ The command supports `-n`/`--name`, `-s`/`--server`, `-k`/`--api-key`,
144
+ `--no-tls-verify`, and `-c`/`--cacert`, with the same connection environment
145
+ variables as `posit connect api`. It prints an empty line if the server hides
146
+ its version. Rsconnect's `CONNECT_SERVER_VERSION` environment variable overrides
147
+ the reported version.
148
+
111
149
  ## `posit connect *`
112
150
 
113
151
  `posit` wraps [`rsconnect-python`](https://github.com/posit-dev/rsconnect-python):
114
152
  it re-exposes the full `rsconnect` command set under `posit connect`, in addition to the `api` utility.
153
+ The `version` command is overridden to report the Connect server version.
115
154
 
116
155
  ## Using `posit` with Claude Code
117
156
 
@@ -0,0 +1,34 @@
1
+ # Releasing
2
+
3
+ The git tag is the only source of the version. `pyproject.toml` has no `version` field —
4
+ `hatch-vcs` reads the tag at build time. Do not add one.
5
+
6
+ Follow these steps to publish a new version to PyPI:
7
+
8
+ 1. Make sure `main` holds everything you want in the release.
9
+ 2. Tag the commit on `main` as `vX.Y.Z`.
10
+ 3. Push the tag. This triggers `.github/workflows/release.yaml`.
11
+
12
+ ```console
13
+ git checkout main && git pull
14
+ git tag v0.2.0
15
+ git push origin v0.2.0
16
+ ```
17
+
18
+ The release workflow confirms that the build picked the tag up and that the tag is on `main`,
19
+ builds the wheel and sdist, smoke-tests the wheel, then publishes to PyPI through GitHub's OIDC
20
+ trusted-publisher flow. No PyPI token is stored in this repo. After PyPI accepts the upload, a
21
+ second job creates a GitHub release for the tag, with generated notes and the built artifacts
22
+ attached.
23
+
24
+ Run `just version` to see the version that a build gets from the current git state. A commit
25
+ after the last tag gets a development version (for example `0.2.1.dev3+g1a2b3c4`), which PyPI
26
+ rejects. Only a clean, tagged commit produces a release version.
27
+
28
+ ## One-time setup for a new repo
29
+
30
+ - Create a `release` environment under the repo's Settings > Environments, with required
31
+ reviewers. The `publish` job runs under this environment; without required reviewers, any tag
32
+ push publishes to PyPI immediately with no human gate.
33
+ - Configure a trusted publisher for `posit-cli` on PyPI with: Owner `posit-dev`, Repository name
34
+ `posit-cli`, Workflow name `release.yaml`, Environment name `release`.
@@ -1,6 +1,7 @@
1
1
  [project]
2
2
  name = "posit-cli"
3
- version = "0.1.0"
3
+ # The version comes from the git tag, through hatch-vcs. Never hard-code it here.
4
+ dynamic = ["version"]
4
5
  description = "A single, friendly command-line interface for Posit Connect, in the spirit of gh."
5
6
  readme = "README.md"
6
7
  requires-python = ">=3.8"
@@ -11,7 +12,7 @@ dependencies = [
11
12
  # posit-cli reuses rsconnect-python's *internal* API (RSConnectExecutor,
12
13
  # RSConnectClient). These have no stability contract, so re-verify the internals
13
14
  # on bumps. See tests/test_rsconnect_contract.py.
14
- "rsconnect-python>=1.30,<2",
15
+ "rsconnect-python>=1.33,<2",
15
16
  # rsconnect imports `keyring` optionally; we depend on it directly so OAuth
16
17
  # tokens land in the OS keyring.
17
18
  "keyring>=23.0",
@@ -24,13 +25,22 @@ dependencies = [
24
25
  posit = "posit_cli.__main__:cli"
25
26
 
26
27
  [project.optional-dependencies]
28
+ snowflake = ["rsconnect-python[snowflake]"]
27
29
  test = ["pytest>=7"]
28
30
  lint = ["ruff>=0.6"]
29
31
 
30
32
  [build-system]
31
- requires = ["hatchling"]
33
+ requires = ["hatchling", "hatch-vcs"]
32
34
  build-backend = "hatchling.build"
33
35
 
36
+ [tool.hatch.version]
37
+ source = "vcs"
38
+
39
+ # Write the resolved version into the package. An sdist has no git metadata, so a
40
+ # wheel built from an sdist reads this file instead of asking git.
41
+ [tool.hatch.build.hooks.vcs]
42
+ version-file = "src/posit_cli/_version.py"
43
+
34
44
  [tool.hatch.build.targets.wheel]
35
45
  packages = ["src/posit_cli"]
36
46
 
@@ -175,6 +175,10 @@ posit connect deploy quarto ./report
175
175
  posit connect deploy manifest ./manifest.json # deploy a prepared bundle
176
176
  ```
177
177
 
178
+ To update existing Connect content instead of creating new content, pass its
179
+ GUID with `--guid <guid>`. Find the GUID with
180
+ `posit connect api v1/content -q '.[] | "\(.guid) \(.title)"'`.
181
+
178
182
  Available deploy targets include `api`, `bokeh`, `bundle`, `dash`, `fastapi`,
179
183
  `flask`, `gradio`, `html`, `manifest`, `nodejs`, `notebook`, `panel`, `pyproject`,
180
184
  `quarto`, `shiny`, `streamlit`, `tensorflow`, `voila`. Use `quickstart` to
@@ -191,6 +195,8 @@ These are rsconnect commands — run `posit connect <command> --help` for detail
191
195
  - `add` / `list` / `remove` / `details` / `info` — manage saved servers and
192
196
  deployment metadata.
193
197
  - `login` / `logout` — OAuth credential management.
198
+ - `token` — print the OAuth access token from `login` (for the Connect SDKs or
199
+ `curl`). `--refresh` gets a new one after a 401; exit status 4 means log in again.
194
200
  - `bootstrap` — create an initial admin user on a fresh Connect instance.
195
201
 
196
202
  ## When something fails
@@ -208,15 +214,21 @@ These are rsconnect commands — run `posit connect <command> --help` for detail
208
214
 
209
215
  ## Installing the CLI
210
216
 
211
- `posit-cli` isn't on PyPI yet; install from GitHub with [`uv`](https://docs.astral.sh/uv/):
217
+ Install from PyPI with [`uv`](https://docs.astral.sh/uv/):
218
+
219
+ ```console
220
+ uv tool install posit-cli # `posit` onto your PATH
221
+ uv tool upgrade posit-cli # later, to update
222
+ ```
223
+
224
+ For unreleased changes, install from GitHub instead:
212
225
 
213
226
  ```console
214
- uv tool install git+https://github.com/posit-dev/posit-cli.git # `posit` onto your PATH
215
- uv tool upgrade posit-cli # later, to update
227
+ uv tool install git+https://github.com/posit-dev/posit-cli.git
216
228
  ```
217
229
 
218
- If GitHub is set up for SSH auth, use the `git+ssh://` form instead (uv requires
219
- the `git@` username):
230
+ If GitHub is set up for SSH auth, use the `git+ssh://` form (uv requires the
231
+ `git@` username):
220
232
 
221
233
  ```console
222
234
  uv tool install git+ssh://git@github.com/posit-dev/posit-cli.git
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '0.2.0'
22
+ __version_tuple__ = version_tuple = (0, 2, 0)
23
+
24
+ __commit_id__ = commit_id = None
@@ -9,6 +9,7 @@ import click
9
9
  from rsconnect.main import cli as rsconnect_cli
10
10
 
11
11
  from .api import api as api_cmd
12
+ from .version import version as version_cmd
12
13
 
13
14
 
14
15
  _epilog = (
@@ -29,3 +30,5 @@ for _name, _cmd in rsconnect_cli.commands.items():
29
30
  connect.add_command(_cmd, name=_name)
30
31
 
31
32
  connect.add_command(api_cmd, name="api")
33
+ # The mounted command reports the rsconnect package version.
34
+ connect.add_command(version_cmd, name="version")
@@ -0,0 +1,68 @@
1
+ """Print the selected Connect server's version."""
2
+
3
+ from typing import Optional
4
+
5
+ import click
6
+ from rsconnect.api import RSConnectException, RSConnectExecutor
7
+
8
+
9
+ @click.command(
10
+ context_settings={"help_option_names": ["-h", "--help"]},
11
+ short_help="Show the Connect server version.",
12
+ )
13
+ @click.option("--name", "-n", default=None, help="Nickname of a saved server.")
14
+ @click.option(
15
+ "--server",
16
+ "-s",
17
+ default=None,
18
+ envvar="CONNECT_SERVER",
19
+ help="Connect server URL [env: CONNECT_SERVER].",
20
+ )
21
+ @click.option(
22
+ "--api-key",
23
+ "-k",
24
+ default=None,
25
+ envvar="CONNECT_API_KEY",
26
+ help="Connect API key [env: CONNECT_API_KEY].",
27
+ )
28
+ @click.option(
29
+ "--no-tls-verify",
30
+ "insecure",
31
+ is_flag=True,
32
+ default=False,
33
+ envvar="CONNECT_INSECURE",
34
+ help="Skip TLS certificate verification (still uses TLS) [env: CONNECT_INSECURE].",
35
+ )
36
+ @click.option(
37
+ "--cacert",
38
+ "-c",
39
+ type=click.Path(exists=True, dir_okay=False),
40
+ default=None,
41
+ envvar="CONNECT_CA_CERTIFICATE",
42
+ help="Path to trusted TLS CA certificate.",
43
+ )
44
+ def version(
45
+ name: Optional[str],
46
+ server: Optional[str],
47
+ api_key: Optional[str],
48
+ insecure: bool,
49
+ cacert: Optional[str],
50
+ ) -> None:
51
+ """Print the Connect server version using saved credentials by default.
52
+
53
+ CONNECT_SERVER_VERSION overrides the reported version. If the server hides
54
+ its version, print an empty line.
55
+ """
56
+ try:
57
+ executor = RSConnectExecutor(
58
+ ctx=None,
59
+ name=name,
60
+ url=server,
61
+ api_key=api_key,
62
+ insecure=insecure,
63
+ cacert=cacert,
64
+ )
65
+ server_version = executor.client.server_version()
66
+ except RSConnectException as exc:
67
+ raise click.ClickException(str(exc)) from exc
68
+ click.echo(server_version)
@@ -17,6 +17,7 @@ def test_executor_surface():
17
17
  assert expected in params, f"RSConnectExecutor lost __init__ param {expected!r}"
18
18
 
19
19
  assert hasattr(RSConnectClient, "request")
20
+ assert callable(getattr(RSConnectClient, "server_version", None))
20
21
  req_params = inspect.signature(RSConnectClient.request).parameters
21
22
  for expected in ("method", "path", "query_params", "body", "headers"):
22
23
  assert expected in req_params, f"RSConnectClient.request lost {expected!r}"