wows-replay-parser 0.1.1__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.
- wows_replay_parser-0.1.1/.github/CODE_OF_CONDUCT.md +17 -0
- wows_replay_parser-0.1.1/.github/CONTRIBUTING.md +70 -0
- wows_replay_parser-0.1.1/.github/ISSUE_TEMPLATE/bug_report.yml +72 -0
- wows_replay_parser-0.1.1/.github/ISSUE_TEMPLATE/config.yml +1 -0
- wows_replay_parser-0.1.1/.github/ISSUE_TEMPLATE/feature_request.yml +32 -0
- wows_replay_parser-0.1.1/.github/ISSUE_TEMPLATE/regression.yml +42 -0
- wows_replay_parser-0.1.1/.github/PULL_REQUEST_TEMPLATE.md +36 -0
- wows_replay_parser-0.1.1/.github/SECRETS.md +24 -0
- wows_replay_parser-0.1.1/.github/dependabot.yml +32 -0
- wows_replay_parser-0.1.1/.github/workflows/canary.yml +145 -0
- wows_replay_parser-0.1.1/.github/workflows/ci.yml +36 -0
- wows_replay_parser-0.1.1/.github/workflows/docs.yml +44 -0
- wows_replay_parser-0.1.1/.github/workflows/publish.yml +76 -0
- wows_replay_parser-0.1.1/.gitignore +20 -0
- wows_replay_parser-0.1.1/CHANGELOG.md +56 -0
- wows_replay_parser-0.1.1/CLAUDE.md +611 -0
- wows_replay_parser-0.1.1/KNOWN_ISSUES.md +67 -0
- wows_replay_parser-0.1.1/LICENSE +192 -0
- wows_replay_parser-0.1.1/PKG-INFO +577 -0
- wows_replay_parser-0.1.1/README.md +346 -0
- wows_replay_parser-0.1.1/SECURITY.md +41 -0
- wows_replay_parser-0.1.1/docs/api/events.md +11 -0
- wows_replay_parser-0.1.1/docs/api/merge.md +14 -0
- wows_replay_parser-0.1.1/docs/api/parsed-replay.md +14 -0
- wows_replay_parser-0.1.1/docs/api/state.md +21 -0
- wows_replay_parser-0.1.1/docs/architecture.md +730 -0
- wows_replay_parser-0.1.1/docs/changelog.md +1 -0
- wows_replay_parser-0.1.1/docs/concepts/dual-perspective.md +58 -0
- wows_replay_parser-0.1.1/docs/concepts/events.md +62 -0
- wows_replay_parser-0.1.1/docs/concepts/parser-pipeline.md +90 -0
- wows_replay_parser-0.1.1/docs/development/contributing.md +1 -0
- wows_replay_parser-0.1.1/docs/development/security.md +1 -0
- wows_replay_parser-0.1.1/docs/getting-started.md +92 -0
- wows_replay_parser-0.1.1/docs/index.md +43 -0
- wows_replay_parser-0.1.1/mkdocs.yml +79 -0
- wows_replay_parser-0.1.1/pyproject.toml +116 -0
- wows_replay_parser-0.1.1/scripts/inspect_blobs.py +495 -0
- wows_replay_parser-0.1.1/scripts/update-gamedata.sh +85 -0
- wows_replay_parser-0.1.1/scripts/verify_method_sort.py +796 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/__init__.py +19 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/api.py +614 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/cli.py +351 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/consumable_calc.py +548 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/events/__init__.py +231 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/events/models.py +905 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/events/stream.py +1515 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/gamedata/__init__.py +11 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/gamedata/alias_registry.py +213 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/gamedata/blob_decoders.py +281 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/gamedata/def_loader.py +248 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/gamedata/entity_registry.py +234 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/gamedata/schema_builder.py +413 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/gamedata_sync.py +278 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/interfaces.py +62 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/merge.py +709 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/packets/__init__.py +30 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/packets/decoder.py +1447 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/packets/implemented_by_parsers.py +676 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/packets/method_id_detector.py +792 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/packets/nested_property.py +115 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/packets/type_id_detector.py +126 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/packets/types.py +146 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/replay/__init__.py +19 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/replay/reader.py +185 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/ribbons.py +288 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/roster.py +742 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/ship_config.py +106 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/state/__init__.py +33 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/state/models.py +220 -0
- wows_replay_parser-0.1.1/src/wows_replay_parser/state/tracker.py +1683 -0
- wows_replay_parser-0.1.1/tests/conftest.py +164 -0
- wows_replay_parser-0.1.1/tests/fixtures/README.md +135 -0
- wows_replay_parser-0.1.1/tests/test_chat.py +157 -0
- wows_replay_parser-0.1.1/tests/test_events.py +229 -0
- wows_replay_parser-0.1.1/tests/test_gamedata.py +70 -0
- wows_replay_parser-0.1.1/tests/test_integration.py +85 -0
- wows_replay_parser-0.1.1/tests/test_interfaces.py +129 -0
- wows_replay_parser-0.1.1/tests/test_merge.py +424 -0
- wows_replay_parser-0.1.1/tests/test_ribbons.py +173 -0
- wows_replay_parser-0.1.1/tests/test_state_tracker.py +265 -0
- wows_replay_parser-0.1.1/uv.lock +1010 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Code of Conduct
|
|
2
|
+
|
|
3
|
+
This project adopts the [Contributor Covenant, version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).
|
|
4
|
+
|
|
5
|
+
Please read it before participating. By contributing you agree to uphold
|
|
6
|
+
its standards.
|
|
7
|
+
|
|
8
|
+
## Enforcement
|
|
9
|
+
|
|
10
|
+
Report unacceptable behaviour to **tb@kleinundpartner.at**.
|
|
11
|
+
|
|
12
|
+
All reports will be reviewed and investigated promptly and fairly. The
|
|
13
|
+
project maintainer is obligated to respect the privacy and security of the
|
|
14
|
+
reporter of any incident.
|
|
15
|
+
|
|
16
|
+
Enforcement follows the Community Impact Guidelines described in the
|
|
17
|
+
Contributor Covenant (correction → warning → temporary ban → permanent ban).
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for taking the time to contribute. This project is small enough that
|
|
4
|
+
the process is informal, but a few conventions keep things tidy.
|
|
5
|
+
|
|
6
|
+
## Development setup
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
git clone https://github.com/toalba/wows-replay-parser.git
|
|
10
|
+
cd wows-replay-parser
|
|
11
|
+
uv venv
|
|
12
|
+
source .venv/bin/activate
|
|
13
|
+
uv sync --all-extras
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Tests and the canary workflow need a `.wowsreplay` fixture plus the gamedata
|
|
17
|
+
tree. See [tests/fixtures/README.md](https://github.com/toalba/wows-replay-parser/blob/main/tests/fixtures/README.md)
|
|
18
|
+
for details. Without fixtures, integration tests skip cleanly — you can
|
|
19
|
+
still work on unit-test coverage.
|
|
20
|
+
|
|
21
|
+
## Running checks locally
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
uv run ruff check src/ tests/ # style + lint
|
|
25
|
+
uv run mypy src/ # type-check (advisory)
|
|
26
|
+
uv run pytest # tests
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
CI mirrors these on every push and pull request.
|
|
30
|
+
|
|
31
|
+
## Style
|
|
32
|
+
|
|
33
|
+
- Line length: **120** characters.
|
|
34
|
+
- `from __future__ import annotations` at the top of every module.
|
|
35
|
+
- Type hints on every public function and dataclass field.
|
|
36
|
+
- Docstrings on public API surface (`ParsedReplay`, events, state models).
|
|
37
|
+
- Prefer `@cached_property` for expensive derived data over eager computation.
|
|
38
|
+
|
|
39
|
+
Ruff enforces most of this. If a rule gets in the way of readability, open
|
|
40
|
+
a PR that disables it in `pyproject.toml` with a short rationale — don't
|
|
41
|
+
work around it with `# noqa`.
|
|
42
|
+
|
|
43
|
+
## Commits
|
|
44
|
+
|
|
45
|
+
Short imperative subject line. Body for the *why* when the change isn't
|
|
46
|
+
self-evident.
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
Fix RIBBON_NAMES inversion
|
|
50
|
+
|
|
51
|
+
The lookup was keyed on names instead of wire ids, so every
|
|
52
|
+
derive_ribbons() call fell through to "Unknown". ...
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Pull requests
|
|
56
|
+
|
|
57
|
+
- Branch from `main`.
|
|
58
|
+
- Keep PRs focused — one logical change per PR.
|
|
59
|
+
- Tests passing (CI will verify).
|
|
60
|
+
- Reasonable commit history — squash trivial fix-ups before requesting review.
|
|
61
|
+
- Update `CHANGELOG.md` if the change is user-facing.
|
|
62
|
+
|
|
63
|
+
## Parser-specific guidance
|
|
64
|
+
|
|
65
|
+
- The `.def` files and `alias.xml` in the gamedata repo are the single source
|
|
66
|
+
of truth for entity definitions. If your change depends on a structural
|
|
67
|
+
assumption, verify it against those files first.
|
|
68
|
+
- Never work around a parser bug inside the renderer. If decoded data is
|
|
69
|
+
wrong, fix the decode.
|
|
70
|
+
- See [CLAUDE.md](https://github.com/toalba/wows-replay-parser/blob/main/CLAUDE.md) for deeper architectural notes.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Something the parser does wrong with a specific replay or gamedata version.
|
|
3
|
+
title: "[Bug] "
|
|
4
|
+
labels: ["bug"]
|
|
5
|
+
body:
|
|
6
|
+
- type: textarea
|
|
7
|
+
id: description
|
|
8
|
+
attributes:
|
|
9
|
+
label: What happened?
|
|
10
|
+
description: A clear description of the bug.
|
|
11
|
+
validations:
|
|
12
|
+
required: true
|
|
13
|
+
|
|
14
|
+
- type: textarea
|
|
15
|
+
id: repro
|
|
16
|
+
attributes:
|
|
17
|
+
label: Reproduction steps
|
|
18
|
+
description: Minimal code or CLI invocation that triggers the bug.
|
|
19
|
+
render: shell
|
|
20
|
+
validations:
|
|
21
|
+
required: true
|
|
22
|
+
|
|
23
|
+
- type: textarea
|
|
24
|
+
id: expected
|
|
25
|
+
attributes:
|
|
26
|
+
label: Expected vs actual behaviour
|
|
27
|
+
|
|
28
|
+
- type: input
|
|
29
|
+
id: parser-version
|
|
30
|
+
attributes:
|
|
31
|
+
label: Parser version / commit
|
|
32
|
+
placeholder: "v0.1.0 or abc123"
|
|
33
|
+
validations:
|
|
34
|
+
required: true
|
|
35
|
+
|
|
36
|
+
- type: input
|
|
37
|
+
id: game-version
|
|
38
|
+
attributes:
|
|
39
|
+
label: WoWs game version
|
|
40
|
+
description: From the replay's JSON header (`clientVersionFromXml`), e.g. "15,3,0,12267945".
|
|
41
|
+
validations:
|
|
42
|
+
required: true
|
|
43
|
+
|
|
44
|
+
- type: input
|
|
45
|
+
id: python-version
|
|
46
|
+
attributes:
|
|
47
|
+
label: Python version
|
|
48
|
+
placeholder: "3.12.3"
|
|
49
|
+
|
|
50
|
+
- type: dropdown
|
|
51
|
+
id: os
|
|
52
|
+
attributes:
|
|
53
|
+
label: OS
|
|
54
|
+
options:
|
|
55
|
+
- Linux
|
|
56
|
+
- macOS
|
|
57
|
+
- Windows
|
|
58
|
+
- Other
|
|
59
|
+
|
|
60
|
+
- type: textarea
|
|
61
|
+
id: logs
|
|
62
|
+
attributes:
|
|
63
|
+
label: Relevant logs / stack trace
|
|
64
|
+
render: shell
|
|
65
|
+
|
|
66
|
+
- type: markdown
|
|
67
|
+
attributes:
|
|
68
|
+
value: |
|
|
69
|
+
**About replay files:** please do NOT attach private-match replays.
|
|
70
|
+
If a replay is needed to reproduce, describe the scenario in words
|
|
71
|
+
or share a sanitized version (player names replaced). Never share
|
|
72
|
+
account IDs or clan tags belonging to others without permission.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
blank_issues_enabled: false
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Propose a new event type, API, or capability.
|
|
3
|
+
title: "[Feat] "
|
|
4
|
+
labels: ["enhancement"]
|
|
5
|
+
body:
|
|
6
|
+
- type: textarea
|
|
7
|
+
id: use-case
|
|
8
|
+
attributes:
|
|
9
|
+
label: Use case
|
|
10
|
+
description: What are you trying to accomplish? A concrete example helps.
|
|
11
|
+
validations:
|
|
12
|
+
required: true
|
|
13
|
+
|
|
14
|
+
- type: textarea
|
|
15
|
+
id: proposal
|
|
16
|
+
attributes:
|
|
17
|
+
label: Proposed API or behaviour
|
|
18
|
+
description: What should the parser expose, and what would consumers write?
|
|
19
|
+
render: python
|
|
20
|
+
|
|
21
|
+
- type: textarea
|
|
22
|
+
id: alternatives
|
|
23
|
+
attributes:
|
|
24
|
+
label: Alternatives considered
|
|
25
|
+
description: Other approaches you evaluated and why you didn't pick them.
|
|
26
|
+
|
|
27
|
+
- type: textarea
|
|
28
|
+
id: references
|
|
29
|
+
attributes:
|
|
30
|
+
label: References
|
|
31
|
+
description: Links to relevant `.def` fields, game-data paths, or decompiled
|
|
32
|
+
client code that confirm the data is actually present in the replay.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Regression (patch break)
|
|
2
|
+
description: A new WoWs game patch broke parsing. Often auto-filed by the canary workflow.
|
|
3
|
+
title: "[Regression] "
|
|
4
|
+
labels: ["patch-regression"]
|
|
5
|
+
body:
|
|
6
|
+
- type: input
|
|
7
|
+
id: last-working
|
|
8
|
+
attributes:
|
|
9
|
+
label: Last known-working parser version / commit
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
|
|
13
|
+
- type: input
|
|
14
|
+
id: first-broken
|
|
15
|
+
attributes:
|
|
16
|
+
label: First broken commit / run URL
|
|
17
|
+
description: Ideally a link to the canary workflow run that caught it.
|
|
18
|
+
validations:
|
|
19
|
+
required: true
|
|
20
|
+
|
|
21
|
+
- type: input
|
|
22
|
+
id: patch-version
|
|
23
|
+
attributes:
|
|
24
|
+
label: WoWs patch version where the break appeared
|
|
25
|
+
description: e.g. "15.4.0.12400000" from the replay JSON header.
|
|
26
|
+
validations:
|
|
27
|
+
required: true
|
|
28
|
+
|
|
29
|
+
- type: textarea
|
|
30
|
+
id: repro
|
|
31
|
+
attributes:
|
|
32
|
+
label: Minimal reproduction
|
|
33
|
+
description: A one-liner or short script that fails now but used to work.
|
|
34
|
+
render: python
|
|
35
|
+
validations:
|
|
36
|
+
required: true
|
|
37
|
+
|
|
38
|
+
- type: textarea
|
|
39
|
+
id: suspected-cause
|
|
40
|
+
attributes:
|
|
41
|
+
label: Suspected cause
|
|
42
|
+
description: E.g. a renamed property, a reordered method, a new packet type.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Short imperative subject. Body should answer *why* the change is being
|
|
3
|
+
made — the diff already shows *what*.
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
## Summary
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Type
|
|
11
|
+
|
|
12
|
+
<!-- Check one -->
|
|
13
|
+
- [ ] Bug fix
|
|
14
|
+
- [ ] Feature
|
|
15
|
+
- [ ] Refactor / cleanup
|
|
16
|
+
- [ ] Docs
|
|
17
|
+
- [ ] CI / tooling
|
|
18
|
+
|
|
19
|
+
## Test plan
|
|
20
|
+
|
|
21
|
+
<!-- How did you verify this works? Delete rows that don't apply. -->
|
|
22
|
+
- [ ] `uv run pytest` green
|
|
23
|
+
- [ ] `uv run ruff check src/ tests/` clean
|
|
24
|
+
- [ ] Added / updated unit tests covering the change
|
|
25
|
+
- [ ] Exercised against a real replay fixture locally
|
|
26
|
+
|
|
27
|
+
## Checklist
|
|
28
|
+
|
|
29
|
+
- [ ] CHANGELOG updated if this is a user-visible change
|
|
30
|
+
- [ ] Docstrings updated for public API touched by this change
|
|
31
|
+
- [ ] If fixing a parser bug, verified it against the `.def` / `alias.xml`
|
|
32
|
+
source of truth
|
|
33
|
+
|
|
34
|
+
## Related issues
|
|
35
|
+
|
|
36
|
+
<!-- Fixes #123 / Closes #456 -->
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Repository secrets
|
|
2
|
+
|
|
3
|
+
Secrets consumed by workflows in `.github/workflows/`. Add them under
|
|
4
|
+
**Settings → Secrets and variables → Actions → New repository secret**.
|
|
5
|
+
|
|
6
|
+
| Secret | Used by | Purpose |
|
|
7
|
+
|---|---|---|
|
|
8
|
+
| `GAMEDATA_PAT` | `canary.yml` | Clone the private `toalba/wows-gamedata` repo over HTTPS. |
|
|
9
|
+
|
|
10
|
+
PyPI publishing (`publish.yml`) uses **Trusted Publishing** and does **not**
|
|
11
|
+
require any secret — it authenticates via OIDC against PyPI directly.
|
|
12
|
+
|
|
13
|
+
## Generating `GAMEDATA_PAT`
|
|
14
|
+
|
|
15
|
+
1. Go to <https://github.com/settings/personal-access-tokens/new> (fine-grained token).
|
|
16
|
+
2. **Resource owner:** `toalba` (or whichever account owns `wows-gamedata`).
|
|
17
|
+
3. **Repository access:** *Only select repositories* → pick `wows-gamedata`.
|
|
18
|
+
4. **Repository permissions:** `Contents: Read-only`, `Metadata: Read-only`.
|
|
19
|
+
5. **Expiration:** 1 year (set a calendar reminder to rotate).
|
|
20
|
+
6. Copy the token, then in this repo: *Settings → Secrets and variables →
|
|
21
|
+
Actions → New repository secret* → name `GAMEDATA_PAT`, paste value, save.
|
|
22
|
+
|
|
23
|
+
The canary skips cleanly with a workflow warning if the secret is missing, so
|
|
24
|
+
rotation lag will not cause spurious failure issues.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: "pip"
|
|
4
|
+
directory: "/"
|
|
5
|
+
schedule:
|
|
6
|
+
interval: "monthly"
|
|
7
|
+
target-branch: "main"
|
|
8
|
+
labels:
|
|
9
|
+
- "dependencies"
|
|
10
|
+
- "automated"
|
|
11
|
+
commit-message:
|
|
12
|
+
prefix: "deps"
|
|
13
|
+
groups:
|
|
14
|
+
minor-and-patch:
|
|
15
|
+
update-types:
|
|
16
|
+
- "minor"
|
|
17
|
+
- "patch"
|
|
18
|
+
|
|
19
|
+
- package-ecosystem: "github-actions"
|
|
20
|
+
directory: "/"
|
|
21
|
+
schedule:
|
|
22
|
+
interval: "monthly"
|
|
23
|
+
target-branch: "main"
|
|
24
|
+
labels:
|
|
25
|
+
- "dependencies"
|
|
26
|
+
- "automated"
|
|
27
|
+
commit-message:
|
|
28
|
+
prefix: "deps"
|
|
29
|
+
groups:
|
|
30
|
+
actions:
|
|
31
|
+
patterns:
|
|
32
|
+
- "*"
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
name: Canary (patch regression)
|
|
2
|
+
# Nightly: run tests against latest wows-gamedata tag; open patch-regression
|
|
3
|
+
# issue on failure, close it on recovery.
|
|
4
|
+
|
|
5
|
+
on:
|
|
6
|
+
schedule:
|
|
7
|
+
- cron: "0 3 * * *" # Daily at 03:00 UTC
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
issues: write
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
canary-test:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
outputs:
|
|
18
|
+
gamedata_tag: ${{ steps.checkout-gamedata.outputs.tag }}
|
|
19
|
+
skipped: ${{ steps.secret-check.outputs.skipped }}
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
|
|
23
|
+
- name: Check GAMEDATA_PAT secret
|
|
24
|
+
id: secret-check
|
|
25
|
+
env:
|
|
26
|
+
GAMEDATA_PAT: ${{ secrets.GAMEDATA_PAT }}
|
|
27
|
+
run: |
|
|
28
|
+
if [ -z "$GAMEDATA_PAT" ]; then
|
|
29
|
+
echo "::warning::GAMEDATA_PAT secret not configured — skipping canary. See .github/SECRETS.md."
|
|
30
|
+
echo "skipped=true" >> "$GITHUB_OUTPUT"
|
|
31
|
+
else
|
|
32
|
+
echo "skipped=false" >> "$GITHUB_OUTPUT"
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
- name: Install uv
|
|
36
|
+
if: steps.secret-check.outputs.skipped != 'true'
|
|
37
|
+
uses: astral-sh/setup-uv@v5
|
|
38
|
+
with:
|
|
39
|
+
enable-cache: true
|
|
40
|
+
|
|
41
|
+
- name: Set up Python 3.12
|
|
42
|
+
if: steps.secret-check.outputs.skipped != 'true'
|
|
43
|
+
run: uv python install 3.12
|
|
44
|
+
|
|
45
|
+
- name: Install project
|
|
46
|
+
if: steps.secret-check.outputs.skipped != 'true'
|
|
47
|
+
run: uv sync --all-extras
|
|
48
|
+
|
|
49
|
+
- name: Clone wows-gamedata at latest tag
|
|
50
|
+
id: checkout-gamedata
|
|
51
|
+
if: steps.secret-check.outputs.skipped != 'true'
|
|
52
|
+
env:
|
|
53
|
+
GAMEDATA_PAT: ${{ secrets.GAMEDATA_PAT }}
|
|
54
|
+
run: |
|
|
55
|
+
set -euo pipefail
|
|
56
|
+
git clone --filter=blob:none --no-checkout \
|
|
57
|
+
"https://x-access-token:${GAMEDATA_PAT}@github.com/toalba/wows-gamedata.git" \
|
|
58
|
+
wows-gamedata
|
|
59
|
+
cd wows-gamedata
|
|
60
|
+
# Pick the latest tag by commit date (tags look like v15.2.0.12267945).
|
|
61
|
+
LATEST_TAG=$(git for-each-ref --sort=-creatordate --format='%(refname:short)' refs/tags | head -n1)
|
|
62
|
+
if [ -z "$LATEST_TAG" ]; then
|
|
63
|
+
echo "::error::No tags found in wows-gamedata"
|
|
64
|
+
exit 1
|
|
65
|
+
fi
|
|
66
|
+
git checkout "$LATEST_TAG"
|
|
67
|
+
echo "Checked out gamedata tag: $LATEST_TAG"
|
|
68
|
+
echo "tag=$LATEST_TAG" >> "$GITHUB_OUTPUT"
|
|
69
|
+
|
|
70
|
+
- name: Run pytest
|
|
71
|
+
id: pytest
|
|
72
|
+
if: steps.secret-check.outputs.skipped != 'true'
|
|
73
|
+
env:
|
|
74
|
+
GAMEDATA_PATH: ${{ github.workspace }}/wows-gamedata/data
|
|
75
|
+
run: |
|
|
76
|
+
set -o pipefail
|
|
77
|
+
uv run pytest tests/ -v --tb=short 2>&1 | tee pytest-output.txt
|
|
78
|
+
|
|
79
|
+
- name: Upload pytest output
|
|
80
|
+
if: always() && steps.secret-check.outputs.skipped != 'true'
|
|
81
|
+
uses: actions/upload-artifact@v4
|
|
82
|
+
with:
|
|
83
|
+
name: pytest-output
|
|
84
|
+
path: pytest-output.txt
|
|
85
|
+
if-no-files-found: ignore
|
|
86
|
+
|
|
87
|
+
report-failure:
|
|
88
|
+
needs: canary-test
|
|
89
|
+
if: failure() && needs.canary-test.outputs.skipped != 'true'
|
|
90
|
+
runs-on: ubuntu-latest
|
|
91
|
+
permissions:
|
|
92
|
+
contents: read
|
|
93
|
+
issues: write
|
|
94
|
+
steps:
|
|
95
|
+
- uses: actions/download-artifact@v4
|
|
96
|
+
with:
|
|
97
|
+
name: pytest-output
|
|
98
|
+
path: .
|
|
99
|
+
continue-on-error: true
|
|
100
|
+
|
|
101
|
+
- name: Open or update patch-regression issue
|
|
102
|
+
env:
|
|
103
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
104
|
+
TAG: ${{ needs.canary-test.outputs.gamedata_tag }}
|
|
105
|
+
RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
106
|
+
run: |
|
|
107
|
+
set -euo pipefail
|
|
108
|
+
{
|
|
109
|
+
echo "Canary failed against gamedata tag **${TAG}**."
|
|
110
|
+
echo ""
|
|
111
|
+
echo "Run: ${RUN_URL}"
|
|
112
|
+
echo ""
|
|
113
|
+
echo '<details><summary>pytest output (tail)</summary>'
|
|
114
|
+
echo '```'
|
|
115
|
+
[ -f pytest-output.txt ] && tail -c 60000 pytest-output.txt || echo "(no artifact)"
|
|
116
|
+
echo '```'
|
|
117
|
+
echo '</details>'
|
|
118
|
+
} > body.md
|
|
119
|
+
EXISTING=$(gh issue list --label patch-regression --state open --json number --jq '.[0].number' || true)
|
|
120
|
+
if [ -n "$EXISTING" ]; then
|
|
121
|
+
gh issue comment "$EXISTING" --body-file body.md
|
|
122
|
+
else
|
|
123
|
+
gh issue create --title "Patch regression: ${TAG}" \
|
|
124
|
+
--label patch-regression --label automated --body-file body.md
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
close-on-recovery:
|
|
128
|
+
needs: canary-test
|
|
129
|
+
if: success() && needs.canary-test.outputs.skipped != 'true'
|
|
130
|
+
runs-on: ubuntu-latest
|
|
131
|
+
permissions:
|
|
132
|
+
contents: read
|
|
133
|
+
issues: write
|
|
134
|
+
steps:
|
|
135
|
+
- name: Close open patch-regression issue (if any)
|
|
136
|
+
env:
|
|
137
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
138
|
+
TAG: ${{ needs.canary-test.outputs.gamedata_tag }}
|
|
139
|
+
run: |
|
|
140
|
+
set -euo pipefail
|
|
141
|
+
EXISTING=$(gh issue list --label patch-regression --state open --json number --jq '.[0].number' || true)
|
|
142
|
+
if [ -n "$EXISTING" ]; then
|
|
143
|
+
gh issue comment "$EXISTING" --body "Canary green on tag ${TAG} — closing."
|
|
144
|
+
gh issue close "$EXISTING"
|
|
145
|
+
fi
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.11", "3.12"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Install uv
|
|
18
|
+
uses: astral-sh/setup-uv@v5
|
|
19
|
+
with:
|
|
20
|
+
enable-cache: true
|
|
21
|
+
|
|
22
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
23
|
+
run: uv python install ${{ matrix.python-version }}
|
|
24
|
+
|
|
25
|
+
- name: Install project
|
|
26
|
+
run: uv sync --all-extras
|
|
27
|
+
|
|
28
|
+
- name: Lint
|
|
29
|
+
run: uv run ruff check src/ tests/
|
|
30
|
+
|
|
31
|
+
- name: Type-check
|
|
32
|
+
run: uv run mypy src/
|
|
33
|
+
continue-on-error: true # mypy is aspirational for now — don't block PRs
|
|
34
|
+
|
|
35
|
+
- name: Run tests
|
|
36
|
+
run: uv run pytest -v
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- "docs/**"
|
|
8
|
+
- "mkdocs.yml"
|
|
9
|
+
- "src/**"
|
|
10
|
+
- ".github/CONTRIBUTING.md"
|
|
11
|
+
- "SECURITY.md"
|
|
12
|
+
- "CHANGELOG.md"
|
|
13
|
+
- "README.md"
|
|
14
|
+
- ".github/workflows/docs.yml"
|
|
15
|
+
workflow_dispatch:
|
|
16
|
+
|
|
17
|
+
permissions:
|
|
18
|
+
contents: write
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
build-and-deploy:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
with:
|
|
26
|
+
fetch-depth: 0 # mkdocs uses git for last-modified timestamps
|
|
27
|
+
|
|
28
|
+
- name: Install uv
|
|
29
|
+
uses: astral-sh/setup-uv@v5
|
|
30
|
+
with:
|
|
31
|
+
enable-cache: true
|
|
32
|
+
|
|
33
|
+
- name: Set up Python 3.12
|
|
34
|
+
run: uv python install 3.12
|
|
35
|
+
|
|
36
|
+
- name: Install project + docs deps
|
|
37
|
+
run: uv sync --extra docs
|
|
38
|
+
|
|
39
|
+
- name: Build mkdocs site
|
|
40
|
+
run: uv run mkdocs build --strict
|
|
41
|
+
|
|
42
|
+
- name: Deploy to gh-pages
|
|
43
|
+
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
|
44
|
+
run: uv run mkdocs gh-deploy --force --no-history
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
test_pypi:
|
|
9
|
+
description: "Publish to TestPyPI instead of PyPI"
|
|
10
|
+
required: false
|
|
11
|
+
default: false
|
|
12
|
+
type: boolean
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
build:
|
|
16
|
+
name: Build distribution
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v5
|
|
23
|
+
|
|
24
|
+
- name: Set up Python
|
|
25
|
+
run: uv python install 3.12
|
|
26
|
+
|
|
27
|
+
- name: Build sdist + wheel
|
|
28
|
+
run: uv build
|
|
29
|
+
|
|
30
|
+
- name: Upload build artifacts
|
|
31
|
+
uses: actions/upload-artifact@v4
|
|
32
|
+
with:
|
|
33
|
+
name: dist
|
|
34
|
+
path: dist/
|
|
35
|
+
|
|
36
|
+
publish-pypi:
|
|
37
|
+
name: Publish to PyPI
|
|
38
|
+
needs: build
|
|
39
|
+
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && !inputs.test_pypi)
|
|
40
|
+
runs-on: ubuntu-latest
|
|
41
|
+
environment:
|
|
42
|
+
name: pypi
|
|
43
|
+
url: https://pypi.org/project/wows-replay-parser/
|
|
44
|
+
permissions:
|
|
45
|
+
id-token: write
|
|
46
|
+
steps:
|
|
47
|
+
- name: Download build artifacts
|
|
48
|
+
uses: actions/download-artifact@v4
|
|
49
|
+
with:
|
|
50
|
+
name: dist
|
|
51
|
+
path: dist/
|
|
52
|
+
|
|
53
|
+
- name: Publish to PyPI
|
|
54
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
55
|
+
|
|
56
|
+
publish-testpypi:
|
|
57
|
+
name: Publish to TestPyPI
|
|
58
|
+
needs: build
|
|
59
|
+
if: github.event_name == 'workflow_dispatch' && inputs.test_pypi
|
|
60
|
+
runs-on: ubuntu-latest
|
|
61
|
+
environment:
|
|
62
|
+
name: testpypi
|
|
63
|
+
url: https://test.pypi.org/project/wows-replay-parser/
|
|
64
|
+
permissions:
|
|
65
|
+
id-token: write
|
|
66
|
+
steps:
|
|
67
|
+
- name: Download build artifacts
|
|
68
|
+
uses: actions/download-artifact@v4
|
|
69
|
+
with:
|
|
70
|
+
name: dist
|
|
71
|
+
path: dist/
|
|
72
|
+
|
|
73
|
+
- name: Publish to TestPyPI
|
|
74
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
75
|
+
with:
|
|
76
|
+
repository-url: https://test.pypi.org/legacy/
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.pyc
|
|
3
|
+
*.pyo
|
|
4
|
+
.mypy_cache/
|
|
5
|
+
.ruff_cache/
|
|
6
|
+
.pytest_cache/
|
|
7
|
+
*.egg-info/
|
|
8
|
+
dist/
|
|
9
|
+
build/
|
|
10
|
+
.venv/
|
|
11
|
+
.claude/
|
|
12
|
+
wows-gamedata/
|
|
13
|
+
*.wowsreplay
|
|
14
|
+
|
|
15
|
+
# Test fixtures — see tests/fixtures/README.md
|
|
16
|
+
tests/fixtures/replays/*.wowsreplay
|
|
17
|
+
tests/fixtures/gamedata/
|
|
18
|
+
|
|
19
|
+
# mkdocs build output
|
|
20
|
+
/site/
|