Awesome Go

stacktower

CategoryUtilities
SubcategoryUtilities
Stars0

Visualize dependency graphs as physical tower structures, inspired by XKCD #2347

About stacktower

This large README is shown as a plain-text preview. Read the full README at the source

# Stacktower

[![CI](https://github.com/matzehuels/stacktower/actions/workflows/ci.yml/badge.svg)](https://github.com/matzehuels/stacktower/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/matzehuels/stacktower/graph/badge.svg)](https://codecov.io/gh/matzehuels/stacktower)
[![Go Report Card](https://goreportcard.com/badge/github.com/matzehuels/stacktower)](https://goreportcard.com/report/github.com/matzehuels/stacktower)
[![Go Reference](https://pkg.go.dev/badge/github.com/matzehuels/stacktower.svg)](https://pkg.go.dev/github.com/matzehuels/stacktower)
[![Release](https://img.shields.io/github/v/release/matzehuels/stacktower)](https://github.com/matzehuels/stacktower/releases)
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

Inspired by [XKCD #2347](https://xkcd.com/2347/), Stacktower renders dependency graphs as **physical towers** where blocks rest on what they depend on. Your application sits at the top, supported by libraries below—all the way down to that one critical package maintained by _some dude in Nebraska_.

<p align="center">
  <img src="blogpost/plots/showcase/python/fastapi.svg" alt="FastAPI dependency tower" width="600">
</p>

---

<p align="center">
  <a href="https://app.stacktower.io"><strong>🚀 Try the Web App</strong></a> · <a href="https://www.stacktower.io"><strong>📖 Read the Story</strong></a>
</p>

---

> ⚠️ **Note:** The ordering algorithms are still experimental and may not produce nicely stacked towers for projects with a large number of dependencies. Results can vary. We're actively working on improvements.

## Installation

### Homebrew (macOS/Linux)

```bash
brew install matzehuels/tap/stacktower
```

### Go

```bash
go install github.com/matzehuels/stacktower@latest
```

### From Source

```bash
git clone https://github.com/matzehuels/stacktower.git
cd stacktower
go build -o bin/stacktower ./cmd/stacktower
```

## Quick Start

```bash
# Render the included Flask example (XKCD-style tower is the default)
stacktower render examples/real/flask.json -o flask.svg
```

## Usage

Stacktower works in two stages: **parse** dependency data from package registries or manifest files, then **render** visualizations.

### Parsing Dependencies

The `parse` command auto-detects whether you're providing a package name or a manifest file:

```bash
stacktower parse <language> <package-or-file> [flags]
```

**Supported languages:** `python`, `rust`, `javascript`, `ruby`, `php`, `java`, `go`

#### From Package Registries

```bash
stacktower parse python fastapi -o fastapi.json                  # PyPI
stacktower parse rust serde -o serde.json                        # crates.io
stacktower parse javascript yargs -o yargs.json                  # npm
stacktower parse ruby rails -o rails.json                        # RubyGems
stacktower parse php monolog/monolog -o monolog.json             # Packagist
stacktower parse java com.google.guava:guava -o guava.json       # Maven Central
stacktower parse go github.com/gin-gonic/gin -o gin.json         # Go Module Proxy
```

Use `package@version` to pin a specific version. Without it, the latest version is resolved:

```bash
stacktower parse python [email protected] -o fastapi.json
stacktower parse rust [email protected] -o serde.json
stacktower parse javascript @angular/[email protected] -o angular.json  # scoped packages work too
```

#### From Manifest Files (Auto-Detected)

```bash
stacktower parse python examples/manifest/poetry.lock -o deps.json
stacktower parse python examples/manifest/requirements.txt -o deps.json
stacktower parse rust examples/manifest/Cargo.toml -o deps.json
stacktower parse javascript examples/manifest/package.json -o deps.json
stacktower parse ruby examples/manifest/Gemfile -o deps.json
stacktower parse php examples/manifest/composer.json -o deps.json
stacktower parse java examples/manifest/pom.xml -o deps.json
stacktower parse go examples/manifest/go.mod -o deps.json
```

When the argument exists on disk or matches a known manifest filename, Stacktower automatically parses it as a manifest.

The project name (root node) is auto-detected from the manifest or a sibling file:

- **Cargo.toml**: `[package].name`
- **go.mod**: `module` directive
- **package.json**: `name` field
- **composer.json**: `name` field
- **pom.xml**: `groupId:artifactId`
- **poetry.lock / requirements.txt**: `pyproject.toml` (sibling)
- **Gemfile**: `*.gemspec` (sibling)

Use `--name` to override the auto-detected name:

```bash
stacktower parse python requirements.txt --name="my-project" -o deps.json
stacktower parse ruby Gemfile -n my-rails-app -o deps.json
```

#### Metadata Enrichment

By default, Stacktower enriches packages with GitHub metadata (stars, maintainers, last commit) for richer visualizations. Set `GITHUB_TOKEN` to enable this:

```bash
export GITHUB_TOKEN=your_token
stacktower parse python fastapi -o fastapi.json

# Skip enrichment if you don't have a token
stacktower parse python fastapi --enrich=false -o fastapi.json
```

#### Vulnerability Scanning

Stacktower can scan your dependency graph for known vulnerabilities using the [OSV.dev](https://osv.dev/) database. Add the `--security-scan` flag during parsing to annotate every package with its highest vulnerability severity:

```bash
# Parse with vulnerability scanning
stacktower parse python fastapi --security-scan -o fastapi.json

# Parse a manifest with scanning
stacktower parse javascript package.json --security-scan -o deps.json
```

When `--security-scan` is enabled, Stacktower queries OSV.dev in a single batch request and writes severity data (`critical`, `high`, `medium`, `low`) into each node's metadata. The scanned graph is cached separately from non-scanned graphs, so subsequent runs are instant.

During rendering, vulnerable packages are automatically colour-coded by severity (red for critical, orange for high, amber for medium, yellow for low). Both tower and node-link visualizations support this. Use `--show-vulns=false` to suppress the colours while keeping the data in the graph:

```bash
# Render with vulnerability colours (default when data exists)
stacktower render fastapi.json -o fastapi.svg

# Render without vulnerability colours
stacktower render fastapi.json --show-vulns=false -o fastapi.svg
```

The `--show-vulns` flag is available on the `render`, `layout`, and `visualize` commands and defaults to `true`.

### Resolving Dependencies

The `resolve` command is a lightweight alternative to `parse`, designed for quickly testing dependency resolution on local manifest and lock files. It auto-detects the language from the filename and prints a human-readable dependency tree instead of graph JSON:

```bash
stacktower resolve <manifest-file>
stacktower resolve <language> <package[@version]>
```

#### From Manifest / Lock Files (Auto-Detected)

```bash
stacktower resolve poetry.lock                          # Python (Poetry)
stacktower resolve uv.lock                              # Python (uv)
stacktower resolve requirements.txt                     # Python (pip)
stacktower resolve package-lock.json                    # JavaScript (npm)
stacktower resolve Cargo.lock                           # Rust
stacktower resolve Gemfile.lock                         # Ruby
stacktower resolve composer.lock                        # PHP
stacktower resolve go.mod                               # Go
```

No `--language` flag needed — the filename is enough.

#### From Package Registries

```bash
stacktower resolve python fastapi
stacktower resolve rust [email protected]
stacktower resolve javascript yargs
```

#### Output

By default, `resolve` prints an indented dependency tree to stdout:

```
fastapi 0.104.1
  starlette 0.27.0
    anyio 4.0.0
      sniffio 1.3.0
  pydantic 2.5.2
    pydantic-core 2.14.5
    typing-extensions 4.8.0

Resolved 7 packages (max depth: 3, direct: 2)
```

Use `-o` to save the resolution as JSON (same format as `parse`), suitable for piping into `stacktower render`:

```bash
stacktower resolve Cargo.lock -o deps.json
stacktower render deps.json -o deps.svg
```

### Listing Versions

The `list` command shows all available versions of a package from its registry, sorted semantically with the latest stable version highlighted:

```bash
stacktower list <language> <package> [flags]
```

```bash
stacktower list python fastapi
stacktower list rust serde
stacktower list javascript react
stacktower list go github.com/gin-gonic/gin
```

Output (truncated to 20 most recent by default):

```
  fastapi  python · 277 versions
  latest   0.129.2

  0.129.1   0.129.0   0.128.8   0.128.7   0.128.6   0.128.5   0.128.4
  0.128.3   0.128.2   0.128.1   0.128.0   0.127.1   0.127.0   0.126.0
  0.125.0   0.124.4   0.124.3   0.124.2   0.124.1   0.124.0

  … 256 older versions not shown (use --all to show all)
```

Use `--all` to see every version:

```bash
stacktower list python fastapi --all
```

Pre-release versions (alpha, beta, rc, dev) are shown dimmed and sorted after stable releases.

### Rendering

The `render` command generates visualizations from parsed JSON graphs:

```bash
stacktower render <file> [flags]
```

This is a shortcut that combines `layout` and `visualize` in one step. For more control, you can run them separately:

```bash
# Two-step workflow with intermediate layout
stacktower layout examples/real/flask.json -o flask.layout.json
stacktower visualize flask.layout.json -o flask.svg
```

#### Visualization Types

```bash
# Hand-drawn XKCD-style tower (default)
stacktower render examples/real/flask.json -o flask.svg

# Disable hand-drawn effects for a cleaner look
stacktower render examples/real/serde.json --style simple --randomize=false --popups=false -o serde.svg

# Traditional node-link diagram (uses Graphviz DOT)
stacktower render examples/real/yargs.json -t nodelink -o yargs.svg
```

#### Output Formats

```bash
# SVG output (default)
stacktower render examples/real/flask.json -o flask.svg

# JSON layout export (for external tools or re-rendering)
stacktower render examples/real/flask.json -f json -o flask.json

# PDF output
stacktower render examples/real/flask.json -f pdf -o flask.pdf

# PNG output (2x scale by default)
stacktower render examples/real/flask.json -f png -o flask.png

# Multiple formats at once (outputs flask.svg, flask.json, flask.pdf)
stacktower render examples/real/flask.json -f svg,json,pdf -o flask
```

Output path behavior:

- **No `-o`**: Derives from input (`input.json` → `input.<format>`)
- **Single format**: Uses exact path (`-o out.svg` → `out.svg`)
- **Multiple formats**: Strips extension, adds format (`-o out -f svg,json` → `out.svg`, `out.json`)

> **Note:** PDF and PNG output requires [librsvg](https://wiki.gnome.org/Projects/LibRsvg):
>
> - macOS: `brew install librsvg`
> - Linux: `apt install librsvg2-bin`

### Included Examples

The repository ships with pre-parsed graphs and manifest files so you can experiment immediately:

```bash
# Real packages with full metadata (XKCD-style by default)
stacktower render examples/real/flask.json -o flask.svg
stacktower render examples/real/serde.json -o serde.svg
stacktower render examples/real/yargs.json -o yargs.svg

# With Nebraska guy maintainer ranking
stacktower render examples/real/flask.json --nebraska -o flask.svg

# Synthetic test cases for layout algorithm testing
stacktower render examples/test/diamond.json -o diamond.svg
stacktower render examples/test/crossing.json -o crossing.svg
```

> **Note:** For accurate Nebraska rankings, parse with `--contributors` to fetch maintainer data:
> ```bash
> stacktower parse python flask --contributors -o flask.json
> stacktower render flask.json --nebraska -o flask.svg
> ```

#### Example Manifest & Lock Files

The `examples/manifest/` directory contains sample manifest and lock files for every supported ecosystem, useful for testing `resolve` and `parse` locally:

| File | Language | Type |
| --- | --- | --- |
| `poetry.lock` | Python | Lock file |
| `uv.lock` | Python | Lock file |
| `pyproject.toml` | Python | Manifest |
| `requirements.txt` | Python | Manifest |
| `package-lock.json` | JavaScript | Lock file |
| `package.json` | JavaScript | Manifest |
| `Cargo.lock` | Rust | Lock file |
| `Cargo.toml` | Rust | Manifest |
| `Gemfile.lock` | Ruby | Lock file |
| `Gemfile` | Ruby | Manifest |
| `composer.lock` | PHP | Lock file |
| `composer.json` | PHP | Manifest |
| `go.mod` | Go | Manifest |
| `pom.xml` | Java | Manifest |

```bash
# Quickly test dependency resolution on any of these
stacktower resolve examples/manifest/poetry.lock
stacktower resolve examples/manifest/Cargo.lock
stacktower resolve examples/manifest/package-lock.json
stacktower resolve examples/manifest/Gemfile.lock
stacktower resolve examples/manifest/composer.lock
```

## Command Reference

Stacktower provides a suite of commands organized around a parse → render workflow. Each command is documented below with all available options and reproducible examples.

### Global Options

These flags apply to all commands:

| Flag              | Description                                                |
| ----------------- | ---------------------------------------------------------- |
| `-v`, `--verbose` | Enable debug logging (search space info, timing details)   |
| `-q`, `--quiet`   | Suppress non-essential output (success messages, stats)    |
| `-h`, `--help`    | Show help for any command                                  |
| `--version`       | Show version information                                   |

---

### `stacktower parse`

Parse dependency graphs from package registries or local manifest files.

```bash
stacktower parse <language> <package-or-file> [flags]
stacktower parse <manifest-file>                       # Auto-detect language
stacktower parse github [owner/repo]                   # Parse from GitHub
```

#### Parse Options

| Flag                    | Description                                                                          |
| ----------------------- | ------------------------------------------------------------------------------------ |
| `-o`, `--output`        | Output file (stdout if empty)                                                        |
| `-n`, `--name`          | Project name for manifest parsing (auto-detected if not set)                         |
| `--max-depth N`         | Maximum dependency depth (default: 10, max: 100)                                     |
| `--max-nodes N`         | Maximum packages to fetch (default: 5000, max: 50000)                                |
| `--workers N`           | Concurrent fetch workers (default: 20)                                               |
| `--enrich`              | Enrich with GitHub metadata — stars, maintainers (default: true)                     |
| `--contributors`        | Fetch GitHub contributors for Nebraska rankings (slower API calls)                   |
| `--security-scan`       | Scan dependencies for known vulnerabilities via OSV.dev                              |
| `--dependency-scope`    | Dependency scope: `prod_only` (default) or `all` (includes dev dependencies)         |
| `--include-prerelease`  | Include prerelease versions (alpha/beta/rc/dev) in resolution                        |
| `--runtime-version`     | Target runtime version for marker evaluation (e.g., `3.11` for Python)               |
| `--no-cache`            | Disable caching                                                                      |

#### Parse Examples

```bash
# Parse from package registries
stacktower parse python requests -o requests.json
stacktower parse python [email protected] -o fastapi.json      # Pin specific version
stacktower parse rust serde -o serde.json
stacktower parse javascript @angular/[email protected] -o angular.json
stacktower parse go github.com/gin-gonic/gin -o gin.json

# Parse from manifest files (language auto-detected)
stacktower parse poetry.lock -o deps.json
stacktower parse package-lock.json -o deps.json
stacktower parse Cargo.lock -o deps.json

# Parse with explicit language
stacktower parse python requirements.txt -o deps.json
stacktower parse javascript package.json --name="my-app" -o deps.json

# Include dev dependencies
stacktower parse python poetry.lock --dependency-scope all -o deps.json

# Target specific Python version for environment markers
stacktower parse python fastapi --runtime-version 3.9 -o fastapi-py39.json

# Scan for vulnerabilities during parsing
stacktower parse python django --security-scan -o django.json

# Full metadata including contributors (slower)
stacktower parse python flask --contributors -o flask.json
```

#### Parse from GitHub

Parse dependencies directly from a GitHub repository with interactive selection:

```bash
stacktower parse github                              # Full interactive flow
stacktower parse github owner/repo                   # Select ref + manifest
stacktower parse github owner/repo --ref v2.0.0     # Parse at specific tag
stacktower parse github owner/repo --ref main       # Explicit branch
stacktower parse github owner/repo --timeout 10m    # Custom timeout
```

| Flag        | Description                                         |
| ----------- | --------------------------------------------------- |
| `--ref`     | Git ref (branch, tag, or commit SHA)                |
| `--timeout` | Timeout for GitHub operations (default: 5m)         |

> **Private repos:** To parse private repositories, first install the GitHub App with
> `stacktower github install` and grant access to the repos you need.

---

### `stacktower resolve`

Lightweight alternative to `parse` for quick dependency resolution testing. Auto-detects language from manifest filenames and outputs a human-readable dependency tree.

```bash
stacktower resolve <manifest-file>
stacktower resolve <language> <package[@version]>
```

#### Resolve Options

| Flag                   | Description                                                                  |
| ---------------------- | ---------------------------------------------------------------------------- |
| `-o`, `--output`       | Output file for JSON (stdout shows tree by default)                          |
| `-n`, `--name`         | Project name (for manifest parsing)                                          |
| `--max-depth N`        | Maximum dependency depth (default: 10)                                       |
| `--max-nodes N`        | Maximum packages to fetch (default: 5000)                                    |
| `--enrich`             | Enrich with GitHub metadata (off by default, unlike `parse`)                 |
| `--dependency-scope`   | Dependency scope: `prod_only` (default) or `all`                             |
| `--include-prerelease` | Include prerelease versions in resolution                                    |
| `--runtime-version`    | Target runtime version for marker evaluation                                 |
| `--no-cache`           | Disable caching                                                              |

#### Resolve Examples

```bash
# Auto-detect language from filename
stacktower resolve poetry.lock
stacktower resolve Cargo.lock
stacktower resolve package-lock.json
stacktower resolve go.mod

# Resolve from registry
stacktower resolve python fastapi
stacktower resolve rust [email protected]

# Save resolution as JSON
stacktower resolve poetry.lock -o deps.json

# Include dev dependencies
stacktower resolve poetry.lock --dependency-scope all

# Target specific runtime
stacktower resolve python fastapi --runtime-version 3.8
```

#### resolve vs parse

| | `resolve` | `parse` |
| --- | --- | --- |
| Language detection | Auto-detected from filename | Must specify language subcommand |
| Default output | Human-readable tree | Graph JSON |
| Metadata enrichment | Off (opt-in with `--enrich`) | On by default (`--enrich=false` to skip) |
| Best for | Local testing, inspecting deps | Rendering pipeline, CI |

---

Frequently Asked Questions

What is stacktower?

stacktower is a Utilities library for the Go programming language. Visualize dependency graphs as physical tower structures, inspired by XKCD #2347

How do I install stacktower?

Install stacktower with the Go module system using `go get matzehuels/stacktower`. Check the repository for the current installation instructions.

What category does stacktower belong to?

stacktower is listed under Utilities, specifically Utilities.

← Back to Utilities