Skip to content

Commit 5f97856

Browse files
committed
feat: initial public release of drift
Fast, interactive file comparison tool with a terminal UI. Compare directories, archives (IPA, APK, JAR, tar), Mach-O binaries, plists, and text files — with structured JSON output for CI pipelines. Architecture: - compare/ — pure-Go comparison engine (tree, binary, plist, text modes) - tui/ — Bubbletea-based interactive terminal UI with split-pane layout - cmd/drift/ — CLI entry point with kong Cross-platform: macOS, Linux, Windows. Binary analysis and plist conversion degrade gracefully when platform tools are unavailable.
1 parent b2d4841 commit 5f97856

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+6739
-69
lines changed
Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,21 @@
11
---
2-
name: 🐛 Bug Report
3-
about: Thank you for taking the time, please report a reproducible bug
4-
title: "[Bug] <Bug Title Here>"
2+
name: Bug Report
3+
about: Report a bug with drift
54
labels: bug
6-
assignees: add codeowner's @name here
7-
5+
assignees: luispadron
86
---
97

10-
**Describe the bug**
11-
*A clear and concise description of what the bug is.*
8+
**What happened?**
129

13-
**To Reproduce:**
14-
*Steps to reproduce the behavior:*
15-
1. Go to '...'
16-
2. Click on '....'
17-
3. Scroll down to '....'
18-
4. See error
10+
**Command run:**
11+
```
12+
drift ...
13+
```
1914

2015
**Expected behavior:**
21-
*A clear and concise description of what you expected to happen.*
22-
23-
**Supporting Material**
24-
*If applicable, add screenshots, output log and/or other documentation to help explain your problem.*
2516

26-
**Environment (please complete the following information):**
27-
- OS: [ex: iOS]
28-
- Version
17+
**Environment:**
18+
- OS:
19+
- drift version (`drift --version`):
2920

3021
**Additional context**
31-
Add any other context that you feel is relevant about the problem here.

.github/ISSUE_TEMPLATE/config.yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
1-
contact_links:
2-
- name: ❓ Questions and Help 🤔
3-
url: https://discord.gg/block-opensource (/add your discord channel if applicable)
4-
about: This issue tracker is not for support questions. Please refer to the community for more help.
1+
blank_issues_enabled: false

.github/assets/drift_logo.png

121 KB
Loading

.github/workflows/ci.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
build-and-test:
10+
runs-on: macos-latest
11+
steps:
12+
- uses: actions/checkout@v6
13+
14+
- uses: actions/setup-go@v6
15+
with:
16+
go-version-file: go.mod
17+
18+
- name: Build
19+
run: go build -o drift ./cmd/drift
20+
21+
- name: Test
22+
run: go test ./...
23+
24+
- name: Vet
25+
run: go vet ./...

.github/workflows/release.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: Release
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
version:
7+
description: "Release version (e.g. 0.1.0, without 'v' prefix)"
8+
required: true
9+
type: string
10+
11+
permissions:
12+
contents: write
13+
14+
jobs:
15+
release:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v6
19+
with:
20+
fetch-depth: 0
21+
22+
- uses: actions/setup-go@v6
23+
with:
24+
go-version-file: go.mod
25+
26+
- name: Create and push tag
27+
run: |
28+
git tag "v${{ inputs.version }}"
29+
git push origin "v${{ inputs.version }}"
30+
31+
- name: Run GoReleaser
32+
uses: goreleaser/goreleaser-action@v7
33+
with:
34+
version: latest
35+
args: release --clean
36+
env:
37+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/drift
2+
.agents/
3+
.DS_Store

.goreleaser.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
version: 2
2+
3+
builds:
4+
- main: ./cmd/drift
5+
binary: drift
6+
ldflags:
7+
- -s -w -X main.Version={{.Version}}
8+
goos:
9+
- linux
10+
- darwin
11+
- windows
12+
goarch:
13+
- amd64
14+
- arm64
15+
16+
archives:
17+
- name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
18+
format_overrides:
19+
- goos: windows
20+
formats: ["zip"]
21+
22+
checksum:
23+
name_template: "checksums.txt"
24+
25+
changelog:
26+
sort: asc

CLAUDE.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# CLAUDE.md
2+
3+
## Build & Test
4+
5+
- **Build:** `go build -o drift ./cmd/drift`
6+
- **Test:** `go test ./...` or `go test ./compare -run TestName`
7+
- **Vet:** `go vet ./...`
8+
9+
## Architecture
10+
11+
`drift` is a two-layer tool: a comparison engine and an interactive TUI.
12+
13+
### Compare engine (`compare/`)
14+
15+
Pure-Go diffing logic. `Compare(pathA, pathB, mode)` returns a `*Result` with a tree of `Node`s representing the comparison. `Detail(result, node)` produces the detailed diff for a single node.
16+
17+
Supported modes: tree (directories/archives), binary (Mach-O), plist, text. Mode is auto-detected from inputs.
18+
19+
Key files:
20+
- `compare.go` — entry point, mode detection, tree comparison
21+
- `directory.go` — directory walking and tree building
22+
- `archive.go` — archive extraction (zip, tar, tar.gz, tar.bz2)
23+
- `binary.go` — Mach-O analysis via nm/size/otool
24+
- `plist.go` — plist conversion via plutil
25+
- `text.go` — line-by-line unified diff
26+
- `types.go``Result`, `Node`, `DetailResult` types
27+
- `hash.go` — content hashing for change detection
28+
29+
### TUI (`tui/`)
30+
31+
Bubbletea-based interactive terminal UI. Three-tier component model:
32+
33+
1. **App** (`app.go`) — root model, layout, keyboard dispatch
34+
2. **Components** (`tree.go`, `detail.go`, `search.go`, `overlay.go`, `alert.go`) — stateful sub-models
35+
3. **Views** (`view_*.go`, `summary.go`, `render.go`) — pure render functions, no state
36+
37+
- `styles.go` — all lipgloss styles
38+
- `help.go` — keybinding definitions
39+
- `components.go` — detail content builder
40+
41+
### Entry point
42+
43+
- `cmd/drift/main.go` — CLI struct, kong parser, `run()` method
44+
45+
## Code style
46+
47+
- Views are pure functions: `func renderX(width int, data T) string`
48+
- Components own state and implement `Update`/`View`
49+
- Use lipgloss for all styling — no raw ANSI

CODEOWNERS

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,4 @@
11
# This CODEOWNERS file denotes the project leads
22
# and encodes their responsibilities for code review.
33

4-
# Instructions: At a minimum, replace the '@GITHUB_USER_NAME_GOES_HERE'
5-
# here with at least one project lead.
6-
7-
# Lines starting with '#' are comments.
8-
# Each line is a file pattern followed by one or more owners.
9-
# The format is described: https://github.blog/2017-07-06-introducing-code-owners/
10-
11-
# These owners will be the default owners for everything in the repo.
124
* @luispadron
13-
14-
15-
# -----------------------------------------------
16-
# BELOW THIS LINE ARE TEMPLATES, UNUSED
17-
# -----------------------------------------------
18-
# Order is important. The last matching pattern has the most precedence.
19-
# So if a pull request only touches javascript files, only these owners
20-
# will be requested to review.
21-
# *.js @octocat @github/js
22-
23-
# You can also use email addresses if you prefer.
24-

README.md

Lines changed: 95 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,107 @@
1-
# drift README
1+
# drift
22

3-
Congrats, project leads! You got a new project to grow!
3+
<p align="center">
4+
<img src=".github/assets/drift_logo.png" alt="Drift logo" width="700">
5+
</p>
46

5-
This stub is meant to help you form a strong community around your work. It's yours to adapt, and may
6-
diverge from this initial structure. Just keep the files seeded in this repo, and the rest is yours to evolve!
7+
A **fast**, **interactive** file **comparison tool**.
78

8-
## Introduction
9+
Compare directories, archives, binaries, plists, and text files with a terminal UI or structured JSON output.
910

10-
Orient users to the project here. This is a good place to start with an assumption
11-
that the user knows very little - so start with the Big Picture and show how this
12-
project fits into it.
11+
## Installation
1312

14-
Then maybe a dive into what this project does.
13+
```sh
14+
go install github.com/block/drift/cmd/drift@latest
15+
```
1516

16-
Diagrams and other visuals are helpful here. Perhaps code snippets showing usage.
17+
Or [download a pre-built binary for your platform from a release](https://github.com/block/drift/releases)
1718

18-
Project leads should complete, alongside this `README`:
19+
Or build from source:
1920

20-
* [CODEOWNERS](./CODEOWNERS) - set project lead(s)
21-
* [CONTRIBUTING.md](./CONTRIBUTING.md) - Fill out how to: install prereqs, build, test, run, access CI, chat, discuss, file issues
22-
* [Bug-report.md](.github/ISSUE_TEMPLATE/bug-report.md) - Fill out `Assignees` add codeowners @names
23-
* [config.yml](.github/ISSUE_TEMPLATE/config.yml) - remove "(/add your discord channel..)" and replace the url with your Discord channel if applicable
21+
```sh
22+
gh repo clone block/drift
23+
cd drift
24+
go run ./cmd/drift --help
25+
```
2426

25-
The other files in this template repo may be used as-is:
27+
## Usage
2628

27-
* [GOVERNANCE.md](./GOVERNANCE.md)
28-
* [LICENSE](./LICENSE)
29+
```sh
30+
# Compare two directories
31+
drift path/a path/b
2932

30-
## Project Resources
33+
# Compare two archives (.ipa, .apk, .aar, .jar, .tar.gz, .tar.bz2)
34+
drift app-v1.ipa app-v2.ipa
3135

32-
| Resource | Description |
33-
| ------------------------------------------ | ------------------------------------------------------------------------------ |
34-
| [CODEOWNERS](./CODEOWNERS) | Outlines the project lead(s) |
35-
| [GOVERNANCE.md](./GOVERNANCE.md) | Project governance |
36-
| [LICENSE](./LICENSE) | Apache License, Version 2.0 |
36+
# Compare two binaries
37+
drift bin-v1 bin-v2
38+
39+
# Force a specific comparison mode
40+
drift -m binary lib-v1.dylib lib-v2.dylib
41+
42+
# JSON output (non-interactive, for scripting)
43+
drift --json path/a path/b
44+
```
45+
46+
### Comparison modes
47+
48+
drift auto-detects the comparison mode based on the inputs:
49+
50+
| Mode | Inputs | What it shows |
51+
|------|--------|---------------|
52+
| **tree** | Directories, archives | File tree with added/removed/modified indicators, per-file diffs |
53+
| **binary** | Mach-O binaries | Sections, sizes, symbols, load commands. Requires `nm` and `size` |
54+
| **plist** | Property lists (.plist) | Structured key-value diff. Binary plists require `plutil` |
55+
| **text** | Everything else | Line-by-line unified diff |
56+
57+
Use `-m <mode>` to override auto-detection.
58+
59+
### Archives
60+
61+
drift transparently extracts and compares the contents of:
62+
- `.ipa` (iOS app bundles)
63+
- `.apk` (Android app bundles)
64+
- `.aar` (Android libraries)
65+
- `.jar` (Java archives)
66+
- `.tar`, `.tar.gz` / `.tgz`, `.tar.bz2`
67+
68+
## Interactive TUI
69+
70+
When stdout is a terminal, drift launches an interactive Bubbletea-based TUI with a split-pane layout: file tree on the left, detail diff on the right.
71+
72+
### Keybindings
73+
74+
| Key | Action |
75+
|-----|--------|
76+
| ``/`k`, ``/`j` | Navigate tree |
77+
| ``/`enter`/`l` | Expand node |
78+
| ``/`h` | Collapse node |
79+
| `tab` | Switch pane focus |
80+
| `n`/`N` | Next/previous change |
81+
| `f` | Cycle filter (all → added → removed → modified) |
82+
| `1`-`4` | Filter: all, added, removed, modified |
83+
| `/` | Search (fuzzy match in tree, text search in detail) |
84+
| `s` | Swap A ↔ B |
85+
| `c` | Copy detail to clipboard |
86+
| `pgup`/`pgdn` | Scroll detail pane |
87+
| `g`/`G` | Jump to top/bottom |
88+
| `?` | Toggle full help |
89+
| `q`/`ctrl+c` | Quit |
90+
91+
## JSON output
92+
93+
Pass `--json` to get structured JSON output suitable for scripting and CI pipelines. For tree mode, the output is the full comparison result. For single-file modes (binary, plist, text), the output includes both the summary and the detailed diff.
94+
95+
## Platform support
96+
97+
drift works on **macOS**, **Linux**, and **Windows**. Core features (directory/archive comparison, text diffing) work everywhere. Some features require external tools and degrade gracefully when they are unavailable:
98+
99+
| Tool | Used for | Availability |
100+
| --- | --- | --- |
101+
| `nm`, `size` | Mach-O binary analysis | macOS (Xcode CLI Tools), Linux (binutils) |
102+
| `plutil` | Binary plist conversion | macOS only (XML plists work everywhere) |
103+
| `xclip` or `xsel` | Clipboard | Linux only (macOS and Windows work natively) |
104+
105+
## License
106+
107+
[Apache License 2.0](LICENSE)

0 commit comments

Comments
 (0)