appmates 0.1.0__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.
- appmates-0.1.0/.claude/launch.json +17 -0
- appmates-0.1.0/.env.example +13 -0
- appmates-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +30 -0
- appmates-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +17 -0
- appmates-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +13 -0
- appmates-0.1.0/.github/dependabot.yml +25 -0
- appmates-0.1.0/.github/workflows/ci.yml +151 -0
- appmates-0.1.0/.github/workflows/codeql.yml +66 -0
- appmates-0.1.0/.github/workflows/release.yml +44 -0
- appmates-0.1.0/.gitignore +51 -0
- appmates-0.1.0/.python-version +1 -0
- appmates-0.1.0/CODE_OF_CONDUCT.md +67 -0
- appmates-0.1.0/CONTRIBUTING.md +50 -0
- appmates-0.1.0/LAUNCH_READINESS.md +196 -0
- appmates-0.1.0/LICENSE +21 -0
- appmates-0.1.0/PKG-INFO +766 -0
- appmates-0.1.0/README.md +730 -0
- appmates-0.1.0/SECURITY.md +58 -0
- appmates-0.1.0/pyproject.toml +100 -0
- appmates-0.1.0/scripts/deploy-web.sh +9 -0
- appmates-0.1.0/scripts/export_conformance.py +796 -0
- appmates-0.1.0/scripts/export_parser_fixtures.py +171 -0
- appmates-0.1.0/scripts/export_specs.py +120 -0
- appmates-0.1.0/src/appmates/__init__.py +3 -0
- appmates-0.1.0/src/appmates/cli/__init__.py +0 -0
- appmates-0.1.0/src/appmates/cli/commands/__init__.py +0 -0
- appmates-0.1.0/src/appmates/cli/commands/app.py +174 -0
- appmates-0.1.0/src/appmates/cli/commands/appstore_connect.py +231 -0
- appmates-0.1.0/src/appmates/cli/commands/competitors.py +389 -0
- appmates-0.1.0/src/appmates/cli/commands/keywords.py +191 -0
- appmates-0.1.0/src/appmates/cli/commands/markets.py +181 -0
- appmates-0.1.0/src/appmates/cli/commands/metadata.py +69 -0
- appmates-0.1.0/src/appmates/cli/commands/niche.py +202 -0
- appmates-0.1.0/src/appmates/cli/commands/pricing.py +54 -0
- appmates-0.1.0/src/appmates/cli/commands/screenshots.py +162 -0
- appmates-0.1.0/src/appmates/cli/commands/specs.py +35 -0
- appmates-0.1.0/src/appmates/cli/commands/submission.py +82 -0
- appmates-0.1.0/src/appmates/cli/commands/tester.py +108 -0
- appmates-0.1.0/src/appmates/cli/console.py +57 -0
- appmates-0.1.0/src/appmates/cli/main.py +82 -0
- appmates-0.1.0/src/appmates/core/__init__.py +0 -0
- appmates-0.1.0/src/appmates/core/clients/__init__.py +0 -0
- appmates-0.1.0/src/appmates/core/clients/appstore_connect.py +358 -0
- appmates-0.1.0/src/appmates/core/clients/itunes.py +288 -0
- appmates-0.1.0/src/appmates/core/config.py +58 -0
- appmates-0.1.0/src/appmates/core/errors.py +47 -0
- appmates-0.1.0/src/appmates/core/models/__init__.py +0 -0
- appmates-0.1.0/src/appmates/core/models/app_metadata.py +65 -0
- appmates-0.1.0/src/appmates/core/models/app_profile.py +177 -0
- appmates-0.1.0/src/appmates/core/models/competitors.py +261 -0
- appmates-0.1.0/src/appmates/core/models/keywords.py +85 -0
- appmates-0.1.0/src/appmates/core/models/market.py +161 -0
- appmates-0.1.0/src/appmates/core/models/report.py +156 -0
- appmates-0.1.0/src/appmates/core/models/testing.py +61 -0
- appmates-0.1.0/src/appmates/core/services/__init__.py +0 -0
- appmates-0.1.0/src/appmates/core/services/app_profile.py +329 -0
- appmates-0.1.0/src/appmates/core/services/asc_sync.py +255 -0
- appmates-0.1.0/src/appmates/core/services/competitor_analyzer.py +439 -0
- appmates-0.1.0/src/appmates/core/services/google_play.py +271 -0
- appmates-0.1.0/src/appmates/core/services/image_fixer.py +246 -0
- appmates-0.1.0/src/appmates/core/services/image_validator.py +411 -0
- appmates-0.1.0/src/appmates/core/services/keyword_builder.py +325 -0
- appmates-0.1.0/src/appmates/core/services/market_analyzer.py +274 -0
- appmates-0.1.0/src/appmates/core/services/market_scanner.py +209 -0
- appmates-0.1.0/src/appmates/core/services/metadata_validator.py +195 -0
- appmates-0.1.0/src/appmates/core/services/pricing_calculator.py +73 -0
- appmates-0.1.0/src/appmates/core/services/reporting.py +447 -0
- appmates-0.1.0/src/appmates/core/services/submission_checker.py +129 -0
- appmates-0.1.0/src/appmates/core/specs/__init__.py +19 -0
- appmates-0.1.0/src/appmates/core/specs/app_health.yaml +65 -0
- appmates-0.1.0/src/appmates/core/specs/apple.yaml +117 -0
- appmates-0.1.0/src/appmates/core/specs/aso.yaml +241 -0
- appmates-0.1.0/src/appmates/core/specs/google_play.yaml +74 -0
- appmates-0.1.0/src/appmates/core/specs/market.yaml +171 -0
- appmates-0.1.0/src/appmates/core/specs/pricing.yaml +45 -0
- appmates-0.1.0/src/appmates/core/specs/registry.py +274 -0
- appmates-0.1.0/src/appmates/py.typed +0 -0
- appmates-0.1.0/tests/__init__.py +0 -0
- appmates-0.1.0/tests/conftest.py +110 -0
- appmates-0.1.0/tests/fixtures/itunes_competitors.json +254 -0
- appmates-0.1.0/tests/fixtures/itunes_habit_tracker.json +213 -0
- appmates-0.1.0/tests/test_app_profile.py +258 -0
- appmates-0.1.0/tests/test_appstore_connect_client.py +339 -0
- appmates-0.1.0/tests/test_asc_sync.py +312 -0
- appmates-0.1.0/tests/test_cli.py +1234 -0
- appmates-0.1.0/tests/test_competitor_analyzer.py +550 -0
- appmates-0.1.0/tests/test_config_and_reporting.py +289 -0
- appmates-0.1.0/tests/test_google_play.py +249 -0
- appmates-0.1.0/tests/test_image_fixer.py +234 -0
- appmates-0.1.0/tests/test_image_validator.py +290 -0
- appmates-0.1.0/tests/test_itunes_client.py +439 -0
- appmates-0.1.0/tests/test_keyword_builder.py +291 -0
- appmates-0.1.0/tests/test_market_analyzer.py +438 -0
- appmates-0.1.0/tests/test_market_scanner.py +157 -0
- appmates-0.1.0/tests/test_metadata_validator.py +199 -0
- appmates-0.1.0/tests/test_pricing_calculator.py +45 -0
- appmates-0.1.0/tests/test_specs.py +120 -0
- appmates-0.1.0/tests/test_submission_checker.py +74 -0
- appmates-0.1.0/uv.lock +1122 -0
- appmates-0.1.0/web/.well-known/security.txt +5 -0
- appmates-0.1.0/web/404.html +47 -0
- appmates-0.1.0/web/README.md +146 -0
- appmates-0.1.0/web/_headers +23 -0
- appmates-0.1.0/web/app.html +679 -0
- appmates-0.1.0/web/app.js +251 -0
- appmates-0.1.0/web/assets/app-store.png +0 -0
- appmates-0.1.0/web/assets/darkHighFive.png +0 -0
- appmates-0.1.0/web/assets/google-play.png +0 -0
- appmates-0.1.0/web/assets/icon.svg +14 -0
- appmates-0.1.0/web/assets/lightHighFive.png +0 -0
- appmates-0.1.0/web/browse-view.js +332 -0
- appmates-0.1.0/web/index.html +592 -0
- appmates-0.1.0/web/landing-demo.js +115 -0
- appmates-0.1.0/web/landing.css +1724 -0
- appmates-0.1.0/web/landing.js +795 -0
- appmates-0.1.0/web/legal-notice.html +65 -0
- appmates-0.1.0/web/legal.css +55 -0
- appmates-0.1.0/web/lib/app-profile.js +355 -0
- appmates-0.1.0/web/lib/community.js +405 -0
- appmates-0.1.0/web/lib/competitors.js +360 -0
- appmates-0.1.0/web/lib/favorites.js +74 -0
- appmates-0.1.0/web/lib/fixer.js +150 -0
- appmates-0.1.0/web/lib/globe-centroids.js +97 -0
- appmates-0.1.0/web/lib/globe-land.js +157 -0
- appmates-0.1.0/web/lib/globe.js +505 -0
- appmates-0.1.0/web/lib/image-facts.js +161 -0
- appmates-0.1.0/web/lib/itunes.js +342 -0
- appmates-0.1.0/web/lib/keywords.js +305 -0
- appmates-0.1.0/web/lib/market.js +313 -0
- appmates-0.1.0/web/lib/metadata.js +146 -0
- appmates-0.1.0/web/lib/platform-badge.js +41 -0
- appmates-0.1.0/web/lib/presence.js +44 -0
- appmates-0.1.0/web/lib/pricing.js +92 -0
- appmates-0.1.0/web/lib/promo-colors.js +25 -0
- appmates-0.1.0/web/lib/promo-form.js +329 -0
- appmates-0.1.0/web/lib/push.js +107 -0
- appmates-0.1.0/web/lib/shot-store.js +44 -0
- appmates-0.1.0/web/lib/specs.json +905 -0
- appmates-0.1.0/web/lib/sponsor-tape.js +273 -0
- appmates-0.1.0/web/lib/testers.js +195 -0
- appmates-0.1.0/web/lib/tests-store.js +56 -0
- appmates-0.1.0/web/lib/validator.js +419 -0
- appmates-0.1.0/web/lib/zip.js +190 -0
- appmates-0.1.0/web/privacy.html +172 -0
- appmates-0.1.0/web/push-sw.js +60 -0
- appmates-0.1.0/web/sponsor-view.js +338 -0
- appmates-0.1.0/web/styles.css +1780 -0
- appmates-0.1.0/web/terms.html +118 -0
- appmates-0.1.0/web/test/conformance-cases.json +23951 -0
- appmates-0.1.0/web/test/conformance.test.js +498 -0
- appmates-0.1.0/web/test/favorites.test.js +103 -0
- appmates-0.1.0/web/test/fixtures/cmyk.jpg +0 -0
- appmates-0.1.0/web/test/fixtures/fixtures.json +104 -0
- appmates-0.1.0/web/test/fixtures/greyscale-alpha.png +0 -0
- appmates-0.1.0/web/test/fixtures/greyscale.jpg +0 -0
- appmates-0.1.0/web/test/fixtures/greyscale.png +0 -0
- appmates-0.1.0/web/test/fixtures/palette-trns.png +0 -0
- appmates-0.1.0/web/test/fixtures/palette.png +0 -0
- appmates-0.1.0/web/test/fixtures/rgb-with-text.png +0 -0
- appmates-0.1.0/web/test/fixtures/rgb.jpg +0 -0
- appmates-0.1.0/web/test/fixtures/rgb.png +0 -0
- appmates-0.1.0/web/test/fixtures/rgba.png +0 -0
- appmates-0.1.0/web/test/fixtures/wide.jpg +0 -0
- appmates-0.1.0/web/test/itunes.test.js +203 -0
- appmates-0.1.0/web/test/parser.test.js +81 -0
- appmates-0.1.0/web/test/zip.test.js +132 -0
- appmates-0.1.0/web/views/admin.js +380 -0
- appmates-0.1.0/web/views/be-tester.js +1175 -0
- appmates-0.1.0/web/views/community.js +1117 -0
- appmates-0.1.0/web/views/competitors.js +175 -0
- appmates-0.1.0/web/views/favorites-tray.js +85 -0
- appmates-0.1.0/web/views/inbox.js +756 -0
- appmates-0.1.0/web/views/keywords.js +89 -0
- appmates-0.1.0/web/views/launch.js +196 -0
- appmates-0.1.0/web/views/markets.js +102 -0
- appmates-0.1.0/web/views/metadata.js +206 -0
- appmates-0.1.0/web/views/niche.js +97 -0
- appmates-0.1.0/web/views/onboard.js +214 -0
- appmates-0.1.0/web/views/overview.js +448 -0
- appmates-0.1.0/web/views/pricing.js +77 -0
- appmates-0.1.0/web/views/rank.js +148 -0
- appmates-0.1.0/web/views/readiness.js +161 -0
- appmates-0.1.0/web/views/screenshots.js +434 -0
- appmates-0.1.0/web/views/shared.js +291 -0
- appmates-0.1.0/web/views/specs.js +63 -0
- appmates-0.1.0/web/views/testers.js +409 -0
- appmates-0.1.0/worker/README.md +59 -0
- appmates-0.1.0/worker/package-lock.json +1537 -0
- appmates-0.1.0/worker/package.json +12 -0
- appmates-0.1.0/worker/src/index.js +176 -0
- appmates-0.1.0/worker/test/rate-limit.test.js +66 -0
- appmates-0.1.0/worker/wrangler.jsonc +23 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Copy to .env and fill in — never commit the real file (.env is gitignored).
|
|
2
|
+
|
|
3
|
+
# --- App Store Connect (optional, only for `appmates asc ...`) -------------
|
|
4
|
+
#
|
|
5
|
+
# Generate a key at:
|
|
6
|
+
# https://appstoreconnect.apple.com/access/integrations/api
|
|
7
|
+
#
|
|
8
|
+
# The private key file itself never leaves your machine — see
|
|
9
|
+
# core/clients/appstore_connect.py for exactly what that means. chmod 600
|
|
10
|
+
# the .p8 file; `appmates asc status` warns if it is more open than that.
|
|
11
|
+
LAUNCHPILOT_APP_STORE_KEY_ID=
|
|
12
|
+
LAUNCHPILOT_APP_STORE_ISSUER_ID=
|
|
13
|
+
LAUNCHPILOT_APP_STORE_PRIVATE_KEY_PATH=
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Something in AppMates isn't working as expected
|
|
4
|
+
title: ""
|
|
5
|
+
labels: bug
|
|
6
|
+
assignees: ""
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
**What happened**
|
|
10
|
+
A clear description of the bug.
|
|
11
|
+
|
|
12
|
+
**What you expected**
|
|
13
|
+
What you expected to happen instead.
|
|
14
|
+
|
|
15
|
+
**How to reproduce**
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
appmates <command that triggers it>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Attach a minimal example file (screenshot, listing text, spec) if the bug is
|
|
22
|
+
data-dependent — strip anything sensitive first.
|
|
23
|
+
|
|
24
|
+
**Environment**
|
|
25
|
+
- AppMates version: `appmates --version`
|
|
26
|
+
- OS:
|
|
27
|
+
- Python version:
|
|
28
|
+
|
|
29
|
+
**Additional context**
|
|
30
|
+
Anything else relevant (store, storefront, error traceback, etc).
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature request
|
|
3
|
+
about: Suggest an idea or a store rule AppMates should know about
|
|
4
|
+
title: ""
|
|
5
|
+
labels: enhancement
|
|
6
|
+
assignees: ""
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
**What's the problem**
|
|
10
|
+
What's missing or annoying today. If this is a store rule change, link the
|
|
11
|
+
source (App Store Connect / Play Console docs, changelog, etc).
|
|
12
|
+
|
|
13
|
+
**What you'd like instead**
|
|
14
|
+
What you want AppMates to do.
|
|
15
|
+
|
|
16
|
+
**Alternatives considered**
|
|
17
|
+
Any workaround you're using today, if any.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
## What this does
|
|
2
|
+
|
|
3
|
+
<!-- One or two sentences. -->
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
<!-- The problem this solves, or the source URL if this is a store rule change. -->
|
|
8
|
+
|
|
9
|
+
## Checklist
|
|
10
|
+
|
|
11
|
+
- [ ] `uv run ruff check . && uv run ruff format --check . && uv run mypy && uv run pytest` all pass
|
|
12
|
+
- [ ] `node --test "web/test/*.test.js"` passes (if `web/` changed)
|
|
13
|
+
- [ ] Scope is one change — unrelated cleanup is in a separate PR
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
# CLI + core engines (pyproject.toml)
|
|
4
|
+
- package-ecosystem: "pip"
|
|
5
|
+
directory: "/"
|
|
6
|
+
schedule:
|
|
7
|
+
interval: "weekly"
|
|
8
|
+
groups:
|
|
9
|
+
python-dependencies:
|
|
10
|
+
patterns: ["*"]
|
|
11
|
+
|
|
12
|
+
# Screenshot relay Worker
|
|
13
|
+
- package-ecosystem: "npm"
|
|
14
|
+
directory: "/worker"
|
|
15
|
+
schedule:
|
|
16
|
+
interval: "weekly"
|
|
17
|
+
groups:
|
|
18
|
+
npm-dependencies:
|
|
19
|
+
patterns: ["*"]
|
|
20
|
+
|
|
21
|
+
# Action versions pinned in .github/workflows/*.yml
|
|
22
|
+
- package-ecosystem: "github-actions"
|
|
23
|
+
directory: "/"
|
|
24
|
+
schedule:
|
|
25
|
+
interval: "weekly"
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
env:
|
|
14
|
+
FORCE_COLOR: "1"
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
lint:
|
|
18
|
+
name: Lint & types
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
|
|
23
|
+
- name: Install uv
|
|
24
|
+
uses: astral-sh/setup-uv@v5
|
|
25
|
+
with:
|
|
26
|
+
enable-cache: true
|
|
27
|
+
|
|
28
|
+
- name: Set up Python
|
|
29
|
+
run: uv python install 3.11
|
|
30
|
+
|
|
31
|
+
- name: Install
|
|
32
|
+
run: uv sync --extra dev
|
|
33
|
+
|
|
34
|
+
- run: uv run ruff check .
|
|
35
|
+
- run: uv run ruff format --check .
|
|
36
|
+
- run: uv run mypy
|
|
37
|
+
|
|
38
|
+
test:
|
|
39
|
+
name: Tests · Python ${{ matrix.python-version }}
|
|
40
|
+
runs-on: ${{ matrix.os }}
|
|
41
|
+
strategy:
|
|
42
|
+
fail-fast: false
|
|
43
|
+
matrix:
|
|
44
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
45
|
+
os: [ubuntu-latest]
|
|
46
|
+
# Pillow behaves differently enough across platforms to be worth one
|
|
47
|
+
# non-Linux run; a full matrix would be wasteful for a pure-Python tool.
|
|
48
|
+
include:
|
|
49
|
+
- python-version: "3.12"
|
|
50
|
+
os: macos-latest
|
|
51
|
+
|
|
52
|
+
# Set for the whole job rather than passed to `uv sync` alone: a --python
|
|
53
|
+
# flag on sync does not carry over to `uv run`, which then falls back to the
|
|
54
|
+
# .python-version file, resolves a different interpreter, and cannot find
|
|
55
|
+
# pytest. UV_PYTHON keeps every uv invocation on the same interpreter.
|
|
56
|
+
env:
|
|
57
|
+
UV_PYTHON: ${{ matrix.python-version }}
|
|
58
|
+
|
|
59
|
+
steps:
|
|
60
|
+
- uses: actions/checkout@v4
|
|
61
|
+
|
|
62
|
+
- name: Install uv
|
|
63
|
+
uses: astral-sh/setup-uv@v5
|
|
64
|
+
with:
|
|
65
|
+
enable-cache: true
|
|
66
|
+
|
|
67
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
68
|
+
run: uv python install ${{ matrix.python-version }}
|
|
69
|
+
|
|
70
|
+
- name: Install
|
|
71
|
+
run: uv sync --extra dev
|
|
72
|
+
|
|
73
|
+
- name: Verify the interpreter matches the matrix
|
|
74
|
+
run: uv run python -c "import sys; assert sys.version.startswith('${{ matrix.python-version }}'), sys.version"
|
|
75
|
+
|
|
76
|
+
- name: Run tests
|
|
77
|
+
run: uv run pytest
|
|
78
|
+
|
|
79
|
+
- name: Upload coverage
|
|
80
|
+
if: matrix.python-version == '3.12' && matrix.os == 'ubuntu-latest'
|
|
81
|
+
uses: codecov/codecov-action@v5
|
|
82
|
+
with:
|
|
83
|
+
files: ./coverage.xml
|
|
84
|
+
fail_ci_if_error: false
|
|
85
|
+
env:
|
|
86
|
+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
|
87
|
+
|
|
88
|
+
web:
|
|
89
|
+
name: Web checker
|
|
90
|
+
runs-on: ubuntu-latest
|
|
91
|
+
steps:
|
|
92
|
+
- uses: actions/checkout@v4
|
|
93
|
+
|
|
94
|
+
- name: Install uv
|
|
95
|
+
uses: astral-sh/setup-uv@v5
|
|
96
|
+
with:
|
|
97
|
+
enable-cache: true
|
|
98
|
+
|
|
99
|
+
- uses: actions/setup-node@v4
|
|
100
|
+
with:
|
|
101
|
+
node-version: "22"
|
|
102
|
+
|
|
103
|
+
- name: Install
|
|
104
|
+
run: uv sync --extra dev
|
|
105
|
+
|
|
106
|
+
# The browser engine reads generated copies of the Python-side data.
|
|
107
|
+
# If someone edits a spec YAML without regenerating, the web page would
|
|
108
|
+
# quietly apply different rules from the CLI, so treat staleness as a
|
|
109
|
+
# build failure rather than a nightly surprise.
|
|
110
|
+
- name: Generated artefacts are current
|
|
111
|
+
run: |
|
|
112
|
+
uv run python scripts/export_specs.py --check
|
|
113
|
+
uv run python scripts/export_conformance.py --check
|
|
114
|
+
uv run python scripts/export_parser_fixtures.py --check
|
|
115
|
+
|
|
116
|
+
- name: Conformance and unit tests
|
|
117
|
+
run: node --test "web/test/*.test.js" | tee /tmp/node-test.log
|
|
118
|
+
|
|
119
|
+
# Our own assertions check the bytes we meant to write; this checks that a
|
|
120
|
+
# real ZIP implementation accepts them.
|
|
121
|
+
- name: Archive opens with a real unzip
|
|
122
|
+
run: |
|
|
123
|
+
ZIP=$(grep -o 'ZIP_FIXTURE=.*' /tmp/node-test.log | head -1 | cut -d= -f2)
|
|
124
|
+
test -n "$ZIP" || { echo "::error::zip fixture path not emitted"; exit 1; }
|
|
125
|
+
unzip -t "$ZIP"
|
|
126
|
+
unzip -o "$ZIP" -d /tmp/zipcheck
|
|
127
|
+
test -f /tmp/zipcheck/locale/en-US/screen-02.jpg
|
|
128
|
+
|
|
129
|
+
smoke:
|
|
130
|
+
name: CLI smoke test
|
|
131
|
+
runs-on: ubuntu-latest
|
|
132
|
+
steps:
|
|
133
|
+
- uses: actions/checkout@v4
|
|
134
|
+
|
|
135
|
+
- name: Install uv
|
|
136
|
+
uses: astral-sh/setup-uv@v5
|
|
137
|
+
|
|
138
|
+
- name: Install
|
|
139
|
+
run: uv sync --extra dev
|
|
140
|
+
|
|
141
|
+
# The commands must actually run end to end, not merely import.
|
|
142
|
+
- name: Exercise the CLI
|
|
143
|
+
run: |
|
|
144
|
+
uv run appmates --version
|
|
145
|
+
uv run appmates specs --json > /dev/null
|
|
146
|
+
uv run appmates check-testers -d 14 -t 12
|
|
147
|
+
# Exit code 1 is the documented "findings" contract; assert it holds.
|
|
148
|
+
if uv run appmates check-testers -d 3 -t 12; then
|
|
149
|
+
echo "::error::expected exit code 1 for an incomplete test period"
|
|
150
|
+
exit 1
|
|
151
|
+
fi
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
name: CodeQL
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
schedule:
|
|
9
|
+
# Catches newly-published CodeQL query rules against unchanged code —
|
|
10
|
+
# a Monday morning UTC run, off the hour to avoid the top-of-hour spike
|
|
11
|
+
# every other repo's cron also fires at.
|
|
12
|
+
- cron: "17 6 * * 1"
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
analyze:
|
|
16
|
+
name: Analyze (${{ matrix.language }})
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
permissions:
|
|
19
|
+
security-events: write
|
|
20
|
+
contents: read
|
|
21
|
+
# codeql-action/analyze calls the Actions API for its own run
|
|
22
|
+
# metadata (unrelated to the SARIF upload below) — without this, that
|
|
23
|
+
# call comes back "Resource not accessible by integration" and the
|
|
24
|
+
# step gets marked failed on the error annotation alone, even though
|
|
25
|
+
# the process catches it, logs a warning, and exits 0. Found by
|
|
26
|
+
# reading the actual failure log rather than guessing: `upload:
|
|
27
|
+
# false` on its own did not fix this, because it was never the
|
|
28
|
+
# upload that was failing.
|
|
29
|
+
actions: read
|
|
30
|
+
|
|
31
|
+
strategy:
|
|
32
|
+
fail-fast: false
|
|
33
|
+
matrix:
|
|
34
|
+
# javascript-typescript covers web/, worker/ in one pass.
|
|
35
|
+
# python covers src/appmates.
|
|
36
|
+
language: ["javascript-typescript", "python"]
|
|
37
|
+
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/checkout@v4
|
|
40
|
+
|
|
41
|
+
- uses: github/codeql-action/init@v4
|
|
42
|
+
with:
|
|
43
|
+
languages: ${{ matrix.language }}
|
|
44
|
+
|
|
45
|
+
# `upload: false` here, not the action's own default upload: on a
|
|
46
|
+
# *private* repo, pushing results to the code-scanning API needs
|
|
47
|
+
# GitHub Advanced Security, which this repo doesn't have. That push
|
|
48
|
+
# would fail once the fix above (actions: read) is in place, so this
|
|
49
|
+
# keeps analyze itself green regardless — the separate
|
|
50
|
+
# `continue-on-error` upload below is what actually tries it, and it
|
|
51
|
+
# starts succeeding on its own the moment this repo goes public (code
|
|
52
|
+
# scanning is free for public repos) or GHAS gets turned on, with no
|
|
53
|
+
# flag to remember to flip back.
|
|
54
|
+
- uses: github/codeql-action/analyze@v4
|
|
55
|
+
with:
|
|
56
|
+
category: "/language:${{ matrix.language }}"
|
|
57
|
+
upload: false
|
|
58
|
+
output: sarif-results
|
|
59
|
+
|
|
60
|
+
- name: Upload SARIF (works once the repo is public, or with GitHub Advanced Security)
|
|
61
|
+
uses: github/codeql-action/upload-sarif@v4
|
|
62
|
+
if: always()
|
|
63
|
+
continue-on-error: true
|
|
64
|
+
with:
|
|
65
|
+
sarif_file: sarif-results
|
|
66
|
+
category: "/language:${{ matrix.language }}"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build:
|
|
9
|
+
name: Build distribution
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- name: Install uv
|
|
15
|
+
uses: astral-sh/setup-uv@v5
|
|
16
|
+
|
|
17
|
+
- name: Build sdist + wheel
|
|
18
|
+
run: uv build
|
|
19
|
+
|
|
20
|
+
- uses: actions/upload-artifact@v4
|
|
21
|
+
with:
|
|
22
|
+
name: dist
|
|
23
|
+
path: dist/
|
|
24
|
+
|
|
25
|
+
publish:
|
|
26
|
+
name: Publish to PyPI
|
|
27
|
+
needs: build
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
# Matches the GitHub Actions environment named on PyPI's trusted-publisher
|
|
30
|
+
# form — not required for OIDC to work, but scopes the id-token
|
|
31
|
+
# permission below to only run under this environment's protection
|
|
32
|
+
# rules, if any get added later.
|
|
33
|
+
environment: pypi
|
|
34
|
+
permissions:
|
|
35
|
+
# Trusted publishing (OIDC) needs this — it's how the job proves its
|
|
36
|
+
# identity to PyPI instead of a stored API token.
|
|
37
|
+
id-token: write
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/download-artifact@v4
|
|
40
|
+
with:
|
|
41
|
+
name: dist
|
|
42
|
+
path: dist/
|
|
43
|
+
|
|
44
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*.egg-info/
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
.venv/
|
|
7
|
+
venv/
|
|
8
|
+
|
|
9
|
+
.pytest_cache/
|
|
10
|
+
.mypy_cache/
|
|
11
|
+
.ruff_cache/
|
|
12
|
+
.coverage
|
|
13
|
+
coverage.xml
|
|
14
|
+
htmlcov/
|
|
15
|
+
|
|
16
|
+
.DS_Store
|
|
17
|
+
.idea/
|
|
18
|
+
.vscode/
|
|
19
|
+
|
|
20
|
+
# Local project config may hold store credentials
|
|
21
|
+
.env
|
|
22
|
+
.env.*
|
|
23
|
+
!.env.example
|
|
24
|
+
appmates.local.toml
|
|
25
|
+
|
|
26
|
+
# Real listing text pulled from App Store Connect via `appmates asc pull`
|
|
27
|
+
# — not secret, but real business data with no reason to sit in git.
|
|
28
|
+
# Both names: `pulled*.json` from earlier docs, `listing.json` from the
|
|
29
|
+
# README/dashboard's own example command.
|
|
30
|
+
pulled*.json
|
|
31
|
+
/listing.json
|
|
32
|
+
|
|
33
|
+
# Local scratch for browser-driven checks
|
|
34
|
+
web/.fixtures/
|
|
35
|
+
|
|
36
|
+
# Cloudflare Pages deploy state — `/.wrangler/` when deployed as
|
|
37
|
+
# `wrangler pages deploy web` from the repo root, `web/.wrangler/` when run
|
|
38
|
+
# as `wrangler pages deploy .` from inside web/ (both invocations have been
|
|
39
|
+
# used; either leaves state in a different place).
|
|
40
|
+
/.wrangler/
|
|
41
|
+
web/.wrangler/
|
|
42
|
+
|
|
43
|
+
# Worker: installed deps and local dev state, neither of which is source
|
|
44
|
+
worker/node_modules/
|
|
45
|
+
worker/.wrangler/
|
|
46
|
+
|
|
47
|
+
# community/ moved to its own private repo (appmates-community) — this
|
|
48
|
+
# local copy is a leftover, not part of the repo at all anymore. Ignored
|
|
49
|
+
# wholesale rather than deleted, since it still holds local dev state
|
|
50
|
+
# (.dev.vars, .wrangler) not worth losing by hand.
|
|
51
|
+
/community/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.11
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our
|
|
6
|
+
community a harassment-free experience for everyone, regardless of age, body
|
|
7
|
+
size, visible or invisible disability, ethnicity, sex characteristics,
|
|
8
|
+
gender identity and expression, level of experience, education,
|
|
9
|
+
socio-economic status, nationality, personal appearance, race, religion, or
|
|
10
|
+
sexual identity and orientation.
|
|
11
|
+
|
|
12
|
+
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
13
|
+
diverse, inclusive, and healthy community.
|
|
14
|
+
|
|
15
|
+
## Our Standards
|
|
16
|
+
|
|
17
|
+
Examples of behavior that contributes to a positive environment:
|
|
18
|
+
|
|
19
|
+
- Demonstrating empathy and kindness toward other people
|
|
20
|
+
- Being respectful of differing opinions, viewpoints, and experiences
|
|
21
|
+
- Giving and gracefully accepting constructive feedback
|
|
22
|
+
- Accepting responsibility and apologizing to those affected by our
|
|
23
|
+
mistakes, and learning from the experience
|
|
24
|
+
- Focusing on what is best not just for us as individuals, but for the
|
|
25
|
+
overall community
|
|
26
|
+
|
|
27
|
+
Examples of unacceptable behavior:
|
|
28
|
+
|
|
29
|
+
- The use of sexualized language or imagery, and sexual attention or
|
|
30
|
+
advances of any kind
|
|
31
|
+
- Trolling, insulting or derogatory comments, and personal or political
|
|
32
|
+
attacks
|
|
33
|
+
- Public or private harassment
|
|
34
|
+
- Publishing others' private information, such as a physical or email
|
|
35
|
+
address, without their explicit permission
|
|
36
|
+
- Other conduct which could reasonably be considered inappropriate in a
|
|
37
|
+
professional setting
|
|
38
|
+
|
|
39
|
+
## Enforcement Responsibilities
|
|
40
|
+
|
|
41
|
+
Project maintainers are responsible for clarifying and enforcing standards
|
|
42
|
+
of acceptable behavior and will take appropriate and fair corrective action
|
|
43
|
+
in response to any behavior deemed inappropriate, threatening, offensive, or
|
|
44
|
+
harmful.
|
|
45
|
+
|
|
46
|
+
## Scope
|
|
47
|
+
|
|
48
|
+
This Code of Conduct applies within all community spaces, and also applies
|
|
49
|
+
when an individual is officially representing the community in public
|
|
50
|
+
spaces.
|
|
51
|
+
|
|
52
|
+
## Enforcement
|
|
53
|
+
|
|
54
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
55
|
+
reported to appmates.contact@gmail.com. All complaints will be reviewed and
|
|
56
|
+
investigated promptly and fairly.
|
|
57
|
+
|
|
58
|
+
All community leaders are obligated to respect the privacy and security of
|
|
59
|
+
the reporter of any incident.
|
|
60
|
+
|
|
61
|
+
## Attribution
|
|
62
|
+
|
|
63
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|
64
|
+
version 2.1, available at
|
|
65
|
+
https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
|
|
66
|
+
|
|
67
|
+
[homepage]: https://www.contributor-covenant.org
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Contributing to AppMates
|
|
2
|
+
|
|
3
|
+
Issues and PRs welcome.
|
|
4
|
+
|
|
5
|
+
## Dev setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/pP4010/appmates.git
|
|
9
|
+
cd appmates
|
|
10
|
+
uv sync --extra dev
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Runs on Python 3.11+, managed with [uv](https://docs.astral.sh/uv/). The web
|
|
14
|
+
frontend (`web/`) is vanilla JS with no build step — edit and reload.
|
|
15
|
+
|
|
16
|
+
## Before opening a PR
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
uv run ruff check .
|
|
20
|
+
uv run ruff format --check .
|
|
21
|
+
uv run mypy
|
|
22
|
+
uv run pytest
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
All four must pass; CI (`.github/workflows/ci.yml`) runs the same checks
|
|
26
|
+
plus a web conformance suite and a CLI smoke test. If you touch
|
|
27
|
+
`web/`, also run the JS tests:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
node --test "web/test/*.test.js"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Store rule changes
|
|
34
|
+
|
|
35
|
+
When a store changes a rule, the fix is usually a one-line edit to
|
|
36
|
+
`src/appmates/core/specs/*.yaml` plus a bump of `last_verified` — please
|
|
37
|
+
include the source URL in your PR description.
|
|
38
|
+
|
|
39
|
+
## Code style
|
|
40
|
+
|
|
41
|
+
- Python: `ruff` (lint + format) and `mypy --strict` — both enforced in CI,
|
|
42
|
+
no separate style guide beyond what they check.
|
|
43
|
+
- JS (`web/`): match the existing file's style; no framework/bundler, keep
|
|
44
|
+
it that way unless discussed first in an issue.
|
|
45
|
+
- Keep PRs scoped to one change. Unrelated cleanup belongs in its own PR.
|
|
46
|
+
|
|
47
|
+
## Reporting a security issue
|
|
48
|
+
|
|
49
|
+
Do not open a public issue for security vulnerabilities — see
|
|
50
|
+
[SECURITY.md](SECURITY.md) for the disclosure process.
|