diff --git a/internal/config/read.go b/internal/config/read.go index b251a1b..89f8e2d 100644 --- a/internal/config/read.go +++ b/internal/config/read.go @@ -2,6 +2,7 @@ package config import ( "errors" + "fmt" "os" ) @@ -11,6 +12,7 @@ const MirrorModePrivateAndPublic = "PRIVATE_AND_PUBLIC" type Config struct { MirrorMode string Gitea GiteaConfig + Github GithubConfig } type GiteaConfig struct { @@ -18,6 +20,11 @@ type GiteaConfig struct { GiteaToken string } +type GithubConfig struct { + Username string + Token *string +} + func Read() (*Config, error) { mirrorMode, err := readMirrorMode() if err != nil { @@ -29,21 +36,27 @@ func Read() (*Config, error) { return nil, err } + github, err := readGithubConfig() + if err != nil { + return nil, err + } + return &Config{ MirrorMode: mirrorMode, Gitea: gitea, + Github: github, }, nil } func readGiteaConfig() (GiteaConfig, error) { url, a := os.LookupEnv("GITEA_URL") if !a { - return GiteaConfig{}, errors.New("") + return GiteaConfig{}, errors.New("missing mandatory parameter GITEA_URL, please specify your target gitea instance") } token, a := os.LookupEnv("GITEA_TOKEN") if !a { - return GiteaConfig{}, errors.New("") + return GiteaConfig{}, errors.New("missing mandatory parameter GITEA_TOKEN, please specify your gitea application token") } return GiteaConfig{ @@ -53,6 +66,24 @@ func readGiteaConfig() (GiteaConfig, error) { } +func readGithubConfig() (GithubConfig, error) { + username, present := os.LookupEnv("GITHUB_USERNAME") + + if !present { + return GithubConfig{}, errors.New("") + } + + var token *string = nil + if val, hasToken := os.LookupEnv("GITHUB_TOKEN"); hasToken { + token = &val + } + + return GithubConfig{ + Username: username, + Token: token, + }, nil +} + func readMirrorMode() (string, error) { input, present := os.LookupEnv("MIRROR_MODE") @@ -64,7 +95,7 @@ func readMirrorMode() (string, error) { case MirrorModePublic, MirrorModePrivateAndPublic: return input, nil default: - return "", errors.New("") + return "", fmt.Errorf("unknown mirror mode %s, please specify a valid mirror mode: PUBLIC, PRIVATE_AND_PUBLIC", input) } } diff --git a/internal/config/read_test.go b/internal/config/read_test.go index 09c7d06..6f2adfa 100644 --- a/internal/config/read_test.go +++ b/internal/config/read_test.go @@ -1,8 +1,10 @@ package config_test import ( + "fmt" "github.com/jaedle/mirror-to-gitea/internal/config" . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/gomega" "os" ) @@ -10,11 +12,16 @@ import ( const mirrorMode = "MIRROR_MODE" const giteaUrl = "GITEA_URL" const giteaToken = "GITEA_TOKEN" +const githubUsername = "GITHUB_USERNAME" +const githubToken = "GITHUB_TOKEN" const publicMirrorMode = "PUBLIC" const privateAndPublicMirrorMode = "PRIVATE_AND_PUBLIC" const unknownMirrorMode = "UNKNOWN" +const aGithubUsername = "a-github-user" +const aGithubToken = "a-github-token" + var _ = Describe("Read", func() { BeforeEach(func() { os.Clearenv() @@ -28,6 +35,43 @@ var _ = Describe("Read", func() { Expect(c).ToNot(BeNil()) }) + Context("github", func() { + It("parses configuration", func() { + aValidEnv() + setEnv(githubUsername, aGithubUsername) + unsetEnv(githubToken) + + c, err := config.Read() + + Expect(err).NotTo(HaveOccurred()) + Expect(c.Github.Username).To(Equal(aGithubUsername)) + Expect(c.Github.Token).To(BeNil()) + }) + + It("parses configuration with token", func() { + aValidEnv() + setEnv(githubUsername, aGithubUsername) + setEnv(githubToken, aGithubToken) + + c, err := config.Read() + + Expect(err).NotTo(HaveOccurred()) + Expect(c.Github.Username).To(Equal(aGithubUsername)) + Expect(*c.Github.Token).To(Equal(aGithubToken)) + }) + + It("fails on missing username", func() { + aValidEnv() + unsetEnv(githubUsername) + + c, err := config.Read() + + Expect(err).To(HaveOccurred()) + Expect(c).To(BeNil()) + }) + + }) + Context("Gitea", func() { It("parses configuration", func() { aValidEnv() @@ -49,6 +93,7 @@ var _ = Describe("Read", func() { Expect(err).To(HaveOccurred()) Expect(c).To(BeNil()) + Expect(err.Error()).To(Equal("missing mandatory parameter GITEA_URL, please specify your target gitea instance")) }) It("fails on missing gitea token", func() { @@ -59,6 +104,8 @@ var _ = Describe("Read", func() { Expect(err).To(HaveOccurred()) Expect(c).To(BeNil()) + Expect(err.Error()).To(Equal("missing mandatory parameter GITEA_TOKEN, please specify your gitea application token")) + }) }) @@ -73,25 +120,18 @@ var _ = Describe("Read", func() { Expect(c.MirrorMode).To(Equal(config.MirrorModePublic)) }) - It("allows public mirror mode PUBLIC", func() { - aValidEnv() - setEnv(mirrorMode, publicMirrorMode) - - c, err := config.Read() - - Expect(err).NotTo(HaveOccurred()) - Expect(c.MirrorMode).To(Equal(config.MirrorModePublic)) - }) - - It("allows mirror mode PRIVATE_AND_PUBLIC", func() { + DescribeTable("parses mirror mode: ", func(in string, exp string) { aValidEnv() - setEnv(mirrorMode, privateAndPublicMirrorMode) + setEnv(mirrorMode, in) c, err := config.Read() Expect(err).NotTo(HaveOccurred()) - Expect(c.MirrorMode).To(Equal(config.MirrorModePrivateAndPublic)) - }) + Expect(c.MirrorMode).To(Equal(exp)) + }, + Entry("public mirror mode", publicMirrorMode, config.MirrorModePublic), + Entry("private mirror mode", privateAndPublicMirrorMode, config.MirrorModePrivateAndPublic), + ) It("fails on unknown mirror mode", func() { aValidEnv() @@ -101,6 +141,9 @@ var _ = Describe("Read", func() { Expect(err).To(HaveOccurred()) Expect(c).To(BeNil()) + + expected := "unknown mirror mode %s, please specify a valid mirror mode: PUBLIC, PRIVATE_AND_PUBLIC" + Expect(err.Error()).To(Equal(fmt.Sprintf(expected, unknownMirrorMode))) }) }) @@ -120,4 +163,5 @@ func aValidEnv() { setEnv(mirrorMode, "PUBLIC") setEnv(giteaUrl, "https://gitea.url") setEnv(giteaToken, "valid") + setEnv(githubUsername, "a-github-username") } diff --git a/vendor/github.com/onsi/ginkgo/extensions/table/table.go b/vendor/github.com/onsi/ginkgo/extensions/table/table.go new file mode 100644 index 0000000..4b00278 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/extensions/table/table.go @@ -0,0 +1,110 @@ +/* + +Table provides a simple DSL for Ginkgo-native Table-Driven Tests + +The godoc documentation describes Table's API. More comprehensive documentation (with examples!) is available at http://onsi.github.io/ginkgo#table-driven-tests + +*/ + +package table + +import ( + "fmt" + "reflect" + + "github.com/onsi/ginkgo/internal/codelocation" + "github.com/onsi/ginkgo/internal/global" + "github.com/onsi/ginkgo/types" +) + +/* +DescribeTable describes a table-driven test. + +For example: + + DescribeTable("a simple table", + func(x int, y int, expected bool) { + Ω(x > y).Should(Equal(expected)) + }, + Entry("x > y", 1, 0, true), + Entry("x == y", 0, 0, false), + Entry("x < y", 0, 1, false), + ) + +The first argument to `DescribeTable` is a string description. +The second argument is a function that will be run for each table entry. Your assertions go here - the function is equivalent to a Ginkgo It. +The subsequent arguments must be of type `TableEntry`. We recommend using the `Entry` convenience constructors. + +The `Entry` constructor takes a string description followed by an arbitrary set of parameters. These parameters are passed into your function. + +Under the hood, `DescribeTable` simply generates a new Ginkgo `Describe`. Each `Entry` is turned into an `It` within the `Describe`. + +It's important to understand that the `Describe`s and `It`s are generated at evaluation time (i.e. when Ginkgo constructs the tree of tests and before the tests run). + +Individual Entries can be focused (with FEntry) or marked pending (with PEntry or XEntry). In addition, the entire table can be focused or marked pending with FDescribeTable and PDescribeTable/XDescribeTable. + +A description function can be passed to Entry in place of the description. The function is then fed with the entry parameters to generate the description of the It corresponding to that particular Entry. + +For example: + + describe := func(desc string) func(int, int, bool) string { + return func(x, y int, expected bool) string { + return fmt.Sprintf("%s x=%d y=%d expected:%t", desc, x, y, expected) + } + } + + DescribeTable("a simple table", + func(x int, y int, expected bool) { + Ω(x > y).Should(Equal(expected)) + }, + Entry(describe("x > y"), 1, 0, true), + Entry(describe("x == y"), 0, 0, false), + Entry(describe("x < y"), 0, 1, false), + ) +*/ +func DescribeTable(description string, itBody interface{}, entries ...TableEntry) bool { + describeTable(description, itBody, entries, types.FlagTypeNone) + return true +} + +/* +You can focus a table with `FDescribeTable`. This is equivalent to `FDescribe`. +*/ +func FDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool { + describeTable(description, itBody, entries, types.FlagTypeFocused) + return true +} + +/* +You can mark a table as pending with `PDescribeTable`. This is equivalent to `PDescribe`. +*/ +func PDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool { + describeTable(description, itBody, entries, types.FlagTypePending) + return true +} + +/* +You can mark a table as pending with `XDescribeTable`. This is equivalent to `XDescribe`. +*/ +func XDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool { + describeTable(description, itBody, entries, types.FlagTypePending) + return true +} + +func describeTable(description string, itBody interface{}, entries []TableEntry, flag types.FlagType) { + itBodyValue := reflect.ValueOf(itBody) + if itBodyValue.Kind() != reflect.Func { + panic(fmt.Sprintf("DescribeTable expects a function, got %#v", itBody)) + } + + global.Suite.PushContainerNode( + description, + func() { + for _, entry := range entries { + entry.generateIt(itBodyValue) + } + }, + flag, + codelocation.New(2), + ) +} diff --git a/vendor/github.com/onsi/ginkgo/extensions/table/table_entry.go b/vendor/github.com/onsi/ginkgo/extensions/table/table_entry.go new file mode 100644 index 0000000..4d9c237 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/extensions/table/table_entry.go @@ -0,0 +1,129 @@ +package table + +import ( + "fmt" + "reflect" + + "github.com/onsi/ginkgo/internal/codelocation" + "github.com/onsi/ginkgo/internal/global" + "github.com/onsi/ginkgo/types" +) + +/* +TableEntry represents an entry in a table test. You generally use the `Entry` constructor. +*/ +type TableEntry struct { + Description interface{} + Parameters []interface{} + Pending bool + Focused bool + codeLocation types.CodeLocation +} + +func (t TableEntry) generateIt(itBody reflect.Value) { + var description string + descriptionValue := reflect.ValueOf(t.Description) + switch descriptionValue.Kind() { + case reflect.String: + description = descriptionValue.String() + case reflect.Func: + values := castParameters(descriptionValue, t.Parameters) + res := descriptionValue.Call(values) + if len(res) != 1 { + panic(fmt.Sprintf("The describe function should return only a value, returned %d", len(res))) + } + if res[0].Kind() != reflect.String { + panic(fmt.Sprintf("The describe function should return a string, returned %#v", res[0])) + } + description = res[0].String() + default: + panic(fmt.Sprintf("Description can either be a string or a function, got %#v", descriptionValue)) + } + + if t.Pending { + global.Suite.PushItNode(description, func() {}, types.FlagTypePending, t.codeLocation, 0) + return + } + + values := castParameters(itBody, t.Parameters) + body := func() { + itBody.Call(values) + } + + if t.Focused { + global.Suite.PushItNode(description, body, types.FlagTypeFocused, t.codeLocation, global.DefaultTimeout) + } else { + global.Suite.PushItNode(description, body, types.FlagTypeNone, t.codeLocation, global.DefaultTimeout) + } +} + +func castParameters(function reflect.Value, parameters []interface{}) []reflect.Value { + res := make([]reflect.Value, len(parameters)) + funcType := function.Type() + for i, param := range parameters { + if param == nil { + inType := funcType.In(i) + res[i] = reflect.Zero(inType) + } else { + res[i] = reflect.ValueOf(param) + } + } + return res +} + +/* +Entry constructs a TableEntry. + +The first argument is a required description (this becomes the content of the generated Ginkgo `It`). +Subsequent parameters are saved off and sent to the callback passed in to `DescribeTable`. + +Each Entry ends up generating an individual Ginkgo It. +*/ +func Entry(description interface{}, parameters ...interface{}) TableEntry { + return TableEntry{ + Description: description, + Parameters: parameters, + Pending: false, + Focused: false, + codeLocation: codelocation.New(1), + } +} + +/* +You can focus a particular entry with FEntry. This is equivalent to FIt. +*/ +func FEntry(description interface{}, parameters ...interface{}) TableEntry { + return TableEntry{ + Description: description, + Parameters: parameters, + Pending: false, + Focused: true, + codeLocation: codelocation.New(1), + } +} + +/* +You can mark a particular entry as pending with PEntry. This is equivalent to PIt. +*/ +func PEntry(description interface{}, parameters ...interface{}) TableEntry { + return TableEntry{ + Description: description, + Parameters: parameters, + Pending: true, + Focused: false, + codeLocation: codelocation.New(1), + } +} + +/* +You can mark a particular entry as pending with XEntry. This is equivalent to XIt. +*/ +func XEntry(description interface{}, parameters ...interface{}) TableEntry { + return TableEntry{ + Description: description, + Parameters: parameters, + Pending: true, + Focused: false, + codeLocation: codelocation.New(1), + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index c75360e..766f345 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -10,6 +10,7 @@ github.com/nxadm/tail/winfile ## explicit github.com/onsi/ginkgo github.com/onsi/ginkgo/config +github.com/onsi/ginkgo/extensions/table github.com/onsi/ginkgo/formatter github.com/onsi/ginkgo/internal/codelocation github.com/onsi/ginkgo/internal/containernode