2024-02-08 16:28:43 +08:00
|
|
|
# > 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|.]
|
|
|
|
#
|
2024-01-02 18:33:19 +08:00
|
|
|
version: "3.0"
|
|
|
|
name: memos-dev
|
2024-02-08 16:28:43 +08:00
|
|
|
volumes:
|
|
|
|
# pnpm uses hard links and node_modules uses symlinks.
|
|
|
|
# Using volumes make things work properly on any host OS.
|
|
|
|
node-modules:
|
|
|
|
pnpm-store:
|
2023-05-29 19:49:47 +08:00
|
|
|
services:
|
|
|
|
web:
|
2024-02-08 16:28:43 +08:00
|
|
|
profiles: ["sqlite", "mysql", "postgres"]
|
2023-12-15 21:46:11 +08:00
|
|
|
image: node:20-alpine
|
2024-02-08 16:28:43 +08:00
|
|
|
ports: [3001:3001]
|
|
|
|
environment:
|
|
|
|
DEV_PROXY_SERVER: http://api:8081/
|
|
|
|
NPM_CONFIG_UPDATE_NOTIFIER: false
|
|
|
|
working_dir: &web-working-dir /work/web
|
2023-10-07 21:24:53 +08:00
|
|
|
entrypoint: ["/bin/sh", "-c"]
|
2024-01-11 21:29:55 +08:00
|
|
|
command: ["corepack enable && pnpm i --frozen-lockfile && pnpm dev"]
|
2024-02-08 16:28:43 +08:00
|
|
|
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
|
2024-02-19 15:10:58 +08:00
|
|
|
- ../web/node_modules:/work/web/node_modules
|
2024-02-08 16:28:43 +08:00
|
|
|
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
|
2023-09-28 22:54:11 +08:00
|
|
|
|
2024-02-08 16:28:43 +08:00
|
|
|
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"]
|
2023-09-28 22:54:11 +08:00
|
|
|
|
|
|
|
buf:
|
|
|
|
profiles: ["tools"]
|
|
|
|
image: bufbuild/buf
|
|
|
|
working_dir: /work/proto
|
|
|
|
command: generate
|
|
|
|
volumes:
|
2024-02-08 16:28:43 +08:00
|
|
|
- ../proto:/work/proto
|
|
|
|
- ../web/src/types/:/work/web/src/types/
|
2023-09-28 22:54:11 +08:00
|
|
|
|
2024-02-08 16:28:43 +08:00
|
|
|
go:
|
2023-09-28 22:54:11 +08:00
|
|
|
profiles: ["tools"]
|
2024-02-08 16:28:43 +08:00
|
|
|
image: *api-image
|
|
|
|
working_dir: *api-working-dir
|
|
|
|
volumes: *api-volumes
|
|
|
|
entrypoint: ["go"]
|
2023-09-28 22:54:11 +08:00
|
|
|
|
2024-02-08 16:28:43 +08:00
|
|
|
goimports:
|
2023-09-28 22:54:11 +08:00
|
|
|
profiles: ["tools"]
|
2024-02-08 16:28:43 +08:00
|
|
|
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 "$$@"
|