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.
Files changed (119) hide show
  1. zernio_sdk-1.2.90/.dockerignore +63 -0
  2. zernio_sdk-1.2.90/.env.example +11 -0
  3. zernio_sdk-1.2.90/.github/.tmp/.generated-actions/run-pypi-publish-in-docker-container/action.yml +1 -0
  4. zernio_sdk-1.2.90/.github/CODEOWNERS +2 -0
  5. zernio_sdk-1.2.90/.github/workflows/generate.yml +180 -0
  6. zernio_sdk-1.2.90/.github/workflows/release-preview.yml +116 -0
  7. zernio_sdk-1.2.90/.github/workflows/release.yml +150 -0
  8. zernio_sdk-1.2.90/.github/workflows/test.yml +66 -0
  9. zernio_sdk-1.2.90/.gitignore +83 -0
  10. zernio_sdk-1.2.90/CHANGELOG.md +57 -0
  11. zernio_sdk-1.2.90/Dockerfile +21 -0
  12. zernio_sdk-1.2.90/Dockerfile.docker +22 -0
  13. zernio_sdk-1.2.90/LICENSE +190 -0
  14. zernio_sdk-1.2.90/PKG-INFO +541 -0
  15. zernio_sdk-1.2.90/README.md +490 -0
  16. zernio_sdk-1.2.90/claude-desktop-config.json +16 -0
  17. zernio_sdk-1.2.90/docs/HTTP_DEPLOYMENT.md +118 -0
  18. zernio_sdk-1.2.90/docs/MCP.md +93 -0
  19. zernio_sdk-1.2.90/examples/01_basic_usage.py +61 -0
  20. zernio_sdk-1.2.90/examples/02_schedule_from_csv.py +52 -0
  21. zernio_sdk-1.2.90/examples/03_cross_posting.py +74 -0
  22. zernio_sdk-1.2.90/examples/04_ai_content_generation.py +72 -0
  23. zernio_sdk-1.2.90/examples/05_download_tools.py +58 -0
  24. zernio_sdk-1.2.90/examples/data/sample_posts.csv +6 -0
  25. zernio_sdk-1.2.90/examples/publish_post.py +52 -0
  26. zernio_sdk-1.2.90/openapi.yaml +13985 -0
  27. zernio_sdk-1.2.90/pyproject.toml +176 -0
  28. zernio_sdk-1.2.90/railway.toml +9 -0
  29. zernio_sdk-1.2.90/scripts/generate_examples.py +233 -0
  30. zernio_sdk-1.2.90/scripts/generate_mcp_docs.py +38 -0
  31. zernio_sdk-1.2.90/scripts/generate_mcp_tools.py +396 -0
  32. zernio_sdk-1.2.90/scripts/generate_models.py +161 -0
  33. zernio_sdk-1.2.90/scripts/generate_readme_reference.py +271 -0
  34. zernio_sdk-1.2.90/scripts/generate_resources.py +535 -0
  35. zernio_sdk-1.2.90/src/late/__init__.py +64 -0
  36. zernio_sdk-1.2.90/src/late/ai/__init__.py +24 -0
  37. zernio_sdk-1.2.90/src/late/ai/content_generator.py +158 -0
  38. zernio_sdk-1.2.90/src/late/ai/protocols.py +75 -0
  39. zernio_sdk-1.2.90/src/late/ai/providers/__init__.py +7 -0
  40. zernio_sdk-1.2.90/src/late/ai/providers/openai.py +163 -0
  41. zernio_sdk-1.2.90/src/late/client/__init__.py +32 -0
  42. zernio_sdk-1.2.90/src/late/client/base.py +346 -0
  43. zernio_sdk-1.2.90/src/late/client/exceptions.py +99 -0
  44. zernio_sdk-1.2.90/src/late/client/late_client.py +100 -0
  45. zernio_sdk-1.2.90/src/late/client/rate_limiter.py +110 -0
  46. zernio_sdk-1.2.90/src/late/enums.py +309 -0
  47. zernio_sdk-1.2.90/src/late/mcp/__init__.py +5 -0
  48. zernio_sdk-1.2.90/src/late/mcp/__main__.py +6 -0
  49. zernio_sdk-1.2.90/src/late/mcp/auth.py +44 -0
  50. zernio_sdk-1.2.90/src/late/mcp/config.py +50 -0
  51. zernio_sdk-1.2.90/src/late/mcp/constants.py +23 -0
  52. zernio_sdk-1.2.90/src/late/mcp/generated_tools.py +3788 -0
  53. zernio_sdk-1.2.90/src/late/mcp/http_server.py +103 -0
  54. zernio_sdk-1.2.90/src/late/mcp/routes.py +110 -0
  55. zernio_sdk-1.2.90/src/late/mcp/server.py +814 -0
  56. zernio_sdk-1.2.90/src/late/mcp/tool_definitions.py +695 -0
  57. zernio_sdk-1.2.90/src/late/models/__init__.py +150 -0
  58. zernio_sdk-1.2.90/src/late/models/_generated/__init__.py +11 -0
  59. zernio_sdk-1.2.90/src/late/models/_generated/models.py +2442 -0
  60. zernio_sdk-1.2.90/src/late/models/responses.py +25 -0
  61. zernio_sdk-1.2.90/src/late/pipelines/__init__.py +14 -0
  62. zernio_sdk-1.2.90/src/late/pipelines/cross_poster.py +208 -0
  63. zernio_sdk-1.2.90/src/late/pipelines/csv_scheduler.py +174 -0
  64. zernio_sdk-1.2.90/src/late/resources/__init__.py +42 -0
  65. zernio_sdk-1.2.90/src/late/resources/_generated/__init__.py +55 -0
  66. zernio_sdk-1.2.90/src/late/resources/_generated/account_groups.py +111 -0
  67. zernio_sdk-1.2.90/src/late/resources/_generated/account_settings.py +169 -0
  68. zernio_sdk-1.2.90/src/late/resources/_generated/accounts.py +645 -0
  69. zernio_sdk-1.2.90/src/late/resources/_generated/analytics.py +401 -0
  70. zernio_sdk-1.2.90/src/late/resources/_generated/api_keys.py +103 -0
  71. zernio_sdk-1.2.90/src/late/resources/_generated/comments.py +337 -0
  72. zernio_sdk-1.2.90/src/late/resources/_generated/connect.py +757 -0
  73. zernio_sdk-1.2.90/src/late/resources/_generated/gmb_attributes.py +81 -0
  74. zernio_sdk-1.2.90/src/late/resources/_generated/gmb_food_menus.py +81 -0
  75. zernio_sdk-1.2.90/src/late/resources/_generated/gmb_location_details.py +119 -0
  76. zernio_sdk-1.2.90/src/late/resources/_generated/gmb_media.py +139 -0
  77. zernio_sdk-1.2.90/src/late/resources/_generated/gmb_place_actions.py +127 -0
  78. zernio_sdk-1.2.90/src/late/resources/_generated/inbox.py +547 -0
  79. zernio_sdk-1.2.90/src/late/resources/_generated/invites.py +69 -0
  80. zernio_sdk-1.2.90/src/late/resources/_generated/logs.py +153 -0
  81. zernio_sdk-1.2.90/src/late/resources/_generated/media.py +71 -0
  82. zernio_sdk-1.2.90/src/late/resources/_generated/messages.py +263 -0
  83. zernio_sdk-1.2.90/src/late/resources/_generated/posts.py +289 -0
  84. zernio_sdk-1.2.90/src/late/resources/_generated/profiles.py +141 -0
  85. zernio_sdk-1.2.90/src/late/resources/_generated/queue.py +219 -0
  86. zernio_sdk-1.2.90/src/late/resources/_generated/reddit.py +137 -0
  87. zernio_sdk-1.2.90/src/late/resources/_generated/reviews.py +141 -0
  88. zernio_sdk-1.2.90/src/late/resources/_generated/tools.py +223 -0
  89. zernio_sdk-1.2.90/src/late/resources/_generated/twitter_engagement.py +149 -0
  90. zernio_sdk-1.2.90/src/late/resources/_generated/usage.py +57 -0
  91. zernio_sdk-1.2.90/src/late/resources/_generated/users.py +65 -0
  92. zernio_sdk-1.2.90/src/late/resources/_generated/validate.py +121 -0
  93. zernio_sdk-1.2.90/src/late/resources/_generated/webhooks.py +207 -0
  94. zernio_sdk-1.2.90/src/late/resources/_generated/whatsapp.py +837 -0
  95. zernio_sdk-1.2.90/src/late/resources/_generated/whatsapp_phone_numbers.py +105 -0
  96. zernio_sdk-1.2.90/src/late/resources/accounts.py +113 -0
  97. zernio_sdk-1.2.90/src/late/resources/analytics.py +79 -0
  98. zernio_sdk-1.2.90/src/late/resources/base.py +82 -0
  99. zernio_sdk-1.2.90/src/late/resources/media.py +443 -0
  100. zernio_sdk-1.2.90/src/late/resources/posts.py +407 -0
  101. zernio_sdk-1.2.90/src/late/resources/profiles.py +190 -0
  102. zernio_sdk-1.2.90/src/late/resources/queue.py +179 -0
  103. zernio_sdk-1.2.90/src/late/resources/tools.py +350 -0
  104. zernio_sdk-1.2.90/src/late/resources/users.py +65 -0
  105. zernio_sdk-1.2.90/src/late/upload/__init__.py +74 -0
  106. zernio_sdk-1.2.90/src/late/upload/config.py +153 -0
  107. zernio_sdk-1.2.90/src/late/upload/direct.py +213 -0
  108. zernio_sdk-1.2.90/src/late/upload/protocols.py +273 -0
  109. zernio_sdk-1.2.90/src/late/upload/smart.py +204 -0
  110. zernio_sdk-1.2.90/src/late/upload/utils.py +70 -0
  111. zernio_sdk-1.2.90/src/late/upload/vercel/__init__.py +14 -0
  112. zernio_sdk-1.2.90/src/late/upload/vercel/client.py +158 -0
  113. zernio_sdk-1.2.90/src/late/upload/vercel/uploader.py +157 -0
  114. zernio_sdk-1.2.90/tests/__init__.py +1 -0
  115. zernio_sdk-1.2.90/tests/conftest.py +15 -0
  116. zernio_sdk-1.2.90/tests/test_client.py +107 -0
  117. zernio_sdk-1.2.90/tests/test_exhaustive.py +1358 -0
  118. zernio_sdk-1.2.90/tests/test_integration.py +968 -0
  119. 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
@@ -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,2 @@
1
+ # Default owners for everything in the repo
2
+ * @getlatedev
@@ -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