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:
parent
33cddb57cd
commit
73e3488edc
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue