Skip to content
Merged
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
7 changes: 5 additions & 2 deletions .github/workflows/go_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,14 @@ jobs:
check-latest: true
cache: true

- name: Allow unprivileged user namespaces
run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0

- name: Download modules
run: go mod download

- name: Build
run: go build -v ./...

- name: Test
run: go test -v ./...
- name: Test (sandboxed, no network)
run: ./scripts/run-tests.sh
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ devenv.local.nix
# Go cache
/.gocache
/.gomodcache
/.gopath
/.gopath
.soulforge
4 changes: 2 additions & 2 deletions internal/esbuild/esbuild_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ func TestESBuildAdmin(t *testing.T) {
}

func TestESBuildAdminWithSCSS(t *testing.T) {
if os.Getenv("NIX_CC") != "" {
t.Skip("Downloading does not work in Nix build")
if !IsDartSassAvailable() {
t.Skip("dart-sass not available locally; install it or run once with network to cache")
}

dir := t.TempDir()
Expand Down
28 changes: 28 additions & 0 deletions internal/esbuild/sass.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,34 @@ var scssVariables []byte
//go:embed static/mixins.scss
var scssMixins []byte

// IsDartSassAvailable checks whether dart-sass is available locally (on PATH or in cache),
// without triggering a network download. Returns true if the binary is available.
func IsDartSassAvailable() bool {
if _, err := exec.LookPath("dart-sass"); err == nil {
return true
}

cache := system.GetCacheWithPrefix("dart-sass")
cacheKey := "dart-sass-" + dartSassVersion + "-" + runtime.GOOS + "-" + runtime.GOARCH

expectedBinary := "sass"
if runtime.GOOS == "windows" {
expectedBinary += ".bat"
}

cachedPath, err := cache.GetFolderCachePath(context.Background(), cacheKey)
if err != nil {
return false
}

executablePath := filepath.Join(cachedPath, expectedBinary)
if _, statErr := os.Stat(executablePath); statErr == nil {
return true
}

return false
}

func locateDartSass(ctx context.Context) (string, error) {
if exePath, err := exec.LookPath("dart-sass"); err == nil {
return exePath, nil
Expand Down
6 changes: 4 additions & 2 deletions internal/extension/theme_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,11 @@ func TestValidateTheme_ReadError(t *testing.T) {
err := os.MkdirAll(resourcesDir, 0755)
assert.NoError(t, err)

// Create a file with no read permissions
// Create a directory in place of theme.json so os.ReadFile fails.
// Using a 0000 file is unreliable because root bypasses permissions,
// which breaks the test in sandboxed CI runs that need root.
themeJSONPath := filepath.Join(resourcesDir, "theme.json")
err = os.WriteFile(themeJSONPath, []byte(`{"previewMedia": "test.png"}`), 0000)
err = os.Mkdir(themeJSONPath, 0755)
assert.NoError(t, err)

ext := &mockExtension{
Expand Down
15 changes: 15 additions & 0 deletions internal/phplint/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@ import (
"github.com/shopware/shopware-cli/logging"
)

// IsPHPWasmCached checks whether a PHP WASM binary for the given version is already in the cache,
// without triggering a network download. Returns true if the binary is available locally.
func IsPHPWasmCached(phpVersion string) bool {
expectedFile := "php-" + phpVersion + ".wasm"
cacheKey := "wasm/php/" + expectedFile
cache := system.GetCacheWithPrefix("php-wasm")

reader, err := cache.Get(context.Background(), cacheKey)
if err != nil {
return false
}
_ = reader.Close()
return true
}

func findPHPWasmFile(ctx context.Context, phpVersion string) ([]byte, error) {
expectedFile := "php-" + phpVersion + ".wasm"
cacheKey := "wasm/php/" + expectedFile
Expand Down
5 changes: 2 additions & 3 deletions internal/phplint/download_test.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package phplint

import (
"os"
"testing"

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

func TestDownloadPHPFile(t *testing.T) {
if os.Getenv("NIX_CC") != "" {
t.Skip("Downloading does not work in Nix build")
if !IsPHPWasmCached("7.4") {
t.Skip("PHP WASM binary not cached; run once with network to download")
}

t.Setenv("SHOPWARE_CLI_CACHE_DIR", t.TempDir())
Expand Down
5 changes: 2 additions & 3 deletions internal/phplint/lint_test.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package phplint

import (
"os"
"testing"

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

func TestLintTestData(t *testing.T) {
if os.Getenv("NIX_CC") != "" {
t.Skip("Downloading does not work in Nix build")
if !IsPHPWasmCached("8.2") {
t.Skip("PHP WASM binary not cached; run once with network to download")
}

t.Setenv("SHOPWARE_CLI_CACHE_DIR", t.TempDir())
Expand Down
58 changes: 58 additions & 0 deletions scripts/run-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"

cd "$REPO_DIR"

if [ $# -eq 0 ]; then
set -- -v ./...
fi

# Warm the build cache while we still have network so the sandboxed run
# does not need to fetch or compile anything from the module proxy.
go test -run='^$' ./...

# Inside the sandbox, force the toolchain to fail fast on any cache miss
# instead of attempting (and hanging on) a network fetch.
export GOFLAGS="${GOFLAGS:-} -mod=readonly"
export GOPROXY=off

# Tells tests that depend on real external downloads (e.g. fetching PHP wasm
# binaries or dart-sass) to skip themselves instead of failing on DNS.
export SHOPWARE_CLI_NO_NETWORK=1

case "$(uname -s)" in
Darwin)
if ! command -v sandbox-exec >/dev/null 2>&1; then
echo "error: sandbox-exec not found" >&2
exit 1
fi
exec sandbox-exec -f "$REPO_DIR/sandbox-no-network.sb" go test "$@"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Enforce full network deny on macOS test sandbox

The Darwin branch executes sandbox-exec with sandbox-no-network.sb, but that profile currently allows network* and only denies outbound TCP on ports 80/443. This means tests can still access external services over other ports/protocols, so the new script does not actually provide the claimed "no network" isolation on macOS.

Useful? React with 👍 / 👎.

;;
Linux)
if ! command -v unshare >/dev/null 2>&1; then
echo "error: unshare not found (expected from util-linux)" >&2
exit 1
fi
# Bring loopback up inside the new netns so tests that use
# httptest.NewServer (127.0.0.1) keep working; only external
# network is blocked, matching the nix-build sandbox.
exec unshare --user --map-root-user --net -- bash -c '
if command -v ip >/dev/null 2>&1; then
ip link set dev lo up
elif command -v ifconfig >/dev/null 2>&1; then
ifconfig lo up
else
echo "error: need either ip (iproute2) or ifconfig to bring up loopback" >&2
exit 1
fi
exec go test "$@"
' bash "$@"
;;
*)
echo "error: unsupported OS: $(uname -s)" >&2
exit 1
;;
esac