159 lines
5.3 KiB
Bash
159 lines
5.3 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
usage() {
|
|
cat <<'USAGE'
|
|
Usage:
|
|
init_workspace.sh --name NAME [options]
|
|
|
|
Required:
|
|
--name NAME Project name
|
|
|
|
Optional:
|
|
--alias ALIAS Project alias (default: slugified name)
|
|
--desc DESC Project description
|
|
--owner OWNER Project owner
|
|
--scale small|medium|large Project scale (default: medium)
|
|
--risk low|medium|high Risk level (default: medium)
|
|
--gate-architecture enabled|disabled Optional gate (default: disabled)
|
|
--gate-code-review enabled|disabled Optional gate (default: disabled)
|
|
--gate-security enabled|disabled Optional gate (default: disabled)
|
|
--gate-privacy enabled|disabled Optional gate (default: disabled)
|
|
--git-enabled true|false Git policy enabled (default: false)
|
|
--git-commit-format FORMAT Commit format (default: "[role][phase] summary - reason")
|
|
--output PATH Output directory (default: ./workspace/specs/{alias}-YYYYMMDD-HHMM)
|
|
-h, --help Show help
|
|
USAGE
|
|
}
|
|
|
|
die() {
|
|
echo "error: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
command -v python3 >/dev/null 2>&1 || die "python3 is required"
|
|
|
|
project_name=""
|
|
project_alias=""
|
|
project_desc=""
|
|
project_owner=""
|
|
scale="medium"
|
|
risk="medium"
|
|
_gate_arch="disabled"
|
|
_gate_code_review="disabled"
|
|
_gate_security="disabled"
|
|
_gate_privacy="disabled"
|
|
git_enabled="false"
|
|
git_commit_format="[role][phase] summary - reason"
|
|
output=""
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--name) project_name="$2"; shift 2;;
|
|
--alias) project_alias="$2"; shift 2;;
|
|
--desc) project_desc="$2"; shift 2;;
|
|
--owner) project_owner="$2"; shift 2;;
|
|
--scale) scale="$2"; shift 2;;
|
|
--risk) risk="$2"; shift 2;;
|
|
--gate-architecture) _gate_arch="$2"; shift 2;;
|
|
--gate-code-review) _gate_code_review="$2"; shift 2;;
|
|
--gate-security) _gate_security="$2"; shift 2;;
|
|
--gate-privacy) _gate_privacy="$2"; shift 2;;
|
|
--git-enabled) git_enabled="$2"; shift 2;;
|
|
--git-commit-format) git_commit_format="$2"; shift 2;;
|
|
--output) output="$2"; shift 2;;
|
|
-h|--help) usage; exit 0;;
|
|
*) die "unknown arg: $1";;
|
|
esac
|
|
done
|
|
|
|
[[ -z "$project_name" ]] && die "--name is required"
|
|
|
|
if [[ -z "$project_alias" ]]; then
|
|
project_alias=$(echo "$project_name" | tr '[:upper:] ' '[:lower:]-' | tr -cd 'a-z0-9-')
|
|
fi
|
|
|
|
if [[ -z "$output" ]]; then
|
|
output="./workspace/specs/${project_alias}-$(date +%Y%m%d-%H%M)"
|
|
fi
|
|
|
|
if [[ -e "$output" ]]; then
|
|
die "output already exists: $output"
|
|
fi
|
|
|
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
template_dir="${script_dir}/../assets/workspace_template"
|
|
|
|
mkdir -p "$output"
|
|
cp -R "$template_dir"/. "$output"/
|
|
|
|
date_str="$(date +%Y-%m-%d)"
|
|
|
|
export TG_PROJECT_NAME="$project_name"
|
|
export TG_PROJECT_ALIAS="$project_alias"
|
|
export TG_PROJECT_DESC="$project_desc"
|
|
export TG_PROJECT_OWNER="$project_owner"
|
|
export TG_DATE="$date_str"
|
|
export TG_SCALE="$scale"
|
|
export TG_RISK="$risk"
|
|
export TG_GATE_ARCH="$_gate_arch"
|
|
export TG_GATE_CODE_REVIEW="$_gate_code_review"
|
|
export TG_GATE_SECURITY="$_gate_security"
|
|
export TG_GATE_PRIVACY="$_gate_privacy"
|
|
export TG_GIT_ENABLED="$git_enabled"
|
|
export TG_GIT_COMMIT_FORMAT="$git_commit_format"
|
|
export TG_CURRENT_PHASE="demand"
|
|
export TG_CURRENT_ROUND="1"
|
|
export TG_P0_COUNT="0"
|
|
export TG_LAST_DECISION="-"
|
|
export TG_NEXT_ACTION="collect requirements"
|
|
|
|
replace_file() {
|
|
python3 - "$1" <<'PY'
|
|
import os
|
|
from pathlib import Path
|
|
|
|
path = Path(os.environ["TARGET_FILE"])
|
|
data = path.read_text()
|
|
repl = {
|
|
"project_name": os.environ.get("TG_PROJECT_NAME", ""),
|
|
"project_alias": os.environ.get("TG_PROJECT_ALIAS", ""),
|
|
"project_desc": os.environ.get("TG_PROJECT_DESC", ""),
|
|
"project_owner": os.environ.get("TG_PROJECT_OWNER", ""),
|
|
"date": os.environ.get("TG_DATE", ""),
|
|
"current_phase": os.environ.get("TG_CURRENT_PHASE", ""),
|
|
"current_round": os.environ.get("TG_CURRENT_ROUND", ""),
|
|
"p0_count": os.environ.get("TG_P0_COUNT", ""),
|
|
"last_decision": os.environ.get("TG_LAST_DECISION", ""),
|
|
"next_action": os.environ.get("TG_NEXT_ACTION", ""),
|
|
}
|
|
for k, v in repl.items():
|
|
data = data.replace("{{" + k + "}}", v)
|
|
|
|
# project.yaml optional fields
|
|
if path.name == "project.yaml":
|
|
data = data.replace("small|medium|large", os.environ.get("TG_SCALE", "medium"))
|
|
data = data.replace("low|medium|high", os.environ.get("TG_RISK", "medium"))
|
|
data = data.replace("enabled|disabled", "enabled" if os.environ.get("TG_GATE_ARCH") == "enabled" else "disabled", 1)
|
|
data = data.replace("enabled|disabled", "enabled" if os.environ.get("TG_GATE_CODE_REVIEW") == "enabled" else "disabled", 1)
|
|
data = data.replace("enabled|disabled", "enabled" if os.environ.get("TG_GATE_SECURITY") == "enabled" else "disabled", 1)
|
|
data = data.replace("enabled|disabled", "enabled" if os.environ.get("TG_GATE_PRIVACY") == "enabled" else "disabled", 1)
|
|
data = data.replace("true|false", "true" if os.environ.get("TG_GIT_ENABLED") == "true" else "false", 1)
|
|
data = data.replace("[role][phase] summary - reason", os.environ.get("TG_GIT_COMMIT_FORMAT", "[role][phase] summary - reason"))
|
|
|
|
path.write_text(data)
|
|
PY
|
|
}
|
|
|
|
for f in \
|
|
"$output/00_meta/project.yaml" \
|
|
"$output/00_meta/session.yaml" \
|
|
"$output/00_meta/summary.md" \
|
|
"$output/00_meta/decision_log.md" \
|
|
"$output/00_meta/status.md"; do
|
|
export TARGET_FILE="$f"
|
|
replace_file "$f"
|
|
done
|
|
|
|
echo "Workspace initialized at: $output"
|