vault/tools/pipeline/internal/pkg/changed/file.go
Ryan Cragun 3e9f84e666
[VAULT-36202] pipeline(releases): add releases list active-versions command (#30658)
While working on VAULT-34829 it became apparent that if our new backporter
could know which branches are active and which CE counterparts are active
then we could completely omit the need for `ce` backport labels and instead
automatically backport to corresponding CE branches that are active.

To facilitate that we can re-use our `.release/versions.hcl` file as it is
the current source of truth for our present backport assistant workflow.

Here we add a new `pipeline releases list versions` command that is capable
of decoding that file and optionally displaying it. It will be used in the
next PR that fully implements VAULT-34829.

As part of this work we refactors `pipeline releases` to include a new `list`
sub-command and moved both `list-active-versions` and `versions` to it.

We also include a few small fixes that were noticed:
  - `.release/verions.hcl` was not up-to-date
  - Our cached dynamic config was not getting recreated when the pipeline
    tool changed. That has been fixed so now dynamic config should always
    get recreated when the pipeline binary changes
  - We now initialize a git client when using the `github` sub-command.
    This will be used in more forthcoming work
  - Update our changed file detection to resolve some incorrect groupings
  - Add some additional changed file helpers that we be used in forthcoming
    work

Signed-off-by: Ryan Cragun <me@ryan.ec>
2025-05-20 11:10:24 -06:00

145 lines
3.2 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package changed
import (
"slices"
"strings"
gh "github.com/google/go-github/v68/github"
)
type (
// File is a changed file in a PR or commit
File struct {
File *gh.CommitFile `json:"file,omitempty"`
Groups FileGroups `json:"groups,omitempty"`
}
// Files is a slice of changed files in a PR or commit
Files []*File
// FileGroup is group name describing a class of file the changed file belongs to
FileGroup string
// FileGroup is a set of groups a changed file belongs to. Use FileGroups.Add() instead of append()
// to ensure uniqueness and ordering
FileGroups []FileGroup
)
const (
FileGroupAutopilot FileGroup = "autopilot"
FileGroupChangelog FileGroup = "changelog"
FileGroupCommunity FileGroup = "community"
FileGroupDocs FileGroup = "docs"
FileGroupEnos FileGroup = "enos"
FileGroupEnterprise FileGroup = "enterprise"
FileGroupGoApp FileGroup = "app"
FileGroupGoToolchain FileGroup = "gotoolchain"
FileGroupPipeline FileGroup = "pipeline"
FileGroupProto FileGroup = "proto"
FileGroupTools FileGroup = "tools"
FileGroupWebUI FileGroup = "ui"
)
// Name is the file name of the changed file
func (f *File) Name() string {
if f == nil || f.File == nil {
return ""
}
return f.File.GetFilename()
}
// Add takes a variadic set of groups and adds them to the ordered set of groups
func (g FileGroups) Add(groups ...FileGroup) FileGroups {
for _, group := range groups {
idx, in := g.In(group)
if in {
continue
}
g = slices.Insert(g, idx, group)
}
return g
}
// In takes a group and determines the index and presence of the group in the group set
func (g FileGroups) In(group FileGroup) (int, bool) {
return slices.BinarySearch(g, group)
}
// All takes another FileGroups and determines whether or not all of the groups in the
// in group are included in FileGroups.
func (g FileGroups) All(groups FileGroups) bool {
for _, group := range groups {
if _, in := g.In(group); !in {
return false
}
}
return true
}
// Any takes another FileGroups and determines whether or not any of the groups in the
// in group are included in FileGroups.
func (g FileGroups) Any(groups FileGroups) bool {
for _, group := range groups {
if _, in := g.In(group); in {
return true
}
}
return false
}
// Groups returns the FileGroups as a slice of strings
func (g FileGroups) Groups() []string {
groups := []string{}
for _, g := range g {
groups = append(groups, string(g))
}
return groups
}
// String is a string representation of all groups a file is in
func (g FileGroups) String() string {
return strings.Join(g.Groups(), ", ")
}
// Names returns a list of file names
func (f Files) Names() []string {
if len(f) < 1 {
return nil
}
files := []string{}
for _, file := range f {
files = append(files, file.Name())
}
return files
}
// EachHasAnyGroup determines whether each file contains the any of the given groups
func (f Files) EachHasAnyGroup(groups FileGroups) bool {
if f == nil {
return false
}
if len(groups) == 0 {
return true
}
for _, file := range f {
if file.Groups == nil {
return false
}
if !file.Groups.Any(groups) {
return false
}
}
return true
}