Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ require (
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3
github.com/hashicorp/golang-lru v0.5.1
github.com/imdario/mergo v0.3.16
github.com/integralist/go-findroot v0.0.0-20160518114804-ac90681525dc
github.com/jarcoal/httpmock v1.2.0
github.com/jcmturner/gokrb5/v8 v8.4.4
github.com/jmoiron/sqlx v1.3.3
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -608,8 +608,6 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/influxdata/tdigest v0.0.1 h1:XpFptwYmnEKUqmkcDjrzffswZ3nvNeevbUSLPP/ZzIY=
github.com/influxdata/tdigest v0.0.1/go.mod h1:Z0kXnxzbTC2qrx4NaIzYkE1k66+6oEDQTvL95hQFh5Y=
github.com/integralist/go-findroot v0.0.0-20160518114804-ac90681525dc h1:4IZpk3M4m6ypx0IlRoEyEyY1gAdicWLMQ0NcG/gBnnA=
github.com/integralist/go-findroot v0.0.0-20160518114804-ac90681525dc/go.mod h1:UlaC6ndby46IJz9m/03cZPKKkR9ykeIVBBDE3UDBdJk=
github.com/jarcoal/httpmock v1.2.0 h1:gSvTxxFR/MEMfsGrvRbdfpRUMBStovlSRLw0Ep1bwwc=
github.com/jarcoal/httpmock v1.2.0/go.mod h1:oCoTsnAz4+UoOUIf5lJOWV2QQIW5UoeUI6aM2YnWAZk=
github.com/jawher/mow.cli v1.0.4/go.mod h1:5hQj2V8g+qYmLUVWqu4Wuja1pI57M83EChYLVZ0sMKk=
Expand Down
8 changes: 3 additions & 5 deletions tests/mq_protocol_tests/framework/avro/kafka_docker_env.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ import (
"encoding/json"
"io"
"net/http"
"path"

"github.com/integralist/go-findroot/find"
"github.com/pingcap/errors"
"github.com/pingcap/log"
"github.com/pingcap/tiflow/tests/mq_protocol_tests/framework"
Expand Down Expand Up @@ -75,11 +73,11 @@ func NewKafkaDockerEnv(dockerComposeFile string) *KafkaDockerEnv {

var file string
if dockerComposeFile == "" {
st, err := find.Repo()
resolvedFile, err := framework.ResolveRepoPath(dockerComposeFilePath)
if err != nil {
log.Fatal("Could not find git repo root", zap.Error(err))
log.Fatal("Could not find repo-local docker-compose file", zap.Error(err))
}
file = path.Join(st.Path, dockerComposeFilePath)
file = resolvedFile
} else {
file = dockerComposeFile
}
Expand Down
7 changes: 3 additions & 4 deletions tests/mq_protocol_tests/framework/canal/kafka_docker_env.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"io"
"net/http"

"github.com/integralist/go-findroot/find"
"github.com/pingcap/errors"
"github.com/pingcap/log"
"github.com/pingcap/tiflow/tests/mq_protocol_tests/framework"
Expand Down Expand Up @@ -51,11 +50,11 @@ func NewKafkaDockerEnv(dockerComposeFile string) *KafkaDockerEnv {
}
var file string
if dockerComposeFile == "" {
st, err := find.Repo()
resolvedFile, err := framework.ResolveRepoPath(dockerComposeFilePath)
if err != nil {
log.Fatal("Could not find git repo root", zap.Error(err))
log.Fatal("Could not find repo-local docker-compose file", zap.Error(err))
}
file = st.Path + dockerComposeFilePath
file = resolvedFile
} else {
file = dockerComposeFile
}
Expand Down
7 changes: 3 additions & 4 deletions tests/mq_protocol_tests/framework/docker_compose_op.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"os"
"os/exec"

"github.com/integralist/go-findroot/find"
"github.com/pingcap/errors"
"github.com/pingcap/log"
cerrors "github.com/pingcap/tiflow/pkg/errors"
Expand Down Expand Up @@ -143,11 +142,11 @@ func execInController(controller, shellCmd string) ([]byte, error) {
func (d *DockerComposeOperator) DumpStdout() error {
log.Info("Dumping container logs")
cmd := exec.Command("docker-compose", "-f", d.FileName, "logs", "-t")
st, err := find.Repo()
stdoutPath, err := ResolveRepoPath("/deployments/ticdc/docker-compose/logs/stdout.log")
if err != nil {
log.Fatal("Could not find git repo root", zap.Error(err))
log.Fatal("Could not find repo-local docker-compose logs directory", zap.Error(err))
}
f, err := os.Create(st.Path + "/deployments/ticdc/docker-compose/logs/stdout.log")
f, err := os.Create(stdoutPath)
if err != nil {
return errors.AddStack(err)
}
Expand Down
7 changes: 4 additions & 3 deletions tests/mq_protocol_tests/framework/docker_compose_op_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,17 @@ package framework
import (
"testing"

"github.com/integralist/go-findroot/find"
"github.com/stretchr/testify/assert"
)

func TestDockerComposeOperator_SetupTearDown(t *testing.T) {
st, err := find.Repo()
// This integration-style test verifies the operator can boot and tear down
// the avro compose stack after resolving its compose file from the repo tree.
fileName, err := ResolveRepoPath(DockerComposeFilePathPrefix + "docker-compose-avro.yml")
assert.NoError(t, err)

d := &DockerComposeOperator{
FileName: st.Path + "/docker-compose-avro.yml",
FileName: fileName,
Controller: "controller0",
}
d.Setup()
Expand Down
7 changes: 3 additions & 4 deletions tests/mq_protocol_tests/framework/mysql/docker_env.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ package mysql
import (
"database/sql"

"github.com/integralist/go-findroot/find"
"github.com/pingcap/errors"
"github.com/pingcap/log"
"github.com/pingcap/tiflow/tests/mq_protocol_tests/framework"
Expand Down Expand Up @@ -46,11 +45,11 @@ func NewDockerEnv(dockerComposeFile string) *DockerEnv {
}
var file string
if dockerComposeFile == "" {
st, err := find.Repo()
resolvedFile, err := framework.ResolveRepoPath(dockerComposeFilePath)
if err != nil {
log.Fatal("Could not find git repo root", zap.Error(err))
log.Fatal("Could not find repo-local docker-compose file", zap.Error(err))
}
file = st.Path + dockerComposeFilePath
file = resolvedFile
} else {
file = dockerComposeFile
}
Expand Down
68 changes: 68 additions & 0 deletions tests/mq_protocol_tests/framework/repo_path.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright 2026 PingCAP, Inc.
//
// Licensed 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package framework

import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
)

// ResolveRepoPath returns an absolute path under the tiflow repository
// without relying on git metadata. This keeps repo-local test assets
// discoverable from symlinked checkouts and git worktrees.
func ResolveRepoPath(rel string) (string, error) {
normalizedRel := filepath.FromSlash(strings.TrimPrefix(rel, "/"))
for _, base := range repoPathCandidates() {
dir := base
for dir != "." && dir != string(filepath.Separator) {
if isRepoRoot(dir) {
return filepath.Join(dir, normalizedRel), nil
}
parent := filepath.Dir(dir)
if parent == dir {
break
}
dir = parent
}
Comment on lines +31 to +40
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current loop condition dir != "." && dir != string(filepath.Separator) prevents the search from checking the filesystem root (e.g., / on Unix) or the current directory if base is relative. Since filepath.Dir returns the same path when it reaches the root of the filesystem or the root of a relative path (like .), it is more robust to use parent == dir as the termination condition. This ensures that every directory in the hierarchy, including the root, is evaluated by isRepoRoot.

Suggested change
for dir != "." && dir != string(filepath.Separator) {
if isRepoRoot(dir) {
return filepath.Join(dir, normalizedRel), nil
}
parent := filepath.Dir(dir)
if parent == dir {
break
}
dir = parent
}
for {
if isRepoRoot(dir) {
return filepath.Join(dir, normalizedRel), nil
}
parent := filepath.Dir(dir)
if parent == dir {
break
}
dir = parent
}

}

return "", fmt.Errorf("cannot resolve repo path for %q", rel)
}

func repoPathCandidates() []string {
var candidates []string
if _, file, _, ok := runtime.Caller(0); ok {
candidates = append(candidates, filepath.Dir(file))
}
if wd, err := os.Getwd(); err == nil {
candidates = append(candidates, wd)
}
return candidates
}

func isRepoRoot(dir string) bool {
for _, marker := range []string{
filepath.Join(dir, "go.mod"),
filepath.Join(dir, "deployments", "ticdc", "docker-compose"),
filepath.Join(dir, "tests", "mq_protocol_tests", "framework"),
} {
if _, err := os.Stat(marker); err != nil {
return false
}
}
return true
}
32 changes: 32 additions & 0 deletions tests/mq_protocol_tests/framework/repo_path_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2026 PingCAP, Inc.
//
// Licensed 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package framework

import (
"os"
"testing"

"github.com/stretchr/testify/require"
)

func TestResolveRepoPath(t *testing.T) {
// This regression test verifies repo-local compose assets can be found
// without invoking git, so worktree and symlinked checkouts stay stable.
composePath, err := ResolveRepoPath(DockerComposeFilePathPrefix + "docker-compose-avro.yml")
require.NoError(t, err)

info, statErr := os.Stat(composePath)
require.NoError(t, statErr)
require.False(t, info.IsDir())
}