zernio-sdk 1.2.90__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.
- zernio_sdk-1.2.90/.dockerignore +63 -0
- zernio_sdk-1.2.90/.env.example +11 -0
- zernio_sdk-1.2.90/.github/.tmp/.generated-actions/run-pypi-publish-in-docker-container/action.yml +1 -0
- zernio_sdk-1.2.90/.github/CODEOWNERS +2 -0
- zernio_sdk-1.2.90/.github/workflows/generate.yml +180 -0
- zernio_sdk-1.2.90/.github/workflows/release-preview.yml +116 -0
- zernio_sdk-1.2.90/.github/workflows/release.yml +150 -0
- zernio_sdk-1.2.90/.github/workflows/test.yml +66 -0
- zernio_sdk-1.2.90/.gitignore +83 -0
- zernio_sdk-1.2.90/CHANGELOG.md +57 -0
- zernio_sdk-1.2.90/Dockerfile +21 -0
- zernio_sdk-1.2.90/Dockerfile.docker +22 -0
- zernio_sdk-1.2.90/LICENSE +190 -0
- zernio_sdk-1.2.90/PKG-INFO +541 -0
- zernio_sdk-1.2.90/README.md +490 -0
- zernio_sdk-1.2.90/claude-desktop-config.json +16 -0
- zernio_sdk-1.2.90/docs/HTTP_DEPLOYMENT.md +118 -0
- zernio_sdk-1.2.90/docs/MCP.md +93 -0
- zernio_sdk-1.2.90/examples/01_basic_usage.py +61 -0
- zernio_sdk-1.2.90/examples/02_schedule_from_csv.py +52 -0
- zernio_sdk-1.2.90/examples/03_cross_posting.py +74 -0
- zernio_sdk-1.2.90/examples/04_ai_content_generation.py +72 -0
- zernio_sdk-1.2.90/examples/05_download_tools.py +58 -0
- zernio_sdk-1.2.90/examples/data/sample_posts.csv +6 -0
- zernio_sdk-1.2.90/examples/publish_post.py +52 -0
- zernio_sdk-1.2.90/openapi.yaml +13985 -0
- zernio_sdk-1.2.90/pyproject.toml +176 -0
- zernio_sdk-1.2.90/railway.toml +9 -0
- zernio_sdk-1.2.90/scripts/generate_examples.py +233 -0
- zernio_sdk-1.2.90/scripts/generate_mcp_docs.py +38 -0
- zernio_sdk-1.2.90/scripts/generate_mcp_tools.py +396 -0
- zernio_sdk-1.2.90/scripts/generate_models.py +161 -0
- zernio_sdk-1.2.90/scripts/generate_readme_reference.py +271 -0
- zernio_sdk-1.2.90/scripts/generate_resources.py +535 -0
- zernio_sdk-1.2.90/src/late/__init__.py +64 -0
- zernio_sdk-1.2.90/src/late/ai/__init__.py +24 -0
- zernio_sdk-1.2.90/src/late/ai/content_generator.py +158 -0
- zernio_sdk-1.2.90/src/late/ai/protocols.py +75 -0
- zernio_sdk-1.2.90/src/late/ai/providers/__init__.py +7 -0
- zernio_sdk-1.2.90/src/late/ai/providers/openai.py +163 -0
- zernio_sdk-1.2.90/src/late/client/__init__.py +32 -0
- zernio_sdk-1.2.90/src/late/client/base.py +346 -0
- zernio_sdk-1.2.90/src/late/client/exceptions.py +99 -0
- zernio_sdk-1.2.90/src/late/client/late_client.py +100 -0
- zernio_sdk-1.2.90/src/late/client/rate_limiter.py +110 -0
- zernio_sdk-1.2.90/src/late/enums.py +309 -0
- zernio_sdk-1.2.90/src/late/mcp/__init__.py +5 -0
- zernio_sdk-1.2.90/src/late/mcp/__main__.py +6 -0
- zernio_sdk-1.2.90/src/late/mcp/auth.py +44 -0
- zernio_sdk-1.2.90/src/late/mcp/config.py +50 -0
- zernio_sdk-1.2.90/src/late/mcp/constants.py +23 -0
- zernio_sdk-1.2.90/src/late/mcp/generated_tools.py +3788 -0
- zernio_sdk-1.2.90/src/late/mcp/http_server.py +103 -0
- zernio_sdk-1.2.90/src/late/mcp/routes.py +110 -0
- zernio_sdk-1.2.90/src/late/mcp/server.py +814 -0
- zernio_sdk-1.2.90/src/late/mcp/tool_definitions.py +695 -0
- zernio_sdk-1.2.90/src/late/models/__init__.py +150 -0
- zernio_sdk-1.2.90/src/late/models/_generated/__init__.py +11 -0
- zernio_sdk-1.2.90/src/late/models/_generated/models.py +2442 -0
- zernio_sdk-1.2.90/src/late/models/responses.py +25 -0
- zernio_sdk-1.2.90/src/late/pipelines/__init__.py +14 -0
- zernio_sdk-1.2.90/src/late/pipelines/cross_poster.py +208 -0
- zernio_sdk-1.2.90/src/late/pipelines/csv_scheduler.py +174 -0
- zernio_sdk-1.2.90/src/late/resources/__init__.py +42 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/__init__.py +55 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/account_groups.py +111 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/account_settings.py +169 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/accounts.py +645 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/analytics.py +401 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/api_keys.py +103 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/comments.py +337 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/connect.py +757 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/gmb_attributes.py +81 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/gmb_food_menus.py +81 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/gmb_location_details.py +119 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/gmb_media.py +139 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/gmb_place_actions.py +127 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/inbox.py +547 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/invites.py +69 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/logs.py +153 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/media.py +71 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/messages.py +263 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/posts.py +289 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/profiles.py +141 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/queue.py +219 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/reddit.py +137 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/reviews.py +141 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/tools.py +223 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/twitter_engagement.py +149 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/usage.py +57 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/users.py +65 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/validate.py +121 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/webhooks.py +207 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/whatsapp.py +837 -0
- zernio_sdk-1.2.90/src/late/resources/_generated/whatsapp_phone_numbers.py +105 -0
- zernio_sdk-1.2.90/src/late/resources/accounts.py +113 -0
- zernio_sdk-1.2.90/src/late/resources/analytics.py +79 -0
- zernio_sdk-1.2.90/src/late/resources/base.py +82 -0
- zernio_sdk-1.2.90/src/late/resources/media.py +443 -0
- zernio_sdk-1.2.90/src/late/resources/posts.py +407 -0
- zernio_sdk-1.2.90/src/late/resources/profiles.py +190 -0
- zernio_sdk-1.2.90/src/late/resources/queue.py +179 -0
- zernio_sdk-1.2.90/src/late/resources/tools.py +350 -0
- zernio_sdk-1.2.90/src/late/resources/users.py +65 -0
- zernio_sdk-1.2.90/src/late/upload/__init__.py +74 -0
- zernio_sdk-1.2.90/src/late/upload/config.py +153 -0
- zernio_sdk-1.2.90/src/late/upload/direct.py +213 -0
- zernio_sdk-1.2.90/src/late/upload/protocols.py +273 -0
- zernio_sdk-1.2.90/src/late/upload/smart.py +204 -0
- zernio_sdk-1.2.90/src/late/upload/utils.py +70 -0
- zernio_sdk-1.2.90/src/late/upload/vercel/__init__.py +14 -0
- zernio_sdk-1.2.90/src/late/upload/vercel/client.py +158 -0
- zernio_sdk-1.2.90/src/late/upload/vercel/uploader.py +157 -0
- zernio_sdk-1.2.90/tests/__init__.py +1 -0
- zernio_sdk-1.2.90/tests/conftest.py +15 -0
- zernio_sdk-1.2.90/tests/test_client.py +107 -0
- zernio_sdk-1.2.90/tests/test_exhaustive.py +1358 -0
- zernio_sdk-1.2.90/tests/test_integration.py +968 -0
- zernio_sdk-1.2.90/uv.lock +1964 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Python cache
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
|
|
8
|
+
# Virtual environments
|
|
9
|
+
.venv/
|
|
10
|
+
venv/
|
|
11
|
+
ENV/
|
|
12
|
+
env/
|
|
13
|
+
|
|
14
|
+
# Environment variables
|
|
15
|
+
.env
|
|
16
|
+
.env.local
|
|
17
|
+
.env.*.local
|
|
18
|
+
|
|
19
|
+
# Git
|
|
20
|
+
.git/
|
|
21
|
+
.gitignore
|
|
22
|
+
.gitattributes
|
|
23
|
+
|
|
24
|
+
# Documentation (keep README.md for hatchling build)
|
|
25
|
+
docs/
|
|
26
|
+
examples/
|
|
27
|
+
|
|
28
|
+
# Tests
|
|
29
|
+
tests/
|
|
30
|
+
.pytest_cache/
|
|
31
|
+
.coverage
|
|
32
|
+
htmlcov/
|
|
33
|
+
.tox/
|
|
34
|
+
|
|
35
|
+
# IDE
|
|
36
|
+
.vscode/
|
|
37
|
+
.idea/
|
|
38
|
+
*.swp
|
|
39
|
+
*.swo
|
|
40
|
+
*~
|
|
41
|
+
|
|
42
|
+
# Build artifacts
|
|
43
|
+
build/
|
|
44
|
+
dist/
|
|
45
|
+
*.egg-info/
|
|
46
|
+
.eggs/
|
|
47
|
+
|
|
48
|
+
# Type checking
|
|
49
|
+
.mypy_cache/
|
|
50
|
+
.pytype/
|
|
51
|
+
.pyre/
|
|
52
|
+
.dmypy.json
|
|
53
|
+
|
|
54
|
+
# Linting
|
|
55
|
+
.ruff_cache/
|
|
56
|
+
|
|
57
|
+
# CI/CD
|
|
58
|
+
.github/
|
|
59
|
+
.gitlab-ci.yml
|
|
60
|
+
|
|
61
|
+
# Misc
|
|
62
|
+
*.log
|
|
63
|
+
.DS_Store
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Late API Configuration (for STDIO mode with Claude Desktop)
|
|
2
|
+
# For HTTP/SSE mode, users provide their API key via request headers
|
|
3
|
+
LATE_API_KEY=your_api_key_here
|
|
4
|
+
|
|
5
|
+
# Server Configuration (optional, has defaults)
|
|
6
|
+
HOST=0.0.0.0
|
|
7
|
+
PORT=8080
|
|
8
|
+
|
|
9
|
+
# AI Provider (optional - for content generation)
|
|
10
|
+
OPENAI_API_KEY=sk-your_openai_key_here
|
|
11
|
+
# ANTHROPIC_API_KEY=sk-ant-your_anthropic_key_here
|
zernio_sdk-1.2.90/.github/.tmp/.generated-actions/run-pypi-publish-in-docker-container/action.yml
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"name": "🏃", "description": "Run Docker container to upload Python distribution packages to PyPI", "inputs": {"user": {"description": "PyPI user", "required": false}, "password": {"description": "Password for your PyPI user or an access token", "required": false}, "repository-url": {"description": "The repository URL to use", "required": false}, "packages-dir": {"description": "The target directory for distribution", "required": false}, "verify-metadata": {"description": "Check metadata before uploading", "required": false}, "skip-existing": {"description": "Do not fail if a Python package distribution exists in the target package index", "required": false}, "verbose": {"description": "Show verbose output.", "required": false}, "print-hash": {"description": "Show hash values of files to be uploaded", "required": false}, "attestations": {"description": "[EXPERIMENTAL] Enable experimental support for PEP 740 attestations. Only works with PyPI and TestPyPI via Trusted Publishing.", "required": false}}, "runs": {"using": "docker", "image": "docker://ghcr.io/pypa/gh-action-pypi-publish:release-v1"}}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
name: Regenerate
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
# Triggered by the API repository when OpenAPI spec changes
|
|
5
|
+
repository_dispatch:
|
|
6
|
+
types: [openapi-updated]
|
|
7
|
+
|
|
8
|
+
# Allow manual trigger
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
generate:
|
|
13
|
+
name: Regenerate from OpenAPI
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
permissions:
|
|
16
|
+
contents: write
|
|
17
|
+
id-token: write
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- name: Checkout repository
|
|
21
|
+
uses: actions/checkout@v4
|
|
22
|
+
with:
|
|
23
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
24
|
+
fetch-depth: 0
|
|
25
|
+
|
|
26
|
+
- name: Install uv
|
|
27
|
+
uses: astral-sh/setup-uv@v4
|
|
28
|
+
with:
|
|
29
|
+
version: "latest"
|
|
30
|
+
|
|
31
|
+
- name: Set up Python
|
|
32
|
+
run: uv python install 3.11
|
|
33
|
+
|
|
34
|
+
- name: Install dependencies
|
|
35
|
+
run: uv sync --all-extras
|
|
36
|
+
|
|
37
|
+
- name: Fetch latest OpenAPI spec
|
|
38
|
+
run: |
|
|
39
|
+
curl -f -o openapi.yaml https://zernio.com/openapi.yaml
|
|
40
|
+
echo "Fetched OpenAPI spec:"
|
|
41
|
+
head -20 openapi.yaml
|
|
42
|
+
|
|
43
|
+
- name: Generate resources
|
|
44
|
+
run: |
|
|
45
|
+
echo "Starting resource generation..."
|
|
46
|
+
uv run python scripts/generate_resources.py
|
|
47
|
+
echo "Resource generation complete."
|
|
48
|
+
|
|
49
|
+
- name: Generate models
|
|
50
|
+
run: |
|
|
51
|
+
echo "Starting model generation..."
|
|
52
|
+
uv run python scripts/generate_models.py || echo "Model generation skipped (requires local docs repo)"
|
|
53
|
+
echo "Model generation complete."
|
|
54
|
+
|
|
55
|
+
- name: Generate MCP tools
|
|
56
|
+
run: |
|
|
57
|
+
echo "Starting MCP tool generation..."
|
|
58
|
+
uv run --with pyyaml python scripts/generate_mcp_tools.py
|
|
59
|
+
echo "MCP tool generation complete."
|
|
60
|
+
|
|
61
|
+
- name: Generate README SDK Reference
|
|
62
|
+
run: |
|
|
63
|
+
echo "Starting README SDK Reference generation..."
|
|
64
|
+
uv run python scripts/generate_readme_reference.py
|
|
65
|
+
echo "README SDK Reference generation complete."
|
|
66
|
+
|
|
67
|
+
- name: Run linter (fix formatting)
|
|
68
|
+
run: |
|
|
69
|
+
uv run ruff check --fix src/late/resources/_generated/ || true
|
|
70
|
+
uv run ruff format src/late/resources/_generated/ || true
|
|
71
|
+
uv run ruff check --fix src/late/mcp/generated_tools.py || true
|
|
72
|
+
uv run ruff format src/late/mcp/generated_tools.py || true
|
|
73
|
+
|
|
74
|
+
- name: Run tests
|
|
75
|
+
run: uv run pytest tests -v --tb=short
|
|
76
|
+
|
|
77
|
+
- name: Check for changes
|
|
78
|
+
id: changes
|
|
79
|
+
run: |
|
|
80
|
+
git add -A
|
|
81
|
+
if git diff --staged --quiet; then
|
|
82
|
+
echo "has_changes=false" >> $GITHUB_OUTPUT
|
|
83
|
+
else
|
|
84
|
+
echo "has_changes=true" >> $GITHUB_OUTPUT
|
|
85
|
+
echo "Changes detected:"
|
|
86
|
+
git diff --staged --name-only
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
- name: Bump version and commit
|
|
90
|
+
if: steps.changes.outputs.has_changes == 'true'
|
|
91
|
+
id: version
|
|
92
|
+
run: |
|
|
93
|
+
git config user.name "github-actions[bot]"
|
|
94
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
95
|
+
|
|
96
|
+
# Get current version
|
|
97
|
+
CURRENT_VERSION=$(uv run python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
|
|
98
|
+
echo "Current version: $CURRENT_VERSION"
|
|
99
|
+
|
|
100
|
+
# Bump patch version
|
|
101
|
+
IFS='.' read -ra VERSION_PARTS <<< "$CURRENT_VERSION"
|
|
102
|
+
MAJOR="${VERSION_PARTS[0]}"
|
|
103
|
+
MINOR="${VERSION_PARTS[1]}"
|
|
104
|
+
PATCH="${VERSION_PARTS[2]}"
|
|
105
|
+
NEW_PATCH=$((PATCH + 1))
|
|
106
|
+
NEW_VERSION="$MAJOR.$MINOR.$NEW_PATCH"
|
|
107
|
+
echo "New version: $NEW_VERSION"
|
|
108
|
+
|
|
109
|
+
# Update version in pyproject.toml
|
|
110
|
+
sed -i "s/version = \"$CURRENT_VERSION\"/version = \"$NEW_VERSION\"/" pyproject.toml
|
|
111
|
+
|
|
112
|
+
# Also update __init__.py if it has a version
|
|
113
|
+
sed -i "s/__version__ = \".*\"/__version__ = \"$NEW_VERSION\"/" src/late/__init__.py || true
|
|
114
|
+
|
|
115
|
+
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
|
|
116
|
+
|
|
117
|
+
# Stage all changes
|
|
118
|
+
git add -A
|
|
119
|
+
|
|
120
|
+
# Commit
|
|
121
|
+
git commit -m "chore: regenerate from OpenAPI spec
|
|
122
|
+
|
|
123
|
+
- Auto-generated SDK updates
|
|
124
|
+
- Version: $NEW_VERSION"
|
|
125
|
+
git push
|
|
126
|
+
|
|
127
|
+
- name: Build package
|
|
128
|
+
if: steps.changes.outputs.has_changes == 'true'
|
|
129
|
+
run: uv build
|
|
130
|
+
|
|
131
|
+
- name: Create GitHub Release
|
|
132
|
+
if: steps.changes.outputs.has_changes == 'true'
|
|
133
|
+
uses: softprops/action-gh-release@v2
|
|
134
|
+
with:
|
|
135
|
+
tag_name: v${{ steps.version.outputs.new_version }}
|
|
136
|
+
name: v${{ steps.version.outputs.new_version }}
|
|
137
|
+
files: dist/*
|
|
138
|
+
generate_release_notes: true
|
|
139
|
+
body: |
|
|
140
|
+
## Auto-generated SDK Update
|
|
141
|
+
|
|
142
|
+
This release was automatically generated from the latest OpenAPI spec.
|
|
143
|
+
|
|
144
|
+
This package is published under two names:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
pip install late-sdk==${{ steps.version.outputs.new_version }}
|
|
148
|
+
# or
|
|
149
|
+
pip install zernio-sdk==${{ steps.version.outputs.new_version }}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
- name: Publish late-sdk to PyPI
|
|
153
|
+
if: steps.changes.outputs.has_changes == 'true'
|
|
154
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
155
|
+
with:
|
|
156
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
157
|
+
|
|
158
|
+
- name: Swap package name to zernio-sdk
|
|
159
|
+
if: steps.changes.outputs.has_changes == 'true'
|
|
160
|
+
run: |
|
|
161
|
+
python3 -c "
|
|
162
|
+
import re
|
|
163
|
+
with open('pyproject.toml', 'r') as f:
|
|
164
|
+
content = f.read()
|
|
165
|
+
content = re.sub(r'name = \"late-sdk\"', 'name = \"zernio-sdk\"', content)
|
|
166
|
+
with open('pyproject.toml', 'w') as f:
|
|
167
|
+
f.write(content)
|
|
168
|
+
"
|
|
169
|
+
|
|
170
|
+
- name: Build as zernio-sdk
|
|
171
|
+
if: steps.changes.outputs.has_changes == 'true'
|
|
172
|
+
run: |
|
|
173
|
+
rm -rf dist/
|
|
174
|
+
uv build
|
|
175
|
+
|
|
176
|
+
- name: Publish zernio-sdk to PyPI
|
|
177
|
+
if: steps.changes.outputs.has_changes == 'true'
|
|
178
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
179
|
+
with:
|
|
180
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Preview release info on PRs to main
|
|
2
|
+
name: Release Preview
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
pull_request:
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
preview:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
pull-requests: write
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0
|
|
19
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v4
|
|
23
|
+
with:
|
|
24
|
+
version: "latest"
|
|
25
|
+
|
|
26
|
+
- name: Set up Python
|
|
27
|
+
run: uv python install 3.11
|
|
28
|
+
|
|
29
|
+
- name: Get version from pyproject.toml
|
|
30
|
+
id: version
|
|
31
|
+
run: |
|
|
32
|
+
VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
|
|
33
|
+
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
34
|
+
|
|
35
|
+
- name: Check if tag exists
|
|
36
|
+
id: check_tag
|
|
37
|
+
run: |
|
|
38
|
+
if git rev-parse "v${{ steps.version.outputs.version }}" >/dev/null 2>&1; then
|
|
39
|
+
echo "exists=true" >> $GITHUB_OUTPUT
|
|
40
|
+
else
|
|
41
|
+
echo "exists=false" >> $GITHUB_OUTPUT
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
- name: Check PyPI for existing version
|
|
45
|
+
id: check_pypi
|
|
46
|
+
run: |
|
|
47
|
+
VERSION="${{ steps.version.outputs.version }}"
|
|
48
|
+
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://pypi.org/pypi/late-sdk/$VERSION/json")
|
|
49
|
+
if [ "$HTTP_STATUS" = "200" ]; then
|
|
50
|
+
echo "exists=true" >> $GITHUB_OUTPUT
|
|
51
|
+
else
|
|
52
|
+
echo "exists=false" >> $GITHUB_OUTPUT
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
- name: Create PR Comment
|
|
56
|
+
uses: actions/github-script@v7
|
|
57
|
+
with:
|
|
58
|
+
script: |
|
|
59
|
+
const version = '${{ steps.version.outputs.version }}';
|
|
60
|
+
const tagExists = '${{ steps.check_tag.outputs.exists }}' === 'true';
|
|
61
|
+
const pypiExists = '${{ steps.check_pypi.outputs.exists }}' === 'true';
|
|
62
|
+
|
|
63
|
+
let body = '## 📦 Release Preview\n\n';
|
|
64
|
+
body += '| Item | Value |\n';
|
|
65
|
+
body += '|------|-------|\n';
|
|
66
|
+
body += `| Version | \`${version}\` |\n`;
|
|
67
|
+
body += `| Git tag exists | ${tagExists ? '✅ Yes' : '❌ No'} |\n`;
|
|
68
|
+
body += `| PyPI version exists | ${pypiExists ? '✅ Yes' : '❌ No'} |\n\n`;
|
|
69
|
+
|
|
70
|
+
if (tagExists || pypiExists) {
|
|
71
|
+
body += '### ⏭️ No Release\n\n';
|
|
72
|
+
if (tagExists && pypiExists) {
|
|
73
|
+
body += `Version \`${version}\` already exists on both GitHub and PyPI.\n\n`;
|
|
74
|
+
} else if (tagExists) {
|
|
75
|
+
body += `Git tag \`v${version}\` already exists.\n\n`;
|
|
76
|
+
} else {
|
|
77
|
+
body += `Version \`${version}\` already exists on PyPI.\n\n`;
|
|
78
|
+
}
|
|
79
|
+
body += '> 💡 **To create a new release**, update `version` in `pyproject.toml`\n';
|
|
80
|
+
} else {
|
|
81
|
+
body += '### 🚀 New Release\n\n';
|
|
82
|
+
body += 'When this PR is merged, the following will happen:\n\n';
|
|
83
|
+
body += `1. ✅ Create GitHub Release \`v${version}\`\n`;
|
|
84
|
+
body += `2. ✅ Publish to PyPI as \`late-sdk==${version}\`\n\n`;
|
|
85
|
+
body += '```bash\n';
|
|
86
|
+
body += `pip install late-sdk==${version}\n`;
|
|
87
|
+
body += '```\n';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Find existing comment
|
|
91
|
+
const { data: comments } = await github.rest.issues.listComments({
|
|
92
|
+
owner: context.repo.owner,
|
|
93
|
+
repo: context.repo.repo,
|
|
94
|
+
issue_number: context.issue.number,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const botComment = comments.find(comment =>
|
|
98
|
+
comment.user.type === 'Bot' &&
|
|
99
|
+
comment.body.includes('📦 Release Preview')
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
if (botComment) {
|
|
103
|
+
await github.rest.issues.updateComment({
|
|
104
|
+
owner: context.repo.owner,
|
|
105
|
+
repo: context.repo.repo,
|
|
106
|
+
comment_id: botComment.id,
|
|
107
|
+
body: body
|
|
108
|
+
});
|
|
109
|
+
} else {
|
|
110
|
+
await github.rest.issues.createComment({
|
|
111
|
+
owner: context.repo.owner,
|
|
112
|
+
repo: context.repo.repo,
|
|
113
|
+
issue_number: context.issue.number,
|
|
114
|
+
body: body
|
|
115
|
+
});
|
|
116
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
release:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
contents: write
|
|
12
|
+
id-token: write
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
with:
|
|
17
|
+
fetch-depth: 0
|
|
18
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
19
|
+
|
|
20
|
+
- name: Install uv
|
|
21
|
+
uses: astral-sh/setup-uv@v4
|
|
22
|
+
with:
|
|
23
|
+
version: "latest"
|
|
24
|
+
|
|
25
|
+
- name: Set up Python
|
|
26
|
+
run: uv python install 3.11
|
|
27
|
+
|
|
28
|
+
- name: Install dependencies
|
|
29
|
+
run: uv sync --all-extras
|
|
30
|
+
|
|
31
|
+
- name: Run tests
|
|
32
|
+
run: uv run pytest tests -v --tb=short
|
|
33
|
+
|
|
34
|
+
- name: Get version from pyproject.toml
|
|
35
|
+
id: version
|
|
36
|
+
run: |
|
|
37
|
+
VERSION=$(uv run python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
|
|
38
|
+
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
39
|
+
echo "::notice::📦 Version in pyproject.toml: $VERSION"
|
|
40
|
+
|
|
41
|
+
- name: Check if tag exists
|
|
42
|
+
id: check_tag
|
|
43
|
+
run: |
|
|
44
|
+
if git rev-parse "v${{ steps.version.outputs.version }}" >/dev/null 2>&1; then
|
|
45
|
+
echo "exists=true" >> $GITHUB_OUTPUT
|
|
46
|
+
echo "::notice::🔄 Tag v${{ steps.version.outputs.version }} already exists - skipping release"
|
|
47
|
+
else
|
|
48
|
+
echo "exists=false" >> $GITHUB_OUTPUT
|
|
49
|
+
echo "::notice::🚀 New version detected! Will create release v${{ steps.version.outputs.version }}"
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
- name: Check PyPI for existing version
|
|
53
|
+
id: check_pypi
|
|
54
|
+
if: steps.check_tag.outputs.exists == 'false'
|
|
55
|
+
run: |
|
|
56
|
+
VERSION="${{ steps.version.outputs.version }}"
|
|
57
|
+
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://pypi.org/pypi/getlate/$VERSION/json")
|
|
58
|
+
if [ "$HTTP_STATUS" = "200" ]; then
|
|
59
|
+
echo "::error::❌ Version $VERSION already exists on PyPI! Bump the version in pyproject.toml"
|
|
60
|
+
exit 1
|
|
61
|
+
else
|
|
62
|
+
echo "::notice::✅ Version $VERSION not found on PyPI - ready to publish"
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
- name: Release Summary
|
|
66
|
+
run: |
|
|
67
|
+
echo "## 📋 Release Summary" >> $GITHUB_STEP_SUMMARY
|
|
68
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
69
|
+
echo "| Item | Value |" >> $GITHUB_STEP_SUMMARY
|
|
70
|
+
echo "|------|-------|" >> $GITHUB_STEP_SUMMARY
|
|
71
|
+
echo "| Version | \`${{ steps.version.outputs.version }}\` |" >> $GITHUB_STEP_SUMMARY
|
|
72
|
+
echo "| Tag exists | ${{ steps.check_tag.outputs.exists }} |" >> $GITHUB_STEP_SUMMARY
|
|
73
|
+
if [ "${{ steps.check_tag.outputs.exists }}" = "true" ]; then
|
|
74
|
+
echo "| Action | ⏭️ **Skipped** (version already released) |" >> $GITHUB_STEP_SUMMARY
|
|
75
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
76
|
+
echo "> 💡 To release a new version, update \`version\` in \`pyproject.toml\`" >> $GITHUB_STEP_SUMMARY
|
|
77
|
+
else
|
|
78
|
+
echo "| Action | 🚀 **New Release** |" >> $GITHUB_STEP_SUMMARY
|
|
79
|
+
echo "| GitHub Release | v${{ steps.version.outputs.version }} |" >> $GITHUB_STEP_SUMMARY
|
|
80
|
+
echo "| PyPI | getlate==${{ steps.version.outputs.version }} |" >> $GITHUB_STEP_SUMMARY
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
- name: Build package
|
|
84
|
+
if: steps.check_tag.outputs.exists == 'false'
|
|
85
|
+
run: |
|
|
86
|
+
uv build
|
|
87
|
+
echo "::notice::📦 Built: $(ls dist/)"
|
|
88
|
+
|
|
89
|
+
- name: Create GitHub Release
|
|
90
|
+
if: steps.check_tag.outputs.exists == 'false'
|
|
91
|
+
uses: softprops/action-gh-release@v2
|
|
92
|
+
with:
|
|
93
|
+
tag_name: v${{ steps.version.outputs.version }}
|
|
94
|
+
name: v${{ steps.version.outputs.version }}
|
|
95
|
+
files: dist/*
|
|
96
|
+
generate_release_notes: true
|
|
97
|
+
body: |
|
|
98
|
+
## Install
|
|
99
|
+
|
|
100
|
+
This package is published under two names:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
pip install late-sdk==${{ steps.version.outputs.version }}
|
|
104
|
+
# or
|
|
105
|
+
pip install zernio-sdk==${{ steps.version.outputs.version }}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
- name: Publish late-sdk to PyPI
|
|
109
|
+
if: steps.check_tag.outputs.exists == 'false'
|
|
110
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
111
|
+
with:
|
|
112
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
113
|
+
|
|
114
|
+
- name: Swap package name to zernio-sdk
|
|
115
|
+
if: steps.check_tag.outputs.exists == 'false'
|
|
116
|
+
run: |
|
|
117
|
+
python3 -c "
|
|
118
|
+
import re
|
|
119
|
+
with open('pyproject.toml', 'r') as f:
|
|
120
|
+
content = f.read()
|
|
121
|
+
content = re.sub(r'name = \"late-sdk\"', 'name = \"zernio-sdk\"', content)
|
|
122
|
+
with open('pyproject.toml', 'w') as f:
|
|
123
|
+
f.write(content)
|
|
124
|
+
"
|
|
125
|
+
|
|
126
|
+
- name: Build as zernio-sdk
|
|
127
|
+
if: steps.check_tag.outputs.exists == 'false'
|
|
128
|
+
run: |
|
|
129
|
+
rm -rf dist/
|
|
130
|
+
uv build
|
|
131
|
+
|
|
132
|
+
- name: Publish zernio-sdk to PyPI
|
|
133
|
+
if: steps.check_tag.outputs.exists == 'false'
|
|
134
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
135
|
+
with:
|
|
136
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
137
|
+
|
|
138
|
+
- name: Post-release Summary
|
|
139
|
+
if: steps.check_tag.outputs.exists == 'false'
|
|
140
|
+
run: |
|
|
141
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
142
|
+
echo "## ✅ Release Complete" >> $GITHUB_STEP_SUMMARY
|
|
143
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
144
|
+
echo "- 🏷️ GitHub: [v${{ steps.version.outputs.version }}](https://github.com/${{ github.repository }}/releases/tag/v${{ steps.version.outputs.version }})" >> $GITHUB_STEP_SUMMARY
|
|
145
|
+
echo "- 📦 PyPI: [getlate ${{ steps.version.outputs.version }}](https://pypi.org/project/getlate/${{ steps.version.outputs.version }}/)" >> $GITHUB_STEP_SUMMARY
|
|
146
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
147
|
+
echo "Install with:" >> $GITHUB_STEP_SUMMARY
|
|
148
|
+
echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
|
|
149
|
+
echo "pip install getlate==${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
|
|
150
|
+
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [develop, main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [develop, main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
strategy:
|
|
15
|
+
matrix:
|
|
16
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
with:
|
|
21
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
22
|
+
|
|
23
|
+
- name: Install uv
|
|
24
|
+
uses: astral-sh/setup-uv@v4
|
|
25
|
+
with:
|
|
26
|
+
version: "latest"
|
|
27
|
+
|
|
28
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
29
|
+
run: uv python install ${{ matrix.python-version }}
|
|
30
|
+
|
|
31
|
+
- name: Install dependencies
|
|
32
|
+
run: uv sync --all-extras
|
|
33
|
+
|
|
34
|
+
- name: Run linting
|
|
35
|
+
run: uv run ruff check src tests
|
|
36
|
+
|
|
37
|
+
- name: Run type checking
|
|
38
|
+
run: uv run mypy src --ignore-missing-imports
|
|
39
|
+
|
|
40
|
+
- name: Run tests
|
|
41
|
+
run: uv run pytest tests -v --tb=short
|
|
42
|
+
|
|
43
|
+
build:
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
needs: test
|
|
46
|
+
permissions:
|
|
47
|
+
contents: read
|
|
48
|
+
|
|
49
|
+
steps:
|
|
50
|
+
- uses: actions/checkout@v4
|
|
51
|
+
with:
|
|
52
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
53
|
+
|
|
54
|
+
- name: Install uv
|
|
55
|
+
uses: astral-sh/setup-uv@v4
|
|
56
|
+
with:
|
|
57
|
+
version: "latest"
|
|
58
|
+
|
|
59
|
+
- name: Build package
|
|
60
|
+
run: uv build
|
|
61
|
+
|
|
62
|
+
- name: Upload build artifacts
|
|
63
|
+
uses: actions/upload-artifact@v4
|
|
64
|
+
with:
|
|
65
|
+
name: dist
|
|
66
|
+
path: dist/
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
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
|
+
*.egg-info/
|
|
24
|
+
.installed.cfg
|
|
25
|
+
*.egg
|
|
26
|
+
|
|
27
|
+
# PyInstaller
|
|
28
|
+
*.manifest
|
|
29
|
+
*.spec
|
|
30
|
+
|
|
31
|
+
# Installer logs
|
|
32
|
+
pip-log.txt
|
|
33
|
+
pip-delete-this-directory.txt
|
|
34
|
+
|
|
35
|
+
# Unit test / coverage reports
|
|
36
|
+
htmlcov/
|
|
37
|
+
.tox/
|
|
38
|
+
.nox/
|
|
39
|
+
.coverage
|
|
40
|
+
.coverage.*
|
|
41
|
+
.cache
|
|
42
|
+
nosetests.xml
|
|
43
|
+
coverage.xml
|
|
44
|
+
*.cover
|
|
45
|
+
*.py,cover
|
|
46
|
+
.hypothesis/
|
|
47
|
+
.pytest_cache/
|
|
48
|
+
|
|
49
|
+
# Translations
|
|
50
|
+
*.mo
|
|
51
|
+
*.pot
|
|
52
|
+
|
|
53
|
+
# Environments
|
|
54
|
+
.env
|
|
55
|
+
.env.local
|
|
56
|
+
.venv
|
|
57
|
+
env/
|
|
58
|
+
venv/
|
|
59
|
+
ENV/
|
|
60
|
+
env.bak/
|
|
61
|
+
venv.bak/
|
|
62
|
+
|
|
63
|
+
# IDEs
|
|
64
|
+
.idea/
|
|
65
|
+
.vscode/
|
|
66
|
+
*.swp
|
|
67
|
+
*.swo
|
|
68
|
+
*~
|
|
69
|
+
|
|
70
|
+
# mypy
|
|
71
|
+
.mypy_cache/
|
|
72
|
+
.dmypy.json
|
|
73
|
+
dmypy.json
|
|
74
|
+
|
|
75
|
+
# ruff
|
|
76
|
+
.ruff_cache/
|
|
77
|
+
|
|
78
|
+
# OS
|
|
79
|
+
.DS_Store
|
|
80
|
+
Thumbs.db
|
|
81
|
+
|
|
82
|
+
# Project specific
|
|
83
|
+
*.log
|