Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fcd8e9fd6 | ||
|
|
e00a96b918 | ||
|
|
b19abbd514 | ||
|
|
0b3c483c0b | ||
|
|
9e46c64490 | ||
|
|
efcb6c6c3f | ||
|
|
65c8ab215b |
No files matched your search
@@ -0,0 +1,22 @@
|
||||
Install the wheel below — the PWA is bundled inside it:
|
||||
|
||||
```
|
||||
pip install ./simtrace_analyser-*.whl
|
||||
simtrace-analyser-server --capture gsmtap
|
||||
```
|
||||
|
||||
The UI is then served at http://127.0.0.1:8081/. If your system Python is
|
||||
externally managed (PEP 668 — common on Arch and Fedora), install into a
|
||||
virtual environment first (`python3 -m venv .venv && . .venv/bin/activate`).
|
||||
|
||||
The source archive is the same tree a clone would have: unzip it and run
|
||||
`./start.sh` (Linux/macOS) or `start.bat` (Windows). Direct SIMtrace2 capture
|
||||
on Linux additionally needs the attached `70-simtrace2-pysniff.rules` udev rule.
|
||||
|
||||
**Runtime zip (Windows-friendly, no installer)** — `simtrace-analyser-*.zip`
|
||||
is the runtime tree only (packages, PWA, launchers, docs — no tests, no git):
|
||||
extract it anywhere and run `start.bat` (Windows) or `./start.sh`
|
||||
(Linux/macOS). Python 3 has to be on `PATH`; `pip install pyusb` is only
|
||||
needed for direct SIMtrace2 capture.
|
||||
|
||||
The project is MIT-licensed (see `LICENSE`).
|
||||
@@ -0,0 +1,115 @@
|
||||
name: Release
|
||||
|
||||
# A pushed v* tag publishes a GitHub release with the wheel, the sdist and the
|
||||
# udev rule. A manual run (workflow_dispatch) does everything except publish,
|
||||
# so the pipeline can be exercised without minting a release.
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ['v*']
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: The tag matches the version strings
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
run: |
|
||||
version=$(python -c "from simtrace_analyser.version import __version__; print(__version__)")
|
||||
if [ "$GITHUB_REF_NAME" != "v$version" ]; then
|
||||
echo "tag $GITHUB_REF_NAME does not match version.py ($version)" >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -q "PWA_VERSION = '$version'" frontend/index.html
|
||||
|
||||
- name: Install build tooling and the runtime dependency
|
||||
run: python -m pip install --upgrade pip pyusb build
|
||||
|
||||
- name: Python tests
|
||||
run: python -m unittest discover -s tests
|
||||
|
||||
- name: PWA tests
|
||||
working-directory: frontend
|
||||
run: node --test
|
||||
|
||||
- name: Build the sdist and the wheel
|
||||
run: python -m build --sdist --wheel
|
||||
|
||||
- name: Build the runtime zip
|
||||
run: python scripts/build_release_zip.py --output dist
|
||||
|
||||
- name: The runtime zip is complete
|
||||
run: |
|
||||
python - <<'PY'
|
||||
import glob
|
||||
import zipfile
|
||||
archive = glob.glob('dist/*.zip')[0]
|
||||
names = zipfile.ZipFile(archive).namelist()
|
||||
for required in ('start.bat', 'frontend/index.html'):
|
||||
assert any(n.endswith('/' + required) for n in names), \
|
||||
'%s is missing from %s' % (required, archive)
|
||||
print('%s: %d entries, launchers and PWA bundled' % (archive, len(names)))
|
||||
PY
|
||||
|
||||
- name: The wheel bundles the PWA
|
||||
run: |
|
||||
python - <<'PY'
|
||||
import glob
|
||||
import zipfile
|
||||
wheel = glob.glob('dist/*.whl')[0]
|
||||
names = zipfile.ZipFile(wheel).namelist()
|
||||
for required in ('simtrace_analyser/web/index.html',
|
||||
'simtrace_analyser/web/sw.js'):
|
||||
assert required in names, '%s is missing from %s' % (required, wheel)
|
||||
print('%s: %d entries, PWA bundled' % (wheel, len(names)))
|
||||
PY
|
||||
|
||||
- name: The installed wheel serves the PWA
|
||||
run: |
|
||||
python -m venv /tmp/smoke
|
||||
/tmp/smoke/bin/pip install --no-deps dist/*.whl
|
||||
cd /tmp
|
||||
/tmp/smoke/bin/simtrace-analyser-server --capture disabled \
|
||||
--db /tmp/smoke.db --port 8099 &
|
||||
server=$!
|
||||
for _ in $(seq 1 20); do
|
||||
if curl -fsS http://127.0.0.1:8099/ -o /tmp/served-index.html; then
|
||||
break
|
||||
fi
|
||||
sleep 0.5
|
||||
done
|
||||
kill "$server"
|
||||
cmp /tmp/served-index.html "$GITHUB_WORKSPACE/frontend/index.html"
|
||||
/tmp/smoke/bin/python -c \
|
||||
"import simtrace_analyser.__main__ as m; p = m._default_web_dir(); assert p.endswith('/simtrace_analyser/web'), p"
|
||||
|
||||
- name: Keep the artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: dist
|
||||
path: dist/*
|
||||
|
||||
- name: Publish the release
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
gh release create "$GITHUB_REF_NAME" \
|
||||
--title "$GITHUB_REF_NAME" \
|
||||
--generate-notes \
|
||||
--notes-file .github/release-notes.md \
|
||||
dist/*.whl dist/*.tar.gz dist/*.zip 70-simtrace2-pysniff.rules
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Anton Troshin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,3 +1,4 @@
|
||||
graft frontend
|
||||
include LICENSE
|
||||
include README.md README_RUS.md 70-simtrace2-pysniff.rules
|
||||
include start.sh start.bat sniff-start.sh sniff-start.bat
|
||||
@@ -94,41 +94,76 @@ No other dependencies. PCAP, GSMTAP, and hex dump output use only Python stdlib.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
git clone https://github.com/anttro/simtrace-analyser.git
|
||||
cd simtrace-analyser
|
||||
pip install pyusb
|
||||
```
|
||||
There are two ways to get the analyser: install a release artifact (no git
|
||||
history), or clone the repository for development. Both bundle the same PWA.
|
||||
|
||||
Or install the package into your environment:
|
||||
### From a release (no git history)
|
||||
|
||||
Everything is on the
|
||||
[latest release](https://github.com/anttro/simtrace-analyser/releases/latest).
|
||||
|
||||
**Wheel** (the PWA is inside the package):
|
||||
|
||||
```sh
|
||||
pip install -e .
|
||||
# then run: simtrace-analyser-server [opts]
|
||||
# or: simtrace2-pysniff [opts]
|
||||
```
|
||||
|
||||
### From a release (no git checkout)
|
||||
|
||||
The release artifacts bundle the PWA, so an installed server serves the same UI
|
||||
as a clone. Download the wheel from the
|
||||
[latest release](https://github.com/anttro/simtrace-analyser/releases/latest)
|
||||
and install it:
|
||||
|
||||
```sh
|
||||
pipx install ./simtrace_analyser-2.7.10-py3-none-any.whl
|
||||
# or: pip install ./simtrace_analyser-2.7.10-py3-none-any.whl
|
||||
pip install ./simtrace_analyser-2.7.13-py3-none-any.whl
|
||||
|
||||
# then run (the UI is served at http://127.0.0.1:8081/):
|
||||
simtrace-analyser-server --capture gsmtap
|
||||
```
|
||||
|
||||
`pip` can also fetch the wheel straight from the release page:
|
||||
If your system Python is externally managed (PEP 668 — common on Arch and
|
||||
Fedora), install into a virtual environment first:
|
||||
`python3 -m venv .venv && . .venv/bin/activate`.
|
||||
|
||||
`pip` can also fetch the wheel straight from the release page (use the version
|
||||
shown there):
|
||||
|
||||
```sh
|
||||
pip install https://github.com/anttro/simtrace-analyser/releases/download/v2.7.10/simtrace_analyser-2.7.10-py3-none-any.whl
|
||||
pip install https://github.com/anttro/simtrace-analyser/releases/download/v2.7.13/simtrace_analyser-2.7.13-py3-none-any.whl
|
||||
```
|
||||
|
||||
With the GitHub CLI, download the latest wheel without knowing the version:
|
||||
|
||||
```sh
|
||||
gh release download --pattern '*.whl'
|
||||
pip install ./simtrace_analyser-*.whl
|
||||
```
|
||||
|
||||
**Source archive** — the *Source code (zip / tar.gz)* assets are the full tree
|
||||
without the git history: unzip, `cd` into it and use the launchers below
|
||||
(`./start.sh` on Linux/macOS, `start.bat` on Windows).
|
||||
|
||||
**Zip (Windows-friendly, no installer)** — `simtrace-analyser-2.7.13.zip` is
|
||||
the runtime tree only (packages, PWA, launchers, docs; no tests, no git):
|
||||
download it, extract it anywhere and run `start.bat` (Windows) or
|
||||
`./start.sh` (Linux/macOS). Python 3 has to be on `PATH`; `pip install pyusb`
|
||||
is only needed for direct SIMtrace2 capture.
|
||||
|
||||
### From a git clone (development)
|
||||
|
||||
```sh
|
||||
git clone https://github.com/anttro/simtrace-analyser.git
|
||||
cd simtrace-analyser
|
||||
./start.sh # Linux/macOS; on Windows: start.bat
|
||||
```
|
||||
|
||||
The launchers add the checkout to `PYTHONPATH` and run
|
||||
`python -m simtrace_analyser`, so no install step is needed for browsing and
|
||||
GSMTAP capture; they accept the server's options (`./start.sh --capture direct
|
||||
--port 8080`). Direct hardware capture needs **PyUSB** (`pip install pyusb`).
|
||||
|
||||
To get the console scripts (`simtrace-analyser-server`, `simtrace2-pysniff`)
|
||||
install the package instead:
|
||||
|
||||
```sh
|
||||
pip install -e .
|
||||
simtrace-analyser-server --capture gsmtap
|
||||
```
|
||||
|
||||
On Windows the `.bat` launchers run the `python` command, so Python 3 has to be
|
||||
on `PATH` (check with `python --version`); direct USB capture additionally
|
||||
needs the driver swap described in [USB Device Access](#usb-device-access).
|
||||
|
||||
## Running
|
||||
|
||||
### Analysis server + PWA (`simtrace-analyser-server`)
|
||||
@@ -397,6 +432,12 @@ Windows does not ship a libusb-compatible driver for SIMtrace2. Use
|
||||
|
||||
After that, `python -m simtrace2_pysniff` works identically to Linux.
|
||||
|
||||
The Windows launchers match their shell counterparts and pass every option
|
||||
through: `start.bat --capture direct` starts the analyser server and
|
||||
`sniff-start.bat` the capture utility. Both call the `python` command, so
|
||||
Python 3 must be on `PATH` (verify with `python --version` in a new command
|
||||
prompt).
|
||||
|
||||
## Utility CLI Options
|
||||
|
||||
```
|
||||
@@ -469,3 +510,10 @@ Use `--inactivity-timeout` to also trigger reconnect on silent firmware hangs.
|
||||
- **[sigrok_iso7816_stream](https://github.com/anttro/sigrok_iso7816_stream)** —
|
||||
FX2LP-based passive ISO 7816 sniffer that emits the same GSMTAP-SIM stream
|
||||
on UDP 4729 (accepted by this project's GSMTAP listener).
|
||||
|
||||
## License
|
||||
|
||||
MIT — see [LICENSE](LICENSE). The decoders and the PWA's DES/3DES
|
||||
implementation are original, written from the ISO 7816, ETSI and 3GPP
|
||||
specifications; no third-party code is bundled. PyUSB (BSD-3-Clause) is a
|
||||
runtime dependency for direct hardware capture.
|
||||
+74
-23
@@ -99,41 +99,79 @@ SIM/UICC из UDP-потока GSMTAP (или импортирует файлы
|
||||
|
||||
## Установка
|
||||
|
||||
```sh
|
||||
git clone https://github.com/anttro/simtrace-analyser.git
|
||||
cd simtrace-analyser
|
||||
pip install pyusb
|
||||
```
|
||||
Есть два способа получить анализатор: установить артефакт релиза (без истории
|
||||
git) или склонировать репозиторий для разработки. В обоих случаях в комплекте
|
||||
одна и та же PWA.
|
||||
|
||||
Или установите пакет в своё окружение:
|
||||
### Из релиза (без истории git)
|
||||
|
||||
Всё находится на странице
|
||||
[последнего релиза](https://github.com/anttro/simtrace-analyser/releases/latest).
|
||||
|
||||
**Wheel** (PWA внутри пакета):
|
||||
|
||||
```sh
|
||||
pip install -e .
|
||||
# затем запуск: simtrace-analyser-server [opts]
|
||||
# или: simtrace2-pysniff [opts]
|
||||
```
|
||||
|
||||
### Из релиза (без клонирования репозитория)
|
||||
|
||||
Релизные артефакты содержат PWA внутри пакета, поэтому установленный сервер
|
||||
отдаёт тот же интерфейс, что и при запуске из клона. Скачайте wheel со
|
||||
страницы [последнего релиза](https://github.com/anttro/simtrace-analyser/releases/latest)
|
||||
и установите его:
|
||||
|
||||
```sh
|
||||
pipx install ./simtrace_analyser-2.7.10-py3-none-any.whl
|
||||
# или: pip install ./simtrace_analyser-2.7.10-py3-none-any.whl
|
||||
pip install ./simtrace_analyser-2.7.13-py3-none-any.whl
|
||||
|
||||
# затем запуск (интерфейс отдаётся на http://127.0.0.1:8081/):
|
||||
simtrace-analyser-server --capture gsmtap
|
||||
```
|
||||
|
||||
`pip` также может скачать wheel прямо со страницы релиза:
|
||||
Если системный Python управляется извне (PEP 668 — обычное дело в Arch и
|
||||
Fedora), сначала создайте виртуальное окружение:
|
||||
`python3 -m venv .venv && . .venv/bin/activate`.
|
||||
|
||||
`pip` также может скачать wheel прямо со страницы релиза (подставьте версию,
|
||||
указанную там):
|
||||
|
||||
```sh
|
||||
pip install https://github.com/anttro/simtrace-analyser/releases/download/v2.7.10/simtrace_analyser-2.7.10-py3-none-any.whl
|
||||
pip install https://github.com/anttro/simtrace-analyser/releases/download/v2.7.13/simtrace_analyser-2.7.13-py3-none-any.whl
|
||||
```
|
||||
|
||||
С GitHub CLI можно скачать wheel последнего релиза, не зная версии:
|
||||
|
||||
```sh
|
||||
gh release download --pattern '*.whl'
|
||||
pip install ./simtrace_analyser-*.whl
|
||||
```
|
||||
|
||||
**Архив исходников** — ассеты *Source code (zip / tar.gz)* содержат полное
|
||||
дерево без истории git: распакуйте, перейдите в каталог и используйте скрипты
|
||||
запуска ниже (`./start.sh` в Linux/macOS, `start.bat` в Windows).
|
||||
|
||||
**Zip (для Windows, без установки)** — `simtrace-analyser-2.7.13.zip`
|
||||
содержит только рабочее дерево (пакеты, PWA, скрипты запуска, документацию;
|
||||
без тестов и git): скачайте, распакуйте в любое место и запустите `start.bat`
|
||||
(Windows) или `./start.sh` (Linux/macOS). Python 3 должен быть в `PATH`;
|
||||
`pip install pyusb` нужен только для прямого захвата с SIMtrace2.
|
||||
|
||||
### Из клона репозитория (для разработки)
|
||||
|
||||
```sh
|
||||
git clone https://github.com/anttro/simtrace-analyser.git
|
||||
cd simtrace-analyser
|
||||
./start.sh # Linux/macOS; в Windows: start.bat
|
||||
```
|
||||
|
||||
Скрипты запуска добавляют каталог в `PYTHONPATH` и запускают
|
||||
`python -m simtrace_analyser`, поэтому для просмотра и захвата GSMTAP
|
||||
установка не нужна; они принимают параметры сервера
|
||||
(`./start.sh --capture direct --port 8080`). Для прямого захвата с
|
||||
оборудования нужен **PyUSB** (`pip install pyusb`).
|
||||
|
||||
Чтобы получить консольные команды (`simtrace-analyser-server`,
|
||||
`simtrace2-pysniff`), установите пакет:
|
||||
|
||||
```sh
|
||||
pip install -e .
|
||||
simtrace-analyser-server --capture gsmtap
|
||||
```
|
||||
|
||||
В Windows скрипты `.bat` вызывают команду `python`, поэтому Python 3 должен
|
||||
быть в `PATH` (проверьте `python --version`); для прямого захвата по USB
|
||||
дополнительно нужна замена драйвера, описанная в разделе
|
||||
[Доступ к USB-устройству](#доступ-к-usb-устройству).
|
||||
|
||||
## Запуск
|
||||
|
||||
### Анализирующий сервер + PWA (`simtrace-analyser-server`)
|
||||
@@ -407,6 +445,12 @@ sudo udevadm trigger
|
||||
|
||||
После этого `python -m simtrace2_pysniff` работает так же, как в Linux.
|
||||
|
||||
Скрипты запуска для Windows соответствуют shell-версиям и передают все
|
||||
параметры: `start.bat --capture direct` запускает сервер анализатора, а
|
||||
`sniff-start.bat` — утилиту захвата. Оба вызывают команду `python`, поэтому
|
||||
Python 3 должен быть в `PATH` (проверьте `python --version` в новом окне
|
||||
команды).
|
||||
|
||||
## Параметры CLI утилиты
|
||||
|
||||
```
|
||||
@@ -486,3 +530,10 @@ for msg in session.iter_messages():
|
||||
- **[sigrok_iso7816_stream](https://github.com/anttro/sigrok_iso7816_stream)** —
|
||||
пассивный ISO 7816 сниффер на базе FX2LP, выдающий такой же поток
|
||||
GSMTAP-SIM на UDP 4729 (принимается GSMTAP-листенером этого проекта).
|
||||
|
||||
## Лицензия
|
||||
|
||||
MIT — см. [LICENSE](LICENSE). Декодеры и реализация DES/3DES в PWA написаны
|
||||
самостоятельно по спецификациям ISO 7816, ETSI и 3GPP; сторонний код в проект
|
||||
не включён. PyUSB (BSD-3-Clause) — зависимость для прямого захвата с
|
||||
оборудования.
|
||||
+1
-1
@@ -3389,7 +3389,7 @@ function onSearchChange() {
|
||||
}
|
||||
|
||||
// ──────────────────── Versioning ────────────────────
|
||||
const PWA_VERSION = '2.7.10';
|
||||
const PWA_VERSION = '2.7.13';
|
||||
const MIN_SERVER_VERSION = '2.3.0';
|
||||
|
||||
function parseVersion(v) {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
const CACHE = 'simtrace-analyser-v92';
|
||||
const CACHE = 'simtrace-analyser-v95';
|
||||
|
||||
const PRECACHE = [
|
||||
'index.html',
|
||||
|
||||
+4
-3
@@ -1,13 +1,14 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=64"]
|
||||
requires = ["setuptools>=77"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "simtrace-analyser"
|
||||
version = "2.7.10"
|
||||
version = "2.7.13"
|
||||
description = "SIMtrace Analyser — APDU trace analyser for GSMTAP/PCAP captures (bundles the simtrace2-pysniff utility)"
|
||||
readme = {file = "README.md", content-type = "text/markdown"}
|
||||
license = {text = "GPL-2.0-or-later"}
|
||||
license = "MIT"
|
||||
license-files = ["LICENSE"]
|
||||
requires-python = ">=3.9"
|
||||
dependencies = [
|
||||
"pyusb>=1.2",
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
"""Build the ready-to-run runtime zip for a GitHub release.
|
||||
|
||||
The wheel needs ``pip`` and the GitHub source archive carries the whole
|
||||
repository (tests, CI, the gitignored ``tools/``). This zip is the runtime
|
||||
tree only: extract it anywhere, run ``start.bat`` on Windows or
|
||||
``./start.sh`` elsewhere, and the server serves the bundled PWA. Python 3
|
||||
must be on ``PATH``; PyUSB is only needed for direct hardware capture.
|
||||
|
||||
The build is deliberately simple - the explicit include lists below are the
|
||||
whole manifest, so nothing from ``tests/``, ``.github/``, ``pyproject.toml``
|
||||
or a build directory can leak into the archive by accident.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parents[1]
|
||||
# The script lives outside the packages; import the version from the checkout.
|
||||
sys.path.insert(0, str(REPO))
|
||||
|
||||
from simtrace_analyser.version import __version__ # noqa: E402
|
||||
|
||||
# The Python packages (all modules, no caches).
|
||||
PACKAGES = ('simtrace_analyser', 'simtrace2_pysniff')
|
||||
|
||||
# The PWA runtime files; ``tests/`` and ``package.json`` are dev-only.
|
||||
FRONTEND = 'frontend'
|
||||
FRONTEND_EXCLUDE = ('tests', 'package.json')
|
||||
|
||||
# Repo-root files that make the extracted tree usable. LICENSE is included
|
||||
# once the repository has one.
|
||||
ROOT_FILES = (
|
||||
'start.sh',
|
||||
'start.bat',
|
||||
'sniff-start.sh',
|
||||
'sniff-start.bat',
|
||||
'70-simtrace2-pysniff.rules',
|
||||
'README.md',
|
||||
'README_RUS.md',
|
||||
'frontend.png',
|
||||
'LICENSE',
|
||||
)
|
||||
|
||||
# Skip editor/byte-code droppings wherever they appear.
|
||||
SKIP_DIRS = ('__pycache__',)
|
||||
SKIP_NAMES = ('.DS_Store',)
|
||||
SKIP_SUFFIXES = ('.pyc',)
|
||||
|
||||
|
||||
def _files(root):
|
||||
"""Yield the regular files under *root*, sorted and cache-free."""
|
||||
for path in sorted(root.rglob('*')):
|
||||
if not path.is_file():
|
||||
continue
|
||||
if any(part in SKIP_DIRS for part in path.parts):
|
||||
continue
|
||||
if path.name in SKIP_NAMES or path.suffix in SKIP_SUFFIXES:
|
||||
continue
|
||||
yield path
|
||||
|
||||
|
||||
def _write_bat(zf, path, arcname):
|
||||
"""Write a .bat entry with CRLF line endings.
|
||||
|
||||
The checkout stores the batch files with LF, and cmd.exe mishandles a
|
||||
batch file whose lines end in a bare LF (labels and ``goto`` in
|
||||
particular), so the archive gets the Windows form. The source files
|
||||
stay untouched. ``writestr`` does not copy the file mode, so set it.
|
||||
"""
|
||||
data = path.read_bytes().replace(b'\r\n', b'\n').replace(b'\n', b'\r\n')
|
||||
info = zipfile.ZipInfo.from_file(path, arcname)
|
||||
info.external_attr = 0o644 << 16
|
||||
info.compress_type = zipfile.ZIP_DEFLATED
|
||||
zf.writestr(info, data)
|
||||
|
||||
|
||||
def build_zip(output_dir, version=None):
|
||||
"""Write ``simtrace-analyser-<version>.zip`` into *output_dir*.
|
||||
|
||||
Returns the archive path. The zip has a single top-level directory
|
||||
``simtrace-analyser-<version>/`` holding the runtime tree.
|
||||
"""
|
||||
version = version or __version__
|
||||
output_dir = Path(output_dir)
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
archive = output_dir / ('simtrace-analyser-%s.zip' % version)
|
||||
top = 'simtrace-analyser-%s' % version
|
||||
|
||||
with zipfile.ZipFile(archive, 'w', zipfile.ZIP_DEFLATED) as zf:
|
||||
for package in PACKAGES:
|
||||
for path in _files(REPO / package):
|
||||
zf.write(path, '%s/%s' % (top, path.relative_to(REPO).as_posix()))
|
||||
|
||||
for path in _files(REPO / FRONTEND):
|
||||
rel = path.relative_to(REPO / FRONTEND)
|
||||
if rel.parts[0] in FRONTEND_EXCLUDE:
|
||||
continue
|
||||
zf.write(path, '%s/%s' % (top, path.relative_to(REPO).as_posix()))
|
||||
|
||||
for name in ROOT_FILES:
|
||||
path = REPO / name
|
||||
if not path.is_file():
|
||||
# LICENSE is not in the repository yet; include it once it is.
|
||||
continue
|
||||
arcname = '%s/%s' % (top, name)
|
||||
if path.suffix == '.bat':
|
||||
_write_bat(zf, path, arcname)
|
||||
else:
|
||||
zf.write(path, arcname)
|
||||
|
||||
return archive
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Build the runtime zip attached to a release')
|
||||
parser.add_argument('--output', default='dist', metavar='DIR',
|
||||
help='directory for the archive (default: dist)')
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
archive = build_zip(args.output)
|
||||
with zipfile.ZipFile(archive) as zf:
|
||||
entries = len(zf.namelist())
|
||||
print(archive)
|
||||
print('%d entries' % entries)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
@@ -1,3 +1,3 @@
|
||||
"""Single source of truth for the SIMtrace Analyser version."""
|
||||
|
||||
__version__ = '2.7.10'
|
||||
__version__ = '2.7.13'
|
||||
+7
-7
@@ -59,7 +59,7 @@ if "%~1"=="--inactivity-timeout" (
|
||||
shift
|
||||
goto :parse_args
|
||||
)
|
||||
set "EXTRA_ARGS=!EXTRA_ARGS! %~1"
|
||||
set "EXTRA_ARGS=!EXTRA_ARGS! %1"
|
||||
shift
|
||||
goto :parse_args
|
||||
|
||||
@@ -67,11 +67,11 @@ goto :parse_args
|
||||
echo Starting simtrace2-pysniff...
|
||||
|
||||
set "ARGS="
|
||||
if not "%GSMTAP%"=="" set "ARGS=!ARGS! --gsmtap %GSMTAP%"
|
||||
if not "%PCAP%"=="" set "ARGS=!ARGS! --pcap %PCAP%"
|
||||
if not "%OUTPUT%"=="" set "ARGS=!ARGS! --output %OUTPUT%"
|
||||
if not "%FORMAT%"=="" set "ARGS=!ARGS! --format %FORMAT%"
|
||||
if not "%INACTIVITY_TIMEOUT%"=="0" set "ARGS=!ARGS! --inactivity-timeout %INACTIVITY_TIMEOUT%"
|
||||
if not "%GSMTAP%"=="" set "ARGS=!ARGS! --gsmtap "%GSMTAP%""
|
||||
if not "%PCAP%"=="" set "ARGS=!ARGS! --pcap "%PCAP%""
|
||||
if not "%OUTPUT%"=="" set "ARGS=!ARGS! --output "%OUTPUT%""
|
||||
if not "%FORMAT%"=="" set "ARGS=!ARGS! --format "%FORMAT%""
|
||||
if not "%INACTIVITY_TIMEOUT%"=="0" set "ARGS=!ARGS! --inactivity-timeout "%INACTIVITY_TIMEOUT%""
|
||||
|
||||
set "PYTHONPATH=%~dp0"
|
||||
python -m simtrace2_pysniff %ARGS% %EXTRA_ARGS%
|
||||
python -m simtrace2_pysniff !ARGS! !EXTRA_ARGS!
|
||||
@@ -3,7 +3,7 @@ setlocal enabledelayedexpansion
|
||||
|
||||
:: simtrace-analyser-server startup script for Windows
|
||||
::
|
||||
:: Usage: start.bat [--capture gsmtap|direct] [--port PORT] [--db PATH] [--gsmtap-port PORT]
|
||||
:: Usage: start.bat [--capture gsmtap|direct|disabled] [--port PORT] [--db PATH] [--gsmtap-port PORT]
|
||||
::
|
||||
:: Options set via environment variables act as defaults; CLI flags override them.
|
||||
|
||||
@@ -42,7 +42,7 @@ if "%~1"=="--gsmtap-port" (
|
||||
shift
|
||||
goto :parse_args
|
||||
)
|
||||
set "EXTRA_ARGS=!EXTRA_ARGS! %~1"
|
||||
set "EXTRA_ARGS=!EXTRA_ARGS! %1"
|
||||
shift
|
||||
goto :parse_args
|
||||
|
||||
@@ -50,5 +50,10 @@ goto :parse_args
|
||||
echo Starting simtrace-analyser-server...
|
||||
echo PWA (simtrace-analyser) will be served at http://127.0.0.1:%PORT%/
|
||||
|
||||
set "ARGS=--capture "%CAPTURE%" --port "%PORT%""
|
||||
if not "%DB%"=="" set "ARGS=!ARGS! --db "%DB%""
|
||||
if /i "%CAPTURE%"=="gsmtap" set "ARGS=!ARGS! --gsmtap-port "%GSMTAP_PORT%""
|
||||
set "ARGS=!ARGS! !EXTRA_ARGS!"
|
||||
|
||||
set "PYTHONPATH=%~dp0"
|
||||
python -m simtrace_analyser --capture %CAPTURE% --port %PORT% --gsmtap-port %GSMTAP_PORT% %EXTRA_ARGS%
|
||||
python -m simtrace_analyser !ARGS!
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
# simtrace-analyser-server startup script
|
||||
#
|
||||
# Usage: ./start.sh [--capture gsmtap|direct] [--port PORT] [--db PATH] [--gsmtap-port PORT]
|
||||
# Usage: ./start.sh [--capture gsmtap|direct|disabled] [--port PORT] [--db PATH] [--gsmtap-port PORT]
|
||||
#
|
||||
# Options set via environment variables act as defaults; CLI flags override them.
|
||||
|
||||
|
||||
@@ -70,6 +70,15 @@ class TestDistributionMetadata(unittest.TestCase):
|
||||
# The sdist carries frontend/, so a wheel built from it has the PWA.
|
||||
self.assertIn('graft frontend', self._read('MANIFEST.in'))
|
||||
|
||||
def test_license_is_mit(self):
|
||||
# The project is MIT; the metadata and the file must not drift apart.
|
||||
pyproject = self._read('pyproject.toml')
|
||||
self.assertIn('license = "MIT"', pyproject)
|
||||
self.assertIn('license-files = ["LICENSE"]', pyproject)
|
||||
license_text = self._read('LICENSE')
|
||||
self.assertTrue(license_text.startswith('MIT License\n'))
|
||||
self.assertIn('Permission is hereby granted, free of charge', license_text)
|
||||
|
||||
def test_setup_py_bundles_the_pwa(self):
|
||||
setup_py = self._read('setup.py')
|
||||
self.assertIn('frontend', setup_py)
|
||||
|
||||
@@ -224,7 +224,7 @@ class TestImportEndpoint(unittest.TestCase):
|
||||
class TestVersion(unittest.TestCase):
|
||||
def test_package_version(self):
|
||||
from simtrace2_pysniff import __version__
|
||||
self.assertEqual(__version__, '2.7.10')
|
||||
self.assertEqual(__version__, '2.7.13')
|
||||
|
||||
def test_status_includes_version(self):
|
||||
import os
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
"""Tests for the ready-to-run release zip (scripts/build_release_zip.py)."""
|
||||
|
||||
import importlib.util
|
||||
import re
|
||||
import tempfile
|
||||
import unittest
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
from simtrace_analyser.version import __version__
|
||||
|
||||
REPO = Path(__file__).resolve().parents[1]
|
||||
|
||||
# scripts/ is not a package, so load the builder from its path.
|
||||
_spec = importlib.util.spec_from_file_location(
|
||||
'build_release_zip', REPO / 'scripts' / 'build_release_zip.py')
|
||||
build_release_zip = importlib.util.module_from_spec(_spec)
|
||||
_spec.loader.exec_module(build_release_zip)
|
||||
|
||||
|
||||
class TestReleaseZip(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls._tmp = tempfile.TemporaryDirectory()
|
||||
cls.archive = build_release_zip.build_zip(cls._tmp.name)
|
||||
with zipfile.ZipFile(cls.archive) as zf:
|
||||
cls.names = zf.namelist()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls._tmp.cleanup()
|
||||
|
||||
def test_name_and_single_top_level_directory(self):
|
||||
top = 'simtrace-analyser-%s' % __version__
|
||||
self.assertEqual(self.archive.name, top + '.zip')
|
||||
for name in self.names:
|
||||
self.assertTrue(name.startswith(top + '/'),
|
||||
'%s is outside %s/' % (name, top))
|
||||
|
||||
def test_required_entries(self):
|
||||
top = 'simtrace-analyser-%s' % __version__
|
||||
for rel in (
|
||||
'simtrace_analyser/__main__.py',
|
||||
'simtrace_analyser/version.py',
|
||||
'simtrace2_pysniff/__main__.py',
|
||||
'frontend/index.html',
|
||||
'frontend/sw.js',
|
||||
'frontend/manifest.json',
|
||||
'start.sh',
|
||||
'start.bat',
|
||||
'sniff-start.sh',
|
||||
'sniff-start.bat',
|
||||
'70-simtrace2-pysniff.rules',
|
||||
'LICENSE',
|
||||
'README.md'):
|
||||
self.assertIn('%s/%s' % (top, rel), self.names)
|
||||
|
||||
def test_dev_only_entries_are_excluded(self):
|
||||
for name in self.names:
|
||||
self.assertNotIn('/tests/', name)
|
||||
self.assertNotIn('/__pycache__/', name)
|
||||
self.assertNotIn('.pyc', name)
|
||||
self.assertNotIn('/.git', name)
|
||||
self.assertNotIn('/.github/', name)
|
||||
self.assertNotIn(
|
||||
'simtrace-analyser-%s/frontend/package.json' % __version__,
|
||||
self.names)
|
||||
self.assertNotIn(
|
||||
'simtrace-analyser-%s/pyproject.toml' % __version__, self.names)
|
||||
|
||||
def test_precache_files_are_in_the_zip(self):
|
||||
# Drift guard: every asset the service worker precaches must travel
|
||||
# with the PWA, otherwise an offline install fails on a fresh extract.
|
||||
text = (REPO / 'frontend' / 'sw.js').read_text()
|
||||
match = re.search(r'PRECACHE\s*=\s*\[(.*?)\]', text, re.S)
|
||||
self.assertIsNotNone(match, 'no PRECACHE array in frontend/sw.js')
|
||||
assets = re.findall(r"'([^']+)'", match.group(1))
|
||||
self.assertTrue(assets)
|
||||
top = 'simtrace-analyser-%s' % __version__
|
||||
for asset in assets:
|
||||
self.assertIn('%s/frontend/%s' % (top, asset), self.names,
|
||||
'precached %s is missing from the zip' % asset)
|
||||
|
||||
def test_batch_files_use_crlf(self):
|
||||
with zipfile.ZipFile(self.archive) as zf:
|
||||
bats = [n for n in self.names if n.endswith('.bat')]
|
||||
self.assertTrue(bats)
|
||||
for name in bats:
|
||||
data = zf.read(name)
|
||||
self.assertIn(b'\r\n', data, name)
|
||||
self.assertNotIn(b'\n', data.replace(b'\r\n', b''),
|
||||
'%s has a bare LF' % name)
|
||||
|
||||
def test_shell_script_is_lf_and_executable(self):
|
||||
name = 'simtrace-analyser-%s/start.sh' % __version__
|
||||
with zipfile.ZipFile(self.archive) as zf:
|
||||
info = zf.getinfo(name)
|
||||
data = zf.read(name)
|
||||
self.assertNotIn(b'\r\n', data)
|
||||
self.assertTrue(info.external_attr >> 16 & 0o100,
|
||||
'start.sh lost its executable bit')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user