palabra-ai 0.1.2__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 (82) hide show
  1. palabra_ai-0.1.2/.github/dependabot.yml +11 -0
  2. palabra_ai-0.1.2/.github/workflows/release.yml +183 -0
  3. palabra_ai-0.1.2/.github/workflows/sync-tag-to-version.yml +108 -0
  4. palabra_ai-0.1.2/.github/workflows/test.yml +47 -0
  5. palabra_ai-0.1.2/.github/workflows/version-bump.yml +128 -0
  6. palabra_ai-0.1.2/.gitignore +156 -0
  7. palabra_ai-0.1.2/.pre-commit-config.yaml +16 -0
  8. palabra_ai-0.1.2/CONTRIBUTING.md +229 -0
  9. palabra_ai-0.1.2/LICENSE +21 -0
  10. palabra_ai-0.1.2/MANIFEST.in +10 -0
  11. palabra_ai-0.1.2/Makefile +11 -0
  12. palabra_ai-0.1.2/PKG-INFO +390 -0
  13. palabra_ai-0.1.2/README.md +352 -0
  14. palabra_ai-0.1.2/RELEASE.md +92 -0
  15. palabra_ai-0.1.2/__init__.py +0 -0
  16. palabra_ai-0.1.2/docs/installation.md +148 -0
  17. palabra_ai-0.1.2/examples/audio_file.py +9 -0
  18. palabra_ai-0.1.2/examples/buffer.py +19 -0
  19. palabra_ai-0.1.2/examples/ffmpeg.py +43 -0
  20. palabra_ai-0.1.2/examples/microphone.py +9 -0
  21. palabra_ai-0.1.2/examples/speech/ar.mp3 +0 -0
  22. palabra_ai-0.1.2/examples/speech/en.mp3 +0 -0
  23. palabra_ai-0.1.2/examples/speech/es.mp3 +0 -0
  24. palabra_ai-0.1.2/examples/transcriptions.py +34 -0
  25. palabra_ai-0.1.2/palabra_ai/__init__.py +134 -0
  26. palabra_ai-0.1.2/palabra_ai/adapter/__init__.py +3 -0
  27. palabra_ai-0.1.2/palabra_ai/adapter/_common.py +23 -0
  28. palabra_ai-0.1.2/palabra_ai/adapter/buffer.py +165 -0
  29. palabra_ai-0.1.2/palabra_ai/adapter/device.py +380 -0
  30. palabra_ai-0.1.2/palabra_ai/adapter/file.py +162 -0
  31. palabra_ai-0.1.2/palabra_ai/base/__init__.py +0 -0
  32. palabra_ai-0.1.2/palabra_ai/base/adapter.py +50 -0
  33. palabra_ai-0.1.2/palabra_ai/base/enum.py +40 -0
  34. palabra_ai-0.1.2/palabra_ai/base/message.py +363 -0
  35. palabra_ai-0.1.2/palabra_ai/base/task.py +82 -0
  36. palabra_ai-0.1.2/palabra_ai/client.py +179 -0
  37. palabra_ai-0.1.2/palabra_ai/config.py +408 -0
  38. palabra_ai-0.1.2/palabra_ai/exc.py +6 -0
  39. palabra_ai-0.1.2/palabra_ai/internal/__init__.py +0 -0
  40. palabra_ai-0.1.2/palabra_ai/internal/audio.py +170 -0
  41. palabra_ai-0.1.2/palabra_ai/internal/buffer.py +135 -0
  42. palabra_ai-0.1.2/palabra_ai/internal/device.py +289 -0
  43. palabra_ai-0.1.2/palabra_ai/internal/realtime.py +195 -0
  44. palabra_ai-0.1.2/palabra_ai/internal/rest.py +69 -0
  45. palabra_ai-0.1.2/palabra_ai/internal/webrtc.py +374 -0
  46. palabra_ai-0.1.2/palabra_ai/internal/ws.py +180 -0
  47. palabra_ai-0.1.2/palabra_ai/lang.py +143 -0
  48. palabra_ai-0.1.2/palabra_ai/py.typed +0 -0
  49. palabra_ai-0.1.2/palabra_ai/task/__init__.py +1 -0
  50. palabra_ai-0.1.2/palabra_ai/task/buffer.py +38 -0
  51. palabra_ai-0.1.2/palabra_ai/task/logger.py +110 -0
  52. palabra_ai-0.1.2/palabra_ai/task/manager.py +238 -0
  53. palabra_ai-0.1.2/palabra_ai/task/realtime.py +106 -0
  54. palabra_ai-0.1.2/palabra_ai/task/receiver.py +146 -0
  55. palabra_ai-0.1.2/palabra_ai/task/sender.py +102 -0
  56. palabra_ai-0.1.2/palabra_ai/task/transcription.py +107 -0
  57. palabra_ai-0.1.2/palabra_ai/types.py +23 -0
  58. palabra_ai-0.1.2/palabra_ai/util/__init__.py +0 -0
  59. palabra_ai-0.1.2/palabra_ai/util/emoji.py +4 -0
  60. palabra_ai-0.1.2/palabra_ai/util/fanout_queue.py +62 -0
  61. palabra_ai-0.1.2/palabra_ai/util/frame_counter.py +67 -0
  62. palabra_ai-0.1.2/palabra_ai/util/logger.py +33 -0
  63. palabra_ai-0.1.2/palabra_ai/util/orjson.py +39 -0
  64. palabra_ai-0.1.2/palabra_ai/util/secret_strip.py +40 -0
  65. palabra_ai-0.1.2/palabra_ai/util/wrapper.py +34 -0
  66. palabra_ai-0.1.2/pyproject.toml +115 -0
  67. palabra_ai-0.1.2/tests/__init__.py +1 -0
  68. palabra_ai-0.1.2/tests/base/__init__.py +1 -0
  69. palabra_ai-0.1.2/tests/base/test_task.py +79 -0
  70. palabra_ai-0.1.2/tests/conftest.py +149 -0
  71. palabra_ai-0.1.2/tests/internal/conftest.py +36 -0
  72. palabra_ai-0.1.2/tests/internal/test_audio.py +212 -0
  73. palabra_ai-0.1.2/tests/internal/test_buffer.py +149 -0
  74. palabra_ai-0.1.2/tests/internal/test_device.py +375 -0
  75. palabra_ai-0.1.2/tests/internal/test_rest.py +56 -0
  76. palabra_ai-0.1.2/tests/internal/test_webrtc.py +313 -0
  77. palabra_ai-0.1.2/tests/task/__init__.py +1 -0
  78. palabra_ai-0.1.2/tests/test_basic.py +19 -0
  79. palabra_ai-0.1.2/tests/test_event.py +97 -0
  80. palabra_ai-0.1.2/tests/test_lang.py +70 -0
  81. palabra_ai-0.1.2/tests/util/__init__.py +1 -0
  82. palabra_ai-0.1.2/tests/util/test_fanout_queue.py +83 -0
@@ -0,0 +1,11 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "pip"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+
8
+ - package-ecosystem: "github-actions"
9
+ directory: "/"
10
+ schedule:
11
+ interval: "weekly"
@@ -0,0 +1,183 @@
1
+ name: Create Release with Assets
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+
8
+ jobs:
9
+ release:
10
+ runs-on: ubuntu-latest
11
+ permissions:
12
+ contents: write
13
+ packages: write
14
+ id-token: write # MANDATORY for OIDC PyPI publishing
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ with:
19
+ ref: ${{ github.ref }} # Use the tag ref to get the updated commit
20
+
21
+ - name: Install uv
22
+ uses: astral-sh/setup-uv@v4
23
+
24
+ - name: Set up Python
25
+ run: uv python install 3.11
26
+
27
+ - name: Build package
28
+ run: |
29
+ # uv builds packages using hatchling
30
+ uv build
31
+
32
+ - name: Check package
33
+ run: |
34
+ # Install twine as a tool (not in system Python)
35
+ uv tool install twine
36
+ uv tool run twine check dist/*
37
+ ls -la dist/
38
+
39
+ - name: Get version from tag
40
+ id: get_version
41
+ run: |
42
+ VERSION="${GITHUB_REF#refs/tags/v}"
43
+ echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
44
+ echo "TAG_NAME=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
45
+
46
+ # Publish to PyPI FIRST (before GitHub Release and Docker)
47
+ - name: Publish to PyPI
48
+ uses: pypa/gh-action-pypi-publish@release/v1
49
+ # No 'with' parameters needed - OIDC authentication is automatic
50
+ # The action will:
51
+ # 1. Request OIDC token from GitHub
52
+ # 2. Exchange it with PyPI for temporary API token
53
+ # 3. Upload the package using that token
54
+ # Your pending publisher configuration will be converted to active publisher on first use
55
+
56
+ - name: Create Release
57
+ uses: softprops/action-gh-release@v1
58
+ with:
59
+ tag_name: ${{ steps.get_version.outputs.TAG_NAME }}
60
+ name: Release ${{ steps.get_version.outputs.TAG_NAME }}
61
+ draft: false
62
+ prerelease: ${{ contains(steps.get_version.outputs.VERSION, 'a') || contains(steps.get_version.outputs.VERSION, 'b') || contains(steps.get_version.outputs.VERSION, 'rc') || contains(steps.get_version.outputs.VERSION, 'dev') }}
63
+ generate_release_notes: true
64
+ files: |
65
+ dist/*.whl
66
+ dist/*.tar.gz
67
+ env:
68
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
69
+
70
+ # Docker image for package distribution
71
+ - name: Log in to GitHub Container Registry
72
+ uses: docker/login-action@v3
73
+ with:
74
+ registry: ghcr.io
75
+ username: ${{ github.actor }}
76
+ password: ${{ secrets.GITHUB_TOKEN }}
77
+
78
+ # Try to link the package to this repository if it exists
79
+ - name: Link Docker package to repository
80
+ continue-on-error: true
81
+ run: |
82
+ OWNER_LOWER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
83
+ REPO_NAME=$(echo "${{ github.repository }}" | cut -d'/' -f2)
84
+ IMAGE_BASE_NAME=$(echo "$REPO_NAME" | sed 's/^draft-//')
85
+
86
+ # Try to link the package to this repository
87
+ echo "Attempting to link ghcr.io/${OWNER_LOWER}/${IMAGE_BASE_NAME} to ${{ github.repository }}..."
88
+
89
+ # Get repository ID
90
+ REPO_ID=$(curl -s \
91
+ -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
92
+ -H "Accept: application/vnd.github.v3+json" \
93
+ "https://api.github.com/repos/${{ github.repository }}" | jq -r '.id')
94
+
95
+ if [ "$REPO_ID" != "null" ] && [ -n "$REPO_ID" ]; then
96
+ # Try to link the package to this repository
97
+ curl -X PUT \
98
+ -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
99
+ -H "Accept: application/vnd.github.v3+json" \
100
+ "https://api.github.com/orgs/${{ github.repository_owner }}/packages/container/${IMAGE_BASE_NAME}" \
101
+ -d "{\"repository_id\": ${REPO_ID}}" || true
102
+ fi
103
+
104
+ - name: Create Dockerfile for package
105
+ run: |
106
+ cat > Dockerfile << EOF
107
+ FROM python:3.11-slim
108
+ LABEL org.opencontainers.image.source=https://github.com/${{ github.repository }}
109
+ LABEL org.opencontainers.image.description="Palabra AI Python SDK"
110
+ LABEL org.opencontainers.image.licenses=MIT
111
+ LABEL org.opencontainers.image.url=https://github.com/${{ github.repository }}
112
+ LABEL org.opencontainers.image.documentation=https://github.com/${{ github.repository }}/blob/main/README.md
113
+ LABEL org.opencontainers.image.vendor="${{ github.repository_owner }}"
114
+ LABEL org.opencontainers.image.version="${{ steps.get_version.outputs.VERSION }}"
115
+
116
+ COPY dist/*.whl /tmp/
117
+ RUN pip install /tmp/*.whl && rm /tmp/*.whl
118
+
119
+ # This is a library image, not meant to be run directly
120
+ CMD ["python", "-c", "import palabra_ai; print(f'Palabra AI SDK v{palabra_ai.__version__} installed')"]
121
+ EOF
122
+
123
+ - name: Build and push Docker image with package
124
+ run: |
125
+ PACKAGE_VERSION=${{ steps.get_version.outputs.VERSION }}
126
+ # Convert repository owner to lowercase for Docker
127
+ OWNER_LOWER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
128
+ # Extract repository name from full repository path
129
+ REPO_NAME=$(echo "${{ github.repository }}" | cut -d'/' -f2)
130
+ # Remove 'draft-' prefix if present for cleaner image name
131
+ IMAGE_BASE_NAME=$(echo "$REPO_NAME" | sed 's/^draft-//')
132
+ IMAGE_NAME=ghcr.io/${OWNER_LOWER}/${IMAGE_BASE_NAME}
133
+
134
+ docker build -t $IMAGE_NAME:$PACKAGE_VERSION -t $IMAGE_NAME:latest .
135
+
136
+ # Try to push, but don't fail the whole workflow if it doesn't work
137
+ if docker push $IMAGE_NAME:$PACKAGE_VERSION && docker push $IMAGE_NAME:latest; then
138
+ echo "📦 Successfully pushed Docker images:"
139
+ echo " - $IMAGE_NAME:$PACKAGE_VERSION"
140
+ echo " - $IMAGE_NAME:latest"
141
+
142
+ # Save image name for use in summary
143
+ echo "IMAGE_NAME=$IMAGE_NAME" >> $GITHUB_ENV
144
+ echo "IMAGE_VERSION=$PACKAGE_VERSION" >> $GITHUB_ENV
145
+ echo "DOCKER_PUSH_SUCCESS=true" >> $GITHUB_ENV
146
+ else
147
+ echo "⚠️ Failed to push Docker images to $IMAGE_NAME"
148
+ echo "This might be due to permissions or existing package conflicts"
149
+ echo "PyPI publishing will continue..."
150
+ echo "DOCKER_PUSH_SUCCESS=false" >> $GITHUB_ENV
151
+ fi
152
+
153
+ - name: Release Summary
154
+ run: |
155
+ echo "## 🎉 Release Summary" >> $GITHUB_STEP_SUMMARY
156
+ echo "" >> $GITHUB_STEP_SUMMARY
157
+ echo "### 📦 Package Information" >> $GITHUB_STEP_SUMMARY
158
+ echo "- **Repository**: ${{ github.repository }}" >> $GITHUB_STEP_SUMMARY
159
+ echo "- **Tag**: ${{ steps.get_version.outputs.TAG_NAME }}" >> $GITHUB_STEP_SUMMARY
160
+ echo "- **Version**: ${{ steps.get_version.outputs.VERSION }}" >> $GITHUB_STEP_SUMMARY
161
+ echo "" >> $GITHUB_STEP_SUMMARY
162
+ echo "### 🚀 Published Artifacts" >> $GITHUB_STEP_SUMMARY
163
+ echo "- **GitHub Release**: https://github.com/${{ github.repository }}/releases/tag/${{ steps.get_version.outputs.TAG_NAME }}" >> $GITHUB_STEP_SUMMARY
164
+ if [ "${{ env.DOCKER_PUSH_SUCCESS }}" == "true" ]; then
165
+ echo "- **Docker Image**: \`${{ env.IMAGE_NAME }}:${{ env.IMAGE_VERSION }}\`" >> $GITHUB_STEP_SUMMARY
166
+ else
167
+ echo "- **Docker Image**: ⚠️ Failed to publish (permissions issue)" >> $GITHUB_STEP_SUMMARY
168
+ fi
169
+ echo "- **PyPI Package**: https://pypi.org/project/palabra-ai/" >> $GITHUB_STEP_SUMMARY
170
+ echo "" >> $GITHUB_STEP_SUMMARY
171
+ echo "### 📥 Installation" >> $GITHUB_STEP_SUMMARY
172
+ echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
173
+ echo "# PyPI" >> $GITHUB_STEP_SUMMARY
174
+ echo "pip install palabra-ai==${{ steps.get_version.outputs.VERSION }}" >> $GITHUB_STEP_SUMMARY
175
+ echo "" >> $GITHUB_STEP_SUMMARY
176
+ if [ "${{ env.DOCKER_PUSH_SUCCESS }}" == "true" ]; then
177
+ echo "# Docker" >> $GITHUB_STEP_SUMMARY
178
+ echo "docker pull ${{ env.IMAGE_NAME }}:${{ env.IMAGE_VERSION }}" >> $GITHUB_STEP_SUMMARY
179
+ echo "" >> $GITHUB_STEP_SUMMARY
180
+ fi
181
+ echo "# From release assets" >> $GITHUB_STEP_SUMMARY
182
+ echo "pip install https://github.com/${{ github.repository }}/releases/download/${{ steps.get_version.outputs.TAG_NAME }}/palabra_ai-${{ steps.get_version.outputs.VERSION }}-py3-none-any.whl" >> $GITHUB_STEP_SUMMARY
183
+ echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
@@ -0,0 +1,108 @@
1
+ name: Update Version from Tag
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+
8
+ jobs:
9
+ update-version:
10
+ runs-on: ubuntu-latest
11
+ permissions:
12
+ contents: write
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ with:
17
+ token: ${{ secrets.GITHUB_TOKEN }}
18
+ fetch-depth: 0 # Need full history to update tag
19
+
20
+ - name: Extract version from tag
21
+ id: extract_version
22
+ run: |
23
+ # Get tag name and remove 'v' prefix
24
+ TAG_NAME="${GITHUB_REF#refs/tags/}"
25
+ VERSION="${TAG_NAME#v}"
26
+ echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT
27
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
28
+ echo "Tag: $TAG_NAME, Version: $VERSION"
29
+
30
+ - name: Update version in pyproject.toml and __init__.py
31
+ run: |
32
+ VERSION="${{ steps.extract_version.outputs.version }}"
33
+
34
+ # Update pyproject.toml and __init__.py using Python for safety
35
+ python << 'EOF'
36
+ import re
37
+ import os
38
+
39
+ version = os.environ.get('VERSION', '${{ steps.extract_version.outputs.version }}')
40
+
41
+ # Update pyproject.toml
42
+ with open('pyproject.toml', 'r') as f:
43
+ content = f.read()
44
+
45
+ # Replace version - handle any spacing and quote style
46
+ pattern = r'(version\s*=\s*["\'])([0-9]+\.[0-9]+\.[0-9]+(?:(?:a|b|rc|dev)[0-9]+)?)(["\'])'
47
+ replacement = rf'\g<1>{version}\g<3>'
48
+ content = re.sub(pattern, replacement, content)
49
+
50
+ with open('pyproject.toml', 'w') as f:
51
+ f.write(content)
52
+
53
+ # Update src/palabra_ai/__init__.py
54
+ with open('src/palabra_ai/__init__.py', 'r') as f:
55
+ content = f.read()
56
+
57
+ # Replace __version__ - handle any spacing and quote style
58
+ pattern = r'(__version__\s*=\s*["\'])([0-9]+\.[0-9]+\.[0-9]+(?:(?:a|b|rc|dev)[0-9]+)?)(["\'])'
59
+ replacement = rf'\g<1>{version}\g<3>'
60
+ content = re.sub(pattern, replacement, content)
61
+
62
+ with open('src/palabra_ai/__init__.py', 'w') as f:
63
+ f.write(content)
64
+ EOF
65
+
66
+ # Configure git
67
+ git config --local user.email "action@github.com"
68
+ git config --local user.name "GitHub Action"
69
+
70
+ # Check if there are changes
71
+ if git diff --quiet; then
72
+ echo "No changes needed - version already matches tag"
73
+ echo "skip_update=true" >> $GITHUB_ENV
74
+ else
75
+ # Commit changes
76
+ git add pyproject.toml src/palabra_ai/__init__.py
77
+ git commit -m "Update version to $VERSION from tag ${{ steps.extract_version.outputs.tag_name }}"
78
+ echo "skip_update=false" >> $GITHUB_ENV
79
+ fi
80
+
81
+ - name: Update tag to point to new commit
82
+ if: env.skip_update == 'false'
83
+ run: |
84
+ TAG_NAME="${{ steps.extract_version.outputs.tag_name }}"
85
+
86
+ # Push the commit to main
87
+ git push origin HEAD:main
88
+
89
+ # Force update the tag to point to the new commit
90
+ git tag -f $TAG_NAME
91
+ git push origin $TAG_NAME --force
92
+
93
+ echo "✅ Tag $TAG_NAME now points to the commit with updated version"
94
+
95
+ - name: Summary
96
+ run: |
97
+ if [ "${{ env.skip_update }}" == "true" ]; then
98
+ echo "## No Update Needed 🟢" >> $GITHUB_STEP_SUMMARY
99
+ echo "Version in pyproject.toml and __init__.py already matches tag ${{ steps.extract_version.outputs.tag_name }}" >> $GITHUB_STEP_SUMMARY
100
+ else
101
+ echo "## Version Updated! 🎉" >> $GITHUB_STEP_SUMMARY
102
+ echo "" >> $GITHUB_STEP_SUMMARY
103
+ echo "- **Tag**: ${{ steps.extract_version.outputs.tag_name }}" >> $GITHUB_STEP_SUMMARY
104
+ echo "- **Version**: ${{ steps.extract_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
105
+ echo "- **Action**: Updated pyproject.toml and __init__.py, moved tag to new commit" >> $GITHUB_STEP_SUMMARY
106
+ echo "" >> $GITHUB_STEP_SUMMARY
107
+ echo "The release workflow will start automatically for the updated tag." >> $GITHUB_STEP_SUMMARY
108
+ fi
@@ -0,0 +1,47 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [ main, develop ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ python-version: ["3.11", "3.12", "3.13"]
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Set up Python ${{ matrix.python-version }}
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+
25
+ - name: Install system dependencies
26
+ run: |
27
+ sudo apt-get update
28
+ sudo apt-get install -y portaudio19-dev
29
+
30
+ - name: Install uv
31
+ uses: astral-sh/setup-uv@v4
32
+
33
+ - name: Install dependencies
34
+ run: |
35
+ uv sync --all-extras --dev
36
+
37
+ - name: Run tests with pytest
38
+ run: |
39
+ uv run pytest -vs --cov=palabra_ai --cov-report=xml --cov-report=html
40
+
41
+ - name: Upload coverage to Codecov
42
+ uses: codecov/codecov-action@v4
43
+ with:
44
+ file: ./coverage.xml
45
+ flags: unittests
46
+ name: codecov-umbrella
47
+ fail_ci_if_error: false
@@ -0,0 +1,128 @@
1
+ name: Version Bump
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ bump_type:
7
+ description: 'Version bump type'
8
+ required: true
9
+ default: 'patch'
10
+ type: choice
11
+ options:
12
+ - patch
13
+ - minor
14
+ - major
15
+ - alpha
16
+ - beta
17
+ - rc
18
+
19
+ jobs:
20
+ create-version-tag:
21
+ runs-on: ubuntu-latest
22
+ permissions:
23
+ contents: write
24
+
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+ with:
28
+ token: ${{ secrets.GITHUB_TOKEN }}
29
+
30
+ - name: Set up Python
31
+ uses: actions/setup-python@v5
32
+ with:
33
+ python-version: '3.11'
34
+
35
+ - name: Get current version
36
+ id: current_version
37
+ run: |
38
+ python << 'EOF'
39
+ import re
40
+ import sys
41
+
42
+ with open('pyproject.toml', 'r') as f:
43
+ content = f.read()
44
+ match = re.search(r'version\s*=\s*["\']([0-9]+\.[0-9]+\.[0-9]+(?:(?:a|b|rc|dev)[0-9]+)?)["\']', content)
45
+ if match:
46
+ print(f"version={match.group(1)}")
47
+ else:
48
+ print('ERROR: Version not found', file=sys.stderr)
49
+ sys.exit(1)
50
+ EOF >> $GITHUB_OUTPUT
51
+
52
+ - name: Calculate new version
53
+ id: new_version
54
+ run: |
55
+ python << 'EOF'
56
+ import re
57
+ import os
58
+ import sys
59
+
60
+ current = "${{ steps.current_version.outputs.version }}"
61
+ bump_type = "${{ github.event.inputs.bump_type }}"
62
+
63
+ # Parse current version
64
+ match = re.match(r'([0-9]+)\.([0-9]+)\.([0-9]+)(?:(a|b|rc)([0-9]+))?', current)
65
+ if not match:
66
+ print(f"ERROR: Invalid version format: {current}", file=sys.stderr)
67
+ sys.exit(1)
68
+
69
+ major = int(match.group(1))
70
+ minor = int(match.group(2))
71
+ patch = int(match.group(3))
72
+ pre_type = match.group(4)
73
+ pre_num = int(match.group(5)) if match.group(5) else 0
74
+
75
+ # Calculate new version
76
+ if bump_type == "major":
77
+ major += 1
78
+ minor = 0
79
+ patch = 0
80
+ new_version = f"{major}.{minor}.{patch}"
81
+ elif bump_type == "minor":
82
+ minor += 1
83
+ patch = 0
84
+ new_version = f"{major}.{minor}.{patch}"
85
+ elif bump_type == "patch":
86
+ if pre_type: # If current is pre-release, just remove pre-release
87
+ new_version = f"{major}.{minor}.{patch}"
88
+ else:
89
+ patch += 1
90
+ new_version = f"{major}.{minor}.{patch}"
91
+ elif bump_type in ["alpha", "beta", "rc"]:
92
+ if pre_type == bump_type[0]: # Same pre-release type
93
+ pre_num += 1
94
+ else:
95
+ pre_num = 1
96
+ new_version = f"{major}.{minor}.{patch}{bump_type[0]}{pre_num}"
97
+
98
+ # Write to GitHub output
99
+ with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
100
+ f.write(f"version={new_version}\n")
101
+
102
+ print(f"New version: {new_version}")
103
+ EOF
104
+
105
+ - name: Create and push tag
106
+ run: |
107
+ git config --local user.email "action@github.com"
108
+ git config --local user.name "GitHub Action"
109
+
110
+ # Create tag
111
+ git tag v${{ steps.new_version.outputs.version }}
112
+
113
+ # Push tag - this will trigger sync-tag-to-version workflow
114
+ git push origin v${{ steps.new_version.outputs.version }}
115
+
116
+ - name: Summary
117
+ run: |
118
+ echo "## Version Tag Created! 🎉" >> $GITHUB_STEP_SUMMARY
119
+ echo "" >> $GITHUB_STEP_SUMMARY
120
+ echo "- **From**: ${{ steps.current_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
121
+ echo "- **To**: ${{ steps.new_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
122
+ echo "- **Type**: ${{ github.event.inputs.bump_type }}" >> $GITHUB_STEP_SUMMARY
123
+ echo "- **Tag**: v${{ steps.new_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
124
+ echo "" >> $GITHUB_STEP_SUMMARY
125
+ echo "The tag will trigger:" >> $GITHUB_STEP_SUMMARY
126
+ echo "1. Update version in pyproject.toml to match the tag" >> $GITHUB_STEP_SUMMARY
127
+ echo "2. Move the tag to the commit with updated version" >> $GITHUB_STEP_SUMMARY
128
+ echo "3. Create a release automatically" >> $GITHUB_STEP_SUMMARY
@@ -0,0 +1,156 @@
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
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ *.manifest
31
+ *.spec
32
+
33
+ # Installer logs
34
+ pip-log.txt
35
+ pip-delete-this-directory.txt
36
+
37
+ # Unit test / coverage reports
38
+ htmlcov/
39
+ .tox/
40
+ .nox/
41
+ .coverage
42
+ .coverage.*
43
+ .cache
44
+ nosetests.xml
45
+ coverage.xml
46
+ *.cover
47
+ *.py,cover
48
+ .hypothesis/
49
+ .pytest_cache/
50
+ cover/
51
+
52
+ # Translations
53
+ *.mo
54
+ *.pot
55
+
56
+ # Django stuff:
57
+ *.log
58
+ local_settings.py
59
+ db.sqlite3
60
+ db.sqlite3-journal
61
+
62
+ # Flask stuff:
63
+ instance/
64
+ .webassets-cache
65
+
66
+ # Scrapy stuff:
67
+ .scrapy
68
+
69
+ # Sphinx documentation
70
+ docs/_build/
71
+
72
+ # PyBuilder
73
+ .pybuilder/
74
+ target/
75
+
76
+ # Jupyter Notebook
77
+ .ipynb_checkpoints
78
+
79
+ # IPython
80
+ profile_default/
81
+ ipython_config.py
82
+
83
+ # pyenv
84
+ .python-version
85
+
86
+ # pipenv
87
+ Pipfile.lock
88
+
89
+ # poetry
90
+ poetry.lock
91
+
92
+ # pdm
93
+ .pdm.toml
94
+ .pdm-python
95
+ .pdm-build/
96
+
97
+ # PEP 582
98
+ __pypackages__/
99
+
100
+ # Celery stuff
101
+ celerybeat-schedule
102
+ celerybeat.pid
103
+
104
+ # SageMath parsed files
105
+ *.sage.py
106
+
107
+ # Environments
108
+ .env
109
+ .venv
110
+ env/
111
+ venv/
112
+ ENV/
113
+ env.bak/
114
+ venv.bak/
115
+
116
+ # Spyder project settings
117
+ .spyderproject
118
+ .spyproject
119
+
120
+ # Rope project settings
121
+ .ropeproject
122
+
123
+ # mkdocs documentation
124
+ /site
125
+
126
+ # mypy
127
+ .mypy_cache/
128
+ .dmypy.json
129
+ dmypy.json
130
+
131
+ # Pyre type checker
132
+ .pyre/
133
+
134
+ # pytype static type analyzer
135
+ .pytype/
136
+
137
+ # Cython debug symbols
138
+ cython_debug/
139
+
140
+ # PyCharm
141
+ .idea/
142
+
143
+ # VSCode
144
+ .vscode/
145
+
146
+ # macOS
147
+ .DS_Store
148
+
149
+ # Windows
150
+ Thumbs.db
151
+ ehthumbs.db
152
+
153
+
154
+
155
+ # uv
156
+ uv.lock
@@ -0,0 +1,16 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v4.5.0
4
+ hooks:
5
+ - id: trailing-whitespace
6
+ - id: end-of-file-fixer
7
+ - id: check-yaml
8
+ - id: check-added-large-files
9
+ - id: check-merge-conflict
10
+
11
+ - repo: https://github.com/astral-sh/ruff-pre-commit
12
+ rev: v0.7.0
13
+ hooks:
14
+ - id: ruff
15
+ args: [--fix]
16
+ - id: ruff-format