@m2s2/cli 0.2.15 → 0.2.17

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/CHANGELOG.md CHANGED
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.17] - 2026-08-02
11
+
12
+
13
+ ## [0.2.16] - 2026-07-31
14
+
15
+ ### Bug Fixes
16
+ - Angular jest-preset-angular v17 setup API + e2e sandbox dev-spawn skip
17
+ - Sync template dependencies to latest @m2s2 releases
18
+ - Formatting and test fixes
19
+ - Clippy fix
20
+
21
+ ### Features
22
+ - Add `m2s2 publish` command for cross-posting blog articles
23
+ - Add cover image support to publishers
24
+
25
+
10
26
  ## [0.2.15] - 2026-07-30
11
27
 
12
28
  ### Bug Fixes
package/README.md CHANGED
@@ -14,6 +14,7 @@ The official CLI for scaffolding and working with [M²S²](https://github.com/M2
14
14
  - [generate service](#m2s2-generate-service)
15
15
  - [upgrade](#m2s2-upgrade)
16
16
  - [completions](#m2s2-completions)
17
+ - [publish](#m2s2-publish)
17
18
  - [Building from Source](#building-from-source)
18
19
  - [Testing](#testing)
19
20
  - [Project Structure](#project-structure)
@@ -402,6 +403,103 @@ The upgrade command hits the GitHub Releases API, compares the latest release ta
402
403
 
403
404
  ---
404
405
 
406
+ ### `m2s2 publish`
407
+
408
+ Publish a Markdown article (YAML frontmatter) to Dev.to, Hashnode, and/or your own blog. Unrelated to the scaffolding commands above — this is a general-purpose cross-posting tool that happens to live in the same CLI.
409
+
410
+ ```bash
411
+ m2s2 publish <file> [OPTIONS]
412
+ ```
413
+
414
+ **Arguments**
415
+
416
+ | Argument | Description |
417
+ |----------|-------------|
418
+ | `<file>` | Path to the Markdown article |
419
+
420
+ **Options**
421
+
422
+ | Flag | Description |
423
+ |------|-------------|
424
+ | `--to <targets>` | Comma-separated target list (`devto`, `hashnode`, `platform`), overriding the frontmatter's `publish:` list |
425
+ | `--update` | Update an existing post instead of creating a new one. Only the `platform` target supports this today — see the table below. |
426
+ | `--preflight-only` | Validate every target and build the exact request each would send, then stop — no network requests, nothing published. |
427
+
428
+ **Article format**
429
+
430
+ ```markdown
431
+ ---
432
+ title: "My Post"
433
+ date: 2026-07-30
434
+ summary: "A short description"
435
+ slug: my-post # optional, derived from the filename otherwise
436
+ tags: [rust, cli]
437
+ excerpt: "..." # optional
438
+ cover_image: https://... # optional — a URL, or a local path resolved relative to the article
439
+ canonical_url: https://... # optional
440
+ publish: [devto, platform] # target list, overridden by --to when given
441
+ ---
442
+
443
+ Article body in Markdown.
444
+ ```
445
+
446
+ `cover_image` accepts either an already-hosted URL or a path to a local file. A local path is uploaded automatically for the `platform` target; Dev.to and Hashnode have no image-upload endpoint in their APIs at all, so a local path there fails *before* anything is published (see the preflight note below) — host the image yourself first and use its URL instead.
447
+
448
+ **Config — `.m2s2-publish.toml`**
449
+
450
+ Credentials live in a `.m2s2-publish.toml` file in the directory you run `m2s2 publish` from — typically your blog-content repo, not a checkout of this CLI. **Make sure that repo gitignores it**, since it holds secrets.
451
+
452
+ ```toml
453
+ [devto]
454
+ api_key = "..."
455
+
456
+ [hashnode]
457
+ token = "..."
458
+ publication_id = "..."
459
+
460
+ [platform]
461
+ endpoint = "https://api.example.com"
462
+ # path = "/admin/blog" (optional, this is the default)
463
+ token = "..."
464
+ # body_command = "./hooks/build-body.sh" (optional, see below)
465
+ ```
466
+
467
+ Any `[section]` that's entirely missing from the file falls back to environment variables instead (the file not existing at all is fine too, as long as env vars cover every target you select). A section already present in the file always wins over env vars — this only fills in a section that's absent, not individual fields within one that exists.
468
+
469
+ | Target | Env vars |
470
+ |--------|----------|
471
+ | `devto` | `M2S2_PUBLISH_DEVTO_API_KEY` |
472
+ | `hashnode` | `M2S2_PUBLISH_HASHNODE_TOKEN` + `M2S2_PUBLISH_HASHNODE_PUBLICATION_ID` |
473
+ | `platform` | `M2S2_PUBLISH_PLATFORM_ENDPOINT` + `M2S2_PUBLISH_PLATFORM_TOKEN` (optionally `_PATH` / `_BODY_COMMAND`) |
474
+
475
+ **Targets**
476
+
477
+ | Target | `--update` | `cover_image` | Notes |
478
+ |--------|------------|----------------|-------|
479
+ | `devto` | Not supported | URL only | Forem/Dev.to API has no image-upload endpoint at all. |
480
+ | `hashnode` | Not supported | URL only | Requires a paid Hashnode Pro subscription as of Hashnode's May 2026 API changes. Same no-upload limitation as Dev.to. |
481
+ | `platform` | Supported | URL, or a local file (uploaded automatically) | A generic target, not specific to any one site — works with any blog exposing a `POST`/`PUT .../admin/blog`-shaped API with bearer auth. `body_command` replaces the request body entirely with your own script's output if the built-in field mapping doesn't match your API. |
482
+
483
+ **Examples**
484
+
485
+ ```bash
486
+ # Publish to whatever targets are listed in the article's frontmatter
487
+ m2s2 publish posts/my-article.md
488
+
489
+ # Publish to specific targets regardless of frontmatter
490
+ m2s2 publish posts/my-article.md --to devto,platform
491
+
492
+ # Update an existing platform post
493
+ m2s2 publish posts/my-article.md --to platform --update
494
+
495
+ # Check everything's valid without publishing anything
496
+ m2s2 publish posts/my-article.md --preflight-only
497
+ ```
498
+
499
+ Every selected target is validated (`--update` support, `cover_image` compatibility) *and* has its exact request built *before* any of them publishes — Dev.to and Hashnode have no update support, so if one target succeeded and a later one then failed, a retry would create a duplicate post on the ones that already succeeded rather than safely resuming. `--preflight-only` runs just that step and stops, so you can check everything's valid — credentials present, cover images resolvable, request built — without publishing anything.
500
+
501
+ ---
502
+
405
503
  ## Building from Source
406
504
 
407
505
  **Prerequisites:** Rust 1.85+ (edition 2024), Node.js 18+ for running scaffolded projects.
@@ -23,7 +23,7 @@
23
23
  "hasInstallScript": true,
24
24
  "license": "MIT",
25
25
  "name": "@m2s2/cli",
26
- "version": "0.2.15"
26
+ "version": "0.2.17"
27
27
  },
28
28
  "node_modules/@isaacs/cliui": {
29
29
  "engines": {
@@ -542,5 +542,5 @@
542
542
  }
543
543
  },
544
544
  "requires": true,
545
- "version": "0.2.15"
545
+ "version": "0.2.17"
546
546
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "artifactDownloadUrls": [
3
- "https://github.com/M2S2-Engineering-Group/m2s2-cli/releases/download/v0.2.15"
3
+ "https://github.com/M2S2-Engineering-Group/m2s2-cli/releases/download/v0.2.17"
4
4
  ],
5
5
  "author": "M²S² Engineering Group <contact@m2s2.io>",
6
6
  "bin": {
@@ -95,7 +95,7 @@
95
95
  "zipExt": ".tar.xz"
96
96
  }
97
97
  },
98
- "version": "0.2.15",
98
+ "version": "0.2.17",
99
99
  "volta": {
100
100
  "node": "18.14.1",
101
101
  "npm": "9.5.0"