Skip to content

Commit 2ec07d2

Browse files
committed
merge #37 into cyphar/filepath-securejoin:main
Aleksa Sarai (6): gha: use stable/oldstable go versions gha: compile-test for all supported Go versions gha: test older Go versions in CI deps: downgrade golang.org/x/sys requirement go: lower Go requirement to Go 1.18 tests: don't call testing.Testing() in mocks LGTMs: cyphar
2 parents 07ceaf0 + d17d372 commit 2ec07d2

15 files changed

Lines changed: 362 additions & 49 deletions

.github/workflows/ci.yml

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,52 @@ on:
1111
- cron: "30 10 * * 0"
1212

1313
jobs:
14-
test-windows:
14+
test-build:
1515
strategy:
1616
fail-fast: false
1717
matrix:
18+
os:
19+
- windows-latest
20+
- ubuntu-latest
21+
- macos-latest
1822
go-version:
23+
- "1.18"
24+
- "1.19"
25+
- "1.20"
1926
- "1.21"
2027
- "1.22"
28+
- "1.23"
2129
- "^1"
30+
runs-on: ${{ matrix.os }}
31+
steps:
32+
- uses: actions/checkout@v4
33+
- uses: actions/setup-go@v5
34+
with:
35+
go-version: ${{ matrix.go-version }}
36+
- name: go build check
37+
run: go build ./...
38+
- name: go test build check
39+
run: go test -run none ./...
40+
41+
test-windows:
42+
strategy:
43+
fail-fast: false
44+
matrix:
45+
go-version:
46+
- "1.18"
47+
- "1.20"
48+
- "1.21"
49+
- "oldstable"
50+
- "stable"
2251
runs-on: windows-latest
2352
steps:
2453
- uses: actions/checkout@v4
2554
- uses: actions/setup-go@v5
2655
with:
2756
go-version: ${{ matrix.go-version }}
2857
- name: mkdir gocoverdir
58+
# We can only use -test.gocoverdir for Go >= 1.20.
59+
if: ${{ matrix.go-version != '1.18' && matrix.go-version != '1.19' }}
2960
run: |
3061
# mktemp --tmpdir -d gocoverdir.XXXXXXXX
3162
function New-TemporaryDirectory {
@@ -42,8 +73,15 @@ jobs:
4273
$GOCOVERDIR = (New-TemporaryDirectory -Prefix "gocoverdir")
4374
echo "GOCOVERDIR=$GOCOVERDIR" >>"$env:GITHUB_ENV"
4475
- name: unit tests
45-
run: go test -v -cover '-test.gocoverdir' "$env:GOCOVERDIR" ./...
76+
run: |
77+
if (Test-Path 'env:GOCOVERDIR') {
78+
go test -v -cover '-test.gocoverdir' "$env:GOCOVERDIR" ./...
79+
} else {
80+
go test -v ./...
81+
}
4682
- name: upload coverage
83+
# We can only use -test.gocoverdir for Go >= 1.20.
84+
if: ${{ matrix.go-version != '1.18' && matrix.go-version != '1.19' }}
4785
uses: actions/upload-artifact@v4
4886
with:
4987
name: coverage-${{ runner.os }}-${{ github.job }}-${{ strategy.job-index }}
@@ -57,24 +95,30 @@ jobs:
5795
- ubuntu-latest
5896
- macos-latest
5997
go-version:
98+
- "1.18"
99+
- "1.20"
60100
- "1.21"
61-
- "1.22"
62-
- "^1"
101+
- "oldstable"
102+
- "stable"
63103
runs-on: ${{ matrix.os }}
64104
steps:
65105
- uses: actions/checkout@v4
66106
- uses: actions/setup-go@v5
67107
with:
68108
go-version: ${{ matrix.go-version }}
69109
- name: mkdir gocoverdir
110+
# We can only use -test.gocoverdir for Go >= 1.20.
111+
if: ${{ matrix.go-version != '1.18' && matrix.go-version != '1.19' }}
70112
run: |
71113
GOCOVERDIR="$(mktemp --tmpdir -d gocoverdir.XXXXXXXX)"
72114
echo "GOCOVERDIR=$GOCOVERDIR" >>"$GITHUB_ENV"
73115
- name: go test
74-
run: go test -v -cover -timeout=30m -test.gocoverdir="$GOCOVERDIR" ./...
116+
run: go test -v -timeout=30m ${GOCOVERDIR:+-cover -test.gocoverdir="$GOCOVERDIR"} ./...
75117
- name: sudo go test
76-
run: sudo go test -v -cover -timeout=30m -test.gocoverdir="$GOCOVERDIR" ./...
118+
run: sudo go test -v -timeout=30m ${GOCOVERDIR:+-cover -test.gocoverdir="$GOCOVERDIR"} ./...
77119
- name: upload coverage
120+
# We can only use -test.gocoverdir for Go >= 1.20.
121+
if: ${{ matrix.go-version != '1.18' && matrix.go-version != '1.19' }}
78122
uses: actions/upload-artifact@v4
79123
with:
80124
name: coverage-${{ runner.os }}-${{ github.job }}-${{ strategy.job-index }}
@@ -89,7 +133,7 @@ jobs:
89133
- uses: actions/checkout@v4
90134
- uses: actions/setup-go@v5
91135
with:
92-
go-version: "^1"
136+
go-version: "stable"
93137
- name: download all coverage
94138
uses: actions/download-artifact@v4
95139
with:

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,27 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
66

77
## [Unreleased] ##
88

9+
### Compatibility ###
10+
- The minimum Go version requirement for `filepath-securejoin` is now Go 1.18
11+
(we use generics internally).
12+
13+
For reference, `filepath-securejoin@v0.3.0` somewhat-arbitrarily bumped the
14+
Go version requirement to 1.21.
15+
16+
While we did make some use of Go 1.21 stdlib features (and in principle Go
17+
versions <= 1.21 are no longer even supported by upstream anymore), some
18+
downstreams have complained that the version bump has meant that they have to
19+
do workarounds when backporting fixes that use the new `filepath-securejoin`
20+
API onto old branches. This is not an ideal situation, but since using this
21+
library is probably better for most downstreams than a hand-rolled
22+
workaround, we now have compatibility shims that allow us to build on older
23+
Go versions.
24+
- Lower minimum version requirement for `golang.org/x/sys` to `v0.18.0` (we
25+
need the wrappers for `fsconfig(2)`), which should also make backporting
26+
patches to older branches easier.
27+
928
## [0.3.5] - 2024-12-06 ##
29+
1030
### Fixed ###
1131
- `MkdirAll` will now no longer return an `EEXIST` error if two racing
1232
processes are creating the same directory. We will still verify that the path

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
module github.com/cyphar/filepath-securejoin
22

3-
go 1.21
3+
go 1.18
44

55
require (
66
github.com/stretchr/testify v1.9.0
7-
golang.org/x/sys v0.28.0
7+
golang.org/x/sys v0.18.0
88
)
99

1010
require (

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
44
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
55
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
66
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
7-
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
8-
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
7+
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
8+
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
99
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
1010
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
1111
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

gocompat_errors_go120.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//go:build linux && go1.20
2+
3+
// Copyright (C) 2024 SUSE LLC. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package securejoin
8+
9+
import (
10+
"fmt"
11+
)
12+
13+
// wrapBaseError is a helper that is equivalent to fmt.Errorf("%w: %w"), except
14+
// that on pre-1.20 Go versions only errors.Is() works properly (errors.Unwrap)
15+
// is only guaranteed to give you baseErr.
16+
func wrapBaseError(baseErr, extraErr error) error {
17+
return fmt.Errorf("%w: %w", extraErr, baseErr)
18+
}

gocompat_errors_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//go:build linux
2+
3+
// Copyright (C) 2024 SUSE LLC. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package securejoin
8+
9+
import (
10+
"errors"
11+
"testing"
12+
13+
"github.com/stretchr/testify/assert"
14+
"github.com/stretchr/testify/require"
15+
)
16+
17+
func TestGoCompatErrorWrap(t *testing.T) {
18+
baseErr := errors.New("base error")
19+
extraErr := errors.New("extra error")
20+
21+
err := wrapBaseError(baseErr, extraErr)
22+
23+
require.Error(t, err)
24+
assert.ErrorIs(t, err, baseErr, "wrapped error should contain base error")
25+
assert.ErrorIs(t, err, extraErr, "wrapped error should contain extra error")
26+
}

gocompat_errors_unsupported.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//go:build linux && !go1.20
2+
3+
// Copyright (C) 2024 SUSE LLC. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package securejoin
8+
9+
import (
10+
"fmt"
11+
)
12+
13+
type wrappedError struct {
14+
inner error
15+
isError error
16+
}
17+
18+
func (err wrappedError) Is(target error) bool {
19+
return err.isError == target
20+
}
21+
22+
func (err wrappedError) Unwrap() error {
23+
return err.inner
24+
}
25+
26+
func (err wrappedError) Error() string {
27+
return fmt.Sprintf("%v: %v", err.isError, err.inner)
28+
}
29+
30+
// wrapBaseError is a helper that is equivalent to fmt.Errorf("%w: %w"), except
31+
// that on pre-1.20 Go versions only errors.Is() works properly (errors.Unwrap)
32+
// is only guaranteed to give you baseErr.
33+
func wrapBaseError(baseErr, extraErr error) error {
34+
return wrappedError{
35+
inner: baseErr,
36+
isError: extraErr,
37+
}
38+
}

gocompat_generics_go121.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//go:build linux && go1.21
2+
3+
// Copyright (C) 2024 SUSE LLC. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package securejoin
8+
9+
import (
10+
"slices"
11+
"sync"
12+
)
13+
14+
func slices_DeleteFunc[S ~[]E, E any](slice S, delFn func(E) bool) S {
15+
return slices.DeleteFunc(slice, delFn)
16+
}
17+
18+
func slices_Contains[S ~[]E, E comparable](slice S, val E) bool {
19+
return slices.Contains(slice, val)
20+
}
21+
22+
func slices_Clone[S ~[]E, E any](slice S) S {
23+
return slices.Clone(slice)
24+
}
25+
26+
func sync_OnceValue[T any](f func() T) func() T {
27+
return sync.OnceValue(f)
28+
}
29+
30+
func sync_OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) {
31+
return sync.OnceValues(f)
32+
}

0 commit comments

Comments
 (0)