Compare commits

...

7 Commits

Author SHA1 Message Date
Ettore Di Giacinto
bac9bac25f ⬆️ Tag 0.32.2 2022-06-06 17:39:58 +02:00
Ettore Di Giacinto
ce95b3ada4 🎨 Cleanups 2022-06-06 17:39:16 +02:00
mudler
49d8c2b972 🎨 Add test-integration-docker target 2022-06-06 17:39:16 +02:00
mudler
2b3a1555f0 🎨 Refactor, use ensureoder where necessary 2022-06-06 17:39:16 +02:00
Itxaka
03e72653c7 Fill referenceID on repo sync (#300)
Signed-off-by: Itxaka <igarcia@suse.com>
2022-06-06 17:36:19 +02:00
Ettore Di Giacinto
264bf53fe7 🎨 Accept types.Logger in WithLogger 2022-05-26 13:09:37 +00:00
Ettore Di Giacinto
edd2275bf5 🎨 Allow to pass by a logger interface to context 2022-05-26 13:06:47 +00:00
19 changed files with 220 additions and 74 deletions

View File

@@ -81,6 +81,12 @@ test-docker:
--workdir /go/src/github.com/mudler/luet -ti golang:latest \ --workdir /go/src/github.com/mudler/luet -ti golang:latest \
bash -c "make test" bash -c "make test"
.PHONY: test-integration-docker
test-integration-docker:
docker run -v $(ROOT_DIR):/go/src/github.com/mudler/luet -v /var/run/docker.sock:/var/run/docker.sock \
--workdir /go/src/github.com/mudler/luet -ti golang:latest \
bash -c "apt-get update && apt-get install docker.io && make test-integration"
multiarch-build: multiarch-build:
goreleaser build --snapshot --rm-dist goreleaser build --snapshot --rm-dist

View File

@@ -30,7 +30,7 @@ var cfgFile string
var Verbose bool var Verbose bool
const ( const (
LuetCLIVersion = "0.32.1" LuetCLIVersion = "0.32.2"
LuetEnvPrefix = "LUET" LuetEnvPrefix = "LUET"
) )

1
go.mod
View File

@@ -53,6 +53,7 @@ require (
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/pterm/pterm v0.12.32-0.20211002183613-ada9ef6790c3 github.com/pterm/pterm v0.12.32-0.20211002183613-ada9ef6790c3
github.com/rancher-sandbox/gofilecache v0.0.0-20210330135715-becdeff5df15 github.com/rancher-sandbox/gofilecache v0.0.0-20210330135715-becdeff5df15
github.com/scrohde/go-toposort v0.0.0-20170629203416-a9a902e065a3 // indirect
github.com/spf13/cast v1.4.1 // indirect github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/cobra v1.2.1 github.com/spf13/cobra v1.2.1
github.com/spf13/viper v1.8.1 github.com/spf13/viper v1.8.1

2
go.sum
View File

@@ -1300,6 +1300,8 @@ github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b/go.mod h1:
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
github.com/scrohde/go-toposort v0.0.0-20170629203416-a9a902e065a3 h1:i8yAzeysv+UgLJ0O8usGQHvtyM0AbPHlI03l5OnWD4A=
github.com/scrohde/go-toposort v0.0.0-20170629203416-a9a902e065a3/go.mod h1:FzYqW619hxj8ogwgAr2ENoIELLwatZvaBnWdTquP99U=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=

View File

@@ -30,7 +30,7 @@ import (
) )
type Context struct { type Context struct {
*logger.Logger types.Logger
context.Context context.Context
types.GarbageCollector types.GarbageCollector
Config *types.LuetConfig Config *types.LuetConfig
@@ -51,7 +51,7 @@ func (c *Context) GetAnnotation(s string) interface{} {
type ContextOption func(c *Context) error type ContextOption func(c *Context) error
// WithLogger sets the logger // WithLogger sets the logger
func WithLogger(l *logger.Logger) ContextOption { func WithLogger(l types.Logger) ContextOption {
return func(c *Context) error { return func(c *Context) error {
c.Logger = l c.Logger = l
return nil return nil
@@ -122,13 +122,14 @@ func (c *Context) WithLoggingContext(name string) types.Context {
ctxCopy.Config = &configCopy ctxCopy.Config = &configCopy
ctxCopy.annotations = ctx.annotations ctxCopy.annotations = ctx.annotations
ctxCopy.Logger, _ = c.Logger.Copy(logger.WithContext(name)) ctxCopy.Logger, _ = c.Logger.Copy()
ctxCopy.Logger.SetContext(name)
return ctxCopy return ctxCopy
} }
// Copy returns a context copy with a reset logging context // Copy returns a context copy with a reset logging context
func (c *Context) Copy() types.Context { func (c *Context) Clone() types.Context {
return c.WithLoggingContext("") return c.WithLoggingContext("")
} }

View File

@@ -25,6 +25,7 @@ import (
log "github.com/ipfs/go-log/v2" log "github.com/ipfs/go-log/v2"
"github.com/kyokomi/emoji" "github.com/kyokomi/emoji"
"github.com/mudler/luet/pkg/api/core/types"
"github.com/pterm/pterm" "github.com/pterm/pterm"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore" "go.uber.org/zap/zapcore"
@@ -114,18 +115,19 @@ func New(opts ...LoggerOptions) (*Logger, error) {
return l, nil return l, nil
} }
func (l *Logger) Copy(opts ...LoggerOptions) (*Logger, error) { // Copy returns a copy of the logger
func (l *Logger) Copy() (types.Logger, error) {
c := *l c := *l
copy := &c copy := &c
for _, o := range opts {
if err := o(copy); err != nil {
return nil, err
}
}
return copy, nil return copy, nil
} }
// SetContext sets the logger context, used to prefix log lines
func (l *Logger) SetContext(name string) {
l.context = name
}
func joinMsg(args ...interface{}) (message string) { func joinMsg(args ...interface{}) (message string) {
for _, m := range args { for _, m := range args {
message += " " + fmt.Sprintf("%v", m) message += " " + fmt.Sprintf("%v", m)

View File

@@ -21,7 +21,6 @@ import (
"os" "os"
"github.com/gookit/color" "github.com/gookit/color"
"github.com/mudler/luet/pkg/api/core/logger"
. "github.com/mudler/luet/pkg/api/core/logger" . "github.com/mudler/luet/pkg/api/core/logger"
. "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
@@ -84,11 +83,12 @@ var _ = Describe("Context and logging", func() {
It("returns copies with logged context", func() { It("returns copies with logged context", func() {
l, err := New(WithLevel("debug")) l, err := New(WithLevel("debug"))
l, _ = l.Copy(logger.WithContext("bazzz")) l2, _ := l.Copy()
l2.SetContext("bazzz")
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(captureStdout(func(w io.Writer) { Expect(captureStdout(func(w io.Writer) {
l.Debug("bar") l2.Debug("bar")
})).To(ContainSubstring("(bazzz) bar")) })).To(ContainSubstring("(bazzz) bar"))
}) })

View File

@@ -6,9 +6,10 @@ import (
"sort" "sort"
"unicode" "unicode"
"github.com/mudler/luet/pkg/helpers"
"github.com/mudler/topsort" "github.com/mudler/topsort"
"github.com/philopon/go-toposort"
"github.com/pkg/errors" "github.com/pkg/errors"
toposort "github.com/scrohde/go-toposort"
) )
// PackageAssert represent a package assertion. // PackageAssert represent a package assertion.
@@ -30,51 +31,48 @@ func (a *PackageAssert) String() string {
return fmt.Sprintf("%s/%s %s %s", a.Package.GetCategory(), a.Package.GetName(), a.Package.GetVersion(), msg) return fmt.Sprintf("%s/%s %s %s", a.Package.GetCategory(), a.Package.GetName(), a.Package.GetVersion(), msg)
} }
func (assertions PackagesAssertions) EnsureOrder() PackagesAssertions { func (assertions PackagesAssertions) EnsureOrder(definitiondb PackageDatabase) (PackagesAssertions, error) {
allAssertions := assertions
orderedAssertions := PackagesAssertions{} orderedAssertions := PackagesAssertions{}
unorderedAssertions := PackagesAssertions{}
fingerprints := []string{}
tmpMap := map[string]PackageAssert{} tmpMap := map[string]PackageAssert{}
for _, a := range assertions { graph := toposort.NewGraph(len(allAssertions))
tmpMap[a.Package.GetFingerPrint()] = a
fingerprints = append(fingerprints, a.Package.GetFingerPrint())
unorderedAssertions = append(unorderedAssertions, a) // Build a list of the ones that must be ordered
if a.Value { for _, a := range assertions {
unorderedAssertions = append(unorderedAssertions, a) // Build a list of the ones that must be ordered tmpMap[a.Package.GetPackageName()] = a
} else { graph.AddNode(a.Package.GetPackageName())
orderedAssertions = append(orderedAssertions, a) // Keep last the ones which are not meant to be installed
}
} }
sort.Sort(unorderedAssertions) edges := map[string]string{}
// Build a topological graph // Build a topological graph
graph := toposort.NewGraph(len(unorderedAssertions)) for _, a := range allAssertions {
graph.AddNodes(fingerprints...)
for _, a := range unorderedAssertions {
for _, req := range a.Package.GetRequires() { for _, req := range a.Package.GetRequires() {
graph.AddEdge(a.Package.GetFingerPrint(), req.GetFingerPrint()) if def, err := definitiondb.FindPackage(req); err == nil { // Provides: Get a chance of being override here
req = def
}
edges[a.Package.GetPackageName()] = req.GetPackageName()
graph.AddNode(req.GetPackageName())
} }
} }
result, ok := graph.Toposort()
for k, v := range edges {
graph.AddEdge(k, v)
}
result, ok := graph.ToposortStable()
if !ok { if !ok {
panic("Cycle found") return nil, fmt.Errorf("cycle found")
} }
for _, res := range result { for _, res := range result {
a, ok := tmpMap[res] a, ok := tmpMap[res]
if !ok { if ok {
panic("fail") orderedAssertions = append(orderedAssertions, a)
// continue
} }
orderedAssertions = append(orderedAssertions, a)
// orderedAssertions = append(PackagesAssertions{a}, orderedAssertions...) // push upfront
} }
//helpers.ReverseAny(orderedAssertions) helpers.ReverseAny(orderedAssertions)
return orderedAssertions return orderedAssertions, nil
} }
// SearchByName searches a string matching a package in the assetion list // SearchByName searches a string matching a package in the assetion list

View File

@@ -0,0 +1,86 @@
// Copyright © 2021 Ettore Di Giacinto <mudler@mocaccino.org>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, see <http://www.gnu.org/licenses/>.
package types_test
import (
types "github.com/mudler/luet/pkg/api/core/types"
"github.com/mudler/luet/pkg/database"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Assertions", func() {
Context("Ordering", func() {
It("orders them correctly", func() {
foo := &types.Package{Name: "foo", PackageRequires: []*types.Package{{Name: "bar"}}}
assertions := types.PackagesAssertions{
{Package: foo},
{Package: &types.Package{Name: "baz", PackageRequires: []*types.Package{{Name: "bar"}}}},
{Package: &types.Package{Name: "bar", PackageRequires: []*types.Package{{}}}},
}
ordered_old, err := assertions.Order(database.NewInMemoryDatabase(false), foo.GetFingerPrint())
Expect(err).ShouldNot(HaveOccurred())
Expect(ordered_old[0].Package.Name).To(Equal("bar"))
ordered, err := assertions.EnsureOrder(database.NewInMemoryDatabase(false))
Expect(err).ShouldNot(HaveOccurred())
Expect(len(ordered)).To(Equal(3))
Expect(ordered[0].Package.Name).To(Equal("bar"))
})
It("errors on cycles", func() {
foo := &types.Package{Name: "foo", PackageRequires: []*types.Package{{Name: "bar"}}}
assertions := types.PackagesAssertions{
{Package: foo},
{Package: &types.Package{Name: "baz", PackageRequires: []*types.Package{{Name: "bar"}}}},
{Package: &types.Package{Name: "bar", PackageRequires: []*types.Package{{Name: "baz"}}}},
}
_, err := assertions.Order(database.NewInMemoryDatabase(false), foo.GetFingerPrint())
Expect(err).Should(HaveOccurred())
_, err = assertions.EnsureOrder(database.NewInMemoryDatabase(false))
Expect(err).Should(HaveOccurred())
})
It("orders them correctly", func() {
foo := &types.Package{Name: "foo", PackageRequires: []*types.Package{{Name: "bar"}}}
assertions := types.PackagesAssertions{
{Package: foo},
{Package: &types.Package{Name: "baz2", PackageRequires: []*types.Package{{Name: "foobaz"}}}},
{Package: &types.Package{Name: "baz", PackageRequires: []*types.Package{{Name: "bar"}}}},
{Package: &types.Package{Name: "bar", PackageRequires: []*types.Package{{}}}},
{Package: &types.Package{Name: "foobaz", PackageRequires: []*types.Package{{}}}},
}
ordered_old, err := assertions.Order(database.NewInMemoryDatabase(false), foo.GetFingerPrint())
Expect(err).ShouldNot(HaveOccurred())
Expect(ordered_old[0].Package.Name).To(Equal("bar"))
Expect(ordered_old[1].Package.Name).ToNot(Equal("foobaz"))
ordered, err := assertions.EnsureOrder(database.NewInMemoryDatabase(false))
Expect(err).ShouldNot(HaveOccurred())
Expect(len(ordered)).To(Equal(5))
Expect(ordered[0].Package.Name).To(Equal("bar"))
Expect(ordered[1].Package.Name).To(Equal("foobaz"))
})
})
})

View File

@@ -19,7 +19,7 @@ type Context interface {
Logger Logger
GarbageCollector GarbageCollector
GetConfig() LuetConfig GetConfig() LuetConfig
Copy() Context Clone() Context
// SetAnnotation sets generic annotations to hold in a context // SetAnnotation sets generic annotations to hold in a context
SetAnnotation(s string, i interface{}) SetAnnotation(s string, i interface{})

View File

@@ -33,7 +33,8 @@ type Logger interface {
Fatalf(string, ...interface{}) Fatalf(string, ...interface{})
Panicf(string, ...interface{}) Panicf(string, ...interface{})
Tracef(string, ...interface{}) Tracef(string, ...interface{})
Copy() (Logger, error)
SetContext(string)
SpinnerStop() SpinnerStop()
Spinner() Spinner()
Ask() bool Ask() bool

View File

@@ -22,6 +22,8 @@ import (
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
"github.com/mudler/luet/pkg/api/core/types" "github.com/mudler/luet/pkg/api/core/types"
box "github.com/mudler/luet/pkg/box" box "github.com/mudler/luet/pkg/box"
fileHelper "github.com/mudler/luet/pkg/helpers/file"
"github.com/mudler/luet/pkg/tree"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@@ -90,3 +92,47 @@ func NewLuetFinalizerFromYaml(data []byte) (*LuetFinalizer, error) {
} }
return &p, err return &p, err
} }
func OrderFinalizers(allRepos types.PackageDatabase, toInstall map[string]ArtifactMatch, solution types.PackagesAssertions) ([]*types.Package, error) {
var toFinalize []*types.Package
if len(toInstall) == 1 {
for _, w := range toInstall {
if fileHelper.Exists(w.Package.Rel(tree.FinalizerFile)) {
// Finalizers needs to run in order and in sequence.
ordered, err := solution.Order(allRepos, w.Package.GetFingerPrint())
if err != nil {
return toFinalize, errors.Wrap(err, "While order a solution for "+w.Package.HumanReadableString())
}
ORDER:
for _, ass := range ordered {
if ass.Value {
installed, ok := toInstall[ass.Package.GetFingerPrint()]
if !ok {
// It was a dep already installed in the system, so we can skip it safely
continue ORDER
}
treePackage, err := installed.Repository.GetTree().GetDatabase().FindPackage(ass.Package)
if err != nil {
return toFinalize, errors.Wrap(err, "Error getting package "+ass.Package.HumanReadableString())
}
toFinalize = append(toFinalize, treePackage)
}
}
}
}
} else {
assertions, err := solution.EnsureOrder(allRepos)
if err != nil {
return toFinalize, err
}
for _, o := range assertions {
if o.Value {
toFinalize = append(toFinalize, o.Package)
}
}
}
return toFinalize, nil
}

View File

@@ -638,7 +638,7 @@ func (l *LuetInstaller) download(syncedRepos Repositories, toDownload map[string
var wg = new(sync.WaitGroup) var wg = new(sync.WaitGroup)
ctx := l.Options.Context.Copy() ctx := l.Options.Context.Clone()
// Check if the terminal is big enough to display a progress bar // Check if the terminal is big enough to display a progress bar
// https://github.com/pterm/pterm/blob/4c725e56bfd9eb38e1c7b9dec187b50b93baa8bd/progressbar_printer.go#L190 // https://github.com/pterm/pterm/blob/4c725e56bfd9eb38e1c7b9dec187b50b93baa8bd/progressbar_printer.go#L190
@@ -816,33 +816,7 @@ func (l *LuetInstaller) computeInstall(o Option, syncedRepos Repositories, cp ty
func (l *LuetInstaller) getFinalizers(allRepos types.PackageDatabase, solution types.PackagesAssertions, toInstall map[string]ArtifactMatch, nodeps bool) ([]*types.Package, error) { func (l *LuetInstaller) getFinalizers(allRepos types.PackageDatabase, solution types.PackagesAssertions, toInstall map[string]ArtifactMatch, nodeps bool) ([]*types.Package, error) {
var toFinalize []*types.Package var toFinalize []*types.Package
if !nodeps { if !nodeps {
// TODO: Lower those errors as l.Options.Context.Warning return OrderFinalizers(allRepos, toInstall, solution)
for _, w := range toInstall {
if !fileHelper.Exists(w.Package.Rel(tree.FinalizerFile)) {
continue
}
// Finalizers needs to run in order and in sequence.
ordered, err := solution.Order(allRepos, w.Package.GetFingerPrint())
if err != nil {
return toFinalize, errors.Wrap(err, "While order a solution for "+w.Package.HumanReadableString())
}
ORDER:
for _, ass := range ordered {
if ass.Value {
installed, ok := toInstall[ass.Package.GetFingerPrint()]
if !ok {
// It was a dep already installed in the system, so we can skip it safely
continue ORDER
}
treePackage, err := installed.Repository.GetTree().GetDatabase().FindPackage(ass.Package)
if err != nil {
return toFinalize, errors.Wrap(err, "Error getting package "+ass.Package.HumanReadableString())
}
toFinalize = append(toFinalize, treePackage)
}
}
}
} else { } else {
for _, c := range toInstall { for _, c := range toInstall {
if !fileHelper.Exists(c.Package.Rel(tree.FinalizerFile)) { if !fileHelper.Exists(c.Package.Rel(tree.FinalizerFile)) {

View File

@@ -457,6 +457,14 @@ func (r *LuetSystemRepository) SetVerify(p bool) {
r.LuetRepository.Verify = p r.LuetRepository.Verify = p
} }
func (r *LuetSystemRepository) GetReferenceID() string {
return r.LuetRepository.ReferenceID
}
func (r *LuetSystemRepository) SetReferenceID(ref string) {
r.LuetRepository.ReferenceID = ref
}
func (r *LuetSystemRepository) GetBackend() compiler.CompilerBackend { func (r *LuetSystemRepository) GetBackend() compiler.CompilerBackend {
return r.Backend return r.Backend
} }
@@ -1051,6 +1059,7 @@ func (r *LuetSystemRepository) fill(r2 *LuetSystemRepository) {
r2.SetPriority(r.GetPriority()) r2.SetPriority(r.GetPriority())
r2.SetName(r.GetName()) r2.SetName(r.GetName())
r2.SetVerify(r.GetVerify()) r2.SetVerify(r.GetVerify())
r2.SetReferenceID(r.GetReferenceID())
} }
func (r *LuetSystemRepository) Serialize() (*LuetSystemRepositoryMetadata, LuetSystemRepository) { func (r *LuetSystemRepository) Serialize() (*LuetSystemRepositoryMetadata, LuetSystemRepository) {

View File

@@ -1,5 +1,7 @@
package toposort package toposort
import "sort"
type Graph struct { type Graph struct {
nodes []string nodes []string
outputs map[string]map[string]int outputs map[string]map[string]int
@@ -59,7 +61,7 @@ func (g *Graph) RemoveEdge(from, to string) bool {
return true return true
} }
func (g *Graph) Toposort() ([]string, bool) { func (g *Graph) toposort(stable bool) ([]string, bool) {
L := make([]string, 0, len(g.nodes)) L := make([]string, 0, len(g.nodes))
S := make([]string, 0, len(g.nodes)) S := make([]string, 0, len(g.nodes))
@@ -69,6 +71,10 @@ func (g *Graph) Toposort() ([]string, bool) {
} }
} }
if stable {
sort.Strings(S)
}
for len(S) > 0 { for len(S) > 0 {
var n string var n string
n, S = S[0], S[1:] n, S = S[0], S[1:]
@@ -79,6 +85,10 @@ func (g *Graph) Toposort() ([]string, bool) {
ms[i-1] = m ms[i-1] = m
} }
if stable {
sort.Strings(ms)
}
for _, m := range ms { for _, m := range ms {
g.unsafeRemoveEdge(n, m) g.unsafeRemoveEdge(n, m)
@@ -99,3 +109,11 @@ func (g *Graph) Toposort() ([]string, bool) {
return L, true return L, true
} }
func (g *Graph) Toposort() ([]string, bool) {
return g.toposort(false)
}
func (g *Graph) ToposortStable() ([]string, bool) {
return g.toposort(true)
}

4
vendor/modules.txt vendored
View File

@@ -370,7 +370,6 @@ github.com/pelletier/go-toml
github.com/peterbourgon/diskv github.com/peterbourgon/diskv
# github.com/philopon/go-toposort v0.0.0-20170620085441-9be86dbd762f # github.com/philopon/go-toposort v0.0.0-20170620085441-9be86dbd762f
## explicit ## explicit
github.com/philopon/go-toposort
# github.com/pkg/errors v0.9.1 # github.com/pkg/errors v0.9.1
## explicit ## explicit
github.com/pkg/errors github.com/pkg/errors
@@ -402,6 +401,9 @@ github.com/rogpeppe/go-internal/internal/syscall/windows
github.com/rogpeppe/go-internal/internal/syscall/windows/sysdll github.com/rogpeppe/go-internal/internal/syscall/windows/sysdll
github.com/rogpeppe/go-internal/lockedfile github.com/rogpeppe/go-internal/lockedfile
github.com/rogpeppe/go-internal/lockedfile/internal/filelock github.com/rogpeppe/go-internal/lockedfile/internal/filelock
# github.com/scrohde/go-toposort v0.0.0-20170629203416-a9a902e065a3
## explicit
github.com/scrohde/go-toposort
# github.com/shopspring/decimal v1.2.0 # github.com/shopspring/decimal v1.2.0
github.com/shopspring/decimal github.com/shopspring/decimal
# github.com/sirupsen/logrus v1.8.1 # github.com/sirupsen/logrus v1.8.1