# App Store Batch Upload Tool Design

## Goal

Add a repo-local batch upload tool under `appstore/` that submits one or more Debian packages to the UnionTech app store developer platform.

The tool must support both:

- multiple releases of the same app
- multiple different apps in one batch

It should replace one-off temporary scripts with a reusable, documented workflow.

## Constraints

- Do not store platform credentials in the repository.
- Use environment variables first for credentials, with interactive input as a fallback.
- Drive batch execution from an `xlsx` manifest instead of hard-coded paths.
- Do not depend on fragile browser form automation for each upload step.
- Continue processing other rows when one release fails, unless authentication itself fails.

## Decision

Use a Python CLI that reads an `xlsx` manifest, logs into the app store once, then performs package and asset submission through the verified backend API.

The browser layer is used only to establish a valid authenticated session and export cookies. Package upload, app lookup, asset upload, and final submission all run through direct HTTP calls.

## Why This Approach

This is the most stable option because the developer portal UI is not reliable enough for unattended batch work. The manual submission already proved that:

- login through the real site works
- cookie-backed API requests work
- direct upload and submit requests succeed

Using the API after login gives us:

- deterministic request and response handling
- clear per-row error reporting
- retry support without replaying UI interactions
- easier testing of manifest parsing and payload generation

## Scope

In scope:

- repo-local tooling under `appstore/`
- batch execution from an `xlsx` file
- one login per batch run
- automatic lookup of existing apps before submission
- create-new-app or submit-new-version behavior based on lookup result
- upload of `.deb`, icon, and screenshots
- structured success and failure reports
- unit tests for parsing, mapping, and batch decisions

Out of scope:

- storing credentials in tracked files
- supporting legacy `.xls` input
- full browser automation of the developer portal workflow
- editing existing app metadata interactively from the tool
- packaging the uploader itself as part of LabelNova

## Directory Structure

The tool lives in a dedicated repo directory:

- `appstore/upload_batch.py`
- `appstore/appstore_client.py`
- `appstore/manifest.py`
- `appstore/models.py`
- `appstore/README.md`
- `appstore/requirements.txt`
- `appstore/examples/template.xlsx`
- `appstore/tests/...`

Responsibilities are split intentionally:

- `manifest.py` reads and validates the workbook
- `models.py` defines normalized app and release records
- `appstore_client.py` owns login-session handling, lookup, uploads, and submit calls
- `upload_batch.py` orchestrates the batch run and writes reports

## Manifest Design

The workbook uses two sheets.

### `apps` Sheet

One row per logical app. Required columns:

- `app_key`
- `app_name_zh`
- `pkg_name`
- `category_id`
- `website`
- `short_desc_zh`
- `full_desc_zh`
- `icon_path`
- `screenshot_1`
- `screenshot_2`
- `screenshot_3`

Optional columns:

- `keywords_zh`
- `app_id_override`

`app_key` is the stable internal join key between sheets.

### `releases` Sheet

One row per package submission. Required columns:

- `enabled`
- `app_key`
- `deb_path`
- `system_platform`
- `arch`

Optional columns:

- `region`
- `baseline`
- `note`

The `enabled` column controls whether a row participates in the batch.

## Manifest Rules

- Relative paths resolve from the workbook directory.
- The workbook is rejected if required sheets or columns are missing.
- Duplicate `app_key` values in `apps` are invalid.
- Missing files are validation failures before any upload begins.
- Human-entered package identity is limited to `pkg_name`; version, size, checksum, and package architecture come from the `.deb` itself.

## Package Metadata Extraction

Each release row must parse the actual `.deb` before submission. The tool extracts:

- `pkg_name`
- `pkg_version`
- `pkg_arch`
- file size
- SHA-256 checksum

If the parsed `pkg_name` disagrees with the linked app row, the release fails validation. The tool does not trust workbook values for version, checksum, or file size.

## Authentication Design

Credential resolution order:

1. environment variables
2. interactive terminal input

The tool logs into the site once per batch run, then exports cookies into an HTTP session used for all backend API calls.

Authentication failure is a batch-fatal error because no row can proceed without a valid session.

## App Matching Strategy

For each enabled release row:

1. If `app_id_override` is present, use it directly.
2. Otherwise query the platform by `pkg_name`.
3. If no existing app is found, create a new app submission.
4. If exactly one app is found, submit against that app.
5. If multiple apps match, mark the row as failed with an explicit ambiguity error.

The tool must not guess between multiple matches.

## Upload Flow

For each valid release row:

1. Resolve the linked app record.
2. Parse the `.deb` metadata.
3. Determine the target app identity.
4. Upload the icon, screenshots, and package through the file upload API.
5. Build the final app submission payload.
6. Submit the payload.
7. Record the result.

Within one run, uploaded files are cached by content hash so the same icon, screenshot, or `.deb` is not uploaded more than once.

## Submission Payload Design

The payload is assembled from three sources:

- app-level workbook metadata from `apps`
- release-level workbook metadata from `releases`
- parsed package metadata from the `.deb`

The tool should map workbook values into the platform payload structure for:

- localized app info
- app basic info
- platform and architecture fit info
- origin package info

Risky fields that must be driven by the parsed `.deb` instead of workbook input:

- package version
- package checksum
- package size
- package architecture identity used by the payload

## Failure Model

Failure classes:

- `auth_failed`
- `manifest_invalid`
- `deb_parse_failed`
- `app_match_ambiguous`
- `asset_upload_failed`
- `submit_failed`

Behavior:

- `auth_failed` stops the whole batch
- other failures mark only the current row as failed
- remaining enabled rows continue

This keeps large manifests usable even when a few rows are broken.

## Output Reports

Each run writes:

- `appstore/output/<timestamp>/report.json`
- `appstore/output/<timestamp>/report.xlsx`

Each row result includes at least:

- `row_id`
- `app_key`
- `deb_path`
- `status`
- `message`
- `app_id`
- `pkg_name`
- `pkg_version`

The output must be sufficient to rerun only failed rows after fixing the workbook or assets.

## CLI Behavior

The initial CLI only needs a small surface:

- workbook path
- optional output directory override
- optional row filter for reruns
- optional dry-run mode for validation without upload

Dry-run mode still parses the workbook and `.deb` files, but does not log in or upload anything.

## Testing Scope

Implementation is complete only when the following are covered by tests:

- workbook parsing and required-column validation
- relative path resolution
- duplicate `app_key` rejection
- `.deb` metadata extraction and package-name mismatch handling
- app matching behavior for zero, one, and multiple matches
- payload construction from app, release, and package metadata
- batch control flow for continue-on-row-failure and stop-on-auth-failure

Tests should focus on pure logic first. Network requests and browser login should be isolated behind client boundaries so they can be stubbed in tests.

## Risks

- platform payload fields may drift if the backend contract changes
- package architecture strings may not map cleanly from Debian metadata to store payload codes
- browser login flow may change even if the backend API remains stable
- operators may provide incomplete screenshots or wrong category values in the workbook

Mitigation:

- keep payload mapping centralized in one client module
- validate workbook fields before any upload begins
- derive package identity from the `.deb` wherever possible
- keep report output explicit enough for targeted reruns

## Recommended Implementation Order

1. Add tests for workbook parsing, row validation, and package metadata extraction.
2. Add the normalized models and manifest reader.
3. Add payload-building helpers and app matching logic.
4. Add the API client with cookie-backed session support and upload helpers.
5. Add the batch CLI and output report generation.
6. Write the example workbook and README usage documentation.
