onde-cli 0.3.0__tar.gz → 0.4.0__tar.gz

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.
Files changed (67) hide show
  1. onde_cli-0.4.0/.agents/AGENTS.md +69 -0
  2. onde_cli-0.4.0/.agents/skills/distribution/SKILL.md +234 -0
  3. onde_cli-0.4.0/.agents/skills/git/SKILL.md +70 -0
  4. {onde_cli-0.3.0 → onde_cli-0.4.0}/.github/workflows/ci.yml +42 -5
  5. {onde_cli-0.3.0 → onde_cli-0.4.0}/.github/workflows/release-github.yml +16 -1
  6. {onde_cli-0.3.0 → onde_cli-0.4.0}/.github/workflows/release-homebrew.yml +1 -1
  7. {onde_cli-0.3.0 → onde_cli-0.4.0}/.github/workflows/release-npm.yml +2 -6
  8. onde_cli-0.4.0/.github/workflows/release-nuget.yml +305 -0
  9. onde_cli-0.4.0/.github/workflows/release-pub.yml +129 -0
  10. {onde_cli-0.3.0 → onde_cli-0.4.0}/.github/workflows/release-pypi.yml +5 -5
  11. onde_cli-0.4.0/.gitignore +17 -0
  12. onde_cli-0.4.0/.nuget/NuGet/NuGet.Config +6 -0
  13. onde_cli-0.4.0/AGENTS.md +69 -0
  14. onde_cli-0.4.0/CLAUDE.md +69 -0
  15. {onde_cli-0.3.0 → onde_cli-0.4.0}/Cargo.lock +522 -537
  16. {onde_cli-0.3.0 → onde_cli-0.4.0}/Cargo.toml +9 -7
  17. {onde_cli-0.3.0 → onde_cli-0.4.0}/LICENSE +1 -1
  18. {onde_cli-0.3.0 → onde_cli-0.4.0}/PKG-INFO +12 -6
  19. {onde_cli-0.3.0 → onde_cli-0.4.0}/README.md +37 -7
  20. {onde_cli-0.3.0 → onde_cli-0.4.0}/build.rs +5 -6
  21. {onde_cli-0.3.0 → onde_cli-0.4.0}/npm/onde-cli/README.md +9 -3
  22. {onde_cli-0.3.0 → onde_cli-0.4.0}/npm/onde-cli/package-lock.json +8 -8
  23. {onde_cli-0.3.0 → onde_cli-0.4.0}/npm/onde-cli/src/index.ts +4 -4
  24. {onde_cli-0.3.0 → onde_cli-0.4.0}/npm/scripts/render-main-package.cjs +2 -2
  25. {onde_cli-0.3.0 → onde_cli-0.4.0}/npm/scripts/render-platform-package.cjs +4 -4
  26. onde_cli-0.4.0/nuget/.gitignore +5 -0
  27. onde_cli-0.4.0/nuget/onde-cli/Onde.Cli.csproj +26 -0
  28. onde_cli-0.4.0/nuget/onde-cli/Program.cs +109 -0
  29. onde_cli-0.4.0/nuget/onde-cli/README.md +57 -0
  30. onde_cli-0.4.0/pub/onde_cli/.gitignore +3 -0
  31. onde_cli-0.4.0/pub/onde_cli/.pubignore +3 -0
  32. onde_cli-0.4.0/pub/onde_cli/CHANGELOG.md +11 -0
  33. onde_cli-0.4.0/pub/onde_cli/LICENSE +201 -0
  34. onde_cli-0.4.0/pub/onde_cli/README.md +58 -0
  35. onde_cli-0.4.0/pub/onde_cli/bin/onde.dart +13 -0
  36. onde_cli-0.4.0/pub/onde_cli/lib/onde_cli.dart +216 -0
  37. onde_cli-0.4.0/pub/onde_cli/pubspec.yaml +20 -0
  38. {onde_cli-0.3.0 → onde_cli-0.4.0}/pypi/README.md +10 -4
  39. {onde_cli-0.3.0 → onde_cli-0.4.0}/src/app.rs +392 -41
  40. {onde_cli-0.3.0 → onde_cli-0.4.0}/src/chat.rs +7 -1
  41. {onde_cli-0.3.0 → onde_cli-0.4.0}/src/finetune.rs +617 -35
  42. {onde_cli-0.3.0 → onde_cli-0.4.0}/src/gguf.rs +493 -42
  43. {onde_cli-0.3.0 → onde_cli-0.4.0}/src/gresiq.rs +35 -1
  44. {onde_cli-0.3.0 → onde_cli-0.4.0}/src/hf_search.rs +3 -3
  45. {onde_cli-0.3.0 → onde_cli-0.4.0}/src/main.rs +8 -0
  46. onde_cli-0.4.0/src/mcp.rs +336 -0
  47. {onde_cli-0.3.0 → onde_cli-0.4.0}/src/ui.rs +105 -21
  48. onde_cli-0.3.0/.agents/skills/git/SKILL.md +0 -43
  49. onde_cli-0.3.0/.gitignore +0 -3
  50. {onde_cli-0.3.0 → onde_cli-0.4.0}/.github/workflows/release-crates.yml +0 -0
  51. {onde_cli-0.3.0 → onde_cli-0.4.0}/.nvmrc +0 -0
  52. {onde_cli-0.3.0 → onde_cli-0.4.0}/assets/ondeinference-apps.png +0 -0
  53. {onde_cli-0.3.0 → onde_cli-0.4.0}/npm/README.md.tmpl +0 -0
  54. {onde_cli-0.3.0 → onde_cli-0.4.0}/npm/onde-cli/.gitignore +0 -0
  55. {onde_cli-0.3.0 → onde_cli-0.4.0}/npm/onde-cli/package.json +0 -0
  56. {onde_cli-0.3.0 → onde_cli-0.4.0}/npm/onde-cli/tsconfig.json +0 -0
  57. {onde_cli-0.3.0 → onde_cli-0.4.0}/npm/package-main.json.tmpl +0 -0
  58. {onde_cli-0.3.0 → onde_cli-0.4.0}/npm/package.json.tmpl +0 -0
  59. {onde_cli-0.3.0 → onde_cli-0.4.0}/pypi/pyproject.toml +0 -0
  60. {onde_cli-0.3.0 → onde_cli-0.4.0}/pyproject.toml +0 -0
  61. {onde_cli-0.3.0 → onde_cli-0.4.0}/rust-toolchain.toml +0 -0
  62. {onde_cli-0.3.0 → onde_cli-0.4.0}/src/hf.rs +0 -0
  63. {onde_cli-0.3.0 → onde_cli-0.4.0}/src/hf_clone.rs +0 -0
  64. {onde_cli-0.3.0 → onde_cli-0.4.0}/src/hf_upload.rs +0 -0
  65. {onde_cli-0.3.0 → onde_cli-0.4.0}/src/merge.rs +0 -0
  66. {onde_cli-0.3.0 → onde_cli-0.4.0}/src/project.rs +0 -0
  67. {onde_cli-0.3.0 → onde_cli-0.4.0}/src/token.rs +0 -0
@@ -0,0 +1,69 @@
1
+ # AGENTS.md
2
+
3
+ `onde-cli` is a Rust TUI (`onde` binary) for managing an Onde Inference account and running a local model pipeline: LoRA fine-tune → adapter merge → GGUF export → local chat test → Hugging Face upload → model assignment to an Onde app. npm, PyPI, NuGet, pub.dev, Homebrew, and crates.io are thin wrappers around this one binary; the Rust crate is the source of truth.
4
+
5
+ ## Build, test, lint
6
+
7
+ ```sh
8
+ cargo build # debug build
9
+ cargo run # launch the TUI
10
+ cargo run -- --mcp # run as an MCP server over stdio (no TUI)
11
+ cargo test --locked # unit tests (heavy ones are #[ignore])
12
+ cargo clippy --all-targets -- -D warnings
13
+ cargo fmt -- --check
14
+ ```
15
+
16
+ The build **requires** these env vars (read from `.env` via `build.rs`): `ONDE_APP_ID`, `ONDE_APP_SECRET`, `GRESIQ_API_KEY`, `GRESIQ_API_SECRET`. `HF_TOKEN` is optional. They are baked in at compile time via `env!(...)` in `src/app.rs`. Changing `.env` triggers a full rebuild.
17
+
18
+ On macOS, candle's `metal` + `accelerate` features are added automatically. **Fine-tuning always runs on CPU** — Metal's backward pass in candle 0.10.2 produces corrupt gradients (1e6–1e29 in magnitude, where CPU gives ~40–65 for identical losses). Use `ONDE_FINETUNE_METAL=1` only to test newer candle versions, and verify with `ONDE_FINETUNE_GRAD_DEBUG=1` (per-step loss/max_abs/norm).
19
+
20
+ Linux CI requires `libclang-dev` (`sudo apt-get install -y libclang-dev`).
21
+
22
+ ### Tests that load real models
23
+
24
+ `src/gguf.rs` has two `#[ignore]` integration tests that export a ~1GB GGUF and run inference through `onde::mistralrs`. They need a Qwen3-0.6B model cached in the Onde App Group container and skip gracefully when it is absent.
25
+
26
+ ```sh
27
+ cargo test gguf::tests::exported_qwen3_gguf_is_runnable -- --ignored --nocapture
28
+ cargo test gguf::tests::finetune_merge_export_run -- --ignored --nocapture
29
+ ```
30
+
31
+ Env knobs for these: `ONDE_TEST_MODEL_DIR`, `ONDE_TEST_DTYPE` (`f16`/`q8_0`), `ONDE_TEST_LR`, `ONDE_TEST_DATA_PATH`, `ONDE_TEST_EPOCHS`, `ONDE_TEST_MAX_SEQ_LEN`, `ONDE_TEST_EVAL_PROMPT` (`||`-separated prompts), `ONDE_TEST_SYSTEM_PROMPT`. This makes the full-pipeline test double as a headless fine-tune runner for a custom dataset.
32
+
33
+ ### Debugging the TUI
34
+
35
+ `main.rs` redirects both stdout and stderr to `~/.cache/onde/debug.log` before ratatui takes the alternate screen, because `mistral.rs` writes to both fds and would otherwise tear up the display. To see what the app or the inference engine is doing, tail that log; `println!`/`eprintln!`/`log::*` all land there, not on screen.
36
+
37
+ ## Architecture — `src/`
38
+
39
+ | File(s) | Responsibility |
40
+ |---|---|
41
+ | `main.rs` | Entry point; redirects stdout/stderr to `~/.cache/onde/debug.log` before ratatui takes over |
42
+ | `app.rs` | `App` struct + `Screen` state machine + `AuthEvent` channel (`tokio::select!` over an `mpsc` channel and a `crossterm` `EventStream`); `App::apply` folds background progress into state |
43
+ | `ui.rs` | Pure ratatui render of `App`; no SDK imports (re-exports types through `app.rs`) |
44
+ | `finetune.rs` | Hand-written LoRA trainer (candle, CPU): Qwen forward pass (RMSNorm, RoPE, GQA, optional Qwen3 QK-norm), LoRA A/B trained on q/v projections in F32, writes `lora_adapter.safetensors`. Gradients are sanitized (non-finite elements zeroed) and globally norm-clipped in a scaled space before each AdamW step; a step is skipped (loudly) only when the norm is zero or non-finite. **Skipping this guard corrupts every weight after the first bad step.** |
45
+ | `merge.rs` | Folds LoRA adapter into base weights (`W + scale·(B@A)`); writes `model.safetensors` plus copied config/tokenizer |
46
+ | `gguf.rs` | Hand-rolled GGUF writer (no llama.cpp). Tensor dims are written innermost-first — reverse of safetensors shape, because candle reverses on read; `head_dim` comes from config and is emitted as `attention.key_length`/`value_length` (Qwen3 decouples it from `hidden_size/num_heads`); `token_type` is an INT32 array; `general.architecture` is chosen from `model_type` so Qwen3 routes to candle's `quantized_qwen3` loader |
47
+ | `chat.rs` | Local GGUF chat via `onde::mistralrs::GgufModelBuilder`, the same engine the Onde SDK uses, so a model can be tested before publishing |
48
+ | `hf_upload.rs` / `hf_clone.rs` / `hf_search.rs` / `hf.rs` | Hugging Face Hub: upload the GGUF, check/create a repo, search models, and resolve/merge the local HF cache (incl. the macOS Onde App Group container) |
49
+ | `gresiq.rs` | `smbcloud-gresiq-sdk` wrapper for apps and the model catalog. "Deploying" a model means `assign_model(app_id, model_id)` against a catalog entry; the end app then fetches that assignment through the Onde SDK's `load_assigned_model` and downloads the GGUF. The CLI can only assign models that already exist in the GresIQ catalog |
50
+ | `token.rs` | Auth token persistence |
51
+ | `project.rs` | Per-project fine-tune workspaces under `~/.onde` |
52
+
53
+ **Background task rule:** network/IO → `tokio::spawn`; CPU-heavy tensor work (`finetune`, `merge`, `gguf`) → `std::thread::spawn` to avoid starving the async runtime.
54
+
55
+ **Adding a feature pattern:** new `Screen` variant → key handler → `AuthEvent` variant → background task that streams progress events.
56
+
57
+ ### The `onde` dependency
58
+
59
+ The `onde` crate provides the inference engine (`onde::mistralrs`, a vendored mistral.rs) and `onde::inference::models::SUPPORTED_MODEL_INFO` (the supported-model catalog the inference picker mirrors). It is normally the published crate; `Cargo.toml` has commented `path`/`[patch.crates-io]` blocks for developing against local checkouts of `onde`, the `smbcloud-*` crates, and `candle` — never commit with those uncommented.
60
+
61
+ ## Conventions
62
+
63
+ - **Git:** merge feature/release/hotfix branches with `--no-ff`. Tag the merge commit on `main`. Never tag a branch tip that hasn't been merged. See `.agents/skills/git/SKILL.md`.
64
+ - **Commits:** include `Co-Authored-By: siGit Code <297239231+sigitc@users.noreply.github.com>` trailer when committing with the agent.
65
+ - **`[patch.crates-io]` and `path =` deps:** commented out in `Cargo.toml` for local dev against `onde`/`smbcloud-*`/`candle`. Never commit with these uncommented.
66
+ - **Distribution:** before touching any wrapper package (npm, PyPI, NuGet, pub.dev, Homebrew), read `.agents/skills/distribution/SKILL.md`. Keep all channel versions aligned with the Rust crate version in `Cargo.toml`.
67
+ - **Wrapper packages** are in `npm/`, `pypi/`, `nuget/`, `pub/`. The npm base manifest is rewritten at release time by `npm/scripts/render-main-package.cjs`; do not hand-edit committed package.json versions as the source of truth for releases.
68
+ - **Clippy** runs with `-D warnings` in CI; all lints must pass.
69
+ - **Rust edition:** 2024; toolchain: stable.
@@ -0,0 +1,234 @@
1
+ ---
2
+ name: distribution
3
+ description: Use when adding, fixing, or documenting package distribution for Onde CLI. Covers wrapper-package design across npm, PyPI, NuGet, pub.dev, GitHub Releases, and Homebrew, plus version alignment, artifact staging, README surfaces, and smoke-test strategy.
4
+ ---
5
+
6
+ # Onde CLI distribution
7
+
8
+ This skill captures how `onde-cli` is distributed today and what to watch when adding new channels.
9
+
10
+ ## Distribution model
11
+
12
+ `onde-cli` is a native Rust CLI first.
13
+
14
+ Everything else is packaging.
15
+
16
+ That means:
17
+
18
+ - build the Rust binary once per target
19
+ - publish thin wrappers that install or launch that binary
20
+ - keep channel-specific logic small and predictable
21
+
22
+ This design keeps behavior consistent across ecosystems and avoids re-implementing CLI logic in each language.
23
+
24
+ ## Channel map
25
+
26
+ ### Native source of truth
27
+
28
+ - Rust crate: `onde-cli`
29
+ - binary name: `onde`
30
+ - root manifest: `Cargo.toml`
31
+
32
+ ### Wrapper channels
33
+
34
+ - npm: JavaScript launcher + optional platform packages
35
+ - PyPI: `maturin` binary wheels
36
+ - NuGet: .NET global tool launcher
37
+ - pub.dev: Dart launcher package
38
+
39
+ ### Native artifact channels
40
+
41
+ - crates.io
42
+ - GitHub Releases
43
+ - Homebrew
44
+
45
+ ## Wrapper design patterns
46
+
47
+ ### npm
48
+
49
+ Use a very small Node launcher that:
50
+
51
+ - detects OS and architecture
52
+ - resolves the installed platform package
53
+ - spawns the binary with inherited stdio
54
+
55
+ Good fit for users who already live in npm tooling.
56
+
57
+ Important release detail:
58
+
59
+ - the committed `npm/onde-cli/package.json` is a working file, not the publish-time source of truth
60
+ - the release workflow rewrites the base package manifest with `npm/scripts/render-main-package.cjs`
61
+ - platform package manifests are generated on the fly with `npm/scripts/render-platform-package.cjs`
62
+ - the actual npm version comes from the release tag via `RELEASE_VERSION`, not from manually editing the committed npm manifest before publish
63
+
64
+ ### PyPI
65
+
66
+ Use `maturin` with `bindings = "bin"` so the native executable is what gets installed.
67
+
68
+ Good fit for Python users who want the CLI but not a Python reimplementation.
69
+
70
+ ### NuGet
71
+
72
+ Use a `.NET global tool` wrapper.
73
+
74
+ Important details:
75
+
76
+ - keep the package as a launcher, not a managed rewrite
77
+ - place native binaries under the tool payload
78
+ - resolve the runtime identifier at startup
79
+ - set Unix execute bits before launch
80
+
81
+ Good fit for .NET developers and CI environments that already use `dotnet tool install`.
82
+
83
+ ### pub.dev
84
+
85
+ Use a Dart package with a command entrypoint and a library that launches the native binary.
86
+
87
+ Important details:
88
+
89
+ - executable command should stay `onde`
90
+ - keep the runtime-id mapping explicit
91
+ - download the correct binary from GitHub Releases on first run
92
+ - cache downloads under `~/.onde/cli`
93
+ - keep the wrapper thin, the Rust binary is still the product
94
+ - publish the pub.dev package only after the matching GitHub Release exists
95
+
96
+ Good fit for Dart and Flutter developers who want the same CLI through familiar tooling.
97
+
98
+ ## Runtime identifier mapping
99
+
100
+ Keep these names aligned anywhere wrappers or workflows refer to native assets:
101
+
102
+ - `darwin-x64`
103
+ - `darwin-arm64`
104
+ - `linux-x64`
105
+ - `linux-arm64`
106
+ - `windows-x64`
107
+ - `windows-arm64`
108
+
109
+ If a wrapper uses one naming scheme and a workflow uploads another, the installed package will pass packaging but fail at runtime.
110
+
111
+ ## Release workflow principles
112
+
113
+ ### Build first, package second
114
+
115
+ Always build native binaries in a target matrix first.
116
+
117
+ Then let the wrapper channel jobs package those artifacts.
118
+
119
+ This avoids rebuilding the product multiple times and makes failures easier to reason about.
120
+
121
+ ### Idempotency
122
+
123
+ Every publish workflow should check whether the target version already exists before publishing.
124
+
125
+ Examples:
126
+
127
+ - npm: `npm view`
128
+ - PyPI: package JSON endpoint
129
+ - NuGet: flat container index
130
+ - pub.dev: package API endpoint
131
+
132
+ ### Smoke tests should match the product shape
133
+
134
+ Because `onde` is TUI-first, avoid smoke tests that assume a traditional CLI help flow unless you know that code path is stable and non-interactive.
135
+
136
+ Safer checks:
137
+
138
+ - install succeeds
139
+ - bundled native binary exists where expected
140
+ - package manager validation passes
141
+
142
+ ## pub.dev-specific notes
143
+
144
+ pub.dev behaves differently from npm and NuGet in one important way:
145
+
146
+ - large bundled binaries quickly hit package size limits
147
+
148
+ That makes the safest design a thin Dart launcher that downloads the right native binary from GitHub Releases at runtime instead of shipping every platform binary inside the package.
149
+
150
+ Practical consequences:
151
+
152
+ - `pub/onde_cli/native/` should stay out of the published package
153
+ - do not set `publish_to: https://pub.dev` in `pubspec.yaml`; omit `publish_to` for normal pub.dev publishing
154
+ - use package-level ignore rules so local native staging does not leak into publish input
155
+ - keep release asset names aligned with the GitHub Release workflow, because the launcher depends on them
156
+ - treat `~/.onde/cli` as the local cache root for downloaded binaries
157
+ - do not let the pub.dev workflow race ahead of GitHub Releases; dispatch it after `release-github.yml` has created the assets
158
+
159
+ Run these during verification:
160
+
161
+ - `dart pub get --directory pub/onde_cli`
162
+ - `dart analyze pub/onde_cli`
163
+ - `dart pub publish --dry-run --directory pub/onde_cli`
164
+
165
+ ## NuGet-specific notes
166
+
167
+ NuGet packaging is sensitive to path layout.
168
+
169
+ The safest pattern is:
170
+
171
+ - copy native files into the publish output
172
+ - let the .NET tool pack process carry them into the final package
173
+
174
+ Avoid custom `PackagePath` patterns that can duplicate path segments or create nested native folders by accident.
175
+
176
+ If the tool installs but cannot find the native binary, inspect the `.nupkg` contents before debugging the launcher.
177
+
178
+ ## README surfaces
179
+
180
+ Every user-facing channel should document the same product with channel-specific installation instructions.
181
+
182
+ Main files:
183
+
184
+ - `README.md`
185
+ - `npm/onde-cli/README.md`
186
+ - `pypi/README.md`
187
+ - `nuget/onde-cli/README.md`
188
+ - `pub/onde_cli/README.md`
189
+
190
+ Keep these aligned on:
191
+
192
+ - product description
193
+ - supported platforms
194
+ - install method
195
+ - license and copyright
196
+ - relevant supporting links like the forward-pass explainer
197
+
198
+ Do not let one channel sound like a different product.
199
+
200
+ ## Version alignment
201
+
202
+ When cutting a release, update every user-facing distribution version that should move with the CLI.
203
+
204
+ At minimum, check:
205
+
206
+ - Rust crate version
207
+ - npm release templates and workflow assumptions
208
+ - npm lockfile version if committed
209
+ - PyPI release version source
210
+ - NuGet package version at pack time
211
+ - pub.dev package version in `pubspec.yaml`
212
+ - workflow example tags and docs that mention concrete versions
213
+
214
+ ## Common failure modes
215
+
216
+ - wrapper version does not match the Rust release version
217
+ - manually bumping the committed npm manifest and assuming that is what npm publishes
218
+ - staged native binaries land in the wrong directory before packaging
219
+ - package builds successfully but installs without the native binary
220
+ - pub.dev launcher points at the wrong GitHub Release asset name or runtime id
221
+ - pub.dev workflow publishes before the matching GitHub Release assets exist
222
+ - pub.dev package still includes bundled binaries and blows the upload size limit
223
+ - smoke test invokes a TUI-first binary in a way that does not make sense for CI
224
+ - README badges and install commands drift out of sync with real package names
225
+
226
+ ## Rule of thumb
227
+
228
+ If the native binary works and the wrappers are boring, distribution stays manageable.
229
+
230
+ If a wrapper starts acting like its own product, complexity grows fast.
231
+
232
+ Keep it thin.
233
+ Keep it aligned.
234
+ Keep the Rust binary as the source of truth.
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: git
3
+ description: Git workflow rules for this repository.
4
+ allowed-tools: Read, Write, Edit, Glob, Grep, AskUserQuestion
5
+ user-invocable: true
6
+ ---
7
+
8
+ # Git skill
9
+
10
+ ## Core rule
11
+
12
+ - Use `--no-ff`. Important.
13
+
14
+ ## What this means in practice
15
+
16
+ - Do not fast-forward merge feature branches, release branches, or hotfix branches.
17
+ - Prefer explicit merge commits so the branch history stays visible.
18
+ - Release prep goes through `development` on its way to `main`, with `--no-ff` at every hop.
19
+ - Tag the merge commit that contains the final release state.
20
+
21
+ ## Branch flow
22
+
23
+ Feature branches merge into `development`. Releases go:
24
+
25
+ ```
26
+ release/vX.Y.Z -> development -> main
27
+ ```
28
+
29
+ Never merge a release branch straight into `main` — `development` has to carry the
30
+ release state too, or the next feature branch starts from a base that is missing it.
31
+
32
+ ## Release guidance
33
+
34
+ ```bash
35
+ # 1. cut the release branch from development, commit the version bumps
36
+ git checkout development
37
+ git checkout -b release/vX.Y.Z
38
+ git commit -am "Prepare vX.Y.Z"
39
+
40
+ # 2. release branch -> development
41
+ git checkout development
42
+ git merge --no-ff release/vX.Y.Z -m "Merge release prep for vX.Y.Z"
43
+
44
+ # 3. development -> main
45
+ git checkout main
46
+ git merge --no-ff development -m "Merge development for vX.Y.Z"
47
+
48
+ # 4. tag the main merge commit, then push
49
+ git tag vX.Y.Z
50
+ git push origin development
51
+ git push origin main
52
+ git push origin vX.Y.Z
53
+ ```
54
+
55
+ The tag belongs on the step-3 merge commit on `main`, not on the release branch and not
56
+ on `development`. Pushing the tag is what triggers the wrapper publish workflows, which
57
+ read the version from the tag.
58
+
59
+ If the work is already on `main`, make a normal commit and tag that commit. Do not rewrite history just to avoid a merge commit.
60
+
61
+ ## Avoid
62
+
63
+ - `git merge --ff`
64
+ - `git pull --ff-only` as a default recommendation for this repo's merge policy
65
+ - Tagging a branch tip that has not been merged into `main` when the intended release source of truth is `main`
66
+ - Merging a release branch directly into `main`, skipping `development`
67
+
68
+ ## Why
69
+
70
+ The repo wants explicit history. Release commits should be easy to find, easy to audit, and clearly connected to the branch that introduced them.
@@ -108,6 +108,39 @@ jobs:
108
108
  GRESIQ_API_SECRET: ${{ secrets.GRESIQ_API_SECRET }}
109
109
  HF_TOKEN: ${{ secrets.HF_TOKEN }}
110
110
 
111
+ dotnet-tool:
112
+ name: .NET tool launcher build
113
+ runs-on: ubuntu-latest
114
+ steps:
115
+ - name: Checkout
116
+ uses: actions/checkout@v6
117
+
118
+ - name: Setup .NET SDK
119
+ uses: actions/setup-dotnet@v4
120
+ with:
121
+ dotnet-version: 8.0.x
122
+
123
+ - name: Build .NET tool launcher
124
+ run: dotnet build nuget/onde-cli/Onde.Cli.csproj --configuration Release
125
+
126
+ dart-package:
127
+ name: Dart pub package check
128
+ runs-on: ubuntu-latest
129
+ steps:
130
+ - name: Checkout
131
+ uses: actions/checkout@v6
132
+
133
+ - name: Setup Dart SDK
134
+ uses: dart-lang/setup-dart@v1
135
+
136
+ - name: Get package dependencies
137
+ working-directory: pub/onde_cli
138
+ run: dart pub get
139
+
140
+ - name: Analyze Dart package
141
+ working-directory: pub/onde_cli
142
+ run: dart analyze
143
+
111
144
  build:
112
145
  name: Build (aarch64-apple-darwin)
113
146
  runs-on: macos-26
@@ -140,13 +173,17 @@ jobs:
140
173
  - clippy
141
174
  - test
142
175
  - check
176
+ - dotnet-tool
177
+ - dart-package
143
178
  - build
144
179
  if: always()
145
180
  steps:
146
181
  - name: Verify all required jobs succeeded
147
182
  run: |
148
- test "${{ needs.fmt.result }}" = "success"
149
- test "${{ needs.clippy.result }}" = "success"
150
- test "${{ needs.test.result }}" = "success"
151
- test "${{ needs.check.result }}" = "success"
152
- test "${{ needs.build.result }}" = "success"
183
+ test "${{ needs.fmt.result }}" = "success"
184
+ test "${{ needs.clippy.result }}" = "success"
185
+ test "${{ needs.test.result }}" = "success"
186
+ test "${{ needs.check.result }}" = "success"
187
+ test "${{ needs.dotnet-tool.result }}" = "success"
188
+ test "${{ needs.dart-package.result }}" = "success"
189
+ test "${{ needs.build.result }}" = "success"
@@ -7,7 +7,7 @@ on:
7
7
  workflow_dispatch:
8
8
  inputs:
9
9
  tag:
10
- description: "Release tag (e.g. v0.1.1)"
10
+ description: "Release tag (e.g. v0.3.1)"
11
11
  required: true
12
12
 
13
13
  permissions:
@@ -190,3 +190,18 @@ jobs:
190
190
  }
191
191
  })
192
192
  console.log('Dispatched release-homebrew.yml for tag ${{ steps.tag.outputs.tag }}')
193
+
194
+ - name: Trigger pub.dev release
195
+ uses: actions/github-script@v7
196
+ with:
197
+ script: |
198
+ await github.rest.actions.createWorkflowDispatch({
199
+ owner: context.repo.owner,
200
+ repo: context.repo.repo,
201
+ workflow_id: 'release-pub.yml',
202
+ ref: 'main',
203
+ inputs: {
204
+ tag: '${{ steps.tag.outputs.tag }}'
205
+ }
206
+ })
207
+ console.log('Dispatched release-pub.yml for tag ${{ steps.tag.outputs.tag }}')
@@ -4,7 +4,7 @@ on:
4
4
  workflow_dispatch:
5
5
  inputs:
6
6
  tag:
7
- description: "Release tag (e.g. v0.1.1)"
7
+ description: "Release tag (e.g. v0.3.1)"
8
8
  required: true
9
9
 
10
10
  permissions:
@@ -7,7 +7,7 @@ on:
7
7
  workflow_dispatch:
8
8
  inputs:
9
9
  tag:
10
- description: "Release tag (e.g. v0.2.0)"
10
+ description: "Release tag (e.g. v0.3.1)"
11
11
  required: true
12
12
 
13
13
  permissions:
@@ -55,11 +55,7 @@ jobs:
55
55
  OS: macos-26-intel,
56
56
  TARGET: x86_64-apple-darwin,
57
57
  }
58
- - {
59
- NAME: darwin-arm64,
60
- OS: macos-26,
61
- TARGET: aarch64-apple-darwin,
62
- }
58
+ - { NAME: darwin-arm64, OS: macos-26, TARGET: aarch64-apple-darwin }
63
59
 
64
60
  steps:
65
61
  - name: Checkout