Skip to content

Commit 4578084

Browse files
committed
ci build
1 parent 8b1fff4 commit 4578084

9 files changed

Lines changed: 114 additions & 22 deletions

File tree

.github/workflows/ci.yml

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [master]
6+
pull_request:
7+
branches: [master]
8+
9+
jobs:
10+
test:
11+
strategy:
12+
fail-fast: false
13+
matrix:
14+
os: [windows-latest, ubuntu-latest, macos-latest]
15+
runs-on: ${{ matrix.os }}
16+
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
21+
- name: Setup Python 3.12
22+
uses: actions/setup-python@v5
23+
with:
24+
python-version: "3.12"
25+
26+
- name: Setup uv
27+
uses: astral-sh/setup-uv@v4
28+
with:
29+
enable-cache: true
30+
31+
# ── Install LLVM (Windows) ──────────────────────────────────────
32+
- name: Cache LLVM (Windows)
33+
if: runner.os == 'Windows'
34+
id: cache-llvm-windows
35+
uses: actions/cache@v4
36+
with:
37+
path: llvm-windows
38+
key: llvm-windows-21.1.1-clang-21.1.1
39+
40+
- name: Install LLVM (Windows)
41+
if: runner.os == 'Windows'
42+
shell: bash
43+
run: |
44+
if [ ! -d "llvm-windows/llvm" ]; then
45+
mkdir -p llvm-windows
46+
curl -sL https://github.com/vovkos/llvm-package-windows/releases/download/llvm-21.1.1/llvm-21.1.1-windows-amd64-msvc17-msvcrt.7z -o llvm-windows/llvm.7z
47+
7z x llvm-windows/llvm.7z -ollvm-windows/llvm
48+
curl -sL https://github.com/vovkos/llvm-package-windows/releases/download/clang-21.1.1/clang-21.1.1-windows-amd64-msvc17-msvcrt.7z -o llvm-windows/clang.7z
49+
7z x llvm-windows/clang.7z -ollvm-windows/llvm -aoa
50+
rm -f llvm-windows/llvm.7z llvm-windows/clang.7z
51+
fi
52+
LLVM_PREFIX=$(ls -d llvm-windows/llvm/llvm-*)
53+
echo "CMAKE_PREFIX_PATH=$(cygpath -w "$(pwd)/$LLVM_PREFIX")" >> "$GITHUB_ENV"
54+
echo "$(pwd)/$LLVM_PREFIX/bin" >> "$GITHUB_PATH"
55+
56+
# ── Install LLVM (Linux) ────────────────────────────────────────
57+
- name: Install LLVM (Linux)
58+
if: runner.os == 'Linux'
59+
run: |
60+
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
61+
sudo add-apt-repository -y "deb http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-21 main"
62+
sudo apt-get update
63+
sudo apt-get install -y llvm-21-dev llvm-21-tools clang-21 lld-21
64+
echo "CMAKE_PREFIX_PATH=/usr/lib/llvm-21" >> "$GITHUB_ENV"
65+
66+
# ── Install LLVM (macOS) ────────────────────────────────────────
67+
- name: Install LLVM (macOS)
68+
if: runner.os == 'macOS'
69+
run: |
70+
brew install llvm
71+
echo "CMAKE_PREFIX_PATH=$(brew --prefix llvm)" >> "$GITHUB_ENV"
72+
73+
# ── Setup MSVC (Windows) ────────────────────────────────────────
74+
- name: Setup MSVC
75+
if: runner.os == 'Windows'
76+
uses: ilammy/msvc-dev-cmd@v1
77+
78+
# ── Install dependencies & run tests ────────────────────────────
79+
- name: Install dependencies
80+
run: uv sync
81+
82+
- name: Run tests
83+
run: uv run pytest tests/ -v

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ python -m uv run python -m shifting_codes.ui.app
2424

2525
## Dependencies
2626

27-
- **llvm-nanobind**: Local editable dependency at `../llvm-nanobind/build/` (must be built separately before tests/UI work)
27+
- **llvm-nanobind**: Built from source via git by default; override to local path in `pyproject.toml` for development
2828
- **z3-solver**: Constraint solving for MBA coefficient generation
2929
- **PyQt6**: GUI framework (UI not yet tested)
3030
- Python 3.12+ required, managed with UV + hatchling build backend

README.md

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Six obfuscation passes are available:
2121

2222
- **Python 3.12+**
2323
- **[UV](https://docs.astral.sh/uv/)** package manager
24-
- **llvm-nanobind** — must be cloned and built separately (see below)
24+
- **LLVM 21** development libraries installed (see [llvm-nanobind](https://github.com/expend20/llvm-nanobind) for platform-specific instructions)
2525

2626
## Installation
2727

@@ -31,21 +31,17 @@ Six obfuscation passes are available:
3131
pip install uv
3232
```
3333

34-
2. **Clone and build llvm-nanobind** as a sibling directory:
34+
2. **Install the project** (builds llvm-nanobind from source automatically):
3535

3636
```bash
37-
git clone -b transformation-api https://github.com/expend20/llvm-nanobind ../llvm-nanobind
38-
cd ../llvm-nanobind
39-
# Follow llvm-nanobind's build instructions to produce the build/ directory
40-
cd -
37+
uv sync
4138
```
4239

43-
The project expects the built package at `../llvm-nanobind/build/`.
44-
45-
3. **Install the project**:
40+
For local development with a local llvm-nanobind checkout, override the source in `pyproject.toml`:
4641

47-
```bash
48-
uv sync
42+
```toml
43+
[tool.uv.sources]
44+
llvm-nanobind = { path = "../llvm-nanobind", editable = true }
4945
```
5046

5147
## Usage

pyproject.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ version = "0.1.0"
44
description = "LLVM obfuscation passes ported from Pluto to Python, with PyQt6 visualization"
55
requires-python = ">=3.12"
66
dependencies = [
7+
"llvm-nanobind",
78
"z3-solver>=4.12",
89
"PyQt6>=6.6",
910
]
@@ -16,7 +17,7 @@ build-backend = "hatchling.build"
1617
packages = ["src/shifting_codes"]
1718

1819
[tool.uv.sources]
19-
llvm-nanobind = { path = "../llvm-nanobind", editable = true }
20+
llvm-nanobind = { git = "https://github.com/expend20/llvm-nanobind" }
2021

2122
[dependency-groups]
2223
dev = [
@@ -26,4 +27,4 @@ dev = [
2627

2728
[tool.pytest.ini_options]
2829
testpaths = ["tests"]
29-
pythonpath = ["src", "../llvm-nanobind/build"]
30+
pythonpath = ["src"]

src/shifting_codes/ui/diff_view.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ def __init__(self, parent: QWidget | None = None):
3232
def set_diff_colors(self, colors: dict[str, QColor]):
3333
self._diff_colors = colors
3434
# Reapply highlights if there's content
35-
if self.document().blockCount() > 1:
35+
doc = self.document()
36+
if doc is not None and doc.blockCount() > 1:
3637
self._apply_highlights()
3738

3839
def show_diff(self, before: str, after: str):

src/shifting_codes/ui/ir_editor.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ def _build_rules(self):
8484
comment_fmt.setForeground(QColor(c["comment"]))
8585
self._rules.append((QRegularExpression(r";.*$"), comment_fmt))
8686

87-
def highlightBlock(self, text: str):
87+
def highlightBlock(self, text: str | None):
88+
if text is None:
89+
return
8890
for pattern, fmt in self._rules:
8991
it = pattern.globalMatch(text)
9092
while it.hasNext():

src/shifting_codes/ui/source_editor.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,9 @@ def _build_rules(self):
112112
self._comment_fmt.setForeground(QColor(c["comment"]))
113113
self._comment_fmt.setFontItalic(True)
114114

115-
def highlightBlock(self, text: str):
115+
def highlightBlock(self, text: str | None):
116+
if text is None:
117+
return
116118
# Apply single-line rules
117119
for pattern, fmt in self._rules:
118120
it = pattern.globalMatch(text)

src/shifting_codes/utils/crypto.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,17 @@ class CryptoRandom:
1313

1414
def __init__(self, seed: int | None = None):
1515
self._seeded = seed is not None
16-
if self._seeded:
17-
self._rng = random.Random(seed)
18-
else:
19-
self._rng = None
16+
self._rng: random.Random | None = random.Random(seed) if self._seeded else None
2017

2118
def get_uint32(self) -> int:
2219
if self._seeded:
20+
assert self._rng is not None
2321
return self._rng.getrandbits(32)
2422
return secrets.randbits(32)
2523

2624
def get_uint64(self) -> int:
2725
if self._seeded:
26+
assert self._rng is not None
2827
return self._rng.getrandbits(64)
2928
return secrets.randbits(64)
3029

@@ -33,6 +32,7 @@ def get_range(self, max_val: int) -> int:
3332
if max_val <= 0:
3433
return 0
3534
if self._seeded:
35+
assert self._rng is not None
3636
return self._rng.randrange(max_val)
3737
return secrets.randbelow(max_val)
3838

tests/test_xtea.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,11 +171,18 @@ def test_xtea_execution_correctness():
171171

172172
# Determine target triple
173173
is_windows = platform.system() == "Windows"
174+
is_macos = platform.system() == "Darwin"
175+
machine = platform.machine()
174176
if is_windows:
175177
triple = "x86_64-pc-windows-msvc"
176178
shared_ext = ".dll"
179+
elif is_macos:
180+
arch = "arm64" if machine == "arm64" else "x86_64"
181+
triple = f"{arch}-apple-darwin"
182+
shared_ext = ".dylib"
177183
else:
178-
triple = "x86_64-unknown-linux-gnu"
184+
arch = "aarch64" if machine == "aarch64" else "x86_64"
185+
triple = f"{arch}-unknown-linux-gnu"
179186
shared_ext = ".so"
180187

181188
tmpdir = tempfile.mkdtemp()

0 commit comments

Comments
 (0)