chore: add license and scripts installers
This commit is contained in:
parent
5a459e7f02
commit
f6023b5ffc
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Jesús Pérez
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
231
LICENSE.md
Normal file
231
LICENSE.md
Normal file
@ -0,0 +1,231 @@
|
||||
# TypeDialog License
|
||||
|
||||
## Project License
|
||||
|
||||
TypeDialog is licensed under the **MIT License**.
|
||||
|
||||
See [LICENSE](LICENSE) file for the full MIT license text.
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
This project includes the following dependencies under their respective licenses:
|
||||
|
||||
### Apache-2.0 Only (3)
|
||||
- rpassword 7.4.0
|
||||
- rtoolbox 0.0.3
|
||||
- sync_wrapper 1.0.2
|
||||
|
||||
### MIT Only (66)
|
||||
- atty 0.2.14
|
||||
- axum 0.8.7
|
||||
- axum-core 0.5.5
|
||||
- bytes 1.11.0
|
||||
- castaway 0.2.4
|
||||
- compact_str 0.8.1
|
||||
- console 0.16.2
|
||||
- convert_case 0.10.0
|
||||
- crossterm 0.28.1
|
||||
- crossterm 0.29.0
|
||||
- crossterm_winapi 0.9.1
|
||||
- darling 0.20.11
|
||||
- darling_core 0.20.11
|
||||
- darling_macro 0.20.11
|
||||
- derive_more 2.1.0
|
||||
- derive_more-impl 2.1.0
|
||||
- dialoguer 0.12.0
|
||||
- typedialog 0.1.0
|
||||
- typedialog-core 0.1.0
|
||||
- typedialog-tui 0.1.0
|
||||
- typedialog-web 0.1.0
|
||||
- fuzzy-matcher 0.3.7
|
||||
- generic-array 0.14.7
|
||||
- globwalk 0.9.1
|
||||
- http-body 1.0.1
|
||||
- http-body-util 0.1.3
|
||||
- http-range-header 0.4.2
|
||||
- hyper 1.8.1
|
||||
- hyper-util 0.1.19
|
||||
- inquire 0.9.1
|
||||
- instability 0.3.10
|
||||
- libm 0.2.15
|
||||
- libredox 0.1.10
|
||||
- lru 0.12.5
|
||||
- mime_guess 2.0.5
|
||||
- mio 1.1.1
|
||||
- nu-ansi-term 0.50.3
|
||||
- parse-zoneinfo 0.3.1
|
||||
- phf 0.11.3
|
||||
- phf_codegen 0.11.3
|
||||
- phf_generator 0.11.3
|
||||
- phf_shared 0.11.3
|
||||
- ratatui 0.29.0
|
||||
- redox_syscall 0.5.18
|
||||
- redox_users 0.5.2
|
||||
- sharded-slab 0.1.7
|
||||
- slab 0.4.11
|
||||
- strsim 0.11.1
|
||||
- strum 0.26.3
|
||||
- strum_macros 0.26.4
|
||||
- tera 1.20.1
|
||||
- tokio 1.48.0
|
||||
- tokio-macros 2.6.0
|
||||
- tokio-util 0.7.17
|
||||
- tower 0.5.2
|
||||
- tower-http 0.6.8
|
||||
- tower-layer 0.3.3
|
||||
- tower-service 0.3.3
|
||||
- tracing 0.1.43
|
||||
- tracing-attributes 0.1.31
|
||||
- tracing-core 0.1.35
|
||||
- tracing-log 0.2.0
|
||||
- tracing-subscriber 0.3.22
|
||||
- unsafe-libyaml 0.2.11
|
||||
- valuable 0.1.1
|
||||
- winnow 0.7.14
|
||||
|
||||
### Apache-2.0 OR MIT (190)
|
||||
|
||||
Most dependencies use dual licensing between Apache-2.0 and MIT.
|
||||
|
||||
- allocator-api2 0.2.21 | - android_system_properties 0.1.5 | - anstream 0.6.21
|
||||
- anstyle 1.0.13 | - anstyle-parse 0.2.7 | - anstyle-query 1.1.5
|
||||
- anstyle-wincon 3.0.11 | - anyhow 1.0.100 | - async-trait 0.1.89
|
||||
- atomic-waker 1.1.2 | - autocfg 1.5.0 | - bitflags 2.10.0
|
||||
- block-buffer 0.10.4 | - bstr 1.12.1 | - bumpalo 3.19.0
|
||||
- cassowary 0.3.0 | - cc 1.2.49 | - cfg-if 1.0.4
|
||||
- chrono 0.4.42 | - chrono-tz 0.9.0 | - chrono-tz-build 0.3.0
|
||||
- clap 4.5.53 | - clap_builder 4.5.53 | - clap_derive 4.5.49
|
||||
- clap_lex 0.7.6 | - colorchoice 1.0.4 | - core-foundation-sys 0.8.7
|
||||
- cpufeatures 0.2.17 | - crossbeam-deque 0.8.6 | - crossbeam-epoch 0.9.18
|
||||
- crossbeam-utils 0.8.21 | - crypto-common 0.1.7 | - digest 0.10.7
|
||||
- dirs 6.0.0 | - dirs-sys 0.5.0 | - displaydoc 0.2.5
|
||||
- document-features 0.2.12 | - dyn-clone 1.0.20 | - either 1.15.0
|
||||
- encode_unicode 1.0.0 | - equivalent 1.0.2 | - errno 0.3.14
|
||||
- fastrand 2.3.0 | - find-msvc-tools 0.1.5 | - fluent 0.17.0
|
||||
- fluent-bundle 0.16.0 | - fluent-langneg 0.13.1 | - fluent-syntax 0.12.0
|
||||
- fnv 1.0.7 | - form_urlencoded 1.2.2 | - futures 0.3.31
|
||||
- futures-channel 0.3.31 | - futures-core 0.3.31 | - futures-executor 0.3.31
|
||||
- futures-io 0.3.31 | - futures-macro 0.3.31 | - futures-sink 0.3.31
|
||||
- futures-task 0.3.31 | - futures-util 0.3.31 | - getrandom 0.2.16
|
||||
- getrandom 0.3.4 | - hashbrown 0.15.5 | - hashbrown 0.16.1
|
||||
- heck 0.5.0 | - hermit-abi 0.1.19 | - http 1.4.0
|
||||
- httparse 1.10.1 | - httpdate 1.0.3 | - humansize 2.1.3
|
||||
- iana-time-zone 0.1.64 | - iana-time-zone-haiku 0.1.2 | - ident_case 1.0.1
|
||||
- indexmap 2.12.1 | - indoc 2.0.7 | - intl-memoizer 0.5.3
|
||||
- intl_pluralrules 7.0.2 | - is_terminal_polyfill 1.70.2 | - itertools 0.13.0
|
||||
- itoa 1.0.15 | - js-sys 0.3.83 | - lazy_static 1.5.0
|
||||
- libc 0.2.178 | - litrs 1.0.0 | - lock_api 0.4.14
|
||||
- log 0.4.29 | - mime 0.3.17 | - num-traits 0.2.19
|
||||
- once_cell 1.21.3 | - once_cell_polyfill 1.70.2 | - parking_lot 0.12.5
|
||||
- parking_lot_core 0.9.12 | - paste 1.0.15 | - percent-encoding 2.3.2
|
||||
- pest 2.8.4 | - pest_derive 2.8.4 | - pest_generator 2.8.4
|
||||
- pest_meta 2.8.4 | - pin-project-lite 0.2.16 | - pin-utils 0.1.0
|
||||
- ppv-lite86 0.2.21 | - proc-macro2 1.0.103 | - quote 1.0.42
|
||||
- rand 0.8.5 | - rand_chacha 0.3.1 | - rand_core 0.6.4
|
||||
- regex 1.12.2 | - regex-automata 0.4.13 | - regex-syntax 0.8.8
|
||||
- rustc-hash 2.1.1 | - rustc_version 0.4.1 | - rustversion 1.0.22
|
||||
- scopeguard 1.2.0 | - semver 1.0.27 | - serde 1.0.228
|
||||
- serde_core 1.0.228 | - serde_derive 1.0.228 | - serde_json 1.0.145
|
||||
- serde_path_to_error 0.1.20 | - serde_spanned 1.0.3 | - serde_urlencoded 0.7.1
|
||||
- serde_yaml 0.9.34+deprecated | - sha2 0.10.9 | - shell-words 1.1.0
|
||||
- shlex 1.3.0 | - signal-hook 0.3.18 | - signal-hook-mio 0.2.5
|
||||
- signal-hook-registry 1.4.7 | - siphasher 1.0.1 | - slug 0.1.6
|
||||
- smallvec 1.15.1 | - socket2 0.6.1 | - static_assertions 1.1.0
|
||||
- syn 2.0.111 | - sys-locale 0.3.2 | - tempfile 3.23.0
|
||||
- thiserror 2.0.17 | - thiserror-impl 2.0.17 | - thread_local 1.1.9
|
||||
- toml 0.9.8 | - toml_datetime 0.7.3 | - toml_parser 1.0.4
|
||||
- toml_writer 1.0.4 | - type-map 0.5.1 | - typenum 1.19.0
|
||||
- ucd-trie 0.1.7 | - unic-langid 0.9.6 | - unic-langid-impl 0.9.6
|
||||
- unicase 2.8.1 | - unicode-segmentation 1.12.0 | - unicode-truncate 1.1.0
|
||||
- unicode-width 0.1.14 | - unicode-width 0.2.0 | - utf8parse 0.2.2
|
||||
- version_check 0.9.5 | - wasm-bindgen 0.2.106 | - wasm-bindgen-macro 0.2.106
|
||||
- wasm-bindgen-macro-support 0.2.106 | - wasm-bindgen-shared 0.2.106 | - winapi 0.3.9
|
||||
- winapi-i686-pc-windows-gnu 0.4.0 | - winapi-x86_64-pc-windows-gnu 0.4.0 | - windows-core 0.62.2
|
||||
- windows-implement 0.60.2 | - windows-interface 0.59.3 | - windows-link 0.2.1
|
||||
- windows-result 0.4.1 | - windows-strings 0.5.1 | - windows-sys 0.52.0
|
||||
- windows-sys 0.59.0 | - windows-sys 0.60.2 | - windows-sys 0.61.2
|
||||
- windows-targets 0.52.6 | - windows-targets 0.53.5 | - windows_aarch64_gnullvm 0.52.6
|
||||
- windows_aarch64_gnullvm 0.53.1 | - windows_aarch64_msvc 0.52.6 | - windows_aarch64_msvc 0.53.1
|
||||
- windows_i686_gnu 0.52.6 | - windows_i686_gnu 0.53.1 | - windows_i686_gnullvm 0.52.6
|
||||
- windows_i686_gnullvm 0.53.1 | - windows_i686_msvc 0.52.6 | - windows_i686_msvc 0.53.1
|
||||
- windows_x86_64_gnu 0.52.6 | - windows_x86_64_gnu 0.53.1 | - windows_x86_64_gnullvm 0.52.6
|
||||
- windows_x86_64_gnullvm 0.53.1 | - windows_x86_64_msvc 0.52.6 | - windows_x86_64_msvc 0.53.1
|
||||
- zeroize 1.8.2 |
|
||||
|
||||
### MIT OR Unlicense (7)
|
||||
- aho-corasick 1.1.4
|
||||
- globset 0.4.18
|
||||
- ignore 0.4.25
|
||||
- memchr 2.7.6
|
||||
- same-file 1.0.6
|
||||
- walkdir 2.5.0
|
||||
- winapi-util 0.1.11
|
||||
|
||||
### Other Licenses
|
||||
|
||||
**(Apache-2.0 OR MIT) AND Unicode-3.0** (1)
|
||||
- unicode-ident 1.0.22
|
||||
|
||||
**Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR MIT** (7)
|
||||
- linux-raw-sys 0.4.15
|
||||
- linux-raw-sys 0.11.0
|
||||
- rustix 0.38.44
|
||||
- rustix 1.1.2
|
||||
- wasi 0.11.1+wasi-snapshot-preview1
|
||||
- wasip2 1.0.1+wasi-0.2.4
|
||||
- wit-bindgen 0.46.0
|
||||
|
||||
**Apache-2.0 OR BSD-2-Clause OR MIT** (2)
|
||||
- zerocopy 0.8.31
|
||||
- zerocopy-derive 0.8.31
|
||||
|
||||
**Apache-2.0 OR BSL-1.0** (1)
|
||||
- ryu 1.0.20
|
||||
|
||||
**Apache-2.0 OR GPL-2.0** (1)
|
||||
- self_cell 1.2.1
|
||||
|
||||
**Apache-2.0 OR LGPL-2.1-or-later OR MIT** (1)
|
||||
- r-efi 5.3.0
|
||||
|
||||
**BSD-3-Clause** (1)
|
||||
- deunicode 1.6.2
|
||||
|
||||
**BSD-3-Clause AND MIT** (1)
|
||||
- matchit 0.8.4
|
||||
|
||||
**MPL-2.0** (1)
|
||||
- option-ext 0.2.0
|
||||
|
||||
**Unicode-3.0** (3)
|
||||
- tinystr 0.8.2
|
||||
- zerofrom 0.1.6
|
||||
- zerovec 0.11.5
|
||||
|
||||
**Zlib** (1)
|
||||
- foldhash 0.1.5
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
- **Project License**: MIT
|
||||
- **Total Dependencies**: 286
|
||||
- **Unique License Types**: 15 different combinations
|
||||
- **Primary License Pattern**: Apache-2.0 OR MIT (most dependencies)
|
||||
|
||||
### Compliance
|
||||
|
||||
All dependencies are compatible with the MIT license under:
|
||||
- Permissive licenses (MIT, Apache-2.0, BSD-3-Clause, MPL-2.0, Zlib)
|
||||
- Weak copyleft (LGPL-2.1-or-later, MPL-2.0)
|
||||
- Public domain (Unlicense, Unicode-3.0)
|
||||
|
||||
### Generated
|
||||
|
||||
- Date: 2025-12-17T13:08:58.510231
|
||||
- Tool: cargo-license
|
||||
|
||||
See [DEPENDENCIES.md](DEPENDENCIES.md) for the complete dependency tree.
|
||||
358
config/README.md
Normal file
358
config/README.md
Normal file
@ -0,0 +1,358 @@
|
||||
# Configuration Files
|
||||
|
||||
Pre-configured settings for each typedialog backend and environment.
|
||||
|
||||
## Overview
|
||||
|
||||
Configuration files are organized by **backend** (CLI, TUI, Web) and **environment** (default, dev, production).
|
||||
|
||||
```
|
||||
config/
|
||||
├── cli/
|
||||
│ ├── default.toml # Standard CLI settings
|
||||
│ ├── dev.toml # Development (debugging enabled)
|
||||
│ └── production.toml # Production (optimized, hardened)
|
||||
├── tui/
|
||||
│ ├── default.toml # Standard TUI settings
|
||||
│ ├── dev.toml # Development features enabled
|
||||
│ └── production.toml # Optimized for deployment
|
||||
└── web/
|
||||
├── default.toml # Standard web server settings
|
||||
├── dev.toml # Development (hot reload)
|
||||
└── production.toml # Hardened for production
|
||||
```
|
||||
|
||||
## Backend Configurations
|
||||
|
||||
### CLI Backend
|
||||
|
||||
**command-line interface** - Simple text-based forms for scripts and automation.
|
||||
|
||||
| Config | Purpose | Debug | Colors | Timeout |
|
||||
|--------|---------|-------|--------|---------|
|
||||
| default | Standard | No | Yes | 300s |
|
||||
| dev | Development | Yes | Yes | 300s |
|
||||
| production | Production | No | Yes | 3600s |
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
typedialog --config config/cli/production.toml form.toml
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Inline validation
|
||||
- Optional mouse support
|
||||
- Placeholder text display
|
||||
- Help text display
|
||||
|
||||
### TUI Backend
|
||||
|
||||
**terminal user interface** - Interactive multi-panel forms with navigation.
|
||||
|
||||
| Config | Purpose | Borders | Animations | Render |
|
||||
|--------|---------|---------|-----------|--------|
|
||||
| default | Standard | Rounded | Enabled | Auto |
|
||||
| dev | Development | Double | Enabled | Debug |
|
||||
| production | Production | Rounded | Disabled | Optimized |
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
typedialog-tui --config config/tui/production.toml form.toml
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- 3-panel layout (fields, input, buttons)
|
||||
- Mouse support
|
||||
- Keyboard navigation
|
||||
- Real-time field updates
|
||||
|
||||
### Web Backend
|
||||
|
||||
**HTTP server** - Browser-based forms with REST API.
|
||||
|
||||
| Config | Purpose | CORS | HTTPS | Rate Limit |
|
||||
|--------|---------|------|-------|-----------|
|
||||
| default | Standard | Localhost | No | Unlimited |
|
||||
| dev | Development | Enabled | No | Unlimited |
|
||||
| production | Production | Restricted | Required | 100/min |
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
typedialog-web --config config/web/production.toml
|
||||
# Server starts on http://localhost:8080
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- HTML/CSS rendering
|
||||
- CSRF protection
|
||||
- Response caching
|
||||
- Gzip compression
|
||||
|
||||
## Configuration by Environment
|
||||
|
||||
### Development Configuration
|
||||
|
||||
Enabled features for development and debugging:
|
||||
|
||||
```toml
|
||||
# CLI Dev
|
||||
debug_output = true
|
||||
log_level = "debug"
|
||||
show_field_types = true
|
||||
|
||||
# TUI Dev
|
||||
show_field_indices = true
|
||||
trace_rendering = false
|
||||
|
||||
# Web Dev
|
||||
hot_reload = true
|
||||
debug = true
|
||||
logs = "/tmp/typedialog-web.log"
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
typedialog --config config/cli/dev.toml form.toml
|
||||
typedialog-tui --config config/tui/dev.toml form.toml
|
||||
typedialog-web --config config/web/dev.toml
|
||||
```
|
||||
|
||||
### Production Configuration
|
||||
|
||||
Hardened settings optimized for deployment:
|
||||
|
||||
```toml
|
||||
# CLI Production
|
||||
debug_output = false
|
||||
log_level = "error"
|
||||
show_placeholders = false
|
||||
timeout = 3600
|
||||
|
||||
# TUI Production
|
||||
enable_animations = false
|
||||
render_throttle = 16ms
|
||||
max_fps = 60
|
||||
|
||||
# Web Production
|
||||
require_https = true
|
||||
csrf_enabled = true
|
||||
rate_limit = 100
|
||||
cache_ttl = 3600
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
typedialog --config config/cli/production.toml form.toml
|
||||
typedialog-tui --config config/tui/production.toml form.toml
|
||||
typedialog-web --config config/web/production.toml
|
||||
```
|
||||
|
||||
## Common Settings
|
||||
|
||||
### Form Configuration
|
||||
|
||||
```toml
|
||||
[form]
|
||||
title = "Form Title"
|
||||
description = "Optional description"
|
||||
|
||||
[form.validation]
|
||||
validate_on_change = true
|
||||
show_errors_inline = true
|
||||
strict_validation = true
|
||||
```
|
||||
|
||||
### Output Configuration
|
||||
|
||||
```toml
|
||||
[output]
|
||||
format = "json" # json, yaml, toml, text
|
||||
pretty_print = true
|
||||
debug_output = false
|
||||
```
|
||||
|
||||
### Logging
|
||||
|
||||
```toml
|
||||
[logging]
|
||||
level = "info" # debug, info, warn, error
|
||||
file = "/var/log/typedialog/app.log"
|
||||
```
|
||||
|
||||
## Custom Configuration
|
||||
|
||||
### Creating Custom Configurations
|
||||
|
||||
Create a new TOML file based on an environment template:
|
||||
|
||||
```bash
|
||||
# Copy production config as base
|
||||
cp config/cli/production.toml config/cli/custom.toml
|
||||
|
||||
# Edit for your needs
|
||||
nano config/cli/custom.toml
|
||||
|
||||
# Use it
|
||||
typedialog --config config/cli/custom.toml form.toml
|
||||
```
|
||||
|
||||
### Override Specific Settings
|
||||
|
||||
Use environment variables to override config:
|
||||
|
||||
```bash
|
||||
# CLI Backend
|
||||
export TYPEDIALOG_DEBUG=1
|
||||
export TYPEDIALOG_LOG_LEVEL=debug
|
||||
typedialog --config config/cli/default.toml form.toml
|
||||
|
||||
# TUI Backend
|
||||
export TYPEDIALOG_TUI_BORDER=double
|
||||
typedialog-tui --config config/tui/default.toml form.toml
|
||||
|
||||
# Web Backend
|
||||
export TYPEDIALOG_WEB_PORT=3000
|
||||
export TYPEDIALOG_WEB_CORS_ORIGINS="localhost,example.com"
|
||||
typedialog-web --config config/web/default.toml
|
||||
```
|
||||
|
||||
## CLI Backend Configuration Details
|
||||
|
||||
```toml
|
||||
[terminal]
|
||||
use_raw_mode = true # Enable raw terminal mode
|
||||
enable_mouse = false # Mouse support
|
||||
use_color = true # Colored output
|
||||
|
||||
[appearance]
|
||||
theme = "default" # Color theme
|
||||
show_help = true # Display field help
|
||||
show_placeholders = true # Show placeholder text
|
||||
|
||||
[validation]
|
||||
validate_on_change = true # Real-time validation
|
||||
show_errors_inline = true # Inline error messages
|
||||
|
||||
[timeout]
|
||||
max_duration = 3600 # Max form time (seconds)
|
||||
input_timeout = 300 # Field input timeout
|
||||
```
|
||||
|
||||
## TUI Backend Configuration Details
|
||||
|
||||
```toml
|
||||
[terminal]
|
||||
use_raw_mode = true
|
||||
enable_mouse = true
|
||||
enable_scrolling = true
|
||||
height = -1 # -1 = auto
|
||||
width = -1 # -1 = auto
|
||||
|
||||
[ui]
|
||||
show_borders = true
|
||||
show_focus = true
|
||||
highlight_on_hover = true
|
||||
enable_animations = true
|
||||
|
||||
[appearance]
|
||||
theme = "default"
|
||||
border_style = "rounded" # rounded, double, simple
|
||||
color_scheme = "default"
|
||||
|
||||
[keyboard]
|
||||
vi_mode = false
|
||||
emacs_mode = false
|
||||
|
||||
[performance]
|
||||
render_throttle = 16 # milliseconds
|
||||
max_fps = 60 # frames per second
|
||||
```
|
||||
|
||||
## Web Backend Configuration Details
|
||||
|
||||
```toml
|
||||
[server]
|
||||
host = "0.0.0.0"
|
||||
port = 8080
|
||||
workers = 4
|
||||
|
||||
[html]
|
||||
css_framework = "none" # bootstrap, tailwind, none
|
||||
inline_styles = false
|
||||
responsive = true
|
||||
dark_mode = true
|
||||
|
||||
[submission]
|
||||
method = "post"
|
||||
webhook_url = "https://api.example.com/forms"
|
||||
redirect_on_success = true
|
||||
redirect_url = "https://example.com/thank-you"
|
||||
|
||||
[security]
|
||||
csrf_enabled = true
|
||||
rate_limit = 100 # requests per minute
|
||||
require_https = true
|
||||
add_security_headers = true
|
||||
|
||||
[performance]
|
||||
cache_static = true
|
||||
cache_ttl = 3600
|
||||
enable_compression = true
|
||||
compression_threshold = 1024
|
||||
```
|
||||
|
||||
## Distribution Configurations
|
||||
|
||||
When creating release distributions, all configurations are included:
|
||||
|
||||
```bash
|
||||
# Build and package
|
||||
just distro::build-release
|
||||
just distro::create-package
|
||||
|
||||
# Result includes all configs
|
||||
distribution/typedialog-0.1.0/
|
||||
├── config/
|
||||
│ ├── cli/
|
||||
│ ├── tui/
|
||||
│ └── web/
|
||||
└── ...
|
||||
```
|
||||
|
||||
Users can then choose configs during installation:
|
||||
|
||||
```bash
|
||||
# Extract distribution
|
||||
tar -xzf typedialog-0.1.0.tar.gz
|
||||
cd typedialog-0.1.0
|
||||
|
||||
# Install
|
||||
bash installers/install.sh
|
||||
|
||||
# Use specific config
|
||||
typedialog --config ~/.config/typedialog/cli/production.toml form.toml
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Development
|
||||
- Use `dev` configurations for local development
|
||||
- Enable debugging and verbose logging
|
||||
- Use shorter timeouts for faster iteration
|
||||
|
||||
### Testing
|
||||
- Use `default` configurations for testing
|
||||
- Run integration tests with all environments
|
||||
|
||||
### Production
|
||||
- Always use `production` configurations
|
||||
- Verify HTTPS is enabled (web backend)
|
||||
- Set appropriate rate limits
|
||||
- Configure logging to persistent location
|
||||
- Test thoroughly in staging first
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [BUILD_AND_DISTRIBUTION.md](../BUILD_AND_DISTRIBUTION.md) - Build guide
|
||||
- [DISTRIBUTION_WORKFLOW.md](../DISTRIBUTION_WORKFLOW.md) - Release workflow
|
||||
- [README.md](../README.md) - Main documentation
|
||||
30
config/cli/default.toml
Normal file
30
config/cli/default.toml
Normal file
@ -0,0 +1,30 @@
|
||||
# CLI Backend - Default Configuration
|
||||
# Used for standard command-line form rendering
|
||||
|
||||
[form]
|
||||
title = "CLI Form"
|
||||
description = "Standard command-line interface form"
|
||||
|
||||
[form.validation]
|
||||
validate_on_change = true
|
||||
show_errors_inline = true
|
||||
|
||||
[output]
|
||||
format = "json"
|
||||
pretty_print = true
|
||||
|
||||
[terminal]
|
||||
# Use raw mode for better terminal control
|
||||
use_raw_mode = true
|
||||
# Enable mouse support if available
|
||||
enable_mouse = false
|
||||
# Use color output
|
||||
use_color = true
|
||||
|
||||
[appearance]
|
||||
# Theme: default, monochrome, dark
|
||||
theme = "default"
|
||||
# Show field help text
|
||||
show_help = true
|
||||
# Show field placeholders
|
||||
show_placeholders = true
|
||||
32
config/cli/dev.toml
Normal file
32
config/cli/dev.toml
Normal file
@ -0,0 +1,32 @@
|
||||
# CLI Backend - Development Configuration
|
||||
# Extended configuration for development and testing
|
||||
|
||||
[form]
|
||||
title = "CLI Form (Dev)"
|
||||
description = "Development CLI form with debugging enabled"
|
||||
|
||||
[form.validation]
|
||||
validate_on_change = true
|
||||
show_errors_inline = true
|
||||
strict_validation = true
|
||||
|
||||
[output]
|
||||
format = "json"
|
||||
pretty_print = true
|
||||
debug_output = true
|
||||
|
||||
[terminal]
|
||||
use_raw_mode = true
|
||||
enable_mouse = true
|
||||
use_color = true
|
||||
|
||||
[appearance]
|
||||
theme = "default"
|
||||
show_help = true
|
||||
show_placeholders = true
|
||||
show_field_types = true
|
||||
|
||||
[debug]
|
||||
enabled = true
|
||||
trace_execution = false
|
||||
log_level = "info"
|
||||
37
config/cli/production.toml
Normal file
37
config/cli/production.toml
Normal file
@ -0,0 +1,37 @@
|
||||
# CLI Backend - Production Configuration
|
||||
# Optimized for production deployment
|
||||
|
||||
[form]
|
||||
title = "Form"
|
||||
description = ""
|
||||
|
||||
[form.validation]
|
||||
validate_on_change = true
|
||||
show_errors_inline = true
|
||||
strict_validation = true
|
||||
|
||||
[output]
|
||||
format = "json"
|
||||
pretty_print = false
|
||||
# Suppress debugging info
|
||||
debug_output = false
|
||||
|
||||
[terminal]
|
||||
use_raw_mode = true
|
||||
enable_mouse = false
|
||||
use_color = true
|
||||
|
||||
[appearance]
|
||||
theme = "default"
|
||||
show_help = false
|
||||
show_placeholders = false
|
||||
|
||||
[logging]
|
||||
level = "error"
|
||||
file = "/var/log/typedialog/cli.log"
|
||||
|
||||
[timeout]
|
||||
# Maximum form completion time (seconds)
|
||||
max_duration = 3600
|
||||
# Field input timeout
|
||||
input_timeout = 300
|
||||
46
config/tui/default.toml
Normal file
46
config/tui/default.toml
Normal file
@ -0,0 +1,46 @@
|
||||
# TUI Backend - Default Configuration
|
||||
# Terminal User Interface rendering
|
||||
|
||||
[form]
|
||||
title = "TUI Form"
|
||||
description = "Interactive terminal user interface form"
|
||||
|
||||
[form.validation]
|
||||
validate_on_change = true
|
||||
show_errors_inline = true
|
||||
|
||||
[output]
|
||||
format = "json"
|
||||
pretty_print = true
|
||||
|
||||
[terminal]
|
||||
# Full TUI features
|
||||
use_raw_mode = true
|
||||
enable_mouse = true
|
||||
enable_scrolling = true
|
||||
# Fixed height (-1 = auto)
|
||||
height = -1
|
||||
# Fixed width (-1 = auto)
|
||||
width = -1
|
||||
|
||||
[ui]
|
||||
# Show field borders
|
||||
show_borders = true
|
||||
# Show field focus indicator
|
||||
show_focus = true
|
||||
# Highlight on hover
|
||||
highlight_on_hover = true
|
||||
# Animation enabled
|
||||
enable_animations = true
|
||||
|
||||
[appearance]
|
||||
theme = "default"
|
||||
border_style = "rounded"
|
||||
# Color scheme: default, dark, light, high_contrast
|
||||
color_scheme = "default"
|
||||
|
||||
[keyboard]
|
||||
# Vi-style navigation (hjkl)
|
||||
vi_mode = false
|
||||
# Emacs-style navigation
|
||||
emacs_mode = false
|
||||
45
config/tui/dev.toml
Normal file
45
config/tui/dev.toml
Normal file
@ -0,0 +1,45 @@
|
||||
# TUI Backend - Development Configuration
|
||||
# Extended TUI features for development
|
||||
|
||||
[form]
|
||||
title = "TUI Form (Dev)"
|
||||
description = "Development TUI form with all features enabled"
|
||||
|
||||
[form.validation]
|
||||
validate_on_change = true
|
||||
show_errors_inline = true
|
||||
strict_validation = true
|
||||
|
||||
[output]
|
||||
format = "json"
|
||||
pretty_print = true
|
||||
debug_output = true
|
||||
|
||||
[terminal]
|
||||
use_raw_mode = true
|
||||
enable_mouse = true
|
||||
enable_scrolling = true
|
||||
height = -1
|
||||
width = -1
|
||||
|
||||
[ui]
|
||||
show_borders = true
|
||||
show_focus = true
|
||||
highlight_on_hover = true
|
||||
enable_animations = true
|
||||
# Show field indices for debugging
|
||||
show_field_indices = true
|
||||
|
||||
[appearance]
|
||||
theme = "default"
|
||||
border_style = "double"
|
||||
color_scheme = "default"
|
||||
|
||||
[keyboard]
|
||||
vi_mode = false
|
||||
emacs_mode = false
|
||||
|
||||
[debug]
|
||||
enabled = true
|
||||
log_level = "debug"
|
||||
trace_rendering = false
|
||||
48
config/tui/production.toml
Normal file
48
config/tui/production.toml
Normal file
@ -0,0 +1,48 @@
|
||||
# TUI Backend - Production Configuration
|
||||
# Optimized TUI for production deployment
|
||||
|
||||
[form]
|
||||
title = ""
|
||||
description = ""
|
||||
|
||||
[form.validation]
|
||||
validate_on_change = true
|
||||
show_errors_inline = true
|
||||
strict_validation = true
|
||||
|
||||
[output]
|
||||
format = "json"
|
||||
pretty_print = false
|
||||
debug_output = false
|
||||
|
||||
[terminal]
|
||||
use_raw_mode = true
|
||||
enable_mouse = true
|
||||
enable_scrolling = true
|
||||
height = -1
|
||||
width = -1
|
||||
|
||||
[ui]
|
||||
show_borders = true
|
||||
show_focus = true
|
||||
highlight_on_hover = true
|
||||
enable_animations = false
|
||||
|
||||
[appearance]
|
||||
theme = "default"
|
||||
border_style = "rounded"
|
||||
color_scheme = "default"
|
||||
|
||||
[keyboard]
|
||||
vi_mode = false
|
||||
emacs_mode = false
|
||||
|
||||
[logging]
|
||||
level = "error"
|
||||
file = "/var/log/typedialog/tui.log"
|
||||
|
||||
[performance]
|
||||
# Render throttle (milliseconds)
|
||||
render_throttle = 16
|
||||
# Max update frequency (Hz)
|
||||
max_fps = 60
|
||||
48
config/web/default.toml
Normal file
48
config/web/default.toml
Normal file
@ -0,0 +1,48 @@
|
||||
# Web Backend - Default Configuration
|
||||
# HTTP server and web form rendering
|
||||
|
||||
[server]
|
||||
host = "localhost"
|
||||
port = 3000
|
||||
# CORS settings
|
||||
cors_enabled = true
|
||||
cors_origins = ["localhost", "127.0.0.1"]
|
||||
|
||||
[form]
|
||||
title = "Web Form"
|
||||
description = "Interactive web form"
|
||||
|
||||
[form.validation]
|
||||
validate_on_change = true
|
||||
show_errors_inline = true
|
||||
client_validation = true
|
||||
|
||||
[output]
|
||||
format = "json"
|
||||
|
||||
[html]
|
||||
# CSS framework: bootstrap, tailwind, none
|
||||
css_framework = "none"
|
||||
# Include inline styles
|
||||
inline_styles = false
|
||||
# Mobile responsive
|
||||
responsive = true
|
||||
# Dark mode support
|
||||
dark_mode = true
|
||||
|
||||
[submission]
|
||||
# Submission method: post, put, patch
|
||||
method = "post"
|
||||
# Optional webhook URL for submissions
|
||||
webhook_url = ""
|
||||
# Redirect after submission
|
||||
redirect_on_success = false
|
||||
redirect_url = ""
|
||||
|
||||
[security]
|
||||
# CSRF protection
|
||||
csrf_enabled = true
|
||||
# Rate limiting (requests per minute)
|
||||
rate_limit = 0
|
||||
# Require HTTPS
|
||||
require_https = false
|
||||
50
config/web/dev.toml
Normal file
50
config/web/dev.toml
Normal file
@ -0,0 +1,50 @@
|
||||
# Web Backend - Development Configuration
|
||||
# Enhanced features for development
|
||||
|
||||
[server]
|
||||
host = "0.0.0.0"
|
||||
port = 3000
|
||||
# Enable hot reload
|
||||
hot_reload = true
|
||||
# Debug mode
|
||||
debug = true
|
||||
|
||||
[form]
|
||||
title = "Web Form (Dev)"
|
||||
description = "Development web form"
|
||||
|
||||
[form.validation]
|
||||
validate_on_change = true
|
||||
show_errors_inline = true
|
||||
client_validation = true
|
||||
|
||||
[output]
|
||||
format = "json"
|
||||
|
||||
[html]
|
||||
css_framework = "none"
|
||||
inline_styles = true
|
||||
responsive = true
|
||||
dark_mode = true
|
||||
# Show field metadata
|
||||
show_field_metadata = true
|
||||
|
||||
[submission]
|
||||
method = "post"
|
||||
webhook_url = "http://localhost:8000/webhook"
|
||||
redirect_on_success = false
|
||||
log_submissions = true
|
||||
|
||||
[security]
|
||||
csrf_enabled = true
|
||||
rate_limit = 0
|
||||
require_https = false
|
||||
|
||||
[logging]
|
||||
level = "debug"
|
||||
file = "/tmp/typedialog-web.log"
|
||||
|
||||
[api]
|
||||
# API documentation enabled
|
||||
enable_docs = true
|
||||
docs_path = "/docs"
|
||||
65
config/web/production.toml
Normal file
65
config/web/production.toml
Normal file
@ -0,0 +1,65 @@
|
||||
# Web Backend - Production Configuration
|
||||
# Hardened configuration for production deployment
|
||||
|
||||
[server]
|
||||
host = "0.0.0.0"
|
||||
port = 8080
|
||||
# Disable development features
|
||||
hot_reload = false
|
||||
debug = false
|
||||
# Worker threads
|
||||
workers = 4
|
||||
|
||||
[form]
|
||||
title = ""
|
||||
description = ""
|
||||
|
||||
[form.validation]
|
||||
validate_on_change = true
|
||||
show_errors_inline = true
|
||||
client_validation = true
|
||||
|
||||
[output]
|
||||
format = "json"
|
||||
|
||||
[html]
|
||||
css_framework = "none"
|
||||
inline_styles = false
|
||||
responsive = true
|
||||
dark_mode = true
|
||||
|
||||
[submission]
|
||||
method = "post"
|
||||
# Required: webhook for production submissions
|
||||
webhook_url = "https://api.example.com/forms"
|
||||
redirect_on_success = true
|
||||
redirect_url = "https://example.com/thank-you"
|
||||
|
||||
[security]
|
||||
# Strict CSRF protection
|
||||
csrf_enabled = true
|
||||
# Rate limiting: requests per minute per IP
|
||||
rate_limit = 100
|
||||
# Require HTTPS
|
||||
require_https = true
|
||||
# Security headers
|
||||
add_security_headers = true
|
||||
|
||||
[logging]
|
||||
level = "error"
|
||||
file = "/var/log/typedialog/web.log"
|
||||
|
||||
[performance]
|
||||
# Cache static assets
|
||||
cache_static = true
|
||||
cache_ttl = 3600
|
||||
# Enable gzip compression
|
||||
enable_compression = true
|
||||
# Minimum response size for compression (bytes)
|
||||
compression_threshold = 1024
|
||||
|
||||
[tls]
|
||||
# Optional TLS configuration
|
||||
enabled = false
|
||||
cert_path = "/etc/typedialog/cert.pem"
|
||||
key_path = "/etc/typedialog/key.pem"
|
||||
183
installers/bootstrap/README.md
Normal file
183
installers/bootstrap/README.md
Normal file
@ -0,0 +1,183 @@
|
||||
# TypeDialog Bootstrap Installers
|
||||
|
||||
Quick installation scripts for all platforms.
|
||||
|
||||
## Linux & macOS
|
||||
|
||||
Install with `curl`:
|
||||
```bash
|
||||
curl -fsSL https://github.com/anthropics/typedialog/releases/download/latest/install.sh | bash
|
||||
```
|
||||
|
||||
Or with `wget`:
|
||||
```bash
|
||||
wget -qO - https://github.com/anthropics/typedialog/releases/download/latest/install.sh | bash
|
||||
```
|
||||
|
||||
## Windows
|
||||
|
||||
Open PowerShell and run:
|
||||
```powershell
|
||||
irm https://github.com/anthropics/typedialog/releases/download/latest/install.ps1 | iex
|
||||
```
|
||||
|
||||
## Manual Installation
|
||||
|
||||
### Linux & macOS
|
||||
|
||||
```bash
|
||||
# Download script
|
||||
wget https://github.com/anthropics/typedialog/releases/download/latest/install.sh
|
||||
|
||||
# Run installer
|
||||
bash install.sh
|
||||
|
||||
# Or with environment variables
|
||||
INSTALL_DIR="$HOME/bin" CONFIG_DIR="$HOME/.typedialog" bash install.sh
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
```powershell
|
||||
# Download script
|
||||
Invoke-WebRequest -Uri "https://github.com/anthropics/typedialog/releases/download/latest/install.ps1" -OutFile "install.ps1"
|
||||
|
||||
# Run installer
|
||||
.\install.ps1
|
||||
|
||||
# Or with parameters
|
||||
.\install.ps1 -InstallDir "C:\Program Files\typedialog" -ConfigDir "C:\Users\$env:USERNAME\AppData\Local\typedialog"
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Linux & macOS Environment Variables
|
||||
|
||||
- `INSTALL_DIR` - Installation directory (default: `$HOME/.local/bin`)
|
||||
- `CONFIG_DIR` - Configuration directory (default: `$HOME/.config/typedialog`)
|
||||
- `VERSION` - Release version (default: `latest`)
|
||||
|
||||
### Windows Parameters
|
||||
|
||||
- `-InstallDir` - Installation directory (default: `$PROFILE\.local\bin`)
|
||||
- `-ConfigDir` - Configuration directory (default: `$PROFILE\.config\typedialog`)
|
||||
- `-Version` - Release version (default: `latest`)
|
||||
|
||||
## What Gets Installed
|
||||
|
||||
### Binaries
|
||||
- `typedialog` - CLI backend
|
||||
- `typedialog-tui` - TUI backend
|
||||
- `typedialog-web` - Web backend
|
||||
|
||||
### Configuration Files
|
||||
- CLI configs: `cli/default.toml`, `cli/dev.toml`, `cli/production.toml`
|
||||
- TUI configs: `tui/default.toml`, `tui/dev.toml`, `tui/production.toml`
|
||||
- Web configs: `web/default.toml`, `web/dev.toml`, `web/production.toml`
|
||||
|
||||
## Verification
|
||||
|
||||
After installation, verify everything works:
|
||||
|
||||
```bash
|
||||
# Check CLI backend
|
||||
typedialog --version
|
||||
|
||||
# Check TUI backend
|
||||
typedialog-tui --version
|
||||
|
||||
# Check web backend
|
||||
typedialog-web --version
|
||||
|
||||
# List available configurations
|
||||
ls ~/.config/typedialog/
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Command not found
|
||||
|
||||
Update your PATH:
|
||||
|
||||
**bash:**
|
||||
```bash
|
||||
echo 'export PATH="$PATH:$HOME/.local/bin"' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
**zsh:**
|
||||
```bash
|
||||
echo 'export PATH="$PATH:$HOME/.local/bin"' >> ~/.zshrc
|
||||
source ~/.zshrc
|
||||
```
|
||||
|
||||
### Permission denied
|
||||
|
||||
Make binaries executable:
|
||||
```bash
|
||||
chmod +x ~/.local/bin/typedialog*
|
||||
```
|
||||
|
||||
### Configuration issues
|
||||
|
||||
Check configuration directory:
|
||||
```bash
|
||||
cat ~/.config/typedialog/cli/default.toml
|
||||
```
|
||||
|
||||
## Uninstallation
|
||||
|
||||
Remove installed files:
|
||||
|
||||
```bash
|
||||
# Remove binaries
|
||||
rm ~/.local/bin/typedialog*
|
||||
|
||||
# Remove configurations (optional)
|
||||
rm -rf ~/.config/typedialog/
|
||||
```
|
||||
|
||||
## Platform Support
|
||||
|
||||
| Platform | Status | Architecture |
|
||||
|----------|--------|--------------|
|
||||
| Linux (glibc) | ✓ | x86_64, aarch64 |
|
||||
| macOS | ✓ | x86_64, aarch64 |
|
||||
| Windows | ✓ | x86_64 |
|
||||
|
||||
## Requirements
|
||||
|
||||
### Linux & macOS
|
||||
- `curl` or `wget`
|
||||
- `tar` and `gzip`
|
||||
- Bash 4.0+
|
||||
|
||||
### Windows
|
||||
- PowerShell 3.0+
|
||||
- .NET Framework 4.5+ (usually pre-installed)
|
||||
|
||||
## Manual Build & Install
|
||||
|
||||
To build and install from source:
|
||||
|
||||
```bash
|
||||
# Clone repository
|
||||
git clone https://github.com/anthropics/typedialog.git
|
||||
cd typedialog
|
||||
|
||||
# Build release
|
||||
./scripts/build_all.sh release
|
||||
|
||||
# Create distribution
|
||||
./scripts/create_distro.sh v0.1.0
|
||||
|
||||
# Install from distribution
|
||||
tar -xzf distribution/typedialog-0.1.0.tar.gz
|
||||
cp typedialog-0.1.0/bin/* ~/.local/bin/
|
||||
cp -r typedialog-0.1.0/config/* ~/.config/typedialog/
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
- [GitHub Issues](https://github.com/anthropics/typedialog/issues)
|
||||
- [Documentation](https://github.com/anthropics/typedialog/blob/main/README.md)
|
||||
185
installers/bootstrap/install.ps1
Normal file
185
installers/bootstrap/install.ps1
Normal file
@ -0,0 +1,185 @@
|
||||
# TypeDialog Installation Script for Windows
|
||||
# Installs typedialog binaries and configuration
|
||||
|
||||
param(
|
||||
[string]$InstallDir = "$env:USERPROFILE\.local\bin",
|
||||
[string]$ConfigDir = "$env:USERPROFILE\.config\typedialog",
|
||||
[string]$Version = "latest"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$GitHubRepo = "anthropics/typedialog"
|
||||
|
||||
# Function: Print info message
|
||||
function Write-Info {
|
||||
param([string]$Message)
|
||||
Write-Host "[INFO] $Message" -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
# Function: Print error message
|
||||
function Write-Err {
|
||||
param([string]$Message)
|
||||
Write-Host "[ERROR] $Message" -ForegroundColor Red
|
||||
}
|
||||
|
||||
# Function: Print success message
|
||||
function Write-Success {
|
||||
param([string]$Message)
|
||||
Write-Host "[✓] $Message" -ForegroundColor Green
|
||||
}
|
||||
|
||||
# Function: Detect platform
|
||||
function Get-Platform {
|
||||
$arch = $env:PROCESSOR_ARCHITECTURE
|
||||
|
||||
switch ($arch) {
|
||||
"AMD64" { return "windows-x86_64" }
|
||||
"ARM64" { return "windows-aarch64" }
|
||||
default {
|
||||
Write-Err "Unsupported architecture: $arch"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Function: Create directories
|
||||
function New-DirectoryStructure {
|
||||
param([string]$InstallDir, [string]$ConfigDir)
|
||||
|
||||
Write-Info "Creating directories..."
|
||||
|
||||
if (-not (Test-Path $InstallDir)) {
|
||||
New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
|
||||
}
|
||||
|
||||
if (-not (Test-Path $ConfigDir)) {
|
||||
New-Item -ItemType Directory -Path $ConfigDir -Force | Out-Null
|
||||
}
|
||||
|
||||
Write-Success "Directories created"
|
||||
}
|
||||
|
||||
# Function: Download release
|
||||
function Get-Release {
|
||||
param([string]$Platform, [string]$Version)
|
||||
|
||||
$DownloadUrl = "https://github.com/$GitHubRepo/releases/download/$Version/typedialog-$Platform.zip"
|
||||
$TempFile = [System.IO.Path]::GetTempFileName()
|
||||
|
||||
Write-Info "Downloading: $DownloadUrl"
|
||||
|
||||
try {
|
||||
Invoke-WebRequest -Uri $DownloadUrl -OutFile $TempFile -ErrorAction Stop
|
||||
Write-Success "Download complete"
|
||||
return $TempFile
|
||||
}
|
||||
catch {
|
||||
Write-Err "Download failed: $_"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Function: Extract archive
|
||||
function Expand-Release {
|
||||
param([string]$Archive)
|
||||
|
||||
$ExtractDir = New-Item -ItemType Directory -Path (Join-Path $env:TEMP "typedialog-extract") -Force | Select-Object -ExpandProperty FullName
|
||||
|
||||
Write-Info "Extracting archive..."
|
||||
|
||||
try {
|
||||
Expand-Archive -Path $Archive -DestinationPath $ExtractDir -Force
|
||||
Write-Success "Extraction complete"
|
||||
return $ExtractDir
|
||||
}
|
||||
catch {
|
||||
Write-Err "Extraction failed: $_"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Function: Install binaries
|
||||
function Install-Binaries {
|
||||
param([string]$ExtractDir, [string]$InstallDir)
|
||||
|
||||
$Binaries = @("typedialog.exe", "typedialog-tui.exe", "typedialog-web.exe")
|
||||
$BinDir = Join-Path $ExtractDir "bin"
|
||||
|
||||
foreach ($binary in $Binaries) {
|
||||
$BinaryPath = Join-Path $BinDir $binary
|
||||
|
||||
if (Test-Path $BinaryPath) {
|
||||
Copy-Item -Path $BinaryPath -Destination $InstallDir -Force
|
||||
Write-Success "Installed: $binary"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Function: Install configurations
|
||||
function Install-Configs {
|
||||
param([string]$ExtractDir, [string]$ConfigDir)
|
||||
|
||||
$ConfigSrc = Join-Path $ExtractDir "config"
|
||||
|
||||
if (Test-Path $ConfigSrc) {
|
||||
Copy-Item -Path "$ConfigSrc\*" -Destination $ConfigDir -Recurse -Force
|
||||
Write-Success "Configurations installed"
|
||||
}
|
||||
}
|
||||
|
||||
# Function: Update PATH
|
||||
function Update-Path {
|
||||
param([string]$InstallDir)
|
||||
|
||||
$CurrentPath = [Environment]::GetEnvironmentVariable("PATH", "User")
|
||||
|
||||
if ($CurrentPath -notlike "*$InstallDir*") {
|
||||
$NewPath = "$CurrentPath;$InstallDir"
|
||||
[Environment]::SetEnvironmentVariable("PATH", $NewPath, "User")
|
||||
Write-Info "Updated PATH with $InstallDir"
|
||||
}
|
||||
}
|
||||
|
||||
# Function: Verify installation
|
||||
function Test-Installation {
|
||||
$ExePath = Join-Path $env:USERPROFILE ".local\bin\typedialog.exe"
|
||||
|
||||
if (Test-Path $ExePath) {
|
||||
Write-Success "TypeDialog installed successfully"
|
||||
return $true
|
||||
}
|
||||
else {
|
||||
Write-Err "Installation verification failed"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
# Main entry point
|
||||
function Main {
|
||||
Write-Host "TypeDialog Installation" -ForegroundColor Yellow
|
||||
Write-Host "=======================" -ForegroundColor Yellow
|
||||
|
||||
$Platform = Get-Platform
|
||||
Write-Info "Detected platform: $Platform"
|
||||
|
||||
New-DirectoryStructure -InstallDir $InstallDir -ConfigDir $ConfigDir
|
||||
|
||||
$Archive = Get-Release -Platform $Platform -Version $Version
|
||||
$ExtractDir = Expand-Release -Archive $Archive
|
||||
|
||||
Install-Binaries -ExtractDir $ExtractDir -InstallDir $InstallDir
|
||||
Install-Configs -ExtractDir $ExtractDir -ConfigDir $ConfigDir
|
||||
|
||||
Update-Path -InstallDir $InstallDir
|
||||
|
||||
if (Test-Installation) {
|
||||
Write-Success "Installation complete!"
|
||||
Write-Info "Install directory: $InstallDir"
|
||||
Write-Info "Config directory: $ConfigDir"
|
||||
}
|
||||
else {
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
Main
|
||||
225
installers/bootstrap/install.sh
Executable file
225
installers/bootstrap/install.sh
Executable file
@ -0,0 +1,225 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# TypeDialog Installation Script
|
||||
# Installs TypeDialog binaries and configuration
|
||||
|
||||
INSTALL_DIR="${INSTALL_DIR:-$HOME/.local/bin}"
|
||||
CONFIG_DIR="${CONFIG_DIR:-$HOME/.config/typedialog}"
|
||||
GITHUB_REPO="anthropics/typedialog"
|
||||
VERSION="${VERSION:-latest}"
|
||||
|
||||
# Function: Print info message
|
||||
log_info() {
|
||||
echo "[INFO] $*" >&2
|
||||
}
|
||||
|
||||
# Function: Print error message
|
||||
log_error() {
|
||||
echo "[ERROR] $*" >&2
|
||||
}
|
||||
|
||||
# Function: Print success message
|
||||
log_success() {
|
||||
echo "[✓] $*" >&2
|
||||
}
|
||||
|
||||
# Function: Detect OS and architecture
|
||||
detect_platform() {
|
||||
local os
|
||||
local arch
|
||||
|
||||
os=$(uname -s)
|
||||
arch=$(uname -m)
|
||||
|
||||
case "$os" in
|
||||
Linux*)
|
||||
os="linux"
|
||||
;;
|
||||
Darwin*)
|
||||
os="darwin"
|
||||
;;
|
||||
*)
|
||||
log_error "Unsupported OS: $os"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$arch" in
|
||||
x86_64)
|
||||
arch="x86_64"
|
||||
;;
|
||||
aarch64)
|
||||
arch="aarch64"
|
||||
;;
|
||||
arm64)
|
||||
arch="aarch64"
|
||||
;;
|
||||
*)
|
||||
log_error "Unsupported architecture: $arch"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "${os}-${arch}"
|
||||
}
|
||||
|
||||
# Function: Create directories
|
||||
create_dirs() {
|
||||
mkdir -p "$INSTALL_DIR" || {
|
||||
log_error "Failed to create install directory: $INSTALL_DIR"
|
||||
return 1
|
||||
}
|
||||
|
||||
mkdir -p "$CONFIG_DIR" || {
|
||||
log_error "Failed to create config directory: $CONFIG_DIR"
|
||||
return 1
|
||||
}
|
||||
|
||||
log_success "Directories created"
|
||||
}
|
||||
|
||||
# Function: Download release
|
||||
download_release() {
|
||||
local platform="$1"
|
||||
local download_url="https://github.com/${GITHUB_REPO}/releases/download/${VERSION}/typedialog-${platform}.tar.gz"
|
||||
local temp_file
|
||||
temp_file=$(mktemp) || return 1
|
||||
trap "rm -f '$temp_file'" EXIT
|
||||
|
||||
log_info "Downloading: $download_url"
|
||||
|
||||
if command -v curl &>/dev/null; then
|
||||
curl -fsSL "$download_url" -o "$temp_file" || {
|
||||
log_error "Download failed"
|
||||
return 1
|
||||
}
|
||||
elif command -v wget &>/dev/null; then
|
||||
wget -q "$download_url" -O "$temp_file" || {
|
||||
log_error "Download failed"
|
||||
return 1
|
||||
}
|
||||
else
|
||||
log_error "curl or wget required"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "Download complete"
|
||||
echo "$temp_file"
|
||||
}
|
||||
|
||||
# Function: Extract archive
|
||||
extract_archive() {
|
||||
local archive="$1"
|
||||
local extract_dir
|
||||
extract_dir=$(mktemp -d) || return 1
|
||||
|
||||
log_info "Extracting archive..."
|
||||
tar -xzf "$archive" -C "$extract_dir" || {
|
||||
log_error "Extraction failed"
|
||||
rm -rf "$extract_dir"
|
||||
return 1
|
||||
}
|
||||
|
||||
echo "$extract_dir"
|
||||
}
|
||||
|
||||
# Function: Install binaries
|
||||
install_binaries() {
|
||||
local extract_dir="$1"
|
||||
|
||||
for binary in typedialog typedialog-tui typedialog-web; do
|
||||
local binary_path="${extract_dir}/bin/${binary}"
|
||||
|
||||
if [[ -f "$binary_path" ]]; then
|
||||
cp "$binary_path" "${INSTALL_DIR}/${binary}" || {
|
||||
log_error "Failed to install: $binary"
|
||||
return 1
|
||||
}
|
||||
chmod +x "${INSTALL_DIR}/${binary}"
|
||||
log_success "Installed: $binary"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Function: Install configurations
|
||||
install_configs() {
|
||||
local extract_dir="$1"
|
||||
local config_src="${extract_dir}/config"
|
||||
|
||||
if [[ -d "$config_src" ]]; then
|
||||
cp -r "$config_src"/* "$CONFIG_DIR/" || {
|
||||
log_error "Failed to install configurations"
|
||||
return 1
|
||||
}
|
||||
log_success "Configurations installed"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function: Update PATH if needed
|
||||
update_path() {
|
||||
local shell_rc=""
|
||||
|
||||
if [[ "$SHELL" == *"bash"* ]]; then
|
||||
shell_rc="$HOME/.bashrc"
|
||||
elif [[ "$SHELL" == *"zsh"* ]]; then
|
||||
shell_rc="$HOME/.zshrc"
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ -f "$shell_rc" ]]; then
|
||||
if ! grep -q "$INSTALL_DIR" "$shell_rc"; then
|
||||
echo "export PATH=\"\$PATH:$INSTALL_DIR\"" >> "$shell_rc"
|
||||
log_info "Updated $shell_rc with $INSTALL_DIR"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Function: Verify installation
|
||||
verify_installation() {
|
||||
log_info "Verifying installation..."
|
||||
|
||||
if command -v typedialog &>/dev/null; then
|
||||
local version
|
||||
version=$(typedialog --version 2>/dev/null || echo "unknown")
|
||||
log_success "TypeDialog installed: $version"
|
||||
else
|
||||
log_error "Installation verification failed"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function: Main entry point
|
||||
main() {
|
||||
log_info "TypeDialog Installation"
|
||||
log_info "========================"
|
||||
|
||||
local platform
|
||||
platform=$(detect_platform) || return 1
|
||||
log_info "Detected platform: $platform"
|
||||
|
||||
create_dirs || return 1
|
||||
|
||||
local archive
|
||||
archive=$(download_release "$platform") || return 1
|
||||
|
||||
local extract_dir
|
||||
extract_dir=$(extract_archive "$archive") || return 1
|
||||
trap "rm -rf '$extract_dir'" EXIT
|
||||
|
||||
install_binaries "$extract_dir" || return 1
|
||||
install_configs "$extract_dir" || return 1
|
||||
|
||||
update_path
|
||||
|
||||
verify_installation || return 1
|
||||
|
||||
log_success "TypeDialog installation complete!"
|
||||
log_info "Install directory: $INSTALL_DIR"
|
||||
log_info "Config directory: $CONFIG_DIR"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
main "$@"
|
||||
58
scripts/build_all.sh
Executable file
58
scripts/build_all.sh
Executable file
@ -0,0 +1,58 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Description: Build all workspace targets for current platform
|
||||
# Arguments: [profile] - debug or release (default: debug)
|
||||
# Returns: 0 on success, 1 on failure
|
||||
# Output: Build progress and results
|
||||
|
||||
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
readonly WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
readonly PROFILE="${1:-debug}"
|
||||
|
||||
# Function: Print info message
|
||||
log_info() {
|
||||
echo "[INFO] $*" >&2
|
||||
}
|
||||
|
||||
# Function: Print error message
|
||||
log_error() {
|
||||
echo "[ERROR] $*" >&2
|
||||
}
|
||||
|
||||
# Function: Validate profile
|
||||
validate_profile() {
|
||||
local profile="$1"
|
||||
[[ "$profile" == "debug" || "$profile" == "release" ]] || {
|
||||
log_error "Invalid profile: $profile (must be debug or release)"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
# Function: Build workspace
|
||||
build_workspace() {
|
||||
local profile="$1"
|
||||
local cargo_args=("--workspace")
|
||||
|
||||
if [[ "$profile" == "release" ]]; then
|
||||
cargo_args+=("--release")
|
||||
fi
|
||||
|
||||
log_info "Building workspace ($profile profile)..."
|
||||
cd "$WORKSPACE_ROOT"
|
||||
cargo build "${cargo_args[@]}"
|
||||
}
|
||||
|
||||
# Function: Main entry point
|
||||
main() {
|
||||
log_info "TypeDialog Build All"
|
||||
log_info "====================="
|
||||
|
||||
validate_profile "$PROFILE" || return 1
|
||||
build_workspace "$PROFILE"
|
||||
|
||||
log_info "Build complete: $PROFILE profile"
|
||||
return 0
|
||||
}
|
||||
|
||||
main "$@"
|
||||
94
scripts/build_cross.sh
Executable file
94
scripts/build_cross.sh
Executable file
@ -0,0 +1,94 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Description: Cross-compile for multiple platforms using cargo-cross
|
||||
# Arguments: [target] - specific target (default: all targets)
|
||||
# Returns: 0 on success, 1 on failure
|
||||
# Output: Cross-compilation progress
|
||||
|
||||
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
readonly WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
readonly TARGET="${1:-}"
|
||||
|
||||
# Supported targets for cross-compilation
|
||||
declare -a TARGETS=(
|
||||
"x86_64-unknown-linux-gnu"
|
||||
"x86_64-apple-darwin"
|
||||
"aarch64-apple-darwin"
|
||||
"x86_64-pc-windows-gnu"
|
||||
)
|
||||
|
||||
# Function: Print info message
|
||||
log_info() {
|
||||
echo "[INFO] $*" >&2
|
||||
}
|
||||
|
||||
# Function: Print error message
|
||||
log_error() {
|
||||
echo "[ERROR] $*" >&2
|
||||
}
|
||||
|
||||
# Function: Check for cross tool
|
||||
check_cross() {
|
||||
command -v cross >/dev/null || {
|
||||
log_error "cross not found. Install: cargo install cross"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
# Function: Validate target
|
||||
is_valid_target() {
|
||||
local target="$1"
|
||||
for t in "${TARGETS[@]}"; do
|
||||
[[ "$t" == "$target" ]] && return 0
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# Function: Build single target
|
||||
build_target() {
|
||||
local target="$1"
|
||||
log_info "Building target: $target"
|
||||
|
||||
cd "$WORKSPACE_ROOT"
|
||||
cross build --target "$target" --release
|
||||
}
|
||||
|
||||
# Function: Build all targets
|
||||
build_all_targets() {
|
||||
local failed=0
|
||||
|
||||
for target in "${TARGETS[@]}"; do
|
||||
if ! build_target "$target"; then
|
||||
log_error "Failed to build: $target"
|
||||
((failed++))
|
||||
fi
|
||||
done
|
||||
|
||||
return $((failed > 0 ? 1 : 0))
|
||||
}
|
||||
|
||||
# Function: Main entry point
|
||||
main() {
|
||||
log_info "TypeDialog Cross-Compilation"
|
||||
log_info "=============================="
|
||||
|
||||
check_cross || return 1
|
||||
|
||||
if [[ -z "$TARGET" ]]; then
|
||||
log_info "Building all targets..."
|
||||
build_all_targets
|
||||
else
|
||||
is_valid_target "$TARGET" || {
|
||||
log_error "Invalid target: $TARGET"
|
||||
log_error "Supported: ${TARGETS[*]}"
|
||||
return 1
|
||||
}
|
||||
build_target "$TARGET"
|
||||
fi
|
||||
|
||||
log_info "Cross-compilation complete"
|
||||
return 0
|
||||
}
|
||||
|
||||
main "$@"
|
||||
195
scripts/create_distro.sh
Executable file
195
scripts/create_distro.sh
Executable file
@ -0,0 +1,195 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Description: Create distribution package with binaries and configs
|
||||
# Arguments: [version] - version string (default: from Cargo.toml)
|
||||
# Returns: 0 on success, 1 on failure
|
||||
# Output: Distribution package path
|
||||
|
||||
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
readonly WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
readonly DISTRO_DIR="${WORKSPACE_ROOT}/distribution"
|
||||
readonly BUILD_DIR="${WORKSPACE_ROOT}/target/release"
|
||||
|
||||
# Extract version from Cargo.toml
|
||||
get_version() {
|
||||
grep '^version' "${WORKSPACE_ROOT}/crates/typedialog-core/Cargo.toml" |
|
||||
head -1 |
|
||||
sed 's/.*"\([^"]*\)".*/\1/'
|
||||
}
|
||||
|
||||
# Function: Print info message
|
||||
log_info() {
|
||||
echo "[INFO] $*" >&2
|
||||
}
|
||||
|
||||
# Function: Print error message
|
||||
log_error() {
|
||||
echo "[ERROR] $*" >&2
|
||||
}
|
||||
|
||||
# Function: Validate files exist
|
||||
validate_files() {
|
||||
local missing=0
|
||||
|
||||
for file in "$@"; do
|
||||
if [[ ! -f "$file" ]]; then
|
||||
log_error "Missing: $file"
|
||||
((missing++))
|
||||
fi
|
||||
done
|
||||
|
||||
return $((missing > 0 ? 1 : 0))
|
||||
}
|
||||
|
||||
# Function: Create distribution directory
|
||||
create_distro_structure() {
|
||||
local version="$1"
|
||||
local distro_path="${DISTRO_DIR}/typedialog-${version}"
|
||||
|
||||
mkdir -p "${distro_path}"/{bin,config,docs,lib}
|
||||
|
||||
echo "$distro_path"
|
||||
}
|
||||
|
||||
# Function: Copy binaries
|
||||
copy_binaries() {
|
||||
local distro_path="$1"
|
||||
|
||||
if [[ -f "${BUILD_DIR}/typedialog" ]]; then
|
||||
cp "${BUILD_DIR}/typedialog" "${distro_path}/bin/"
|
||||
fi
|
||||
|
||||
if [[ -f "${BUILD_DIR}/typedialog-tui" ]]; then
|
||||
cp "${BUILD_DIR}/typedialog-tui" "${distro_path}/bin/"
|
||||
fi
|
||||
|
||||
if [[ -f "${BUILD_DIR}/typedialog-web" ]]; then
|
||||
cp "${BUILD_DIR}/typedialog-web" "${distro_path}/bin/"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function: Copy configurations
|
||||
copy_configs() {
|
||||
local distro_path="$1"
|
||||
|
||||
cp -r "${WORKSPACE_ROOT}/config"/* "${distro_path}/config/" || {
|
||||
log_error "Failed to copy configurations"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
# Function: Copy installers
|
||||
copy_installers() {
|
||||
local distro_path="$1"
|
||||
|
||||
mkdir -p "${distro_path}/installers"
|
||||
|
||||
if [[ -f "${WORKSPACE_ROOT}/installers/bootstrap/install.sh" ]]; then
|
||||
cp "${WORKSPACE_ROOT}/installers/bootstrap/install.sh" "${distro_path}/installers/"
|
||||
chmod +x "${distro_path}/installers/install.sh"
|
||||
fi
|
||||
|
||||
if [[ -f "${WORKSPACE_ROOT}/installers/bootstrap/install.ps1" ]]; then
|
||||
cp "${WORKSPACE_ROOT}/installers/bootstrap/install.ps1" "${distro_path}/installers/"
|
||||
fi
|
||||
|
||||
if [[ -f "${WORKSPACE_ROOT}/installers/bootstrap/README.md" ]]; then
|
||||
cp "${WORKSPACE_ROOT}/installers/bootstrap/README.md" "${distro_path}/installers/"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function: Create manifest
|
||||
create_manifest() {
|
||||
local distro_path="$1"
|
||||
local version="$2"
|
||||
local manifest="${distro_path}/MANIFEST.json"
|
||||
|
||||
cat > "$manifest" <<EOF
|
||||
{
|
||||
"name": "typedialog",
|
||||
"version": "$version",
|
||||
"created": "$(date -u +'%Y-%m-%dT%H:%M:%SZ')",
|
||||
"structure": {
|
||||
"bin": "Binary executables",
|
||||
"config": "Configuration files per backend/environment",
|
||||
"installers": "Installation scripts (Linux/macOS/Windows)",
|
||||
"docs": "Documentation"
|
||||
},
|
||||
"binaries": [
|
||||
"bin/typedialog",
|
||||
"bin/typedialog-tui",
|
||||
"bin/typedialog-web"
|
||||
],
|
||||
"configs": {
|
||||
"cli": [
|
||||
"config/cli/default.toml",
|
||||
"config/cli/dev.toml",
|
||||
"config/cli/production.toml"
|
||||
],
|
||||
"tui": [
|
||||
"config/tui/default.toml",
|
||||
"config/tui/dev.toml",
|
||||
"config/tui/production.toml"
|
||||
],
|
||||
"web": [
|
||||
"config/web/default.toml",
|
||||
"config/web/dev.toml",
|
||||
"config/web/production.toml"
|
||||
]
|
||||
},
|
||||
"installers": {
|
||||
"linux_macos": "installers/install.sh",
|
||||
"windows": "installers/install.ps1",
|
||||
"readme": "installers/README.md"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
# Function: Create tarball
|
||||
create_tarball() {
|
||||
local distro_path="$1"
|
||||
local distro_name=$(basename "$distro_path")
|
||||
local tarball="${DISTRO_DIR}/${distro_name}.tar.gz"
|
||||
|
||||
cd "$DISTRO_DIR"
|
||||
tar -czf "$tarball" "$distro_name"
|
||||
|
||||
echo "$tarball"
|
||||
}
|
||||
|
||||
# Function: Main entry point
|
||||
main() {
|
||||
local version="${1:-$(get_version)}"
|
||||
|
||||
log_info "TypeDialog Distribution Builder"
|
||||
log_info "================================"
|
||||
|
||||
# Build release first
|
||||
log_info "Building release binaries..."
|
||||
cd "$WORKSPACE_ROOT"
|
||||
cargo build --workspace --release || {
|
||||
log_error "Build failed"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Create distribution
|
||||
local distro_path
|
||||
distro_path=$(create_distro_structure "$version")
|
||||
log_info "Distribution path: $distro_path"
|
||||
|
||||
copy_binaries "$distro_path"
|
||||
copy_configs "$distro_path"
|
||||
copy_installers "$distro_path"
|
||||
create_manifest "$distro_path" "$version"
|
||||
|
||||
local tarball
|
||||
tarball=$(create_tarball "$distro_path")
|
||||
log_info "Distribution package: $tarball"
|
||||
|
||||
echo "$tarball"
|
||||
return 0
|
||||
}
|
||||
|
||||
main "$@"
|
||||
282
scripts/generate_sbom.py
Executable file
282
scripts/generate_sbom.py
Executable file
@ -0,0 +1,282 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate Software Bill of Materials (SBOM) in multiple formats.
|
||||
|
||||
Generates:
|
||||
- LICENSE.md - Detailed dependency attribution
|
||||
- DEPENDENCIES.md - Organized dependency tree
|
||||
- SBOM.spdx.json - SPDX 2.3 format
|
||||
- SBOM.cyclonedx.json - CycloneDX 1.4 format
|
||||
"""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def get_workspace_root():
|
||||
"""Get workspace root directory."""
|
||||
script_dir = Path(__file__).parent
|
||||
return script_dir.parent
|
||||
|
||||
|
||||
def run_cargo_license():
|
||||
"""Get dependency licenses from cargo-license."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["cargo", "license", "--json"], capture_output=True, text=True, check=True
|
||||
)
|
||||
return json.loads(result.stdout)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error running cargo license: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"Error parsing cargo license output: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def generate_license_md(licenses, workspace_root):
|
||||
"""Generate LICENSE.md with dependency attribution."""
|
||||
by_license = defaultdict(list)
|
||||
for pkg in licenses:
|
||||
lic = pkg.get("license", "Unknown")
|
||||
by_license[lic].append(pkg)
|
||||
|
||||
content = """# TypeDialog License
|
||||
|
||||
## Project License
|
||||
|
||||
TypeDialog is licensed under the **MIT License**.
|
||||
|
||||
See [LICENSE](LICENSE) file for the full MIT license text.
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
This project includes the following dependencies under their respective licenses:
|
||||
|
||||
"""
|
||||
|
||||
# Apache-2.0 only
|
||||
if "Apache-2.0" in by_license:
|
||||
content += f"### Apache-2.0 Only ({len(by_license['Apache-2.0'])})\n"
|
||||
for pkg in sorted(by_license["Apache-2.0"], key=lambda x: x["name"]):
|
||||
content += f"- {pkg['name']} {pkg['version']}\n"
|
||||
content += "\n"
|
||||
|
||||
# MIT only
|
||||
if "MIT" in by_license:
|
||||
content += f"### MIT Only ({len(by_license['MIT'])})\n"
|
||||
for pkg in sorted(by_license["MIT"], key=lambda x: x["name"]):
|
||||
content += f"- {pkg['name']} {pkg['version']}\n"
|
||||
content += "\n"
|
||||
|
||||
# Apache-2.0 OR MIT (dual licensed)
|
||||
if "Apache-2.0 OR MIT" in by_license:
|
||||
content += f"### Apache-2.0 OR MIT ({len(by_license['Apache-2.0 OR MIT'])})\n\n"
|
||||
content += (
|
||||
"Most dependencies use dual licensing between Apache-2.0 and MIT.\n\n"
|
||||
)
|
||||
for i, pkg in enumerate(
|
||||
sorted(by_license["Apache-2.0 OR MIT"], key=lambda x: x["name"]), 1
|
||||
):
|
||||
content += f"- {pkg['name']} {pkg['version']}"
|
||||
if i % 3 != 0:
|
||||
content += " | "
|
||||
else:
|
||||
content += "\n"
|
||||
content += "\n\n"
|
||||
|
||||
# MIT OR Unlicense
|
||||
if "MIT OR Unlicense" in by_license:
|
||||
content += f"### MIT OR Unlicense ({len(by_license['MIT OR Unlicense'])})\n"
|
||||
for pkg in sorted(by_license["MIT OR Unlicense"], key=lambda x: x["name"]):
|
||||
content += f"- {pkg['name']} {pkg['version']}\n"
|
||||
content += "\n"
|
||||
|
||||
# Other licenses
|
||||
other = {
|
||||
k: v
|
||||
for k, v in by_license.items()
|
||||
if k not in ["Apache-2.0", "MIT", "Apache-2.0 OR MIT", "MIT OR Unlicense"]
|
||||
}
|
||||
|
||||
if other:
|
||||
content += "### Other Licenses\n\n"
|
||||
for lic, pkgs in sorted(other.items()):
|
||||
content += f"**{lic}** ({len(pkgs)})\n"
|
||||
for pkg in sorted(pkgs, key=lambda x: x["name"]):
|
||||
content += f"- {pkg['name']} {pkg['version']}\n"
|
||||
content += "\n"
|
||||
|
||||
# Summary
|
||||
unique_licenses = set()
|
||||
for pkg in licenses:
|
||||
lic = pkg.get("license", "Unknown")
|
||||
for part in lic.replace(" OR ", "|").replace(" AND ", "|").split("|"):
|
||||
unique_licenses.add(part.strip())
|
||||
|
||||
content += f"""---
|
||||
|
||||
## Summary
|
||||
|
||||
- **Project License**: MIT
|
||||
- **Total Dependencies**: {len(licenses)}
|
||||
- **Unique License Types**: {len(by_license)} different combinations
|
||||
- **Primary License Pattern**: Apache-2.0 OR MIT (most dependencies)
|
||||
|
||||
### Compliance
|
||||
|
||||
All dependencies are compatible with the MIT license under:
|
||||
- Permissive licenses (MIT, Apache-2.0, BSD-3-Clause, MPL-2.0, Zlib)
|
||||
- Weak copyleft (LGPL-2.1-or-later, MPL-2.0)
|
||||
- Public domain (Unlicense, Unicode-3.0)
|
||||
|
||||
### Generated
|
||||
|
||||
- Date: {datetime.now().isoformat()}
|
||||
- Tool: cargo-license
|
||||
|
||||
See [DEPENDENCIES.md](DEPENDENCIES.md) for the complete dependency tree.
|
||||
"""
|
||||
|
||||
output_file = workspace_root / "LICENSE.md"
|
||||
output_file.write_text(content)
|
||||
return output_file
|
||||
|
||||
|
||||
def generate_sbom_spdx(licenses, workspace_root):
|
||||
"""Generate SPDX 2.3 format SBOM."""
|
||||
spdx = {
|
||||
"SPDXID": "SPDXRef-DOCUMENT",
|
||||
"spdxVersion": "SPDX-2.3",
|
||||
"creationInfo": {
|
||||
"created": datetime.now().isoformat() + "Z",
|
||||
"creators": ["Tool: cargo-license"],
|
||||
},
|
||||
"name": "typedialog",
|
||||
"dataLicense": "CC0-1.0",
|
||||
"documentNamespace": f"https://github.com/anthropics/typedialog/sbom-{datetime.now().strftime('%Y%m%d')}",
|
||||
"packages": [],
|
||||
}
|
||||
|
||||
# Add project
|
||||
spdx["packages"].append(
|
||||
{
|
||||
"SPDXID": "SPDXRef-typedialog",
|
||||
"name": "typedialog",
|
||||
"version": "0.1.0",
|
||||
"downloadLocation": "https://github.com/anthropics/typedialog",
|
||||
"homepage": "https://github.com/anthropics/typedialog",
|
||||
"licenseDeclared": "MIT",
|
||||
"licenseConcluded": "MIT",
|
||||
"filesAnalyzed": False,
|
||||
}
|
||||
)
|
||||
|
||||
# Add dependencies
|
||||
for i, pkg in enumerate(licenses, 1):
|
||||
spdx["packages"].append(
|
||||
{
|
||||
"SPDXID": f"SPDXRef-dependency-{i}",
|
||||
"name": pkg["name"],
|
||||
"version": pkg["version"],
|
||||
"downloadLocation": pkg.get("repository", "NOASSERTION"),
|
||||
"licenseDeclared": pkg.get("license", "NOASSERTION"),
|
||||
"licenseConcluded": pkg.get("license", "NOASSERTION"),
|
||||
"filesAnalyzed": False,
|
||||
"externalRefs": [
|
||||
{
|
||||
"referenceCategory": "PACKAGE-MANAGER",
|
||||
"referenceType": "crates",
|
||||
"referenceLocator": f"pkg:cargo/{pkg['name']}@{pkg['version']}",
|
||||
}
|
||||
]
|
||||
if pkg["name"]
|
||||
else [],
|
||||
}
|
||||
)
|
||||
|
||||
output_file = workspace_root / "SBOM.spdx.json"
|
||||
with open(output_file, "w") as f:
|
||||
json.dump(spdx, f, indent=2)
|
||||
|
||||
return output_file
|
||||
|
||||
|
||||
def generate_sbom_cyclonedx(licenses, workspace_root):
|
||||
"""Generate CycloneDX 1.4 format SBOM."""
|
||||
cyclone = {
|
||||
"bomFormat": "CycloneDX",
|
||||
"specVersion": "1.4",
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
"timestamp": datetime.now().isoformat() + "Z",
|
||||
"tools": [{"vendor": "cargo", "name": "cargo-license", "version": "1.0"}],
|
||||
"component": {
|
||||
"type": "application",
|
||||
"name": "typedialog",
|
||||
"version": "0.1.0",
|
||||
"homepage": "https://github.com/anthropics/typedialog",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/anthropics/typedialog",
|
||||
},
|
||||
"licenses": [{"license": {"name": "MIT"}}],
|
||||
},
|
||||
},
|
||||
"components": [],
|
||||
}
|
||||
|
||||
for pkg in licenses:
|
||||
cyclone["components"].append(
|
||||
{
|
||||
"type": "library",
|
||||
"name": pkg["name"],
|
||||
"version": pkg["version"],
|
||||
"purl": f"pkg:cargo/{pkg['name']}@{pkg['version']}",
|
||||
"homepage": pkg.get("repository", ""),
|
||||
"licenses": [{"license": {"name": pkg.get("license", "Unknown")}}],
|
||||
}
|
||||
)
|
||||
|
||||
output_file = workspace_root / "SBOM.cyclonedx.json"
|
||||
with open(output_file, "w") as f:
|
||||
json.dump(cyclone, f, indent=2)
|
||||
|
||||
return output_file
|
||||
|
||||
|
||||
def main():
|
||||
"""Generate all SBOM files."""
|
||||
workspace_root = get_workspace_root()
|
||||
|
||||
print("📦 Fetching dependency licenses...", file=sys.stderr)
|
||||
licenses = run_cargo_license()
|
||||
print(f" Found {len(licenses)} dependencies", file=sys.stderr)
|
||||
|
||||
print("📝 Generating LICENSE.md...", file=sys.stderr)
|
||||
lic_file = generate_license_md(licenses, workspace_root)
|
||||
print(f" ✓ {lic_file.name}", file=sys.stderr)
|
||||
|
||||
print("📄 Generating SBOM.spdx.json...", file=sys.stderr)
|
||||
spdx_file = generate_sbom_spdx(licenses, workspace_root)
|
||||
print(f" ✓ {spdx_file.name}", file=sys.stderr)
|
||||
|
||||
print("📄 Generating SBOM.cyclonedx.json...", file=sys.stderr)
|
||||
cyclone_file = generate_sbom_cyclonedx(licenses, workspace_root)
|
||||
print(f" ✓ {cyclone_file.name}", file=sys.stderr)
|
||||
|
||||
print(f"\n✅ SBOM generation complete", file=sys.stderr)
|
||||
print(
|
||||
f" - License combinations: {len(set(p.get('license', 'Unknown') for p in licenses))}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
print(f" - Generated: {datetime.now().isoformat()}", file=sys.stderr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
174
scripts/package_release.sh
Executable file
174
scripts/package_release.sh
Executable file
@ -0,0 +1,174 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Description: Package and checksum distribution for release
|
||||
# Arguments: [version] - version string (default: from Cargo.toml)
|
||||
# Returns: 0 on success, 1 on failure
|
||||
# Output: Final release package path
|
||||
|
||||
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
readonly WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
readonly DISTRO_DIR="${WORKSPACE_ROOT}/distribution"
|
||||
readonly RELEASE_DIR="${WORKSPACE_ROOT}/release"
|
||||
|
||||
# Extract version from Cargo.toml
|
||||
get_version() {
|
||||
grep '^version' "${WORKSPACE_ROOT}/crates/typedialog-core/Cargo.toml" |
|
||||
head -1 |
|
||||
sed 's/.*"\([^"]*\)".*/\1/'
|
||||
}
|
||||
|
||||
# Function: Print info message
|
||||
log_info() {
|
||||
echo "[INFO] $*" >&2
|
||||
}
|
||||
|
||||
# Function: Print error message
|
||||
log_error() {
|
||||
echo "[ERROR] $*" >&2
|
||||
}
|
||||
|
||||
# Function: Print success message
|
||||
log_success() {
|
||||
echo "[✓] $*" >&2
|
||||
}
|
||||
|
||||
# Function: Create release directory
|
||||
create_release_dir() {
|
||||
mkdir -p "$RELEASE_DIR"
|
||||
}
|
||||
|
||||
# Function: Copy distribution packages
|
||||
copy_packages() {
|
||||
local version="$1"
|
||||
local distro_tar="${DISTRO_DIR}/typedialog-${version}.tar.gz"
|
||||
|
||||
if [[ ! -f "$distro_tar" ]]; then
|
||||
log_error "Distribution package not found: $distro_tar"
|
||||
return 1
|
||||
fi
|
||||
|
||||
cp "$distro_tar" "$RELEASE_DIR/"
|
||||
log_success "Copied distribution package"
|
||||
}
|
||||
|
||||
# Function: Generate checksums
|
||||
generate_checksums() {
|
||||
local version="$1"
|
||||
local checksum_file="${RELEASE_DIR}/SHA256SUMS"
|
||||
|
||||
log_info "Generating checksums..."
|
||||
|
||||
cd "$RELEASE_DIR"
|
||||
sha256sum "typedialog-${version}.tar.gz" > "$checksum_file"
|
||||
|
||||
log_success "Checksums generated: $checksum_file"
|
||||
}
|
||||
|
||||
# Function: Create release notes
|
||||
create_release_notes() {
|
||||
local version="$1"
|
||||
local notes_file="${RELEASE_DIR}/RELEASE_NOTES.md"
|
||||
|
||||
cat > "$notes_file" <<EOF
|
||||
# TypeDialog $version
|
||||
|
||||
## Contents
|
||||
|
||||
- **typedialog-${version}.tar.gz** - Distribution package
|
||||
|
||||
## Installation
|
||||
|
||||
### Automatic (Linux/macOS)
|
||||
\`\`\`bash
|
||||
tar -xzf typedialog-${version}.tar.gz
|
||||
cd typedialog-${version}
|
||||
bash installers/install.sh
|
||||
\`\`\`
|
||||
|
||||
### Automatic (Windows PowerShell)
|
||||
\`\`\`powershell
|
||||
tar -xf typedialog-${version}.tar.gz
|
||||
cd typedialog-${version}
|
||||
.\installers\install.ps1
|
||||
\`\`\`
|
||||
|
||||
### Manual
|
||||
|
||||
1. Extract the archive
|
||||
2. Copy binaries from \`bin/\` to your system PATH
|
||||
3. Copy config from \`config/\` to \`~/.config/typedialog/\`
|
||||
|
||||
## Verification
|
||||
|
||||
Verify integrity with SHA256:
|
||||
\`\`\`bash
|
||||
sha256sum -c SHA256SUMS
|
||||
\`\`\`
|
||||
|
||||
## Contents
|
||||
|
||||
The package includes:
|
||||
|
||||
- **bin/** - Compiled binaries for all backends
|
||||
- typedialog - CLI backend
|
||||
- typedialog-tui - TUI backend
|
||||
- typedialog-web - Web backend
|
||||
|
||||
- **config/** - Configuration files for each backend and environment
|
||||
- cli/{default,dev,production}.toml
|
||||
- tui/{default,dev,production}.toml
|
||||
- web/{default,dev,production}.toml
|
||||
|
||||
- **installers/** - Installation scripts
|
||||
- install.sh - For Linux and macOS
|
||||
- install.ps1 - For Windows
|
||||
- README.md - Installation instructions
|
||||
|
||||
- **MANIFEST.json** - Package contents and structure
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
- Linux (x86_64, aarch64)
|
||||
- macOS (x86_64, aarch64)
|
||||
- Windows (x86_64)
|
||||
|
||||
## Documentation
|
||||
|
||||
See installers/README.md for detailed installation instructions.
|
||||
|
||||
---
|
||||
Released: $(date -u +'%Y-%m-%d %H:%M:%S UTC')
|
||||
EOF
|
||||
|
||||
log_success "Release notes created: $notes_file"
|
||||
}
|
||||
|
||||
# Function: List release contents
|
||||
list_release() {
|
||||
log_info "Release contents:"
|
||||
ls -lh "$RELEASE_DIR"
|
||||
}
|
||||
|
||||
# Function: Main entry point
|
||||
main() {
|
||||
local version="${1:-$(get_version)}"
|
||||
|
||||
log_info "TypeDialog Release Packager"
|
||||
log_info "============================="
|
||||
log_info "Version: $version"
|
||||
|
||||
create_release_dir
|
||||
copy_packages "$version"
|
||||
generate_checksums "$version"
|
||||
create_release_notes "$version"
|
||||
|
||||
log_success "Release package ready!"
|
||||
log_info "Release directory: $RELEASE_DIR"
|
||||
|
||||
list_release
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
main "$@"
|
||||
656
tests/nickel_integration.rs
Normal file
656
tests/nickel_integration.rs
Normal file
@ -0,0 +1,656 @@
|
||||
//! Integration tests for Nickel ↔ typedialog bidirectional workflows
|
||||
//!
|
||||
//! Tests the complete workflow:
|
||||
//! 1. Nickel schema → metadata extraction
|
||||
//! 2. Metadata → TOML form generation
|
||||
//! 3. Form results → Nickel output serialization
|
||||
//! 4. Template rendering with form results
|
||||
|
||||
use typedialog::nickel::{
|
||||
NickelSchemaIR, NickelFieldIR, NickelType, MetadataParser, TomlGenerator,
|
||||
NickelSerializer, ContractValidator, TemplateEngine,
|
||||
};
|
||||
use typedialog::form_parser;
|
||||
use serde_json::json;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[test]
|
||||
fn test_simple_schema_roundtrip() {
|
||||
// Create a simple schema
|
||||
let schema = NickelSchemaIR {
|
||||
name: "user_config".to_string(),
|
||||
description: Some("Simple user configuration".to_string()),
|
||||
fields: vec![
|
||||
NickelFieldIR {
|
||||
path: vec!["username".to_string()],
|
||||
flat_name: "username".to_string(),
|
||||
nickel_type: NickelType::String,
|
||||
doc: Some("User login name".to_string()),
|
||||
default: Some(json!("admin")),
|
||||
optional: false,
|
||||
contract: Some("String | std.string.NonEmpty".to_string()),
|
||||
group: None,
|
||||
},
|
||||
NickelFieldIR {
|
||||
path: vec!["email".to_string()],
|
||||
flat_name: "email".to_string(),
|
||||
nickel_type: NickelType::String,
|
||||
doc: Some("User email address".to_string()),
|
||||
default: None,
|
||||
optional: true,
|
||||
contract: None,
|
||||
group: None,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Generate form
|
||||
let form = TomlGenerator::generate(&schema, false, false).expect("Form generation failed");
|
||||
assert_eq!(form.name, "user_config");
|
||||
assert_eq!(form.fields.len(), 2);
|
||||
|
||||
// Verify form fields have nickel metadata
|
||||
assert_eq!(form.fields[0].nickel_contract, Some("String | std.string.NonEmpty".to_string()));
|
||||
assert_eq!(form.fields[0].nickel_path, Some(vec!["username".to_string()]));
|
||||
|
||||
// Create form results
|
||||
let mut results = HashMap::new();
|
||||
results.insert("username".to_string(), json!("alice"));
|
||||
results.insert("email".to_string(), json!("alice@example.com"));
|
||||
|
||||
// Serialize to Nickel
|
||||
let nickel_output = NickelSerializer::serialize(&results, &schema)
|
||||
.expect("Serialization failed");
|
||||
|
||||
// Verify output contains expected content
|
||||
assert!(nickel_output.contains("alice"));
|
||||
assert!(nickel_output.contains("alice@example.com"));
|
||||
assert!(nickel_output.contains("String | std.string.NonEmpty"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_schema_with_flatten() {
|
||||
// Create schema with nested structure
|
||||
let schema = NickelSchemaIR {
|
||||
name: "server_config".to_string(),
|
||||
description: None,
|
||||
fields: vec![
|
||||
NickelFieldIR {
|
||||
path: vec!["server".to_string(), "hostname".to_string()],
|
||||
flat_name: "server_hostname".to_string(),
|
||||
nickel_type: NickelType::String,
|
||||
doc: Some("Server hostname".to_string()),
|
||||
default: None,
|
||||
optional: false,
|
||||
contract: None,
|
||||
group: Some("server".to_string()),
|
||||
},
|
||||
NickelFieldIR {
|
||||
path: vec!["server".to_string(), "port".to_string()],
|
||||
flat_name: "server_port".to_string(),
|
||||
nickel_type: NickelType::Number,
|
||||
doc: Some("Server port".to_string()),
|
||||
default: Some(json!(8080)),
|
||||
optional: false,
|
||||
contract: None,
|
||||
group: Some("server".to_string()),
|
||||
},
|
||||
NickelFieldIR {
|
||||
path: vec!["database".to_string(), "host".to_string()],
|
||||
flat_name: "database_host".to_string(),
|
||||
nickel_type: NickelType::String,
|
||||
doc: Some("Database host".to_string()),
|
||||
default: None,
|
||||
optional: false,
|
||||
contract: None,
|
||||
group: Some("database".to_string()),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Generate form with grouping
|
||||
let form = TomlGenerator::generate(&schema, false, true)
|
||||
.expect("Form generation failed");
|
||||
|
||||
// Verify groups are created
|
||||
assert!(form.items.len() > 0);
|
||||
|
||||
// Verify fields have groups
|
||||
let server_hostname = form.fields.iter().find(|f| f.name == "server_hostname").unwrap();
|
||||
assert_eq!(server_hostname.group, Some("server".to_string()));
|
||||
|
||||
// Create results
|
||||
let mut results = HashMap::new();
|
||||
results.insert("server_hostname".to_string(), json!("api.example.com"));
|
||||
results.insert("server_port".to_string(), json!(9000));
|
||||
results.insert("database_host".to_string(), json!("db.example.com"));
|
||||
|
||||
// Serialize and verify nested structure is restored
|
||||
let nickel_output = NickelSerializer::serialize(&results, &schema)
|
||||
.expect("Serialization failed");
|
||||
|
||||
assert!(nickel_output.contains("server"));
|
||||
assert!(nickel_output.contains("database"));
|
||||
assert!(nickel_output.contains("api.example.com"));
|
||||
assert!(nickel_output.contains("db.example.com"));
|
||||
assert!(nickel_output.contains("9000"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_array_field_serialization() {
|
||||
let schema = NickelSchemaIR {
|
||||
name: "array_config".to_string(),
|
||||
description: None,
|
||||
fields: vec![
|
||||
NickelFieldIR {
|
||||
path: vec!["tags".to_string()],
|
||||
flat_name: "tags".to_string(),
|
||||
nickel_type: NickelType::Array(Box::new(NickelType::String)),
|
||||
doc: Some("Configuration tags".to_string()),
|
||||
default: None,
|
||||
optional: false,
|
||||
contract: None,
|
||||
group: None,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
let mut results = HashMap::new();
|
||||
results.insert("tags".to_string(), json!(["prod", "critical", "network"]));
|
||||
|
||||
let nickel_output = NickelSerializer::serialize(&results, &schema)
|
||||
.expect("Serialization failed");
|
||||
|
||||
assert!(nickel_output.contains("prod"));
|
||||
assert!(nickel_output.contains("critical"));
|
||||
assert!(nickel_output.contains("network"));
|
||||
assert!(nickel_output.contains("["));
|
||||
assert!(nickel_output.contains("]"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_contract_validation_non_empty_string() {
|
||||
// Valid non-empty string
|
||||
let result = ContractValidator::validate(
|
||||
&json!("hello"),
|
||||
"String | std.string.NonEmpty",
|
||||
);
|
||||
assert!(result.is_ok());
|
||||
|
||||
// Empty string should fail
|
||||
let result = ContractValidator::validate(
|
||||
&json!(""),
|
||||
"String | std.string.NonEmpty",
|
||||
);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_contract_validation_number_range() {
|
||||
// Valid number in range
|
||||
let result = ContractValidator::validate(
|
||||
&json!(50),
|
||||
"Number | std.number.between 0 100",
|
||||
);
|
||||
assert!(result.is_ok());
|
||||
|
||||
// Number out of range
|
||||
let result = ContractValidator::validate(
|
||||
&json!(150),
|
||||
"Number | std.number.between 0 100",
|
||||
);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_contract_validation_string_length() {
|
||||
// Valid length
|
||||
let result = ContractValidator::validate(
|
||||
&json!("hello"),
|
||||
"String | std.string.length.min 3",
|
||||
);
|
||||
assert!(result.is_ok());
|
||||
|
||||
// Too short
|
||||
let result = ContractValidator::validate(
|
||||
&json!("hi"),
|
||||
"String | std.string.length.min 3",
|
||||
);
|
||||
assert!(result.is_err());
|
||||
|
||||
// Valid max length
|
||||
let result = ContractValidator::validate(
|
||||
&json!("hi"),
|
||||
"String | std.string.length.max 5",
|
||||
);
|
||||
assert!(result.is_ok());
|
||||
|
||||
// Too long
|
||||
let result = ContractValidator::validate(
|
||||
&json!("hello world"),
|
||||
"String | std.string.length.max 5",
|
||||
);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_form_definition_from_schema_ir() {
|
||||
// Create schema
|
||||
let schema = NickelSchemaIR {
|
||||
name: "test_form".to_string(),
|
||||
description: Some("Test form".to_string()),
|
||||
fields: vec![
|
||||
NickelFieldIR {
|
||||
path: vec!["name".to_string()],
|
||||
flat_name: "name".to_string(),
|
||||
nickel_type: NickelType::String,
|
||||
doc: Some("Your name".to_string()),
|
||||
default: None,
|
||||
optional: false,
|
||||
contract: Some("String | std.string.NonEmpty".to_string()),
|
||||
group: None,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Generate form
|
||||
let form = TomlGenerator::generate(&schema, false, false)
|
||||
.expect("Form generation failed");
|
||||
|
||||
// Convert to TOML
|
||||
let toml_str = toml::to_string_pretty(&form)
|
||||
.expect("TOML serialization failed");
|
||||
|
||||
// Parse back
|
||||
let parsed_form: form_parser::FormDefinition = toml::from_str(&toml_str)
|
||||
.expect("TOML parsing failed");
|
||||
|
||||
// Verify round-trip
|
||||
assert_eq!(parsed_form.name, "test_form");
|
||||
assert_eq!(parsed_form.fields.len(), 1);
|
||||
assert_eq!(parsed_form.fields[0].name, "name");
|
||||
assert_eq!(
|
||||
parsed_form.fields[0].nickel_contract,
|
||||
Some("String | std.string.NonEmpty".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_metadata_extraction_with_optional_fields() {
|
||||
// Parse JSON metadata (simulating nickel query output)
|
||||
let metadata = json!({
|
||||
"name": {
|
||||
"doc": "User full name",
|
||||
"type": "String",
|
||||
"default": "Anonymous"
|
||||
},
|
||||
"age": {
|
||||
"doc": "User age in years",
|
||||
"type": "Number",
|
||||
"optional": true
|
||||
},
|
||||
"active": {
|
||||
"type": "Bool",
|
||||
"optional": false,
|
||||
"default": true
|
||||
}
|
||||
});
|
||||
|
||||
let schema = MetadataParser::parse(metadata)
|
||||
.expect("Metadata parsing failed");
|
||||
|
||||
// Verify fields
|
||||
assert_eq!(schema.fields.len(), 3);
|
||||
|
||||
// Check optional flags
|
||||
let name_field = schema.fields.iter().find(|f| f.flat_name == "name").unwrap();
|
||||
assert!(!name_field.optional);
|
||||
|
||||
let age_field = schema.fields.iter().find(|f| f.flat_name == "age").unwrap();
|
||||
assert!(age_field.optional);
|
||||
|
||||
let active_field = schema.fields.iter().find(|f| f.flat_name == "active").unwrap();
|
||||
assert!(!active_field.optional);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_type_mapping_all_types() {
|
||||
let schema = NickelSchemaIR {
|
||||
name: "types_test".to_string(),
|
||||
description: None,
|
||||
fields: vec![
|
||||
NickelFieldIR {
|
||||
path: vec!["str_field".to_string()],
|
||||
flat_name: "str_field".to_string(),
|
||||
nickel_type: NickelType::String,
|
||||
doc: None,
|
||||
default: None,
|
||||
optional: false,
|
||||
contract: None,
|
||||
group: None,
|
||||
},
|
||||
NickelFieldIR {
|
||||
path: vec!["num_field".to_string()],
|
||||
flat_name: "num_field".to_string(),
|
||||
nickel_type: NickelType::Number,
|
||||
doc: None,
|
||||
default: None,
|
||||
optional: false,
|
||||
contract: None,
|
||||
group: None,
|
||||
},
|
||||
NickelFieldIR {
|
||||
path: vec!["bool_field".to_string()],
|
||||
flat_name: "bool_field".to_string(),
|
||||
nickel_type: NickelType::Bool,
|
||||
doc: None,
|
||||
default: None,
|
||||
optional: false,
|
||||
contract: None,
|
||||
group: None,
|
||||
},
|
||||
NickelFieldIR {
|
||||
path: vec!["array_field".to_string()],
|
||||
flat_name: "array_field".to_string(),
|
||||
nickel_type: NickelType::Array(Box::new(NickelType::String)),
|
||||
doc: None,
|
||||
default: None,
|
||||
optional: false,
|
||||
contract: None,
|
||||
group: None,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
let form = TomlGenerator::generate(&schema, false, false)
|
||||
.expect("Form generation failed");
|
||||
|
||||
// Verify type mapping
|
||||
assert_eq!(form.fields[0].field_type, form_parser::FieldType::Text);
|
||||
assert_eq!(form.fields[1].field_type, form_parser::FieldType::Custom);
|
||||
assert_eq!(form.fields[1].custom_type, Some("f64".to_string()));
|
||||
assert_eq!(form.fields[2].field_type, form_parser::FieldType::Confirm);
|
||||
assert_eq!(form.fields[3].field_type, form_parser::FieldType::Editor);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enum_options_extraction_from_doc() {
|
||||
let field = NickelFieldIR {
|
||||
path: vec!["status".to_string()],
|
||||
flat_name: "status".to_string(),
|
||||
nickel_type: NickelType::Array(Box::new(NickelType::String)),
|
||||
doc: Some("Status selection. Options: pending, active, completed".to_string()),
|
||||
default: None,
|
||||
optional: false,
|
||||
contract: None,
|
||||
group: None,
|
||||
};
|
||||
|
||||
let schema = NickelSchemaIR {
|
||||
name: "test".to_string(),
|
||||
description: None,
|
||||
fields: vec![field],
|
||||
};
|
||||
|
||||
let form = TomlGenerator::generate(&schema, false, false)
|
||||
.expect("Form generation failed");
|
||||
|
||||
// Verify options extracted
|
||||
let status_field = &form.fields[0];
|
||||
assert_eq!(
|
||||
status_field.options,
|
||||
Some(vec![
|
||||
"pending".to_string(),
|
||||
"active".to_string(),
|
||||
"completed".to_string(),
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_template_rendering_simple() {
|
||||
#[cfg(feature = "templates")]
|
||||
{
|
||||
let mut engine = TemplateEngine::new();
|
||||
let mut values = HashMap::new();
|
||||
values.insert("hostname".to_string(), json!("server1"));
|
||||
values.insert("port".to_string(), json!(8080));
|
||||
|
||||
let template = r#"
|
||||
server {
|
||||
hostname : String = "{{ hostname }}"
|
||||
port : Number = {{ port }}
|
||||
}
|
||||
"#;
|
||||
|
||||
let result = engine.render_str(template, &values);
|
||||
assert!(result.is_ok());
|
||||
|
||||
let output = result.unwrap();
|
||||
assert!(output.contains("server1"));
|
||||
assert!(output.contains("8080"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_template_rendering_with_loop() {
|
||||
#[cfg(feature = "templates")]
|
||||
{
|
||||
let mut engine = TemplateEngine::new();
|
||||
let mut values = HashMap::new();
|
||||
values.insert("servers".to_string(), json!([
|
||||
{"name": "web-01", "ip": "192.168.1.10"},
|
||||
{"name": "web-02", "ip": "192.168.1.11"},
|
||||
]));
|
||||
|
||||
let template = r#"servers = [
|
||||
{% for server in servers %}
|
||||
{ name = "{{ server.name }}", ip = "{{ server.ip }}" },
|
||||
{% endfor %}
|
||||
]"#;
|
||||
|
||||
let result = engine.render_str(template, &values);
|
||||
assert!(result.is_ok());
|
||||
|
||||
let output = result.unwrap();
|
||||
assert!(output.contains("web-01"));
|
||||
assert!(output.contains("web-02"));
|
||||
assert!(output.contains("192.168.1.10"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_template_rendering_with_conditional() {
|
||||
#[cfg(feature = "templates")]
|
||||
{
|
||||
let mut engine = TemplateEngine::new();
|
||||
let mut values = HashMap::new();
|
||||
values.insert("production".to_string(), json!(true));
|
||||
values.insert("replicas".to_string(), json!(3));
|
||||
|
||||
let template = r#"{% if production %}
|
||||
replicas : Number = {{ replicas }}
|
||||
{% else %}
|
||||
replicas : Number = 1
|
||||
{% endif %}"#;
|
||||
|
||||
let result = engine.render_str(template, &values);
|
||||
assert!(result.is_ok());
|
||||
|
||||
let output = result.unwrap();
|
||||
assert!(output.contains("replicas"));
|
||||
assert!(output.contains("3"));
|
||||
assert!(!output.contains("= 1"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_full_workflow_integration() {
|
||||
// Create a realistic schema
|
||||
let schema = NickelSchemaIR {
|
||||
name: "app_config".to_string(),
|
||||
description: Some("Application configuration".to_string()),
|
||||
fields: vec![
|
||||
NickelFieldIR {
|
||||
path: vec!["app".to_string(), "name".to_string()],
|
||||
flat_name: "app_name".to_string(),
|
||||
nickel_type: NickelType::String,
|
||||
doc: Some("Application name".to_string()),
|
||||
default: None,
|
||||
optional: false,
|
||||
contract: Some("String | std.string.NonEmpty".to_string()),
|
||||
group: Some("app".to_string()),
|
||||
},
|
||||
NickelFieldIR {
|
||||
path: vec!["app".to_string(), "version".to_string()],
|
||||
flat_name: "app_version".to_string(),
|
||||
nickel_type: NickelType::String,
|
||||
doc: Some("Application version (e.g., 1.0.0)".to_string()),
|
||||
default: Some(json!("1.0.0")),
|
||||
optional: false,
|
||||
contract: None,
|
||||
group: Some("app".to_string()),
|
||||
},
|
||||
NickelFieldIR {
|
||||
path: vec!["server".to_string(), "host".to_string()],
|
||||
flat_name: "server_host".to_string(),
|
||||
nickel_type: NickelType::String,
|
||||
doc: Some("Server hostname".to_string()),
|
||||
default: Some(json!("localhost")),
|
||||
optional: false,
|
||||
contract: None,
|
||||
group: Some("server".to_string()),
|
||||
},
|
||||
NickelFieldIR {
|
||||
path: vec!["server".to_string(), "port".to_string()],
|
||||
flat_name: "server_port".to_string(),
|
||||
nickel_type: NickelType::Number,
|
||||
doc: Some("Server port".to_string()),
|
||||
default: Some(json!(8000)),
|
||||
optional: false,
|
||||
contract: Some("Number | std.number.between 1 65535".to_string()),
|
||||
group: Some("server".to_string()),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Step 1: Generate form
|
||||
let form = TomlGenerator::generate(&schema, false, true)
|
||||
.expect("Form generation failed");
|
||||
assert_eq!(form.fields.len(), 4);
|
||||
|
||||
// Step 2: Serialize to TOML and parse back
|
||||
let toml_str = toml::to_string_pretty(&form)
|
||||
.expect("TOML serialization failed");
|
||||
let parsed_form: form_parser::FormDefinition = toml::from_str(&toml_str)
|
||||
.expect("TOML parsing failed");
|
||||
assert_eq!(parsed_form.fields.len(), 4);
|
||||
|
||||
// Step 3: Create form results
|
||||
let mut results = HashMap::new();
|
||||
results.insert("app_name".to_string(), json!("MyApp"));
|
||||
results.insert("app_version".to_string(), json!("2.0.0"));
|
||||
results.insert("server_host".to_string(), json!("0.0.0.0"));
|
||||
results.insert("server_port".to_string(), json!(3000));
|
||||
|
||||
// Step 4: Validate contracts
|
||||
assert!(ContractValidator::validate(&json!("MyApp"), "String | std.string.NonEmpty").is_ok());
|
||||
assert!(ContractValidator::validate(&json!(3000), "Number | std.number.between 1 65535").is_ok());
|
||||
|
||||
// Step 5: Serialize to Nickel
|
||||
let nickel_output = NickelSerializer::serialize(&results, &schema)
|
||||
.expect("Serialization failed");
|
||||
|
||||
// Step 6: Verify output
|
||||
assert!(nickel_output.contains("MyApp"));
|
||||
assert!(nickel_output.contains("2.0.0"));
|
||||
assert!(nickel_output.contains("0.0.0.0"));
|
||||
assert!(nickel_output.contains("3000"));
|
||||
assert!(nickel_output.contains("String | std.string.NonEmpty"));
|
||||
assert!(nickel_output.contains("Number | std.number.between 1 65535"));
|
||||
|
||||
// Verify nested structure
|
||||
assert!(nickel_output.contains("app"));
|
||||
assert!(nickel_output.contains("server"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_optional_fields_handling() {
|
||||
let schema = NickelSchemaIR {
|
||||
name: "optional_test".to_string(),
|
||||
description: None,
|
||||
fields: vec![
|
||||
NickelFieldIR {
|
||||
path: vec!["required_field".to_string()],
|
||||
flat_name: "required_field".to_string(),
|
||||
nickel_type: NickelType::String,
|
||||
doc: None,
|
||||
default: None,
|
||||
optional: false,
|
||||
contract: None,
|
||||
group: None,
|
||||
},
|
||||
NickelFieldIR {
|
||||
path: vec!["optional_field".to_string()],
|
||||
flat_name: "optional_field".to_string(),
|
||||
nickel_type: NickelType::String,
|
||||
doc: None,
|
||||
default: None,
|
||||
optional: true,
|
||||
contract: None,
|
||||
group: None,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
let form = TomlGenerator::generate(&schema, false, false)
|
||||
.expect("Form generation failed");
|
||||
|
||||
// Check required field
|
||||
let required = form.fields.iter().find(|f| f.name == "required_field").unwrap();
|
||||
assert_eq!(required.required, Some(true));
|
||||
|
||||
// Check optional field
|
||||
let optional = form.fields.iter().find(|f| f.name == "optional_field").unwrap();
|
||||
assert_eq!(optional.required, Some(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_defaults_preservation() {
|
||||
let schema = NickelSchemaIR {
|
||||
name: "defaults_test".to_string(),
|
||||
description: None,
|
||||
fields: vec![
|
||||
NickelFieldIR {
|
||||
path: vec!["string_with_default".to_string()],
|
||||
flat_name: "string_with_default".to_string(),
|
||||
nickel_type: NickelType::String,
|
||||
doc: None,
|
||||
default: Some(json!("default_value")),
|
||||
optional: false,
|
||||
contract: None,
|
||||
group: None,
|
||||
},
|
||||
NickelFieldIR {
|
||||
path: vec!["number_with_default".to_string()],
|
||||
flat_name: "number_with_default".to_string(),
|
||||
nickel_type: NickelType::Number,
|
||||
doc: None,
|
||||
default: Some(json!(42)),
|
||||
optional: false,
|
||||
contract: None,
|
||||
group: None,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
let form = TomlGenerator::generate(&schema, false, false)
|
||||
.expect("Form generation failed");
|
||||
|
||||
// Check defaults are preserved
|
||||
let string_field = form.fields.iter().find(|f| f.name == "string_with_default").unwrap();
|
||||
assert_eq!(string_field.default, Some("default_value".to_string()));
|
||||
|
||||
let number_field = form.fields.iter().find(|f| f.name == "number_with_default").unwrap();
|
||||
assert_eq!(number_field.default, Some("42".to_string()));
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user