SHELL := /usr/bin/env bash # define standard colors ifneq (,$(findstring xterm,${TERM})) BLACK := $(shell tput -Txterm setaf 0) RED := $(shell tput -Txterm setaf 1) GREEN := $(shell tput -Txterm setaf 2) YELLOW := $(shell tput -Txterm setaf 3) LIGHTPURPLE := $(shell tput -Txterm setaf 4) PURPLE := $(shell tput -Txterm setaf 5) BLUE := $(shell tput -Txterm setaf 6) WHITE := $(shell tput -Txterm setaf 7) RESET := $(shell tput -Txterm sgr0) else BLACK := "" RED := "" GREEN := "" YELLOW := "" LIGHTPURPLE := "" PURPLE := "" BLUE := "" WHITE := "" RESET := "" endif ENV_PATH ?= ./apps/nextjs-app DOCKER_COMPOSE ?= docker compose DOCKER_COMPOSE_ENV_FILE := $(wildcard ./dockers/.env) COMPOSE_FILES := $(wildcard ./dockers/*.yml) COMPOSE_FILE_ARGS := --env-file $(DOCKER_COMPOSE_ENV_FILE) $(foreach yml,$(COMPOSE_FILES),-f $(yml)) NETWORK_MODE ?= teablenet CI_JOB_ID ?= 0 CI ?= 0 # Timeout used to await services to become healthy TIMEOUT ?= 300 SCRATCH ?= /tmp UNAME_S := $(shell uname -s) # prisma database url defaults SQLITE_PRISMA_DATABASE_URL ?= file:../../db/main.db # set param statement_cache_size=1 to avoid query error `ERROR: cached plan must not change result type` after alter column type (modify field type) POSTGES_PRISMA_DATABASE_URL ?= postgresql://teable:teable\@127.0.0.1:5432/teable?schema=public\&statement_cache_size=1 # If the first make argument is "start", "stop"... ifeq (docker.start,$(firstword $(MAKECMDGOALS))) SERVICE_TARGET = true else ifeq (docker.stop,$(firstword $(MAKECMDGOALS))) SERVICE_TARGET = true else ifeq (docker.restart,$(firstword $(MAKECMDGOALS))) SERVICE_TARGET = true else ifeq (docker.up,$(firstword $(MAKECMDGOALS))) SERVICE_TARGET = true else ifeq (docker.await,$(firstword $(MAKECMDGOALS))) SERVICE_TARGET = true else ifeq (docker.run,$(firstword $(MAKECMDGOALS))) RUN_TARGET = true else ifeq (docker.integration,$(firstword $(MAKECMDGOALS))) INTEGRATION_TARGET = true endif ifdef SERVICE_TARGET # .. then use the rest as arguments for the make target SERVICE := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)) # ...and turn them into do-nothing targets $(eval $(SERVICE):;@:) else ifdef RUN_TARGET # Isolate second argument as service, the rest is arguments for run command SERVICE := $(wordlist 2, 2, $(MAKECMDGOALS)) SERVICE_ARGS := $(wordlist 3, $(words $(MAKECMDGOALS)),$(MAKECMDGOALS)) else ifdef INTEGRATION_TARGET # Isolate second argument as integration module, the rest as arguments INTEGRATION_MODULE := $(wordlist 2, 2, $(MAKECMDGOALS)) $(eval $(INTEGRATION_MODULE):;@:) INTEGRATION_ARGS := $(wordlist 3, $(words $(MAKECMDGOALS)),$(MAKECMDGOALS)) $(eval $(INTEGRATION_ARGS):;@:) endif # # Never use the network=host mode when running CI jobs, and add extra # distinguishing identifiers to the network name and container names to # prevent collisions with jobs from the same project running at the same # time. # ifneq ($(CI_JOB_ID),) NETWORK_MODE := teablenet-$(CI_JOB_ID) endif ifeq ($(CI),0) export NODE_ENV = development endif ifeq ($(UNAME_S),Linux) DOCKER_GID ?= $(shell getent group docker | cut -d: -f 3) else ifeq ($(UNAME_S),Darwin) DOCKER_GID ?= $(shell id -g) else $(error Sorry, '${UNAME_S}' is not supported yet) endif DOCKER_COMPOSE_ARGS := DOCKER_UID=$(shell id -u) \ DOCKER_GID=$(DOCKER_GID) \ NETWORK_MODE=$(NETWORK_MODE) define print_db_mode_options @echo -e "\nSelect a database to start.\n" @echo -e "1)sqlite Lightweight embedded, ideal for mobile and embedded systems, simple, resource-efficient, easy integration (default database)" @echo -e "2)postges(pg) Powerful and scalable, suitable for complex enterprise needs, highly customizable, rich community support\n" endef define print_db_push_options @echo -e "The 'db pull' command connects to your database and adds Prisma models to your Prisma schema that reflect the current database schema.\n" @echo -e "1) sqlite" @echo -e "2) postges(pg)\n" endef .PHONY: db-mode sqlite.mode postgres.mode gen-prisma-schema gen-sqlite-prisma-schema gen-postgres-prisma-schema .DEFAULT_GOAL := help docker.create.network: ifneq ($(NETWORK_MODE),host) @docker network inspect $(NETWORK_MODE) &> /dev/null || ([ $$? -ne 0 ] && docker network create $(NETWORK_MODE)) $(info ${GREEN}network $(NETWORK_MODE) create success${RESET}) endif docker.rm.network: ifneq ($(NETWORK_MODE),host) @docker network inspect $(NETWORK_MODE) &> /dev/null && ([ $$? -eq 0 ] && docker network rm $(NETWORK_MODE)) || true $(warning ${GREEN}network $(NETWORK_MODE) removed${RESET}) endif docker.run: docker.create.network $(DOCKER_COMPOSE_ARGS) $(DOCKER_COMPOSE) $(COMPOSE_FILE_ARGS) run -T --no-deps --rm $(SERVICE) $(SERVICE_ARGS) docker.up: docker.create.network @$(DOCKER_COMPOSE_ARGS) $(DOCKER_COMPOSE) $(COMPOSE_FILE_ARGS) up --no-recreate -d $(SERVICE) docker.down: docker.rm.network $(DOCKER_COMPOSE_ARGS) $(DOCKER_COMPOSE) $(COMPOSE_FILE_ARGS) down docker.start: $(DOCKER_COMPOSE_ARGS) $(DOCKER_COMPOSE) $(COMPOSE_FILE_ARGS) start $(SERVICE) docker.stop: $(DOCKER_COMPOSE_ARGS) $(DOCKER_COMPOSE) $(COMPOSE_FILE_ARGS) stop $(SERVICE) docker.restart: make docker.stop $(SERVICE) make docker.start $(SERVICE) TIME := 0 docker.await: ## max timeout of 300 @time=$(TIME); \ for i in $(SERVICE); do \ current_service=$$($(DOCKER_COMPOSE_ARGS) $(DOCKER_COMPOSE) $(COMPOSE_FILE_ARGS) ps -q $${i}); \ if [ -z "$${current_service}" ]; then \ continue; \ fi; \ service_has_health=$$(docker inspect -f '{{.State.Health.Status}}' $${current_service}); \ if [ -z "$${service_has_health}" ]; then \ continue; \ fi; \ while [ "$$(docker inspect -f '{{.State.Health.Status}}' $${current_service})" != "healthy" ] ; do \ sleep 1; \ time=$$(expr $$time + 1); \ if [ $${time} -gt $(TIMEOUT) ]; then \ echo "${YELLOW}Timeout reached waiting for $${i} to become healthy${RESET}"; \ docker logs $${i}; \ exit 1; \ fi; \ done; \ echo "${GREEN}Service $${i} is healthy${RESET}"; \ done docker.status: $(DOCKER_COMPOSE_ARGS) $(DOCKER_COMPOSE) $(COMPOSE_FILE_ARGS) ps docker.images: $(DOCKER_COMPOSE_ARGS) $(DOCKER_COMPOSE) $(COMPOSE_FILE_ARGS) images build.app: @zx --version || pnpm add -g zx; \ zx scripts/build-image.mjs --file=dockers/teable/Dockerfile \ --tag=teable:develop build.db-migrate: @zx --version || pnpm add -g zx; \ zx scripts/build-image.mjs --file=dockers/teable/Dockerfile.db-migrate \ --tag=teable-db-migrate:develop sqlite.integration.test: @export PRISMA_DATABASE_URL='file:../../db/main.db'; \ export CALC_CHUNK_SIZE=400; \ make sqlite.mode; \ pnpm -F "./packages/**" run build; \ pnpm g:test-e2e-cover postgres.integration.test: docker.create.network @TEST_PG_CONTAINER_NAME=teable-postgres-$(CI_JOB_ID); \ docker rm -fv $$TEST_PG_CONTAINER_NAME | true; \ $(DOCKER_COMPOSE_ARGS) $(DOCKER_COMPOSE) $(COMPOSE_FILE_ARGS) run -p 25432:5432 -d -T --no-deps --rm --name $$TEST_PG_CONTAINER_NAME teable-postgres; \ chmod +x scripts/wait-for; \ scripts/wait-for 127.0.0.1:25432 --timeout=15 -- echo 'pg database started successfully' && \ export PRISMA_DATABASE_URL=postgresql://teable:teable@127.0.0.1:25432/e2e_test_teable?schema=public\&statement_cache_size=1\&connection_limit=20 && \ make postgres.mode && \ pnpm -F "./packages/**" run build && \ pnpm g:test-e2e-cover && \ docker rm -fv $$TEST_PG_CONTAINER_NAME gen-sqlite-prisma-schema: @cd ./packages/db-main-prisma; \ echo '{ "PRISMA_PROVIDER": "sqlite" }' | pnpm mustache - ./prisma/template.prisma > ./prisma/sqlite/schema.prisma @echo 'generate【 prisma/sqlite/schema.prisma 】success.' gen-postgres-prisma-schema: @cd ./packages/db-main-prisma; \ echo '{ "PRISMA_PROVIDER": "postgres" }' | pnpm mustache - ./prisma/template.prisma > ./prisma/postgres/schema.prisma @echo 'generate【 prisma/postgres/schema.prisma 】success.' gen-prisma-schema: gen-sqlite-prisma-schema gen-postgres-prisma-schema ## Generate 'schema.prisma' files for all versions of the system sqlite-db.push: ## db.push by sqlite @cd ./packages/db-main-prisma; \ pnpm prisma-db-push --schema ./prisma/sqlite/schema.prisma postgres-db.push: ## db.push by postgres @cd ./packages/db-main-prisma; \ pnpm prisma-db-push --schema ./prisma/postgres/schema.prisma db.push: ## connects to your database and adds Prisma models to your Prisma schema that reflect the current database schema. $(print_db_push_options) @read -p "Enter a command: " command; \ if [ "$$command" = "1" ] || [ "$$command" = "sqlite" ]; then \ make gen-sqlite-prisma-schema; \ make sqlite-db.push; \ elif [ "$$command" = "2" ] || [ "$$command" = "postges" ] || [ "$$command" = "pg" ]; then \ make gen-postgres-prisma-schema; \ make postgres-db.push; \ else echo "Unknown command."; fi sqlite-db-migration: @_MIGRATION_NAME=$(if $(_MIGRATION_NAME),$(_MIGRATION_NAME),`read -p "Enter name of the migration (sqlite): " migration_name; echo $$migration_name`); \ make gen-sqlite-prisma-schema; \ PRISMA_DATABASE_URL=file:../../db/.shadow/main.db \ pnpm -F @teable/db-main-prisma prisma-migrate dev --schema ./prisma/sqlite/schema.prisma --name $$_MIGRATION_NAME postgres-db-migration: @_MIGRATION_NAME=$(if $(_MIGRATION_NAME),$(_MIGRATION_NAME),`read -p "Enter name of the migration (postgres): " migration_name; echo $$migration_name`); \ make gen-postgres-prisma-schema; \ PRISMA_DATABASE_URL=postgresql://teable:teable@127.0.0.1:5432/teable?schema=shadow \ pnpm -F @teable/db-main-prisma prisma-migrate dev --schema ./prisma/postgres/schema.prisma --name $$_MIGRATION_NAME db-migration: ## Reruns the existing migration history in the shadow database in order to detect schema drift (edited or deleted migration file, or a manual changes to the database schema) @read -p "Enter name of the migration: " migration_name; \ make sqlite-db-migration _MIGRATION_NAME=$$migration_name; \ make postgres-db-migration _MIGRATION_NAME=$$migration_name sqlite.mode: ## sqlite.mode @cd ./packages/db-main-prisma; \ pnpm prisma-generate --schema ./prisma/sqlite/schema.prisma; \ pnpm prisma-migrate deploy --schema ./prisma/sqlite/schema.prisma postgres.mode: ## postgres.mode @cd ./packages/db-main-prisma; \ pnpm prisma-generate --schema ./prisma/postgres/schema.prisma; \ pnpm prisma-migrate deploy --schema ./prisma/postgres/schema.prisma # Override environment variable files based on variables RUN_DB_MODE ?= sqlite FILE_ENV_PATHS = $(ENV_PATH)/.env.development* $(ENV_PATH)/.env.test* switch.prisma.env: ifeq ($(CI)-$(RUN_DB_MODE),0-sqlite) @for file in $(FILE_ENV_PATHS); do \ echo $$file; \ perl -i -pe 's~^PRISMA_DATABASE_URL=.*~PRISMA_DATABASE_URL=$(SQLITE_PRISMA_DATABASE_URL)~' $$file; \ if ! grep -q '^CALC_CHUNK_SIZE=' $$file; then \ echo "CALC_CHUNK_SIZE=400" >> $$file; \ else \ perl -i -pe 's~^CALC_CHUNK_SIZE=.*~CALC_CHUNK_SIZE=400~' $$file; \ fi; \ done else ifeq ($(CI)-$(RUN_DB_MODE),0-postges) @for file in $(FILE_ENV_PATHS); do \ echo $$file; \ perl -i -pe 's~^PRISMA_DATABASE_URL=.*~PRISMA_DATABASE_URL=$(POSTGES_PRISMA_DATABASE_URL)~' $$file; \ done endif switch-db-mode: ## Switch Database environment $(print_db_mode_options) @read -p "Enter a command: " command; \ if [ "$$command" = "1" ] || [ "$$command" = "sqlite" ]; then \ make switch.prisma.env RUN_DB_MODE=sqlite; \ make sqlite.mode; \ elif [ "$$command" = "2" ] || [ "$$command" = "postges" ] || [ "$$command" = "pg" ]; then \ make switch.prisma.env RUN_DB_MODE=postges; \ make docker.up teable-postgres; \ make docker.await teable-postgres; \ make postgres.mode; \ else \ echo "Unknown command."; fi help: ## show this help @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'