8adfd39bff
Ships marketplace-design-v2 scoped to bundle hygiene (NO consumer UI, NO install dialog, NO marketplace). 2-3 day cap respected — Approach C narrow slice. Anti-feature commitment: scope creep → new design doc + /office-hours. ## What ships ### New module ordinis-bundle-spec - BundleManifest.java — canonical apiVersion: ordinis.io/v1 record (Metadata + DictionaryEntry, Jackson-bindable, forward-compat ignoreUnknown) - ManifestParser.java — YAML → record + structural validate (apiVersion/kind enforce, semver regex, bundle-id regex, path traversal guard на schemaRef, unique dict names, scope enum) - BundleSignatureVerifier.java — ed25519 sign/verify (JDK 21 native) - Zero Spring/JPA/Kafka deps — pure Java + Jackson YAML ### CUOD reference implementation - ordinis-cuod-bundle/manifest.yaml — 40 dicts canonical entries - Test fixture pin: cuod-manifest-fixture.yaml parses+validates clean (regression guard на каждом CI run) ### Tests - 12 cases ManifestParser (happy + reject paths) - 7 cases BundleSignatureVerifier (sign/verify roundtrip, tamper, wrong key) - 1 case CUOD fixture pin - Total 20 tests, all green ### Docs - docs/integration/bundle-format-spec.md — formal spec для Java handoff - docs/user-guide/bundle-authoring.md — how-to для новых bundle authors ## Why now (N=1 customer) Per office-hours v2 decision: marketplace UI build = N customers value scale, but bundle hygiene + sign format = AT N=1 ROI: - Java team handoff requires documented spec независимо - Sales demo «here's how customer #2 would ship custom bundle» - Optionality для marketplace UI когда customer #2 LOI surfaces ## Explicitly NOT shipped (Phase 2 scope if ever) - Publisher Maven plugin (separate artifact when actual publish needed) - Browse catalog UI / install dialog (no customer #2 with use case) - Dep resolution / topological install - Multi-tenant Nexus namespacing - Bundle uninstall flow
143 lines
4.5 KiB
Markdown
143 lines
4.5 KiB
Markdown
# Authoring a Bundle
|
|
|
|
How to package справочники как distributable Ordinis bundle.
|
|
|
|
См. также: [Bundle Format Specification](../integration/bundle-format-spec.md) — formal spec.
|
|
|
|
---
|
|
|
|
## Quick start
|
|
|
|
```bash
|
|
# 1. Create bundle structure
|
|
mkdir -p my-bundle/src/main/resources/bundles/my-id
|
|
cd my-bundle
|
|
|
|
# 2. Add at least one schema
|
|
cat > src/main/resources/bundles/my-id/widget.schema.json <<'JSON'
|
|
{
|
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
"type": "object",
|
|
"x-id-source": "code",
|
|
"required": ["code", "name"],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"code": { "type": "string", "pattern": "^[A-Z][A-Z0-9_]{1,31}$", "x-unique": true },
|
|
"name": {
|
|
"type": "object", "x-localized": true,
|
|
"patternProperties": { "^[a-z]{2}-[A-Z]{2}$": { "type": "string" } },
|
|
"required": ["ru-RU"]
|
|
}
|
|
}
|
|
}
|
|
JSON
|
|
|
|
# 3. Write manifest
|
|
cat > manifest.yaml <<'YAML'
|
|
apiVersion: ordinis.io/v1
|
|
kind: Bundle
|
|
metadata:
|
|
id: my-org.widgets
|
|
name: My Widgets Bundle
|
|
version: 1.0.0
|
|
scope: PUBLIC
|
|
dictionaries:
|
|
- name: widget
|
|
schemaVersion: 1.0.0
|
|
scope: PUBLIC
|
|
schemaRef: src/main/resources/bundles/my-id/widget.schema.json
|
|
YAML
|
|
```
|
|
|
|
---
|
|
|
|
## Versioning
|
|
|
|
- `metadata.version` — semver. Patch на data fixes, minor на add'инге dict'ов, major на breaking schema changes.
|
|
- `dictionaries[].schemaVersion` — per-dict semver, independent от bundle version. Bump когда меняется shape конкретной schema.
|
|
- Breaking schema change (remove required field, change type, add `additionalProperties: false`) → MAJOR bump bundle.
|
|
|
|
---
|
|
|
|
## Validation locally
|
|
|
|
```bash
|
|
# Через maven (если bundle — Maven artifact):
|
|
mvn test -pl ordinis-bundle-spec -Dtest=CuodManifestFixtureTest
|
|
|
|
# Или programmatically:
|
|
import cloud.nstart.terravault.ordinis.bundle.spec.*;
|
|
|
|
byte[] yaml = Files.readAllBytes(Path.of("manifest.yaml"));
|
|
List<String> errors = ManifestParser.parseAndValidate(yaml);
|
|
if (!errors.isEmpty()) {
|
|
errors.forEach(System.err::println);
|
|
System.exit(1);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Signing (production publish)
|
|
|
|
Ed25519 keypair generation:
|
|
```bash
|
|
openssl genpkey -algorithm Ed25519 -out signing-priv.pem
|
|
openssl pkey -in signing-priv.pem -pubout -outform DER -out signing-pub.der
|
|
```
|
|
|
|
Sign bundle archive (until publisher Maven plugin lands — manual для now):
|
|
```java
|
|
byte[] archive = Files.readAllBytes(Path.of("bundle.tar.gz"));
|
|
byte[] privKey = Files.readAllBytes(Path.of("signing-priv.der")); // convert PEM→DER first
|
|
String sig = BundleSignatureVerifier.sign(archive, privKey);
|
|
// → patch manifest.yaml: metadata.signature: <sig>
|
|
// OR write sidecar bundle.sig: <sig>
|
|
```
|
|
|
|
Public key распространяется к operators которые verify bundles перед install.
|
|
|
|
---
|
|
|
|
## Conventions
|
|
|
|
### Naming
|
|
|
|
- `metadata.id` — reverse-DNS (`ru.cuod.dzz-ground-segment`, `com.acme.widgets`).
|
|
- `dictionaries[].name` — snake_case (`spacecraft`, `ground_station`, `frequency_band`).
|
|
|
|
### Locales
|
|
|
|
Localized fields (`x-localized: true`) — каждое значение это объект `{lang-COUNTRY: "..."}`:
|
|
```json
|
|
"name": { "ru-RU": "Космический аппарат", "en-US": "Spacecraft" }
|
|
```
|
|
|
|
Bundle сам не объявляет supported locales — это per-dict. Manifest may add optional `metadata.locales` в future v1.x но spec строго ignores unknown сейчас.
|
|
|
|
### Foreign keys
|
|
|
|
`x-references: "<target_dict>.<field>"` — backend validates на write. Если target dict в том же bundle — order ensured (см. consumer protocol §6 в spec).
|
|
|
|
---
|
|
|
|
## Anti-patterns
|
|
|
|
- ❌ Не shipпите `additionalProperties: true` в production schemas — типизация теряется
|
|
- ❌ Не используйте `$ref` к внешним URLs (security risk + offline build fail)
|
|
- ❌ Не делайте 100+ dicts в одном bundle — расщепляйте по domain (e.g. `cuod-spacecraft`, `cuod-radio`)
|
|
- ❌ Не bump'айте `schemaVersion` без реального schema change
|
|
|
|
---
|
|
|
|
## Handoff checklist (Java team)
|
|
|
|
Когда передаёте bundle authoring к новой команде:
|
|
1. ✅ Прочитать [bundle-format-spec.md](../integration/bundle-format-spec.md)
|
|
2. ✅ Изучить `ordinis-cuod-bundle/manifest.yaml` как working example
|
|
3. ✅ Прогнать `mvn test -pl ordinis-bundle-spec` локально
|
|
4. ✅ Generate signing keypair, distribute pub key к ops
|
|
5. ✅ Set up CI publish job (когда `ordinis-bundle-publisher` plugin landed)
|
|
|
|
Для вопросов: ordinis maintainers (см. CODEOWNERS).
|