Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions crates/registryctl/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
Schema and maintained DHIS2 offline journey cover the same closed grammar.
Target attributes are caller-supplied request context, not authenticated
identifiers.
- `registryctl init --from` now installs deterministic project-local JSON Schemas and VS Code and
Zed workspace mappings. `registryctl authoring editor` verifies or safely refreshes the same
version-matched setup for an existing project.

### Changed

- `registryctl init --from` and `registryctl init relay` now print concise human-readable results
with tailored next commands. Both forms accept `--format json` for the versioned
`registryctl.init.v1` machine-readable report.

### Removed

Expand Down
10 changes: 10 additions & 0 deletions crates/registryctl/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ registryctl start
registryctl smoke
```

Initialization prints the created project, notable artifacts, and next commands. Add
`--format json` to either `init relay` or `init --from` for the versioned
`registryctl.init.v1` machine-readable report.

The generated project contains a local Registry Relay configuration, sample
XLSX workbook, Compose file, project manifest, local demo credentials, and an
optional Bruno API collection.
Expand All @@ -46,11 +50,17 @@ and Notary inputs. Available starters are `http`, `dhis2-tracker`,

```sh
registryctl init --from http --project-dir registry-project
registryctl authoring editor --project-dir registry-project
registryctl test --project-dir registry-project
registryctl check --project-dir registry-project --environment local --explain
registryctl build --project-dir registry-project --environment local
```

Initialization copies the five schemas embedded in `registryctl`, configures project-relative VS
Code and Zed schema mappings, and reports the generated editor manifest. The explicit
`authoring editor` command verifies the setup and safely refreshes an unchanged generated bundle
after an upgrade.

The authoring contract accepts one to eight exact selector inputs and up to
sixteen typed inputs in total. Canonical selectors have a fixed 4096-byte
aggregate ceiling. Input names match `[a-z][a-z0-9_]{0,63}` and use a bounded
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ This starter demonstrates one bounded product-neutral HTTP integration.
From this workspace directory:

```bash
registryctl authoring editor --project-dir .
registryctl test --project-dir . --integration person-record --fixture active-person --trace
registryctl test --project-dir . --integration person-record --fixture active-person --watch
registryctl test --project-dir .
registryctl check --project-dir . --environment local --explain
registryctl build --project-dir . --environment local
registryctl authoring schema --kind integration > integration.schema.json
```

`check` is human-readable by default. Use `--format json` only for machine
consumers. The generated schema can be selected from an editor modeline.
consumers. Editor setup uses the five schemas copied from this `registryctl`
build for VS Code and Zed.

Edit `integrations/person-record/integration.yaml` and its synthetic fixtures.
Keep real destinations and credentials only in `environments/` secret bindings.
89 changes: 74 additions & 15 deletions crates/registryctl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ mod project_authoring;

pub use project_authoring::{
build_registry_project, check_registry_project, init_registry_project,
render_project_authoring_diagnostics, test_registry_project, test_registry_project_selected,
ProjectAuthoringDiagnostic, ProjectAuthoringDiagnostics, ProjectBuildOptions,
ProjectCheckOptions, ProjectCommandReport, ProjectInitOptions, ProjectStarter,
render_project_authoring_diagnostics, setup_registry_project_editor, test_registry_project,
test_registry_project_selected, ProjectAuthoringDiagnostic, ProjectAuthoringDiagnostics,
ProjectBuildOptions, ProjectCheckOptions, ProjectCommandReport, ProjectEditorSetupOptions,
ProjectEditorSetupReport, ProjectInitOptions, ProjectSchemaKind, ProjectStarter,
ProjectTestOptions, ProjectTestSelection, SemanticChange,
};

Expand Down Expand Up @@ -73,6 +74,48 @@ const UPDATE_CHECK_CACHE_SECONDS: u64 = 60 * 60 * 24;
const PROJECT_SCHEMA_VERSION: &str = "registryctl/v1";
const CONFIG_BUNDLE_SIGNATURE_SCHEMA: &str = "registry.platform.config_bundle_signatures.v1";
const CONFIG_TRUST_ANCHOR_SCHEMA: &str = "registry.platform.config_trust_anchor.v1";
const INIT_REPORT_SCHEMA_VERSION: &str = "registryctl.init.v1";

#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum InitProjectKind {
RegistryProject,
RelaySpreadsheetApi,
}

#[derive(Clone, Debug, Serialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum InitSource {
Starter {
id: String,
release: String,
content_digest: String,
content_state: &'static str,
},
Sample {
id: String,
},
}

#[derive(Clone, Debug, Serialize)]
pub struct InitArtifacts {
pub project_file: PathBuf,
#[serde(skip_serializing_if = "Option::is_none")]
pub bruno_collection: Option<PathBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
pub editor_manifest: Option<PathBuf>,
}

#[derive(Clone, Debug, Serialize)]
pub struct InitReport {
pub schema_version: &'static str,
pub status: &'static str,
pub project: String,
pub project_kind: InitProjectKind,
pub output: PathBuf,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Serialize init paths without failing on non-UTF8

When init --format json is used on Unix with a non-UTF-8 destination path, this PathBuf field makes serde_json::to_string_pretty fail with path contains invalid UTF-8 characters after the initializer has already copied or written the project. The previous starter report converted paths to display strings, and the Relay initializer also printed display paths, so JSON mode now leaves a successfully-created project behind while exiting nonzero; please convert or reject paths before doing writes so the init command remains atomic.

Useful? React with 👍 / 👎.

pub source: InitSource,
pub artifacts: InitArtifacts,
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
#[serde(deny_unknown_fields)]
Expand Down Expand Up @@ -905,7 +948,7 @@ pub fn init_spreadsheet_api(
dir: &Path,
sample: Sample,
image_lock: &RegistryctlImageLock,
) -> Result<()> {
) -> Result<InitReport> {
match sample {
Sample::Benefits => init_benefits_project(dir, image_lock),
}
Expand Down Expand Up @@ -1249,14 +1292,13 @@ pub fn smoke_project(project_dir: &Path) -> Result<()> {
}
}

pub fn bruno_generate_project(project_dir: &Path, force: bool) -> Result<()> {
pub fn bruno_generate_project(project_dir: &Path, force: bool) -> Result<PathBuf> {
let project = Project::load(project_dir)?;
let secrets = LocalEnv::load(&project_dir.join(&project.local.secrets_env))?;
let collection_dir = project_dir.join(BRUNO_COLLECTION_DIR);
let files = bruno_files(&project, &secrets)?;
write_generated_files(project_dir, &collection_dir, files, force)?;
println!("Bruno collection: {}", collection_dir.display());
Ok(())
Ok(collection_dir)
}

pub fn bruno_open_project(project_dir: &Path) -> Result<()> {
Expand Down Expand Up @@ -1732,7 +1774,7 @@ impl SecretRedactor {
}
}

fn init_benefits_project(dir: &Path, image_lock: &RegistryctlImageLock) -> Result<()> {
fn init_benefits_project(dir: &Path, image_lock: &RegistryctlImageLock) -> Result<InitReport> {
if dir.exists() {
let mut entries =
fs::read_dir(dir).with_context(|| format!("failed to inspect {}", dir.display()))?;
Expand Down Expand Up @@ -1763,8 +1805,22 @@ fn init_benefits_project(dir: &Path, image_lock: &RegistryctlImageLock) -> Resul
write_text(dir.join("secrets/local.env"), &credentials.env_file())?;
write_text(dir.join("output/.gitkeep"), "")?;
sample::write_benefits_workbook(&dir.join("data/benefits_casework.xlsx"))?;
bruno_generate_project(dir, false)?;
Ok(())
let bruno_collection = bruno_generate_project(dir, false)?;
Ok(InitReport {
schema_version: INIT_REPORT_SCHEMA_VERSION,
status: "initialized",
project: generated_project_name(dir),
project_kind: InitProjectKind::RelaySpreadsheetApi,
output: dir.to_path_buf(),
source: InitSource::Sample {
id: Sample::Benefits.id().to_string(),
},
artifacts: InitArtifacts {
project_file: dir.join("registryctl.yaml"),
bruno_collection: Some(bruno_collection),
editor_manifest: None,
},
})
}

fn write_text(path: PathBuf, contents: &str) -> Result<()> {
Expand Down Expand Up @@ -2773,11 +2829,7 @@ struct LocalSection<'a> {
}

fn registryctl_manifest(dir: &Path, image_lock: &RegistryctlImageLock) -> Result<String> {
let name = dir
.file_name()
.and_then(|name| name.to_str())
.unwrap_or("my-first-api")
.to_string();
let name = generated_project_name(dir);
let manifest = ProjectManifest {
schema_version: PROJECT_SCHEMA_VERSION,
project: ProjectSection {
Expand All @@ -2804,6 +2856,13 @@ fn registryctl_manifest(dir: &Path, image_lock: &RegistryctlImageLock) -> Result
serde_yaml::to_string(&manifest).context("failed to render registryctl manifest")
}

fn generated_project_name(dir: &Path) -> String {
dir.file_name()
.and_then(|name| name.to_str())
.unwrap_or("my-first-api")
.to_string()
}

fn compose_yaml(image_lock: &RegistryctlImageLock) -> String {
include_str!("templates/compose.yaml").replace("{{relay_image}}", image_lock.relay_image())
}
Expand Down
Loading
Loading