-
Notifications
You must be signed in to change notification settings - Fork 86
Expand file tree
/
Copy pathMakefile
More file actions
271 lines (231 loc) · 9.5 KB
/
Copy pathMakefile
File metadata and controls
271 lines (231 loc) · 9.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
.DEFAULT_GOAL := help
# Local sqlite DB used for `make run` / `make db-*` targets
LOCAL_DB_URI ?= sqlite:///instance/access.db
# Ports — overridable via the PORT env var so Claude Code Desktop Preview's
# autoPort can pick a free port per worktree. See .claude/launch.json.
BACKEND_PORT ?= $(if $(PORT),$(PORT),6060)
FRONTEND_PORT ?= $(if $(PORT),$(PORT),3000)
# Space-separated example plugin directory names to install (editable) into the
# local venv before running, e.g.:
# make run-backend PLUGINS="conditional_access notifications_slack"
# Mirrors the Dockerfile's per-plugin INSTALL_*_PLUGIN build args for local dev.
# Default empty ⇒ no extra plugins, so `make run-backend` is unchanged. The
# audit logger is already loaded via the dev dependency group and needs no flag.
PLUGINS ?=
.PHONY: help
help:
@echo "Access dev targets:"
@echo " make run Run backend (port $(BACKEND_PORT)) and frontend (port $(FRONTEND_PORT)) together"
@echo " make run-backend Run uvicorn on port $(BACKEND_PORT) with --reload"
@echo " make run-frontend Run Vite dev server on port $(FRONTEND_PORT)"
@echo " (add PLUGINS=\"dir1 dir2\" to install example plugins)"
@echo ""
@echo "Database:"
@echo " make db-migrate alembic upgrade head"
@echo " make db-downgrade alembic downgrade -1"
@echo " make db-current alembic current"
@echo " make db-history alembic history"
@echo " make db-revision msg=… alembic revision --autogenerate"
@echo " make db-init email=… alembic upgrade + access init <email>"
@echo ""
@echo "Management commands:"
@echo " make sync access sync"
@echo " make notify access notify"
@echo " make sync-app-groups access sync-app-groups (loads .env)"
@echo " make shell access shell (REPL, loads .env)"
@echo ""
@echo "Docker:"
@echo " make build docker build"
@echo " make build-all-plugins docker build with every INSTALL_*_PLUGIN arg on (mirrors CI)"
@echo " make run-docker docker compose up --build"
@echo " make compose-down docker compose down"
@echo ""
@echo "Tests / lint:"
@echo " make pytest pytest, incl. example plugins (sqlite in-memory)"
@echo " make pytest-postgres pytest tests/ against a disposable postgres:16 container"
@echo " make ruff ruff check + ruff format --check"
@echo " make ty ty check"
@echo " make test-backend ruff + ty + pytest"
@echo " make test-frontend vitest (frontend unit tests, single run)"
@echo " make test test-backend + test-frontend (everything)"
@echo ""
@echo "Other:"
@echo " make dev Sync deps into .venv via uv (idempotent)"
@echo " make clean Remove caches and build artifacts"
.env:
@if [ ! -f .env ]; then \
echo "Create a .env file based on the README before running. Example:"; \
echo " CURRENT_OKTA_USER_EMAIL=you@example.com"; \
echo " OKTA_DOMAIN=mydomain.oktapreview.com"; \
echo " OKTA_API_TOKEN=…"; \
echo " DATABASE_URI=$(LOCAL_DB_URI)"; \
exit 1; \
fi
.PHONY: clean
clean:
rm -rf \
.pytest_cache \
.ruff_cache \
**/__pycache__ \
dist/ \
build/ \
*.egg-info/
.PHONY: dev
dev:
uv sync
# Install any plugins named in PLUGINS into the venv after `uv sync` (which
# would otherwise prune them). Editable, so local edits to the plugin are picked
# up on reload. A plugin's requirements.txt is installed first when present.
.PHONY: install-plugins
install-plugins: dev
@for p in $(PLUGINS); do \
if [ ! -d "examples/plugins/$$p" ]; then \
echo "No such example plugin: examples/plugins/$$p"; exit 1; \
fi; \
echo "Installing example plugin: $$p"; \
if [ -f "examples/plugins/$$p/requirements.txt" ]; then \
uv pip install -r "examples/plugins/$$p/requirements.txt"; \
fi; \
uv pip install -e "examples/plugins/$$p"; \
done
# ----------------------------------------------------------------------
# Run targets
# ----------------------------------------------------------------------
.PHONY: run
run: .env install-plugins db-migrate
@mkdir -p .claude
@printf '%s\n' "$(BACKEND_PORT)" > .claude/.api-port
DATABASE_URI=$(LOCAL_DB_URI) \
uv run uvicorn --env-file .env --reload --host 0.0.0.0 --port $(BACKEND_PORT) api.asgi:app & \
npm install && npx vite --host 0.0.0.0 --port $(FRONTEND_PORT)
.PHONY: run-frontend
run-frontend:
npm install && npx vite --host 0.0.0.0 --port $(FRONTEND_PORT)
.PHONY: run-backend
run-backend: .env install-plugins db-migrate
@mkdir -p .claude
@printf '%s\n' "$(BACKEND_PORT)" > .claude/.api-port
DATABASE_URI=$(LOCAL_DB_URI) \
uv run uvicorn --env-file .env --reload --host 0.0.0.0 --port $(BACKEND_PORT) api.asgi:app
# ----------------------------------------------------------------------
# Database / migrations
# ----------------------------------------------------------------------
.PHONY: db-migrate
db-migrate: dev
DATABASE_URI=$(LOCAL_DB_URI) uv run alembic upgrade head
.PHONY: db-downgrade
db-downgrade: dev
DATABASE_URI=$(LOCAL_DB_URI) uv run alembic downgrade -1
.PHONY: db-current
db-current: dev
DATABASE_URI=$(LOCAL_DB_URI) uv run alembic current
.PHONY: db-history
db-history: dev
uv run alembic history
.PHONY: db-revision
db-revision: dev
@if [ -z "$(msg)" ]; then echo "usage: make db-revision msg=\"<message>\""; exit 1; fi
DATABASE_URI=$(LOCAL_DB_URI) uv run alembic revision --autogenerate -m "$(msg)"
.PHONY: db-init
db-init: db-migrate
@if [ -z "$(email)" ]; then echo "usage: make db-init email=<admin-okta-email>"; exit 1; fi
DATABASE_URI=$(LOCAL_DB_URI) uv run access init "$(email)"
# ----------------------------------------------------------------------
# Sync / management commands
# ----------------------------------------------------------------------
.PHONY: sync
sync: install-plugins
DATABASE_URI=$(LOCAL_DB_URI) uv run access sync
.PHONY: notify
notify: install-plugins
DATABASE_URI=$(LOCAL_DB_URI) uv run access notify
.PHONY: shell
shell: .env install-plugins
set -a && . ./.env && set +a && \
DATABASE_URI=$(LOCAL_DB_URI) uv run access shell
# Unlike `sync`/`notify`, this loads the full .env so the app-group lifecycle
# plugins get the credentials they need (e.g. Google/Okta). `access` has no
# --env-file flag, so we source .env into the environment ourselves: `set -a`
# auto-exports every var defined while sourcing. We then force
# DATABASE_URI=$(LOCAL_DB_URI) -- exactly like `sync`/`notify` -- so this points
# at the same migrated instance/access.db the dev server uses, rather than
# whatever DATABASE_URI .env happens to set.
.PHONY: sync-app-groups
sync-app-groups: .env install-plugins
set -a && . ./.env && set +a && \
DATABASE_URI=$(LOCAL_DB_URI) uv run access sync-app-groups
# ----------------------------------------------------------------------
# Docker
# ----------------------------------------------------------------------
.PHONY: build
build:
docker build -t access .
# Same build the "Build Docker Image" CI check runs: every optional-plugin
# install branch in the Dockerfile enabled, no secrets. Use this to reproduce a
# CI image-build failure locally.
.PHONY: build-all-plugins
build-all-plugins:
docker build -t access:all-plugins \
--build-arg INSTALL_AUDIT_LOGGER_PLUGIN=true \
--build-arg INSTALL_CONDITIONAL_ACCESS_PLUGIN=true \
--build-arg INSTALL_DATADOG_METRICS_PLUGIN=true \
--build-arg INSTALL_GOOGLE_GROUP_LIFECYCLE_PLUGIN=true \
--build-arg INSTALL_HEALTH_CHECK_PLUGIN=true \
--build-arg INSTALL_NOTIFICATIONS_PLUGIN=true \
--build-arg INSTALL_SLACK_NOTIFICATIONS_PLUGIN=true \
.
.PHONY: run-docker
run-docker:
docker compose up --build --remove-orphans
.PHONY: compose-down
compose-down:
docker compose down || :
# ----------------------------------------------------------------------
# Tests / linting
# ----------------------------------------------------------------------
.PHONY: pytest
pytest: dev
uv run pytest
# ----------------------------------------------------------------------
# Postgres integration test target
# ----------------------------------------------------------------------
# Boots a disposable postgres:16 container on port 5433, runs pytest with
# TEST_DATABASE_URI pointed at it, then stops the container. The default
# `make pytest` keeps using the in-memory sqlite the test fixtures fall
# back to.
PG_TEST_CONTAINER ?= access-test-pg
PG_TEST_PORT ?= 5433
PG_TEST_URI := postgresql+asyncpg://postgres:postgres@localhost:$(PG_TEST_PORT)/access_test
.PHONY: pytest-postgres
pytest-postgres: dev pytest-postgres-up
TEST_DATABASE_URI='$(PG_TEST_URI)' uv run pytest tests/; \
status=$$?; \
$(MAKE) pytest-postgres-down; \
exit $$status
.PHONY: pytest-postgres-up
pytest-postgres-up:
@if [ -z "$$(docker ps -q -f name=^$(PG_TEST_CONTAINER)$$)" ]; then \
docker run -d --rm --name $(PG_TEST_CONTAINER) \
-e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=access_test \
-p $(PG_TEST_PORT):5432 postgres:16 >/dev/null; \
echo "Waiting for postgres on :$(PG_TEST_PORT)..."; \
until docker exec $(PG_TEST_CONTAINER) pg_isready -U postgres >/dev/null 2>&1; do sleep 0.5; done; \
echo "postgres ready."; \
fi
.PHONY: pytest-postgres-down
pytest-postgres-down:
@docker rm -f $(PG_TEST_CONTAINER) >/dev/null 2>&1 || :
.PHONY: ty
ty: dev
uv run ty check .
.PHONY: ruff
ruff: dev
uv run ruff check .
uv run ruff format --check .
.PHONY: test-backend
test-backend: ruff ty pytest
.PHONY: test-frontend
test-frontend:
npm install && npx vitest run
.PHONY: test
test: test-backend test-frontend