Skip to content

Commit

Permalink
Merge pull request #454 from YOU54F/feat/linux-musl
Browse files Browse the repository at this point in the history
feat: support linux musl
  • Loading branch information
YOU54F authored Jan 23, 2025
2 parents 0de92f4 + 3d48a9d commit c74d37a
Show file tree
Hide file tree
Showing 17 changed files with 143 additions and 46 deletions.
41 changes: 38 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ on:
pull_request:
branches:
- master
workflow_dispatch:
inputs:
SKIP_ALPINE_PROVIDER_TESTS:
description: 'Skip alpine provider tests'
required: false
default: 'false'

env:
PACT_BROKER_BASE_URL: https://testdemo.pactflow.io
Expand Down Expand Up @@ -75,19 +81,48 @@ jobs:
if: ${{ always() }}

test-containers:
runs-on: ubuntu-latest
name: ${{ matrix.go-version }}-test-container
continue-on-error: ${{ matrix.experimental }}
runs-on: ${{ matrix.os }}
name: ${{ matrix.os }}-${{ matrix.variant }}-${{ matrix.go-version }}-test-container
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, ubuntu-24.04-arm]
go-version: ["1.22", "1.23"]
variant: [debian]
experimental: [false]
include:
- variant: alpine
go-version: 1.22
experimental: true
os: ubuntu-latest
- variant: alpine
go-version: 1.23
experimental: true
os: ubuntu-latest
- variant: alpine
go-version: 1.22
experimental: true
os: ubuntu-24.04-arm
- variant: alpine
go-version: 1.23
experimental: true
os: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v4

- name: Test dockerfile
run: make docker_test_all
run: |
if [[ "${{ matrix.variant }}" == "alpine" ]]; then
export SKIP_PROVIDER_TESTS=true
if [[ "${{ github.event.inputs.SKIP_ALPINE_PROVIDER_TESTS }}" == "false" ]]; then
export SKIP_PROVIDER_TESTS=false
fi
fi
make docker_test_all
env:
GO_VERSION: ${{ matrix.go-version }}
IMAGE_VARIANT: ${{ matrix.variant }}

finish:
needs: [test,test-containers]
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ _*
*.log
pact-go
pacts
examples/pacts/*.json
logs
tmp
coverage.txt
Expand Down
10 changes: 10 additions & 0 deletions Dockerfile.alpine
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
ARG VERSION=1.22
FROM golang:${VERSION}-alpine

RUN apk add --no-cache curl gcc musl-dev gzip openjdk17-jre bash protoc protobuf-dev make file

COPY . /go/src/github.com/pact-foundation/pact-go

WORKDIR /go/src/github.com/pact-foundation/pact-go

CMD ["make", "test"]
File renamed without changes.
39 changes: 30 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,23 @@ TEST?=./...
.DEFAULT_GOAL := ci
DOCKER_HOST_HTTP?="http://host.docker.internal"
PACT_CLI="docker run --rm -v ${PWD}:${PWD} -e PACT_BROKER_BASE_URL=$(DOCKER_HOST_HTTP) -e PACT_BROKER_USERNAME -e PACT_BROKER_PASSWORD pactfoundation/pact-cli"
PLUGIN_PACT_PROTOBUF_VERSION=0.3.15
PLUGIN_PACT_PROTOBUF_VERSION=0.5.4
PLUGIN_PACT_CSV_VERSION=0.0.6
PLUGIN_PACT_MATT_VERSION=0.1.1
PLUGIN_PACT_AVRO_VERSION=0.0.6

GO_VERSION?=1.22
IMAGE_VARIANT?=debian
ci:: docker deps clean bin test pact
PACT_DOWNLOAD_DIR=/tmp
ifeq ($(OS),Windows_NT)
PACT_DOWNLOAD_DIR=$$TMP
endif

SKIP_RACE?=false
RACE?=-race
ifeq ($(SKIP_RACE),true)
RACE=
endif
# Run the ci target from a developer machine with the environment variables
# set as if it was on Travis CI.
# Use this for quick feedback when playing around with your workflows.
Expand All @@ -37,24 +42,32 @@ docker:
docker compose up -d

docker_build:
docker build -f Dockerfile --build-arg GO_VERSION=${GO_VERSION} -t pactfoundation/pact-go-test .
docker build -f Dockerfile.$(IMAGE_VARIANT) --build-arg GO_VERSION=${GO_VERSION} -t pactfoundation/pact-go-test-$(IMAGE_VARIANT) .

docker_test: docker_build
docker run \
-e LOG_LEVEL=INFO \
-e SKIP_PROVIDER_TESTS=$(SKIP_PROVIDER_TESTS) \
-e SKIP_RACE=$(SKIP_RACE) \
--rm \
pactfoundation/pact-go-test \
-it \
pactfoundation/pact-go-test-$(IMAGE_VARIANT) \
/bin/sh -c "make test"
docker_pact: docker_build
docker run \
-e LOG_LEVEL=INFO \
-e SKIP_PROVIDER_TESTS=$(SKIP_PROVIDER_TESTS) \
-e SKIP_RACE=$(SKIP_RACE) \
--rm \
pactfoundation/pact-go-test \
pactfoundation/pact-go-test-$(IMAGE_VARIANT) \
/bin/sh -c "make pact_local"
docker_test_all: docker_build
docker run \
-e LOG_LEVEL=INFO \
-e SKIP_PROVIDER_TESTS=$(SKIP_PROVIDER_TESTS) \
-e SKIP_RACE=$(SKIP_RACE) \
--rm \
pactfoundation/pact-go-test \
pactfoundation/pact-go-test-$(IMAGE_VARIANT) \
/bin/sh -c "make test && make pact_local"

bin:
Expand Down Expand Up @@ -114,7 +127,9 @@ pact: clean install docker
pact_local: clean download_plugins install
@echo "--- 🔨 Running Pact examples"
go test -v -tags=consumer -count=1 github.com/pact-foundation/pact-go/v2/examples/...
SKIP_PUBLISH=true go test -v -timeout=30s -tags=provider -count=1 github.com/pact-foundation/pact-go/v2/examples/...
if [ "$(SKIP_PROVIDER_TESTS)" != "true" ]; then \
SKIP_PUBLISH=true go test -v -timeout=30s -tags=provider -count=1 github.com/pact-foundation/pact-go/v2/examples/...; \
fi

publish:
@echo "-- 📃 Publishing pacts"
Expand All @@ -124,13 +139,19 @@ release:
echo "--- 🚀 Releasing it"
"$(CURDIR)/scripts/release.sh"

ifeq ($(SKIP_PROVIDER_TESTS),true)
PROVIDER_TEST_TAGS=
else
PROVIDER_TEST_TAGS=-tags=provider
endif

test: deps install
@echo "--- ✅ Running tests"
@if [ -f coverage.txt ]; then rm coverage.txt; fi;
@echo "mode: count" > coverage.txt
@for d in $$(go list ./... | grep -v vendor | grep -v examples); \
do \
go test -v -race -coverprofile=profile.out -covermode=atomic $$d; \
go test -v $(RACE) -coverprofile=profile.out $(PROVIDER_TEST_TAGS) -covermode=atomic $$d; \
if [ $$? != 0 ]; then \
exit 1; \
fi; \
Expand All @@ -143,7 +164,7 @@ test: deps install


testrace:
go test -race $(TEST) $(TESTARGS)
go test $(RACE) $(TEST) $(TESTARGS)

updatedeps:
go get -d -v -p 2 ./...
Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,12 +285,17 @@ _\*_ v3 support is limited to the subset of functionality required to enable lan
| MacOS | arm64 || All |
| Linux (libc) | x86_64 || All |
| Linux (libc) | arm64 || All |
| Linux (musl) | x86_64 | | - |
| Linux (musl) | arm64 | | - |
| Linux (musl) | x86_64 | | v2.2.0 |
| Linux (musl) | arm64 | | v2.2.0 |
| Windows | x86_64 || All |
| Windows | x86 || - |
| Windows | arm64 || - |

⚠️ - Known issues

> - Linux (musl) provider verification is known to experience segmentation faults, reproducible in our CI system.
> - Assistance is greatly appreciated if musl support is important to you.
</details>

## Roadmap
Expand Down
2 changes: 1 addition & 1 deletion consumer/http_v4_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func TestHttpV4TypeSystem(t *testing.T) {
UponReceiving("some scenario").
UsingPlugin(PluginConfig{
Plugin: "protobuf",
Version: "0.3.15",
Version: "0.5.4",
}).
WithRequest("GET", "/").
// WithRequest("GET", "/", func(b *V4InteractionWithPluginRequestBuilder) {
Expand Down
6 changes: 3 additions & 3 deletions examples/grpc/grpc_consumer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func TestGetFeatureSuccess(t *testing.T) {
Given("feature 'Big Tree' exists").
UsingPlugin(message.PluginConfig{
Plugin: "protobuf",
Version: "0.3.15",
Version: "0.5.4",
}).
WithContents(grpcInteraction, "application/protobuf").
StartTransport("grpc", "127.0.0.1", nil). // For plugin tests, we can't assume if a transport is needed, so this is optional
Expand Down Expand Up @@ -123,7 +123,7 @@ func TestGetFeatureError(t *testing.T) {
Given("feature does not exist at -1, -1").
UsingPlugin(message.PluginConfig{
Plugin: "protobuf",
Version: "0.3.15",
Version: "0.5.4",
}).
WithContents(grpcInteraction, "application/protobuf").
StartTransport("grpc", "127.0.0.1", nil). // For plugin tests, we can't assume if a transport is needed, so this is optional
Expand Down Expand Up @@ -194,7 +194,7 @@ func TestSaveFeature(t *testing.T) {
Given("feature does not exist at -1, -1").
UsingPlugin(message.PluginConfig{
Plugin: "protobuf",
Version: "0.3.15",
Version: "0.5.4",
}).
WithContents(grpcInteraction, "application/protobuf").
StartTransport("grpc", "127.0.0.1", nil). // For plugin tests, we can't assume if a transport is needed, so this is optional
Expand Down
2 changes: 1 addition & 1 deletion examples/protobuf-message/protobuf_consumer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func TestPluginMessageConsumer(t *testing.T) {
ExpectsToReceive("feature message").
UsingPlugin(message.PluginConfig{
Plugin: "protobuf",
Version: "0.3.15",
Version: "0.5.4",
}).
WithContents(protoMessage, "application/protobuf").
ExecuteTest(t, func(m message.AsynchronousMessage) error {
Expand Down
2 changes: 1 addition & 1 deletion examples/protobuf-message/protobuf_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func TestPluginMessageProvider(t *testing.T) {
},
})
return feature, message.Metadata{
"contentType": "application/protobuf;message=Feature", // <- This is required to ensure the correct type is matched
"contentType": "application/protobuf;message=.routeguide.Feature", // <- This is required to ensure the correct type is matched
}, nil
},
}
Expand Down
27 changes: 14 additions & 13 deletions installer/installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,6 @@ func NewInstaller(opts ...installerConfig) (*Installer, error) {
log.Println("[WARN] amd64 architecture not detected, defaulting to x86_64. Behaviour may be undefined")
}

// Only perform a check if current OS is linux
if runtime.GOOS == "linux" {
err := i.checkMusl()
if err != nil {
log.Println("[DEBUG] unable to check for presence musl library due to error:", err)
}
}

return i, nil
}

Expand Down Expand Up @@ -260,7 +252,13 @@ func (i *Installer) getDownloadURLForPackage(pkg string) (string, error) {
return "", fmt.Errorf("unable to find package details for package: %s", pkg)
}

return fmt.Sprintf(downloadTemplate, pkg, pkgInfo.version, osToLibName[i.os], i.os, i.arch, osToExtension[i.os]), nil
if checkMusl() && i.os == linux {
return fmt.Sprintf(downloadTemplate, pkg, pkgInfo.version, osToLibName[i.os], i.os, i.arch+"-musl", osToExtension[i.os]), nil
} else {
return fmt.Sprintf(downloadTemplate, pkg, pkgInfo.version, osToLibName[i.os], i.os, i.arch, osToExtension[i.os]), nil

}

}

func (i *Installer) getLibDstForPackage(pkg string) (string, error) {
Expand Down Expand Up @@ -331,20 +329,23 @@ func checkVersion(lib, version, versionRange string) error {
}

// checkMusl checks if the OS uses musl library instead of glibc
func (i *Installer) checkMusl() error {
func checkMusl() bool {
lddPath, err := exec.LookPath("ldd")
if err != nil {
return fmt.Errorf("could not find ldd in environment path")
return false
}

cmd := exec.Command(lddPath, "/bin/echo")
out, err := cmd.CombinedOutput()

if err != nil {
return false
}
if strings.Contains(string(out), "musl") {
log.Println("[WARN] Usage of musl library is known to cause problems, prefer using glibc instead.")
return true
}

return err
return false
}

// download template structure: "https://github.com/pact-foundation/pact-reference/releases/download/PACKAGE-vVERSION/LIBNAME-OS-ARCH.EXTENSION.gz"
Expand Down
29 changes: 25 additions & 4 deletions installer/installer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,37 @@ func TestInstallerDownloader(t *testing.T) {
test Installer
}{
{
name: "ffi lib - linux x86",
name: "ffi lib - linux x86_64",
pkg: FFIPackage,
want: fmt.Sprintf("https://github.com/pact-foundation/pact-reference/releases/download/libpact_ffi-v%s/libpact_ffi-linux-x86_64.so.gz", packages[FFIPackage].version),
want: func() string {
if checkMusl() {
return fmt.Sprintf("https://github.com/pact-foundation/pact-reference/releases/download/libpact_ffi-v%s/libpact_ffi-linux-x86_64-musl.so.gz", packages[FFIPackage].version)
} else {
return fmt.Sprintf("https://github.com/pact-foundation/pact-reference/releases/download/libpact_ffi-v%s/libpact_ffi-linux-x86_64.so.gz", packages[FFIPackage].version)
}
}(),
test: Installer{
os: linux,
arch: x86_64,
},
},
{
name: "ffi lib - macos x86",
name: "ffi lib - linux aarch64",
pkg: FFIPackage,
want: func() string {
if checkMusl() {
return fmt.Sprintf("https://github.com/pact-foundation/pact-reference/releases/download/libpact_ffi-v%s/libpact_ffi-linux-aarch64-musl.so.gz", packages[FFIPackage].version)
} else {
return fmt.Sprintf("https://github.com/pact-foundation/pact-reference/releases/download/libpact_ffi-v%s/libpact_ffi-linux-aarch64.so.gz", packages[FFIPackage].version)
}
}(),
test: Installer{
os: linux,
arch: aarch64,
},
},
{
name: "ffi lib - macos x86_64",
pkg: FFIPackage,
want: fmt.Sprintf("https://github.com/pact-foundation/pact-reference/releases/download/libpact_ffi-v%s/libpact_ffi-macos-x86_64.dylib.gz", packages[FFIPackage].version),
test: Installer{
Expand All @@ -62,7 +83,7 @@ func TestInstallerDownloader(t *testing.T) {
},
},
{
name: "ffi lib - windows x86",
name: "ffi lib - windows x86_64",
pkg: FFIPackage,
want: fmt.Sprintf("https://github.com/pact-foundation/pact-reference/releases/download/libpact_ffi-v%s/pact_ffi-windows-x86_64.dll.gz", packages[FFIPackage].version),
test: Installer{
Expand Down
Loading

0 comments on commit c74d37a

Please sign in to comment.