forked from b.peck/BAT
exchange work
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
---
|
||||
name: ignition-exchange-convert
|
||||
description: Convert an existing Ignition project into a publishable Exchange resource — re-namespacing views/styles/scripts, migrating Perspective session properties to view properties, renaming Python to the guide's conventions — and prove the conversion did not break anything. Use when asked to make a project Exchange-ready, de-brand a project for publication, or refactor one out of session-scoped state. Bundles verify_refs.py and parity.py, which catch the two failure modes a conformance checker cannot see: dangling references and silently dropped features. Not for merely checking conformance (use ignition-exchange-conformance) or uploading (use ignition-exchange-publish).
|
||||
---
|
||||
|
||||
# Converting a project into an Exchange resource
|
||||
|
||||
A conversion is a large mechanical rename plus one genuine refactor. It is
|
||||
**very easy to produce a project that is perfectly named and completely broken** —
|
||||
so the verification gates below are not optional garnish, they are the point.
|
||||
|
||||
Check the target shape first with
|
||||
[`ignition-exchange-conformance`](../ignition-exchange-conformance/SKILL.md);
|
||||
this skill is what to do about the findings.
|
||||
|
||||
## Work on a copy
|
||||
|
||||
Never convert in place. Copy the project to its new name, leave the original
|
||||
untouched, and diff against it at the end. The original is your parity baseline —
|
||||
without it you cannot prove you did not drop a feature.
|
||||
|
||||
Ignition project folder name **is** the project name, so the copy's directory
|
||||
must already be the new lowercase-dashed name.
|
||||
|
||||
## Order matters
|
||||
|
||||
Do it in this order. Step 2 invalidates bindings that step 3 would otherwise
|
||||
have to rewrite twice.
|
||||
|
||||
1. **Delete what must not ship.** Vision `client-tags/`, scratch views, dev-only
|
||||
resources.
|
||||
2. **Session properties → view properties**, while paths are still the familiar
|
||||
old ones. Verify the app still works before touching namespaces.
|
||||
3. **Re-namespace** views, style classes, scripts, page config — one mechanical
|
||||
sweep.
|
||||
4. **Python pass** — tabs, camelCase, docstrings, loggers.
|
||||
5. **Content sweep** — branding, internal names, placeholder text.
|
||||
6. **Verify** (gates below), then package.
|
||||
|
||||
## Step 2 is the real refactor
|
||||
|
||||
Everything else is search-and-replace. This one changes how the app works.
|
||||
|
||||
The guide forbids session custom properties in an Exchange resource because
|
||||
importing merges them into the host project's session props. Replace with:
|
||||
|
||||
- **State lives on the root view** as `custom` properties.
|
||||
- **Flows down** to child views as `params` (the guide is explicit: `params` =
|
||||
public/configuration, `custom` = internal).
|
||||
- **Flows up** as page-scoped messages handled on the root view, named
|
||||
`exchange.resourceName.handlerName`.
|
||||
|
||||
**Bindings must stay reactive.** Do not park binding-read state in
|
||||
`system.util.getGlobals()` — globals do not trigger Perspective binding updates.
|
||||
Globals are only for non-reactive cross-session data, and most resources need
|
||||
none.
|
||||
|
||||
Before rewriting a binding, check **what it actually depended on**. A bidirectional
|
||||
binding that writes a session prop does not necessarily re-trigger the data fetch;
|
||||
if the fetch keyed off a `refreshToken`, then an explicit Apply button was already
|
||||
the commit point and your message-based version preserves behaviour exactly. Read
|
||||
the dependency graph rather than assuming.
|
||||
|
||||
## Jython 2.7 constraints
|
||||
|
||||
Gateway-scoped code is Jython 2.7. Violations are silent until they reach the gateway.
|
||||
|
||||
- **No f-strings.** `%` formatting only.
|
||||
- No `typing`, `statistics`, `zoneinfo`.
|
||||
- Java exceptions bypass `except Exception:` — use bare `except:` with
|
||||
`sys.exc_info()` in gateway-facing defensive code.
|
||||
- **Scripts embedded in `view.json`** are JSON strings that must be tab-indented:
|
||||
the first character of every line in the block is a tab.
|
||||
|
||||
## Python renames
|
||||
|
||||
The guide wants camelCase functions and variables (deliberately not PEP-8) and
|
||||
tab indentation. Two traps:
|
||||
|
||||
- **Rename through the tokenizer, not regex.** A blind replace will corrupt
|
||||
string literals and attribute names that happen to match (`lt.tm_wday`).
|
||||
- **Do not rename data-dictionary keys.** Keys inside dicts returned to view
|
||||
bindings are a wire format between the scripts and every binding that reads
|
||||
them. They are not covered by any rule and renaming them is a large, risky
|
||||
diff for no conformance gain.
|
||||
|
||||
Tab conversion applies to **indentation** only — bracket continuation lines
|
||||
starting with spaces are fine and normal. Verify with the tokenizer's INDENT
|
||||
tokens, not by grepping for leading spaces.
|
||||
|
||||
## Verification gates
|
||||
|
||||
Run all four. Each catches something the others cannot.
|
||||
|
||||
### 1 · Conformance
|
||||
|
||||
```bash
|
||||
python3 skills/ignition-exchange-conformance/exchange_lint.py <new-project>
|
||||
```
|
||||
Target: **0 failures**. Warnings are a judgement call.
|
||||
|
||||
### 2 · Referential integrity
|
||||
|
||||
```bash
|
||||
python3 skills/ignition-exchange-convert/verify_refs.py <new-project> --old <OldNamespace>
|
||||
```
|
||||
|
||||
Resolves every embedded view path, style-class reference, `view.params.X` /
|
||||
`view.custom.X`, and every call into the project library against the functions
|
||||
that actually exist. Also flags leftover old-namespace strings, surviving
|
||||
`session.custom` reads, untabbed script transforms, f-strings, and `resource.json`
|
||||
drift. Target: **0 breakages**.
|
||||
|
||||
With ~40 function renames, a missed call site is the single most likely defect,
|
||||
and nothing else finds it.
|
||||
|
||||
### 3 · Feature parity
|
||||
|
||||
```bash
|
||||
python3 skills/ignition-exchange-convert/parity.py <original-project> <new-project>
|
||||
```
|
||||
|
||||
Fingerprints every view — component types and names, bindings by target property
|
||||
and type, script transforms, event and message handlers, embedded views, declared
|
||||
params — with names normalised for case, punctuation and snake/camel. **A pure
|
||||
rename cancels out; a dropped feature does not.**
|
||||
|
||||
Target: **zero `LOST` or `MISSING` lines.** `ADDED` lines are expected — the
|
||||
session-props refactor legitimately adds params and handlers. Read the diff, do
|
||||
not just count it.
|
||||
|
||||
### 4 · Behaviour
|
||||
|
||||
If the project has a test suite over its pure modules, run it against the
|
||||
converted library. Point the package name at the new location and alias
|
||||
camelCase functions **and their keyword arguments** back to the original names —
|
||||
a renamed kwarg (`higher_is_worse` → `higherIsWorse`) breaks callers that pass it
|
||||
by name, which no static check catches.
|
||||
|
||||
Compare against the **original project's** result, not against zero failures. A
|
||||
pre-existing failure staying pre-existing is a pass.
|
||||
|
||||
### Then: load it on a gateway
|
||||
|
||||
Static analysis cannot prove a screen renders. Load the project and open it.
|
||||
A clean project load proves resources parse; only a browser proves components
|
||||
render.
|
||||
|
||||
## De-branding
|
||||
|
||||
Conformance checkers read names, never label text. Sweep user-visible strings
|
||||
explicitly — `props.text`, `title`, `placeholder`, `tooltip` — for company names,
|
||||
project codenames, contest or demo references, internal hostnames, ticket numbers.
|
||||
Strip the text rather than deleting the component, so layout and parity are
|
||||
unchanged.
|
||||
|
||||
## Testing a minimum-version claim
|
||||
|
||||
To claim a floor lower than the gateway you built on, load the project on that
|
||||
version — **against a copy of the files**. An older gateway rewrites every
|
||||
`resource.json` with `lastModification` metadata and leaves root-owned
|
||||
`.resources/` artifacts behind that your user account cannot delete (clean up
|
||||
with a throwaway container rather than sudo).
|
||||
|
||||
A clean load proves resources parse. It does **not** prove screens render — a
|
||||
component property introduced in a later version fails at render time, not load
|
||||
time. Open the client.
|
||||
|
||||
## Reporting
|
||||
|
||||
Report what the gates actually said, with numbers. A conversion reported
|
||||
accurately with two known gaps is worth far more than one claimed clean. If a
|
||||
gate is a false positive, say which and why — the tooling is not infallible and
|
||||
both bundled scripts have known blind spots documented in their docstrings.
|
||||
|
||||
## Provenance
|
||||
|
||||
Procedure and both scripts derived from a full conversion of a 20-view / 32-style
|
||||
/ 3-module Perspective project (2026-09-15), verified on Ignition 8.1.20 and
|
||||
8.3.7. The gate thresholds are the ones that actually caught defects during it.
|
||||
Reference in New Issue
Block a user