From 5528a95fb04bc23d81a420d9df6972e152ee71ec Mon Sep 17 00:00:00 2001 From: j Date: Mon, 4 May 2026 09:03:27 -0500 Subject: [PATCH 1/2] ADD: Docker capability --- .gitignore | 1 + README.md | 36 ++++++++++++++++++++++++++ actions/govbot/.dockerignore | 31 +++++++++++++++++++++++ actions/govbot/Dockerfile | 44 ++++++++++++++++++++++++++++++++ actions/govbot/src/pipeline.rs | 46 +++++++++++++++++++++++----------- 5 files changed, 143 insertions(+), 15 deletions(-) create mode 100644 actions/govbot/.dockerignore create mode 100644 actions/govbot/Dockerfile diff --git a/.gitignore b/.gitignore index 7c3d13e..8f11d00 100644 --- a/.gitignore +++ b/.gitignore @@ -120,3 +120,4 @@ sample_data/* +.govbot diff --git a/README.md b/README.md index 6bd480e..58af48b 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,42 @@ govbot update # update govbot to latest version govbot --help # see all commands and options ``` +### Docker + +* Build the image from source: + +```bash +docker build -t govbot:latest -f actions/govbot/Dockerfile actions/govbot/ +``` + +* Run the setup wizard (writes `govbot.yml` and other config files to the current directory): + +```bash +docker run --rm -it -v $(pwd):/home/govbot govbot:latest +``` + +Mounting `$(pwd)` to `/home/govbot` ensures config files and downloaded data persist between runs. + +#### Common Docker Commands + +```bash +# Run the full pipeline (clone, tag, build) using govbot.yml config +docker run --rm -v $(pwd):/home/govbot govbot:latest + +# Clone a specific state (or use "all" for all states) +docker run --rm -v $(pwd):/home/govbot govbot:latest clone il + +# Stream legislative activity logs +docker run --rm -v $(pwd):/home/govbot govbot:latest logs + +# Build RSS feeds (requires tags defined in govbot.yml) +docker run --rm -v $(pwd):/home/govbot govbot:latest build + +# Delete all cloned repos, then re-clone only the state in govbot.yml +docker run --rm -v $(pwd):/home/govbot govbot:latest delete all +docker run --rm -v $(pwd):/home/govbot govbot:latest +``` + # 🏛️ Govbot Legislation Effort - Nearly all state governments diff --git a/actions/govbot/.dockerignore b/actions/govbot/.dockerignore new file mode 100644 index 0000000..105770d --- /dev/null +++ b/actions/govbot/.dockerignore @@ -0,0 +1,31 @@ +# Build artifacts +target/ +# Keep Cargo.lock for reproducible builds +# Cargo.lock + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# Git +.git/ +.gitignore + +# Docs +docs/ + +# Other actions +../format/ +../scrape/ +../extract/ +../format/ +../pipeline-manager/ +../report-publisher/ + +# Mocks +mocks/ + +# Snapshots +__snapshots__/ \ No newline at end of file diff --git a/actions/govbot/Dockerfile b/actions/govbot/Dockerfile new file mode 100644 index 0000000..52e8b65 --- /dev/null +++ b/actions/govbot/Dockerfile @@ -0,0 +1,44 @@ +# syntax=docker/dockerfile:1 + +# ── Stage 1: Build ──────────────────────────────────────────────── +FROM docker.io/library/rust:1-slim AS builder + +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + pkg-config \ + libssl-dev \ + libgit2-dev \ + libonnx-dev \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /home/govbot + +# Copy dependency manifests first (layer caching) +COPY Cargo.toml Cargo.lock ./ + +# Fetch dependencies (populates Cargo cache) +RUN cargo fetch + +# Copy full source +COPY . . + +# Build release binary +RUN cargo build --release --bin govbot + +# ── Stage 2: Runtime ───────────────────────────────────────────── +# Use trixie-slim to match the glibc version in rust:1-slim (builder) +FROM docker.io/library/debian:trixie-slim AS runtime + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + git \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /home/govbot + +COPY --from=builder \ + /home/govbot/target/release/govbot \ + /usr/local/bin/govbot + +ENTRYPOINT ["govbot"] \ No newline at end of file diff --git a/actions/govbot/src/pipeline.rs b/actions/govbot/src/pipeline.rs index f744cca..88a1aac 100644 --- a/actions/govbot/src/pipeline.rs +++ b/actions/govbot/src/pipeline.rs @@ -2,6 +2,14 @@ use anyhow::{Context, Result}; use std::path::Path; use std::process::{Command, Stdio}; +/// Check if the config has non-empty tags defined. +fn has_tags(config: &serde_json::Value) -> bool { + config.get("tags") + .and_then(|t| t.as_object()) + .map(|o| !o.is_empty()) + .unwrap_or(false) +} + /// Run the full govbot pipeline: clone/update → tag → build. /// /// Smart update behavior: @@ -15,6 +23,9 @@ pub fn run_pipeline(config_path: &Path) -> Result<()> { .parent() .unwrap_or_else(|| Path::new(".")); + // Load config once for reuse throughout the pipeline + let config = crate::publish::load_config(config_path)?; + let repos_dir = cwd.join(".govbot").join("repos"); let has_repos = repos_dir.exists() && std::fs::read_dir(&repos_dir) @@ -37,7 +48,6 @@ pub fn run_pipeline(config_path: &Path) -> Result<()> { .status() } else { // First run: clone based on config - let config = crate::publish::load_config(config_path)?; let repos = crate::publish::get_repos_from_config(&config); let mut cmd = Command::new(&govbot_bin); @@ -78,22 +88,28 @@ pub fn run_pipeline(config_path: &Path) -> Result<()> { _ => {} } - // Step 3: Build RSS feeds - eprintln!(); - eprintln!("=== Step 3/3: Building RSS feeds ==="); - eprintln!(); + // Step 3: Build RSS feeds (only if tags are defined) + if has_tags(&config) { + eprintln!(); + eprintln!("=== Step 3/3: Building RSS feeds ==="); + eprintln!(); - let build_status = Command::new(&govbot_bin) - .arg("build") - .current_dir(cwd) - .stdin(Stdio::inherit()) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .status() - .context("Failed to run govbot build")?; + let build_status = Command::new(&govbot_bin) + .arg("build") + .current_dir(cwd) + .stdin(Stdio::inherit()) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .status() + .context("Failed to run govbot build")?; - if !build_status.success() { - anyhow::bail!("Build step failed with exit code: {}", build_status.code().unwrap_or(-1)); + if !build_status.success() { + anyhow::bail!("Build step failed with exit code: {}", build_status.code().unwrap_or(-1)); + } + } else { + eprintln!(); + eprintln!("=== Step 3/3: Skipping RSS feed build ==="); + eprintln!("No tags defined in govbot.yml. Add tags, then run 'govbot build' to generate feeds."); } eprintln!(); From 4f67f1d5e499398634e9a8cd81e44292c7f33ae5 Mon Sep 17 00:00:00 2001 From: j Date: Mon, 4 May 2026 10:40:29 -0500 Subject: [PATCH 2/2] ADD: govbot.yml for Docker to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 8f11d00..7f7469d 100644 --- a/.gitignore +++ b/.gitignore @@ -121,3 +121,4 @@ sample_data/* .govbot +govbot.yml \ No newline at end of file