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.
- interstellar-1.1.7/.github/templates/formula.rb.template +40 -0
- interstellar-1.1.7/.github/workflows/pr.yml +258 -0
- interstellar-1.1.7/.github/workflows/release.yml +374 -0
- interstellar-1.1.7/.github/workflows/test.yml +35 -0
- interstellar-1.1.7/.gitignore +209 -0
- interstellar-1.1.7/.python-version +1 -0
- interstellar-1.1.7/LICENSE +21 -0
- interstellar-1.1.7/Makefile +57 -0
- interstellar-1.1.7/PKG-INFO +373 -0
- interstellar-1.1.7/README.md +351 -0
- interstellar-1.1.7/assets/qr_bnb.png +0 -0
- interstellar-1.1.7/assets/qr_btc.png +0 -0
- interstellar-1.1.7/assets/qr_eth.png +0 -0
- interstellar-1.1.7/assets/qr_xmr.png +0 -0
- interstellar-1.1.7/pyproject.toml +89 -0
- interstellar-1.1.7/scripts/build.sh +22 -0
- interstellar-1.1.7/scripts/qr.py +109 -0
- interstellar-1.1.7/src/interstellar/__init__.py +18 -0
- interstellar-1.1.7/src/interstellar/__main__.py +6 -0
- interstellar-1.1.7/src/interstellar/cli.py +270 -0
- interstellar-1.1.7/src/interstellar/tools.py +176 -0
- interstellar-1.1.7/tests/conftest.py +10 -0
- interstellar-1.1.7/tests/smoke.py +137 -0
- interstellar-1.1.7/tests/test_cli.py +642 -0
- interstellar-1.1.7/tests/test_tools.py +207 -0
- interstellar-1.1.7/uv.lock +937 -0
|
@@ -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
|