391 lines
11 KiB
Markdown
391 lines
11 KiB
Markdown
# Pling Upload Findings And Plan
|
|
|
|
This repository can already build a distributable KDE Plasma widget package with
|
|
`make package`. The missing piece is publishing the generated `.plasmoid` file
|
|
to Pling/OpenDesktop as part of a release workflow.
|
|
|
|
The safest route is to own a small uploader script instead of depending on the
|
|
unofficial `pling-publisher` package. A Python implementation using `niquests`
|
|
should be possible without Playwright for the normal path.
|
|
|
|
## Repository Context
|
|
|
|
- The package artifact is produced by `make package`.
|
|
- The expected output is `build/org.kde.plasma.starter.plasmoid`, derived from
|
|
`package/metadata.json`.
|
|
- There is currently no GitHub Actions workflow in this repository.
|
|
- The package includes a compiled Qt/QML C++ module, so CI needs a runner with
|
|
the right KDE/Qt build dependencies.
|
|
- `package/metadata.json` currently declares version `1.0`; release automation
|
|
should decide whether the source of truth is the Git tag, this metadata file,
|
|
or both.
|
|
|
|
## Pling/OpenDesktop Findings
|
|
|
|
### Official API
|
|
|
|
There does not appear to be a supported official upload API for editing content
|
|
files on Pling/OpenDesktop.
|
|
|
|
A recent OpenDesktop forum answer says the OCS API is effectively read-only for
|
|
this use case and that content-edit upload support is not implemented:
|
|
|
|
- https://forum.opendesktop.org/t/ocs-api-content-edit-unknown-request/20947
|
|
|
|
An older CI/CD thread also points to there being no built-in continuous
|
|
deployment integration for Pling:
|
|
|
|
- https://forum.opendesktop.org/t/there-is-a-way-to-setup-a-continuous-deployment-from-a-app-in-pling/18876
|
|
|
|
So this should be treated as browser/API automation of the existing web app, not
|
|
as use of a stable public publishing API.
|
|
|
|
### Existing Unofficial Publisher
|
|
|
|
`pling-publisher` is an unofficial Python package:
|
|
|
|
- https://pypi.org/project/pling-publisher/
|
|
- https://github.com/dmzoneill/pling-publisher
|
|
|
|
Its general approach is useful as prior art, but I would not make this
|
|
repository depend on it. It logs in with a session, scrapes edit-page values, and
|
|
posts multipart upload data to Pling's internal upload endpoints. Owning a small
|
|
script gives us better control over:
|
|
|
|
- dependency trust
|
|
- logging and secret handling
|
|
- failure behavior
|
|
- compatibility fixes when Pling changes its pages
|
|
- dry-run and CI behavior
|
|
|
|
### Current Web Flow
|
|
|
|
Pling redirects to OpenDesktop for the account and product edit flow. The
|
|
canonical base URL should be:
|
|
|
|
```text
|
|
https://www.opendesktop.org
|
|
```
|
|
|
|
The login page is a normal HTML form with these important fields:
|
|
|
|
- `email`
|
|
- `password`
|
|
- `redirect_url`
|
|
- `csrf`
|
|
- `twit`
|
|
|
|
The site also sets and checks bot-detection signals. In local testing, a normal
|
|
browser-like request could fetch the login form, and a fake login returned the
|
|
expected "incorrect login" page rather than a hard block. That means a
|
|
`niquests.Session` implementation is likely viable today.
|
|
|
|
The current first-party upload UI posts files through product controller
|
|
endpoints, not only through raw ppload internals. KDE's hosted source shows the
|
|
relevant behavior here:
|
|
|
|
- https://lxr.kde.org/source/webapps/ocs-webserver/application/modules/default/views/scripts/user/products.phtml
|
|
- https://lxr.kde.org/source/webapps/ocs-webserver/application/modules/default/controllers/ProductController.php
|
|
|
|
Important upload/edit endpoints are exposed as HTML data attributes on the edit
|
|
page, especially around the upload modal:
|
|
|
|
- `data-addpploadfile-uri`
|
|
- `data-updatepploadfile-uri`
|
|
- `data-deletepploadfile-uri`
|
|
- `data-deletepploadfiles-uri`
|
|
- `data-product-id`
|
|
- `data-ppload-collection-id`
|
|
|
|
The server-side flow expects a multipart field named `file_upload`.
|
|
|
|
The practical shape is:
|
|
|
|
1. Log in.
|
|
2. Fetch the product edit page.
|
|
3. Parse the upload endpoint URLs from the page.
|
|
4. POST the `.plasmoid` artifact as `file_upload`.
|
|
5. Read the JSON response.
|
|
6. Optionally POST metadata such as version, description, compatibility flags,
|
|
category, and tags.
|
|
7. Optionally delete/archive older uploaded files.
|
|
|
|
## Is This A Playwright Automation?
|
|
|
|
Not as the first choice.
|
|
|
|
The recommended implementation is HTTP automation with `niquests`, plus an HTML
|
|
parser such as `selectolax`, `beautifulsoup4`, or `lxml`.
|
|
|
|
Playwright should be kept as a fallback or diagnostic tool because:
|
|
|
|
- the normal upload flow is form posts and JSON responses
|
|
- HTTP automation is easier to run in CI
|
|
- HTTP automation is easier to test and log safely
|
|
- Playwright/headless browsers are more likely to trigger bot checks
|
|
- Playwright can accidentally become a brittle click-script tied to UI layout
|
|
|
|
Playwright may become necessary if Pling changes the flow to require a real
|
|
browser-only challenge, but the script should fail closed rather than trying to
|
|
bypass CAPTCHA, 2FA, or anti-bot checks.
|
|
|
|
## Recommended Python Design
|
|
|
|
Create a small repository-owned uploader, for example:
|
|
|
|
```text
|
|
scripts/pling_upload.py
|
|
```
|
|
|
|
Use:
|
|
|
|
- `niquests.Session` for cookies and requests
|
|
- an HTML parser for CSRF and endpoint discovery
|
|
- `argparse` or `typer` for CLI arguments
|
|
- explicit dry-run support
|
|
- structured, redacted logging
|
|
|
|
Suggested inputs:
|
|
|
|
```text
|
|
PLING_USERNAME
|
|
PLING_PASSWORD
|
|
PLING_PROJECT_ID
|
|
PLING_BASE_URL=https://www.opendesktop.org
|
|
PLING_DELETE_OLD_FILES=false
|
|
```
|
|
|
|
Suggested CLI:
|
|
|
|
```text
|
|
python scripts/pling_upload.py \
|
|
--artifact build/org.kde.plasma.starter.plasmoid \
|
|
--project-id "$PLING_PROJECT_ID" \
|
|
--version "$VERSION" \
|
|
--description-file RELEASE_NOTES.md
|
|
```
|
|
|
|
Suggested dry run:
|
|
|
|
```text
|
|
python scripts/pling_upload.py \
|
|
--project-id "$PLING_PROJECT_ID" \
|
|
--dry-run
|
|
```
|
|
|
|
Dry run should:
|
|
|
|
- log in
|
|
- fetch the product edit page
|
|
- parse upload endpoints
|
|
- optionally list existing files
|
|
- avoid uploading or deleting anything
|
|
|
|
## Recommended HTTP Flow
|
|
|
|
### 1. Start A Session
|
|
|
|
Use a browser-like user agent and keep redirects enabled.
|
|
|
|
Set conservative headers:
|
|
|
|
```text
|
|
User-Agent: Mozilla/5.0 ...
|
|
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
|
Accept-Language: en-US,en;q=0.9
|
|
```
|
|
|
|
The site currently uses a `verified=1` cookie in its browser-side checks. The
|
|
script can set it before login, but should not rely on that as a stable contract.
|
|
|
|
### 2. Fetch Login Page
|
|
|
|
GET:
|
|
|
|
```text
|
|
https://www.opendesktop.org/login/
|
|
```
|
|
|
|
Parse hidden form fields, especially:
|
|
|
|
- `csrf`
|
|
- `redirect_url`
|
|
- `twit`
|
|
|
|
### 3. Submit Login
|
|
|
|
POST the login form with:
|
|
|
|
- `email`
|
|
- `password`
|
|
- parsed hidden fields
|
|
|
|
After login, verify success by checking that the session can access the product
|
|
edit page. Do not treat HTTP 200 alone as success.
|
|
|
|
### 4. Fetch Product Edit Page
|
|
|
|
GET:
|
|
|
|
```text
|
|
https://www.opendesktop.org/p/<project_id>/edit
|
|
```
|
|
|
|
Parse upload-related data attributes from the page. Fail clearly if any required
|
|
endpoint is missing.
|
|
|
|
### 5. Upload Artifact
|
|
|
|
POST multipart form data to the parsed `data-addpploadfile-uri` endpoint.
|
|
|
|
Use multipart field:
|
|
|
|
```text
|
|
file_upload
|
|
```
|
|
|
|
Recommended headers:
|
|
|
|
```text
|
|
X-Requested-With: XMLHttpRequest
|
|
Accept: application/json
|
|
Referer: https://www.opendesktop.org/p/<project_id>/edit
|
|
```
|
|
|
|
Require JSON with:
|
|
|
|
```text
|
|
status: ok
|
|
```
|
|
|
|
Capture the returned file id and collection id.
|
|
|
|
### 6. Update File Metadata
|
|
|
|
POST to the parsed `data-updatepploadfile-uri` endpoint with:
|
|
|
|
- `file_id`
|
|
- `file_version`
|
|
- `file_description`
|
|
- optional `file_category`
|
|
- optional `file_tags`
|
|
- optional `ocs_compatible=1`
|
|
|
|
This step should also require JSON `status: ok`.
|
|
|
|
### 7. Optional Cleanup
|
|
|
|
Old file cleanup should be opt-in. It is useful, but destructive.
|
|
|
|
The script can list files via the edit page's file listing endpoint and then
|
|
POST old file ids to `data-deletepploadfile-uri`. Default behavior should be to
|
|
keep all uploaded files until the workflow has been proven on a test product.
|
|
|
|
## CI/CD Plan
|
|
|
|
### Stage 1: GitHub Release Artifact
|
|
|
|
First add CI that builds the `.plasmoid` and attaches it to a GitHub Release.
|
|
This is the most reliable publishing target and gives users a fallback if Pling
|
|
upload fails.
|
|
|
|
Trigger:
|
|
|
|
```text
|
|
on:
|
|
release:
|
|
types: [published]
|
|
```
|
|
|
|
or tag push:
|
|
|
|
```text
|
|
on:
|
|
push:
|
|
tags:
|
|
- "v*"
|
|
```
|
|
|
|
### Stage 2: Manual Pling Dry Run
|
|
|
|
Add a manual `workflow_dispatch` job that only logs in, parses the edit page,
|
|
and reports whether upload endpoints are available.
|
|
|
|
This validates secrets and Pling compatibility without publishing anything.
|
|
|
|
### Stage 3: Manual Test Upload
|
|
|
|
Run the uploader manually against either:
|
|
|
|
- a disposable Pling test product, or
|
|
- the real product with old-file cleanup disabled
|
|
|
|
Verify the uploaded file manually from the Pling UI.
|
|
|
|
### Stage 4: Release Upload
|
|
|
|
Enable automatic Pling upload after GitHub release artifact creation succeeds.
|
|
|
|
The release workflow should:
|
|
|
|
1. build the package
|
|
2. upload it to the GitHub Release
|
|
3. upload it to Pling
|
|
4. fail visibly if Pling upload fails
|
|
|
|
The workflow should not delete old Pling files by default.
|
|
|
|
### Stage 5: Optional Old-File Policy
|
|
|
|
After the upload path is stable, decide whether to:
|
|
|
|
- keep all historical files
|
|
- keep the latest N files
|
|
- delete/archive all files older than the current release
|
|
|
|
This should be a separate explicit setting, not a default.
|
|
|
|
## Security Notes
|
|
|
|
- Store credentials only as GitHub Actions secrets.
|
|
- Prefer a dedicated Pling/OpenDesktop account if project ownership allows it.
|
|
- Do not log passwords, cookies, CSRF tokens, or full response bodies from
|
|
authenticated pages.
|
|
- Mask usernames/project ids in logs if desired.
|
|
- Do not attempt to bypass CAPTCHA, 2FA, or stronger anti-bot challenges.
|
|
- Keep upload retries conservative to avoid account lockouts.
|
|
- Use GitHub protected environments if releases should require approval before
|
|
publishing to Pling.
|
|
|
|
## Failure Modes To Expect
|
|
|
|
- Login succeeds locally but fails in GitHub Actions because of bot checks.
|
|
- The edit page HTML changes and endpoint parsing breaks.
|
|
- Pling/OpenDesktop redirects domains differently.
|
|
- The upload succeeds but metadata update fails.
|
|
- The account lacks permission to edit the product id.
|
|
- The file upload endpoint returns HTML instead of JSON on auth failure.
|
|
- Deleting old files removes something that should have stayed available.
|
|
|
|
Each of these should produce a clear, non-secret error message and stop the
|
|
workflow.
|
|
|
|
## Open Questions
|
|
|
|
- Should the release version come from the Git tag or `package/metadata.json`?
|
|
- Should the Pling file description come from GitHub release notes, a checked-in
|
|
changelog, or a static text field?
|
|
- Should old Pling files be kept forever at first?
|
|
- Is there a separate Pling test product available for the first upload?
|
|
- Does the real product need special GHNS/OCS compatibility metadata beyond
|
|
`ocs_compatible=1`?
|
|
|
|
## Recommendation
|
|
|
|
Implement a repository-owned `niquests` uploader first, not Playwright.
|
|
|
|
Treat Pling upload as a best-effort release publishing step built on top of the
|
|
current web UI. Keep GitHub Releases as the reliable primary artifact host, then
|
|
publish to Pling after the artifact exists. Add dry-run and manual test upload
|
|
before making Pling upload automatic on release.
|