Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
20 changes: 18 additions & 2 deletions .github/workflows/ci-lint-go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,27 @@ jobs:
cache-dependency-path: "${{ inputs.project-directory == '' && '.' || inputs.project-directory }}/go.sum"
id: go

- name: Determine golangci-lint version
id: linter-version
working-directory: ./${{ inputs.project-directory == '' && '.' || inputs.project-directory }}
shell: bash
run: |
# golangci-lint must be built with a Go version >= the module's go directive.
# v2.0.2 is built with Go 1.24, v2.9.0 is the first built with Go 1.25.
MODULE_GO_VERSION=$(grep '^go ' go.mod | awk '{print $2}')
MODULE_MAJOR_MINOR=$(echo "${MODULE_GO_VERSION}" | grep -oE '^[0-9]+\.[0-9]+')

if [ "$(printf '%s\n' "${MODULE_MAJOR_MINOR}" "1.25" | sort -V | head -n1)" = "1.25" ]; then
echo "version=v2.9.0" >> $GITHUB_OUTPUT
else
echo "version=v2.0.2" >> $GITHUB_OUTPUT
fi
Comment thread
mdelapenya marked this conversation as resolved.
Outdated
echo "Module requires Go ${MODULE_GO_VERSION}, using golangci-lint $(grep 'version=' $GITHUB_OUTPUT | cut -d= -f2)"

- name: golangci-lint
uses: golangci/golangci-lint-action@1481404843c368bc19ca9406f87d6e0fc97bdcfd # v7.0.0
with:
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
version: v2.0.2
version: ${{ steps.linter-version.outputs.version }}
# Optional: working directory, useful for monorepos
working-directory: ${{ inputs.project-directory }}

Expand Down
37 changes: 31 additions & 6 deletions .github/workflows/ci-test-go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,34 @@ jobs:
cache-dependency-path: '${{ inputs.project-directory }}/go.sum'
id: go

- name: Check Go version compatibility
id: go-compat
working-directory: ./${{ inputs.project-directory == '' && '.' || inputs.project-directory }}
shell: bash
run: |
# Read the minimum Go version required by the module
MODULE_GO_VERSION=$(grep '^go ' go.mod | awk '{print $2}')
RUNNER_GO_VERSION=$(go env GOVERSION | sed 's/^go//')
echo "Module requires Go ${MODULE_GO_VERSION}, runner has Go ${RUNNER_GO_VERSION}"

# Extract major.minor for comparison
MODULE_MAJOR_MINOR=$(echo "${MODULE_GO_VERSION}" | grep -oE '^[0-9]+\.[0-9]+')
RUNNER_MAJOR_MINOR=$(echo "${RUNNER_GO_VERSION}" | grep -oE '^[0-9]+\.[0-9]+')

if [ "$(printf '%s\n' "${MODULE_MAJOR_MINOR}" "${RUNNER_MAJOR_MINOR}" | sort -V | head -n1)" = "${MODULE_MAJOR_MINOR}" ]; then
echo "compatible=true" >> $GITHUB_OUTPUT
else
echo "compatible=false" >> $GITHUB_OUTPUT
echo "::warning::Skipping tests: module requires Go ${MODULE_GO_VERSION} but runner has Go ${RUNNER_GO_VERSION}"
fi

- name: ensure compilation
if: steps.go-compat.outputs.compatible == 'true'
working-directory: ./${{ inputs.project-directory }}
run: go build ./...

- name: Install dependencies
if: steps.go-compat.outputs.compatible == 'true'
shell: bash
run: |
SCRIPT_PATH="./.github/scripts/${{ inputs.project-directory }}/install-dependencies.sh"
Expand All @@ -88,23 +111,25 @@ jobs:

# Setup Testcontainers Cloud Client right before your Testcontainers tests
- name: Setup Testcontainers Cloud Client
if: ${{ inputs.testcontainers-cloud }}
if: ${{ steps.go-compat.outputs.compatible == 'true' && inputs.testcontainers-cloud }}
uses: atomicjar/testcontainers-cloud-setup-action@c335bdbb570ec7c48f72c7d450c077f0a002293e # v1.3
with:
token: ${{ secrets.TCC_TOKEN }}

- name: go test
if: steps.go-compat.outputs.compatible == 'true'
working-directory: ./${{ inputs.project-directory }}
timeout-minutes: 30
run: make test-unit

- name: Run checker
if: steps.go-compat.outputs.compatible == 'true'
run: |
./scripts/check_environment.sh

# (Optionally) When you don't need Testcontainers Cloud anymore, you could terminate sessions eagerly
- name: Terminate Testcontainers Cloud Client active sessions
if: ${{ inputs.testcontainers-cloud }}
if: ${{ steps.go-compat.outputs.compatible == 'true' && inputs.testcontainers-cloud }}
uses: atomicjar/testcontainers-cloud-setup-action@c335bdbb570ec7c48f72c7d450c077f0a002293e # v1.3
with:
action: terminate
Expand All @@ -113,10 +138,10 @@ jobs:
uses: test-summary/action@31493c76ec9e7aa675f1585d3ed6f1da69269a86 # v2.4
with:
paths: "**/${{ inputs.project-directory }}/TEST-unit*.xml"
if: always()
if: ${{ always() && steps.go-compat.outputs.compatible == 'true' }}

- name: Decide if Sonar must be run
if: ${{ matrix.platform == 'ubuntu-latest' }}
if: ${{ steps.go-compat.outputs.compatible == 'true' && matrix.platform == 'ubuntu-latest' }}
run: |
if [[ "1.24.x" == "${{ inputs.go-version }}" ]] && \
[[ "true" != "${{ inputs.rootless-docker }}" ]] && \
Expand All @@ -128,7 +153,7 @@ jobs:
fi

- name: Set Sonar Cloud environment variables
if: ${{ env.SHOULD_RUN_SONAR == 'true' }}
if: ${{ steps.go-compat.outputs.compatible == 'true' && env.SHOULD_RUN_SONAR == 'true' }}
run: |
echo "PROJECT_VERSION=$(grep 'latest_version' mkdocs.yml | cut -d':' -f2 | tr -d ' ')" >> $GITHUB_ENV
if [ "${{ inputs.project-directory }}" == "" ]; then
Expand All @@ -144,7 +169,7 @@ jobs:
fi

- name: SonarQube Scan
if: ${{ env.SHOULD_RUN_SONAR == 'true' }}
if: ${{ steps.go-compat.outputs.compatible == 'true' && env.SHOULD_RUN_SONAR == 'true' }}
uses: SonarSource/sonarqube-scan-action@aa494459d7c39c106cc77b166de8b4250a32bb97 # v5.1.0
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
Expand Down
27 changes: 20 additions & 7 deletions modules/compose/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import (
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/flags"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/compose"
"github.com/docker/compose/v5/pkg/api"
"github.com/docker/compose/v5/pkg/compose"
"github.com/google/uuid"

"github.com/testcontainers/testcontainers-go"
Expand Down Expand Up @@ -145,21 +145,34 @@ func NewDockerComposeWith(opts ...ComposeStackOption) (*DockerCompose, error) {
return nil, ErrNoStackConfigured
}

// Create Docker CLI for compose service (uses moby/moby client internally)
dockerCli, err := command.NewDockerCli()
if err != nil {
return nil, fmt.Errorf("new docker client: %w", err)
return nil, fmt.Errorf("new docker cli: %w", err)
}

if err = dockerCli.Initialize(flags.NewClientOptions()); err != nil {
return nil, fmt.Errorf("initialize docker cli: %w", err)
}

if err = dockerCli.Initialize(flags.NewClientOptions(), command.WithInitializeClient(makeClient)); err != nil {
return nil, fmt.Errorf("initialize docker client: %w", err)
composeService, err := compose.NewComposeService(dockerCli)
if err != nil {
return nil, fmt.Errorf("new compose service: %w", err)
}

// Create a separate testcontainers Docker client for provider and direct API calls.
// Compose v5 uses moby/moby/client internally, which is not type-compatible with
// docker/docker/client used by testcontainers, so we cannot share the CLI client.
provider, err := testcontainers.NewDockerProvider(testcontainers.WithLogger(composeOptions.Logger))
if err != nil {
return nil, fmt.Errorf("new docker provider: %w", err)
}

dockerClient := dockerCli.Client()
dockerClient, err := testcontainers.NewDockerClientWithOpts(context.Background())
if err != nil {
return nil, fmt.Errorf("new docker client: %w", err)
}

provider.SetClient(dockerClient)
Comment thread
mdelapenya marked this conversation as resolved.

composeAPI := &DockerCompose{
Expand All @@ -168,7 +181,7 @@ func NewDockerComposeWith(opts ...ComposeStackOption) (*DockerCompose, error) {
temporaryConfigs: composeOptions.temporaryPaths,
logger: composeOptions.Logger,
projectProfiles: composeOptions.Profiles,
composeService: compose.NewComposeService(dockerCli),
composeService: composeService,
dockerClient: dockerClient,
waitStrategies: make(map[string]wait.Strategy),
containers: make(map[string]*testcontainers.DockerContainer),
Expand Down
40 changes: 9 additions & 31 deletions modules/compose/compose_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ import (

"github.com/compose-spec/compose-go/v2/cli"
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v5/pkg/api"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
dockernetwork "github.com/docker/docker/api/types/network"
Expand Down Expand Up @@ -530,29 +529,16 @@ func (d *DockerCompose) lookupNetworks(ctx context.Context) error {
}

func (d *DockerCompose) compileProject(ctx context.Context) (*types.Project, error) {
const nameAndDefaultConfigPath = 2
projectOptions := make([]cli.ProjectOptionsFn, len(d.projectOptions), len(d.projectOptions)+nameAndDefaultConfigPath)

copy(projectOptions, d.projectOptions)
projectOptions = append(projectOptions, cli.WithName(d.name), cli.WithDefaultConfigPath)

compiledOptions, err := cli.NewProjectOptions(d.configs, projectOptions...)
if err != nil {
return nil, fmt.Errorf("new project options: %w", err)
}

proj, err := compiledOptions.LoadProject(ctx)
proj, err := d.composeService.LoadProject(ctx, api.ProjectLoadOptions{
ProjectName: d.name,
ConfigPaths: d.configs,
Profiles: d.projectProfiles,
ProjectOptionsFns: d.projectOptions,
})
if err != nil {
return nil, fmt.Errorf("load project: %w", err)
}

if len(d.projectProfiles) > 0 {
proj, err = proj.WithProfiles(d.projectProfiles)
if err != nil {
return nil, fmt.Errorf("with profiles: %w", err)
}
}

for i, s := range proj.Services {
s.CustomLabels = map[string]string{
api.ProjectLabel: proj.Name,
Expand All @@ -565,9 +551,9 @@ func (d *DockerCompose) compileProject(ctx context.Context) (*types.Project, err

testcontainers.AddGenericLabels(s.CustomLabels)

for i, envFile := range compiledOptions.EnvFiles {
for j, envFile := range s.EnvFiles {
// add a label for each env file, indexed by its position
s.CustomLabels[fmt.Sprintf("%s.%d", api.EnvironmentFileLabel, i)] = envFile
s.CustomLabels[fmt.Sprintf("%s.%d", api.EnvironmentFileLabel, j)] = envFile.Path
}

proj.Services[i] = s
Expand Down Expand Up @@ -600,11 +586,3 @@ func withEnv(env map[string]string) func(*cli.ProjectOptions) error {
return nil
}
}

func makeClient(*command.DockerCli) (client.APIClient, error) {
dockerClient, err := testcontainers.NewDockerClientWithOpts(context.Background())
if err != nil {
return nil, fmt.Errorf("new docker client: %w", err)
}
return dockerClient, nil
}
2 changes: 1 addition & 1 deletion modules/compose/compose_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"testing"
"time"

"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v5/pkg/api"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/volume"
"github.com/google/uuid"
Expand Down
10 changes: 6 additions & 4 deletions modules/compose/compose_local.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,11 @@ func (dc *LocalDockerCompose) getDockerComposeEnvironment() map[string]string {
environment := map[string]string{}

composeFileEnvVariableValue := ""
var composeFileEnvVariableValueSb121 strings.Builder
for _, abs := range dc.absComposeFilePaths {
composeFileEnvVariableValue += abs + string(os.PathListSeparator)
composeFileEnvVariableValueSb121.WriteString(abs + string(os.PathListSeparator))
}
composeFileEnvVariableValue += composeFileEnvVariableValueSb121.String()

environment[envProjectName] = dc.Identifier
environment[envComposeFile] = composeFileEnvVariableValue
Expand Down Expand Up @@ -238,10 +240,10 @@ func (dc *LocalDockerCompose) determineVersion() error {
return fmt.Errorf("parsing major version: %w", err)
}

switch majorVersion {
case 1:
switch {
case majorVersion == 1:
dc.ComposeVersion = composeVersion1{}
case 2:
case majorVersion >= 2:
dc.ComposeVersion = composeVersion2{}
default:
return fmt.Errorf("unexpected compose version %d", majorVersion)
Expand Down
Loading
Loading