diff --git a/Makefile b/Makefile index 99cd0d95e6c..cd74395f9bd 100644 --- a/Makefile +++ b/Makefile @@ -77,7 +77,7 @@ plugins-registry: build-server: make build-server -C backend -build: build-plugin build-server +build: build-server all: build diff --git a/backend/Dockerfile b/backend/Dockerfile index b85a5ec8d1a..2c31a3d53fc 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -168,18 +168,22 @@ ARG DEBUG= ENV LD_LIBRARY_PATH=/app/libs RUN mkdir -p /app/libs COPY --from=build /usr/local/deps/target/lib/*.so* /app/libs -COPY --from=build /go/bin /usr/bin RUN ldconfig -vn /app/libs # apps -COPY --from=build /app/bin /app/bin +# The single binary already contains every plugin; the .so files that +# `make build-plugin` produces stay in the builder stage and are never copied. +COPY --from=build /app/bin/lake /app/bin/lake COPY --from=build /app/resources /app/resources ENV PATH="/app/bin:${PATH}" ENV DEBUG="$DEBUG" USER root -RUN chgrp -R 0 /app && \ +# Empty mount point for sideloaded plugins: dropping a .so here registers it on +# top of the compiled-in set at startup; see PLUGIN_DIR. +RUN mkdir -p /app/bin/plugins && \ + chgrp -R 0 /app && \ chmod -R g=u /app USER devlake diff --git a/backend/Dockerfile.local b/backend/Dockerfile.local index 8651900d5ab..e2046db3bd2 100644 --- a/backend/Dockerfile.local +++ b/backend/Dockerfile.local @@ -72,15 +72,12 @@ RUN --mount=type=cache,target=/root/.cache/go-build \ --mount=type=cache,target=/go/pkg/mod \ make swag -# Build plugins (parallel, with persistent Go build cache) +# Build the server. `build` is `build-server`: the framework, the API and every +# plugin in the generated registry link into this one binary. No .so files are +# produced for the release image. RUN --mount=type=cache,target=/root/.cache/go-build \ --mount=type=cache,target=/go/pkg/mod \ - make build-plugin - -# Build server -RUN --mount=type=cache,target=/root/.cache/go-build \ - --mount=type=cache,target=/go/pkg/mod \ - VERSION=${TAG}@${SHA} make build-server + VERSION=${TAG}@${SHA} make build FROM debian:bookworm-slim @@ -102,7 +99,10 @@ RUN ldconfig WORKDIR /app -COPY --from=builder /app/bin ./bin +COPY --from=builder /app/bin/lake ./bin/lake + +# Empty mount point for sideloaded plugins +RUN mkdir -p bin/plugins # Create empty python plugins dir to avoid startup error RUN mkdir -p python/plugins diff --git a/backend/Makefile b/backend/Makefile index a09f198a2e4..e8bb9128ec8 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -57,8 +57,13 @@ swag: mock # Regenerate plugins/all, the package that links the Go plugins into the server # binary. Run it after adding, removing or renaming a plugin; CI checks that the # checked-in file matches the tree. +# +# GOOS/GOARCH are cleared because `backend/Dockerfile` exports them to +# cross-compile the server: `go run` would otherwise build the generator for the +# target platform and fail with "exec format error" on the build host. Same +# reason as MOCKERY above. plugins-registry: - go run ./tools/pluginsgen + GOOS= GOARCH= CGO_ENABLED=0 go run ./tools/pluginsgen build-plugin: scripts/build-plugins.sh @@ -72,15 +77,19 @@ build-server: plugins-registry swag build-python: #don't mix this with the other build commands scripts/build-python.sh -build: build-plugin build-server +# `build` produces the only artifact the release ships: one binary with the +# framework, the API and every plugin in the generated registry compiled in. +# Use build-plugin when you want shared objects instead, either to sideload +# them into an existing binary or to ship a core-only server. +build: build-server run: go run server/main.go -dev: build-plugin build-python run +dev: build-python run godev: - DISABLED_REMOTE_PLUGINS=true make build-plugin run + DISABLED_REMOTE_PLUGINS=true make run debug: build-plugin-debug dlv debug server/main.go diff --git a/backend/scripts/build-server.sh b/backend/scripts/build-server.sh index b390185c3f0..479e1fcfa40 100755 --- a/backend/scripts/build-server.sh +++ b/backend/scripts/build-server.sh @@ -22,9 +22,13 @@ echo build-server.sh VERSION: $VERSION ROOT_DIR=$(dirname $(dirname "$0")) VERSION=${VERSION:-$(git describe --tags --always --dirty || true)} EXTRA="" +# Strip the symbol table and DWARF from release builds: they roughly halve the +# binary and nothing needs them at runtime. A debug build keeps them for delve. +LDFLAGS="-s -w" if [ -n "$DEVLAKE_DEBUG" ]; then EXTRA="-gcflags='all=-N -l'" + LDFLAGS="" fi -go build $EXTRA -ldflags "-X 'github.com/apache/devlake/core/version.Version=$VERSION'" -o $ROOT_DIR/bin/lake $ROOT_DIR/server/ \ No newline at end of file +go build $EXTRA -trimpath -ldflags "$LDFLAGS -X 'github.com/apache/devlake/core/version.Version=$VERSION'" -o $ROOT_DIR/bin/lake $ROOT_DIR/server/ \ No newline at end of file diff --git a/env.example b/env.example index effca30dbaa..68be8d6d249 100755 --- a/env.example +++ b/env.example @@ -46,7 +46,10 @@ # certificates) on the dashboard container to file paths mounted into it. # GRAFANA_DATABASE_SSL_MODE=disable -# Lake plugin dir, absolute path or relative path +# Lake plugin dir, absolute path or relative path. +# The release binary already contains every official plugin. This directory is +# scanned at startup for sideloaded .so plugins, which are registered on top of +# the compiled-in set. PLUGIN_DIR=bin/plugins REMOTE_PLUGIN_DIR=python/plugins