interstellar 1.1.7__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.
@@ -0,0 +1,40 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ # Homebrew formula for {{NAME}}
5
+ # Auto-generated from .github/templates/formula.rb.template
6
+ # Supports both release (url/sha256) and HEAD (head) modes
7
+
8
+ class {{CLASS_NAME}} < Formula
9
+ include Language::Python::Virtualenv
10
+
11
+ desc "{{DESCRIPTION}}"
12
+ homepage "{{HOMEPAGE}}"
13
+ {{SOURCE_LINE}}
14
+ license "MIT"
15
+
16
+ depends_on "python@3.13"
17
+
18
+ def install
19
+ # Create venv with pip included (not --without-pip)
20
+ python3 = "python3.13"
21
+ system python3, "-m", "venv", libexec
22
+
23
+ # Install the package and all dependencies
24
+ system libexec/"bin/pip", "install", "--upgrade", "pip"
25
+ system libexec/"bin/pip", "install", buildpath.to_s
26
+
27
+ # Create wrapper scripts in bin that use the venv
28
+ bin.install_symlink Dir[libexec/"bin/{{NAME}}"]
29
+ end
30
+
31
+ test do
32
+ # Test version command
33
+ assert_match(/v\d+\.\d+\.\d+/, shell_output("#{bin}/{{NAME}} version"))
34
+
35
+ # Test help command shows expected subcommands
36
+ help_output = shell_output("#{bin}/{{NAME}} --help")
37
+ assert_match "deconstruct", help_output
38
+ assert_match "reconstruct", help_output
39
+ end
40
+ end
@@ -0,0 +1,258 @@
1
+ name: Pull Request
2
+
3
+ on:
4
+ pull_request:
5
+ branches:
6
+ - master
7
+
8
+ permissions:
9
+ contents: write # Needed to push QR code updates
10
+ pull-requests: write
11
+ id-token: write # Required for OIDC trusted publishing to TestPyPI
12
+
13
+ jobs:
14
+ test:
15
+ name: Test
16
+ uses: ./.github/workflows/test.yml
17
+
18
+ qr:
19
+ name: QR Codes
20
+ needs: test
21
+ runs-on: ubuntu-latest
22
+ # Skip if triggered by QR code update commit to prevent infinite loop
23
+ if: "!contains(github.event.head_commit.message, '[skip-qr]')"
24
+ steps:
25
+ - uses: actions/checkout@v5
26
+ with:
27
+ ref: ${{ github.head_ref }}
28
+ token: ${{ secrets.GITHUB_TOKEN }}
29
+
30
+ - name: Set up Python
31
+ uses: actions/setup-python@v6
32
+ with:
33
+ python-version: '3.13'
34
+
35
+ - name: Install uv
36
+ uses: astral-sh/setup-uv@v7
37
+
38
+ - name: Install dependencies
39
+ run: make install
40
+
41
+ - name: Generate QR codes
42
+ run: make qr
43
+
44
+ - name: Commit if changed
45
+ run: |
46
+ git config user.name "github-actions[bot]"
47
+ git config user.email "github-actions[bot]@users.noreply.github.com"
48
+ git add assets/
49
+ if ! git diff --cached --quiet; then
50
+ git commit -m "Update QR codes [skip-qr]"
51
+ git push
52
+ fi
53
+
54
+ version:
55
+ name: Version Preview
56
+ needs: test # Only run after tests pass
57
+ runs-on: ubuntu-latest
58
+ outputs:
59
+ pypi_version: ${{ steps.set-version.outputs.pypi_version }}
60
+ environment:
61
+ name: testpypi
62
+ url: https://test.pypi.org/p/${{ github.event.repository.name }}
63
+
64
+ steps:
65
+ - uses: actions/checkout@v5
66
+ with:
67
+ fetch-depth: 0 # Need full history for version calculation
68
+
69
+ - name: Calculate next version (dry run)
70
+ id: version
71
+ uses: anothrNick/github-tag-action@v1
72
+ env:
73
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
74
+ DRY_RUN: true
75
+ DEFAULT_BUMP: minor
76
+ WITH_V: true
77
+ PRERELEASE: true
78
+ PRERELEASE_SUFFIX: rc
79
+ BRANCH_HISTORY: compare
80
+ INITIAL_VERSION: 1.0.3
81
+
82
+ - name: Set up Python
83
+ uses: actions/setup-python@v6
84
+ with:
85
+ python-version: '3.13'
86
+
87
+ - name: Install uv
88
+ uses: astral-sh/setup-uv@v7
89
+
90
+ - name: Update version in pyproject.toml
91
+ id: set-version
92
+ run: |
93
+ # Get base version from tag action (e.g., v1.2.0-rc.0)
94
+ BASE_TAG="${NEW_TAG}"
95
+ # Strip 'v' prefix and '-rc.X' suffix to get base version (e.g., 1.2.0)
96
+ BASE_VERSION="${BASE_TAG#v}"
97
+ BASE_VERSION="${BASE_VERSION%-rc.*}"
98
+ # Create unique prerelease version: {base}rc{pr_number}.dev{run_number}
99
+ # e.g., 1.2.0rc42.dev7 for PR #42, run #7
100
+ PYPI_VERSION="${BASE_VERSION}rc${{ github.event.pull_request.number }}.dev${{ github.run_number }}"
101
+ sed -i "s/^version = .*/version = \"$PYPI_VERSION\"/" pyproject.toml
102
+ echo "Tag version: $NEW_TAG"
103
+ echo "PyPI version: $PYPI_VERSION"
104
+ echo "pypi_version=$PYPI_VERSION" >> $GITHUB_OUTPUT
105
+ grep "^version" pyproject.toml
106
+ env:
107
+ NEW_TAG: ${{ steps.version.outputs.new_tag }}
108
+
109
+ - name: Build distributions
110
+ run: |
111
+ uv build --sdist
112
+ uv build --wheel
113
+
114
+ - name: Publish to TestPyPI
115
+ uses: pypa/gh-action-pypi-publish@release/v1
116
+ with:
117
+ repository-url: https://test.pypi.org/legacy/
118
+ # Uses OIDC trusted publishing - configure at:
119
+ # https://test.pypi.org/manage/account/publishing/
120
+
121
+ - name: Comment version info on PR
122
+ uses: actions/github-script@v8
123
+ with:
124
+ script: |
125
+ const oldTag = '${{ steps.version.outputs.old_tag }}' || 'none';
126
+ const newTag = '${{ steps.version.outputs.new_tag }}';
127
+ const part = '${{ steps.version.outputs.part }}';
128
+
129
+ // Extract base version (remove -rc.X suffix for display)
130
+ const baseVersion = newTag.replace(/-rc\.\d+$/, '');
131
+
132
+ const body = `## 📦 Version Preview
133
+
134
+ - **Current version:** \`${oldTag}\`
135
+ - **Bump type:** \`${part}\`
136
+ - **Release version:** \`${baseVersion}\`
137
+ - **TestPyPI version:** \`${newTag}\`
138
+
139
+ When this PR is merged, version will be bumped to \`${baseVersion}\`.
140
+
141
+ To change the bump type, include in commit message: \`#major\`, \`#minor\`, or \`#patch\``;
142
+
143
+ // Find existing bot comment with version preview
144
+ const { data: comments } = await github.rest.issues.listComments({
145
+ issue_number: context.issue.number,
146
+ owner: context.repo.owner,
147
+ repo: context.repo.repo
148
+ });
149
+
150
+ const botComment = comments.find(c =>
151
+ c.user.type === 'Bot' &&
152
+ c.body.includes('## 📦 Version Preview')
153
+ );
154
+
155
+ if (botComment) {
156
+ // Check if version info changed by comparing release version and bump type
157
+ const oldVersionMatch = botComment.body.match(/Release version:\*\* `([^`]+)`/);
158
+ const oldBumpMatch = botComment.body.match(/Bump type:\*\* `([^`]+)`/);
159
+ const oldReleaseVersion = oldVersionMatch ? oldVersionMatch[1] : '';
160
+ const oldBumpType = oldBumpMatch ? oldBumpMatch[1] : '';
161
+
162
+ if (oldReleaseVersion === baseVersion && oldBumpType === part) {
163
+ console.log('Version info unchanged, skipping comment update');
164
+ return;
165
+ }
166
+
167
+ // Update existing comment
168
+ await github.rest.issues.updateComment({
169
+ comment_id: botComment.id,
170
+ owner: context.repo.owner,
171
+ repo: context.repo.repo,
172
+ body: body
173
+ });
174
+ console.log('Updated existing version comment');
175
+ } else {
176
+ // Create new comment
177
+ await github.rest.issues.createComment({
178
+ issue_number: context.issue.number,
179
+ owner: context.repo.owner,
180
+ repo: context.repo.repo,
181
+ body: body
182
+ });
183
+ console.log('Created new version comment');
184
+ }
185
+
186
+ smoke-pypi:
187
+ name: Smoke PyPI
188
+ needs: version
189
+ runs-on: ubuntu-latest
190
+
191
+ steps:
192
+ - uses: actions/checkout@v5
193
+
194
+ - uses: actions/setup-python@v6
195
+ with:
196
+ python-version: '3.13'
197
+
198
+ - name: Wait for TestPyPI indexing
199
+ run: sleep 180
200
+
201
+ - name: Install from TestPyPI
202
+ run: |
203
+ pip install --index-url https://test.pypi.org/simple/ \
204
+ --extra-index-url https://pypi.org/simple/ \
205
+ ${{ github.event.repository.name }}==${{ needs.version.outputs.pypi_version }}
206
+
207
+ - name: Run smoke tests
208
+ run: python tests/smoke.py ${{ github.event.repository.name }}
209
+
210
+ smoke-homebrew:
211
+ name: Smoke Homebrew
212
+ needs: test
213
+ runs-on: macos-latest
214
+
215
+ steps:
216
+ - uses: actions/checkout@v5
217
+
218
+ - name: Create local tap and generate formula
219
+ run: |
220
+ NAME="${{ github.event.repository.name }}"
221
+ REPO="${{ github.repository }}"
222
+
223
+ # Create a local tap
224
+ brew tap-new local/tap
225
+ TAP_PATH="$(brew --repository)/Library/Taps/local/homebrew-tap/Formula"
226
+ mkdir -p "$TAP_PATH"
227
+
228
+ # Get description from pyproject.toml
229
+ DESCRIPTION=$(grep '^description' pyproject.toml | sed 's/description = "\(.*\)"/\1/')
230
+
231
+ # Convert name to Ruby class name (capitalize, remove hyphens)
232
+ CLASS_NAME=$(echo "$NAME" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1' | tr -d ' ')
233
+
234
+ # Build source line for HEAD install (points to PR branch)
235
+ SOURCE_LINE="head \"https://github.com/${REPO}.git\", branch: \"${{ github.head_ref }}\""
236
+
237
+ # Generate formula from template into the tap
238
+ sed -e "s|{{NAME}}|${NAME}|g" \
239
+ -e "s|{{CLASS_NAME}}|${CLASS_NAME}|g" \
240
+ -e "s|{{DESCRIPTION}}|${DESCRIPTION}|g" \
241
+ -e "s|{{HOMEPAGE}}|https://github.com/${REPO}|g" \
242
+ -e "s|{{SOURCE_LINE}}|${SOURCE_LINE}|g" \
243
+ .github/templates/formula.rb.template > "$TAP_PATH/${NAME}.rb"
244
+
245
+ echo "Generated formula at $TAP_PATH/${NAME}.rb:"
246
+ cat "$TAP_PATH/${NAME}.rb"
247
+
248
+ - name: Install formula from HEAD
249
+ run: brew install --HEAD local/tap/${{ github.event.repository.name }}
250
+
251
+ - name: Run formula test
252
+ run: brew test local/tap/${{ github.event.repository.name }}
253
+
254
+ - name: Verify CLI works
255
+ run: |
256
+ ${{ github.event.repository.name }} version
257
+ ${{ github.event.repository.name }} --help
258
+
@@ -0,0 +1,374 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+
8
+ permissions:
9
+ contents: write
10
+ id-token: write
11
+
12
+ jobs:
13
+ test:
14
+ name: Test
15
+ uses: ./.github/workflows/test.yml
16
+
17
+ version:
18
+ name: Version
19
+ needs: test # Only release after tests pass
20
+ runs-on: ubuntu-latest
21
+ outputs:
22
+ new_version: ${{ steps.version.outputs.new_tag }}
23
+ tag_name: ${{ steps.version.outputs.new_tag }}
24
+ pypi_version: ${{ steps.set-version.outputs.pypi_version }}
25
+
26
+ steps:
27
+ - uses: actions/checkout@v5
28
+ with:
29
+ fetch-depth: 0
30
+ token: ${{ secrets.GITHUB_TOKEN }}
31
+
32
+ - name: Bump version and push tag
33
+ id: version
34
+ uses: anothrNick/github-tag-action@v1
35
+ env:
36
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
37
+ DEFAULT_BUMP: minor
38
+ WITH_V: true
39
+ TAG_CONTEXT: repo
40
+ BRANCH_HISTORY: compare
41
+ INITIAL_VERSION: 1.0.3
42
+
43
+ - name: Update version in pyproject.toml
44
+ id: set-version
45
+ run: |
46
+ # Strip 'v' prefix for PyPI-compatible version
47
+ PYPI_VERSION="${NEW_TAG#v}"
48
+ sed -i "s/^version = .*/version = \"$PYPI_VERSION\"/" pyproject.toml
49
+ echo "Tag version: $NEW_TAG"
50
+ echo "PyPI version: $PYPI_VERSION"
51
+ echo "pypi_version=$PYPI_VERSION" >> $GITHUB_OUTPUT
52
+ grep "^version" pyproject.toml
53
+ env:
54
+ NEW_TAG: ${{ steps.version.outputs.new_tag }}
55
+ # Note: Version is injected locally for this job only.
56
+ # Not committed to repo - downstream jobs inject version from tag.
57
+
58
+ publish:
59
+ name: Publish PyPI
60
+ needs: version
61
+ runs-on: ubuntu-latest
62
+ environment:
63
+ name: pypi
64
+ url: https://pypi.org/p/${{ github.event.repository.name }}
65
+
66
+ steps:
67
+ - uses: actions/checkout@v5
68
+ with:
69
+ ref: master
70
+ fetch-depth: 1
71
+
72
+ - name: Pull latest changes
73
+ run: git pull origin master
74
+
75
+ - name: Set up Python
76
+ uses: actions/setup-python@v6
77
+ with:
78
+ python-version: '3.13'
79
+
80
+ - name: Install uv
81
+ uses: astral-sh/setup-uv@v7
82
+
83
+ - name: Inject version from tag
84
+ run: |
85
+ PYPI_VERSION="${{ needs.version.outputs.new_version }}"
86
+ PYPI_VERSION="${PYPI_VERSION#v}" # Strip 'v' prefix
87
+ sed -i "s/^version = .*/version = \"$PYPI_VERSION\"/" pyproject.toml
88
+ echo "PyPI version: $PYPI_VERSION"
89
+ grep "^version" pyproject.toml
90
+
91
+ - name: Build distributions
92
+ run: |
93
+ uv build --sdist
94
+ uv build --wheel
95
+
96
+ - name: Publish to PyPI
97
+ uses: pypa/gh-action-pypi-publish@release/v1
98
+ # Uses OIDC trusted publishing - configure at:
99
+ # https://pypi.org/manage/account/publishing/
100
+
101
+ build-portable:
102
+ name: Build Portable (${{ matrix.os }})
103
+ needs: version
104
+ runs-on: ${{ matrix.os }}
105
+ strategy:
106
+ matrix:
107
+ os: [ubuntu-latest, macos-latest, windows-latest]
108
+ include:
109
+ - os: ubuntu-latest
110
+ artifact_suffix: linux-portable
111
+ binary_name: ''
112
+ - os: macos-latest
113
+ artifact_suffix: macos-portable
114
+ binary_name: ''
115
+ - os: windows-latest
116
+ artifact_suffix: windows-portable.exe
117
+ binary_name: .exe
118
+
119
+ steps:
120
+ - uses: actions/checkout@v5
121
+ with:
122
+ ref: master
123
+
124
+ - name: Pull latest changes
125
+ run: git pull origin master
126
+
127
+ - name: Set up Python
128
+ uses: actions/setup-python@v6
129
+ with:
130
+ python-version: '3.13'
131
+
132
+ - name: Install uv
133
+ uses: astral-sh/setup-uv@v7
134
+
135
+ - name: Build onefile binary
136
+ env:
137
+ MODE: onefile
138
+ CI: true
139
+ run: make build
140
+ shell: bash
141
+
142
+ - name: Rename binary with platform suffix
143
+ run: |
144
+ mv ${{ github.event.repository.name }}${{ matrix.binary_name }} ${{ github.event.repository.name }}-${{ matrix.artifact_suffix }}
145
+ chmod +x ${{ github.event.repository.name }}-${{ matrix.artifact_suffix }} || true
146
+ shell: bash
147
+
148
+ - name: Upload artifact
149
+ uses: actions/upload-artifact@v5
150
+ with:
151
+ name: ${{ github.event.repository.name }}-${{ matrix.artifact_suffix }}
152
+ path: ${{ github.event.repository.name }}-${{ matrix.artifact_suffix }}
153
+ if-no-files-found: error
154
+
155
+ build-fast:
156
+ name: Build Fast (${{ matrix.os }})
157
+ needs: version
158
+ runs-on: ${{ matrix.os }}
159
+ strategy:
160
+ matrix:
161
+ os: [ubuntu-latest, macos-latest, windows-latest]
162
+ include:
163
+ - os: ubuntu-latest
164
+ platform: linux
165
+ - os: macos-latest
166
+ platform: macos
167
+ - os: windows-latest
168
+ platform: windows
169
+
170
+ steps:
171
+ - uses: actions/checkout@v5
172
+ with:
173
+ ref: master
174
+
175
+ - name: Pull latest changes
176
+ run: git pull origin master
177
+
178
+ - name: Set up Python
179
+ uses: actions/setup-python@v6
180
+ with:
181
+ python-version: '3.13'
182
+
183
+ - name: Install uv
184
+ uses: astral-sh/setup-uv@v7
185
+
186
+ - name: Build standalone distribution
187
+ env:
188
+ MODE: standalone
189
+ CI: true
190
+ run: make build
191
+ shell: bash
192
+
193
+ - name: Create archive
194
+ run: tar -czf "${{ github.event.repository.name }}-${{ matrix.platform }}-fast.tar.gz" cli.dist/
195
+ shell: bash
196
+
197
+ - name: Upload artifact
198
+ uses: actions/upload-artifact@v5
199
+ with:
200
+ name: ${{ github.event.repository.name }}-${{ matrix.platform }}-fast
201
+ path: ${{ github.event.repository.name }}-${{ matrix.platform }}-fast.tar.gz
202
+ if-no-files-found: error
203
+
204
+ release:
205
+ name: Release
206
+ needs: [version, build-portable, build-fast]
207
+ runs-on: ubuntu-latest
208
+
209
+ steps:
210
+ - uses: actions/checkout@v5
211
+ with:
212
+ ref: master
213
+
214
+ - name: Download all artifacts
215
+ uses: actions/download-artifact@v5
216
+ with:
217
+ path: artifacts
218
+
219
+ - name: Prepare release assets
220
+ run: |
221
+ mkdir -p release-assets
222
+ find artifacts -type f -exec cp {} release-assets/ \;
223
+ ls -lah release-assets/
224
+
225
+ - name: Create Release
226
+ uses: softprops/action-gh-release@v2
227
+ with:
228
+ tag_name: ${{ needs.version.outputs.tag_name }}
229
+ files: release-assets/*
230
+ body: |
231
+ ## 📦 Binary Options
232
+
233
+ | Variant | Description | Startup Time |
234
+ |---------|-------------|--------------|
235
+ | **Portable** (`*-portable`) | Single file, no installation needed | ~10 sec |
236
+ | **Fast** (`*-fast.tar.gz`) | Optimized for speed, extract and run | ~1 sec |
237
+
238
+ Choose **Portable** for convenience, **Fast** for performance.
239
+ generate_release_notes: true
240
+ draft: false
241
+ prerelease: ${{ contains(needs.version.outputs.new_version, 'alpha') || contains(needs.version.outputs.new_version, 'beta') || contains(needs.version.outputs.new_version, 'rc') }}
242
+
243
+ smoke-pypi:
244
+ name: Smoke PyPI
245
+ needs: [version, publish]
246
+ runs-on: ubuntu-latest
247
+
248
+ steps:
249
+ - uses: actions/checkout@v5
250
+
251
+ - uses: actions/setup-python@v6
252
+ with:
253
+ python-version: '3.13'
254
+
255
+ - name: Wait for PyPI indexing
256
+ run: sleep 180
257
+
258
+ - name: Install from PyPI
259
+ run: pip install ${{ github.event.repository.name }}==${{ needs.version.outputs.pypi_version }}
260
+
261
+ - name: Run smoke tests
262
+ run: python tests/smoke.py ${{ github.event.repository.name }}
263
+
264
+ smoke-binary:
265
+ name: Smoke Binary (${{ matrix.os }})
266
+ needs: [build-portable, build-fast]
267
+ runs-on: ${{ matrix.os }}
268
+ strategy:
269
+ matrix:
270
+ os: [ubuntu-latest, macos-latest, windows-latest]
271
+ include:
272
+ - os: ubuntu-latest
273
+ artifact_suffix: linux-portable
274
+ - os: macos-latest
275
+ artifact_suffix: macos-portable
276
+ - os: windows-latest
277
+ artifact_suffix: windows-portable.exe
278
+
279
+ steps:
280
+ - uses: actions/checkout@v5
281
+
282
+ - uses: actions/setup-python@v6
283
+ with:
284
+ python-version: '3.13'
285
+
286
+ - name: Install uv
287
+ uses: astral-sh/setup-uv@v7
288
+
289
+ - name: Install package (for import tests)
290
+ run: make install
291
+
292
+ - name: Download binary
293
+ uses: actions/download-artifact@v5
294
+ with:
295
+ name: ${{ github.event.repository.name }}-${{ matrix.artifact_suffix }}
296
+
297
+ - name: Make executable
298
+ run: chmod +x ./${{ github.event.repository.name }}-${{ matrix.artifact_suffix }} || true
299
+ shell: bash
300
+
301
+ - name: Run smoke tests
302
+ run: make smoke NAME=./${{ github.event.repository.name }}-${{ matrix.artifact_suffix }}
303
+
304
+ homebrew:
305
+ name: Update Homebrew
306
+ needs: [version, release]
307
+ runs-on: ubuntu-latest
308
+
309
+ steps:
310
+ - uses: actions/checkout@v5
311
+
312
+ - name: Generate formula from template
313
+ run: |
314
+ NAME="${{ github.event.repository.name }}"
315
+ TAG="${{ needs.version.outputs.tag_name }}"
316
+ REPO="${{ github.repository }}"
317
+
318
+ # Get description from pyproject.toml
319
+ DESCRIPTION=$(grep '^description' pyproject.toml | sed 's/description = "\(.*\)"/\1/')
320
+
321
+ # Calculate SHA256 of release tarball
322
+ TARBALL_URL="https://github.com/${REPO}/archive/refs/tags/${TAG}.tar.gz"
323
+ SHA256=$(curl -sL "$TARBALL_URL" | shasum -a 256 | cut -d' ' -f1)
324
+
325
+ # Convert name to Ruby class name (capitalize, remove hyphens)
326
+ CLASS_NAME=$(echo "$NAME" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1' | tr -d ' ')
327
+
328
+ # Build source lines for release (url + sha256 + head)
329
+ SOURCE_LINE="url \"${TARBALL_URL}\"\n sha256 \"${SHA256}\"\n head \"https://github.com/${REPO}.git\", branch: \"master\""
330
+
331
+ # Generate formula from template into Formula directory
332
+ mkdir -p Formula
333
+ sed -e "s|{{NAME}}|${NAME}|g" \
334
+ -e "s|{{CLASS_NAME}}|${CLASS_NAME}|g" \
335
+ -e "s|{{DESCRIPTION}}|${DESCRIPTION}|g" \
336
+ -e "s|{{HOMEPAGE}}|https://github.com/${REPO}|g" \
337
+ -e "s|{{SOURCE_LINE}}|${SOURCE_LINE}|g" \
338
+ .github/templates/formula.rb.template > "Formula/${NAME}.rb"
339
+
340
+ echo "Generated formula:"
341
+ cat "Formula/${NAME}.rb"
342
+
343
+ - name: Push to homebrew-tap
344
+ uses: cpina/github-action-push-to-another-repository@v1
345
+ env:
346
+ API_TOKEN_GITHUB: ${{ secrets.HOMEBREW_TAP_TOKEN }}
347
+ with:
348
+ source-directory: 'Formula'
349
+ destination-github-username: '${{ github.repository_owner }}'
350
+ destination-repository-name: 'homebrew-tap'
351
+ target-directory: 'Formula'
352
+ target-branch: 'master'
353
+ user-email: 'github-actions[bot]@users.noreply.github.com'
354
+ user-name: 'github-actions[bot]'
355
+ commit-message: 'Update ${{ github.event.repository.name }} to ${{ needs.version.outputs.tag_name }}'
356
+
357
+ smoke-homebrew:
358
+ name: Smoke Homebrew
359
+ needs: [version, homebrew]
360
+ runs-on: macos-latest
361
+
362
+ steps:
363
+ - name: Tap and install formula
364
+ run: |
365
+ brew tap ${{ github.repository_owner }}/tap
366
+ brew install ${{ github.event.repository.name }}
367
+
368
+ - name: Run formula test
369
+ run: brew test ${{ github.event.repository.name }}
370
+
371
+ - name: Verify CLI works
372
+ run: |
373
+ ${{ github.event.repository.name }} version
374
+ ${{ github.event.repository.name }} --help
@@ -0,0 +1,35 @@
1
+ name: Test
2
+
3
+ on:
4
+ workflow_call: # Only callable by other workflows (pr.yml, release.yml)
5
+
6
+ jobs:
7
+ test:
8
+ name: Run Tests
9
+ runs-on: ${{ matrix.os }}
10
+ strategy:
11
+ matrix:
12
+ os: [ubuntu-latest, macos-latest, windows-latest]
13
+ python-version: ['3.13']
14
+
15
+ steps:
16
+ - uses: actions/checkout@v5
17
+
18
+ - name: Set up Python
19
+ uses: actions/setup-python@v6
20
+ with:
21
+ python-version: ${{ matrix.python-version }}
22
+
23
+ - name: Install uv
24
+ uses: astral-sh/setup-uv@v7
25
+ with:
26
+ enable-cache: true
27
+
28
+ - name: Install dependencies
29
+ run: make ci DEV=1
30
+
31
+ - name: Lint with ruff
32
+ run: make lint
33
+
34
+ - name: Run tests with coverage
35
+ run: make cov