exchange work
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
---
|
||||
name: ignition-exchange-conformance
|
||||
description: Check whether an Ignition project follows Inductive Automation's Exchange Resources Style Guide before publishing it to the Ignition Exchange — resource namespacing, Perspective/Vision/Python naming, session-property and client-tag prohibitions, database and logger conventions. Use when asked whether a project is Exchange-ready, when reviewing a project intended for the Exchange, or before packaging one for upload. Bundles a stdlib-only checker (exchange_lint.py) with 38 rules traceable to guide sections. Not for converting a project into that shape (use ignition-exchange-convert) or for packaging and upload (use ignition-exchange-publish).
|
||||
---
|
||||
|
||||
# Ignition Exchange conformance
|
||||
|
||||
Tests an Ignition project against the **Exchange Resources Style Guide v1.1.0**.
|
||||
|
||||
Run `exchange_lint.py` (next to this file) rather than checking by eye — the rules
|
||||
are numerous, mechanical, and easy to half-apply.
|
||||
|
||||
```bash
|
||||
python3 skills/ignition-exchange-conformance/exchange_lint.py /path/to/Project
|
||||
```
|
||||
|
||||
Stdlib only, Python 3.8+. Accepts a project folder, a folder containing several
|
||||
projects (a gateway's `projects/` directory, an unpacked backup, a repo), or a
|
||||
project export `.zip`. Infers the resource name from the namespace the project
|
||||
already uses — no configuration.
|
||||
|
||||
Exit codes: `0` clean · `1` findings · `2` bad invocation.
|
||||
|
||||
## The one rule that is actually mandatory
|
||||
|
||||
The guide states exactly one `must`:
|
||||
|
||||
> Views, Styles, Scripts, etc. **must** all be contained within an appropriately
|
||||
> named folder. This allows easy imports into existing projects, where those
|
||||
> project resources won't overlap, interfere, or overwrite existing parts of a
|
||||
> user's project.
|
||||
|
||||
Everything else — every naming convention — the guide explicitly calls optional:
|
||||
|
||||
> We encourage you to follow the naming conventions outlined here, however
|
||||
> **these conventions are not required**.
|
||||
|
||||
So `exchange_lint.py` reports **FAIL** only for namespacing, the storage
|
||||
prohibitions, and the project-name character set. Naming is **warn**. Don't
|
||||
report warnings as blockers, and don't contort a project to silence them —
|
||||
a consistent house style is defensible and the guide says so.
|
||||
|
||||
## Namespace roots — two spellings, both correct
|
||||
|
||||
Per the guide's Project Browser screenshots:
|
||||
|
||||
| Under `Exchange/<ResourceName>/` | Under `exchange/<resource-name>/` |
|
||||
|---|---|
|
||||
| Views, Windows, Templates | Style classes |
|
||||
| Named queries, Reports | Project library (scripts) |
|
||||
| SFCs, Transaction groups | WebDev sources |
|
||||
| Alarm pipelines, Tags | Images |
|
||||
|
||||
Uppercase roots take Title Case or PascalCase names; lowercase roots take
|
||||
kebab-case — **except the project library**, which is code-facing and uses
|
||||
lowercase or camelCase (`exchange.resourceName.scriptName`), matching the
|
||||
logger convention `exchange.resourceName.LoggerName`.
|
||||
|
||||
## The storage prohibitions
|
||||
|
||||
Both are FAILs because both break a clean import, and both are easy to miss:
|
||||
|
||||
- **Perspective session custom properties.** Importing merges them into the host
|
||||
project's session props. Use view `params`/`custom` plus, for genuinely
|
||||
non-reactive cross-session state, `system.util.getGlobals()['exchange']['resourceName']`.
|
||||
- **Vision client tags.** Same reasoning.
|
||||
|
||||
## What the checker cannot tell you
|
||||
|
||||
A clean report is not "ready to publish". Four gaps, all of which have bitten:
|
||||
|
||||
1. **Content.** It reads names and structure, never label text. A visible string
|
||||
like `"Acme Internal — do not distribute"` passes silently. Always sweep
|
||||
user-visible strings separately:
|
||||
```bash
|
||||
python3 - <<'PY'
|
||||
import json, glob, re
|
||||
bad = re.compile(r"internal|confidential|do not|contest|demo only|todo|fixme", re.I)
|
||||
for f in glob.glob("<project>/com.inductiveautomation.perspective/views/**/view.json", recursive=True):
|
||||
v = json.load(open(f))
|
||||
def walk(n):
|
||||
yield n
|
||||
for c in (n.get("children") or []): yield from walk(c)
|
||||
for n in walk(v.get("root") or {}):
|
||||
t = (n.get("props") or {}).get("text")
|
||||
if isinstance(t, str) and bad.search(t): print(f, repr(t))
|
||||
PY
|
||||
```
|
||||
2. **Referential integrity.** It does not check that embedded view paths, style
|
||||
classes or script calls resolve. See [`ignition-exchange-convert`](../ignition-exchange-convert/SKILL.md).
|
||||
3. **Runtime.** Nothing is executed or rendered. Load it on a gateway.
|
||||
4. **The shipped package.** `E-UP-DOCS` checks the working directory, not the
|
||||
export zip. See [`ignition-exchange-publish`](../ignition-exchange-publish/SKILL.md).
|
||||
|
||||
## Findings that need interpretation
|
||||
|
||||
**`E-STYLE-HIER` false positives.** It decides where a style class belongs by
|
||||
finding which views reference it, reading only literal `props.style.classes`
|
||||
strings. A class name built in a binding —
|
||||
`'exchange/my-resource/priority/' + level.lower()` — is invisible, so a shared
|
||||
class gets reported as single-use. Verify before acting; do not duplicate style
|
||||
classes to silence it.
|
||||
|
||||
**`E-PAGE-ROOT` is not in the guide.** It is an added caution: a page mapped to
|
||||
`/` takes over the root URL of any project the resource is imported into. Real
|
||||
trade-off — a resource with no `/` page cannot be launched from the gateway's
|
||||
Perspective project list, only from its full page URL
|
||||
(`/data/perspective/client/<project>/<page>`). Flag the trade-off; let the user
|
||||
decide.
|
||||
|
||||
**`E-PY-VAR` volume.** A project written in idiomatic snake_case produces
|
||||
hundreds of warnings, since the guide wants camelCase for variables and
|
||||
functions (deliberately diverging from PEP-8; `UPPER_SNAKE` constants are
|
||||
exempt). Offer `--ignore E-PY-VAR,E-PY-FUNC` rather than a mass rename, unless
|
||||
the user wants full conformance.
|
||||
|
||||
## Useful flags
|
||||
|
||||
| Flag | Effect |
|
||||
|---|---|
|
||||
| `--strict` | Warnings become failures |
|
||||
| `--ignore E-PY-VAR,E-COMP-NAME` | Suppress rules |
|
||||
| `-v` | Every occurrence (default collapses at 8 per rule) |
|
||||
| `--json` | Machine-readable, for CI |
|
||||
| `--list-rules` | All 38 rules with descriptions |
|
||||
|
||||
## Reporting results
|
||||
|
||||
The summary reads `N of 38 rules exercised`. Quote it. A project with no
|
||||
database, tags or named queries legitimately leaves those rules unexercised, and
|
||||
a narrow pass should never be presented as a broad one.
|
||||
|
||||
## Self-test
|
||||
|
||||
`test_exchange_lint.py` builds a conforming and a non-conforming project in a
|
||||
temp dir, asserts the checker is silent on the first and fires the expected rule
|
||||
id on the second, and asserts every declared rule is reachable in the source.
|
||||
Run it after changing any rule.
|
||||
|
||||
```bash
|
||||
python3 skills/ignition-exchange-conformance/test_exchange_lint.py
|
||||
```
|
||||
|
||||
## Provenance
|
||||
|
||||
Rules derived from Exchange Resources Style Guide v1.1.0. Checker exercised
|
||||
against four real Perspective projects and validated on Ignition **8.1.20** and
|
||||
**8.3.7** gateways (2026-09-15). Where a rule is an inference rather than guide
|
||||
text it says so in its description — currently only `E-PAGE-ROOT`.
|
||||
Reference in New Issue
Block a user