Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion cmd/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var pushCmd = &cobra.Command{
RunE: func(cmd *cobra.Command, args []string) error {
version.LogVersion()
cacheDirectory := cachedirectory.NewCacheDirectory(rootFlags.cacheDir)
return push.Push(cmd.Context(), cacheDirectory, pushFlags.destinationURL, pushFlags.destinationToken, pushFlags.destinationRepository, pushFlags.actionsAdminUser, pushFlags.force, pushFlags.pushSSH, pushFlags.gitURL)
return push.Push(cmd.Context(), cacheDirectory, pushFlags.destinationURL, pushFlags.destinationToken, pushFlags.destinationRepository, pushFlags.actionsAdminUser, pushFlags.force, pushFlags.pushSSH, pushFlags.gitURL, pushFlags.maxTags, pushFlags.includeBranches)
},
}

Expand All @@ -28,6 +28,8 @@ type pushFlagFields struct {
force bool
pushSSH bool
gitURL string
maxTags int
includeBranches bool
}

var pushFlags = pushFlagFields{}
Expand All @@ -48,4 +50,6 @@ func (f *pushFlagFields) Init(cmd *cobra.Command) {
cmd.Flags().BoolVar(&f.pushSSH, "push-ssh", false, "Push Git contents over SSH rather than HTTPS. To use this option you must have SSH access to your GitHub Enterprise instance configured.")
cmd.Flags().StringVar(&f.gitURL, "git-url", "", "Use a custom Git URL for pushing the Action repository contents to.")
cmd.Flags().MarkHidden("git-url")
cmd.Flags().IntVar(&f.maxTags, "max-tags", 50, "Maximum number of most recent tags to synchronize. Set to 0 to sync all tags.")
cmd.Flags().BoolVar(&f.includeBranches, "include-branches", false, "Include all branches in synchronization. By default, only the main branch and tags are synchronized.")
}
2 changes: 1 addition & 1 deletion cmd/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ var syncCmd = &cobra.Command{
if err != nil {
return err
}
err = push.Push(cmd.Context(), cacheDirectory, pushFlags.destinationURL, pushFlags.destinationToken, pushFlags.destinationRepository, pushFlags.actionsAdminUser, pushFlags.force, pushFlags.pushSSH, pushFlags.gitURL)
err = push.Push(cmd.Context(), cacheDirectory, pushFlags.destinationURL, pushFlags.destinationToken, pushFlags.destinationRepository, pushFlags.actionsAdminUser, pushFlags.force, pushFlags.pushSSH, pushFlags.gitURL, pushFlags.maxTags, pushFlags.includeBranches)
if err != nil {
return err
}
Expand Down
133 changes: 126 additions & 7 deletions internal/push/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
"net/url"
"os"
"path/filepath"
"sort"
"strings"
"time"

"github.com/go-git/go-git/v5/plumbing"

Expand Down Expand Up @@ -54,6 +56,8 @@
force bool
pushSSH bool
gitURL string
maxTags int
includeBranches bool
}

func (pushService *pushService) createRepository() (*github.Repository, error) {
Expand Down Expand Up @@ -175,6 +179,103 @@
return splitRefSpecs
}

// Returns the most recent tags up to maxTags limit. If maxTags is 0, returns all tags.
func (pushService *pushService) getAllowedTagsMap(gitRepository *git.Repository) (map[string]bool, error) {
allowedTags := make(map[string]bool)
if pushService.maxTags == 0 {
tags, err := gitRepository.Tags()
if err != nil {
return nil, errors.Wrap(err, "Error listing tags.")
}

Check failure on line 190 in internal/push/push.go

View workflow job for this annotation

GitHub Actions / Lint

File is not properly formatted (goimports)
err = tags.ForEach(func(ref *plumbing.Reference) error {
allowedTags[ref.Name().Short()] = true
return nil
})
return allowedTags, err
}

type tagInfo struct {
name string
time time.Time
}

var tagInfos []tagInfo
tags, err := gitRepository.Tags()
if err != nil {
return nil, errors.Wrap(err, "Error listing tags.")
}

err = tags.ForEach(func(ref *plumbing.Reference) error {
var tagTime time.Time

// First try to get it as an annotated tag
tagObj, err := gitRepository.TagObject(ref.Hash())
if err == nil {
// It's an annotated tag, use the tag date
tagTime = tagObj.Tagger.When
} else {
// It's a lightweight tag, get the commit it points to
commit, err := gitRepository.CommitObject(ref.Hash())
if err != nil {
// If we can't get the commit, use zero time (will be filtered out)
tagTime = time.Time{}
} else {
tagTime = commit.Committer.When
}
}

tagInfos = append(tagInfos, tagInfo{
name: ref.Name().Short(),
time: tagTime,
})
return nil
})

if err != nil {
return nil, err
}

sort.Slice(tagInfos, func(i, j int) bool {
return tagInfos[i].time.After(tagInfos[j].time) // most recent first
})

limit := pushService.maxTags
if limit > len(tagInfos) {
limit = len(tagInfos)
}

for i := 0; i < limit; i++ {
allowedTags[tagInfos[i].name] = true
}

return allowedTags, nil
}

// Determines if a reference should be included based on filtering rules:
// - For tags, only includes those in the allowedTags set.
// - Always includes the main branch.
// - For other branches, includes them only if includeBranches is enabled.
func (pushService *pushService) shouldIncludeReference(ref *plumbing.Reference, allowedTags map[string]bool) bool {
refName := ref.Name().String()

if refName == "refs/heads/main" {
return true
}

if strings.HasPrefix(refName, "refs/tags/") {
tagName := ref.Name().Short()
return allowedTags[tagName]
}

if strings.HasPrefix(refName, "refs/heads/") {
return pushService.includeBranches
}

// No other refs are currently pulled, but include any that might in the future to be safe.
return true
}

func (pushService *pushService) pushGit(repository *github.Repository, initialPush bool) error {
remoteURL := pushService.gitURL
if remoteURL == "" {
Expand Down Expand Up @@ -231,14 +332,22 @@
if err != nil {
return errors.Wrap(err, "Error reading releases.")
}

allowedTags, err := pushService.getAllowedTagsMap(gitRepository)
if err != nil {
return err
}

initialRefSpecs := []config.RefSpec{}
for _, releasePathStat := range releasePathStats {
tagReferenceName := plumbing.NewTagReferenceName(releasePathStat.Name())
_, err := gitRepository.Reference(tagReferenceName, true)
if err != nil {
return errors.Wrapf(err, "Error finding local tag reference %s.", tagReferenceName)
if allowedTags[releasePathStat.Name()] {
tagReferenceName := plumbing.NewTagReferenceName(releasePathStat.Name())
_, err := gitRepository.Reference(tagReferenceName, true)
if err != nil {
return errors.Wrapf(err, "Error finding local tag reference %s.", tagReferenceName)
}
initialRefSpecs = append(initialRefSpecs, config.RefSpec("+"+tagReferenceName.String()+":"+tagReferenceName.String()))
}
initialRefSpecs = append(initialRefSpecs, config.RefSpec("+"+tagReferenceName.String()+":"+tagReferenceName.String()))
}
refSpecBatches = append(refSpecBatches, splitLargeRefSpecs(initialRefSpecs)...)
} else {
Expand All @@ -248,14 +357,22 @@
config.RefSpec(defaultBranchRefSpec),
},
)

allowedTags, err := pushService.getAllowedTagsMap(gitRepository)
if err != nil {
return err
}

nonDefaultRefSpecs := []config.RefSpec{}
localReferences, err := gitRepository.References()
if err != nil {
return errors.Wrap(err, "Error listing local references.")
}
localReferences.ForEach(func(ref *plumbing.Reference) error {
if ref.Name().String() != defaultBranchRef && strings.HasPrefix(ref.Name().String(), "refs/") {
nonDefaultRefSpecs = append(nonDefaultRefSpecs, config.RefSpec("+"+ref.Name().String()+":"+ref.Name().String()))
if pushService.shouldIncludeReference(ref, allowedTags) {
nonDefaultRefSpecs = append(nonDefaultRefSpecs, config.RefSpec("+"+ref.Name().String()+":"+ref.Name().String()))
}
}
return nil
})
Expand Down Expand Up @@ -432,7 +549,7 @@
return nil
}

func Push(ctx context.Context, cacheDirectory cachedirectory.CacheDirectory, destinationURL string, destinationToken string, destinationRepository string, actionsAdminUser string, force bool, pushSSH bool, gitURL string) error {
func Push(ctx context.Context, cacheDirectory cachedirectory.CacheDirectory, destinationURL string, destinationToken string, destinationRepository string, actionsAdminUser string, force bool, pushSSH bool, gitURL string, maxTags int, includeBranches bool) error {
err := cacheDirectory.CheckOrCreateVersionFile(false, version.Version())
if err != nil {
return err
Expand Down Expand Up @@ -491,6 +608,8 @@
force: force,
pushSSH: pushSSH,
gitURL: gitURL,
maxTags: maxTags,
includeBranches: includeBranches,
}

repository, err := pushService.createRepository()
Expand Down
2 changes: 2 additions & 0 deletions internal/push/push_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
destinationRepositoryOwner: "destination-repository-owner",
destinationRepositoryName: "destination-repository-name",
destinationToken: &token,
maxTags: 0, // no tag limit

Check failure on line 44 in internal/push/push_test.go

View workflow job for this annotation

GitHub Actions / Lint

File is not properly formatted (goimports)
includeBranches: true,
}
}

Expand Down
Loading