feat: install workspace backend config template

This commit is contained in:
Keisuke Hirata 2026-07-02 01:38:47 +09:00
parent 8b1e4b721f
commit a123db83ad
No known key found for this signature in database
6 changed files with 118 additions and 2 deletions

View File

@ -2,7 +2,7 @@
title: 'Workspace Backend設定ファイルのスキーマを定義する' title: 'Workspace Backend設定ファイルのスキーマを定義する'
state: 'closed' state: 'closed'
created_at: '2026-07-01T14:41:48Z' created_at: '2026-07-01T14:41:48Z'
updated_at: '2026-07-01T15:48:28Z' updated_at: '2026-07-01T16:38:31Z'
assignee: null assignee: null
queued_by: 'yoi ticket' queued_by: 'yoi ticket'
queued_at: '2026-07-01T15:32:38Z' queued_at: '2026-07-01T15:32:38Z'

View File

@ -129,4 +129,29 @@ Validation:
- `nix build .#yoi --no-link` - `nix build .#yoi --no-link`
---
<!-- event: implementation_report author: hare at: 2026-07-01T16:38:31Z -->
## Implementation report
Moved the Workspace Backend default config template into runtime resources and wired init-time copying.
Changes:
- Added `resources/workspace-backend.default.toml` as the packaged template source.
- Embedded the template with `include_str!` from `workspace-server::config`.
- Added `.yoi/workspace-backend.default.toml` as the workspace-local copied template path.
- `yoi-workspace-server serve` now ensures the default template exists after workspace identity initialization and before reading `.local` config.
- Copying uses create-new semantics and does not overwrite an existing workspace-local default template.
- Removed the previously untracked `.yoi/workspace-backend.default.toml` workspace copy from the repository root.
Validation:
- `cargo fmt --check`
- `cargo test -p yoi-workspace-server`
- `cargo check -p yoi`
- TOML parse check with Deno
- `git diff --check`
- `nix build .#yoi --no-link`
--- ---

View File

@ -10,6 +10,10 @@ use crate::server::{AuthConfig, ServerConfig};
use crate::{Error, Result}; use crate::{Error, Result};
pub const WORKSPACE_BACKEND_CONFIG_RELATIVE_PATH: &str = ".yoi/workspace-backend.local.toml"; pub const WORKSPACE_BACKEND_CONFIG_RELATIVE_PATH: &str = ".yoi/workspace-backend.local.toml";
pub const WORKSPACE_BACKEND_DEFAULT_CONFIG_RELATIVE_PATH: &str =
".yoi/workspace-backend.default.toml";
pub const WORKSPACE_BACKEND_DEFAULT_CONFIG_TEMPLATE: &str =
include_str!("../../../resources/workspace-backend.default.toml");
const DEFAULT_LISTEN: &str = "127.0.0.1:8787"; const DEFAULT_LISTEN: &str = "127.0.0.1:8787";
const DEFAULT_FRONTEND_URL: &str = "http://127.0.0.1:5173"; const DEFAULT_FRONTEND_URL: &str = "http://127.0.0.1:5173";
const DEFAULT_MAX_RECORDS: usize = 200; const DEFAULT_MAX_RECORDS: usize = 200;
@ -88,6 +92,33 @@ impl WorkspaceBackendConfigFile {
.join(WORKSPACE_BACKEND_CONFIG_RELATIVE_PATH) .join(WORKSPACE_BACKEND_CONFIG_RELATIVE_PATH)
} }
pub fn default_template_path_for_workspace(workspace_root: impl AsRef<Path>) -> PathBuf {
workspace_root
.as_ref()
.join(WORKSPACE_BACKEND_DEFAULT_CONFIG_RELATIVE_PATH)
}
pub fn ensure_default_template_for_workspace(workspace_root: impl AsRef<Path>) -> Result<()> {
let path = Self::default_template_path_for_workspace(workspace_root);
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
match fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(&path)
{
Ok(mut file) => {
use std::io::Write;
file.write_all(WORKSPACE_BACKEND_DEFAULT_CONFIG_TEMPLATE.as_bytes())?;
file.sync_all()?;
Ok(())
}
Err(error) if error.kind() == io::ErrorKind::AlreadyExists => Ok(()),
Err(error) => Err(Error::Io(error)),
}
}
pub fn load_for_workspace(workspace_root: impl AsRef<Path>) -> Result<Self> { pub fn load_for_workspace(workspace_root: impl AsRef<Path>) -> Result<Self> {
let path = Self::path_for_workspace(workspace_root); let path = Self::path_for_workspace(workspace_root);
match fs::read_to_string(&path) { match fs::read_to_string(&path) {
@ -335,6 +366,20 @@ root = ".local-data"
); );
} }
#[test]
fn copies_default_template_without_overwriting() {
let dir = tempfile::tempdir().unwrap();
WorkspaceBackendConfigFile::ensure_default_template_for_workspace(dir.path()).unwrap();
let path = WorkspaceBackendConfigFile::default_template_path_for_workspace(dir.path());
let raw = fs::read_to_string(&path).unwrap();
assert_eq!(raw, WORKSPACE_BACKEND_DEFAULT_CONFIG_TEMPLATE);
WorkspaceBackendConfigFile::parse_str(&raw, &path).unwrap();
fs::write(&path, "# custom template\n").unwrap();
WorkspaceBackendConfigFile::ensure_default_template_for_workspace(dir.path()).unwrap();
assert_eq!(fs::read_to_string(&path).unwrap(), "# custom template\n");
}
#[test] #[test]
fn token_value_field_is_not_in_schema() { fn token_value_field_is_not_in_schema() {
let error = WorkspaceBackendConfigFile::parse_str( let error = WorkspaceBackendConfigFile::parse_str(

View File

@ -16,7 +16,7 @@ pub mod store;
pub use config::{ pub use config::{
ResolvedWorkspaceBackendConfig, WORKSPACE_BACKEND_CONFIG_RELATIVE_PATH, ResolvedWorkspaceBackendConfig, WORKSPACE_BACKEND_CONFIG_RELATIVE_PATH,
WorkspaceBackendConfigFile, WORKSPACE_BACKEND_DEFAULT_CONFIG_RELATIVE_PATH, WorkspaceBackendConfigFile,
}; };
pub use identity::{WORKSPACE_IDENTITY_RELATIVE_PATH, WorkspaceIdentity}; pub use identity::{WORKSPACE_IDENTITY_RELATIVE_PATH, WorkspaceIdentity};
pub use records::{ pub use records::{

View File

@ -67,6 +67,7 @@ async fn run() -> Result<(), Box<dyn std::error::Error>> {
async fn run_serve(options: ServeOptions) -> Result<(), Box<dyn std::error::Error>> { async fn run_serve(options: ServeOptions) -> Result<(), Box<dyn std::error::Error>> {
let identity = WorkspaceIdentity::load_or_init(&options.workspace)?; let identity = WorkspaceIdentity::load_or_init(&options.workspace)?;
WorkspaceBackendConfigFile::ensure_default_template_for_workspace(&options.workspace)?;
let config_file = WorkspaceBackendConfigFile::load_for_workspace(&options.workspace)?; let config_file = WorkspaceBackendConfigFile::load_for_workspace(&options.workspace)?;
let mut resolved = config_file.resolve(&options.workspace, identity)?; let mut resolved = config_file.resolve(&options.workspace, identity)?;
if let Some(db) = options.db { if let Some(db) = options.db {

View File

@ -0,0 +1,45 @@
# Workspace Backend local config template.
#
# Copied to `.yoi/workspace-backend.default.toml` during workspace init.
# Copy that file to `.yoi/workspace-backend.local.toml` and edit it.
# The `.local` file is intentionally git-ignored.
#
# Omit a key to use the built-in default. TOML has no `null`, so optional
# settings are represented by leaving the key commented out.
[server]
# Backend HTTP/WebSocket listen address.
listen = "127.0.0.1:8787"
# Browser-facing frontend URL used by local tooling/display.
frontend_url = "http://127.0.0.1:5173"
# Static SPA build directory override. Leave commented for dev/API-only mode.
# Relative paths are resolved from the workspace root.
# static_assets_dir = "web/workspace/dist"
[data]
# Backend data root override. Leave commented to use the user-data default:
# <data_dir>/workspace-server/<workspace_id>/
# Relative paths are resolved from the workspace root.
# root = ".yoi/workspace-backend.data"
# Explicit control-plane SQLite DB path override.
# If omitted, this defaults to `<data.root>/workspace.db`.
# workspace_database_path = ".yoi/workspace-backend.data/workspace.db"
# Explicit embedded Runtime fs-store root override.
# If omitted, this defaults to `<data.root>/embedded-runtime`.
# embedded_runtime_store_root = ".yoi/workspace-backend.data/embedded-runtime"
[limits]
max_records = 200
# Remote Runtime sources. Token values must not be written here.
# Use token_ref only after secret-ref resolution is implemented for this config.
#
# [[runtimes.remote]]
# id = "example"
# endpoint = "http://127.0.0.1:8790"
# display_name = "Example Runtime"
# token_ref = "local:example-runtime-token"