This is an automated email from the ASF dual-hosted git repository.

klesh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git


The following commit(s) were added to refs/heads/main by this push:
     new 420e494b4 feat(docker): add Dockerfile.local for local development and 
fix migrations (#8866)
420e494b4 is described below

commit 420e494b4ede9a16c4bfb59de099c797bcd7f5f1
Author: Fábio Luciano <[email protected]>
AuthorDate: Tue May 5 06:04:26 2026 -0300

    feat(docker): add Dockerfile.local for local development and fix migrations 
(#8866)
    
    * fix(migrations): add new fields and improve migration scripts for various 
plugins
    
    * feat(docker): add Dockerfile.local for local development and update 
docker-compose configuration
    
    * chore: add license to dockerfile
    
    ---------
    
    Co-authored-by: Fábio Luciano <[email protected]>
---
 backend/Dockerfile                                 |  4 +-
 backend/Dockerfile.local                           | 92 ++++++++++++++++++++++
 .../20240821_add_index_to_tool_github_jobs.go      |  5 ++
 .../20250623_add_display_name_fields.go            | 41 +++++++---
 .../20251123_add_scope_config_id_to_s3_slice.go    | 22 +++---
 .../20230316_modify_character_set.go               | 34 +++-----
 .../20240325_modify_commit_character_type.go       | 18 ++---
 .../20240508_modify_commit_character_type.go       | 16 +---
 .../20250629_add_scope_config_id_to_projects.go    | 12 ++-
 docker-compose-dev.yml                             |  7 +-
 10 files changed, 175 insertions(+), 76 deletions(-)

diff --git a/backend/Dockerfile b/backend/Dockerfile
index 60a31b3eb..4d8fc489e 100644
--- a/backend/Dockerfile
+++ b/backend/Dockerfile
@@ -63,8 +63,8 @@ COPY --from=debian-arm64 /lib/aarch64-linux-gnu 
/rootfs-arm64/lib/aarch64-linux-
 
 RUN for arch in aarch64 x86_64 ; do \
         mkdir -p /tmp/build/${arch} && cd /tmp/build/${arch} && \
-        wget 
https://github.com/libgit2/libgit2/archive/refs/tags/v1.3.2.tar.gz -O - | tar 
-xz && \
-        cd libgit2-1.3.2 && \
+        wget 
https://github.com/libgit2/libgit2/archive/refs/tags/v1.3.0.tar.gz -O - | tar 
-xz && \
+        cd libgit2-1.3.0 && \
         mkdir build && cd build && \
         if [ "$arch" = "aarch64" ] ; then \
             cmake .. -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc \
diff --git a/backend/Dockerfile.local b/backend/Dockerfile.local
new file mode 100644
index 000000000..d4dbb0c07
--- /dev/null
+++ b/backend/Dockerfile.local
@@ -0,0 +1,92 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#Apache DevLake is an effort undergoing incubation at The Apache Software
+#Foundation (ASF), sponsored by the Apache Incubator PMC.
+#
+#Incubation is required of all newly accepted projects until a further review
+#indicates that the infrastructure, communications, and decision making process
+#have stabilized in a manner consistent with other successful ASF projects.
+#
+#While incubation status is not necessarily a reflection of the completeness 
or stability of the code,
+#it does indicate that the project has yet to be fully endorsed by the ASF.
+
+FROM golang:1.20.5-bookworm as builder
+
+ARG GOPROXY=
+ARG HTTP_PROXY=
+ARG HTTPS_PROXY=
+
+RUN apt-get update && apt-get install -y \
+    gcc \
+    binutils \
+    libfindbin-libs-perl \
+    cmake \
+    libssh2-1-dev \
+    libssl-dev \
+    zlib1g-dev
+
+# Install libgit2 for native platform only
+RUN mkdir -p /tmp/build && cd /tmp/build && \
+    wget https://github.com/libgit2/libgit2/archive/refs/tags/v1.3.0.tar.gz -O 
- | tar -xz && \
+    cd libgit2-1.3.0 && \
+    mkdir build && cd build && \
+    cmake .. -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=/usr/local && \
+    make -j$(nproc) install && \
+    ldconfig
+
+RUN go install github.com/vektra/mockery/[email protected]
+RUN go install github.com/swaggo/swag/cmd/[email protected]
+
+WORKDIR /app
+COPY . /app
+ENV GOBIN=/app/bin
+
+ARG TAG=
+ARG SHA=
+
+# Generate mocks and swagger
+RUN make mock
+RUN make swag
+
+# Build plugins
+RUN make build-plugin
+
+# Build server
+RUN VERSION=${TAG}@${SHA} make build-server
+
+FROM debian:bookworm-slim
+
+RUN apt-get update && apt-get install -y \
+    libssh2-1 \
+    libssl3 \
+    ca-certificates \
+    && rm -rf /var/lib/apt/lists/*
+
+# Copy libgit2
+COPY --from=builder /usr/local/lib/libgit2.so* /usr/local/lib/
+RUN ldconfig
+
+WORKDIR /app
+
+COPY --from=builder /app/bin ./bin
+
+# Create empty python plugins dir to avoid startup error
+RUN mkdir -p python/plugins
+
+ENV PORT=8080
+EXPOSE 8080
+
+CMD ["./bin/lake"]
diff --git 
a/backend/plugins/github/models/migrationscripts/20240821_add_index_to_tool_github_jobs.go
 
b/backend/plugins/github/models/migrationscripts/20240821_add_index_to_tool_github_jobs.go
index 4933e557f..43ea38034 100644
--- 
a/backend/plugins/github/models/migrationscripts/20240821_add_index_to_tool_github_jobs.go
+++ 
b/backend/plugins/github/models/migrationscripts/20240821_add_index_to_tool_github_jobs.go
@@ -43,6 +43,11 @@ func (script *addIndexToGithubJobs) Up(basicRes 
context.BasicRes) errors.Error {
                if err := db.Exec(sql); err != nil {
                        return err
                }
+       } else if u.Scheme == "postgres" || u.Scheme == "postgresql" {
+               sql := "CREATE INDEX IF NOT EXISTS idx_repo_id_connection_id ON 
_tool_github_jobs (repo_id, connection_id)"
+               if err := db.Exec(sql); err != nil {
+                       return err
+               }
        }
        return nil
 }
diff --git 
a/backend/plugins/q_dev/models/migrationscripts/20250623_add_display_name_fields.go
 
b/backend/plugins/q_dev/models/migrationscripts/20250623_add_display_name_fields.go
index e165cfd83..f0c772faf 100644
--- 
a/backend/plugins/q_dev/models/migrationscripts/20250623_add_display_name_fields.go
+++ 
b/backend/plugins/q_dev/models/migrationscripts/20250623_add_display_name_fields.go
@@ -21,29 +21,44 @@ import (
        "github.com/apache/incubator-devlake/core/context"
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/plugin"
+       "github.com/apache/incubator-devlake/helpers/migrationhelper"
 )
 
 var _ plugin.MigrationScript = (*addDisplayNameFields)(nil)
 
 type addDisplayNameFields struct{}
 
-func (*addDisplayNameFields) Up(basicRes context.BasicRes) errors.Error {
-       db := basicRes.GetDal()
+type QDevConnection20250623 struct {
+       IdentityStoreId     string `gorm:"type:VARCHAR(255)"`
+       IdentityStoreRegion string `gorm:"type:VARCHAR(255)"`
+}
+
+func (QDevConnection20250623) TableName() string {
+       return "_tool_q_dev_connections"
+}
+
+type QDevUserData20250623 struct {
+       DisplayName string `gorm:"type:VARCHAR(255)"`
+}
 
-       // Add Identity Center fields to connections table
-       // Ignore error if column already exists (MySQL error 1060)
-       _ = db.Exec("ALTER TABLE _tool_q_dev_connections ADD COLUMN 
identity_store_id VARCHAR(255)")
-       _ = db.Exec("ALTER TABLE _tool_q_dev_connections ADD COLUMN 
identity_store_region VARCHAR(255)")
+func (QDevUserData20250623) TableName() string {
+       return "_tool_q_dev_user_data"
+}
 
-       // Add display_name column to user_data table
-       // Ignore error if column already exists (MySQL error 1060)
-       _ = db.Exec("ALTER TABLE _tool_q_dev_user_data ADD COLUMN display_name 
VARCHAR(255)")
+type QDevUserMetrics20250623 struct {
+       DisplayName string `gorm:"type:VARCHAR(255)"`
+}
 
-       // Add display_name column to user_metrics table
-       // Ignore error if column already exists (MySQL error 1060)
-       _ = db.Exec("ALTER TABLE _tool_q_dev_user_metrics ADD COLUMN 
display_name VARCHAR(255)")
+func (QDevUserMetrics20250623) TableName() string {
+       return "_tool_q_dev_user_metrics"
+}
 
-       return nil
+func (*addDisplayNameFields) Up(basicRes context.BasicRes) errors.Error {
+       return migrationhelper.AutoMigrateTables(basicRes,
+               &QDevConnection20250623{},
+               &QDevUserData20250623{},
+               &QDevUserMetrics20250623{},
+       )
 }
 
 func (*addDisplayNameFields) Version() uint64 {
diff --git 
a/backend/plugins/q_dev/models/migrationscripts/20251123_add_scope_config_id_to_s3_slice.go
 
b/backend/plugins/q_dev/models/migrationscripts/20251123_add_scope_config_id_to_s3_slice.go
index e072e4f59..fdeb10583 100644
--- 
a/backend/plugins/q_dev/models/migrationscripts/20251123_add_scope_config_id_to_s3_slice.go
+++ 
b/backend/plugins/q_dev/models/migrationscripts/20251123_add_scope_config_id_to_s3_slice.go
@@ -20,23 +20,21 @@ package migrationscripts
 import (
        "github.com/apache/incubator-devlake/core/context"
        "github.com/apache/incubator-devlake/core/errors"
+       "github.com/apache/incubator-devlake/helpers/migrationhelper"
 )
 
 type addScopeConfigIdToS3Slice struct{}
 
+type QDevS3Slice20251123 struct {
+       ScopeConfigId uint64 `gorm:"type:BIGINT DEFAULT 0"`
+}
+
+func (QDevS3Slice20251123) TableName() string {
+       return "_tool_q_dev_s3_slices"
+}
+
 func (*addScopeConfigIdToS3Slice) Up(basicRes context.BasicRes) errors.Error {
-       db := basicRes.GetDal()
-
-       // Add scope_config_id column to _tool_q_dev_s3_slices table
-       err := db.Exec(`
-               ALTER TABLE _tool_q_dev_s3_slices
-               ADD COLUMN scope_config_id BIGINT UNSIGNED DEFAULT 0
-       `)
-       if err != nil {
-               return errors.Convert(err)
-       }
-
-       return nil
+       return migrationhelper.AutoMigrateTables(basicRes, 
&QDevS3Slice20251123{})
 }
 
 func (*addScopeConfigIdToS3Slice) Version() uint64 {
diff --git 
a/backend/plugins/sonarqube/models/migrationscripts/20230316_modify_character_set.go
 
b/backend/plugins/sonarqube/models/migrationscripts/20230316_modify_character_set.go
index fa648e838..b1f62cd68 100644
--- 
a/backend/plugins/sonarqube/models/migrationscripts/20230316_modify_character_set.go
+++ 
b/backend/plugins/sonarqube/models/migrationscripts/20230316_modify_character_set.go
@@ -18,8 +18,6 @@ limitations under the License.
 package migrationscripts
 
 import (
-       "net/url"
-
        "github.com/apache/incubator-devlake/core/context"
        "github.com/apache/incubator-devlake/core/errors"
 )
@@ -27,52 +25,46 @@ import (
 type modifyCharacterSet struct{}
 
 func (*modifyCharacterSet) Up(basicRes context.BasicRes) errors.Error {
-       dbUrl := basicRes.GetConfig("DB_URL")
-       if dbUrl == "" {
-               return errors.BadInput.New("DB_URL is required")
-       }
-       u, err1 := url.Parse(dbUrl)
-       if err1 != nil {
-               return errors.Convert(err1)
-       }
-       if u.Scheme == "mysql" {
-               err := basicRes.GetDal().Exec(`ALTER TABLE 
_tool_sonarqube_projects CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`)
+       db := basicRes.GetDal()
+       // Character set conversion is MySQL-specific; PostgreSQL uses UTF-8 by 
default
+       if db.Dialect() == "mysql" {
+               err := db.Exec(`ALTER TABLE _tool_sonarqube_projects CONVERT TO 
CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`)
                if err != nil {
                        return err
                }
-               err = basicRes.GetDal().Exec(`ALTER TABLE 
_tool_sonarqube_issues CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`)
+               err = db.Exec(`ALTER TABLE _tool_sonarqube_issues CONVERT TO 
CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`)
                if err != nil {
                        return err
                }
-               err = basicRes.GetDal().Exec(`ALTER TABLE 
_tool_sonarqube_issue_code_blocks CONVERT TO CHARACTER SET utf8mb4 COLLATE 
utf8mb4_bin;`)
+               err = db.Exec(`ALTER TABLE _tool_sonarqube_issue_code_blocks 
CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`)
                if err != nil {
                        return err
                }
-               err = basicRes.GetDal().Exec(`ALTER TABLE 
_tool_sonarqube_hotspots CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`)
+               err = db.Exec(`ALTER TABLE _tool_sonarqube_hotspots CONVERT TO 
CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`)
                if err != nil {
                        return err
                }
-               err = basicRes.GetDal().Exec(`ALTER TABLE 
_tool_sonarqube_file_metrics CONVERT TO CHARACTER SET utf8mb4 COLLATE 
utf8mb4_bin;`)
+               err = db.Exec(`ALTER TABLE _tool_sonarqube_file_metrics CONVERT 
TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`)
                if err != nil {
                        return err
                }
-               err = basicRes.GetDal().Exec(`ALTER TABLE 
_tool_sonarqube_accounts CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`)
+               err = db.Exec(`ALTER TABLE _tool_sonarqube_accounts CONVERT TO 
CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`)
                if err != nil {
                        return err
                }
-               err = basicRes.GetDal().Exec(`ALTER TABLE cq_projects CONVERT 
TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`)
+               err = db.Exec(`ALTER TABLE cq_projects CONVERT TO CHARACTER SET 
utf8mb4 COLLATE utf8mb4_bin;`)
                if err != nil {
                        return err
                }
-               err = basicRes.GetDal().Exec(`ALTER TABLE cq_issues CONVERT TO 
CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`)
+               err = db.Exec(`ALTER TABLE cq_issues CONVERT TO CHARACTER SET 
utf8mb4 COLLATE utf8mb4_bin;`)
                if err != nil {
                        return err
                }
-               err = basicRes.GetDal().Exec(`ALTER TABLE cq_issue_code_blocks 
CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`)
+               err = db.Exec(`ALTER TABLE cq_issue_code_blocks CONVERT TO 
CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`)
                if err != nil {
                        return err
                }
-               err = basicRes.GetDal().Exec(`ALTER TABLE cq_file_metrics 
CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`)
+               err = db.Exec(`ALTER TABLE cq_file_metrics CONVERT TO CHARACTER 
SET utf8mb4 COLLATE utf8mb4_bin;`)
                if err != nil {
                        return err
                }
diff --git 
a/backend/plugins/sonarqube/models/migrationscripts/20240325_modify_commit_character_type.go
 
b/backend/plugins/sonarqube/models/migrationscripts/20240325_modify_commit_character_type.go
index 56f058980..1e61f138f 100644
--- 
a/backend/plugins/sonarqube/models/migrationscripts/20240325_modify_commit_character_type.go
+++ 
b/backend/plugins/sonarqube/models/migrationscripts/20240325_modify_commit_character_type.go
@@ -18,8 +18,6 @@ limitations under the License.
 package migrationscripts
 
 import (
-       "net/url"
-
        "github.com/apache/incubator-devlake/core/context"
        "github.com/apache/incubator-devlake/core/errors"
 )
@@ -27,20 +25,14 @@ import (
 type modifyCommitCharacterType struct{}
 
 func (*modifyCommitCharacterType) Up(basicRes context.BasicRes) errors.Error {
-       dbUrl := basicRes.GetConfig("DB_URL")
-       if dbUrl == "" {
-               return errors.BadInput.New("DB_URL is required")
-       }
-       u, err1 := url.Parse(dbUrl)
-       if err1 != nil {
-               return errors.Convert(err1)
-       }
-       if u.Scheme == "mysql" {
-               err := basicRes.GetDal().Exec(`ALTER TABLE commits MODIFY 
COLUMN message LONGTEXT CHARACTER SET binary;`)
+       db := basicRes.GetDal()
+       // Binary column types are MySQL-specific; PostgreSQL uses bytea 
natively
+       if db.Dialect() == "mysql" {
+               err := db.Exec(`ALTER TABLE commits MODIFY COLUMN message 
LONGTEXT CHARACTER SET binary;`)
                if err != nil {
                        return err
                }
-               err = basicRes.GetDal().Exec(`ALTER TABLE commit_files MODIFY 
COLUMN file_path VARBINARY(255);`)
+               err = db.Exec(`ALTER TABLE commit_files MODIFY COLUMN file_path 
VARBINARY(255);`)
                if err != nil {
                        return err
                }
diff --git 
a/backend/plugins/sonarqube/models/migrationscripts/20240508_modify_commit_character_type.go
 
b/backend/plugins/sonarqube/models/migrationscripts/20240508_modify_commit_character_type.go
index 5ba5d668d..1a130170b 100644
--- 
a/backend/plugins/sonarqube/models/migrationscripts/20240508_modify_commit_character_type.go
+++ 
b/backend/plugins/sonarqube/models/migrationscripts/20240508_modify_commit_character_type.go
@@ -18,8 +18,6 @@ limitations under the License.
 package migrationscripts
 
 import (
-       "net/url"
-
        "github.com/apache/incubator-devlake/core/context"
        "github.com/apache/incubator-devlake/core/errors"
 )
@@ -27,16 +25,10 @@ import (
 type modifyCommitCharacterType0508 struct{}
 
 func (*modifyCommitCharacterType0508) Up(basicRes context.BasicRes) 
errors.Error {
-       dbUrl := basicRes.GetConfig("DB_URL")
-       if dbUrl == "" {
-               return errors.BadInput.New("DB_URL is required")
-       }
-       u, err1 := url.Parse(dbUrl)
-       if err1 != nil {
-               return errors.Convert(err1)
-       }
-       if u.Scheme == "mysql" {
-               err := basicRes.GetDal().Exec(`ALTER TABLE commit_files MODIFY 
COLUMN file_path VARBINARY(1024);`)
+       db := basicRes.GetDal()
+       // VARBINARY is MySQL-specific; PostgreSQL uses bytea natively
+       if db.Dialect() == "mysql" {
+               err := db.Exec(`ALTER TABLE commit_files MODIFY COLUMN 
file_path VARBINARY(1024);`)
                if err != nil {
                        return err
                }
diff --git 
a/backend/plugins/testmo/models/migrationscripts/20250629_add_scope_config_id_to_projects.go
 
b/backend/plugins/testmo/models/migrationscripts/20250629_add_scope_config_id_to_projects.go
index e8970412a..b13d1233c 100644
--- 
a/backend/plugins/testmo/models/migrationscripts/20250629_add_scope_config_id_to_projects.go
+++ 
b/backend/plugins/testmo/models/migrationscripts/20250629_add_scope_config_id_to_projects.go
@@ -21,13 +21,21 @@ import (
        "github.com/apache/incubator-devlake/core/context"
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/plugin"
+       "github.com/apache/incubator-devlake/helpers/migrationhelper"
 )
 
 type addScopeConfigIdToProjects struct{}
 
+type TestmoProject20250629 struct {
+       ScopeConfigId uint64 `gorm:"type:BIGINT NOT NULL DEFAULT 0"`
+}
+
+func (TestmoProject20250629) TableName() string {
+       return "_tool_testmo_projects"
+}
+
 func (*addScopeConfigIdToProjects) Up(basicRes context.BasicRes) errors.Error {
-       db := basicRes.GetDal()
-       return db.Exec("ALTER TABLE `_tool_testmo_projects` ADD COLUMN 
`scope_config_id` bigint unsigned NOT NULL DEFAULT 0")
+       return migrationhelper.AutoMigrateTables(basicRes, 
&TestmoProject20250629{})
 }
 
 func (*addScopeConfigIdToProjects) Version() uint64 {
diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml
index b56720db2..b575a6029 100644
--- a/docker-compose-dev.yml
+++ b/docker-compose-dev.yml
@@ -79,9 +79,10 @@ services:
       - mysql
 
   devlake:
-    image: devlake.docker.scarf.sh/apache/devlake:latest
+    image: devlake-local:latest
     build:
       context: backend
+      dockerfile: Dockerfile.local
       args:
         HTTPS_PROXY: "${HTTPS_PROXY}"
         GOPROXY: "${GOPROXY}"
@@ -93,6 +94,10 @@ services:
     env_file:
       - ./.env
     environment:
+      DB_URL: postgres://merico:merico@postgres:5432/lake?sslmode=disable
+      E2E_DB_URL: 
postgres://merico:merico@postgres:5432/lake_test?sslmode=disable
+      REMOTE_PLUGIN_DIR: ""
+      FORCE_MIGRATION: "true"
       LOGGING_DIR: /app/logs
       TZ: UTC
       # LOGOUT_URI: 
https://xxx.amazoncognito.com/logout?client_id=yyy&logout_uri=http%3A%2F%2Flocalhost%3A4180%2Foauth2%2Fsign_out

Reply via email to