Skip to content

Commit 8e6b1e9

Browse files
ptrthomasclaude
andcommitted
integrate karate.sh/manifest.json for artifact downloads
- Rewrite src/manifest.rs to parse new manifest schema - Update setup command to fetch JAR URL from manifest - Update update command to use manifest for version checking - Add config support for channel (stable/beta) and version pinning - Cache manifest locally after successful fetch - Update docs/spec.md with manifest schema documentation - Add Netlify migration testing checklist to tracker.md The CLI now fetches artifact download URLs from https://karate.sh/manifest.json instead of using the GitHub API directly, avoiding rate limits. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent deb47f6 commit 8e6b1e9

6 files changed

Lines changed: 368 additions & 161 deletions

File tree

docs/spec.md

Lines changed: 80 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ Managed by the launcher:
5757
A simple online manifest the launcher reads, e.g.:
5858

5959
```
60-
https://github.com/karatelabs/karate-cli-manifest/releases/latest/download/manifest.json
60+
https://karate.sh/manifest.json
6161
```
6262

6363
Contains channel → versions → URLs → checksums.
@@ -482,54 +482,97 @@ Note: A `.karate` folder with only `karate.json` is treated as config-only, not
482482

483483
---
484484

485-
# **8. GitHub Manifest Format**
485+
# **8. Release Manifest (karate.sh)**
486+
487+
The CLI fetches artifact download URLs from a central manifest hosted at karate.sh. This avoids GitHub API rate limits and provides a single source of truth for all releases.
486488

487489
## **Location**
488490

489491
```
490-
https://github.com/karatelabs/karate-cli-manifest/releases/latest/download/manifest.json
492+
https://karate.sh/manifest.json
491493
```
492494

495+
**Source repository:** [github.com/karatelabs/karate-sh](https://github.com/karatelabs/karate-sh) (private)
496+
493497
## **Manifest Schema**
494498

495499
```json
496500
{
497501
"schema_version": 1,
498-
"channels": {
499-
"stable": {
500-
"version": "2.0.0",
501-
"karate_jar": {
502-
"url": "https://github.com/karatelabs/karate/releases/download/v2.0.0/karate-2.0.0-all.jar",
503-
"sha256": "abc123..."
504-
},
505-
"jre": {
506-
"version": "17.0.12",
507-
"platforms": {
508-
"macos-aarch64": { "url": "...", "sha256": "..." },
509-
"macos-x64": { "url": "...", "sha256": "..." },
510-
"linux-x64": { "url": "...", "sha256": "..." },
511-
"linux-aarch64": { "url": "...", "sha256": "..." },
512-
"windows-x64": { "url": "...", "sha256": "..." }
513-
}
514-
},
515-
"plugins": {
516-
"xplorer": {
517-
"version": "1.3.0",
518-
"url": "...",
519-
"sha256": "..."
502+
"generated_at": "2025-02-05T00:00:00Z",
503+
"artifacts": {
504+
"karate-cli": {
505+
"description": "Karate CLI - Rust binary launcher",
506+
"repo": "karatelabs/karate-cli",
507+
"versions": {
508+
"0.1.2": {
509+
"channels": ["stable"],
510+
"released_at": "2025-11-30T00:00:00Z",
511+
"platforms": {
512+
"macos-aarch64": { "url": "https://github.com/.../karate-darwin-arm64.tar.gz", "sha256": "..." },
513+
"macos-x64": { "url": "https://github.com/.../karate-darwin-x64.tar.gz", "sha256": "..." },
514+
"linux-x64": { "url": "https://github.com/.../karate-linux-x64.tar.gz", "sha256": "..." },
515+
"linux-aarch64": { "url": "https://github.com/.../karate-linux-arm64.tar.gz", "sha256": "..." },
516+
"windows-x64": { "url": "https://github.com/.../karate-windows-x64.zip", "sha256": "..." }
517+
}
520518
}
521519
}
522520
},
523-
"beta": { ... }
521+
"karate": {
522+
"description": "Karate Core - Standalone testing JAR",
523+
"repo": "karatelabs/karate",
524+
"versions": {
525+
"1.5.2": {
526+
"channels": ["stable"],
527+
"released_at": "2025-11-30T00:00:00Z",
528+
"url": "https://github.com/karatelabs/karate/releases/download/v1.5.2/karate-1.5.2.jar",
529+
"sha256": "ccf4740c64a154c4c2457d6f0fd19a8f37902c29d32aac4e23012e0a878614be"
530+
}
531+
}
532+
}
524533
},
525-
"defaults": {
526-
"karate_jar_url_template": "https://github.com/karatelabs/karate/releases/download/v{version}/karate-{version}-all.jar",
527-
"jre_version": "17.0.12"
534+
"channel_defaults": {
535+
"stable": { "karate-cli": "0.1.2", "karate": "1.5.2" },
536+
"beta": {}
528537
}
529538
}
530539
```
531540

532-
**Note:** The `defaults` section enables the launcher to work without fetching manifest, using URL templates and conventions.
541+
## **Channels**
542+
543+
- **stable** — Production releases
544+
- **beta** — Pre-release versions (RC, alpha, etc.)
545+
546+
Users can switch channels via config:
547+
```bash
548+
karate config --global # Set "channel": "beta"
549+
```
550+
551+
## **Adding a New Release**
552+
553+
When a new Karate release is published:
554+
555+
1. Clone the `karatelabs/karate-sh` repository
556+
2. Edit `public/manifest.json`:
557+
- Add new version entry under the artifact
558+
- Get SHA256 from GitHub release `.sha256` files
559+
- Update `channel_defaults.stable` if promoting to stable
560+
3. Commit and push to main
561+
4. Netlify auto-deploys to karate.sh
562+
563+
See the [karate-sh README](https://github.com/karatelabs/karate-sh) for detailed instructions.
564+
565+
## **Version Pinning**
566+
567+
Users can pin a specific version in their config:
568+
```json
569+
{
570+
"channel": "stable",
571+
"karate_version": "1.5.2"
572+
}
573+
```
574+
575+
Setting `karate_version` to anything other than `"latest"` will use that exact version.
533576

534577
---
535578

@@ -808,16 +851,16 @@ npm bin/karate → Rust CLI → JRE/JAR (self-managed)
808851

809852
# **12. Future Enhancements (Post-MVP)**
810853

811-
## **12.1 Central Manifest at karate.sh**
854+
## **12.1 Central Manifest at karate.sh** ✅ IMPLEMENTED
812855

813-
Host a central manifest at `https://karate.sh/manifest.json` that provides:
856+
The central manifest is now live at `https://karate.sh/manifest.json`:
814857

815-
* Download locations for all Karate releases (OSS and commercial)
816-
* Version discovery for Karate JAR, JRE, and plugins
817-
* Checksums for integrity verification
818-
* Support for commercial/non-open-source tools (Xplorer, MCP server, etc.)
858+
* Download locations for karate-cli and karate JAR
859+
* SHA-256 checksums for integrity verification
860+
* Channel support (stable, beta) for version management
861+
* Hosted on Netlify, managed via [github.com/karatelabs/karate-sh](https://github.com/karatelabs/karate-sh)
819862

820-
This enables the CLI to resolve downloads from a single authoritative source rather than hardcoded GitHub URLs.
863+
See Section 8 for manifest schema and release workflow.
821864

822865
## **12.2 Rust-Native `init` Command**
823866

docs/tracker.md

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Karate CLI Development Tracker
22

3-
> Last updated: 2025-11-30
3+
> Last updated: 2025-02-05
44
55
## Progress Overview
66

@@ -12,7 +12,8 @@
1212
| JAR Delegation | ✅ Complete | Pass-through to Karate JAR works |
1313
| Doctor Command | ✅ Complete | Full diagnostics with JSON output |
1414
| GitHub Releases | ✅ Complete | v0.1.0 with all platform binaries + checksums |
15-
| karate.sh Site | ✅ Complete | AWS Amplify hosting, install scripts |
15+
| karate.sh Site | ✅ Complete | Migrated to Netlify, manifest.json live |
16+
| Central Manifest | ✅ Complete | karate.sh/manifest.json with SHA256 checksums |
1617
| Distribution | 🔄 In Progress | npm, Homebrew, Chocolatey pending |
1718

1819
---
@@ -114,7 +115,7 @@
114115

115116
### Phase 2: Distribution
116117

117-
#### karate.sh Universal Installer
118+
#### karate.sh Site & Central Manifest
118119
- [x] Shell script for Unix/macOS (install.sh)
119120
- [x] OS/arch detection (darwin/linux, x64/arm64)
120121
- [x] Binary download from GitHub releases
@@ -124,9 +125,17 @@
124125
- [x] PowerShell script for Windows (install.ps1)
125126
- [x] Same functionality
126127
- [x] Auto-adds to user PATH
127-
- [x] AWS Amplify hosting at karate.sh
128+
- [x] **Migrated to Netlify** (from AWS Amplify)
128129
- [x] Landing page with install instructions
129130
- [x] install.sh and install.ps1 served
131+
- [x] CORS headers for manifest.json
132+
- [x] **Central manifest at karate.sh/manifest.json**
133+
- [x] Schema: artifacts, versions, channels, SHA256 checksums
134+
- [x] Channels: stable, beta
135+
- [x] CLI fetches from manifest (avoids GitHub API rate limits)
136+
- [x] Version pinning via config (karate_version)
137+
- [x] Channel selection via config (channel)
138+
- [x] Source: [github.com/karatelabs/karate-sh](https://github.com/karatelabs/karate-sh)
130139

131140
**Install URLs:**
132141
- Unix/macOS: `curl -fsSL https://karate.sh/install.sh | sh`
@@ -221,6 +230,30 @@ mkdir -p .karate/ext
221230
cargo run -- doctor # Shows local override active
222231
```
223232

233+
### Netlify Migration Testing (2025-02-05) 🔄 PENDING
234+
235+
Waiting for DNS propagation to karate.sh. Once live, test:
236+
237+
- [ ] **Manifest endpoint**: `curl https://karate.sh/manifest.json | jq .schema_version`
238+
- [ ] **CORS headers**: `curl -I https://karate.sh/manifest.json` includes `access-control-allow-origin: *`
239+
- [ ] **Install scripts**:
240+
- [ ] `curl https://karate.sh/install.sh` returns script
241+
- [ ] `curl https://karate.sh/install.ps1` returns script
242+
- [ ] **Landing page**: `curl -I https://karate.sh/` returns 200
243+
- [ ] **CLI setup from manifest**:
244+
```bash
245+
KARATE_HOME=./home/.karate cargo run -- setup --all --force
246+
# Should fetch from karate.sh/manifest.json
247+
# Should download JAR with SHA256 verification
248+
```
249+
- [ ] **CLI update from manifest**:
250+
```bash
251+
KARATE_HOME=./home/.karate cargo run -- update
252+
# Should check versions via manifest
253+
```
254+
- [ ] **Channel selection**: Test `channel: "beta"` in config
255+
- [ ] **Version pinning**: Test `karate_version: "1.5.2"` in config
256+
224257
### Universal Installer Testing (2025-11-30)
225258

226259
Tested platforms via Docker and native:

src/commands/setup.rs

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
//! Setup command - first-run wizard and targeted setup.
22
33
use crate::cli::SetupArgs;
4-
use crate::download::{download_file, extract_tar_gz, fetch_latest_release, resolve_justj_jre};
4+
use crate::config::load_merged_config;
5+
use crate::download::{download_file, extract_tar_gz, resolve_justj_jre};
56
use crate::error::ExitCode;
67
use crate::jre::{find_active_jre, find_system_jre, JreSource, MIN_JAVA_VERSION};
8+
use crate::manifest::{fetch_manifest, MANIFEST_URL};
79
use crate::platform::{KaratePaths, Platform};
810
use anyhow::Result;
911
use console::style;
@@ -253,28 +255,74 @@ async fn download_jre(platform: &Platform, paths: &KaratePaths, java_version: u8
253255
Ok(())
254256
}
255257

256-
/// Download Karate JAR from GitHub releases
258+
/// Download Karate JAR using manifest from karate.sh
257259
async fn download_karate_jar(paths: &KaratePaths) -> Result<()> {
258-
println!(" Fetching latest release info...");
259-
260-
let release = fetch_latest_release("karatelabs", "karate").await?;
261-
let version = release.tag_name.trim_start_matches('v');
260+
// Load config to get channel and version preferences
261+
let config = load_merged_config()?;
262+
let channel = &config.channel;
263+
264+
println!(" Fetching release manifest from karate.sh...");
265+
266+
let manifest = fetch_manifest().await.map_err(|e| {
267+
anyhow::anyhow!(
268+
"Failed to fetch manifest from {}: {}\n\n\
269+
Check your network connection or try again later.",
270+
MANIFEST_URL,
271+
e
272+
)
273+
})?;
274+
275+
// Determine version: use config if pinned, otherwise get latest from channel
276+
let version = if config.karate_version != "latest" {
277+
// User pinned a specific version
278+
println!(
279+
" Using pinned version: {}",
280+
style(&config.karate_version).cyan()
281+
);
282+
config.karate_version.clone()
283+
} else {
284+
// Get latest from configured channel
285+
manifest
286+
.get_latest_version("karate", channel)
287+
.map(|s| s.to_string())
288+
.ok_or_else(|| {
289+
anyhow::anyhow!(
290+
"No '{}' karate version found in manifest.\n\
291+
Available channels: stable, beta\n\
292+
Set channel with: karate config --global",
293+
channel
294+
)
295+
})?
296+
};
262297

263-
println!(" Latest version: {}", style(version).green());
298+
if channel != "stable" {
299+
println!(" Channel: {}", style(channel).yellow());
300+
}
301+
println!(" Version: {}", style(&version).green());
302+
303+
let (url, sha256) = manifest
304+
.get_jar_download("karate", &version)
305+
.ok_or_else(|| {
306+
anyhow::anyhow!(
307+
"No download URL found for karate {} in manifest.\n\
308+
Check available versions at: {}",
309+
version,
310+
MANIFEST_URL
311+
)
312+
})?;
264313

265-
// Find the main karate JAR (not robot, not sbom)
266314
let jar_name = format!("karate-{}.jar", version);
267-
let asset = release
268-
.assets
269-
.iter()
270-
.find(|a| a.name == jar_name)
271-
.ok_or_else(|| anyhow::anyhow!("Could not find {} in release assets", jar_name))?;
272-
273315
println!(" Downloading {}...", jar_name);
274-
println!(" {}", style(&asset.browser_download_url).dim());
316+
println!(" {}", style(url).dim());
275317

276318
let dest = paths.dist.join(&jar_name);
277-
download_file(&asset.browser_download_url, &dest, None).await?;
319+
download_file(url, &dest, Some(sha256)).await?;
320+
321+
// Cache the manifest for future use
322+
let cache_path = paths.cache.join("manifest.json");
323+
if let Err(e) = crate::manifest::save_manifest_cache(&manifest, &cache_path) {
324+
eprintln!(" {} Failed to cache manifest: {}", style("!").yellow(), e);
325+
}
278326

279327
println!(" {} Karate JAR installed", style("✓").green());
280328
Ok(())

0 commit comments

Comments
 (0)