@@ -2,12 +2,17 @@ name: Automatic Release Creation
2
2
3
3
on :
4
4
workflow_dispatch :
5
+ schedule :
6
+ - cron : ' 0 10 * * *'
5
7
6
8
jobs :
7
- detect-last-release :
9
+ create-metadata :
8
10
runs-on : ubuntu-latest
9
11
outputs :
10
- last_release : ${{ steps.last-release.outputs.hash }}
12
+ hash : ${{ steps.last-release.outputs.hash }}
13
+ version : ${{ steps.create-version.outputs.version}}
14
+ npm_packages : ${{ steps.create-npm-packages.outputs.npm_packages}}
15
+ pypi_packages : ${{ steps.create-pypi-packages.outputs.pypi_packages}}
11
16
steps :
12
17
- uses : actions/checkout@v4
13
18
with :
@@ -20,112 +25,188 @@ jobs:
20
25
echo "hash=${HASH}" >> $GITHUB_OUTPUT
21
26
echo "Using last release hash: ${HASH}"
22
27
23
- create-tag-name :
28
+ - name : Install uv
29
+ uses : astral-sh/setup-uv@v5
30
+
31
+ - name : Create version name
32
+ id : create-version
33
+ run : |
34
+ VERSION=$(uv run --script scripts/release.py generate-version)
35
+ echo "version $VERSION"
36
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
37
+
38
+ - name : Create notes
39
+ run : |
40
+ HASH="${{ steps.last-release.outputs.hash }}"
41
+ uv run --script scripts/release.py generate-notes --directory src/ $HASH > RELEASE_NOTES.md
42
+ cat RELEASE_NOTES.md
43
+
44
+ - name : Release notes
45
+ uses : actions/upload-artifact@v4
46
+ with :
47
+ name : release-notes
48
+ path : RELEASE_NOTES.md
49
+
50
+ - name : Create python matrix
51
+ id : create-pypi-packages
52
+ run : |
53
+ HASH="${{ steps.last-release.outputs.hash }}"
54
+ PYPI=$(uv run --script scripts/release.py generate-matrix --pypi --directory src $HASH)
55
+ echo "pypi_packages $PYPI"
56
+ echo "pypi_packages=$PYPI" >> $GITHUB_OUTPUT
57
+
58
+ - name : Create npm matrix
59
+ id : create-npm-packages
60
+ run : |
61
+ HASH="${{ steps.last-release.outputs.hash }}"
62
+ NPM=$(uv run --script scripts/release.py generate-matrix --npm --directory src $HASH)
63
+ echo "npm_packages $NPM"
64
+ echo "npm_packages=$NPM" >> $GITHUB_OUTPUT
65
+
66
+ update-packages :
67
+ needs : [create-metadata]
68
+ if : ${{ needs.create-metadata.outputs.npm_packages != '[]' || needs.create-metadata.outputs.pypi_packages != '[]' }}
24
69
runs-on : ubuntu-latest
25
70
outputs :
26
- tag_name : ${{ steps.last-release .outputs.tag }}
71
+ changes_made : ${{ steps.commit .outputs.changes_made }}
27
72
steps :
28
- - name : Get last release hash
29
- id : last-release
73
+ - uses : actions/checkout@v4
74
+ with :
75
+ fetch-depth : 0
76
+
77
+ - name : Install uv
78
+ uses : astral-sh/setup-uv@v5
79
+
80
+ - name : Update packages
30
81
run : |
31
- DATE=$(date +%Y.%m.%d)
32
- echo "tag=v${DATE}" >> $GITHUB_OUTPUT
33
- echo "Using tag: v${DATE}"
82
+ HASH="${{ needs.create-metadata.outputs.hash }}"
83
+ uv run --script scripts/release.py update-packages --directory src/ $HASH
34
84
35
- detect-packages :
36
- needs : [detect-last-release]
85
+ - name : Configure git
86
+ run : |
87
+ git config --global user.name "GitHub Actions"
88
+ git config --global user.email "[email protected] "
89
+
90
+ - name : Commit changes
91
+ id : commit
92
+ run : |
93
+ VERSION="${{ needs.create-metadata.outputs.version }}"
94
+ git add -u
95
+ if git diff-index --quiet HEAD; then
96
+ echo "changes_made=false" >> $GITHUB_OUTPUT
97
+ else
98
+ git commit -m 'Automatic update of packages'
99
+ git tag -a "$VERSION" -m "Release $VERSION"
100
+ git push origin "$VERSION"
101
+ echo "changes_made=true" >> $GITHUB_OUTPUT
102
+ fi
103
+
104
+ publish-pypi :
105
+ needs : [update-packages, create-metadata]
106
+ strategy :
107
+ fail-fast : false
108
+ matrix :
109
+ package : ${{ fromJson(needs.create-metadata.outputs.pypi_packages) }}
110
+ name : Build ${{ matrix.package }}
111
+ environment : release
112
+ permissions :
113
+ id-token : write # Required for trusted publishing
37
114
runs-on : ubuntu-latest
38
- outputs :
39
- packages : ${{ steps.find-packages.outputs.packages }}
40
115
steps :
41
116
- uses : actions/checkout@v4
42
117
with :
43
- fetch-depth : 0
118
+ ref : ${{ needs.create-metadata.outputs.version }}
44
119
45
120
- name : Install uv
46
121
uses : astral-sh/setup-uv@v5
47
122
48
- - name : Find packages
49
- id : find-packages
50
- working-directory : src
123
+ - name : Set up Python
124
+ uses : actions/setup-python@v5
125
+ with :
126
+ python-version-file : " src/${{ matrix.package }}/.python-version"
127
+
128
+ - name : Install dependencies
129
+ working-directory : src/${{ matrix.package }}
130
+ run : uv sync --frozen --all-extras --dev
131
+
132
+ - name : Run pyright
133
+ working-directory : src/${{ matrix.package }}
134
+ run : uv run --frozen pyright
135
+
136
+ - name : Build package
137
+ working-directory : src/${{ matrix.package }}
138
+ run : uv build
139
+
140
+ - name : Publish package to PyPI
141
+ uses : pypa/gh-action-pypi-publish@release/v1
142
+ with :
143
+ packages-dir : src/${{ matrix.package }}/dist
144
+
145
+ publish-npm :
146
+ needs : [update-packages, create-metadata]
147
+ strategy :
148
+ fail-fast : false
149
+ matrix :
150
+ package : ${{ fromJson(needs.create-metadata.outputs.npm_packages) }}
151
+ name : Build ${{ matrix.package }}
152
+ environment : release
153
+ runs-on : ubuntu-latest
154
+ steps :
155
+ - uses : actions/checkout@v4
156
+ with :
157
+ ref : ${{ needs.create-metadata.outputs.version }}
158
+
159
+ - uses : actions/setup-node@v4
160
+ with :
161
+ node-version : 22
162
+ cache : npm
163
+ registry-url : ' https://registry.npmjs.org'
164
+
165
+ - name : Install dependencies
166
+ working-directory : src/${{ matrix.package }}
167
+ run : npm ci
168
+
169
+ - name : Check if version exists on npm
170
+ working-directory : src/${{ matrix.package }}
51
171
run : |
52
- cat << 'EOF' > find_packages.py
53
- import json
54
- import os
55
- import subprocess
56
- from itertools import chain
57
- from pathlib import Path
58
-
59
- packages = []
60
-
61
- print("Starting package detection...")
62
- print(f"Using LAST_RELEASE: {os.environ['LAST_RELEASE']}")
63
-
64
- # Find all directories containing package.json or pyproject.toml
65
- paths = chain(Path('.').glob('*/package.json'), Path('.').glob('*/pyproject.toml'))
66
- for path in paths:
67
- print(f"\nChecking path: {path}")
68
- # Check for changes in .py or .ts files
69
- # Run git diff from the specific directory
70
- cmd = ['git', 'diff', '--name-only', f'{os.environ["LAST_RELEASE"]}..HEAD', '--', '.']
71
- result = subprocess.run(cmd, capture_output=True, text=True, cwd=path.parent)
72
-
73
- # Check if any .py or .ts files were changed
74
- changed_files = result.stdout.strip().split('\n')
75
- print(f"Changed files found: {changed_files}")
76
-
77
- has_changes = any(f.endswith(('.py', '.ts')) for f in changed_files if f)
78
- if has_changes:
79
- print(f"Adding package: {path.parent}")
80
- packages.append(str(path.parent))
81
-
82
- print(f"\nFinal packages list: {packages}")
83
-
84
- # Write output
85
- with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
86
- f.write(f"packages={json.dumps(packages)}\n")
87
- EOF
88
-
89
- LAST_RELEASE=${{ needs.detect-last-release.outputs.last_release }} uv run --script --python 3.12 find_packages.py
90
-
91
- create-tag :
92
- needs : [detect-packages, create-tag-name]
172
+ VERSION=$(jq -r .version package.json)
173
+ if npm view --json | jq --arg version "$VERSION" '[.[]][0].versions | contains([$version])'; then
174
+ echo "Version $VERSION already exists on npm"
175
+ exit 1
176
+ fi
177
+ echo "Version $VERSION is new, proceeding with publish"
178
+
179
+ - name : Build package
180
+ working-directory : src/${{ matrix.package }}
181
+ run : npm run build
182
+
183
+ - name : Publish package
184
+ working-directory : src/${{ matrix.package }}
185
+ run : |
186
+ npm publish --access public
187
+ env :
188
+ NODE_AUTH_TOKEN : ${{ secrets.NPM_TOKEN }}
189
+
190
+ create-release :
191
+ needs : [update-packages, create-metadata, publish-pypi, publish-npm]
192
+ if : needs.update-packages.outputs.changes_made == 'true'
93
193
runs-on : ubuntu-latest
194
+ environment : release
94
195
permissions :
95
196
contents : write
96
197
steps :
97
198
- uses : actions/checkout@v4
98
199
200
+ - name : Download release notes
201
+ uses : actions/download-artifact@v4
202
+ with :
203
+ name : release-notes
204
+
99
205
- name : Create release
100
206
env :
101
- GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
207
+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN}}
102
208
run : |
103
- # Configure git
104
- git config --global user.name "GitHub Actions"
105
- git config --global user.email "[email protected] "
106
-
107
- # Get packages array
108
- PACKAGES='${{ needs.detect-packages.outputs.packages }}'
109
-
110
- if [ "$(echo "$PACKAGES" | jq 'length')" -gt 0 ]; then
111
- # Generate comprehensive release notes
112
- {
113
- echo "# Release ${{ needs.create-tag-name.outputs.tag_name }}"
114
- echo ""
115
- echo "## Updated Packages"
116
- echo "$PACKAGES" | jq -r '.[]' | while read -r package; do
117
- echo "- $package"
118
- done
119
- } > notes.md
120
-
121
- # Create and push tag
122
- git tag -a "${{ needs.create-tag-name.outputs.tag_name }}" -m "Release ${{ needs.create-tag-name.outputs.tag_name }}"
123
- git push origin "${{ needs.create-tag-name.outputs.tag_name }}"
124
-
125
- # Create GitHub release
126
- gh release create "${{ needs.create-tag-name.outputs.tag_name }}" \
127
- --title "Release ${{ needs.create-tag-name.outputs.tag_name }}" \
128
- --notes-file notes.md
129
- else
130
- echo "No packages need release"
131
- fi
209
+ VERSION="${{ needs.create-metadata.outputs.version }}"
210
+ gh release create "$VERSION" \
211
+ --title "Release $VERSION" \
212
+ --notes-file RELEASE_NOTES.md
0 commit comments