mirror of
https://github.com/hashicorp/vault.git
synced 2025-11-14 15:21:11 +01:00
* [VAULT-39424] pipeline(close-origin-pr): add support for closing the origin of copied PRs
When we copy a community contributed Pull Request to Enterprise the
source PR is effectively orphaned, leaving the original PR still
opened, the author unsure of what state the copied PR is in, and any
issues associated with it open.
When the copied PR is closed we ought to close the origin PR if it's
still open, and any other issues that might be associated with either
the origin PR or the copied PR.
We can also add comments to both PRs that include links to each other
and the squash commit to make discovery of the work visible to those
with access to both repos. Unfortunately there is no way to know what
the SHA will be when it's synced so we have to rely on the
'Co-Authored-By:' trailers in commit message.
There are some challenges to this:
- The automation should only execute when copied PRs are closed
- How to determine the origin PR from only the copied PR
- How to determine the PR's linked issues (which the v3 REST API does not expose)
We solved them by:
- Requiring the PR HEAD ref to start with `copy/`
- Encoding the origin PR information in the PR HEAD ref.
e.g. `copy/hashicorp/vault/31580/ryan/VAULT-39424-test-ce`
- Using the V4 GraphQL API to determine "closed issue references"
The result is a new `pipeline` CLI command that can close the origin PR,
all of the issues, and write status comments on each PR with links to
everything to establish omnidirectional linking in the Github UI.
```bash
pipeline github close origin-pull-request 9903
```
* fix feedback
---------
Signed-off-by: Ryan Cragun <me@ryan.ec>
Co-authored-by: Ryan Cragun <me@ryan.ec>
87 lines
2.3 KiB
Go
87 lines
2.3 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/google/go-github/v74/github"
|
|
"github.com/hashicorp/vault/tools/pipeline/internal/pkg/git"
|
|
"github.com/shurcooL/githubv4"
|
|
"github.com/spf13/cobra"
|
|
"golang.org/x/oauth2"
|
|
)
|
|
|
|
type githubCommandState struct {
|
|
Git *git.Client
|
|
GithubV3 *github.Client
|
|
GithubV4 *githubv4.Client
|
|
}
|
|
|
|
var githubCmdState = &githubCommandState{
|
|
GithubV3: github.NewClient(nil),
|
|
GithubV4: githubv4.NewClient(nil),
|
|
Git: git.NewClient(git.WithLoadTokenFromEnv()),
|
|
}
|
|
|
|
func newGithubCmd() *cobra.Command {
|
|
githubCmd := &cobra.Command{
|
|
Use: "github",
|
|
Short: "Github commands",
|
|
Long: "Github commands",
|
|
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
|
if token, set := os.LookupEnv("GITHUB_TOKEN"); set {
|
|
githubCmdState.GithubV3 = githubCmdState.GithubV3.WithAuthToken(token)
|
|
githubCmdState.GithubV4 = githubv4.NewClient(
|
|
oauth2.NewClient(context.Background(),
|
|
oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}),
|
|
),
|
|
)
|
|
} else {
|
|
fmt.Println("\x1b[1;33;49mWARNING\x1b[0m: GITHUB_TOKEN has not been set. While not always required for read actions on public repositories you're likely to get throttled without it")
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
githubCmd.AddCommand(newGithubCheckCmd())
|
|
githubCmd.AddCommand(newGithubCloseCmd())
|
|
githubCmd.AddCommand(newGithubCopyCmd())
|
|
githubCmd.AddCommand(newGithubCreateCmd())
|
|
githubCmd.AddCommand(newGithubFindCmd())
|
|
githubCmd.AddCommand(newGithubListCmd())
|
|
githubCmd.AddCommand(newGithubSyncCmd())
|
|
|
|
return githubCmd
|
|
}
|
|
|
|
func writeToGithubOutput(key string, bytes []byte) error {
|
|
devPath, ok := os.LookupEnv("GITHUB_OUTPUT")
|
|
if !ok {
|
|
return fmt.Errorf("$GITHUB_OUTPUT has not been set. Cannot write %s to it", key)
|
|
}
|
|
|
|
expanded, err := filepath.Abs(devPath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to expand $GITHUB_OUTPUT path: %w", err)
|
|
}
|
|
|
|
dev, err := os.OpenFile(expanded, os.O_APPEND|os.O_WRONLY, 0o644)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to open $GITHUB_OUTPUT for writing: %w", err)
|
|
}
|
|
defer func() { _ = dev.Close() }()
|
|
|
|
_, err = dev.Write(append(append([]byte(key+"="), bytes...), []byte("\n")...))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to write key %s to $GITHUB_OUTPUT: %w", key, err)
|
|
}
|
|
|
|
return nil
|
|
}
|