Skip to content

Commit 4045ba7

Browse files
committed
v0.1.2
1 parent 35be2ac commit 4045ba7

File tree

11 files changed

+362
-31
lines changed

11 files changed

+362
-31
lines changed

.github/scripts/build_cn_bin.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import argparse
2+
import ipaddress
3+
import urllib.request
4+
from pathlib import Path
5+
6+
from build_bin import BinBuilder
7+
8+
9+
def _load_cidrs(url: str) -> list[str]:
10+
with urllib.request.urlopen(url) as response:
11+
content = response.read().decode("utf-8")
12+
cidrs = []
13+
for line in content.splitlines():
14+
line = line.strip()
15+
if not line or line.startswith("#"):
16+
continue
17+
cidrs.append(line)
18+
return cidrs
19+
20+
21+
def _validate_first(cidr_list: list[str]) -> None:
22+
if not cidr_list:
23+
raise SystemExit("No CIDR data found")
24+
ipaddress.ip_network(cidr_list[0], strict=False)
25+
26+
27+
def main() -> None:
28+
parser = argparse.ArgumentParser(description="Build poptrie CN bin from CIDR list")
29+
parser.add_argument("--url", required=True, help="CIDR list URL")
30+
parser.add_argument("--output", required=True, help="Output bin path")
31+
args = parser.parse_args()
32+
33+
cidrs = _load_cidrs(args.url)
34+
_validate_first(cidrs)
35+
36+
builder = BinBuilder()
37+
for cidr in cidrs:
38+
builder.add_cidr(cidr)
39+
40+
output_path = Path(args.output)
41+
output_path.parent.mkdir(parents=True, exist_ok=True)
42+
builder.save(str(output_path))
43+
44+
45+
if __name__ == "__main__":
46+
main()

.github/scripts/repack_wheels.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import shutil
2+
import subprocess
3+
import sys
4+
import tempfile
5+
import zipfile
6+
from pathlib import Path
7+
8+
9+
def _read_version(cargo_toml: Path) -> str:
10+
for line in cargo_toml.read_text(encoding="utf-8").splitlines():
11+
if line.strip().startswith("version"):
12+
return line.split("=", 1)[1].strip().strip('"')
13+
raise SystemExit("Version not found in Cargo.toml")
14+
15+
16+
def _load_setup_template(template_path: Path, version: str) -> str:
17+
template = template_path.read_text(encoding="utf-8")
18+
if "__VERSION__" not in template:
19+
raise SystemExit("setup.py template missing __VERSION__ placeholder")
20+
return template.replace("__VERSION__", version)
21+
22+
23+
def main() -> None:
24+
repo_root = Path.cwd()
25+
dist_dir = repo_root / "dist"
26+
cargo_toml = repo_root / "Cargo.toml"
27+
build_src = repo_root / "build_bin.py"
28+
ipsearcher_src = repo_root / "example_production.py"
29+
setup_template = repo_root / ".github" / "scripts" / "setup.py"
30+
31+
if not cargo_toml.exists():
32+
raise SystemExit("Cargo.toml not found")
33+
if not build_src.exists():
34+
raise SystemExit("build_bin.py not found")
35+
if not ipsearcher_src.exists():
36+
raise SystemExit("example_production.py not found")
37+
if not setup_template.exists():
38+
raise SystemExit("setup.py template not found")
39+
40+
version = _read_version(cargo_toml)
41+
setup_py = _load_setup_template(setup_template, version)
42+
43+
wheels = sorted(dist_dir.glob("*.whl"))
44+
if not wheels:
45+
raise SystemExit("No wheels found in dist")
46+
47+
for wheel in wheels:
48+
temp_dir = Path(tempfile.mkdtemp())
49+
with zipfile.ZipFile(wheel, "r") as zf:
50+
zf.extractall(temp_dir)
51+
package_dir = temp_dir / "poptrie"
52+
if not package_dir.exists():
53+
raise SystemExit(f"poptrie package not found in {wheel.name}")
54+
55+
suffixes = (".so", ".pyd", ".dll", ".dylib")
56+
for ext_file in temp_dir.iterdir():
57+
if ext_file.is_file() and ext_file.name.startswith("poptrie") and ext_file.suffix in suffixes:
58+
target = package_dir / ext_file.name
59+
if not target.exists():
60+
shutil.move(str(ext_file), str(target))
61+
62+
shutil.copy2(build_src, package_dir / "build.py")
63+
shutil.copy2(ipsearcher_src, package_dir / "ipsearcher.py")
64+
(temp_dir / "setup.py").write_text(setup_py, encoding="utf-8")
65+
66+
wheel.unlink()
67+
subprocess.run(
68+
[sys.executable, "setup.py", "bdist_wheel"],
69+
cwd=temp_dir,
70+
check=True,
71+
)
72+
built_wheels = sorted((temp_dir / "dist").glob("*.whl"))
73+
if not built_wheels:
74+
raise SystemExit("bdist_wheel did not produce a wheel")
75+
for built_wheel in built_wheels:
76+
shutil.copy2(built_wheel, dist_dir / built_wheel.name)
77+
shutil.rmtree(temp_dir)
78+
79+
80+
if __name__ == "__main__":
81+
main()

.github/scripts/setup.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from setuptools import find_packages, setup
2+
3+
4+
setup(
5+
name="poptrie",
6+
version="__VERSION__",
7+
description="Fast IP lookup using poptrie",
8+
author="HarmonSir",
9+
author_email="[email protected]",
10+
license="Apache-2.0",
11+
packages=find_packages(),
12+
include_package_data=True,
13+
package_data={
14+
"poptrie": ["*.so", "*.pyd", "*.dll", "*.dylib"],
15+
},
16+
)
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
name: Cron CN Bin Release
2+
3+
on:
4+
schedule:
5+
- cron: "0 1 */14 * *"
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: write
10+
11+
concurrency:
12+
group: cron-bin
13+
cancel-in-progress: true
14+
15+
jobs:
16+
build:
17+
runs-on: ubuntu-latest
18+
19+
steps:
20+
- name: Checkout main branch
21+
uses: actions/checkout@v4
22+
with:
23+
ref: main
24+
25+
- name: Set up Python
26+
uses: actions/setup-python@v5
27+
with:
28+
python-version: "3.13"
29+
30+
- name: Create venv
31+
run: |
32+
python -m venv .venv
33+
echo "PYTHON_EXEC=$PWD/.venv/bin/python" >> $GITHUB_ENV
34+
35+
- name: Install build deps
36+
run: $PYTHON_EXEC -m pip install --upgrade pip
37+
38+
- name: Build CN bin
39+
run: |
40+
export PYTHONPATH="$GITHUB_WORKSPACE"
41+
$PYTHON_EXEC .github/scripts/build_cn_bin.py \
42+
--url "https://github.com/Loyalsoldier/geoip/raw/refs/heads/release/text/cn.txt" \
43+
--output dist/china-ip.bin
44+
45+
- name: Set release tag
46+
run: echo "CRON_TAG=cron-$(date -u +%Y%m%d)" >> $GITHUB_ENV
47+
48+
- name: Publish cron release
49+
uses: softprops/action-gh-release@v2
50+
with:
51+
tag_name: ${{ env.CRON_TAG }}
52+
name: ${{ env.CRON_TAG }}
53+
files: dist/china-ip.bin
54+
55+
- name: Cleanup old cron releases
56+
uses: actions/github-script@v7
57+
with:
58+
script: |
59+
const releases = await github.paginate(github.rest.repos.listReleases, {
60+
owner: context.repo.owner,
61+
repo: context.repo.repo,
62+
per_page: 100,
63+
});
64+
const cronReleases = releases
65+
.filter((release) => release.tag_name.startsWith("cron-"))
66+
.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
67+
const toDelete = cronReleases.slice(3);
68+
for (const release of toDelete) {
69+
await github.rest.repos.deleteRelease({
70+
owner: context.repo.owner,
71+
repo: context.repo.repo,
72+
release_id: release.id,
73+
});
74+
await github.rest.git.deleteRef({
75+
owner: context.repo.owner,
76+
repo: context.repo.repo,
77+
ref: `tags/${release.tag_name}`,
78+
});
79+
}

.github/workflows/release.yml

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ on:
55
branches:
66
- main
77

8+
concurrency:
9+
group: release-${{ github.ref }}
10+
cancel-in-progress: true
11+
812
defaults:
913
run:
1014
shell: bash
@@ -56,10 +60,10 @@ jobs:
5660

5761
steps:
5862
- name: Checkout
59-
uses: actions/checkout@v4
63+
uses: actions/checkout@v6
6064

6165
- name: Set up Python
62-
uses: actions/setup-python@v5
66+
uses: actions/setup-python@v6
6367
with:
6468
python-version: ${{ matrix.python-version }}
6569

@@ -81,15 +85,21 @@ jobs:
8185
echo "PYTHON_EXEC=$(pwd)/.venv/Scripts/python.exe" >> $GITHUB_ENV
8286
8387
- name: Install maturin
84-
run: $PYTHON_EXEC -m pip install --upgrade pip maturin
88+
run: $PYTHON_EXEC -m pip install --upgrade pip maturin setuptools wheel
8589

8690
- name: Build wheels
8791
run: $PYTHON_EXEC -m maturin build --release --out dist
8892

93+
- name: Repack wheels
94+
run: |
95+
$PYTHON_EXEC .github/scripts/repack_wheels.py
96+
8997
- name: Install wheel
9098
run: $PYTHON_EXEC -m pip install dist/*.whl
9199

92100
- name: Run Tests
101+
env:
102+
POPTRIE_TEST_GEOIP_URL: "https://github.com/Loyalsoldier/geoip/raw/refs/heads/release/text/cn.txt"
93103
run: |
94104
$PYTHON_EXEC -m unittest discover tests
95105
@@ -106,6 +116,22 @@ jobs:
106116
permissions:
107117
contents: write
108118
steps:
119+
- name: Checkout
120+
uses: actions/checkout@v6
121+
122+
- name: Set up Python
123+
uses: actions/setup-python@v6
124+
with:
125+
python-version: "3.13"
126+
127+
- name: Create venv
128+
run: |
129+
python -m venv .venv
130+
echo "PYTHON_EXEC=$PWD/.venv/bin/python" >> $GITHUB_ENV
131+
132+
- name: Install build deps
133+
run: $PYTHON_EXEC -m pip install --upgrade pip
134+
109135
- name: Cleanup latest release assets
110136
uses: actions/github-script@v7
111137
with:
@@ -129,13 +155,45 @@ jobs:
129155
throw error;
130156
}
131157
}
158+
- name: Remove latest release
159+
uses: actions/github-script@v7
160+
with:
161+
script: |
162+
try {
163+
const release = await github.rest.repos.getReleaseByTag({
164+
owner: context.repo.owner,
165+
repo: context.repo.repo,
166+
tag: "latest",
167+
});
168+
await github.rest.repos.deleteRelease({
169+
owner: context.repo.owner,
170+
repo: context.repo.repo,
171+
release_id: release.data.id,
172+
});
173+
await github.rest.git.deleteRef({
174+
owner: context.repo.owner,
175+
repo: context.repo.repo,
176+
ref: "tags/latest",
177+
});
178+
} catch (error) {
179+
if (error.status !== 404) {
180+
throw error;
181+
}
182+
}
132183
133184
- name: Download wheels
134185
uses: actions/download-artifact@v4
135186
with:
136187
path: dist
137188
merge-multiple: true
138189

190+
- name: Build CN bin
191+
run: |
192+
export PYTHONPATH="$GITHUB_WORKSPACE"
193+
$PYTHON_EXEC .github/scripts/build_cn_bin.py \
194+
--url "https://github.com/Loyalsoldier/geoip/raw/refs/heads/release/text/cn.txt" \
195+
--output dist/china-ip.bin
196+
139197
- name: Publish latest release
140198
uses: softprops/action-gh-release@v2
141199
with:

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "poptrie"
3-
version = "0.1.1"
3+
version = "0.1.2"
44
edition = "2021"
55

66
[lib]

build_bin.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,12 @@ def save(self, output_path):
125125
print(f"构建完成!压缩后大小: {len(final_data) / 1024:.2f} KB")
126126

127127

128-
# --- 使用方式 ---
129-
builder = BinBuilder()
130-
# 这里放入你收集的中国 IP CIDR 列表
131-
china_cidrs = ["1.0.1.0/24", "110.16.0.0/12", "240e::/18"]
132-
for c in china_cidrs:
133-
builder.add_cidr(c)
134-
135-
builder.save("china_ip.bin")
128+
if __name__ == '__main__':
129+
# --- 使用方式 ---
130+
builder = BinBuilder()
131+
# 这里放入你收集的中国 IP CIDR 列表
132+
china_cidrs = ["1.0.1.0/24", "110.16.0.0/12", "240e::/18"]
133+
for c in china_cidrs:
134+
builder.add_cidr(c)
135+
136+
builder.save("china_ip.bin")

0 commit comments

Comments
 (0)