mirror of
				https://github.com/usememos/memos.git
				synced 2025-10-27 06:46:00 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			340 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			YAML
		
	
	
	
	
	
			
		
		
	
	
			340 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			YAML
		
	
	
	
	
	
| # > Memos development environment <
 | |
| #
 | |
| # Available profiles: sqlite, mysql, postgres.
 | |
| # Use `docker compose --profile PROFILE_NAME up` to launch only services within the profile.
 | |
| #
 | |
| # Services in the `tools` profile are used for running one-off tasks like linting, generating code, etc.
 | |
| #
 | |
| # Services started in all database profiles:
 | |
| #   Front-end:  http://localhost:3001
 | |
| #   API:        http://localhost:8081
 | |
| #   Adminer:    http://localhost:8091
 | |
| #
 | |
| # On Windows, run this before using docker-compose on a new terminal:
 | |
| # $Env:HOME=$Env:USERPROFILE
 | |
| #
 | |
| # > Start Memos in development mode:
 | |
| # docker compose -f ./scripts/docker-compose.dev.yaml --profile [sqlite|mysql|postgres] up --detach
 | |
| #
 | |
| # > Stop all services:
 | |
| # docker compose -f ./scripts/docker-compose.dev.yaml --profile sqlite --profile postgres --profile mysql down
 | |
| #
 | |
| # > Remove related volumes: (all other files are mapped to ./air/docker/ directory)
 | |
| # docker volume rm memos-dev_pnpm-store memos-dev_node-modules
 | |
| #
 | |
| # One-off tasks:
 | |
| # > pnpm:
 | |
| # docker compose -f ./scripts/docker-compose.dev.yaml run --rm pnpm [add|remove|update] [PACKAGE_NAME] [--save-dev]
 | |
| #
 | |
| # > buf: (run this after modifying .proto files)
 | |
| # docker compose -f ./scripts/docker-compose.dev.yaml run --rm buf generate
 | |
| #
 | |
| # > go:
 | |
| # docker compose -f ./scripts/docker-compose.dev.yaml run --rm go mod tidy -go=1.22
 | |
| #
 | |
| # > golangci-lint: (run this before submitting Pull Requests affecting Go code)
 | |
| # docker compose -f ./scripts/docker-compose.dev.yaml run --rm golangci-lint run
 | |
| #
 | |
| # > goimports: (run this if golangci-lint shows "File is not `goimports`-ed"
 | |
| # docker compose -f ./scripts/docker-compose.dev.yaml run --rm goimports -local https://github.com/usememos/memos -w [FILE|.]
 | |
| #
 | |
| version: "3.0"
 | |
| name: memos-dev
 | |
| volumes:
 | |
|   # pnpm uses hard links and node_modules uses symlinks.
 | |
|   # Using volumes make things work properly on any host OS.
 | |
|   node-modules:
 | |
|   pnpm-store:
 | |
| services:
 | |
|   web:
 | |
|     profiles: ["sqlite", "mysql", "postgres"]
 | |
|     image: node:20-alpine
 | |
|     ports: [3001:3001]
 | |
|     environment:
 | |
|       DEV_PROXY_SERVER: http://api:8081/
 | |
|       NPM_CONFIG_UPDATE_NOTIFIER: false
 | |
|     working_dir: &web-working-dir /work/web
 | |
|     entrypoint: ["/bin/sh", "-c"]
 | |
|     command: ["corepack enable && pnpm i --frozen-lockfile && pnpm dev"]
 | |
|     tmpfs: &web-tmpfs /work/node_modules/:exec # avoid ERR_PNPM_LINKING_FAILED
 | |
|     volumes: &web-volumes
 | |
|       - node-modules:/work/web/node_modules
 | |
|       - pnpm-store:/work/web/.pnpm-store
 | |
|       - ../proto:/work/proto
 | |
|       - ../web:/work/web
 | |
|       - ../web/node_modules:/work/web/node_modules
 | |
|     healthcheck:
 | |
|       test: ["CMD", "wget", "-qO", "-", "http://localhost:3001"]
 | |
|       interval: 10s
 | |
|       timeout: 5s
 | |
| 
 | |
|   api:
 | |
|     profiles: ["sqlite"]
 | |
|     image: &api-image golang:1.22-alpine
 | |
|     ports: &api-ports [8081:8081]
 | |
|     environment:
 | |
|       MEMOS_DRIVER: sqlite
 | |
|       MEMOS_DATA: /var/opt/memos
 | |
|     working_dir: &api-working-dir /work
 | |
|     volumes: &api-volumes
 | |
|       - $HOME/go/pkg/:/go/pkg/ # Share go mod cache with host
 | |
|       - ../.air/docker/go-build:/root/.cache/go-build
 | |
|       - ../.air/docker/go/bin:/go/bin
 | |
|       - ../.air/docker/memosdata:/var/opt/memos
 | |
|       - ..:/work/
 | |
|     configs: &api-configs
 | |
|       - source: air-entrypoint.sh
 | |
|         target: /usr/local/bin/entrypoint.sh
 | |
|     entrypoint: &api-entrypoint ["/bin/sh", "/usr/local/bin/entrypoint.sh"]
 | |
|     command: &api-command ["-c", "./scripts/.air.toml"]
 | |
|     healthcheck: &api-healthcheck
 | |
|       test: ["CMD", "wget", "-qO", "-", "http://localhost:8081/api/v1/ping"]
 | |
|       interval: 10s
 | |
|       timeout: 5s
 | |
| 
 | |
|   api-mysql:
 | |
|     profiles: ["mysql"]
 | |
|     depends_on: { mysql: { condition: service_healthy } }
 | |
|     hostname: api
 | |
|     environment:
 | |
|       { MEMOS_DRIVER: mysql, MEMOS_DSN: memos:memos@tcp(mysql)/memos }
 | |
|     image: *api-image
 | |
|     ports: *api-ports
 | |
|     working_dir: *api-working-dir
 | |
|     volumes: *api-volumes
 | |
|     configs: *api-configs
 | |
|     entrypoint: *api-entrypoint
 | |
|     command: *api-command
 | |
|     healthcheck: *api-healthcheck
 | |
| 
 | |
|   api-postgres:
 | |
|     profiles: ["postgres"]
 | |
|     depends_on: { postgres: { condition: service_healthy } }
 | |
|     hostname: api
 | |
|     environment:
 | |
|       MEMOS_DSN: "postgresql://memos:memos@postgres:5432/memos?sslmode=disable"
 | |
|       MEMOS_DRIVER: postgres
 | |
|     image: *api-image
 | |
|     ports: *api-ports
 | |
|     working_dir: *api-working-dir
 | |
|     volumes: *api-volumes
 | |
|     configs: *api-configs
 | |
|     entrypoint: *api-entrypoint
 | |
|     command: *api-command
 | |
|     healthcheck: *api-healthcheck
 | |
| 
 | |
|   mysql:
 | |
|     profiles: ["mysql"]
 | |
|     image: mysql
 | |
|     environment:
 | |
|       MYSQL_USER: memos
 | |
|       MYSQL_PASSWORD: memos
 | |
|       MYSQL_DATABASE: memos
 | |
|       MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
 | |
|     volumes: [../.air/docker/mysql:/var/lib/mysql]
 | |
|     healthcheck:
 | |
|       test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
 | |
|       interval: 10s
 | |
|       timeout: 5s
 | |
| 
 | |
|   postgres:
 | |
|     profiles: ["postgres"]
 | |
|     image: postgres:alpine
 | |
|     hostname: postgres
 | |
|     volumes: [../.air/docker/postgres:/var/lib/postgresql/data]
 | |
|     environment:
 | |
|       { POSTGRES_DB: memos, POSTGRES_USER: memos, POSTGRES_PASSWORD: memos }
 | |
|     healthcheck:
 | |
|       test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
 | |
|       interval: 10s
 | |
|       timeout: 5s
 | |
| 
 | |
|   pnpm:
 | |
|     profiles: ["tools"]
 | |
|     image: node:20-alpine
 | |
|     environment: { NPM_CONFIG_UPDATE_NOTIFIER: false }
 | |
|     working_dir: *web-working-dir
 | |
|     volumes: *web-volumes
 | |
|     tmpfs: *web-tmpfs
 | |
|     configs:
 | |
|       - source: pnpm-entrypoint.sh
 | |
|         target: /usr/local/bin/entrypoint.sh
 | |
|     entrypoint: ["sh", "/usr/local/bin/entrypoint.sh"]
 | |
| 
 | |
|   buf:
 | |
|     profiles: ["tools"]
 | |
|     image: bufbuild/buf
 | |
|     working_dir: /work/proto
 | |
|     command: generate
 | |
|     volumes:
 | |
|       - ../proto:/work/proto
 | |
|       - ../web/src/types/:/work/web/src/types/
 | |
| 
 | |
|   go:
 | |
|     profiles: ["tools"]
 | |
|     image: *api-image
 | |
|     working_dir: *api-working-dir
 | |
|     volumes: *api-volumes
 | |
|     entrypoint: ["go"]
 | |
| 
 | |
|   goimports:
 | |
|     profiles: ["tools"]
 | |
|     image: *api-image
 | |
|     working_dir: *api-working-dir
 | |
|     volumes: *api-volumes
 | |
|     configs:
 | |
|       - source: goimports-entrypoint.sh
 | |
|         target: /usr/local/bin/entrypoint.sh
 | |
|     entrypoint: ["/bin/sh", "/usr/local/bin/entrypoint.sh"]
 | |
| 
 | |
|   golangci-lint:
 | |
|     profiles: ["tools"]
 | |
|     image: *api-image
 | |
|     working_dir: *api-working-dir
 | |
|     volumes: *api-volumes
 | |
|     configs:
 | |
|       - source: golangci-lint-entrypoint.sh
 | |
|         target: /usr/local/bin/entrypoint.sh
 | |
|     entrypoint: ["/bin/sh", "/usr/local/bin/entrypoint.sh"]
 | |
| 
 | |
|   adminer-mysql:
 | |
|     profiles: ["mysql"]
 | |
|     depends_on: { mysql: { condition: service_healthy } }
 | |
|     image: adminer
 | |
|     environment: &adminer-environment
 | |
|       ADMINER_DEFAULT_DRIVER: server # "server" is mysql
 | |
|       ADMINER_DEFAULT_SERVER: mysql
 | |
|       ADMINER_DEFAULT_USERNAME: memos
 | |
|       ADMINER_DEFAULT_PASSWORD: memos
 | |
|       ADMINER_DEFAULT_DB: memos
 | |
|       ADMINER_DESIGN: dracula # light: pepa-linha | https://www.adminer.org/#extras
 | |
|       ADMINER_PLUGINS: tables-filter table-structure edit-textarea dump-json # https://www.adminer.org/en/plugins/
 | |
|     ports: &adminer-ports [127.0.0.1:8091:8080]
 | |
|     healthcheck: &adminer-healthcheck
 | |
|       test: 'php -r "exit(strpos(file_get_contents(\"http://localhost:8080/\"), \"Adminer\") !== false ? 0 : 1);"'
 | |
|       interval: 10s
 | |
|       timeout: 5s
 | |
|     configs: &adminer-configs
 | |
|       - source: adminer-index.php
 | |
|         target: /var/www/html/index.php
 | |
| 
 | |
|   adminer-postgres:
 | |
|     profiles: ["postgres"]
 | |
|     depends_on: { postgres: { condition: service_healthy } }
 | |
|     image: adminer
 | |
|     ports: *adminer-ports
 | |
|     healthcheck: *adminer-healthcheck
 | |
|     configs: *adminer-configs
 | |
|     environment:
 | |
|       <<: *adminer-environment
 | |
|       ADMINER_DEFAULT_DRIVER: pgsql
 | |
|       ADMINER_DEFAULT_SERVER: postgres
 | |
| 
 | |
|   adminer-sqlite:
 | |
|     profiles: ["sqlite"]
 | |
|     image: adminer
 | |
|     ports: *adminer-ports
 | |
|     healthcheck: *adminer-healthcheck
 | |
|     configs: *adminer-configs
 | |
|     environment:
 | |
|       <<: *adminer-environment
 | |
|       ADMINER_DEFAULT_PASSWORD: ""
 | |
|       ADMINER_DEFAULT_DRIVER: sqlite
 | |
|       ADMINER_DEFAULT_DB: /data/memos_dev.db
 | |
|     volumes: [../.air/docker/memosdata:/data]
 | |
| 
 | |
| configs:
 | |
|   # Patched version of adminer index.php to fill the login form with default values
 | |
|   # and allow passwordless login whenever ADMINER_DEFAULT_DRIVER is sqlite.
 | |
|   adminer-index.php:
 | |
|     content: |
 | |
|       <?php
 | |
|       namespace docker {
 | |
|         function adminer_object() {
 | |
|           require_once('plugins/plugin.php');
 | |
|           class Adminer extends \AdminerPlugin {
 | |
|             function _callParent($$function, $$args) {
 | |
|               if ($$function === 'loginForm') {
 | |
|                 ob_start();
 | |
|                 $$return = \Adminer::loginForm();
 | |
|                 $$form = ob_get_clean();
 | |
|                 $$driver = $$_ENV["ADMINER_DEFAULT_DRIVER"] ?: "server";
 | |
|                 $$server = $$_ENV["ADMINER_DEFAULT_SERVER"] ?: "db";
 | |
|                 $$username = $$_ENV["ADMINER_DEFAULT_USERNAME"];
 | |
|                 $$password = $$_ENV["ADMINER_DEFAULT_PASSWORD"];
 | |
|                 $$db = $$_ENV["ADMINER_DEFAULT_DB"];
 | |
|                 $$form = preg_replace('/ name="auth\[server\]" value="(.*)"/', ' name="auth[server]" value="' . $$server . '"', $$form);
 | |
|                 $$form = str_replace(' id="username" value="" ', ' id="username" value="' . $$username . '" ', $$form);
 | |
|                 $$form = str_replace('name="auth[db]" value=""', 'name="auth[db]" value="' . $$db . '" ', $$form);
 | |
|                 $$form = str_replace('type="password"', 'type="password" value="' . $$password . '"', $$form);
 | |
|                 $$form = preg_replace('/<option value="(.*)" selected/', '/<option value="$$1"/', $$form);
 | |
|                 $$form = preg_replace('/<option value="' . $$driver . '"/', '<option value="' . $$driver . '" selected', $$form);
 | |
|                 echo $$form;
 | |
|                 return $$return;
 | |
|               }
 | |
|               return parent::_callParent($$function, $$args);
 | |
|             }
 | |
|           }
 | |
|           $$plugins = [];
 | |
|           foreach (glob('plugins-enabled/*.php') as $$plugin) {
 | |
|             $$plugins[] = require($$plugin);
 | |
|           }
 | |
|           class AdminerSoftware extends Adminer {
 | |
|             function login($$login, $$password) {
 | |
|                 return substr($$_ENV["ADMINER_DEFAULT_DRIVER"], 0, 6) == 'sqlite' ? true : parent::login($$login, $$password);
 | |
|             }
 | |
|           }
 | |
|           return new AdminerSoftware($$plugins);
 | |
|         }
 | |
|       }
 | |
|       namespace {
 | |
|         if (basename($$_SERVER['DOCUMENT_URI'] ?? $$_SERVER['REQUEST_URI']) === 'adminer.css' && is_readable('adminer.css')) {
 | |
|           header('Content-Type: text/css');
 | |
|           readfile('adminer.css');
 | |
|           exit;
 | |
|         }
 | |
|         function adminer_object() {
 | |
|             return \docker\adminer_object();
 | |
|         }
 | |
|         require('adminer.php');
 | |
|       }
 | |
|   # Patched version of node's container entrypoint to run commands with pnpm by default.
 | |
|   pnpm-entrypoint.sh:
 | |
|     content: |
 | |
|       set -eu
 | |
|       corepack enable pnpm
 | |
|       pnpm "$$@"
 | |
|   # Entrypoint for air container. Installs air and run passed commands.
 | |
|   air-entrypoint.sh:
 | |
|     content: |
 | |
|       set -eu
 | |
|       if [ -z $$(command -v "air") ]; then
 | |
|         echo "Installing air..."
 | |
|         wget -O- -nv https://raw.githubusercontent.com/cosmtrek/air/master/install.sh | sh -s -- -b \
 | |
|           $$(go env GOPATH)/bin v1.49.0
 | |
|       fi
 | |
|       cd /work
 | |
|       /go/bin/air "$$@"
 | |
|   # Entrypoint for goimports container.
 | |
|   goimports-entrypoint.sh:
 | |
|     content: |
 | |
|       set -eu
 | |
|       if [ -z $$(command -v "goimports") ]; then
 | |
|         echo "Installing goimports..."
 | |
|         go install golang.org/x/tools/cmd/goimports@latest        
 | |
|       fi
 | |
|       cd /work
 | |
|       echo "Running goimports..."
 | |
|       goimports "$$@"
 | |
|   # Entrypoint for golangci-lint container.
 | |
|   golangci-lint-entrypoint.sh:
 | |
|     content: |
 | |
|       set -eu
 | |
|       if [ -z $$(command -v "golangci-lint") ]; then
 | |
|         echo "Installing golangci-lint..."
 | |
|         wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b \
 | |
|           $$(go env GOPATH)/bin v1.55.2
 | |
|       fi
 | |
|       cd /work
 | |
|       golangci-lint --version
 | |
|       golangci-lint "$$@"
 |