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.
Files changed (81) hide show
  1. wows_replay_parser-0.1.1/.github/CODE_OF_CONDUCT.md +17 -0
  2. wows_replay_parser-0.1.1/.github/CONTRIBUTING.md +70 -0
  3. wows_replay_parser-0.1.1/.github/ISSUE_TEMPLATE/bug_report.yml +72 -0
  4. wows_replay_parser-0.1.1/.github/ISSUE_TEMPLATE/config.yml +1 -0
  5. wows_replay_parser-0.1.1/.github/ISSUE_TEMPLATE/feature_request.yml +32 -0
  6. wows_replay_parser-0.1.1/.github/ISSUE_TEMPLATE/regression.yml +42 -0
  7. wows_replay_parser-0.1.1/.github/PULL_REQUEST_TEMPLATE.md +36 -0
  8. wows_replay_parser-0.1.1/.github/SECRETS.md +24 -0
  9. wows_replay_parser-0.1.1/.github/dependabot.yml +32 -0
  10. wows_replay_parser-0.1.1/.github/workflows/canary.yml +145 -0
  11. wows_replay_parser-0.1.1/.github/workflows/ci.yml +36 -0
  12. wows_replay_parser-0.1.1/.github/workflows/docs.yml +44 -0
  13. wows_replay_parser-0.1.1/.github/workflows/publish.yml +76 -0
  14. wows_replay_parser-0.1.1/.gitignore +20 -0
  15. wows_replay_parser-0.1.1/CHANGELOG.md +56 -0
  16. wows_replay_parser-0.1.1/CLAUDE.md +611 -0
  17. wows_replay_parser-0.1.1/KNOWN_ISSUES.md +67 -0
  18. wows_replay_parser-0.1.1/LICENSE +192 -0
  19. wows_replay_parser-0.1.1/PKG-INFO +577 -0
  20. wows_replay_parser-0.1.1/README.md +346 -0
  21. wows_replay_parser-0.1.1/SECURITY.md +41 -0
  22. wows_replay_parser-0.1.1/docs/api/events.md +11 -0
  23. wows_replay_parser-0.1.1/docs/api/merge.md +14 -0
  24. wows_replay_parser-0.1.1/docs/api/parsed-replay.md +14 -0
  25. wows_replay_parser-0.1.1/docs/api/state.md +21 -0
  26. wows_replay_parser-0.1.1/docs/architecture.md +730 -0
  27. wows_replay_parser-0.1.1/docs/changelog.md +1 -0
  28. wows_replay_parser-0.1.1/docs/concepts/dual-perspective.md +58 -0
  29. wows_replay_parser-0.1.1/docs/concepts/events.md +62 -0
  30. wows_replay_parser-0.1.1/docs/concepts/parser-pipeline.md +90 -0
  31. wows_replay_parser-0.1.1/docs/development/contributing.md +1 -0
  32. wows_replay_parser-0.1.1/docs/development/security.md +1 -0
  33. wows_replay_parser-0.1.1/docs/getting-started.md +92 -0
  34. wows_replay_parser-0.1.1/docs/index.md +43 -0
  35. wows_replay_parser-0.1.1/mkdocs.yml +79 -0
  36. wows_replay_parser-0.1.1/pyproject.toml +116 -0
  37. wows_replay_parser-0.1.1/scripts/inspect_blobs.py +495 -0
  38. wows_replay_parser-0.1.1/scripts/update-gamedata.sh +85 -0
  39. wows_replay_parser-0.1.1/scripts/verify_method_sort.py +796 -0
  40. wows_replay_parser-0.1.1/src/wows_replay_parser/__init__.py +19 -0
  41. wows_replay_parser-0.1.1/src/wows_replay_parser/api.py +614 -0
  42. wows_replay_parser-0.1.1/src/wows_replay_parser/cli.py +351 -0
  43. wows_replay_parser-0.1.1/src/wows_replay_parser/consumable_calc.py +548 -0
  44. wows_replay_parser-0.1.1/src/wows_replay_parser/events/__init__.py +231 -0
  45. wows_replay_parser-0.1.1/src/wows_replay_parser/events/models.py +905 -0
  46. wows_replay_parser-0.1.1/src/wows_replay_parser/events/stream.py +1515 -0
  47. wows_replay_parser-0.1.1/src/wows_replay_parser/gamedata/__init__.py +11 -0
  48. wows_replay_parser-0.1.1/src/wows_replay_parser/gamedata/alias_registry.py +213 -0
  49. wows_replay_parser-0.1.1/src/wows_replay_parser/gamedata/blob_decoders.py +281 -0
  50. wows_replay_parser-0.1.1/src/wows_replay_parser/gamedata/def_loader.py +248 -0
  51. wows_replay_parser-0.1.1/src/wows_replay_parser/gamedata/entity_registry.py +234 -0
  52. wows_replay_parser-0.1.1/src/wows_replay_parser/gamedata/schema_builder.py +413 -0
  53. wows_replay_parser-0.1.1/src/wows_replay_parser/gamedata_sync.py +278 -0
  54. wows_replay_parser-0.1.1/src/wows_replay_parser/interfaces.py +62 -0
  55. wows_replay_parser-0.1.1/src/wows_replay_parser/merge.py +709 -0
  56. wows_replay_parser-0.1.1/src/wows_replay_parser/packets/__init__.py +30 -0
  57. wows_replay_parser-0.1.1/src/wows_replay_parser/packets/decoder.py +1447 -0
  58. wows_replay_parser-0.1.1/src/wows_replay_parser/packets/implemented_by_parsers.py +676 -0
  59. wows_replay_parser-0.1.1/src/wows_replay_parser/packets/method_id_detector.py +792 -0
  60. wows_replay_parser-0.1.1/src/wows_replay_parser/packets/nested_property.py +115 -0
  61. wows_replay_parser-0.1.1/src/wows_replay_parser/packets/type_id_detector.py +126 -0
  62. wows_replay_parser-0.1.1/src/wows_replay_parser/packets/types.py +146 -0
  63. wows_replay_parser-0.1.1/src/wows_replay_parser/replay/__init__.py +19 -0
  64. wows_replay_parser-0.1.1/src/wows_replay_parser/replay/reader.py +185 -0
  65. wows_replay_parser-0.1.1/src/wows_replay_parser/ribbons.py +288 -0
  66. wows_replay_parser-0.1.1/src/wows_replay_parser/roster.py +742 -0
  67. wows_replay_parser-0.1.1/src/wows_replay_parser/ship_config.py +106 -0
  68. wows_replay_parser-0.1.1/src/wows_replay_parser/state/__init__.py +33 -0
  69. wows_replay_parser-0.1.1/src/wows_replay_parser/state/models.py +220 -0
  70. wows_replay_parser-0.1.1/src/wows_replay_parser/state/tracker.py +1683 -0
  71. wows_replay_parser-0.1.1/tests/conftest.py +164 -0
  72. wows_replay_parser-0.1.1/tests/fixtures/README.md +135 -0
  73. wows_replay_parser-0.1.1/tests/test_chat.py +157 -0
  74. wows_replay_parser-0.1.1/tests/test_events.py +229 -0
  75. wows_replay_parser-0.1.1/tests/test_gamedata.py +70 -0
  76. wows_replay_parser-0.1.1/tests/test_integration.py +85 -0
  77. wows_replay_parser-0.1.1/tests/test_interfaces.py +129 -0
  78. wows_replay_parser-0.1.1/tests/test_merge.py +424 -0
  79. wows_replay_parser-0.1.1/tests/test_ribbons.py +173 -0
  80. wows_replay_parser-0.1.1/tests/test_state_tracker.py +265 -0
  81. 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/