7 Commits
Author SHA1 Message Date
catarrh 5fcd8e9fd6 Relicense to MIT (v2.7.13)
Release / release (push) Waiting to run
The project's only runtime dependency is PyUSB (BSD-3-Clause) and it bundles
no third-party code (the PWA's DES/3DES is written from the FIPS 46-3 tables,
AES uses WebCrypto, the decoders come from the ISO 7816/ETSI/3GPP specs), so
nothing forced GPL; the GPL-2.0-or-later declaration is replaced with MIT.

- LICENSE: the MIT text, Copyright (c) 2026 Anton Troshin.
- pyproject: license = "MIT" + license-files = ["LICENSE"] (PEP 639), and the
  build requirement moves to setuptools>=77 for the SPDX form.  The wheel now
  carries dist-info/licenses/LICENSE and License-Expression: MIT, the sdist
  and the runtime zip carry LICENSE at the top.
- READMEs (EN/RU): a License section (original decoders/DES implementation,
  PyUSB as the only runtime dependency).
- tests: the packaging test pins the MIT declaration and the LICENSE text;
  the release-zip test requires LICENSE in the archive.
- version 2.7.13; SW cache v95.

Copies published before this tag (v2.7.10-v2.7.12) remain under the license
they were released with.
2026-10-21 01:03:55 +03:00
catarrh e00a96b918 Release a Windows-friendly runtime zip (v2.7.12)
Release / release (push) Waiting to run
The releases carried the wheel and the sdist; the only zip was GitHub's source
archive of the whole repository (tests, CI, tools).  A Windows user just wants
to extract a folder and double-click start.bat.

- scripts/build_release_zip.py builds simtrace-analyser-<version>.zip with a
  single top-level directory: the two Python packages, the PWA runtime files
  (frontend/tests and package.json stay out), the four launchers, the udev
  rule, the READMEs and the screenshot.  The .bat entries are written with
  CRLF line endings (cmd.exe mishandles bare-LF labels/goto), the .sh files
  keep their executable bit; LICENSE is included automatically once the repo
  has one.
- tests/test_release_zip.py pins the archive shape, the exclusions, the sw.js
  PRECACHE drift (every precached asset must travel), the CRLF/LF line endings
  and start.sh's mode.
- the release workflow builds the zip, checks it carries start.bat and the
  PWA, uploads it with the run and attaches it to the release.
- READMEs (EN/RU) and the release-notes template offer the zip as the
  Windows-friendly option; version 2.7.12, PWA cache v94.

Verified: the built zip (34 entries, 658 KB compressed) extracted and started
on Linux serves the same index.html; 553 Python + 80 Node tests.
2026-10-21 00:17:06 +03:00
catarrh b19abbd514 CI: install pyusb before the tests
tests/test_capture.py builds a DirectSniffer, whose lazy import reaches
simtrace2_pysniff.device -> usb.core; the runner has no PyUSB, so two tests
failed there while passing on a workstation that already had it.  The job now
installs the declared runtime dependency next to build.
2026-10-20 23:29:07 +03:00
catarrh 0b3c483c0b CI: a tag push builds, tests and publishes the release
The artifacts and the GitHub release were made by hand (`python -m build` plus
`gh release create`).  `.github/workflows/release.yml` now does it from the
pushed tag: Python and PWA tests, the sdist/wheel build, a check that the
wheel carries the PWA, an install-and-serve smoke test of that wheel, and
`gh release create` with the generated notes (prepended by
`.github/release-notes.md`) and the three assets (wheel, sdist, udev rule).

A `workflow_dispatch` run does everything except publish, so the pipeline can
be exercised without minting a release; the artifacts land in the run.  A tag
that does not match version.py/PWA_VERSION fails before anything is published.
2026-10-20 23:28:13 +03:00
catarrh 9e46c64490 README: pip is the install command (pipx dropped)
The release notes and the README's local-wheel examples still led with
`pipx install`; only the direct-URL form had been switched to pip earlier.
Both READMEs now show a plain `pip install <wheel>` (with the PEP 668 note to
use a virtual environment when the system Python is externally managed), and
the `gh release download` example installs with pip too.  The v2.7.10 and
v2.7.11 release bodies are edited to match.
2026-10-20 23:25:24 +03:00
catarrh efcb6c6c3f Version 2.7.11: the Windows launcher and install-doc fixes
Ships the launcher/doc commit on top of v2.7.10 as a release:
- start.bat passes --db/DB to the server (it only stored the value before) and
  sends --gsmtap-port only for gsmtap capture, like start.sh;
- both .bat launchers quote their values and keep the quoting of pass-through
  options, so paths with spaces work (--db, --web-dir, --pcap, --output);
- the READMEs describe both install paths (release artifacts or a git clone)
  and document the launchers and the Windows python-on-PATH requirement.

Version strings in version.py, pyproject.toml, PWA_VERSION and the version
test move together; SW cache simtrace-analyser-v93.
2026-10-20 23:18:57 +03:00
catarrh 65c8ab215b README: two explicit install paths; Windows launchers pass --db and quote paths
- the Install section now leads with the release artifacts (the wheel via
  pipx/pip, the direct release URL, `gh release download` for the
  version-agnostic case, and the source archive) and keeps the git clone as
  the development path.  It finally names the launchers (./start.sh,
  start.bat): a clone needs no install, but the README never said how to start
  it while the Running section only showed the console script that an install
  provides.
- Windows: the USB section now says the .bat launchers mirror the shell ones
  and call the `python` command (Python 3 must be on PATH); the Install section
  carries the same note.
- start.bat: --db/DB was parsed and then never passed to the server; it is
  passed now, and --gsmtap-port is sent only for gsmtap capture (as start.sh
  already did).  Both .bat files passed values unquoted and stripped the
  quotes of pass-through options, so paths with spaces broke
  (--db "C:\My Data\...", --web-dir, --pcap, --output); values are quoted now
  and pass-through keeps the original quoting (argument assembly through
  delayed expansion).
- the launcher usage comments include the `disabled` capture mode.
- verified the argument assembly with Wine cmd.exe and an echo harness: the
  quoted paths survive, --db arrives, --gsmtap-port appears only for gsmtap;
  547 Python + 80 Node tests.
2026-10-20 23:15:54 +03:00
17 changed files with 574 additions and 64 deletions

No files matched your search

+22
View File
@@ -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`).
+115
View File
@@ -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
+21
View File
@@ -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
View File
@@ -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
+71 -23
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1,4 +1,4 @@
const CACHE = 'simtrace-analyser-v92';
const CACHE = 'simtrace-analyser-v95';
const PRECACHE = [
'index.html',
+4 -3
View File
@@ -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",
+132
View File
@@ -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 -1
View File
@@ -1,3 +1,3 @@
"""Single source of truth for the SIMtrace Analyser version."""
__version__ = '2.7.10'
__version__ = '2.7.13'
+7 -7
View File
@@ -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!
+8 -3
View File
@@ -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 -1
View File
@@ -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.
+9
View File
@@ -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)
+1 -1
View File
@@ -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
+105
View File
@@ -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()