Skip to content

Commit 0becc5b

Browse files
committed
Release 0.1.26 CLI operations
1 parent 832234b commit 0becc5b

12 files changed

Lines changed: 1070 additions & 54 deletions

File tree

.github/ISSUE_TEMPLATE/auth_bug.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ body:
2929
id: version
3030
attributes:
3131
label: python-substack version
32-
placeholder: 0.1.25
32+
placeholder: 0.1.26
3333
validations:
3434
required: true
3535
- type: input

.github/workflows/ci.yml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: Tests
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
test:
14+
runs-on: ubuntu-latest
15+
strategy:
16+
fail-fast: false
17+
matrix:
18+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
19+
steps:
20+
- uses: actions/checkout@v6
21+
- uses: actions/setup-python@v6
22+
with:
23+
python-version: ${{ matrix.python-version }}
24+
cache: pip
25+
- name: Install project and test runner
26+
run: |
27+
python -m pip install poetry==2.3.2 pytest==9.1.1
28+
poetry install --all-extras
29+
- name: Run tests
30+
run: poetry run pytest -q
31+
32+
build:
33+
needs: test
34+
runs-on: ubuntu-latest
35+
steps:
36+
- uses: actions/checkout@v6
37+
- uses: actions/setup-python@v6
38+
with:
39+
python-version: "3.12"
40+
cache: pip
41+
- name: Install build tools
42+
run: python -m pip install poetry==2.3.2 twine==6.2.0
43+
- name: Validate and build package
44+
run: |
45+
poetry check
46+
poetry build
47+
python -m twine check dist/*
48+
- name: Smoke test wheel installations
49+
run: |
50+
WHEEL=$(find dist -name '*.whl' -print -quit)
51+
python -m venv /tmp/python-substack-base
52+
/tmp/python-substack-base/bin/pip install "$WHEEL"
53+
/tmp/python-substack-base/bin/substack --version
54+
/tmp/python-substack-base/bin/python -c "import substack"
55+
python -m venv /tmp/python-substack-mcp
56+
/tmp/python-substack-mcp/bin/pip install "${WHEEL}[mcp]"
57+
/tmp/python-substack-mcp/bin/python -c "import substack_mcp.mcp_server"
58+
- uses: actions/upload-artifact@v7
59+
with:
60+
name: python-substack-distributions
61+
path: dist/
62+
if-no-files-found: error

.github/workflows/ci_publish.yml

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,36 @@
11
name: Publish
2+
23
on:
34
release:
45
types:
56
- created
67
workflow_dispatch:
78

9+
permissions:
10+
contents: read
11+
812
jobs:
913
deploy:
1014
runs-on: ubuntu-latest
1115
steps:
12-
- uses: actions/checkout@v3
13-
- name: Publish python poetry package
14-
uses: JRubics/poetry-publish@v1.15
16+
- uses: actions/checkout@v6
17+
- uses: actions/setup-python@v6
18+
with:
19+
python-version: "3.12"
20+
cache: pip
21+
- name: Install release tools
22+
run: python -m pip install poetry==2.3.2 pytest==9.1.1 twine==6.2.0
23+
- name: Test release source
24+
run: |
25+
poetry install --all-extras
26+
poetry run pytest -q
27+
poetry check
28+
- name: Build and validate distributions
29+
run: |
30+
poetry build
31+
python -m twine check dist/*
32+
- name: Publish validated distributions
33+
uses: pypa/gh-action-pypi-publish@release/v1
1534
with:
16-
pypi_token: ${{ secrets.PYPI_TOKEN }}
35+
user: __token__
36+
password: ${{ secrets.PYPI_TOKEN }}

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
# Changelog
22

3+
## 0.1.26
4+
5+
### Added
6+
7+
- Unified `substack` CLI for publication status and draft operations.
8+
- Commands to list publications, inspect drafts, schedule, unschedule, publish, and delete.
9+
- Stable `--json` output for automation and `--publication-url` overrides.
10+
- Interactive confirmation and non-interactive `--yes` protection for publishing and deletion.
11+
- Pull-request CI across Python 3.10 through 3.14.
12+
13+
### Improved
14+
15+
- Clear CLI error output, exit codes, timezone validation, and secret redaction.
16+
- Package positioning, keywords, operational examples, and release validation.
17+
- PyPI publishing now uploads the exact tested wheel and source distribution.
18+
319
## 0.1.25
420

521
### Added

README.md

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
# Python Substack
22

3-
Unofficial Python tools for publishing to [Substack](https://substack.com/).
3+
An unofficial Python SDK and CLI for managing [Substack](https://substack.com/) publications and drafts.
44

5+
[![PyPI](https://img.shields.io/pypi/v/python-substack)](https://pypi.org/project/python-substack/)
6+
[![Python](https://img.shields.io/pypi/pyversions/python-substack)](https://pypi.org/project/python-substack/)
7+
[![Tests](https://github.com/ma2za/python-substack/actions/workflows/ci.yml/badge.svg)](https://github.com/ma2za/python-substack/actions/workflows/ci.yml)
8+
[![Release](https://github.com/ma2za/python-substack/actions/workflows/ci_publish.yml/badge.svg)](https://github.com/ma2za/python-substack/actions/workflows/ci_publish.yml)
9+
[![License](https://img.shields.io/pypi/l/python-substack)](LICENSE)
510
[![Downloads](https://static.pepy.tech/badge/python-substack/month)](https://pepy.tech/project/python-substack)
6-
![Release Build](https://github.com/ma2za/python-substack/actions/workflows/ci_publish.yml/badge.svg)
711

812
## Features
913

14+
- Inspect authentication and publication status from the terminal.
15+
- List publications and inspect, schedule, publish, or delete drafts.
16+
- Use stable JSON output in scripts and automation.
1017
- Create drafts and publish posts from Python.
1118
- Convert Markdown into Substack's editor document format.
1219
- Upload local images while rendering Markdown.
@@ -43,6 +50,49 @@ Use either `EMAIL` and `PASSWORD`, or cookie-based authentication with `COOKIES_
4350

4451
Newer Substack accounts may only have magic-link sign-in enabled. To set a password, sign out of Substack, choose "Sign in with password", then choose "Set a new password".
4552

53+
## CLI Operations
54+
55+
Check authentication, the selected publication, and subscriber count:
56+
57+
```bash
58+
substack status
59+
```
60+
61+
List available publications or target one without changing `.env`:
62+
63+
```bash
64+
substack publications list
65+
substack --publication-url https://example.substack.com drafts list
66+
```
67+
68+
List and inspect drafts:
69+
70+
```bash
71+
substack drafts list --limit 10
72+
substack drafts get 12345
73+
```
74+
75+
Schedule with a timezone-aware ISO 8601 timestamp, or remove a schedule:
76+
77+
```bash
78+
substack drafts schedule 12345 --at 2026-08-01T09:00:00+03:00
79+
substack drafts unschedule 12345
80+
```
81+
82+
Publishing and deletion prompt for confirmation. Use `--yes` for intentional non-interactive execution:
83+
84+
```bash
85+
substack drafts publish 12345 --no-send
86+
substack drafts delete 12345 --yes
87+
```
88+
89+
Global options must appear before the command. `--json` returns stable envelopes containing the raw Substack responses:
90+
91+
```bash
92+
substack --json drafts list
93+
substack --cookies cookies.json --json status
94+
```
95+
4696
## Quickstart
4797

4898
```python
@@ -78,7 +128,7 @@ print(result["draft"]["id"])
78128

79129
`create_draft_from_markdown` creates a draft by default. It only publishes when `publish=True` is passed.
80130

81-
## CLI
131+
## Content Publishing CLI
82132

83133
Check authentication without creating a draft:
84134

@@ -338,6 +388,8 @@ pytest
338388

339389
Live Substack tests are opt-in. Set `RUN_SUBSTACK_E2E=1` and configure credentials before running them.
340390

391+
The CLI operations smoke test is separately opt-in. Set `RUN_SUBSTACK_CLI_E2E=1` to create, schedule, unschedule, inspect, and delete a disposable draft. It never publishes the draft.
392+
341393
Release changes are tracked in [CHANGELOG.md](CHANGELOG.md).
342394

343395
## Disclaimer

docs/releases/0.1.26.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# python-substack 0.1.26
2+
3+
Version 0.1.26 adds a unified command-line interface for day-to-day Substack operations.
4+
5+
## Highlights
6+
7+
- Check authentication, publication selection, and subscriber count with `substack status`.
8+
- List publications and inspect drafts without writing Python code.
9+
- Schedule, unschedule, publish, and delete drafts with explicit safety controls.
10+
- Use `--json` for stable machine-readable output in scripts and automation.
11+
- Override the selected publication per command with `--publication-url`.
12+
- Run on Python 3.10 through 3.14 with pull-request CI and validated release artifacts.
13+
14+
Existing Python APIs, MCP tools, environment variables, and console commands remain available.
15+
16+
## Quick examples
17+
18+
```bash
19+
substack status
20+
substack drafts list
21+
substack --json drafts get 12345
22+
substack drafts schedule 12345 --at 2026-08-01T09:00:00+03:00
23+
substack drafts publish 12345 --no-send
24+
```
25+
26+
## Announcement copy
27+
28+
python-substack 0.1.26 is now a practical Substack operations CLI as well as a Python SDK. The new `substack` command can inspect publications and drafts, manage schedules, publish without sending email, and safely delete drafts. It also provides stable JSON output for automation. Install it with `pip install -U python-substack`.

0 commit comments

Comments
 (0)