132 lines
4.8 KiB
Text
132 lines
4.8 KiB
Text
|
|
#!/usr/bin/env nu
|
||
|
|
|
||
|
|
# Blog Post Creator / Editor
|
||
|
|
#
|
||
|
|
# Orchestrates the typedialog-web nickel-roundtrip workflow for creating
|
||
|
|
# or editing post metadata (.ncl files).
|
||
|
|
#
|
||
|
|
# Modes:
|
||
|
|
# new — load form defaults → typedialog-web → write to target .ncl
|
||
|
|
# edit -- load existing .ncl → typedialog-web → overwrite in place
|
||
|
|
# agent — write prescribed values directly to .ncl (no browser, no TUI)
|
||
|
|
#
|
||
|
|
# Usage:
|
||
|
|
# nu new-post.nu --type blog --lang en --category devops --slug my-post
|
||
|
|
# nu new-post.nu --edit site/content/blog/en/devops/my-post.ncl
|
||
|
|
# nu new-post.nu --type blog --lang en --slug my-post --agent values.json
|
||
|
|
|
||
|
|
def main [
|
||
|
|
--type: string = "blog" # Content type
|
||
|
|
--lang: string = "en" # Language directory
|
||
|
|
--category: string = "" # Category slug (matches parent dir)
|
||
|
|
--slug: string = "" # Post slug (becomes filename stem)
|
||
|
|
--edit: string = "" # Path to existing .ncl to edit (skips new)
|
||
|
|
--agent: string = "" # JSON file with prescribed field values (agent mode)
|
||
|
|
--form: string = "" # Override form template path
|
||
|
|
--content-dir: string = "" # Override SITE_CONTENT_PATH
|
||
|
|
--dry-run # Print resolved paths without opening typedialog
|
||
|
|
--verbose
|
||
|
|
] {
|
||
|
|
let content_root = resolve_env "SITE_CONTENT_PATH" $content_dir "site/content"
|
||
|
|
let form_base = if $form != "" { $form } else {
|
||
|
|
$"rustelo/forms/content/($type)/post.ncl"
|
||
|
|
}
|
||
|
|
|
||
|
|
if not ($form_base | path exists) {
|
||
|
|
error make { msg: $"Form template not found: ($form_base)" }
|
||
|
|
}
|
||
|
|
|
||
|
|
# Determine mode
|
||
|
|
let mode = if $edit != "" { "edit" } else if $agent != "" { "agent" } else { "new" }
|
||
|
|
|
||
|
|
# Resolve working .ncl path
|
||
|
|
let ncl_path = if $mode == "edit" {
|
||
|
|
$edit
|
||
|
|
} else {
|
||
|
|
if $slug == "" { error make { msg: "--slug is required for new posts" } }
|
||
|
|
if $category != "" {
|
||
|
|
$"($content_root)/($type)/($lang)/($category)/($slug).ncl"
|
||
|
|
} else {
|
||
|
|
$"($content_root)/($type)/($lang)/($slug).ncl"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if $verbose or $dry_run {
|
||
|
|
print $" mode : ($mode)"
|
||
|
|
print $" form : ($form_base)"
|
||
|
|
print $" output : ($ncl_path)"
|
||
|
|
}
|
||
|
|
if $dry_run { return }
|
||
|
|
|
||
|
|
# Prepare working copy for typedialog
|
||
|
|
let work_ncl = if $mode == "edit" {
|
||
|
|
$ncl_path
|
||
|
|
} else {
|
||
|
|
# Copy form template to a temp file — typedialog edits it in place
|
||
|
|
let tmp = $"/tmp/new-post-(random chars).ncl"
|
||
|
|
cp ($form_base | path expand) $tmp
|
||
|
|
$tmp
|
||
|
|
}
|
||
|
|
|
||
|
|
match $mode {
|
||
|
|
"new" | "edit" => {
|
||
|
|
print $"(ansi cyan)Opening typedialog form(ansi reset) — ($work_ncl)"
|
||
|
|
let result = do {
|
||
|
|
typedialog-web nickel-roundtrip ($work_ncl | path expand)
|
||
|
|
} | complete
|
||
|
|
|
||
|
|
if $result.exit_code != 0 {
|
||
|
|
error make { msg: $"typedialog failed: ($result.stderr)" }
|
||
|
|
}
|
||
|
|
|
||
|
|
if $mode == "new" {
|
||
|
|
mkdir ($ncl_path | path dirname)
|
||
|
|
cp $work_ncl $ncl_path
|
||
|
|
rm $work_ncl
|
||
|
|
print $"(ansi green)Created(ansi reset) → ($ncl_path)"
|
||
|
|
} else {
|
||
|
|
print $"(ansi green)Updated(ansi reset) → ($ncl_path)"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
"agent" => {
|
||
|
|
# Agent mode: merge prescribed JSON values into the form template
|
||
|
|
if not ($agent | path exists) {
|
||
|
|
error make { msg: $"Agent values file not found: ($agent)" }
|
||
|
|
}
|
||
|
|
let values = open $agent
|
||
|
|
let base = open ($form_base | path expand)
|
||
|
|
|
||
|
|
# Render filled .ncl by patching fields in the base form
|
||
|
|
mut ncl_lines = $base | lines
|
||
|
|
for entry in ($values | items { |k, v| { key: $k, val: $v } }) {
|
||
|
|
let pat = $" ($entry.key) ="
|
||
|
|
let replacement = $" ($entry.key) = ($entry.val | to nuon),"
|
||
|
|
$ncl_lines = $ncl_lines | each { |l|
|
||
|
|
if ($l | str starts-with $pat) { $replacement } else { $l }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
mkdir ($ncl_path | path dirname)
|
||
|
|
$ncl_lines | str join "\n" | save --force $ncl_path
|
||
|
|
print $"(ansi green)Agent wrote(ansi reset) → ($ncl_path)"
|
||
|
|
}
|
||
|
|
|
||
|
|
_ => { error make { msg: $"Unknown mode: ($mode)" } }
|
||
|
|
}
|
||
|
|
|
||
|
|
# Validate the result
|
||
|
|
print " validating .ncl..."
|
||
|
|
let validate = do { nickel-export ($ncl_path | path expand) } | complete
|
||
|
|
if $validate.exit_code != 0 {
|
||
|
|
print $"(ansi yellow)WARNING(ansi reset) .ncl validation failed — check fields before publishing"
|
||
|
|
print $validate.stderr
|
||
|
|
} else {
|
||
|
|
print $" (ansi green)✓(ansi reset) valid"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
def resolve_env [var_name: string, cli_arg: string, fallback: string] {
|
||
|
|
if $cli_arg != "" { return $cli_arg }
|
||
|
|
try { $env | get $var_name } catch { $fallback }
|
||
|
|
}
|