ossplate 0.1.0 → 0.1.2

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,28 @@
1
- # JavaScript Wrapper For Ossplate
1
+ # Ossplate
2
2
 
3
- This package is the JavaScript wrapper surface for Ossplate.
3
+ ![Ossplate armor](https://raw.githubusercontent.com/stefdevscore/ossplate/dev/assets/illustrations/armour05platemail.png)
4
4
 
5
- It delegates to the canonical Rust binary instead of implementing its own CLI behavior.
5
+ `ossplate` helps you start and maintain a project that ships the same CLI through Rust, npm, and PyPI.
6
6
 
7
- Use `OSSPLATE_BINARY` during local development to point the wrapper at a specific binary.
7
+ Use it to:
8
+
9
+ - create a new scaffolded project
10
+ - initialize an existing directory
11
+ - validate project identity and metadata
12
+ - keep owned files in sync
13
+
14
+ Common commands:
15
+
16
+ ```bash
17
+ ossplate version
18
+ ossplate create <target>
19
+ ossplate init --path <dir>
20
+ ossplate validate
21
+ ossplate sync --check
22
+ ```
23
+
24
+ Learn more:
25
+
26
+ - [Main documentation](../docs/README.md)
27
+ - [Testing guide](../docs/testing.md)
28
+ - [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.2"
43
43
  }
@@ -38,8 +38,32 @@ 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
+ logfile=$(mktemp)
48
+ cleanup() {
49
+ rm -f "$logfile"
50
+ }
51
+ trap cleanup EXIT
52
+ set +e
53
+ npm publish --access public --provenance 2>&1 | tee "$logfile"
54
+ status=${PIPESTATUS[0]}
55
+ set -e
56
+ if [ "$status" -eq 0 ]; then
57
+ echo "::notice title=npm::published with OIDC trusted publishing."
58
+ exit 0
59
+ fi
60
+ if [ -z "${NODE_AUTH_TOKEN:-}" ]; then
61
+ echo "::error title=npm::OIDC publish failed and NPM_TOKEN is not configured."
62
+ exit "$status"
63
+ fi
64
+ if grep -qiE "already exists|cannot publish over|previously published" "$logfile"; then
65
+ echo "::notice title=npm::package version already exists; skipping."
66
+ exit 0
67
+ fi
68
+ echo "::warning title=npm::OIDC publish failed; retrying with NPM_TOKEN fallback."
69
+ npm publish --access public
@@ -74,23 +74,28 @@ jobs:
74
74
  env:
75
75
  CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token || secrets.CARGO_TOKEN }}
76
76
  run: |
77
+ logfile=$(mktemp)
78
+ cleanup() {
79
+ rm -f "$logfile"
80
+ }
81
+ trap cleanup EXIT
77
82
  if [ -n "${{ steps.auth.outputs.token }}" ]; then
78
83
  echo "::notice title=cargo::using crates.io OIDC token from trusted publishing."
79
84
  else
80
85
  echo "::notice title=cargo::OIDC token unavailable; falling back to CARGO_TOKEN secret."
81
86
  fi
82
87
  set +e
83
- cargo publish 2>&1 | tee cargo-publish.log
88
+ cargo publish 2>&1 | tee "$logfile"
84
89
  status=${PIPESTATUS[0]}
85
90
  set -e
86
91
  if [ "$status" -eq 0 ]; then
87
92
  exit 0
88
93
  fi
89
- if grep -q "status 429 Too Many Requests" cargo-publish.log; then
94
+ if grep -q "status 429 Too Many Requests" "$logfile"; then
90
95
  echo "::warning title=cargo::crates.io rate limit hit. Treating this publish attempt as non-blocking."
91
96
  exit 0
92
97
  fi
93
- if grep -qi "already exists on crates.io index" cargo-publish.log; then
98
+ if grep -qi "already exists on crates.io index" "$logfile"; then
94
99
  echo "::notice title=cargo::crate version already exists; skipping."
95
100
  exit 0
96
101
  fi
@@ -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 armor](https://raw.githubusercontent.com/stefdevscore/ossplate/dev/assets/illustrations/armour05platemail.png)
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
 
@@ -158,7 +158,7 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
158
158
 
159
159
  [[package]]
160
160
  name = "ossplate"
161
- version = "0.1.0"
161
+ version = "0.1.2"
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.2"
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,45 @@ 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
+ let image_url = github_raw_url(
1045
+ &config.project.repository,
1046
+ "dev",
1047
+ "assets/illustrations/armour05platemail.png",
1048
+ );
1044
1049
  format!(
1045
- r#"# {language} Wrapper For {name}
1050
+ r#"# {name}
1051
+
1052
+ ![{name} armor]({image_url})
1053
+
1054
+ `{command}` helps you start and maintain a project that ships the same CLI through Rust, npm, and PyPI.
1055
+
1056
+ Use it to:
1057
+
1058
+ - create a new scaffolded project
1059
+ - initialize an existing directory
1060
+ - validate project identity and metadata
1061
+ - keep owned files in sync
1062
+
1063
+ Common commands:
1046
1064
 
1047
- This package is the {language} wrapper surface for {name}.
1065
+ ```bash
1066
+ {command} version
1067
+ {command} create <target>
1068
+ {command} init --path <dir>
1069
+ {command} validate
1070
+ {command} sync --check
1071
+ ```
1048
1072
 
1049
- It delegates to the canonical Rust binary instead of implementing its own CLI behavior.
1073
+ Learn more:
1050
1074
 
1051
- Use `OSSPLATE_BINARY` during local development to point the wrapper at a specific binary.
1075
+ - [Main documentation](../docs/README.md)
1076
+ - [Testing guide](../docs/testing.md)
1077
+ - [Architecture](../docs/architecture.md)
1052
1078
  "#,
1053
- language = language,
1054
1079
  name = config.project.name,
1080
+ command = config.packages.command,
1081
+ image_url = image_url,
1055
1082
  )
1056
1083
  }
1057
1084
 
@@ -1062,6 +1089,15 @@ fn render_root_readme_identity(config: &ToolConfig) -> String {
1062
1089
  )
1063
1090
  }
1064
1091
 
1092
+ fn github_raw_url(repository: &str, branch: &str, path: &str) -> String {
1093
+ let trimmed = repository.trim_end_matches('/');
1094
+ if let Some(rest) = trimmed.strip_prefix("https://github.com/") {
1095
+ format!("https://raw.githubusercontent.com/{rest}/{branch}/{path}")
1096
+ } else {
1097
+ format!("{trimmed}/{path}")
1098
+ }
1099
+ }
1100
+
1065
1101
  fn validate_workflow_name(
1066
1102
  path: &str,
1067
1103
  expected_name: &str,
@@ -1266,7 +1302,7 @@ mod tests {
1266
1302
  let root = make_fixture_root();
1267
1303
  fs::write(
1268
1304
  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",
1305
+ "{\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
1306
  )
1271
1307
  .unwrap();
1272
1308
  let output = validate_repo(&root).unwrap();
@@ -1386,7 +1422,7 @@ mod tests {
1386
1422
  target.join("core-rs/Cargo.toml"),
1387
1423
  r#"[package]
1388
1424
  name = "bad-core"
1389
- version = "0.1.0"
1425
+ version = "0.1.2"
1390
1426
  "#,
1391
1427
  )
1392
1428
  .unwrap();
@@ -1440,7 +1476,7 @@ version = "0.1.0"
1440
1476
  fs::write(
1441
1477
  root.join("README.md"),
1442
1478
  original.replace(
1443
- "A practical baseline for shipping one project",
1479
+ "Build one project, ship it everywhere",
1444
1480
  "Changed identity text",
1445
1481
  ),
1446
1482
  )
@@ -1449,7 +1485,7 @@ version = "0.1.0"
1449
1485
  sync_repo(&root, false).unwrap();
1450
1486
  let synced = fs::read_to_string(root.join("README.md")).unwrap();
1451
1487
  assert!(synced.contains("## What This Tool Gives You"));
1452
- assert!(synced.contains("A practical baseline for shipping one project"));
1488
+ assert!(synced.contains("Build one project, ship it everywhere"));
1453
1489
  }
1454
1490
 
1455
1491
  #[test]
@@ -1499,7 +1535,7 @@ version = "0.1.0"
1499
1535
  let config = r#"[project]
1500
1536
  name = "Ossplate"
1501
1537
  slug = "ossplate"
1502
- description = "A practical baseline for shipping one project across Cargo, npm, and PyPI without starting from scratch every time."
1538
+ description = "Build one project, ship it everywhere."
1503
1539
  repository = "https://github.com/stefdevscore/ossplate"
1504
1540
  license = "Unlicense"
1505
1541
 
@@ -1522,7 +1558,7 @@ command = "ossplate"
1522
1558
  root.join("core-rs/Cargo.toml"),
1523
1559
  r#"[package]
1524
1560
  name = "ossplate"
1525
- version = "0.1.0"
1561
+ version = "0.1.2"
1526
1562
  edition = "2021"
1527
1563
  authors = ["Stef <stefdevscore@github.com>"]
1528
1564
  description = "A practical baseline for shipping one project across Cargo, npm, and PyPI without starting from scratch every time."
@@ -1535,14 +1571,14 @@ homepage = "https://github.com/stefdevscore/ossplate"
1535
1571
  .unwrap();
1536
1572
  fs::write(
1537
1573
  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",
1574
+ "{\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
1575
  )
1540
1576
  .unwrap();
1541
1577
  fs::write(
1542
1578
  root.join("wrapper-py/pyproject.toml"),
1543
1579
  r#"[project]
1544
1580
  name = "ossplate"
1545
- description = "A practical baseline for shipping one project across Cargo, npm, and PyPI without starting from scratch every time."
1581
+ description = "Build one project, ship it everywhere."
1546
1582
  license = { text = "Unlicense" }
1547
1583
  authors = [
1548
1584
  { 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,28 @@
1
- # JavaScript Wrapper For Ossplate
1
+ # Ossplate
2
2
 
3
- This package is the JavaScript wrapper surface for Ossplate.
3
+ ![Ossplate armor](https://raw.githubusercontent.com/stefdevscore/ossplate/dev/assets/illustrations/armour05platemail.png)
4
4
 
5
- It delegates to the canonical Rust binary instead of implementing its own CLI behavior.
5
+ `ossplate` helps you start and maintain a project that ships the same CLI through Rust, npm, and PyPI.
6
6
 
7
- Use `OSSPLATE_BINARY` during local development to point the wrapper at a specific binary.
7
+ Use it to:
8
+
9
+ - create a new scaffolded project
10
+ - initialize an existing directory
11
+ - validate project identity and metadata
12
+ - keep owned files in sync
13
+
14
+ Common commands:
15
+
16
+ ```bash
17
+ ossplate version
18
+ ossplate create <target>
19
+ ossplate init --path <dir>
20
+ ossplate validate
21
+ ossplate sync --check
22
+ ```
23
+
24
+ Learn more:
25
+
26
+ - [Main documentation](../docs/README.md)
27
+ - [Testing guide](../docs/testing.md)
28
+ - [Architecture](../docs/architecture.md)
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "ossplate",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
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.2",
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.2"
43
43
  }
@@ -1,7 +1,28 @@
1
- # Python Wrapper For Ossplate
1
+ # Ossplate
2
2
 
3
- This package is the Python wrapper surface for Ossplate.
3
+ ![Ossplate armor](https://raw.githubusercontent.com/stefdevscore/ossplate/dev/assets/illustrations/armour05platemail.png)
4
4
 
5
- It delegates to the canonical Rust binary instead of implementing its own CLI behavior.
5
+ `ossplate` helps you start and maintain a project that ships the same CLI through Rust, npm, and PyPI.
6
6
 
7
- Use `OSSPLATE_BINARY` during local development to point the wrapper at a specific binary.
7
+ Use it to:
8
+
9
+ - create a new scaffolded project
10
+ - initialize an existing directory
11
+ - validate project identity and metadata
12
+ - keep owned files in sync
13
+
14
+ Common commands:
15
+
16
+ ```bash
17
+ ossplate version
18
+ ossplate create <target>
19
+ ossplate init --path <dir>
20
+ ossplate validate
21
+ ossplate sync --check
22
+ ```
23
+
24
+ Learn more:
25
+
26
+ - [Main documentation](../docs/README.md)
27
+ - [Testing guide](../docs/testing.md)
28
+ - [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.2"
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
- }