chore: replace Makefile with justfile to match repo convention

Justfile mirrors the haskell-reverse-proxy pattern (grouped recipes:
build, run, demo, test, quality, db, util). 33 recipes covering full
lifecycle - including parameterized recipes (cre rotate ID, cre policy
show NAME) that read more naturally as 'just rotate ...' than as
make targets.

User-edited docker-compose.yml port mappings preserved
(postgres:18 on 6022, localstack on 45666, vault on 18201,
fake-github on 7115).
This commit is contained in:
CarterPerez-dev 2026-04-29 01:28:37 -04:00
parent 33cddb57cd
commit 73e3488edc
7 changed files with 205 additions and 69 deletions

View File

@ -1,57 +0,0 @@
# ©AngelaMos | 2026
# Makefile
.PHONY: build build-dev test test-unit test-integration lint format format-check demo demo-full demo-full-down check ci clean
CRYSTAL ?= crystal
SHARDS ?= shards
build:
$(SHARDS) build cre --release --no-debug
build-dev:
$(SHARDS) build cre
test:
$(CRYSTAL) spec --order=random
test-unit:
$(CRYSTAL) spec spec/unit --order=random
test-integration:
$(CRYSTAL) spec spec/integration --order=random
lint:
@if [ -x ./bin/ameba ]; then \
./bin/ameba; \
else \
echo "ameba not installed (requires libpcre3-dev on Debian 13+); skipping lint"; \
fi
format:
$(CRYSTAL) tool format
format-check:
$(CRYSTAL) tool format --check
demo:
$(SHARDS) build cre && ./bin/cre demo
demo-full:
docker compose -f docker/docker-compose.yml up -d
@echo "Waiting for services..."
@sleep 8
$(SHARDS) build cre && ./bin/cre run --config=config/demo-full.cr
demo-full-down:
docker compose -f docker/docker-compose.yml down -v
check:
$(MAKE) format-check
$(MAKE) lint
$(MAKE) test
ci: check
clean:
rm -rf bin lib .shards .crystal coverage tmp

View File

@ -45,7 +45,7 @@ You'll see live narration of an in-memory SQLite + tempfile rotation, with audit
### Tier 2 - Full mocked stack (under 2 minutes)
```bash
make demo-full
just demo-full
```
Brings up Docker Compose with PostgreSQL 16, LocalStack (AWS Secrets Manager), HashiCorp Vault dev mode, and a fake-GitHub Flask service. CRE talks to all four with real network calls.
@ -109,7 +109,7 @@ All components are fibers in one OS process. The bus is in-process - Crystal cha
```
credential-rotation-enforcer/
├── shard.yml Crystal manifest (1.20+)
├── Makefile build / test / demo / lint targets
├── justfile build / test / demo / lint recipes
├── policies/ USER policy files (compiled in)
├── src/cre/
│ ├── cli/ subcommand dispatch + 9 commands
@ -147,7 +147,7 @@ crystal spec # all 200+ tests
crystal spec spec/unit # unit only (no DB required)
DATABASE_URL=postgres://cre_test:cre_test@localhost:5432/cre_test \
crystal spec spec/integration # integration with real PG
make check # format + lint + unit
just ci # format + lint + unit
```
## License

View File

@ -3,12 +3,12 @@
services:
postgres:
image: postgres:16
image: postgres:18
environment:
POSTGRES_USER: cre
POSTGRES_PASSWORD: cre
POSTGRES_DB: cre
ports: ["5432:5432"]
ports: ["6022:5432"]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U cre"]
interval: 3s
@ -20,7 +20,7 @@ services:
environment:
SERVICES: secretsmanager
DEBUG: 0
ports: ["4566:4566"]
ports: ["45666:4566"]
vault:
image: hashicorp/vault:latest
@ -28,7 +28,7 @@ services:
environment:
VAULT_DEV_ROOT_TOKEN_ID: dev-root-token
VAULT_DEV_LISTEN_ADDRESS: 0.0.0.0:8200
ports: ["8200:8200"]
ports: ["18201:8200"]
command: vault server -dev
fake-github:
@ -36,4 +36,4 @@ services:
working_dir: /app
volumes: ["./fake-github:/app:ro"]
command: ["sh", "-c", "pip install --quiet flask && python /app/app.py"]
ports: ["7000:7000"]
ports: ["7115:7000"]

View File

@ -0,0 +1,194 @@
# =============================================================================
# AngelaMos | 2026
# Justfile - Credential Rotation Enforcer (cre)
# =============================================================================
set export
set shell := ["bash", "-uc"]
set windows-shell := ["powershell.exe", "-NoLogo", "-Command"]
project := file_name(justfile_directory())
version := `git describe --tags --always 2>/dev/null || echo "dev"`
binary := "bin/cre"
db_default := "sqlite:cre.db"
test_db_url := "postgres://cre_test:cre_test@localhost:5432/cre_test"
# =============================================================================
# Default
# =============================================================================
default:
@just --list --unsorted
# =============================================================================
# Build
# =============================================================================
[group('build')]
build:
shards build cre --release --no-debug
[group('build')]
build-dev:
shards build cre
[group('build')]
deps:
shards install
[group('build')]
clean:
rm -rf bin lib .shards .crystal coverage tmp
[group('build')]
rebuild: clean deps build
# =============================================================================
# Run (cre lifecycle)
# =============================================================================
[group('run')]
run db=db_default:
shards build cre && {{binary}} run --db={{db}}
[group('run')]
watch db=db_default:
shards build cre && {{binary}} watch --db={{db}}
[group('run')]
check db=":memory:":
shards build cre && {{binary}} check --db={{db}}
[group('run')]
rotate ID db=db_default:
shards build cre && {{binary}} rotate {{ID}} --db={{db}}
[group('run')]
audit-verify db=db_default:
shards build cre && {{binary}} audit verify --db={{db}}
[group('run')]
policy-list:
shards build cre && {{binary}} policy list
[group('run')]
policy-show name:
shards build cre && {{binary}} policy show {{name}}
[group('run')]
export framework="soc2" out="evidence.zip" db=db_default:
shards build cre && {{binary}} export --framework={{framework}} --out={{out}} --db={{db}}
# =============================================================================
# Demos (Tier 1 / Tier 2 / Tier 3)
# =============================================================================
[group('demo')]
demo:
shards build cre && {{binary}} demo
[group('demo')]
demo-full:
docker compose -f docker/docker-compose.yml up -d
@echo "Waiting for services..."
@sleep 8
shards build cre && {{binary}} run --config=config/demo-full.cr
[group('demo')]
demo-full-down:
docker compose -f docker/docker-compose.yml down -v
[group('demo')]
demo-full-logs:
docker compose -f docker/docker-compose.yml logs -f --tail=50
# =============================================================================
# Test
# =============================================================================
[group('test')]
test:
crystal spec --order=random
[group('test')]
test-unit:
crystal spec spec/unit --order=random
[group('test')]
test-integration:
DATABASE_URL={{test_db_url}} crystal spec spec/integration --order=random
[group('test')]
test-watch:
@echo "Watching spec/ for changes..."
@while true; do \
inotifywait -qre close_write src/ spec/ 2>/dev/null && crystal spec --order=random; \
done
[group('test')]
coverage:
@echo "Coverage requires kcov. Skipping if not installed."
kcov --include-path=src coverage/ ./bin/cre || true
# =============================================================================
# Quality (format / lint)
# =============================================================================
[group('quality')]
format:
crystal tool format
[group('quality')]
format-check:
crystal tool format --check
[group('quality')]
lint:
@if [ -x ./bin/ameba ]; then \
./bin/ameba; \
else \
echo "ameba not installed (requires libpcre3-dev on Debian 13+); skipping lint"; \
fi
[group('quality')]
check-all: format-check lint test
[group('quality')]
ci: check-all
# =============================================================================
# Database (Tier 2 helpers)
# =============================================================================
[group('db')]
db-up:
docker run -d --rm --name cre-pg -e POSTGRES_USER=cre_test -e POSTGRES_PASSWORD=cre_test -e POSTGRES_DB=cre_test -p 5432:5432 postgres:16
[group('db')]
db-down:
docker stop cre-pg 2>/dev/null || true
[group('db')]
db-shell:
docker exec -it cre-pg psql -U cre_test cre_test
# =============================================================================
# Utility
# =============================================================================
[group('util')]
version:
@echo "{{project}} {{version}}"
[group('util')]
loc:
@echo "Source LOC:"
@find src -name "*.cr" -exec wc -l {} \; | awk '{total+=$1} END{print " " total}'
@echo "Spec LOC:"
@find spec -name "*.cr" -exec wc -l {} \; | awk '{total+=$1} END{print " " total}'
@echo "Spec files: $(find spec -name '*_spec.cr' | wc -l)"
[group('util')]
binary-info:
@ls -la {{binary}} 2>/dev/null || echo "binary not built; run 'just build'"
@file {{binary}} 2>/dev/null || true

View File

@ -49,7 +49,7 @@ Runtime: under 1 second.
### Tier 2 - Docker Compose
```
$ make demo-full
$ just demo-full
```
Brings up: PostgreSQL 16, LocalStack (AWS Secrets Manager), HashiCorp Vault dev mode, a fake-GitHub Flask service. CRE connects to all four and rotates one credential through each rotator. Demonstrates real network calls, real auth (SigV4 to LocalStack, token to Vault, bearer to fake-GitHub), real persistence to Postgres, real append-only audit triggers.

View File

@ -87,8 +87,7 @@ describe CRE::Notifiers::TelegramSubscriber do
bus = CRE::Engine::EventBus.new
sub = CRE::Notifiers::TelegramSubscriber.new(
bus, CRE::Notifiers::Telegram.new("FAKE"), [1_i64], notify_on_success: false,
)
bus, CRE::Notifiers::Telegram.new("FAKE"), [1_i64], notify_on_success: false)
sub.start
bus.run
bus.publish CRE::Events::RotationCompleted.new(UUID.random, UUID.random)

View File

@ -83,7 +83,7 @@ describe CRE::Rotators::VaultDynamicRotator do
end
it "skips lease revocation when no current_lease_id" do
cred = vault_credential # no current_lease_id
cred = vault_credential # no current_lease_id
WebMock.stub(:get, "http://vault.test/v1/database/creds/myrole")
.to_return(body: %({"lease_id":"new","lease_duration":3600,"data":{"username":"u","password":"p"}}))
rotator = CRE::Rotators::VaultDynamicRotator.new(vault_client)