mirror of
https://github.com/apricote/releaser-pleaser.git
synced 2026-01-13 13:21:00 +00:00
feat: get matching PRs from GitHub
This commit is contained in:
parent
d7136c1f64
commit
a06bbec1f6
5 changed files with 253 additions and 34 deletions
|
|
@ -17,11 +17,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var logger *slog.Logger
|
||||||
|
|
||||||
// rootCmd represents the base command when called without any subcommands
|
// rootCmd represents the base command when called without any subcommands
|
||||||
var rootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
Use: "releaser-pleaser",
|
Use: "releaser-pleaser",
|
||||||
|
|
@ -47,6 +50,10 @@ func Execute() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
logger = slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
|
||||||
|
Level: slog.LevelDebug,
|
||||||
|
}))
|
||||||
|
|
||||||
// Here you will define your flags and configuration settings.
|
// Here you will define your flags and configuration settings.
|
||||||
// Cobra supports persistent flags, which, if defined here,
|
// Cobra supports persistent flags, which, if defined here,
|
||||||
// will be global for your application.
|
// will be global for your application.
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,7 @@ package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
rp "github.com/apricote/releaser-pleaser"
|
rp "github.com/apricote/releaser-pleaser"
|
||||||
|
|
@ -21,8 +18,10 @@ var runCmd = &cobra.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
flagForge string
|
flagForge string
|
||||||
flagRepo string
|
flagBranch string
|
||||||
|
flagOwner string
|
||||||
|
flagRepo string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
@ -31,51 +30,60 @@ func init() {
|
||||||
// Here you will define your flags and configuration settings.
|
// Here you will define your flags and configuration settings.
|
||||||
|
|
||||||
runCmd.PersistentFlags().StringVar(&flagForge, "forge", "", "")
|
runCmd.PersistentFlags().StringVar(&flagForge, "forge", "", "")
|
||||||
|
runCmd.PersistentFlags().StringVar(&flagBranch, "branch", "main", "")
|
||||||
|
runCmd.PersistentFlags().StringVar(&flagOwner, "owner", "", "")
|
||||||
runCmd.PersistentFlags().StringVar(&flagRepo, "repo", "", "")
|
runCmd.PersistentFlags().StringVar(&flagRepo, "repo", "", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func run(cmd *cobra.Command, args []string) error {
|
func run(cmd *cobra.Command, args []string) error {
|
||||||
|
ctx := cmd.Context()
|
||||||
|
|
||||||
var f rp.Forge
|
var f rp.Forge
|
||||||
|
|
||||||
forgeOptions := rp.ForgeOptions{
|
forgeOptions := rp.ForgeOptions{
|
||||||
Repository: flagRepo,
|
Repository: flagRepo,
|
||||||
|
BaseBranch: flagBranch,
|
||||||
}
|
}
|
||||||
|
|
||||||
switch flagForge {
|
switch flagForge {
|
||||||
case "gitlab":
|
//case "gitlab":
|
||||||
f = rp.NewGitLab(forgeOptions)
|
//f = rp.NewGitLab(forgeOptions)
|
||||||
case "github":
|
case "github":
|
||||||
f = rp.NewGitHub(forgeOptions)
|
f = rp.NewGitHub(logger, &rp.GitHubOptions{
|
||||||
|
ForgeOptions: forgeOptions,
|
||||||
|
Owner: flagOwner,
|
||||||
|
Repo: flagRepo,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("Repo URL: " + f.RepoURL())
|
tag, err := f.LatestTag(ctx)
|
||||||
|
|
||||||
//repo, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{
|
|
||||||
// URL: .RepoURL(),
|
|
||||||
// SingleBranch: true,
|
|
||||||
// Depth: CommitSearchDepth,
|
|
||||||
//})
|
|
||||||
repo, err := git.PlainOpen("~/git/listory")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
commits, previousTag, err := rp.ReleasableCommits(repo)
|
logger.InfoContext(ctx, "Latest Tag", "tag.hash", tag.Hash, "tag.name", tag.Name)
|
||||||
|
|
||||||
|
releaseableCommits, err := f.CommitsSince(ctx, tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
analyzedCommits, versionBump, err := rp.AnalyzeCommits(commits)
|
logger.InfoContext(ctx, "Found releasable commits", "length", len(releaseableCommits))
|
||||||
|
|
||||||
|
changesets, err := f.Changesets(ctx, releaseableCommits)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, commit := range analyzedCommits {
|
logger.InfoContext(ctx, "Found changesets", "length", len(changesets))
|
||||||
title, _, _ := strings.Cut(commit.Message, "\n")
|
|
||||||
fmt.Printf("%s %s\n", commit.Hash, title)
|
for _, changeset := range changesets {
|
||||||
|
fmt.Printf("%s %s\n", changeset.Identifier, changeset.URL)
|
||||||
|
for _, entry := range changeset.ChangelogEntries {
|
||||||
|
fmt.Printf(" - %s %s\n", entry.Hash, entry.Description)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fmt.Printf("Previous Tag: %s\n", previousTag.Name)
|
fmt.Printf("Previous Tag: %s\n", tag.Name)
|
||||||
fmt.Printf("Recommended Bump: %v\n", versionBump)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
213
forge.go
213
forge.go
|
|
@ -1,38 +1,231 @@
|
||||||
package rp
|
package rp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"github.com/google/go-github/v63/github"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Changeset struct {
|
||||||
|
URL string
|
||||||
|
Identifier string
|
||||||
|
ChangelogEntries []AnalyzedCommit
|
||||||
|
}
|
||||||
|
|
||||||
type Forge interface {
|
type Forge interface {
|
||||||
RepoURL() string
|
RepoURL() string
|
||||||
|
|
||||||
|
// LatestTag returns the last tag created on the main branch. If no tag is found, it returns nil.
|
||||||
|
LatestTag(context.Context) (*Tag, error)
|
||||||
|
|
||||||
|
// CommitsSince returns all commits to main branch after the Tag. The tag can be `nil`, in which case this
|
||||||
|
// function should return all commits.
|
||||||
|
CommitsSince(context.Context, *Tag) ([]Commit, error)
|
||||||
|
|
||||||
|
// Changesets looks up the Pull/Merge Requests for each commit, returning its parsed metadata.
|
||||||
|
Changesets(context.Context, []Commit) ([]Changeset, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ForgeOptions struct {
|
type ForgeOptions struct {
|
||||||
Repository string
|
Repository string
|
||||||
|
BaseBranch string
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Forge = &GitHub{}
|
var _ Forge = &GitHub{}
|
||||||
var _ Forge = &GitLab{}
|
|
||||||
|
// var _ Forge = &GitLab{}
|
||||||
|
|
||||||
type GitHub struct {
|
type GitHub struct {
|
||||||
options ForgeOptions
|
options *GitHubOptions
|
||||||
|
|
||||||
|
client *github.Client
|
||||||
|
log *slog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitHub) RepoURL() string {
|
func (g *GitHub) RepoURL() string {
|
||||||
return fmt.Sprintf("https://github.com/%s", g.options.Repository)
|
return fmt.Sprintf("https://github.com/%s", g.options.Repository)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitHub) autodiscover() {
|
func (g *GitHub) LatestTag(ctx context.Context) (*Tag, error) {
|
||||||
// Read settings from GitHub Actions env vars
|
g.log.Debug("listing all tags in github repository")
|
||||||
}
|
// We only get the first page because the latest tag is returned as the first item
|
||||||
|
tags, _, err := g.client.Repositories.ListTags(ctx, g.options.Owner, g.options.Repo, nil)
|
||||||
func NewGitHub(options ForgeOptions) *GitHub {
|
if err != nil {
|
||||||
gh := &GitHub{
|
return nil, err
|
||||||
options: options,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gh.autodiscover()
|
if len(tags) > 0 {
|
||||||
|
// TODO: Is tags sorted?
|
||||||
|
tag := tags[0]
|
||||||
|
return &Tag{
|
||||||
|
Hash: tag.GetCommit().GetSHA(),
|
||||||
|
Name: tag.GetName(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GitHub) CommitsSince(ctx context.Context, tag *Tag) ([]Commit, error) {
|
||||||
|
var repositoryCommits []*github.RepositoryCommit
|
||||||
|
var err error
|
||||||
|
if tag != nil {
|
||||||
|
repositoryCommits, err = g.commitsSinceTag(ctx, tag)
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var commits = make([]Commit, 0, len(repositoryCommits))
|
||||||
|
for _, ghCommit := range repositoryCommits {
|
||||||
|
commits = append(commits, Commit{
|
||||||
|
Hash: ghCommit.GetSHA(),
|
||||||
|
Message: ghCommit.GetCommit().GetMessage(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return commits, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GitHub) commitsSinceTag(ctx context.Context, tag *Tag) ([]*github.RepositoryCommit, error) {
|
||||||
|
head := g.options.BaseBranch
|
||||||
|
log := g.log.With("base", tag.Hash, "head", head)
|
||||||
|
log.Debug("comparing commits", "base", tag.Hash, "head", head)
|
||||||
|
|
||||||
|
page := 1
|
||||||
|
|
||||||
|
var repositoryCommits []*github.RepositoryCommit
|
||||||
|
for {
|
||||||
|
log.Debug("fetching page", "page", page)
|
||||||
|
comparison, resp, err := g.client.Repositories.CompareCommits(
|
||||||
|
ctx, g.options.Owner, g.options.Repo,
|
||||||
|
tag.Hash, head, &github.ListOptions{
|
||||||
|
Page: page,
|
||||||
|
PerPage: 100,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if repositoryCommits == nil {
|
||||||
|
// Pre-initialize slice on first request
|
||||||
|
log.Debug("found commits", "length", comparison.GetTotalCommits())
|
||||||
|
repositoryCommits = make([]*github.RepositoryCommit, 0, comparison.GetTotalCommits())
|
||||||
|
}
|
||||||
|
|
||||||
|
repositoryCommits = append(repositoryCommits, comparison.Commits...)
|
||||||
|
|
||||||
|
if page == resp.LastPage || resp.LastPage == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
page = resp.NextPage
|
||||||
|
}
|
||||||
|
|
||||||
|
return repositoryCommits, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GitHub) Changesets(ctx context.Context, commits []Commit) ([]Changeset, error) {
|
||||||
|
// We naively look up the associated PR for each commit through the "List pull requests associated with a commit"
|
||||||
|
// endpoint. This requires len(commits) requests.
|
||||||
|
// Using the "List pull requests" endpoint might be faster, as it allows us to fetch 100 arbitrary PRs per request,
|
||||||
|
// but worst case we need to look up all PRs made in the repository ever.
|
||||||
|
|
||||||
|
changesets := make([]Changeset, 0, len(commits))
|
||||||
|
|
||||||
|
for _, commit := range commits {
|
||||||
|
log := g.log.With("commit.hash", commit.Hash)
|
||||||
|
page := 1
|
||||||
|
var associatedPRs []*github.PullRequest
|
||||||
|
|
||||||
|
for {
|
||||||
|
log.Debug("fetching pull requests associated with commit", "page", page)
|
||||||
|
prs, resp, err := g.client.PullRequests.ListPullRequestsWithCommit(
|
||||||
|
ctx, g.options.Owner, g.options.Repo,
|
||||||
|
commit.Hash, &github.ListOptions{
|
||||||
|
Page: page,
|
||||||
|
PerPage: 100,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
associatedPRs = append(associatedPRs, prs...)
|
||||||
|
|
||||||
|
if page == resp.LastPage || resp.LastPage == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
page = resp.NextPage
|
||||||
|
}
|
||||||
|
|
||||||
|
var pullrequest *github.PullRequest
|
||||||
|
for _, pr := range associatedPRs {
|
||||||
|
// We only look for the PR that has this commit set as the "merge commit" => The result of squashing this branch onto main
|
||||||
|
if pr.GetMergeCommitSHA() == commit.Hash {
|
||||||
|
pullrequest = pr
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pullrequest == nil {
|
||||||
|
log.Warn("did not find associated pull request, not considering it for changesets")
|
||||||
|
// No pull request was found for this commit, nothing to do here
|
||||||
|
// TODO: We could also return the minimal changeset for this commit, so at least it would come up in the changelog.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log = log.With("pullrequest.id", pullrequest.GetID())
|
||||||
|
|
||||||
|
// TODO: Parse PR description for overrides
|
||||||
|
changelogEntries, _, err := AnalyzeCommits([]Commit{commit})
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("unable to parse changelog entries", "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(changelogEntries) > 0 {
|
||||||
|
changesets = append(changesets, Changeset{
|
||||||
|
URL: pullrequest.GetHTMLURL(),
|
||||||
|
Identifier: fmt.Sprintf("#%d", pullrequest.GetNumber()),
|
||||||
|
ChangelogEntries: changelogEntries,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changesets, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GitHubOptions) autodiscover() {
|
||||||
|
// TODO: Read settings from GitHub Actions env vars
|
||||||
|
}
|
||||||
|
|
||||||
|
type GitHubOptions struct {
|
||||||
|
ForgeOptions
|
||||||
|
|
||||||
|
Owner string
|
||||||
|
Repo string
|
||||||
|
|
||||||
|
APIToken string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGitHub(log *slog.Logger, options *GitHubOptions) *GitHub {
|
||||||
|
options.autodiscover()
|
||||||
|
|
||||||
|
client := github.NewClient(nil)
|
||||||
|
if options.APIToken != "" {
|
||||||
|
client = client.WithAuthToken(options.APIToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
gh := &GitHub{
|
||||||
|
options: options,
|
||||||
|
|
||||||
|
client: client,
|
||||||
|
log: log.With("forge", "github"),
|
||||||
|
}
|
||||||
|
|
||||||
return gh
|
return gh
|
||||||
}
|
}
|
||||||
|
|
|
||||||
5
go.mod
5
go.mod
|
|
@ -5,6 +5,7 @@ go 1.22.4
|
||||||
require (
|
require (
|
||||||
github.com/go-git/go-billy/v5 v5.5.0
|
github.com/go-git/go-billy/v5 v5.5.0
|
||||||
github.com/go-git/go-git/v5 v5.12.0
|
github.com/go-git/go-git/v5 v5.12.0
|
||||||
|
github.com/leodido/go-conventionalcommits v0.12.0
|
||||||
github.com/spf13/cobra v1.8.1
|
github.com/spf13/cobra v1.8.1
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
)
|
)
|
||||||
|
|
@ -19,12 +20,14 @@ require (
|
||||||
github.com/emirpasic/gods v1.18.1 // indirect
|
github.com/emirpasic/gods v1.18.1 // indirect
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
|
github.com/google/go-github/v63 v63.0.0 // indirect
|
||||||
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||||
github.com/leodido/go-conventionalcommits v0.12.0 // indirect
|
|
||||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/rwtodd/Go.Sed v0.0.0-20230610052213-ba3e9c186f0a // indirect
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
github.com/skeema/knownhosts v1.2.2 // indirect
|
github.com/skeema/knownhosts v1.2.2 // indirect
|
||||||
|
|
|
||||||
8
go.sum
8
go.sum
|
|
@ -35,8 +35,13 @@ github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZt
|
||||||
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
|
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/go-github/v63 v63.0.0 h1:13xwK/wk9alSokujB9lJkuzdmQuVn2QCPeck76wR3nE=
|
||||||
|
github.com/google/go-github/v63 v63.0.0/go.mod h1:IqbcrgUmIcEaioWrGYei/09o+ge5vhffGOcxrO0AfmA=
|
||||||
|
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||||
|
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||||
|
|
@ -63,6 +68,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/rwtodd/Go.Sed v0.0.0-20230610052213-ba3e9c186f0a h1:URwYffGNuBQkfwkcn+1CZhb8IE/mKSXxPXp/zzQsn80=
|
||||||
|
github.com/rwtodd/Go.Sed v0.0.0-20230610052213-ba3e9c186f0a/go.mod h1:c6qgHcSUeSISur4+Kcf3WYTvpL07S8eAsoP40hDiQ1I=
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
|
@ -146,6 +153,7 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue