diff --git a/bashbrew/go/src/bashbrew/cmd-build.go b/bashbrew/go/src/bashbrew/cmd-build.go index 9711f10be346b..038b6fbd88859 100644 --- a/bashbrew/go/src/bashbrew/cmd-build.go +++ b/bashbrew/go/src/bashbrew/cmd-build.go @@ -81,7 +81,7 @@ func cmdBuild(c *cli.Context) error { return cli.NewMultiError(fmt.Errorf(`failed fetching git repo for %q (tags %q)`, r.RepoName, entry.TagsString()), err) } - archive, err := gitArchive(commit, entry.Directory) + archive, err := gitArchive(commit, entry.ArchDirectory(arch)) if err != nil { return cli.NewMultiError(fmt.Errorf(`failed generating git archive for %q (tags %q)`, r.RepoName, entry.TagsString()), err) } diff --git a/bashbrew/go/src/bashbrew/cmd-put-shared.go b/bashbrew/go/src/bashbrew/cmd-put-shared.go new file mode 100644 index 0000000000000..d3dd79a96a740 --- /dev/null +++ b/bashbrew/go/src/bashbrew/cmd-put-shared.go @@ -0,0 +1,46 @@ +package main + +import ( + "fmt" + "os" + "path" + "strings" + + "github.com/codegangsta/cli" +) + +func cmdPutShared(c *cli.Context) error { + repos, err := repos(c.Bool("all"), c.Args()...) + if err != nil { + return cli.NewMultiError(fmt.Errorf(`failed gathering repo list`), err) + } + + namespace := c.String("namespace") + + if namespace == "" { + return fmt.Errorf(`"--namespace" is a required flag for "put-shared"`) + } + + fmt.Fprintf(os.Stderr, "warning: this subcommand is still a big WIP -- it doesn't do anything yet!\n") + + for _, repo := range repos { + r, err := fetch(repo) + if err != nil { + return cli.NewMultiError(fmt.Errorf(`failed fetching repo %q`, repo), err) + } + + // TODO handle all multi-architecture tags first (regardless of whether they have SharedTags) + + targetRepo := path.Join(namespace, r.RepoName) + for _, group := range r.Manifest.GetSharedTagGroups() { + // TODO build up a YAML file + entryTags := []string{} + for _, entry := range group.Entries { + entryTags = append(entryTags, entry.Tags[0]) + } + fmt.Printf("Putting %s (tags %s) <= %s\n", targetRepo, strings.Join(group.SharedTags, ", "), strings.Join(entryTags, ", ")) + } + } + + return nil +} diff --git a/bashbrew/go/src/bashbrew/config.go b/bashbrew/go/src/bashbrew/config.go index 061987bbad5f4..fb625303c1c80 100644 --- a/bashbrew/go/src/bashbrew/config.go +++ b/bashbrew/go/src/bashbrew/config.go @@ -18,6 +18,8 @@ type FlagsConfigEntry struct { Commands []string `delim:"," strip:"\n\r\t "` + // TODO arch namespace mappings (for intermediate pushing before put-shared, and for put-shared to pull from to join together in one big happy family) + Library string Cache string Debug string @@ -26,6 +28,7 @@ type FlagsConfigEntry struct { BuildOrder string Pull string + Arch string Constraints []string `delim:"," strip:"\n\r\t "` ExclusiveConstraints string ApplyConstraints string @@ -55,6 +58,9 @@ func (dst *FlagsConfigEntry) Apply(src FlagsConfigEntry) { if src.Pull != "" { dst.Pull = src.Pull } + if src.Arch != "" { + dst.Arch = src.Arch + } if len(src.Constraints) > 0 { dst.Constraints = src.Constraints[:] } @@ -73,6 +79,7 @@ func (config FlagsConfigEntry) Vars() map[string]map[string]interface{} { "cache": config.Cache, "debug": config.Debug, + "arch": config.Arch, "constraint": config.Constraints, "exclusive-constraints": config.ExclusiveConstraints, }, diff --git a/bashbrew/go/src/bashbrew/docker.go b/bashbrew/go/src/bashbrew/docker.go index c22d162b73fab..bfe721e793cdc 100644 --- a/bashbrew/go/src/bashbrew/docker.go +++ b/bashbrew/go/src/bashbrew/docker.go @@ -24,7 +24,7 @@ func (r Repo) DockerFrom(entry *manifest.Manifest2822Entry) (string, error) { return "", err } - dockerfileFile := path.Join(entry.Directory, "Dockerfile") + dockerfileFile := path.Join(entry.ArchDirectory(arch), "Dockerfile") cacheKey := strings.Join([]string{ commit, @@ -130,9 +130,9 @@ func (r Repo) dockerBuildUniqueBits(entry *manifest.Manifest2822Entry) ([]string dockerFromIdCache[from] = fromId } return []string{ - entry.GitRepo, - entry.GitCommit, - entry.Directory, + entry.ArchGitRepo(arch), + entry.ArchGitCommit(arch), + entry.ArchDirectory(arch), fromId, }, nil } diff --git a/bashbrew/go/src/bashbrew/git.go b/bashbrew/go/src/bashbrew/git.go index 2a152866a6bc3..ee9bde7bf362d 100644 --- a/bashbrew/go/src/bashbrew/git.go +++ b/bashbrew/go/src/bashbrew/git.go @@ -102,12 +102,12 @@ var gitRepoCache = map[string]string{} func (r Repo) fetchGitRepo(entry *manifest.Manifest2822Entry) (string, error) { cacheKey := strings.Join([]string{ - entry.GitRepo, - entry.GitFetch, - entry.GitCommit, + entry.ArchGitRepo(arch), + entry.ArchGitFetch(arch), + entry.ArchGitCommit(arch), }, "\n") if commit, ok := gitRepoCache[cacheKey]; ok { - entry.GitCommit = commit + entry.SetGitCommit(arch, commit) return commit, nil } @@ -116,27 +116,27 @@ func (r Repo) fetchGitRepo(entry *manifest.Manifest2822Entry) (string, error) { return "", err } - if manifest.GitCommitRegex.MatchString(entry.GitCommit) { - commit, err := getGitCommit(entry.GitCommit) + if manifest.GitCommitRegex.MatchString(entry.ArchGitCommit(arch)) { + commit, err := getGitCommit(entry.ArchGitCommit(arch)) if err == nil { gitRepoCache[cacheKey] = commit - entry.GitCommit = commit + entry.SetGitCommit(arch, commit) return commit, nil } } - fetchString := entry.GitFetch + ":" - if entry.GitCommit == "FETCH_HEAD" { + fetchString := entry.ArchGitFetch(arch) + ":" + if entry.ArchGitCommit(arch) == "FETCH_HEAD" { // fetch remote tag references to a local tag ref so that we can cache them and not re-fetch every time localRef := "refs/tags/" + gitNormalizeForTagUsage(cacheKey) commit, err := getGitCommit(localRef) if err == nil { gitRepoCache[cacheKey] = commit - entry.GitCommit = commit + entry.SetGitCommit(arch, commit) return commit, nil } fetchString += localRef - } else if entry.GitFetch == manifest.DefaultLineBasedFetch { + } else if entry.ArchGitFetch(arch) == manifest.DefaultLineBasedFetch { // backwards compat (see manifest/line-based.go in go-dockerlibrary) refBase := "refs/remotes" refBaseDir := filepath.Join(gitCache(), refBase) @@ -154,17 +154,17 @@ func (r Repo) fetchGitRepo(entry *manifest.Manifest2822Entry) (string, error) { // we create a temporary remote dir so that we can clean it up completely afterwards } - if strings.HasPrefix(entry.GitRepo, "git://github.com/") { - fmt.Fprintf(os.Stderr, "warning: insecure protocol git:// detected: %s\n", entry.GitRepo) - entry.GitRepo = strings.Replace(entry.GitRepo, "git://", "https://", 1) + if strings.HasPrefix(entry.ArchGitRepo(arch), "git://github.com/") { + fmt.Fprintf(os.Stderr, "warning: insecure protocol git:// detected: %s\n", entry.ArchGitRepo(arch)) + entry.SetGitRepo(arch, strings.Replace(entry.ArchGitRepo(arch), "git://", "https://", 1)) } - _, err = git("fetch", "--quiet", "--no-tags", entry.GitRepo, fetchString) + _, err = git("fetch", "--quiet", "--no-tags", entry.ArchGitRepo(arch), fetchString) if err != nil { return "", err } - commit, err := getGitCommit(entry.GitCommit) + commit, err := getGitCommit(entry.ArchGitCommit(arch)) if err != nil { return "", err } @@ -175,6 +175,6 @@ func (r Repo) fetchGitRepo(entry *manifest.Manifest2822Entry) (string, error) { } gitRepoCache[cacheKey] = commit - entry.GitCommit = commit + entry.SetGitCommit(arch, commit) return commit, nil } diff --git a/bashbrew/go/src/bashbrew/main.go b/bashbrew/go/src/bashbrew/main.go index 77197883d6fcf..017c41f6ad196 100644 --- a/bashbrew/go/src/bashbrew/main.go +++ b/bashbrew/go/src/bashbrew/main.go @@ -6,6 +6,8 @@ import ( "path/filepath" "github.com/codegangsta/cli" + + "github.com/docker-library/go-dockerlibrary/manifest" ) // TODO somewhere, ensure that the Docker engine we're talking to is API version 1.22+ (Docker 1.10+) @@ -18,6 +20,7 @@ var ( defaultLibrary string defaultCache string + arch string constraints []string exclusiveConstraints bool @@ -27,6 +30,7 @@ var ( // separated so that FlagsConfig.ApplyTo can access them flagEnvVars = map[string]string{ "debug": "BASHBREW_DEBUG", + "arch": "BASHBREW_ARCH", "config": "BASHBREW_CONFIG", "library": "BASHBREW_LIBRARY", "cache": "BASHBREW_CACHE", @@ -72,6 +76,12 @@ func main() { Usage: "do not apply any sorting, even via --build-order", }, + cli.StringFlag{ + Name: "arch", + Value: manifest.DefaultArchitecture, + EnvVar: flagEnvVars["arch"], + Usage: "the current platform architecture", + }, cli.StringSliceFlag{ Name: "constraint", Usage: "build constraints (see Constraints in Manifest2822Entry)", @@ -127,6 +137,7 @@ func main() { debugFlag = c.GlobalBool("debug") noSortFlag = c.GlobalBool("no-sort") + arch = c.GlobalString("arch") constraints = c.GlobalStringSlice("constraint") exclusiveConstraints = c.GlobalBool("exclusive-constraints") @@ -222,7 +233,18 @@ func main() { Before: subcommandBeforeFactory("push"), Action: cmdPush, }, + { + Name: "put-shared", + Usage: `updated shared tags in the registry`, + Flags: []cli.Flag{ + commonFlags["all"], + commonFlags["namespace"], + }, + Before: subcommandBeforeFactory("put-shared"), + Action: cmdPutShared, + }, + // TODO --depth flag for children and parents { Name: "children", Aliases: []string{ diff --git a/bashbrew/go/src/bashbrew/repo.go b/bashbrew/go/src/bashbrew/repo.go index 554ea8b09631b..8ec7730cc5e3d 100644 --- a/bashbrew/go/src/bashbrew/repo.go +++ b/bashbrew/go/src/bashbrew/repo.go @@ -79,6 +79,15 @@ var haveOutputSkippedMessage = map[string]bool{} func (r Repo) SkipConstraints(entry manifest.Manifest2822Entry) bool { repoTag := r.RepoName + ":" + entry.Tags[0] + // TODO decide if "arch" and "constraints" should be handled separately (but probably not) + if !entry.HasArchitecture(arch) { + if !haveOutputSkippedMessage[repoTag] { + fmt.Fprintf(os.Stderr, "skipping %q (due to architecture %q; only %q supported)\n", repoTag, arch, entry.ArchitecturesString()) + haveOutputSkippedMessage[repoTag] = true + } + return true + } + if len(entry.Constraints) == 0 { if exclusiveConstraints { if !haveOutputSkippedMessage[repoTag] { diff --git a/bashbrew/go/vendor/manifest b/bashbrew/go/vendor/manifest index 08eeb822209df..6fa456ebb941e 100644 --- a/bashbrew/go/vendor/manifest +++ b/bashbrew/go/vendor/manifest @@ -10,7 +10,7 @@ { "importpath": "github.com/docker-library/go-dockerlibrary", "repository": "https://github.com/docker-library/go-dockerlibrary", - "revision": "08ef5a968ebdd83dcc42998a96b6528837b55273", + "revision": "663a091da13fc848e27a16048fb39c4e4067056e", "branch": "master" }, { diff --git a/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/README.md b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/README.md new file mode 100644 index 0000000000000..3fc11ce809bf0 --- /dev/null +++ b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/README.md @@ -0,0 +1,5 @@ +# `import "github.com/docker-library/go-dockerlibrary/manifest"` + +[![Travis Build Status](https://travis-ci.org/docker-library/go-dockerlibrary.svg?branch=master)](https://travis-ci.org/docker-library/go-dockerlibrary) [![GoDoc](https://godoc.org/github.com/docker-library/go-dockerlibrary?status.svg)](https://godoc.org/github.com/docker-library/go-dockerlibrary) [![codecov](https://codecov.io/gh/docker-library/go-dockerlibrary/branch/master/graph/badge.svg)](https://codecov.io/gh/docker-library/go-dockerlibrary) + +This package contains the core parsing elements of [the `bashbrew` tool used by the Docker Official Images](https://github.com/docker-library/official-images/tree/master/bashbrew). diff --git a/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/example.go b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/example.go deleted file mode 100644 index d4f364fdb5318..0000000000000 --- a/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/example.go +++ /dev/null @@ -1,67 +0,0 @@ -// +build ignore - -package main - -import ( - "bufio" - "fmt" - "strings" - - "github.com/docker-library/go-dockerlibrary/manifest" -) - -func main() { - // TODO comment parsing - man, err := manifest.Parse(bufio.NewReader(strings.NewReader(`# RFC 2822 - - # I LOVE CAKE - -Maintainers: InfoSiftr (@infosiftr), - Johan Euphrosine (@proppy) -GitRepo: https://github.com/docker-library/golang.git -GitFetch: refs/heads/master - - - # hi - - - # blasphemer - - -# Go 1.6 -Tags: 1.6.1, 1.6, 1, latest -GitCommit: 0ce80411b9f41e9c3a21fc0a1bffba6ae761825a -Directory: 1.6 - - -# Go 1.5 -Tags: 1.5.3 -GitCommit: d7e2a8d90a9b8f5dfd5bcd428e0c33b68c40cc19 -Directory: 1.5 - - -Tags: 1.5 -GitCommit: d7e2a8d90a9b8f5dfd5bcd428e0c33b68c40cc19 -Directory: 1.5 - - -`))) - if err != nil { - panic(err) - } - fmt.Printf("-------------\n2822:\n%s\n", man) - - man, err = manifest.Parse(bufio.NewReader(strings.NewReader(` -# first set -a: b@c d -e: b@c d - - # second set -f: g@h -i: g@h j -`))) - if err != nil { - panic(err) - } - fmt.Printf("-------------\nline-based:\n%v\n", man) -} diff --git a/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/example_test.go b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/example_test.go new file mode 100644 index 0000000000000..5dc0b97ff4665 --- /dev/null +++ b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/example_test.go @@ -0,0 +1,192 @@ +package manifest_test + +import ( + "bufio" + "fmt" + "strings" + + "github.com/docker-library/go-dockerlibrary/manifest" +) + +func Example() { + man, err := manifest.Parse(bufio.NewReader(strings.NewReader(`# RFC 2822 + + # I LOVE CAKE + +Maintainers: InfoSiftr (@infosiftr), + Johan Euphrosine (@proppy) +GitFetch: refs/heads/master +GitRepo: https://github.com/docker-library/golang.git +SharedTags: latest +arm64v8-GitRepo: https://github.com/docker-library/golang.git +Architectures: amd64 + + + # hi + + + # blasphemer + + +# Go 1.6 +Tags: 1.6.1, 1.6, 1 +arm64v8-GitRepo: https://github.com/docker-library/golang.git +Directory: 1.6 +GitCommit: 0ce80411b9f41e9c3a21fc0a1bffba6ae761825a +Constraints: some-random-build-server + + +# Go 1.5 +Tags: 1.5.3 +GitCommit: d7e2a8d90a9b8f5dfd5bcd428e0c33b68c40cc19 +SharedTags: 1.5.3-debian, 1.5-debian +Directory: 1.5 +s390x-GitCommit: b6c460e7cd79b595267870a98013ec3078b490df +i386-GitFetch: refs/heads/i386 +ppc64le-Directory: 1.5/ppc64le + + +Tags: 1.5 +SharedTags: 1.5-debian +GitCommit: d7e2a8d90a9b8f5dfd5bcd428e0c33b68c40cc19 +Directory: 1.5 +s390x-GitCommit: b6c460e7cd79b595267870a98013ec3078b490df +i386-GitFetch: refs/heads/i386 +ppc64le-Directory: 1.5/ppc64le + +SharedTags: raspbian +GitCommit: deadbeefdeadbeefdeadbeefdeadbeefdeadbeef +Tags: raspbian-s390x +Architectures: s390x + + +`))) + if err != nil { + panic(err) + } + fmt.Printf("-------------\n2822:\n%s\n", man) + + fmt.Printf("\nShared Tag Groups:\n") + for _, group := range man.GetSharedTagGroups() { + fmt.Printf("\n - %s\n", strings.Join(group.SharedTags, ", ")) + for _, entry := range group.Entries { + fmt.Printf(" - %s\n", entry.TagsString()) + } + } + fmt.Printf("\n") + + man, err = manifest.Parse(bufio.NewReader(strings.NewReader(` +# maintainer: InfoSiftr (@infosiftr) +# maintainer: John Smith (@example-jsmith) + +# first set +a: b@c d +e: b@c d + + # second set +f: g@h +i: g@h j +`))) + if err != nil { + panic(err) + } + fmt.Printf("-------------\nline-based:\n%v\n", man) + + // Output: + // ------------- + // 2822: + // Maintainers: InfoSiftr (@infosiftr), Johan Euphrosine (@proppy) + // SharedTags: latest + // GitRepo: https://github.com/docker-library/golang.git + // arm64v8-GitRepo: https://github.com/docker-library/golang.git + // + // Tags: 1.6.1, 1.6, 1 + // GitCommit: 0ce80411b9f41e9c3a21fc0a1bffba6ae761825a + // Directory: 1.6 + // Constraints: some-random-build-server + // + // Tags: 1.5.3, 1.5 + // SharedTags: 1.5.3-debian, 1.5-debian + // GitCommit: d7e2a8d90a9b8f5dfd5bcd428e0c33b68c40cc19 + // Directory: 1.5 + // i386-GitFetch: refs/heads/i386 + // ppc64le-Directory: 1.5/ppc64le + // s390x-GitCommit: b6c460e7cd79b595267870a98013ec3078b490df + // + // Tags: raspbian-s390x + // SharedTags: raspbian + // Architectures: s390x + // GitCommit: deadbeefdeadbeefdeadbeefdeadbeefdeadbeef + // + // Shared Tag Groups: + // + // - latest + // - 1.6.1, 1.6, 1 + // + // - 1.5.3-debian, 1.5-debian + // - 1.5.3, 1.5 + // + // - raspbian + // - raspbian-s390x + // + // ------------- + // line-based: + // Maintainers: InfoSiftr (@infosiftr), John Smith (@example-jsmith) + // GitFetch: refs/heads/* + // + // Tags: a, e + // GitRepo: b + // GitCommit: c + // Directory: d + // + // Tags: f + // GitRepo: g + // GitFetch: refs/tags/h + // GitCommit: FETCH_HEAD + // + // Tags: i + // GitRepo: g + // GitFetch: refs/tags/h + // GitCommit: FETCH_HEAD + // Directory: j +} + +func ExampleFetch_local() { + repoName, tagName, man, err := manifest.Fetch("testdata", "bash:4.4") + if err != nil { + panic(err) + } + + fmt.Printf("%s:%s\n\n", repoName, tagName) + + fmt.Println(man.GetTag(tagName).ClearDefaults(manifest.DefaultManifestEntry).String()) + + // Output: + // bash:4.4 + // + // Maintainers: Tianon Gravi (@tianon) + // Tags: 4.4.12, 4.4, 4, latest + // GitRepo: https://github.com/tianon/docker-bash.git + // GitCommit: 1cbb5cf49b4c53bd5a986abf7a1afeb9a80eac1e + // Directory: 4.4 +} + +func ExampleFetch_remote() { + repoName, tagName, man, err := manifest.Fetch("/home/jsmith/docker/official-images/library", "https://github.com/docker-library/official-images/raw/1a3c4cd6d5cd53bd538a6f56a69f94c5b35325a7/library/bash:4.4") + if err != nil { + panic(err) + } + + fmt.Printf("%s:%s\n\n", repoName, tagName) + + fmt.Println(man.GetTag(tagName).ClearDefaults(manifest.DefaultManifestEntry).String()) + + // Output: + // bash:4.4 + // + // Maintainers: Tianon Gravi (@tianon) + // Tags: 4.4.12, 4.4, 4, latest + // GitRepo: https://github.com/tianon/docker-bash.git + // GitCommit: 1cbb5cf49b4c53bd5a986abf7a1afeb9a80eac1e + // Directory: 4.4 +} diff --git a/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/parse_test.go b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/parse_test.go new file mode 100644 index 0000000000000..02a2e4a671a80 --- /dev/null +++ b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/parse_test.go @@ -0,0 +1,20 @@ +package manifest_test + +import ( + "strings" + "testing" + + "github.com/docker-library/go-dockerlibrary/manifest" +) + +func TestParseError(t *testing.T) { + invalidManifest := `this is just completely bogus and invalid no matter how you slice it` + + man, err := manifest.Parse(strings.NewReader(invalidManifest)) + if err == nil { + t.Errorf("Expected error, got valid manifest instead:\n%s", man) + } + if !strings.HasPrefix(err.Error(), "cannot parse manifest in either format:") { + t.Errorf("Unexpected error: %v", err) + } +} diff --git a/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/rfc2822.go b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/rfc2822.go index 1046449a2b915..f80d41e4736f8 100644 --- a/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/rfc2822.go +++ b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/rfc2822.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "regexp" + "sort" "strings" "github.com/docker-library/go-dockerlibrary/pkg/stripper" @@ -27,25 +28,37 @@ type Manifest2822Entry struct { Maintainers []string `delim:"," strip:"\n\r\t "` - Tags []string `delim:"," strip:"\n\r\t "` + Tags []string `delim:"," strip:"\n\r\t "` + SharedTags []string `delim:"," strip:"\n\r\t "` + + Architectures []string `delim:"," strip:"\n\r\t "` GitRepo string GitFetch string GitCommit string Directory string + // architecture-specific versions of the above fields are in Paragraph.Values as ARCH-Field, ala s390x-Directory Constraints []string `delim:"," strip:"\n\r\t "` } -var DefaultManifestEntry = Manifest2822Entry{ - GitFetch: "refs/heads/master", - Directory: ".", -} +var ( + DefaultArchitecture = "amd64" + + DefaultManifestEntry = Manifest2822Entry{ + Architectures: []string{DefaultArchitecture}, + + GitFetch: "refs/heads/master", + Directory: ".", + } +) func (entry Manifest2822Entry) Clone() Manifest2822Entry { // SLICES! grr entry.Maintainers = append([]string{}, entry.Maintainers...) entry.Tags = append([]string{}, entry.Tags...) + entry.SharedTags = append([]string{}, entry.SharedTags...) + entry.Architectures = append([]string{}, entry.Architectures...) entry.Constraints = append([]string{}, entry.Constraints...) return entry } @@ -60,23 +73,60 @@ func (entry Manifest2822Entry) TagsString() string { return strings.Join(entry.Tags, StringSeparator2822) } +func (entry Manifest2822Entry) SharedTagsString() string { + return strings.Join(entry.SharedTags, StringSeparator2822) +} + +func (entry Manifest2822Entry) ArchitecturesString() string { + return strings.Join(entry.Architectures, StringSeparator2822) +} + func (entry Manifest2822Entry) ConstraintsString() string { return strings.Join(entry.Constraints, StringSeparator2822) } // if this method returns "true", then a.Tags and b.Tags can safely be combined (for the purposes of building) func (a Manifest2822Entry) SameBuildArtifacts(b Manifest2822Entry) bool { - return a.GitRepo == b.GitRepo && a.GitFetch == b.GitFetch && a.GitCommit == b.GitCommit && a.Directory == b.Directory && a.ConstraintsString() == b.ConstraintsString() + // check xxxarch-GitRepo, etc. fields for sameness first + for _, key := range append(a.archFields(), b.archFields()...) { + if a.Paragraph.Values[key] != b.Paragraph.Values[key] { + return false + } + } + + return a.ArchitecturesString() == b.ArchitecturesString() && a.GitRepo == b.GitRepo && a.GitFetch == b.GitFetch && a.GitCommit == b.GitCommit && a.Directory == b.Directory && a.ConstraintsString() == b.ConstraintsString() +} + +func isArchField(field string) bool { + return strings.HasSuffix(field, "-GitRepo") || strings.HasSuffix(field, "-GitFetch") || strings.HasSuffix(field, "-GitCommit") || strings.HasSuffix(field, "-Directory") +} + +// returns a list of architecture-specific fields in an Entry +func (entry Manifest2822Entry) archFields() []string { + ret := []string{} + for key, val := range entry.Paragraph.Values { + if isArchField(key) && val != "" { + ret = append(ret, key) + } + } + return ret } // returns a new Entry with any of the values that are equal to the values in "defaults" cleared func (entry Manifest2822Entry) ClearDefaults(defaults Manifest2822Entry) Manifest2822Entry { + entry = entry.Clone() // make absolutely certain we have a deep clone if entry.MaintainersString() == defaults.MaintainersString() { entry.Maintainers = nil } if entry.TagsString() == defaults.TagsString() { entry.Tags = nil } + if entry.SharedTagsString() == defaults.SharedTagsString() { + entry.SharedTags = nil + } + if entry.ArchitecturesString() == defaults.ArchitecturesString() { + entry.Architectures = nil + } if entry.GitRepo == defaults.GitRepo { entry.GitRepo = "" } @@ -89,6 +139,11 @@ func (entry Manifest2822Entry) ClearDefaults(defaults Manifest2822Entry) Manifes if entry.Directory == defaults.Directory { entry.Directory = "" } + for _, key := range defaults.archFields() { + if defaults.Paragraph.Values[key] == entry.Paragraph.Values[key] { + delete(entry.Paragraph.Values, key) + } + } if entry.ConstraintsString() == defaults.ConstraintsString() { entry.Constraints = nil } @@ -103,6 +158,12 @@ func (entry Manifest2822Entry) String() string { if str := entry.TagsString(); str != "" { ret = append(ret, "Tags: "+str) } + if str := entry.SharedTagsString(); str != "" { + ret = append(ret, "SharedTags: "+str) + } + if str := entry.ArchitecturesString(); str != "" { + ret = append(ret, "Architectures: "+str) + } if str := entry.GitRepo; str != "" { ret = append(ret, "GitRepo: "+str) } @@ -115,6 +176,11 @@ func (entry Manifest2822Entry) String() string { if str := entry.Directory; str != "" { ret = append(ret, "Directory: "+str) } + archFields := entry.archFields() + sort.Strings(archFields) // consistent ordering + for _, key := range archFields { + ret = append(ret, key+": "+entry.Paragraph.Values[key]) + } if str := entry.ConstraintsString(); str != "" { ret = append(ret, "Constraints: "+str) } @@ -136,6 +202,48 @@ func (manifest Manifest2822) String() string { return strings.Join(ret, "\n\n") } +func (entry *Manifest2822Entry) SetGitRepo(arch string, repo string) { + if entry.Paragraph.Values == nil { + entry.Paragraph.Values = map[string]string{} + } + entry.Paragraph.Values[arch+"-GitRepo"] = repo +} + +func (entry Manifest2822Entry) ArchGitRepo(arch string) string { + if val, ok := entry.Paragraph.Values[arch+"-GitRepo"]; ok && val != "" { + return val + } + return entry.GitRepo +} + +func (entry Manifest2822Entry) ArchGitFetch(arch string) string { + if val, ok := entry.Paragraph.Values[arch+"-GitFetch"]; ok && val != "" { + return val + } + return entry.GitFetch +} + +func (entry *Manifest2822Entry) SetGitCommit(arch string, commit string) { + if entry.Paragraph.Values == nil { + entry.Paragraph.Values = map[string]string{} + } + entry.Paragraph.Values[arch+"-GitCommit"] = commit +} + +func (entry Manifest2822Entry) ArchGitCommit(arch string) string { + if val, ok := entry.Paragraph.Values[arch+"-GitCommit"]; ok && val != "" { + return val + } + return entry.GitCommit +} + +func (entry Manifest2822Entry) ArchDirectory(arch string) string { + if val, ok := entry.Paragraph.Values[arch+"-Directory"]; ok && val != "" { + return val + } + return entry.Directory +} + func (entry Manifest2822Entry) HasTag(tag string) bool { for _, existingTag := range entry.Tags { if tag == existingTag { @@ -145,6 +253,26 @@ func (entry Manifest2822Entry) HasTag(tag string) bool { return false } +// HasSharedTag returns true if the given tag exists in entry.SharedTags. +func (entry Manifest2822Entry) HasSharedTag(tag string) bool { + for _, existingTag := range entry.SharedTags { + if tag == existingTag { + return true + } + } + return false +} + +// HasArchitecture returns true if the given architecture exists in entry.Architectures +func (entry Manifest2822Entry) HasArchitecture(arch string) bool { + for _, existingArch := range entry.Architectures { + if arch == existingArch { + return true + } + } + return false +} + func (manifest Manifest2822) GetTag(tag string) *Manifest2822Entry { for _, entry := range manifest.Entries { if entry.HasTag(tag) { @@ -154,6 +282,62 @@ func (manifest Manifest2822) GetTag(tag string) *Manifest2822Entry { return nil } +// GetSharedTag returns a list of entries with the given tag in entry.SharedTags (or the empty list if there are no entries with the given tag). +func (manifest Manifest2822) GetSharedTag(tag string) []Manifest2822Entry { + ret := []Manifest2822Entry{} + for _, entry := range manifest.Entries { + if entry.HasSharedTag(tag) { + ret = append(ret, entry) + } + } + return ret +} + +// GetAllSharedTags returns a list of the sum of all SharedTags in all entries of this image manifest (in the order they appear in the file). +func (manifest Manifest2822) GetAllSharedTags() []string { + fakeEntry := Manifest2822Entry{} + for _, entry := range manifest.Entries { + fakeEntry.SharedTags = append(fakeEntry.SharedTags, entry.SharedTags...) + } + fakeEntry.DeduplicateSharedTags() + return fakeEntry.SharedTags +} + +type SharedTagGroup struct { + SharedTags []string + Entries []*Manifest2822Entry +} + +// GetSharedTagGroups returns a map of shared tag groups to the list of entries they share (as described in https://github.com/docker-library/go-dockerlibrary/pull/2#issuecomment-277853597). +func (manifest Manifest2822) GetSharedTagGroups() []SharedTagGroup { + inter := map[string][]string{} + interOrder := []string{} // order matters, and maps randomize order + interKeySep := "," + for _, sharedTag := range manifest.GetAllSharedTags() { + interKeyParts := []string{} + for _, entry := range manifest.GetSharedTag(sharedTag) { + interKeyParts = append(interKeyParts, entry.Tags[0]) + } + interKey := strings.Join(interKeyParts, interKeySep) + if _, ok := inter[interKey]; !ok { + interOrder = append(interOrder, interKey) + } + inter[interKey] = append(inter[interKey], sharedTag) + } + ret := []SharedTagGroup{} + for _, tags := range interOrder { + group := SharedTagGroup{ + SharedTags: inter[tags], + Entries: []*Manifest2822Entry{}, + } + for _, tag := range strings.Split(tags, interKeySep) { + group.Entries = append(group.Entries, manifest.GetTag(tag)) + } + ret = append(ret, group) + } + return ret +} + func (manifest *Manifest2822) AddEntry(entry Manifest2822Entry) error { if len(entry.Tags) < 1 { return fmt.Errorf("missing Tags") @@ -165,20 +349,36 @@ func (manifest *Manifest2822) AddEntry(entry Manifest2822Entry) error { return fmt.Errorf("Tags %q has invalid Maintainers: %q (expected format %q)", strings.Join(invalidMaintainers, ", "), MaintainersFormat) } + entry.DeduplicateSharedTags() + seenTag := map[string]bool{} for _, tag := range entry.Tags { if otherEntry := manifest.GetTag(tag); otherEntry != nil { return fmt.Errorf("Tags %q includes duplicate tag: %q (duplicated in %q)", entry.TagsString(), tag, otherEntry.TagsString()) } + if otherEntries := manifest.GetSharedTag(tag); len(otherEntries) > 0 { + return fmt.Errorf("Tags %q includes tag conflicting with a shared tag: %q (shared tag in %q)", entry.TagsString(), tag, otherEntries[0].TagsString()) + } if seenTag[tag] { return fmt.Errorf("Tags %q includes duplicate tag: %q", entry.TagsString(), tag) } seenTag[tag] = true } + for _, tag := range entry.SharedTags { + if otherEntry := manifest.GetTag(tag); otherEntry != nil { + return fmt.Errorf("Tags %q includes conflicting shared tag: %q (duplicated in %q)", entry.TagsString(), tag, otherEntry.TagsString()) + } + if seenTag[tag] { + return fmt.Errorf("Tags %q includes duplicate tag: %q (in SharedTags)", entry.TagsString(), tag) + } + seenTag[tag] = true + } for i, existingEntry := range manifest.Entries { if existingEntry.SameBuildArtifacts(entry) { manifest.Entries[i].Tags = append(existingEntry.Tags, entry.Tags...) + manifest.Entries[i].SharedTags = append(existingEntry.SharedTags, entry.SharedTags...) + manifest.Entries[i].DeduplicateSharedTags() return nil } } @@ -210,20 +410,51 @@ func (entry Manifest2822Entry) InvalidMaintainers() []string { return invalid } +// DeduplicateSharedTags will remove duplicate values from entry.SharedTags, preserving order. +func (entry *Manifest2822Entry) DeduplicateSharedTags() { + aggregate := []string{} + seen := map[string]bool{} + for _, tag := range entry.SharedTags { + if seen[tag] { + continue + } + seen[tag] = true + aggregate = append(aggregate, tag) + } + entry.SharedTags = aggregate +} + type decoderWrapper struct { *control.Decoder } func (decoder *decoderWrapper) Decode(entry *Manifest2822Entry) error { + // reset Architectures and SharedTags so that they can be either inherited or replaced, not additive + sharedTags := entry.SharedTags + entry.SharedTags = nil + arches := entry.Architectures + entry.Architectures = nil + for { err := decoder.Decoder.Decode(entry) if err != nil { return err } + // ignore empty paragraphs (blank lines at the start, excess blank lines between paragraphs, excess blank lines at EOF) - if len(entry.Paragraph.Order) > 0 { - return nil + if len(entry.Paragraph.Order) == 0 { + continue + } + + // if we had no SharedTags or Architectures, restore our "default" (original) values + if len(entry.SharedTags) == 0 { + entry.SharedTags = sharedTags + } + if len(entry.Architectures) == 0 { + entry.Architectures = arches } + + return nil } } diff --git a/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/testdata/bash b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/testdata/bash new file mode 100644 index 0000000000000..0610dcf4c1ca8 --- /dev/null +++ b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/manifest/testdata/bash @@ -0,0 +1,38 @@ +# this is a snapshot of https://github.com/docker-library/official-images/raw/1a3c4cd6d5cd53bd538a6f56a69f94c5b35325a7/library/bash + +# this file is generated via https://github.com/tianon/docker-bash/blob/cd1de3dfc885b3395cd354ddb988922350b092a7/generate-stackbrew-library.sh + +Maintainers: Tianon Gravi (@tianon) +GitRepo: https://github.com/tianon/docker-bash.git + +Tags: 4.4.12, 4.4, 4, latest +GitCommit: 1cbb5cf49b4c53bd5a986abf7a1afeb9a80eac1e +Directory: 4.4 + +Tags: 4.3.48, 4.3 +GitCommit: 1cbb5cf49b4c53bd5a986abf7a1afeb9a80eac1e +Directory: 4.3 + +Tags: 4.2.53, 4.2 +GitCommit: 1cbb5cf49b4c53bd5a986abf7a1afeb9a80eac1e +Directory: 4.2 + +Tags: 4.1.17, 4.1 +GitCommit: 1cbb5cf49b4c53bd5a986abf7a1afeb9a80eac1e +Directory: 4.1 + +Tags: 4.0.44, 4.0 +GitCommit: 4438745d601d10d300e363f24205a3ca75307803 +Directory: 4.0 + +Tags: 3.2.57, 3.2, 3 +GitCommit: 1cbb5cf49b4c53bd5a986abf7a1afeb9a80eac1e +Directory: 3.2 + +Tags: 3.1.23, 3.1 +GitCommit: 1cbb5cf49b4c53bd5a986abf7a1afeb9a80eac1e +Directory: 3.1 + +Tags: 3.0.22, 3.0 +GitCommit: 1cbb5cf49b4c53bd5a986abf7a1afeb9a80eac1e +Directory: 3.0 diff --git a/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/execpipe/execpipe_example_test.go b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/execpipe/execpipe_example_test.go new file mode 100644 index 0000000000000..dd45783d67a91 --- /dev/null +++ b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/execpipe/execpipe_example_test.go @@ -0,0 +1,26 @@ +package execpipe_test + +import ( + "bytes" + "fmt" + "io" + "strings" + + "github.com/docker-library/go-dockerlibrary/pkg/execpipe" +) + +func Example() { + pipe, err := execpipe.RunCommand("go", "version") + if err != nil { + panic(err) + } + defer pipe.Close() + + var buf bytes.Buffer + io.Copy(&buf, pipe) + + fmt.Println(strings.SplitN(buf.String(), " version ", 2)[0]) + + // Output: + // go +} diff --git a/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/execpipe/execpipe_test.go b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/execpipe/execpipe_test.go new file mode 100644 index 0000000000000..b8452ac0b6314 --- /dev/null +++ b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/execpipe/execpipe_test.go @@ -0,0 +1,31 @@ +package execpipe_test + +import ( + "os" + "os/exec" + "testing" + + "github.com/docker-library/go-dockerlibrary/pkg/execpipe" +) + +func TestStdoutPipeError(t *testing.T) { + cmd := exec.Command("nothing", "really", "matters", "in", "the", "end") + + // set "Stdout" so that "cmd.StdoutPipe" fails + // https://golang.org/src/os/exec/exec.go?s=16834:16883#L587 + cmd.Stdout = os.Stdout + + _, err := execpipe.Run(cmd) + if err == nil { + t.Errorf("Expected execpipe.Run to fail -- it did not") + } +} + +func TestStartError(t *testing.T) { + // craft a definitely-invalid command so that "cmd.Start" fails + // https://golang.org/src/os/exec/exec.go?s=8739:8766#L303 + _, err := execpipe.RunCommand("nothing-really-matters-in-the-end--bogus-command") + if err == nil { + t.Errorf("Expected execpipe.RunCommand to fail -- it did not") + } +} diff --git a/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/stripper/comments_example_test.go b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/stripper/comments_example_test.go new file mode 100644 index 0000000000000..924ea7b210da3 --- /dev/null +++ b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/stripper/comments_example_test.go @@ -0,0 +1,32 @@ +package stripper_test + +import ( + "io" + "os" + "strings" + + "github.com/docker-library/go-dockerlibrary/pkg/stripper" +) + +func ExampleCommentStripper() { + r := strings.NewReader(` +# opening comment +a: b +# comment! +c: d # not a comment + +# another cheeky comment +e: f +`) + + comStrip := stripper.NewCommentStripper(r) + + // using CopyBuffer to force smaller Read sizes (better testing coverage that way) + io.CopyBuffer(os.Stdout, comStrip, make([]byte, 32)) + + // Output: + // a: b + // c: d # not a comment + // + // e: f +} diff --git a/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/templatelib/doc.go b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/templatelib/doc.go new file mode 100644 index 0000000000000..9ef68ee743641 --- /dev/null +++ b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/templatelib/doc.go @@ -0,0 +1,8 @@ +/* +Package templatelib implements a group of useful functions for use with the stdlib text/template package. + +Usage: + + tmpl, err := template.New("some-template").Funcs(templatelib.FuncMap).Parse("Hi, {{ join " " .Names }}") +*/ +package templatelib diff --git a/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/templatelib/lib.go b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/templatelib/lib.go index 98ac86d1ceffc..85a21cc5ed7a1 100644 --- a/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/templatelib/lib.go +++ b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/templatelib/lib.go @@ -67,12 +67,15 @@ func stringsModifierActionFactory(a func(string, string) string) func([]string, } } -// TODO write some tests for these - var FuncMap = template.FuncMap{ + // {{- $isGitHub := hasPrefix "https://github.com/" $url -}} + // {{- $isHtml := hasSuffix ".html" $url -}} "hasPrefix": swapStringsFuncBoolArgsOrder(strings.HasPrefix), "hasSuffix": swapStringsFuncBoolArgsOrder(strings.HasSuffix), + // {{- $hugeIfTrue := .SomeValue | ternary "HUGE" "not so huge" -}} + // if .SomeValue is truthy, $hugeIfTrue will be "HUGE" + // (otherwise, "not so huge") "ternary": func(truthy interface{}, falsey interface{}, val interface{}) interface{} { if t, ok := template.IsTrue(val); !ok { panic(fmt.Sprintf(`template.IsTrue(%+v) says things are NOT OK`, val)) @@ -83,14 +86,26 @@ var FuncMap = template.FuncMap{ } }, + // First Tag: {{- .Tags | first -}} + // Last Tag: {{- .Tags | last -}} "first": thingsActionFactory("first", true, func(args []interface{}, arg interface{}) interface{} { return arg }), "last": thingsActionFactory("last", false, func(args []interface{}, arg interface{}) interface{} { return arg }), + // JSON data dump: {{ json . }} + // (especially nice for taking data and piping it to "jq") + // (ie "some-tool inspect --format '{{ json . }}' some-things | jq .") "json": func(v interface{}) (string, error) { j, err := json.Marshal(v) return string(j), err }, - "join": stringsActionFactory("join", true, strings.Join), + + // Everybody: {{- join ", " .Names -}} + // Concat: {{- join "/" "https://github.com" "jsmith" "some-repo" -}} + "join": stringsActionFactory("join", true, strings.Join), + + // {{- $mungedUrl := $url | replace "git://" "https://" | trimSuffixes ".git" -}} + // turns: git://github.com/jsmith/some-repo.git + // into: https://github.com/jsmith/some-repo "trimPrefixes": stringsActionFactory("trimPrefixes", false, stringsModifierActionFactory(strings.TrimPrefix)), "trimSuffixes": stringsActionFactory("trimSuffixes", false, stringsModifierActionFactory(strings.TrimSuffix)), "replace": stringsActionFactory("replace", false, func(strs []string, str string) string { diff --git a/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/templatelib/lib_example_test.go b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/templatelib/lib_example_test.go new file mode 100644 index 0000000000000..a36bb633d1da0 --- /dev/null +++ b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/templatelib/lib_example_test.go @@ -0,0 +1,193 @@ +package templatelib_test + +import ( + "os" + "text/template" + + "github.com/docker-library/go-dockerlibrary/pkg/templatelib" +) + +func Example_prefixSuffix() { + tmpl, err := template.New("github-or-html").Funcs(templatelib.FuncMap).Parse(` + {{- . -}} + + {{- if hasPrefix "https://github.com/" . -}} + {{- " " -}} GitHub + {{- end -}} + + {{- if hasSuffix ".html" . -}} + {{- " " -}} HTML + {{- end -}} + + {{- "\n" -}} + `) + if err != nil { + panic(err) + } + + err = tmpl.Execute(os.Stdout, "https://github.com/example/example") + if err != nil { + panic(err) + } + + err = tmpl.Execute(os.Stdout, "https://example.com/test.html") + if err != nil { + panic(err) + } + + err = tmpl.Execute(os.Stdout, "https://example.com") + if err != nil { + panic(err) + } + + err = tmpl.Execute(os.Stdout, "https://github.com/example/example/raw/master/test.html") + if err != nil { + panic(err) + } + + // Output: + // https://github.com/example/example GitHub + // https://example.com/test.html HTML + // https://example.com + // https://github.com/example/example/raw/master/test.html GitHub HTML +} + +func Example_ternary() { + tmpl, err := template.New("huge-if-true").Funcs(templatelib.FuncMap).Parse(` + {{- range $a := . -}} + {{ printf "%#v: %s\n" $a (ternary "HUGE" "not so huge" $a) }} + {{- end -}} + `) + + err = tmpl.Execute(os.Stdout, []interface{}{ + true, + false, + "true", + "false", + "", + nil, + 1, + 0, + 9001, + []bool{}, + []bool{false}, + }) + if err != nil { + panic(err) + } + + // Output: + // true: HUGE + // false: not so huge + // "true": HUGE + // "false": HUGE + // "": not so huge + // : not so huge + // 1: HUGE + // 0: not so huge + // 9001: HUGE + // []bool{}: not so huge + // []bool{false}: HUGE +} + +func Example_firstLast() { + tmpl, err := template.New("first-and-last").Funcs(templatelib.FuncMap).Parse(`First: {{ . | first }}, Last: {{ . | last }}`) + + err = tmpl.Execute(os.Stdout, []interface{}{ + "a", + "b", + "c", + }) + if err != nil { + panic(err) + } + + // Output: + // First: a, Last: c +} + +func Example_json() { + tmpl, err := template.New("json").Funcs(templatelib.FuncMap).Parse(` + {{- json . -}} + `) + + err = tmpl.Execute(os.Stdout, map[string]interface{}{ + "a": []string{"1", "2", "3"}, + "b": map[string]bool{"1": true, "2": false, "3": true}, + "c": nil, + }) + if err != nil { + panic(err) + } + + // Output: + // {"a":["1","2","3"],"b":{"1":true,"2":false,"3":true},"c":null} +} + +func Example_join() { + tmpl, err := template.New("join").Funcs(templatelib.FuncMap).Parse(` + Array: {{ . | join ", " }}{{ "\n" -}} + Args: {{ join ", " "a" "b" "c" -}} + `) + + err = tmpl.Execute(os.Stdout, []string{ + "1", + "2", + "3", + }) + if err != nil { + panic(err) + } + + // Output: + // Array: 1, 2, 3 + // Args: a, b, c +} + +func Example_trimReplaceGitToHttps() { + tmpl, err := template.New("git-to-https").Funcs(templatelib.FuncMap).Parse(` + {{- range . -}} + {{- . | replace "git://" "https://" | trimSuffixes ".git" }}{{ "\n" -}} + {{- end -}} + `) + + err = tmpl.Execute(os.Stdout, []string{ + "git://github.com/jsmith/some-repo.git", + "https://github.com/jsmith/some-repo.git", + "https://github.com/jsmith/some-repo", + }) + if err != nil { + panic(err) + } + + // Output: + // https://github.com/jsmith/some-repo + // https://github.com/jsmith/some-repo + // https://github.com/jsmith/some-repo +} + +func Example_trimReplaceGitToGo() { + tmpl, err := template.New("git-to-go").Funcs(templatelib.FuncMap).Parse(` + {{- range . -}} + {{- . | trimPrefixes "git://" "http://" "https://" "ssh://" | trimSuffixes ".git" }}{{ "\n" -}} + {{- end -}} + `) + + err = tmpl.Execute(os.Stdout, []string{ + "git://github.com/jsmith/some-repo.git", + "https://github.com/jsmith/some-repo.git", + "https://github.com/jsmith/some-repo", + "ssh://github.com/jsmith/some-repo.git", + "github.com/jsmith/some-repo", + }) + if err != nil { + panic(err) + } + + // Output: + // github.com/jsmith/some-repo + // github.com/jsmith/some-repo + // github.com/jsmith/some-repo + // github.com/jsmith/some-repo + // github.com/jsmith/some-repo +} diff --git a/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/templatelib/lib_test.go b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/templatelib/lib_test.go new file mode 100644 index 0000000000000..9143aa28722b4 --- /dev/null +++ b/bashbrew/go/vendor/src/github.com/docker-library/go-dockerlibrary/pkg/templatelib/lib_test.go @@ -0,0 +1,45 @@ +package templatelib_test + +import ( + "testing" + "text/template" + "unsafe" + + "github.com/docker-library/go-dockerlibrary/pkg/templatelib" +) + +func TestTernaryPanic(t *testing.T) { + // one of the only places template.IsTrue will return "false" for the "ok" value is an UnsafePointer (hence this test) + + defer func() { + if r := recover(); r == nil { + t.Errorf("Expected panic, executed successfully instead") + } else if errText, ok := r.(string); !ok || errText != `template.IsTrue() says things are NOT OK` { + t.Errorf("Unexpected panic: %v", errText) + } + }() + + tmpl, err := template.New("unsafe-pointer").Funcs(templatelib.FuncMap).Parse(`{{ ternary "true" "false" . }}`) + + err = tmpl.Execute(nil, unsafe.Pointer(uintptr(0))) + if err != nil { + t.Errorf("Expected panic, got error instead: %v", err) + } +} + +func TestJoinPanic(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("Expected panic, executed successfully instead") + } else if errText, ok := r.(string); !ok || errText != `"join" requires at least one argument` { + t.Errorf("Unexpected panic: %v", r) + } + }() + + tmpl, err := template.New("join-no-arg").Funcs(templatelib.FuncMap).Parse(`{{ join }}`) + + err = tmpl.Execute(nil, nil) + if err != nil { + t.Errorf("Expected panic, got error instead: %v", err) + } +}