Vault Automation cccc6f9e4c
Backport [VAULT-39160] actions(hcp): add support for testing custom images on HCP into ce/main (#9433)
[VAULT-39160] actions(hcp): add support for testing custom images on HCP (#9345)

Add support for running the `cloud` scenario with a custom image in the
int HCP environment. We support two new tags that trigger new
functionality. If the `hcp/build-image` tag is present on a PR at the
time of `build`, we'll automatically trigger a custom build for the int
environment. If the `hcp/test` tag is present, we'll trigger a custom
build and run the `cloud` scenario with the resulting image.

* Fix a bug in our custom build pattern to handle prerelease versions.
* pipeline(hcp): add `--github-output` support to `show image` and
  `wait image` commands.
* enos(hcp/create_vault_cluster): use a unique identifier for HVN
  and vault clusters.
* actions(enos-cloud): add workflow to execute the `cloud` enos
  scenario.
* actions(build): add support for triggering a custom build and running
  the `enos-cloud` scenario.
* add more debug logging and query without a status
* add shim build-hcp-image for CE workflows

Signed-off-by: Ryan Cragun <me@ryan.ec>
Co-authored-by: Ryan Cragun <me@ryan.ec>
2025-09-19 09:00:55 -07:00

164 lines
4.2 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package github
import (
"context"
"fmt"
"log/slog"
gh "github.com/google/go-github/v74/github"
slogctx "github.com/veqryn/slog-context"
)
// getWorkflow attempts to locate the workflow associated with our workflow name.
func getWorkflow(
ctx context.Context,
client *gh.Client,
owner string,
repo string,
name string,
) (*gh.Workflow, error) {
slog.Default().DebugContext(slogctx.Append(ctx,
slog.String("owner", owner),
slog.String("repo", repo),
slog.String("name", name),
), "getting github actions workflow")
opts := &gh.ListOptions{PerPage: PerPageMax}
for {
wfs, res, err := client.Actions.ListWorkflows(ctx, owner, repo, opts)
if err != nil {
return nil, err
}
for _, wf := range wfs.Workflows {
if wf.GetName() == name {
return wf, nil
}
}
if res.NextPage == 0 {
return nil, fmt.Errorf("no workflow matching %s could be found", name)
}
opts.Page = res.NextPage
}
}
// getWorkflowRuns gets the workflow runs associated with a workflow ID.
func getWorkflowRuns(
ctx context.Context,
client *gh.Client,
owner string,
repo string,
id int64,
opts *gh.ListWorkflowRunsOptions,
) ([]*WorkflowRun, error) {
var runs []*WorkflowRun
opts.ListOptions = gh.ListOptions{PerPage: PerPageMax}
// By default our status will be "success" which elimates in_progress runs.
// Instead, we'll try both so that we're sure to include what's actually
// running along with historical runs.
for _, status := range []string{"", "success", "in_progress"} {
var runsForStatus []*WorkflowRun
for {
opts.Status = status
slog.Default().DebugContext(slogctx.Append(ctx,
slog.String("owner", owner),
slog.String("repo", repo),
slog.Int64("workflow-id", id),
slog.String("query-status", opts.Status),
), "getting github actions workflow runs")
wfrs, res, err := client.Actions.ListWorkflowRunsByID(ctx, owner, repo, id, opts)
if err != nil {
return nil, err
}
for _, r := range wfrs.WorkflowRuns {
runsForStatus = append(runsForStatus, &WorkflowRun{Run: r})
}
if res.NextPage == 0 {
if len(runsForStatus) > 0 {
slog.Default().DebugContext(slogctx.Append(ctx,
slog.String("owner", owner),
slog.String("repo", repo),
slog.Int64("workflow-id", id),
slog.String("query-status", opts.Status),
slog.Int("count", len(runsForStatus)),
), "found github actions workflow runs")
} else {
slog.Default().DebugContext(slogctx.Append(ctx,
slog.String("owner", owner),
slog.String("repo", repo),
slog.Int64("workflow-id", id),
slog.String("query-status", opts.Status),
), "no github actions workflow runs found for status")
}
runs = append(runs, runsForStatus...)
break
}
opts.ListOptions.Page = res.NextPage
}
}
return runs, nil
}
// getWorkflowRunArtifacts gets the artifacts associated with a workflow run
func getWorkflowRunArtifacts(
ctx context.Context,
client *gh.Client,
owner string,
repo string,
id int64,
) (gh.ArtifactList, error) {
slog.Default().DebugContext(slogctx.Append(ctx,
slog.String("owner", owner),
slog.String("repo", repo),
slog.Int64("run-id", id),
), "getting github actions workflow run artifacts")
opts := &gh.ListOptions{PerPage: PerPageMax}
artifacts := gh.ArtifactList{}
defer func() {
if count := artifacts.GetTotalCount(); count > 0 {
slog.Default().DebugContext(slogctx.Append(ctx,
slog.String("owner", owner),
slog.String("repo", repo),
slog.Int64("run-id", id),
slog.Int64("count", count),
), "found workflow run artifacts")
} else {
slog.Default().DebugContext(slogctx.Append(ctx,
slog.String("owner", owner),
slog.String("repo", repo),
slog.Int64("run-id", id),
), "no workflow run artifacts found")
}
}()
for {
arts, res, err := client.Actions.ListWorkflowRunArtifacts(ctx, owner, repo, id, opts)
if err != nil {
return artifacts, err
}
newTotal := artifacts.GetTotalCount() + arts.GetTotalCount()
artifacts.TotalCount = &newTotal
artifacts.Artifacts = append(artifacts.Artifacts, arts.Artifacts...)
if res.NextPage == 0 {
return artifacts, nil
}
opts.Page = res.NextPage
}
}