mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-03-17 20:47:50 +02:00
refactor: just started fixing tests
This commit is contained in:
parent
4e6982a524
commit
9416dcc751
@ -11,22 +11,24 @@ import (
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/goreleaser/goreleaser/build"
|
||||
api "github.com/goreleaser/goreleaser/build"
|
||||
"github.com/goreleaser/goreleaser/config"
|
||||
"github.com/goreleaser/goreleaser/context"
|
||||
"github.com/goreleaser/goreleaser/internal/artifact"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Default builder instance
|
||||
var Default = &Builder{}
|
||||
|
||||
func init() {
|
||||
build.Register("go", Default)
|
||||
api.Register("go", Default)
|
||||
}
|
||||
|
||||
type Builder struct {
|
||||
}
|
||||
// Builder is golang builder
|
||||
type Builder struct{}
|
||||
|
||||
// Default set the defaults for a golang build
|
||||
func (*Builder) Default(build config.Build) config.Build {
|
||||
if build.Main == "" {
|
||||
build.Main = "."
|
||||
@ -43,28 +45,29 @@ func (*Builder) Default(build config.Build) config.Build {
|
||||
if build.Ldflags == "" {
|
||||
build.Ldflags = "-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}"
|
||||
}
|
||||
if build.Lang == "go" && len(build.Targets) == 0 {
|
||||
if len(build.Targets) == 0 {
|
||||
build.Targets = matrix(build)
|
||||
}
|
||||
return build
|
||||
}
|
||||
|
||||
func (*Builder) Build(ctx *context.Context, cfg config.Build, options build.Options) error {
|
||||
if err := checkMain(ctx, cfg); err != nil {
|
||||
// Build builds a golang build
|
||||
func (*Builder) Build(ctx *context.Context, build config.Build, options api.Options) error {
|
||||
if err := checkMain(ctx, build); err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := []string{"go", "build"}
|
||||
if cfg.Flags != "" {
|
||||
cmd = append(cmd, strings.Fields(cfg.Flags)...)
|
||||
if build.Flags != "" {
|
||||
cmd = append(cmd, strings.Fields(build.Flags)...)
|
||||
}
|
||||
flags, err := ldflags(ctx, cfg)
|
||||
flags, err := ldflags(ctx, build)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd = append(cmd, "-ldflags="+flags, "-o", options.Path, cfg.Main)
|
||||
cmd = append(cmd, "-ldflags="+flags, "-o", options.Path, build.Main)
|
||||
var target = newBuildTarget(options.Target)
|
||||
var env = append(cfg.Env, target.Env()...)
|
||||
if err := build.Run(ctx, cmd, env); err != nil {
|
||||
var env = append(build.Env, target.Env()...)
|
||||
if err := api.Run(ctx, cmd, env); err != nil {
|
||||
return errors.Wrapf(err, "failed to build for %s", options.Target)
|
||||
}
|
||||
ctx.Artifacts.Add(artifact.Artifact{
|
||||
@ -75,7 +78,7 @@ func (*Builder) Build(ctx *context.Context, cfg config.Build, options build.Opti
|
||||
Goarch: target.arch,
|
||||
Goarm: target.arm,
|
||||
Extra: map[string]string{
|
||||
"Binary": cfg.Binary,
|
||||
"Binary": build.Binary,
|
||||
"Ext": options.Ext,
|
||||
},
|
||||
})
|
||||
|
@ -1,13 +1,214 @@
|
||||
package golang
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
api "github.com/goreleaser/goreleaser/build"
|
||||
"github.com/goreleaser/goreleaser/config"
|
||||
"github.com/goreleaser/goreleaser/context"
|
||||
"github.com/goreleaser/goreleaser/internal/testlib"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var runtimeTarget = runtime.GOOS + "_" + runtime.GOARCH
|
||||
|
||||
func TestBuild(t *testing.T) {
|
||||
folder, back := testlib.Mktmp(t)
|
||||
defer back()
|
||||
writeGoodMain(t, folder)
|
||||
var config = config.Project{
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Binary: "foo",
|
||||
Goos: []string{
|
||||
"linux",
|
||||
"windows",
|
||||
"darwin",
|
||||
},
|
||||
Goarch: []string{
|
||||
"amd64",
|
||||
"arm",
|
||||
},
|
||||
Goarm: []string{
|
||||
"6",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
var ctx = context.New(config)
|
||||
var build = Default.Default(ctx.Config.Builds[0])
|
||||
assert.ElementsMatch(t, build.Targets, []string{
|
||||
"linux_amd64",
|
||||
"darwin_amd64",
|
||||
"windows_amd64",
|
||||
"linux_arm_6",
|
||||
})
|
||||
for _, target := range build.Targets {
|
||||
var err = Default.Build(ctx, build, api.Options{
|
||||
Target: target,
|
||||
Name: build.Binary,
|
||||
Path: filepath.Join(folder, "dist", target, build.Binary),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
assert.Len(t, ctx.Artifacts.List(), len(build.Targets))
|
||||
}
|
||||
|
||||
func TestBuildFailed(t *testing.T) {
|
||||
folder, back := testlib.Mktmp(t)
|
||||
defer back()
|
||||
writeGoodMain(t, folder)
|
||||
var config = config.Project{
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Flags: "-flag-that-dont-exists-to-force-failure",
|
||||
Targets: []string{
|
||||
runtimeTarget,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
var ctx = context.New(config)
|
||||
var err = Default.Build(ctx, ctx.Config.Builds[0], api.Options{
|
||||
Target: "darwin_amd64",
|
||||
})
|
||||
assertContainsError(t, err, `flag provided but not defined: -flag-that-dont-exists-to-force-failure`)
|
||||
assert.Empty(t, ctx.Artifacts.List())
|
||||
}
|
||||
|
||||
func TestRunInvalidLdflags(t *testing.T) {
|
||||
folder, back := testlib.Mktmp(t)
|
||||
defer back()
|
||||
writeGoodMain(t, folder)
|
||||
var config = config.Project{
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Binary: "nametest",
|
||||
Flags: "-v",
|
||||
Ldflags: "-s -w -X main.version={{.Version}",
|
||||
Targets: []string{
|
||||
runtimeTarget,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
var ctx = context.New(config)
|
||||
var err = Default.Build(ctx, ctx.Config.Builds[0], api.Options{
|
||||
Target: runtimeTarget,
|
||||
})
|
||||
assert.EqualError(t, err, `template: ldflags:1: unexpected "}" in operand`)
|
||||
}
|
||||
|
||||
func TestRunPipeWithoutMainFunc(t *testing.T) {
|
||||
folder, back := testlib.Mktmp(t)
|
||||
defer back()
|
||||
writeMainWithoutMainFunc(t, folder)
|
||||
var config = config.Project{
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Binary: "no-main",
|
||||
Hooks: config.Hooks{},
|
||||
Targets: []string{
|
||||
runtimeTarget,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
var ctx = context.New(config)
|
||||
t.Run("empty", func(t *testing.T) {
|
||||
ctx.Config.Builds[0].Main = ""
|
||||
assert.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
|
||||
Target: runtimeTarget,
|
||||
}), `build for no-main does not contain a main function`)
|
||||
})
|
||||
t.Run("not main.go", func(t *testing.T) {
|
||||
ctx.Config.Builds[0].Main = "foo.go"
|
||||
assert.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
|
||||
Target: runtimeTarget,
|
||||
}), `could not open foo.go: stat foo.go: no such file or directory`)
|
||||
})
|
||||
t.Run("glob", func(t *testing.T) {
|
||||
ctx.Config.Builds[0].Main = "."
|
||||
assert.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
|
||||
Target: runtimeTarget,
|
||||
}), `build for no-main does not contain a main function`)
|
||||
})
|
||||
t.Run("fixed main.go", func(t *testing.T) {
|
||||
ctx.Config.Builds[0].Main = "main.go"
|
||||
assert.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
|
||||
Target: runtimeTarget,
|
||||
}), `build for no-main does not contain a main function`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRunPipeWithMainFuncNotInMainGoFile(t *testing.T) {
|
||||
folder, back := testlib.Mktmp(t)
|
||||
defer back()
|
||||
assert.NoError(t, ioutil.WriteFile(
|
||||
filepath.Join(folder, "foo.go"),
|
||||
[]byte("package main\nfunc main() {println(0)}"),
|
||||
0644,
|
||||
))
|
||||
var config = config.Project{
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Binary: "foo",
|
||||
Hooks: config.Hooks{},
|
||||
Targets: []string{
|
||||
runtimeTarget,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
var ctx = context.New(config)
|
||||
t.Run("empty", func(t *testing.T) {
|
||||
ctx.Config.Builds[0].Main = ""
|
||||
assert.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
|
||||
Target: runtimeTarget,
|
||||
}))
|
||||
})
|
||||
t.Run("foo.go", func(t *testing.T) {
|
||||
ctx.Config.Builds[0].Main = "foo.go"
|
||||
assert.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
|
||||
Target: runtimeTarget,
|
||||
}))
|
||||
})
|
||||
t.Run("glob", func(t *testing.T) {
|
||||
ctx.Config.Builds[0].Main = "."
|
||||
assert.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
|
||||
Target: runtimeTarget,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
// FIXME: probably should be refactored
|
||||
func TestRunPipeWithInvalidOS(t *testing.T) {
|
||||
folder, back := testlib.Mktmp(t)
|
||||
defer back()
|
||||
writeGoodMain(t, folder)
|
||||
var config = config.Project{
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Lang: "go",
|
||||
Flags: "-v",
|
||||
Goos: []string{
|
||||
"windows",
|
||||
},
|
||||
Goarch: []string{
|
||||
"arm",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.NoError(t, Default.Build(context.New(config), config.Builds[0], api.Options{
|
||||
Target: "windows_arm",
|
||||
}))
|
||||
}
|
||||
|
||||
func TestLdFlagsFullTemplate(t *testing.T) {
|
||||
var config = config.Project{
|
||||
Builds: []config.Build{
|
||||
@ -55,3 +256,28 @@ func TestInvalidTemplate(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
func writeMainWithoutMainFunc(t *testing.T, folder string) {
|
||||
assert.NoError(t, ioutil.WriteFile(
|
||||
filepath.Join(folder, "main.go"),
|
||||
[]byte("package main\nconst a = 2\nfunc notMain() {println(0)}"),
|
||||
0644,
|
||||
))
|
||||
}
|
||||
|
||||
func writeGoodMain(t *testing.T, folder string) {
|
||||
assert.NoError(t, ioutil.WriteFile(
|
||||
filepath.Join(folder, "main.go"),
|
||||
[]byte("package main\nvar a = 1\nfunc main() {println(0)}"),
|
||||
0644,
|
||||
))
|
||||
}
|
||||
|
||||
func assertContainsError(t *testing.T, err error, s string) {
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), s)
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Package build needs to be documented
|
||||
// Package build provides a pipe that can build binaries for several
|
||||
// languages.
|
||||
package build
|
||||
|
||||
import (
|
||||
|
@ -1,12 +1,12 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
api "github.com/goreleaser/goreleaser/build"
|
||||
"github.com/goreleaser/goreleaser/config"
|
||||
"github.com/goreleaser/goreleaser/context"
|
||||
"github.com/goreleaser/goreleaser/internal/testlib"
|
||||
@ -15,14 +15,31 @@ import (
|
||||
|
||||
var emptyEnv []string
|
||||
|
||||
type fakeBuilder struct{}
|
||||
|
||||
func (*fakeBuilder) Default(build config.Build) config.Build {
|
||||
return build
|
||||
}
|
||||
|
||||
func (*fakeBuilder) Build(ctx *context.Context, build config.Build, options api.Options) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
api.Register("fake", &fakeBuilder{})
|
||||
}
|
||||
|
||||
func TestPipeDescription(t *testing.T) {
|
||||
assert.NotEmpty(t, Pipe{}.String())
|
||||
}
|
||||
|
||||
func TestBuild(t *testing.T) {
|
||||
var builder = &fakeBuilder{}
|
||||
api.Register("fake", builder)
|
||||
var config = config.Project{
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Lang: "fake",
|
||||
Binary: "testing",
|
||||
Flags: "-n",
|
||||
Env: []string{"BLAH=1"},
|
||||
@ -30,19 +47,21 @@ func TestBuild(t *testing.T) {
|
||||
},
|
||||
}
|
||||
var ctx = context.New(config)
|
||||
assert.NoError(t, doBuild(ctx, ctx.Config.Builds[0], buildtarget.Runtime))
|
||||
assert.NoError(t, doBuild(ctx, ctx.Config.Builds[0], "darwin_amd64"))
|
||||
}
|
||||
|
||||
// FIXME: test is wrong
|
||||
func TestRunFullPipe(t *testing.T) {
|
||||
folder, back := testlib.Mktmp(t)
|
||||
defer back()
|
||||
writeGoodMain(t, folder)
|
||||
var binary = filepath.Join(folder, buildtarget.Runtime.String(), "testing")
|
||||
var binary = filepath.Join(folder, "darwin_amd64", "testing")
|
||||
var pre = filepath.Join(folder, "pre")
|
||||
var post = filepath.Join(folder, "post")
|
||||
var config = config.Project{
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Lang: "fake",
|
||||
Binary: "testing",
|
||||
Flags: "-v",
|
||||
Ldflags: "-X main.test=testing",
|
||||
@ -103,74 +122,11 @@ func TestRunPipeArmBuilds(t *testing.T) {
|
||||
assert.True(t, exists(binary), binary)
|
||||
}
|
||||
|
||||
func TestBuildFailed(t *testing.T) {
|
||||
folder, back := testlib.Mktmp(t)
|
||||
defer back()
|
||||
writeGoodMain(t, folder)
|
||||
var config = config.Project{
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Flags: "-flag-that-dont-exists-to-force-failure",
|
||||
Goos: []string{
|
||||
runtime.GOOS,
|
||||
},
|
||||
Goarch: []string{
|
||||
runtime.GOARCH,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
var ctx = context.New(config)
|
||||
assertContainsError(t, Pipe{}.Run(ctx), `flag provided but not defined: -flag-that-dont-exists-to-force-failure`)
|
||||
assert.Empty(t, ctx.Artifacts.List())
|
||||
}
|
||||
|
||||
func TestRunPipeWithInvalidOS(t *testing.T) {
|
||||
folder, back := testlib.Mktmp(t)
|
||||
defer back()
|
||||
writeGoodMain(t, folder)
|
||||
var config = config.Project{
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Flags: "-v",
|
||||
Goos: []string{
|
||||
"windows",
|
||||
},
|
||||
Goarch: []string{
|
||||
"arm",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.NoError(t, Pipe{}.Run(context.New(config)))
|
||||
}
|
||||
|
||||
func TestRunInvalidLdflags(t *testing.T) {
|
||||
folder, back := testlib.Mktmp(t)
|
||||
defer back()
|
||||
writeGoodMain(t, folder)
|
||||
var config = config.Project{
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Binary: "nametest",
|
||||
Flags: "-v",
|
||||
Ldflags: "-s -w -X main.version={{.Version}",
|
||||
Goos: []string{
|
||||
runtime.GOOS,
|
||||
},
|
||||
Goarch: []string{
|
||||
runtime.GOARCH,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.EqualError(t, Pipe{}.Run(context.New(config)), `template: ldflags:1: unexpected "}" in operand`)
|
||||
}
|
||||
|
||||
// FIXME: probably can use fake builder here
|
||||
func TestRunPipeFailingHooks(t *testing.T) {
|
||||
folder, back := testlib.Mktmp(t)
|
||||
defer back()
|
||||
writeGoodMain(t, folder)
|
||||
// writeGoodMain(t, folder)
|
||||
var config = config.Project{
|
||||
Builds: []config.Build{
|
||||
{
|
||||
@ -199,80 +155,6 @@ func TestRunPipeFailingHooks(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestRunPipeWithouMainFunc(t *testing.T) {
|
||||
folder, back := testlib.Mktmp(t)
|
||||
defer back()
|
||||
writeMainWithoutMainFunc(t, folder)
|
||||
var config = config.Project{
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Binary: "no-main",
|
||||
Hooks: config.Hooks{},
|
||||
Goos: []string{
|
||||
runtime.GOOS,
|
||||
},
|
||||
Goarch: []string{
|
||||
runtime.GOARCH,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
var ctx = context.New(config)
|
||||
t.Run("empty", func(t *testing.T) {
|
||||
ctx.Config.Builds[0].Main = ""
|
||||
assert.EqualError(t, Pipe{}.Run(ctx), `build for no-main does not contain a main function`)
|
||||
})
|
||||
t.Run("not main.go", func(t *testing.T) {
|
||||
ctx.Config.Builds[0].Main = "foo.go"
|
||||
assert.EqualError(t, Pipe{}.Run(ctx), `could not open foo.go: stat foo.go: no such file or directory`)
|
||||
})
|
||||
t.Run("glob", func(t *testing.T) {
|
||||
ctx.Config.Builds[0].Main = "."
|
||||
assert.EqualError(t, Pipe{}.Run(ctx), `build for no-main does not contain a main function`)
|
||||
})
|
||||
t.Run("fixed main.go", func(t *testing.T) {
|
||||
ctx.Config.Builds[0].Main = "main.go"
|
||||
assert.EqualError(t, Pipe{}.Run(ctx), `build for no-main does not contain a main function`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRunPipeWithMainFuncNotInMainGoFile(t *testing.T) {
|
||||
folder, back := testlib.Mktmp(t)
|
||||
defer back()
|
||||
assert.NoError(t, ioutil.WriteFile(
|
||||
filepath.Join(folder, "foo.go"),
|
||||
[]byte("package main\nfunc main() {println(0)}"),
|
||||
0644,
|
||||
))
|
||||
var config = config.Project{
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Binary: "foo",
|
||||
Hooks: config.Hooks{},
|
||||
Goos: []string{
|
||||
runtime.GOOS,
|
||||
},
|
||||
Goarch: []string{
|
||||
runtime.GOARCH,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
var ctx = context.New(config)
|
||||
t.Run("empty", func(t *testing.T) {
|
||||
ctx.Config.Builds[0].Main = ""
|
||||
assert.NoError(t, Pipe{}.Run(ctx))
|
||||
})
|
||||
t.Run("foo.go", func(t *testing.T) {
|
||||
ctx.Config.Builds[0].Main = "foo.go"
|
||||
assert.NoError(t, Pipe{}.Run(ctx))
|
||||
})
|
||||
t.Run("glob", func(t *testing.T) {
|
||||
ctx.Config.Builds[0].Main = "."
|
||||
assert.NoError(t, Pipe{}.Run(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
func TestDefaultNoBuilds(t *testing.T) {
|
||||
var ctx = &context.Context{
|
||||
Config: config.Project{},
|
||||
@ -362,32 +244,6 @@ func TestDefaultFillSingleBuild(t *testing.T) {
|
||||
assert.Equal(t, ctx.Config.Builds[0].Binary, "foo")
|
||||
}
|
||||
|
||||
func exists(file string) bool {
|
||||
_, err := os.Stat(file)
|
||||
return !os.IsNotExist(err)
|
||||
}
|
||||
|
||||
func writeMainWithoutMainFunc(t *testing.T, folder string) {
|
||||
assert.NoError(t, ioutil.WriteFile(
|
||||
filepath.Join(folder, "main.go"),
|
||||
[]byte("package main\nconst a = 2\nfunc notMain() {println(0)}"),
|
||||
0644,
|
||||
))
|
||||
}
|
||||
|
||||
func writeGoodMain(t *testing.T, folder string) {
|
||||
assert.NoError(t, ioutil.WriteFile(
|
||||
filepath.Join(folder, "main.go"),
|
||||
[]byte("package main\nvar a = 1\nfunc main() {println(0)}"),
|
||||
0644,
|
||||
))
|
||||
}
|
||||
|
||||
func assertContainsError(t *testing.T, err error, s string) {
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), s)
|
||||
}
|
||||
|
||||
func TestExtWindows(t *testing.T) {
|
||||
assert.Equal(t, ".exe", extFor("windows_amd64"))
|
||||
assert.Equal(t, ".exe", extFor("windows_386"))
|
||||
@ -398,3 +254,17 @@ func TestExtOthers(t *testing.T) {
|
||||
assert.Empty(t, "", extFor("linuxwin_386"))
|
||||
assert.Empty(t, "", extFor("winasdasd_sad"))
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
func exists(file string) bool {
|
||||
_, err := os.Stat(file)
|
||||
return !os.IsNotExist(err)
|
||||
}
|
||||
|
||||
func assertContainsError(t *testing.T, err error, s string) {
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), s)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user