Merge remote-tracking branch 'origin/main' into dot

This commit is contained in:
2026-09-09 21:20:10 -04:00
9 changed files with 326 additions and 33 deletions

View File

@@ -5,31 +5,51 @@ end
set host $argv[1]
set branch $argv[2]
set branch_source explicit
if test -z "$branch"
set branch_source current
set branch (git branch --show-current)
end
if test -z "$branch"
echo "deploy-to: could not determine the current branch" >&2
return 2
end
git rev-parse --show-toplevel >/dev/null 2>&1
or begin
echo "deploy-to: run this from a git checkout" >&2
return 2
end
set dirty_lines (git status --porcelain)
if test (count $dirty_lines) -gt 0
echo "deploy-to: refusing to deploy with a dirty local worktree" >&2
set untracked_lines (git status --porcelain --untracked-files=normal | string match -r '^\?\? ')
if test (count $untracked_lines) -gt 0
echo "deploy-to: refusing to deploy untracked files" >&2
echo "deploy-to: git add new files that Nix must see, or remove them" >&2
return 1
end
git fetch origin
or return $status
if test -z "$branch"; or not git rev-parse --verify --quiet refs/remotes/origin/$branch >/dev/null
if test "$branch_source" = explicit
echo "deploy-to: origin/$branch does not exist" >&2
return 1
end
set branch_source upstream
set upstream_ref (git rev-parse --abbrev-ref --symbolic-full-name '@{upstream}' 2>/dev/null)
set branch (string replace -r '^origin/' '' -- $upstream_ref)
end
if test -z "$branch"; or not git rev-parse --verify --quiet refs/remotes/origin/$branch >/dev/null
set branch_source default
set origin_head (git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null)
set branch (string replace -r '^origin/' '' -- $origin_head)
end
if test -z "$branch"
echo "deploy-to: could not determine a deploy branch" >&2
return 2
end
set remote_ref refs/remotes/origin/$branch
git rev-parse --verify --quiet $remote_ref >/dev/null
or begin
@@ -37,17 +57,37 @@ or begin
return 1
end
set local_head (git rev-parse HEAD)
set remote_head (git rev-parse $remote_ref)
if test "$local_head" != "$remote_head"
echo "deploy-to: local HEAD differs from origin/$branch" >&2
echo "deploy-to: push or reset the branch before deploying" >&2
return 1
set has_local_changes false
if not git diff --quiet $remote_ref --
set has_local_changes true
end
set escaped_branch (string escape -- $branch)
set escaped_host (string escape -- $host)
set remote_command "cd ~/dev && git fetch origin && git switch -- $escaped_branch && git reset --hard origin/$escaped_branch && sudo nixos-rebuild switch --flake .#$escaped_host --print-build-logs"
set remote_command "cd ~/dev && git fetch origin && git switch -- $escaped_branch && git reset --hard origin/$escaped_branch && git clean -fd"
set patch_file ""
set remote_patch_file ""
if test "$has_local_changes" = true
set patch_file (mktemp)
set remote_patch_file "/tmp/deploy-to-$host.patch"
git diff --binary $remote_ref -- >$patch_file
or begin
rm -f $patch_file
return $status
end
scp $patch_file $host:$remote_patch_file
set scp_status $status
rm -f $patch_file
if test $scp_status -ne 0
return $scp_status
end
set escaped_remote_patch_file (string escape -- $remote_patch_file)
set remote_command "$remote_command && git apply --index $escaped_remote_patch_file && rm -f $escaped_remote_patch_file"
end
set remote_command "$remote_command && sudo nixos-rebuild switch --flake .#$escaped_host --print-build-logs"
ssh -t $host $remote_command

View File

@@ -3,13 +3,19 @@ load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library")
rust_library(
name = "ariadne_lib",
crate_name = "ariadne",
srcs = ["src/lib.rs"],
srcs = [
"lib.rs",
"pipeline.rs",
],
)
rust_binary(
name = "ariadne",
crate_name = "ariadne_cli",
srcs = ["src/main.rs"],
deps = [":ariadne_lib"],
srcs = ["main.rs"],
deps = [
":ariadne_lib",
"//third_party/crates:clap",
],
)

View File

@@ -3,6 +3,13 @@ name = "ariadne"
version = "0.1.0"
edition = "2024"
[lib]
path = "lib.rs"
[[bin]]
name = "ariadne"
path = "main.rs"
[dependencies]
clap = { version = "4", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }

View File

@@ -1,18 +1,53 @@
# Ariadne
Ariadne analyzes Clew output for a local Ren'Py game install.
Keep project-owned source under `src/ariadne/`.
Keep game-derived files local and ignored under `src/ariadne/output/`.
Ariadne analyzes Clew-produced data for local Ren'Py projects.
Clew owns extraction, preparation, and Ren'Py AST export.
Ariadne consumes Clew's `clew-renpy-ast-v1` JSON from `output/ast/`.
Ariadne maps that data into internal analysis state.
Ariadne consumes Clew output and builds analysis artifacts from it.
Keep project-owned source under `src/ariadne/`; keep generated or game-derived files out of Git.
## Bazel shape
## CLI
Run the current placeholder CLI with:
Run the default target, `all`:
```sh
bazel run //src/ariadne:ariadne
```
By default, Ariadne writes output under `output/`.
Use `--output-dir` to choose a different output directory:
```sh
bazel run //src/ariadne:ariadne -- --output-dir scratch/ariadne-output
```
Run through a specific target stage and its prerequisites:
```sh
bazel run //src/ariadne:ariadne -- parse
bazel run //src/ariadne:ariadne -- add-jump-edges
```
Available target stages:
- `all`
- `parse`
- `add-implicit-edges`
- `add-init-edges`
- `add-jump-edges`
Relative `--output-dir` paths resolve from the workspace root when Ariadne runs through `bazel run`.
## Pipeline skeleton
Ariadne currently has pipeline scaffolding only.
Each executable stage prepares an output directory at:
```text
<output-dir>/graph/<stage>/
```
If a stage output directory already exists and is not empty, Ariadne fails unless `--force` is set.
With `--force`, Ariadne removes the existing stage output directory before recreating it.
Stage implementations are placeholders and do not write graph artifacts yet.

59
src/ariadne/lib.rs Normal file
View File

@@ -0,0 +1,59 @@
use std::fs;
use std::io;
use std::path::PathBuf;
use crate::pipeline::Stage;
pub mod pipeline;
const OUTPUT_GRAPH_DIR: &str = "graph";
pub type Result<T> = io::Result<T>;
#[derive(Debug, Clone)]
pub struct Config {
pub output_dir: PathBuf,
pub force: bool,
pub target_stage: pipeline::Stage,
}
pub fn run(config: &Config) -> Result<()> {
for stage in config.target_stage.with_prerequisites() {
run_stage(config, stage)?;
}
Ok(())
}
fn get_output_dir(config: &Config, stage: Stage) -> Result<PathBuf> {
let output_dir = config.output_dir.join(OUTPUT_GRAPH_DIR).join(stage.name());
if output_dir.exists() && !output_dir.is_dir() {
return Err(io::Error::new(
io::ErrorKind::NotADirectory,
format!("output path is not a directory: {path}", path = output_dir.display()),
));
}
if output_dir.is_dir() && fs::read_dir(&output_dir)?.next().transpose()?.is_some() {
if !config.force {
return Err(io::Error::new(
io::ErrorKind::AlreadyExists,
format!(
"output directory already exists and is not empty: {path}",
path = output_dir.display()
),
));
}
fs::remove_dir_all(&output_dir)?;
}
fs::create_dir_all(&output_dir)?;
Ok(output_dir)
}
fn run_stage(config: &Config, stage: Stage) -> Result<()> {
let output_dir = get_output_dir(config, stage)?;
pipeline::run_stage(stage, &output_dir)
}

51
src/ariadne/main.rs Normal file
View File

@@ -0,0 +1,51 @@
use std::path::PathBuf;
use clap::Parser;
#[derive(Debug, Parser)]
struct Args {
#[arg(long, default_value = "output")]
output_dir: PathBuf,
#[arg(long)]
force: bool,
#[arg(default_value = "all", value_parser = parse_stage)]
target_stage: ariadne::pipeline::Stage,
}
impl From<Args> for ariadne::Config {
fn from(args: Args) -> Self {
ariadne::Config {
output_dir: resolve_workspace_path(args.output_dir),
force: args.force,
target_stage: args.target_stage,
}
}
}
fn parse_stage(value: &str) -> Result<ariadne::pipeline::Stage, String> {
ariadne::pipeline::Stage::ALL
.iter()
.copied()
.find(|stage| stage.name() == value)
.ok_or_else(|| format!("unknown stage: {value}"))
}
// TODO: Move this into a shared Rust utility crate.
fn resolve_workspace_path(path: PathBuf) -> PathBuf {
if path.is_absolute() {
return path;
}
if let Some(workspace) = std::env::var_os("BUILD_WORKSPACE_DIRECTORY") {
return PathBuf::from(workspace).join(path);
}
path
}
fn main() -> ariadne::Result<()> {
let config: ariadne::Config = Args::parse().into();
ariadne::run(&config)
}

101
src/ariadne/pipeline.rs Normal file
View File

@@ -0,0 +1,101 @@
use std::collections::BTreeSet;
use std::path::Path;
use crate::Result;
#[derive(Debug, Default, Clone, Copy, Eq, Hash, PartialEq, Ord, PartialOrd)]
pub enum Stage {
#[default]
All,
Parse,
AddImplicitEdges,
AddInitEdges,
AddJumpEdges,
}
impl Stage {
pub const ALL: &'static [Stage] = &[
Stage::All,
Stage::Parse,
Stage::AddImplicitEdges,
Stage::AddInitEdges,
Stage::AddJumpEdges,
];
pub fn name(self) -> &'static str {
match self {
Stage::All => "all",
Stage::Parse => "parse",
Stage::AddImplicitEdges => "add-implicit-edges",
Stage::AddInitEdges => "add-init-edges",
Stage::AddJumpEdges => "add-jump-edges",
}
}
pub fn prerequisites(self) -> &'static [Stage] {
match self {
Stage::All => &[
Stage::AddImplicitEdges,
Stage::AddInitEdges,
Stage::AddJumpEdges,
],
Stage::Parse => &[],
Stage::AddImplicitEdges => &[Stage::Parse],
Stage::AddInitEdges => &[Stage::Parse],
Stage::AddJumpEdges => &[Stage::Parse],
}
}
pub fn with_prerequisites(self) -> Vec<Stage> {
self.with_prerequisites_impl(BTreeSet::new())
}
fn with_prerequisites_impl(self, seen: BTreeSet<Stage>) -> Vec<Stage> {
if seen.contains(&self) {
return Vec::new();
}
let mut seen = seen;
seen.insert(self);
let mut stages = Vec::new();
for prerequisite in self.prerequisites() {
let prerequisite_stages = prerequisite.with_prerequisites_impl(seen.clone());
seen.extend(prerequisite_stages.iter().copied());
stages.extend(prerequisite_stages);
}
if self != Stage::All {
stages.push(self);
}
stages
}
}
pub fn run_stage(stage: Stage, output_dir: &Path) -> Result<()> {
match stage {
Stage::All => Ok(()),
Stage::Parse => run_parse(output_dir),
Stage::AddImplicitEdges => run_add_implicit_edges(output_dir),
Stage::AddInitEdges => run_add_init_edges(output_dir),
Stage::AddJumpEdges => run_add_jump_edges(output_dir),
}
}
fn run_parse(_output_dir: &Path) -> Result<()> {
Ok(())
}
fn run_add_implicit_edges(_output_dir: &Path) -> Result<()> {
Ok(())
}
fn run_add_init_edges(_output_dir: &Path) -> Result<()> {
Ok(())
}
fn run_add_jump_edges(_output_dir: &Path) -> Result<()> {
Ok(())
}

View File

@@ -1,3 +0,0 @@
pub fn run() {
println!("Ariadne");
}

View File

@@ -1,3 +0,0 @@
fn main() {
ariadne::run();
}