ossplate 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,26 @@
1
- # JavaScript Wrapper For Ossplate
1
+ # Ossplate
2
2
 
3
- This package is the JavaScript wrapper surface for Ossplate.
3
+ `ossplate` helps you start and maintain a project that ships the same CLI through Rust, npm, and PyPI.
4
4
 
5
- It delegates to the canonical Rust binary instead of implementing its own CLI behavior.
5
+ Use it to:
6
6
 
7
- Use `OSSPLATE_BINARY` during local development to point the wrapper at a specific binary.
7
+ - create a new scaffolded project
8
+ - initialize an existing directory
9
+ - validate project identity and metadata
10
+ - keep owned files in sync
11
+
12
+ Common commands:
13
+
14
+ ```bash
15
+ ossplate version
16
+ ossplate create <target>
17
+ ossplate init --path <dir>
18
+ ossplate validate
19
+ ossplate sync --check
20
+ ```
21
+
22
+ Learn more:
23
+
24
+ - [Main documentation](../docs/README.md)
25
+ - [Testing guide](../docs/testing.md)
26
+ - [Architecture](../docs/architecture.md)
Binary file
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "bin": {
4
4
  "ossplate": "bin/ossplate.js"
5
5
  },
6
- "description": "A practical baseline for shipping one project across Cargo, npm, and PyPI without starting from scratch every time.",
6
+ "description": "Build one project, ship it everywhere.",
7
7
  "devDependencies": {
8
8
  "@types/node": "^24.6.0",
9
9
  "typescript": "^5.9.3"
@@ -39,5 +39,5 @@
39
39
  "test": "npm run build && node --test test/cli.test.js"
40
40
  },
41
41
  "type": "module",
42
- "version": "0.1.0"
42
+ "version": "0.1.1"
43
43
  }
@@ -38,8 +38,27 @@ jobs:
38
38
  - if: steps.published.outputs.already != 'true'
39
39
  run: npm ci
40
40
  working-directory: ./wrapper-js
41
- - if: steps.published.outputs.already != 'true'
42
- run: npm publish --access public
41
+ - name: Publish to npm
42
+ if: steps.published.outputs.already != 'true'
43
43
  working-directory: ./wrapper-js
44
44
  env:
45
45
  NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
46
+ run: |
47
+ set +e
48
+ npm publish --access public --provenance 2>&1 | tee npm-publish.log
49
+ status=${PIPESTATUS[0]}
50
+ set -e
51
+ if [ "$status" -eq 0 ]; then
52
+ echo "::notice title=npm::published with OIDC trusted publishing."
53
+ exit 0
54
+ fi
55
+ if [ -z "${NODE_AUTH_TOKEN:-}" ]; then
56
+ echo "::error title=npm::OIDC publish failed and NPM_TOKEN is not configured."
57
+ exit "$status"
58
+ fi
59
+ if grep -qiE "already exists|cannot publish over|previously published" npm-publish.log; then
60
+ echo "::notice title=npm::package version already exists; skipping."
61
+ exit 0
62
+ fi
63
+ echo "::warning title=npm::OIDC publish failed; retrying with NPM_TOKEN fallback."
64
+ npm publish --access public
@@ -21,6 +21,8 @@ cargo run --manifest-path core-rs/Cargo.toml -- sync --check
21
21
  cargo run --manifest-path core-rs/Cargo.toml -- sync
22
22
  ```
23
23
 
24
+ Release workflow guidance lives in [`docs/releases.md`](./docs/releases.md).
25
+
24
26
  Current ownership model:
25
27
 
26
28
  - `ossplate.toml` is the canonical identity source
@@ -1,36 +1,32 @@
1
1
  <!-- ossplate:readme-identity:start -->
2
2
  # Ossplate
3
3
 
4
- A practical baseline for shipping one project across Cargo, npm, and PyPI without starting from scratch every time.
4
+ Build one project, ship it everywhere.
5
5
  <!-- ossplate:readme-identity:end -->
6
6
 
7
- ## What This Tool Gives You
7
+ ![Ossplate chestplate](./assets/illustrations/chestplate.svg)
8
8
 
9
- - a canonical Rust CLI in [`core-rs/`](./core-rs)
10
- - a thin npm wrapper in [`wrapper-js/`](./wrapper-js)
11
- - a thin Python wrapper in [`wrapper-py/`](./wrapper-py)
12
- - normal `push` / `pull_request` CI
13
- - rerun-safe publish workflows for npm, PyPI, and Cargo
14
- - setup and upgrade docs in [`docs/`](./docs/README.md)
9
+ `ossplate` helps you start and maintain a project that ships the same CLI through Rust, npm, and PyPI.
15
10
 
16
- ## Philosophy
11
+ It gives you a working baseline with:
17
12
 
18
- This project is intentionally small.
13
+ - one real core CLI
14
+ - thin JavaScript and Python wrappers
15
+ - release-ready workflows for Cargo, npm, and PyPI
16
+ - a scaffold you can create, adopt, and keep in sync
19
17
 
20
- It exists to validate and synchronize the shared identity of a multi-registry OSS scaffold before broader scaffolding features are added.
18
+ ## What It Does
21
19
 
22
- ## Core Commands
20
+ Use `ossplate` when you want a single command-line tool to exist cleanly in multiple ecosystems without maintaining three separate implementations.
23
21
 
24
- ```bash
25
- cargo run --manifest-path core-rs/Cargo.toml -- validate
26
- cargo run --manifest-path core-rs/Cargo.toml -- sync --check
27
- cargo run --manifest-path core-rs/Cargo.toml -- create ../my-new-project
28
- cargo run --manifest-path core-rs/Cargo.toml -- init --path ../existing-project
29
- ```
22
+ It can:
30
23
 
31
- Wrapper installs expose the same command surface as `ossplate`.
24
+ - create a new scaffolded project
25
+ - initialize an existing directory with the expected structure
26
+ - validate project identity and metadata
27
+ - synchronize the files it owns
32
28
 
33
- `create` and `init` now work from packaged wrapper artifacts as well as a source checkout. Installed wrappers carry a curated scaffold payload rather than a broad repo snapshot. Use flags to set the project identity during scaffold creation instead of editing `ossplate.toml` by hand first:
29
+ ## Quick Start
34
30
 
35
31
  ```bash
36
32
  cargo run --manifest-path core-rs/Cargo.toml -- create ../my-new-project \
@@ -44,29 +40,38 @@ cargo run --manifest-path core-rs/Cargo.toml -- create ../my-new-project \
44
40
  --command "my-project"
45
41
  ```
46
42
 
47
- ## Local Verify
43
+ Then check that everything is aligned:
48
44
 
49
- Run the full local verification flow with:
45
+ ```bash
46
+ cargo run --manifest-path core-rs/Cargo.toml -- validate
47
+ cargo run --manifest-path core-rs/Cargo.toml -- sync --check
48
+ ```
49
+
50
+ ## Core Commands
50
51
 
51
52
  ```bash
52
- ./scripts/verify.sh
53
+ ossplate version
54
+ ossplate create <target>
55
+ ossplate init --path <dir>
56
+ ossplate validate
57
+ ossplate sync --check
53
58
  ```
54
59
 
55
- This is the recommended local mirror of the CI gate.
60
+ The same command surface is available through the Rust binary and the packaged JS/Python wrappers.
56
61
 
57
- ## Verification
62
+ ## Why It’s Useful
58
63
 
59
- - local workflow and layered testing guidance live in [docs/testing.md](./docs/testing.md)
60
- - contributor workflow lives in [CONTRIBUTING.md](./CONTRIBUTING.md)
61
- - `ossplate validate` reports owned metadata drift
62
- - `ossplate sync --check` fails if owned metadata would be rewritten
63
- - JS and Python artifact tests prove installed distributions can run `version`, `create`, and `validate`
64
+ - You keep one source of truth for CLI behavior.
65
+ - You avoid drift between Rust, npm, and PyPI releases.
66
+ - You get a real scaffold instead of a fake demo project.
67
+ - You can publish with modern registry workflows instead of assembling release plumbing from scratch.
64
68
 
65
- ## Release Auth
69
+ ## Learn More
66
70
 
67
- - PyPI publishes from [`.github/workflows/publish.yml`](./.github/workflows/publish.yml) via GitHub OIDC trusted publishing
68
- - Cargo publishes from [`.github/workflows/publish.yml`](./.github/workflows/publish.yml) via OIDC trusted publishing with `secrets.CARGO_TOKEN` as fallback
69
- - npm publishes from [`.github/workflows/publish-npm.yml`](./.github/workflows/publish-npm.yml) with `secrets.NPM_TOKEN`
71
+ - [Documentation](./docs/README.md)
72
+ - [Testing Guide](./docs/testing.md)
73
+ - [Release Guide](./docs/releases.md)
74
+ - [Architecture](./docs/architecture.md)
70
75
 
71
76
  ## License
72
77
 
@@ -0,0 +1,46 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 420" role="img" aria-labelledby="title desc">
2
+ <title id="title">Ossplate chestplate illustration</title>
3
+ <desc id="desc">A bronze chestplate with shoulder guards on a warm background.</desc>
4
+ <defs>
5
+ <linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
6
+ <stop offset="0%" stop-color="#f7ecdd"/>
7
+ <stop offset="100%" stop-color="#dcb88b"/>
8
+ </linearGradient>
9
+ <linearGradient id="metal" x1="0%" y1="0%" x2="0%" y2="100%">
10
+ <stop offset="0%" stop-color="#ddaf68"/>
11
+ <stop offset="50%" stop-color="#b77735"/>
12
+ <stop offset="100%" stop-color="#7b491c"/>
13
+ </linearGradient>
14
+ <linearGradient id="rim" x1="0%" y1="0%" x2="100%" y2="100%">
15
+ <stop offset="0%" stop-color="#fff0bf"/>
16
+ <stop offset="100%" stop-color="#a05d24"/>
17
+ </linearGradient>
18
+ <linearGradient id="highlight" x1="0%" y1="0%" x2="100%" y2="100%">
19
+ <stop offset="0%" stop-color="#fff8de" stop-opacity="0.95"/>
20
+ <stop offset="100%" stop-color="#fff8de" stop-opacity="0"/>
21
+ </linearGradient>
22
+ <filter id="shadow" x="-20%" y="-20%" width="140%" height="160%">
23
+ <feDropShadow dx="0" dy="16" stdDeviation="12" flood-color="#673d18" flood-opacity="0.28"/>
24
+ </filter>
25
+ </defs>
26
+
27
+ <rect width="720" height="420" fill="url(#bg)"/>
28
+ <circle cx="110" cy="84" r="58" fill="#fff7de" opacity="0.4"/>
29
+ <circle cx="607" cy="314" r="76" fill="#a86e33" opacity="0.12"/>
30
+
31
+ <g filter="url(#shadow)" transform="translate(150 28)">
32
+ <path d="M151 46c12-18 27-29 59-34 16-2 42-3 59-3s43 1 59 3c32 5 47 16 59 34l24 35c7 10 4 24-6 31l-22 16c-8 6-19 7-29 2l-27-13v90c0 60-26 102-58 129-32 26-64 39-91 45-27-6-59-19-91-45-32-27-58-69-58-129v-90l-27 13c-10 5-21 4-29-2l-22-16c-10-7-13-21-6-31z" fill="url(#metal)"/>
33
+ <path d="M151 46c12-18 27-29 59-34 16-2 42-3 59-3s43 1 59 3c32 5 47 16 59 34l24 35c7 10 4 24-6 31l-22 16c-8 6-19 7-29 2l-27-13v90c0 60-26 102-58 129-32 26-64 39-91 45-27-6-59-19-91-45-32-27-58-69-58-129v-90l-27 13c-10 5-21 4-29-2l-22-16c-10-7-13-21-6-31z" fill="none" stroke="#6b4118" stroke-width="10" stroke-linejoin="round"/>
34
+ <path d="M210 52c11-14 26-20 44-22 15-2 29-2 46-2s31 0 46 2c18 2 33 8 44 22l15 20c-41 18-92 27-105 29-13-2-64-11-105-29z" fill="#c78741" opacity="0.88"/>
35
+ <path d="M126 74l32 16c10 5 21 4 30-2l20-13c18 9 43 16 72 20v204c-56-15-121-61-121-158V93c0-8-12-13-33-19z" fill="#845022" opacity="0.34"/>
36
+ <path d="M474 74l-32 16c-10 5-21 4-30-2l-20-13c-18 9-43 16-72 20v204c56-15 121-61 121-158V93c0-8 12-13 33-19z" fill="#f0c27a" opacity="0.2"/>
37
+ <path d="M300 92v214" stroke="#6b4118" stroke-width="8" stroke-linecap="round" opacity="0.82"/>
38
+ <path d="M196 134c29 12 65 18 104 18s75-6 104-18" stroke="#6b4118" stroke-width="7" stroke-linecap="round" opacity="0.72"/>
39
+ <path d="M206 215c24 9 56 14 94 14s70-5 94-14" stroke="#6b4118" stroke-width="6" stroke-linecap="round" opacity="0.62"/>
40
+ <path d="M235 42c19 10 40 16 65 19-13 10-23 24-29 41-25-4-48-12-68-25 7-16 17-27 32-35z" fill="url(#highlight)" opacity="0.72"/>
41
+ <path d="M300 61c25-3 46-9 65-19 15 8 25 19 32 35-20 13-43 21-68 25-6-17-16-31-29-41z" fill="url(#highlight)" opacity="0.5"/>
42
+ <path d="M147 62l-41 58c-5 7-3 17 4 22l18 13c5 4 12 4 18 1l41-19-9-53c-2-11-14-21-31-22z" fill="url(#rim)" opacity="0.95"/>
43
+ <path d="M453 62l41 58c5 7 3 17-4 22l-18 13c-5 4-12 4-18 1l-41-19 9-53c2-11 14-21 31-22z" fill="url(#rim)" opacity="0.95"/>
44
+ <path d="M214 310c26 14 56 23 86 28 30-5 60-14 86-28" stroke="#f3d39a" stroke-width="6" stroke-linecap="round" opacity="0.45"/>
45
+ </g>
46
+ </svg>
@@ -158,7 +158,7 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
158
158
 
159
159
  [[package]]
160
160
  name = "ossplate"
161
- version = "0.1.0"
161
+ version = "0.1.1"
162
162
  dependencies = [
163
163
  "anyhow",
164
164
  "clap",
@@ -1,9 +1,9 @@
1
1
  [package]
2
2
  name = "ossplate"
3
- version = "0.1.0"
3
+ version = "0.1.1"
4
4
  edition = "2021"
5
5
  authors = ["Stef <stefdevscore@github.com>"]
6
- description = "A practical baseline for shipping one project across Cargo, npm, and PyPI without starting from scratch every time."
6
+ description = "Build one project, ship it everywhere."
7
7
  license = "Unlicense"
8
8
  readme = "../README.md"
9
9
  repository = "https://github.com/stefdevscore/ossplate"
@@ -1040,18 +1040,37 @@ fn validate_wrapper_readme(
1040
1040
  }
1041
1041
  }
1042
1042
 
1043
- fn render_wrapper_readme(language: &str, config: &ToolConfig) -> String {
1043
+ fn render_wrapper_readme(_language: &str, config: &ToolConfig) -> String {
1044
1044
  format!(
1045
- r#"# {language} Wrapper For {name}
1045
+ r#"# {name}
1046
1046
 
1047
- This package is the {language} wrapper surface for {name}.
1047
+ `{command}` helps you start and maintain a project that ships the same CLI through Rust, npm, and PyPI.
1048
1048
 
1049
- It delegates to the canonical Rust binary instead of implementing its own CLI behavior.
1049
+ Use it to:
1050
1050
 
1051
- Use `OSSPLATE_BINARY` during local development to point the wrapper at a specific binary.
1051
+ - create a new scaffolded project
1052
+ - initialize an existing directory
1053
+ - validate project identity and metadata
1054
+ - keep owned files in sync
1055
+
1056
+ Common commands:
1057
+
1058
+ ```bash
1059
+ {command} version
1060
+ {command} create <target>
1061
+ {command} init --path <dir>
1062
+ {command} validate
1063
+ {command} sync --check
1064
+ ```
1065
+
1066
+ Learn more:
1067
+
1068
+ - [Main documentation](../docs/README.md)
1069
+ - [Testing guide](../docs/testing.md)
1070
+ - [Architecture](../docs/architecture.md)
1052
1071
  "#,
1053
- language = language,
1054
1072
  name = config.project.name,
1073
+ command = config.packages.command,
1055
1074
  )
1056
1075
  }
1057
1076
 
@@ -1266,7 +1285,7 @@ mod tests {
1266
1285
  let root = make_fixture_root();
1267
1286
  fs::write(
1268
1287
  root.join("wrapper-js/package.json"),
1269
- "{\n \"name\": \"bad\",\n \"description\": \"A practical baseline for shipping one project across Cargo, npm, and PyPI without starting from scratch every time.\",\n \"bin\": { \"ossplate\": \"bin/ossplate.js\" },\n \"author\": \"Stef <stefdevscore@github.com>\",\n \"license\": \"Unlicense\",\n \"repository\": { \"url\": \"https://github.com/stefdevscore/ossplate\" }\n}\n",
1288
+ "{\n \"name\": \"bad\",\n \"description\": \"Build one project, ship it everywhere.\",\n \"bin\": { \"ossplate\": \"bin/ossplate.js\" },\n \"author\": \"Stef <stefdevscore@github.com>\",\n \"license\": \"Unlicense\",\n \"repository\": { \"url\": \"https://github.com/stefdevscore/ossplate\" }\n}\n",
1270
1289
  )
1271
1290
  .unwrap();
1272
1291
  let output = validate_repo(&root).unwrap();
@@ -1386,7 +1405,7 @@ mod tests {
1386
1405
  target.join("core-rs/Cargo.toml"),
1387
1406
  r#"[package]
1388
1407
  name = "bad-core"
1389
- version = "0.1.0"
1408
+ version = "0.1.1"
1390
1409
  "#,
1391
1410
  )
1392
1411
  .unwrap();
@@ -1440,7 +1459,7 @@ version = "0.1.0"
1440
1459
  fs::write(
1441
1460
  root.join("README.md"),
1442
1461
  original.replace(
1443
- "A practical baseline for shipping one project",
1462
+ "Build one project, ship it everywhere",
1444
1463
  "Changed identity text",
1445
1464
  ),
1446
1465
  )
@@ -1449,7 +1468,7 @@ version = "0.1.0"
1449
1468
  sync_repo(&root, false).unwrap();
1450
1469
  let synced = fs::read_to_string(root.join("README.md")).unwrap();
1451
1470
  assert!(synced.contains("## What This Tool Gives You"));
1452
- assert!(synced.contains("A practical baseline for shipping one project"));
1471
+ assert!(synced.contains("Build one project, ship it everywhere"));
1453
1472
  }
1454
1473
 
1455
1474
  #[test]
@@ -1499,7 +1518,7 @@ version = "0.1.0"
1499
1518
  let config = r#"[project]
1500
1519
  name = "Ossplate"
1501
1520
  slug = "ossplate"
1502
- description = "A practical baseline for shipping one project across Cargo, npm, and PyPI without starting from scratch every time."
1521
+ description = "Build one project, ship it everywhere."
1503
1522
  repository = "https://github.com/stefdevscore/ossplate"
1504
1523
  license = "Unlicense"
1505
1524
 
@@ -1522,7 +1541,7 @@ command = "ossplate"
1522
1541
  root.join("core-rs/Cargo.toml"),
1523
1542
  r#"[package]
1524
1543
  name = "ossplate"
1525
- version = "0.1.0"
1544
+ version = "0.1.1"
1526
1545
  edition = "2021"
1527
1546
  authors = ["Stef <stefdevscore@github.com>"]
1528
1547
  description = "A practical baseline for shipping one project across Cargo, npm, and PyPI without starting from scratch every time."
@@ -1535,14 +1554,14 @@ homepage = "https://github.com/stefdevscore/ossplate"
1535
1554
  .unwrap();
1536
1555
  fs::write(
1537
1556
  root.join("wrapper-js/package.json"),
1538
- "{\n \"name\": \"ossplate\",\n \"description\": \"A practical baseline for shipping one project across Cargo, npm, and PyPI without starting from scratch every time.\",\n \"bin\": { \"ossplate\": \"bin/ossplate.js\" },\n \"author\": \"Stef <stefdevscore@github.com>\",\n \"license\": \"Unlicense\",\n \"repository\": { \"url\": \"https://github.com/stefdevscore/ossplate\" }\n}\n",
1557
+ "{\n \"name\": \"ossplate\",\n \"description\": \"Build one project, ship it everywhere.\",\n \"bin\": { \"ossplate\": \"bin/ossplate.js\" },\n \"author\": \"Stef <stefdevscore@github.com>\",\n \"license\": \"Unlicense\",\n \"repository\": { \"url\": \"https://github.com/stefdevscore/ossplate\" }\n}\n",
1539
1558
  )
1540
1559
  .unwrap();
1541
1560
  fs::write(
1542
1561
  root.join("wrapper-py/pyproject.toml"),
1543
1562
  r#"[project]
1544
1563
  name = "ossplate"
1545
- description = "A practical baseline for shipping one project across Cargo, npm, and PyPI without starting from scratch every time."
1564
+ description = "Build one project, ship it everywhere."
1546
1565
  license = { text = "Unlicense" }
1547
1566
  authors = [
1548
1567
  { name = "Stef", email = "stefdevscore@github.com" }
@@ -5,7 +5,9 @@ This docs area explains how to use `ossplate` as a real tool for validating, syn
5
5
  ## Start Here
6
6
 
7
7
  - [Customizing The Template](./customizing-the-template.md)
8
+ - [Architecture](./architecture.md)
8
9
  - [Testing Guide](./testing.md)
10
+ - [Release Guide](./releases.md)
9
11
  - [Phase 1 Contract](./phase-1-contract.md)
10
12
  - [Upgrade Plan](./upgrade-plan.md)
11
13
  - `ossplate validate` checks owned metadata drift
@@ -18,4 +20,5 @@ This docs area explains how to use `ossplate` as a real tool for validating, syn
18
20
  - the canonical config and command surface
19
21
  - the required rename and customization surface before first release
20
22
  - the layered testing and packaging workflow
23
+ - the release operator flow and rerun-safe publish expectations
21
24
  - the phased plan for turning this tool into a broader scaffold product
@@ -1,6 +1,6 @@
1
- # Customizing The Template
1
+ # Adopting The Scaffold
2
2
 
3
- Use this checklist after creating or cloning a scaffold managed by `ossplate`. The goal is to replace inherited identity deliberately and then use the tool to keep owned metadata in sync.
3
+ Use this checklist after creating or cloning a scaffold managed by `ossplate`. The goal is to adopt the scaffold under your own project identity and then use the tool to keep owned metadata in sync.
4
4
 
5
5
  ## Canonical Source Of Truth
6
6
 
@@ -24,18 +24,18 @@ It currently owns:
24
24
 
25
25
  ## Validation Policy
26
26
 
27
- - Placeholder identities are allowed only when the repository is discussing the template itself.
28
- - Placeholder identities are not allowed in shipping metadata or package-facing docs for an adopted project.
27
+ - Template-source identities are allowed only when the repository is discussing `ossplate` itself.
28
+ - Inherited identities are not allowed in shipping metadata or package-facing docs for an adopted project.
29
29
  - Command and package naming must be chosen intentionally before release.
30
30
  - Author, repository, and license fields must be set explicitly rather than inherited accidentally.
31
31
 
32
32
  The current validator follows this policy through the Rust core rather than a standalone JS rule engine.
33
33
 
34
- ## Required Renames
34
+ ## Required Identity Changes
35
35
 
36
- Replace these defaults before reuse:
36
+ Replace these inherited defaults before reuse:
37
37
 
38
- | Surface | Current placeholder | Where it lives |
38
+ | Surface | Current scaffold value | Where it lives |
39
39
  | --- | --- | --- |
40
40
  | Rust crate name | `ossplate` | `core-rs/Cargo.toml` |
41
41
  | npm package name | `ossplate` | `wrapper-js/package.json` |
@@ -43,7 +43,7 @@ Replace these defaults before reuse:
43
43
  | CLI command | `ossplate` | `ossplate.toml`, `wrapper-js/package.json`, `wrapper-py/pyproject.toml` |
44
44
  | Repository URL | `https://github.com/stefdevscore/ossplate` | Rust, npm, Python metadata |
45
45
  | Author/email | `Stef <stefdevscore@github.com>` / `stefdevscore@github.com` | Rust, npm, Python metadata |
46
- | Package-facing template branding | `OSS template`, `template` identity in wrapper docs | `wrapper-js/README.md`, `wrapper-py/README.md` |
46
+ | Package-facing scaffold branding | `ossplate` identity in wrapper docs | `wrapper-js/README.md`, `wrapper-py/README.md` |
47
47
 
48
48
  ## Files To Review
49
49
 
@@ -139,6 +139,6 @@ cargo run --manifest-path core-rs/Cargo.toml -- init \
139
139
 
140
140
  This tool is trying to optimize for a real delivery baseline:
141
141
 
142
- - CI should fail before placeholder identity reaches a release path.
142
+ - CI should fail before inherited identity reaches a release path.
143
143
  - package metadata should not be inherited by accident
144
144
  - future phases can add richer scaffold creation and maintenance without first cleaning up metadata drift
@@ -69,3 +69,5 @@ CI currently enforces:
69
69
  - Python tests and wheel build
70
70
 
71
71
  The current artifact tests are the required release-confidence floor. Future phases can add broader platform coverage or slower end-to-end suites without changing the basic layered model.
72
+
73
+ For release-specific operator steps, version bumps, and rerun-safe publish expectations, see [`docs/releases.md`](./releases.md).
@@ -1,251 +1,88 @@
1
- # Ossplate Upgrade Plan
1
+ # Ossplate Roadmap
2
2
 
3
- ## Goal
4
-
5
- Turn `ossplate` from a minimal multi-registry demo into a production-ready scaffold tool with:
6
-
7
- - one canonical core program
8
- - real wrapper parity across Rust, TypeScript/JavaScript, and Python
9
- - robust CI and publish workflows with explicit auth fallbacks
10
- - layered testing guidance and enforcement
11
- - a structure that can scale toward a hexagonal architecture shell
12
-
13
- The product behavior is now real in the core maintenance and scaffold paths. Remaining work is about hardening, ergonomics, and release scale.
14
-
15
- ## Operating Principles
16
-
17
- - Keep one source of truth for CLI behavior and output contracts.
18
- - Prefer thin wrappers over duplicated implementations.
19
- - Fail early when template placeholders were not replaced.
20
- - Use OIDC where the registry path is configured and supported, with explicit token fallbacks elsewhere.
21
- - Require install/build/test/package checks before any publish step.
22
- - Keep the starter small, but not fake in the critical paths.
23
-
24
- ## Phase 0: Stabilize the Template Baseline
25
-
26
- Purpose: make the current scaffold safe to evolve and hard to misuse.
27
-
28
- ### P0
29
-
30
- - Add a release-readiness validator that fails on placeholder metadata.
31
- - Validate package names, crate name, binary names, repository URLs, author fields, and obvious placeholder strings.
32
- - Add a root documentation index under `docs/` so implementation docs have a stable home.
33
-
34
- ### P1
35
-
36
- - Normalize naming conventions across Cargo, npm, and PyPI packages.
37
- - Define the canonical command name and the expected wrapper naming pattern.
38
- - Document the template customization surface:
39
- project name, package ids, repo URL, author, license, binary name, description.
40
-
41
- ### P2
42
-
43
- - Remove or tighten weak placeholder copy in README files.
44
- - Add a checklist for creating a new project from the template.
45
-
46
- ### Exit Criteria
47
-
48
- - A new maintainer can identify every required rename/customization step.
49
- - CI can fail automatically if placeholder identity leaks into a release path.
50
-
51
- ## Phase 1: Establish the Canonical Core and Wrapper Parity
52
-
53
- Purpose: stop treating each ecosystem as a separate product stub.
54
-
55
- ### P0
56
-
57
- - Make the Rust binary the canonical executable implementation.
58
- - Define a small, stable placeholder command contract:
59
- `--help`, `version`, `health`, and one example command returning structured JSON.
60
- - Convert the JavaScript package into a real wrapper around the packaged binary.
61
- - Convert the Python package into a real wrapper around the packaged binary.
62
- - Ensure wrappers preserve exit codes, stdout, and stderr from the core binary.
63
-
64
- ### P1
65
-
66
- - Move `wrapper-js` source to TypeScript and publish built JavaScript artifacts.
67
- - Add binary resolution logic for supported platforms in both wrappers.
68
- - Add wrapper tests that assert parity against the core binary output contract.
69
-
70
- ### P2
71
-
72
- - Add a small shared contract document covering commands, arguments, output, and error behavior.
73
- - Add support for environment-based binary overrides for local development and debugging.
74
-
75
- ### Exit Criteria
76
-
77
- - Rust, JS, and Python packages all exercise the same underlying behavior.
78
- - Wrapper packages no longer maintain separate hand-written CLI logic.
79
-
80
- ## Phase 2: Production-Grade Quality Gates
81
-
82
- Purpose: make the scaffold trustworthy before publish automation runs.
83
-
84
- ### P0
85
-
86
- - Expand CI to enforce core quality gates in all three environments.
87
- - Add Rust checks:
88
- `cargo fmt --check`, `cargo clippy -- -D warnings`, `cargo test`.
89
- - Add JS/TS checks:
90
- install, typecheck, test, package dry-run.
91
- - Add Python checks:
92
- environment setup, tests, wheel/sdist build, install-from-built-artifact smoke test.
93
- - Add a cross-package parity job that verifies wrappers match core contract behavior.
94
-
95
- ### P1
96
-
97
- - Add artifact-level smoke tests for packaged npm and wheel outputs.
98
- - Add a matrix strategy where it materially improves confidence for supported platforms.
99
- - Make CI logs and job names intentionally clear for template adopters.
100
-
101
- ### P2
102
-
103
- - Add optional local aggregate commands or scripts for running all checks consistently.
104
- - Add a contributor quickstart for running the same quality gates outside CI.
105
-
106
- ### Exit Criteria
107
-
108
- - A release candidate must pass formatting, linting, unit tests, packaging, and parity checks.
109
- - Published artifacts are verified before publish jobs run.
110
-
111
- ## Phase 3: Robust Publishing with OIDC First and Concrete Fallbacks
112
-
113
- Purpose: keep publishing reliable even when registry auth or registry behavior is imperfect.
114
-
115
- ### P0
116
-
117
- - Keep publish workflows rerun-safe with published-version detection before attempting release.
118
- - Use OIDC or trusted publishing where supported and configured.
119
- - For the current `ossplate` baseline:
120
- PyPI uses OIDC, Cargo uses OIDC with `CARGO_TOKEN` fallback, and npm uses `NPM_TOKEN`.
121
- - Add explicit secret-based fallbacks:
122
- `NPM_TOKEN`, PyPI API token, and `CARGO_REGISTRY_TOKEN`.
123
- - Make auth mode selection visible in workflow logs.
124
- - Preserve non-destructive handling of already-published versions.
125
-
126
- ### P1
127
-
128
- - Document exact registry setup steps for both preferred and fallback auth modes.
129
- - Add validation that publish jobs fail clearly when neither OIDC nor token auth is configured.
130
- - Tighten registry-specific behavior:
131
- npm public access, PyPI trusted publishing expectations, crates.io version detection and retry behavior.
132
-
133
- ### P2
134
-
135
- - Add optional manual dispatch inputs for dry-run or targeted registry publish flows if they simplify maintenance.
136
- - Add guidance for organizations that intentionally disable OIDC and standardize on tokens.
137
-
138
- ### Exit Criteria
139
-
140
- - Publish workflows are understandable, rerunnable, and resilient.
141
- - Auth failures are explicit and actionable rather than implicit or flaky.
142
-
143
- ## Phase 4: Layered Testing Guidance and Scaffolding
144
-
145
- Purpose: teach adopters how to scale verification without overcomplicating the starter.
146
-
147
- ### P0
148
-
149
- - Add docs for the default testing pyramid:
150
- smoke, unit, integration/parity, and release verification.
151
- - Define minimum required tests for any project generated from this template.
152
- - Document how wrapper parity tests fit into the scaffold.
153
-
154
- ### P1
155
-
156
- - Add optional docs for live end-to-end browser testing with Playwright when a generated project includes a web UI.
157
- - Clarify that Playwright is not a default requirement for non-UI projects.
158
- - Add example CI placement for smoke vs slower e2e suites.
159
-
160
- ### P2
161
-
162
- - Add template examples of failure triage:
163
- unit regression, packaging regression, publish regression, wrapper parity regression.
164
-
165
- ### Exit Criteria
166
-
167
- - The testing strategy is clear enough that teams do not improvise incompatible structures from scratch.
168
- - Optional e2e guidance exists without forcing UI assumptions onto every project.
169
-
170
- ## Phase 5: Architecture Shell and Scaling Docs
3
+ ## Current State
171
4
 
172
- Purpose: leave room for serious product evolution without bloating the first scaffold cut.
5
+ `ossplate` is no longer a placeholder scaffold. It now ships as a real multi-registry tool with:
173
6
 
174
- ### P1
7
+ - a canonical Rust CLI
8
+ - thin npm and Python wrappers
9
+ - real `validate`, `sync`, `create`, and `init` commands
10
+ - curated scaffold payloads in installed wrapper artifacts
11
+ - CI quality gates across Rust, JS, and Python
12
+ - OIDC-first publishing for PyPI, Cargo, and npm, with token fallbacks where configured
175
13
 
176
- - Add architecture docs for a hexagonal baseline:
177
- domain, application, adapters, and delivery surfaces.
178
- - Define where product logic belongs and where it should not live.
179
- - Document wrappers as adapters rather than alternate implementations.
14
+ Published names are aligned as:
180
15
 
181
- ### P2
16
+ - crates.io: `ossplate`
17
+ - npm: `ossplate`
18
+ - PyPI: `ossplate`
19
+ - CLI: `ossplate`
182
20
 
183
- - Add an example directory shell that future projects can expand into as complexity grows.
184
- - Link to the stronger internal reference material that informed this structure.
185
- - Add ADR guidance if the template grows beyond a small starter.
21
+ ## Completed
186
22
 
187
- ### Exit Criteria
23
+ ### Foundation
188
24
 
189
- - The template can scale into a maintainable project layout without major restructuring.
190
- - The initial starter remains small and understandable.
25
+ - Placeholder identity validation is wired into CI.
26
+ - `ossplate.toml` is the canonical identity source.
27
+ - Root docs, testing docs, customization docs, and release docs now exist.
191
28
 
192
- ## Recommended Execution Order
29
+ ### Product Surface
193
30
 
194
- 1. Phase 0
195
- 2. Phase 1
196
- 3. Phase 2
197
- 4. Phase 3
198
- 5. Phase 4
199
- 6. Phase 5
31
+ - Rust is the only source of product logic.
32
+ - JS and Python are thin wrappers around the packaged binary.
33
+ - The command surface is now real:
34
+ `version`, `validate`, `sync`, `create`, `init`.
200
35
 
201
- Rationale:
36
+ ### Packaging And Artifact Reality
202
37
 
203
- - Phase 0 prevents accidental misuse while the template is still changing.
204
- - Phase 1 fixes the most important structural flaw: duplicated CLI stubs.
205
- - Phase 2 makes the scaffold trustworthy.
206
- - Phase 3 hardens release automation after the package surfaces are stable.
207
- - Phase 4 and Phase 5 improve scale and adoption quality without blocking the core scaffold.
38
+ - Installed npm and Python artifacts carry the scaffold payload required by `create` and `init`.
39
+ - Scaffold payload staging is curated by manifest rather than broad repo-copy behavior.
40
+ - Artifact tests verify required content and exclude known non-shipping content.
208
41
 
209
- ## Suggested Near-Term Milestones
42
+ ### Quality Gates
210
43
 
211
- ### Milestone A
44
+ - CI enforces Rust format, clippy, and tests.
45
+ - CI enforces JS build, tests, and package dry-run.
46
+ - CI enforces Python tests and wheel build.
47
+ - Wrapper parity and installed-artifact smoke paths are exercised.
48
+ - `./scripts/verify.sh` mirrors the local gate.
212
49
 
213
- Complete Phases 0 and 1.
50
+ ### Publishing
214
51
 
215
- Outcome:
216
- `ossplate` becomes a real scaffold with one canonical core and thin wrappers.
52
+ - Publish flows are rerun-safe.
53
+ - PyPI uses OIDC.
54
+ - Cargo uses OIDC with `CARGO_TOKEN` fallback.
55
+ - npm uses OIDC with `NPM_TOKEN` fallback.
56
+ - The release operator flow is documented in [`docs/releases.md`](./releases.md).
217
57
 
218
- ### Milestone B
58
+ ## Remaining Priorities
219
59
 
220
- Complete Phase 2 and the P0 items in Phase 3.
60
+ ## P1
221
61
 
222
- Outcome:
223
- the scaffold becomes release-grade with meaningful quality gates and robust publish behavior.
62
+ - Expand `sync` ownership carefully into a few more identity-only surfaces where bounded ownership is safe.
63
+ - Improve `validate` and `sync --check` output further if field-level drift becomes hard to read at scale.
64
+ - Add a short architecture note describing the current boundary:
65
+ Rust core as product logic, JS/Python as adapters, scaffold payload as distribution asset.
224
66
 
225
- ### Milestone C
67
+ ## P2
226
68
 
227
- Complete Phases 4 and 5.
69
+ - Add optional guidance for generated projects that need browser-based live end-to-end tests.
70
+ - Add examples of failure triage for packaging, publish, and parity regressions.
71
+ - Tighten workflow/docs ownership boundaries further if more fields become sync-managed.
228
72
 
229
- Outcome:
230
- the scaffold becomes easier to adopt, extend, and scale across future projects.
73
+ ## P3
231
74
 
232
- ## Current State
75
+ - Add architecture-shell guidance for teams that want to scale generated projects into a fuller hexagonal layout.
76
+ - Add ADR guidance if the scaffold evolves into a broader product platform.
233
77
 
234
- Completed:
78
+ ## Release Maintenance
235
79
 
236
- - release-readiness checks are wired into CI
237
- - JS and Python are thin wrappers over the Rust core
238
- - the Rust core now provides `version`, `validate`, `sync`, `create`, and `init`
239
- - `ossplate.toml` is the canonical identity source for owned metadata surfaces
240
- - installed npm and Python artifacts now carry the staged scaffold payload for `create` and `init`
241
- - artifact smoke tests prove installed distributions can run `version`, `create`, and `validate`
242
- - scaffold payload staging is now driven by an explicit curated manifest instead of a broad repo snapshot
243
- - Python packaging stages distribution assets through its build hook
244
- - CI now enforces Rust formatting and clippy alongside the existing test/package checks
80
+ - Refresh GitHub Actions dependencies ahead of the Node 20 runner deprecation.
81
+ - Keep trusted publishing and token fallback documentation aligned with real registry configuration.
82
+ - Keep release-version bumps aligned across Cargo, npm, and PyPI metadata.
245
83
 
246
- ## Next Steps
84
+ ## Suggested Next Work
247
85
 
248
- - expand `sync` ownership carefully beyond the root README identity block and into selected workflow identity fields where safe
249
- - improve `validate` and `sync --check` output further if diff-style rendering becomes necessary
250
- - keep the root verification workflow aligned with CI as new checks are added
251
- - harden publish workflows with the OIDC-first fallback strategy from Phase 3
86
+ 1. Decide whether `sync` should own any additional identity-only fields beyond the current bounded set.
87
+ 2. Improve `validate` and `sync --check` if larger repos need richer drift output.
88
+ 3. Add optional browser-e2e guidance for generated projects that include a UI.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "Ossplate"
3
- description = "A practical baseline for shipping one project across Cargo, npm, and PyPI without starting from scratch every time."
3
+ description = "Build one project, ship it everywhere."
4
4
  repository = "https://github.com/stefdevscore/ossplate"
5
5
  license = "Unlicense"
6
6
 
@@ -1,7 +1,26 @@
1
- # JavaScript Wrapper For Ossplate
1
+ # Ossplate
2
2
 
3
- This package is the JavaScript wrapper surface for Ossplate.
3
+ `ossplate` helps you start and maintain a project that ships the same CLI through Rust, npm, and PyPI.
4
4
 
5
- It delegates to the canonical Rust binary instead of implementing its own CLI behavior.
5
+ Use it to:
6
6
 
7
- Use `OSSPLATE_BINARY` during local development to point the wrapper at a specific binary.
7
+ - create a new scaffolded project
8
+ - initialize an existing directory
9
+ - validate project identity and metadata
10
+ - keep owned files in sync
11
+
12
+ Common commands:
13
+
14
+ ```bash
15
+ ossplate version
16
+ ossplate create <target>
17
+ ossplate init --path <dir>
18
+ ossplate validate
19
+ ossplate sync --check
20
+ ```
21
+
22
+ Learn more:
23
+
24
+ - [Main documentation](../docs/README.md)
25
+ - [Testing guide](../docs/testing.md)
26
+ - [Architecture](../docs/architecture.md)
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "ossplate",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "ossplate",
9
- "version": "0.1.0",
9
+ "version": "0.1.1",
10
10
  "license": "Unlicense",
11
11
  "bin": {
12
12
  "ossplate": "bin/ossplate.js"
@@ -3,7 +3,7 @@
3
3
  "bin": {
4
4
  "ossplate": "bin/ossplate.js"
5
5
  },
6
- "description": "A practical baseline for shipping one project across Cargo, npm, and PyPI without starting from scratch every time.",
6
+ "description": "Build one project, ship it everywhere.",
7
7
  "devDependencies": {
8
8
  "@types/node": "^24.6.0",
9
9
  "typescript": "^5.9.3"
@@ -39,5 +39,5 @@
39
39
  "test": "npm run build && node --test test/cli.test.js"
40
40
  },
41
41
  "type": "module",
42
- "version": "0.1.0"
42
+ "version": "0.1.1"
43
43
  }
@@ -1,7 +1,26 @@
1
- # Python Wrapper For Ossplate
1
+ # Ossplate
2
2
 
3
- This package is the Python wrapper surface for Ossplate.
3
+ `ossplate` helps you start and maintain a project that ships the same CLI through Rust, npm, and PyPI.
4
4
 
5
- It delegates to the canonical Rust binary instead of implementing its own CLI behavior.
5
+ Use it to:
6
6
 
7
- Use `OSSPLATE_BINARY` during local development to point the wrapper at a specific binary.
7
+ - create a new scaffolded project
8
+ - initialize an existing directory
9
+ - validate project identity and metadata
10
+ - keep owned files in sync
11
+
12
+ Common commands:
13
+
14
+ ```bash
15
+ ossplate version
16
+ ossplate create <target>
17
+ ossplate init --path <dir>
18
+ ossplate validate
19
+ ossplate sync --check
20
+ ```
21
+
22
+ Learn more:
23
+
24
+ - [Main documentation](../docs/README.md)
25
+ - [Testing guide](../docs/testing.md)
26
+ - [Architecture](../docs/architecture.md)
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ossplate"
7
- version = "0.1.0"
8
- description = "A practical baseline for shipping one project across Cargo, npm, and PyPI without starting from scratch every time."
7
+ version = "0.1.1"
8
+ description = "Build one project, ship it everywhere."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  license = { text = "Unlicense" }
package/dist/index.d.ts DELETED
@@ -1,10 +0,0 @@
1
- export declare function resolveOssplateBinary(options?: {
2
- baseDir?: string;
3
- platform?: NodeJS.Platform;
4
- arch?: string;
5
- }): string;
6
- export declare function runOssplate(args?: string[], options?: {
7
- baseDir?: string;
8
- platform?: NodeJS.Platform;
9
- arch?: string;
10
- }): void;
package/dist/index.js DELETED
@@ -1,56 +0,0 @@
1
- import { accessSync, constants } from "node:fs";
2
- import { spawn } from "node:child_process";
3
- import { fileURLToPath } from "node:url";
4
- import { dirname, join } from "node:path";
5
- import { arch as runtimeArch, platform as runtimePlatform } from "node:os";
6
- const __dirname = dirname(fileURLToPath(import.meta.url));
7
- const ENV_OVERRIDE = "OSSPLATE_BINARY";
8
- const TEMPLATE_ROOT_ENV = "OSSPLATE_TEMPLATE_ROOT";
9
- const TARGETS = {
10
- darwin: { arm64: "darwin-arm64", x64: "darwin-x64" },
11
- linux: { x64: "linux-x64" },
12
- win32: { x64: "win32-x64" }
13
- };
14
- export function resolveOssplateBinary(options = {}) {
15
- const envOverride = process.env[ENV_OVERRIDE];
16
- if (envOverride) {
17
- return envOverride;
18
- }
19
- const platform = options.platform ?? runtimePlatform();
20
- const arch = options.arch ?? runtimeArch();
21
- const target = TARGETS[platform]?.[arch];
22
- if (!target) {
23
- throw new Error(`Unsupported platform/arch: ${platform}/${arch}`);
24
- }
25
- const executable = platform === "win32" ? "ossplate.exe" : "ossplate";
26
- const baseDir = options.baseDir ?? join(__dirname, "..");
27
- const packagedPath = join(baseDir, "bin", target, executable);
28
- assertExecutable(packagedPath);
29
- return packagedPath;
30
- }
31
- export function runOssplate(args = [], options = {}) {
32
- const binPath = resolveOssplateBinary(options);
33
- const baseDir = options.baseDir ?? join(__dirname, "..");
34
- const child = spawn(binPath, args, {
35
- stdio: "inherit",
36
- env: {
37
- ...process.env,
38
- [TEMPLATE_ROOT_ENV]: process.env[TEMPLATE_ROOT_ENV] ?? join(baseDir, "scaffold")
39
- }
40
- });
41
- child.on("exit", (code) => {
42
- process.exit(code ?? 0);
43
- });
44
- child.on("error", (error) => {
45
- console.error(`ossplate: ${error.message}`);
46
- process.exit(1);
47
- });
48
- }
49
- function assertExecutable(filePath) {
50
- try {
51
- accessSync(filePath, constants.X_OK);
52
- }
53
- catch {
54
- throw new Error(`Bundled ossplate binary not found or not executable at ${filePath}`);
55
- }
56
- }