Compare commits

..

3 commits

Author SHA1 Message Date
af70a267f5 test(e2e): introduce e2e test framework with local forgejo 2025-06-15 21:56:18 +02:00
6ed133946e feat(forge): add new forge for forgejo
We only support repositories hosted on Forgejo instances, but not
Forgejo Actions or Woodpecker as CI solutions for now.
2025-06-15 21:56:15 +02:00
f3269de8fe refactor(cmd): use factories instead of global cobra command structs
This enables us to create new commands for e2e tests.
2025-06-15 16:28:54 +02:00
14 changed files with 218 additions and 348 deletions

View file

@ -41,35 +41,6 @@ jobs:
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: unit
test-e2e-forgejo:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
with:
go-version-file: go.mod
# We can not use "jobs.<job>.services".
# We want to mount the config file, which is only available after "Checkout".
- name: Start Forgejo
working-directory: test/e2e/forgejo
run: docker compose up --wait
- name: Run tests
run: go test -tags e2e_forgejo -v -race -coverpkg=./... -coverprofile=coverage.txt ./test/e2e/forgejo
- name: Upload results to Codecov
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: e2e
go-mod-tidy:
runs-on: ubuntu-latest

View file

@ -5,15 +5,14 @@ import (
"log/slog"
"strings"
"github.com/apricote/releaser-pleaser/internal/forge/forgejo"
"github.com/spf13/cobra"
rp "github.com/apricote/releaser-pleaser"
"github.com/apricote/releaser-pleaser/internal/commitparser/conventionalcommits"
"github.com/apricote/releaser-pleaser/internal/forge"
"github.com/apricote/releaser-pleaser/internal/forge/forgejo"
"github.com/apricote/releaser-pleaser/internal/forge/github"
"github.com/apricote/releaser-pleaser/internal/forge/gitlab"
"github.com/apricote/releaser-pleaser/internal/log"
"github.com/apricote/releaser-pleaser/internal/updater"
"github.com/apricote/releaser-pleaser/internal/versioning"
)
@ -35,11 +34,10 @@ func newRunCommand() *cobra.Command {
Use: "run",
RunE: func(cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()
logger := log.GetLogger(cmd.ErrOrStderr())
var err error
logger.DebugContext(ctx, "run called",
slog.DebugContext(ctx, "run called",
"forge", flagForge,
"branch", flagBranch,
"owner", flagOwner,
@ -55,8 +53,8 @@ func newRunCommand() *cobra.Command {
switch flagForge {
case "gitlab":
logger.DebugContext(ctx, "using forge GitLab")
f, err = gitlab.New(logger, &gitlab.Options{
slog.DebugContext(ctx, "using forge GitLab")
f, err = gitlab.New(slog.Default(), &gitlab.Options{
Options: forgeOptions,
Path: fmt.Sprintf("%s/%s", flagOwner, flagRepo),
})
@ -65,15 +63,15 @@ func newRunCommand() *cobra.Command {
return fmt.Errorf("failed to create gitlab client: %w", err)
}
case "github":
logger.DebugContext(ctx, "using forge GitHub")
f = github.New(logger, &github.Options{
slog.DebugContext(ctx, "using forge GitHub")
f = github.New(slog.Default(), &github.Options{
Options: forgeOptions,
Owner: flagOwner,
Repo: flagRepo,
})
case "forgejo":
logger.DebugContext(ctx, "using forge Forgejo")
f, err = forgejo.New(logger, &forgejo.Options{
slog.DebugContext(ctx, "using forge Forgejo")
f, err = forgejo.New(slog.Default(), &forgejo.Options{
Options: forgeOptions,
Owner: flagOwner,
Repo: flagRepo,
@ -83,7 +81,7 @@ func newRunCommand() *cobra.Command {
Username: flagUsername,
})
if err != nil {
logger.ErrorContext(ctx, "failed to create client", "err", err)
slog.ErrorContext(ctx, "failed to create client", "err", err)
return fmt.Errorf("failed to create forgejo client: %w", err)
}
default:
@ -94,9 +92,9 @@ func newRunCommand() *cobra.Command {
releaserPleaser := rp.New(
f,
logger,
slog.Default(),
flagBranch,
conventionalcommits.NewParser(logger),
conventionalcommits.NewParser(slog.Default()),
versioning.SemVer,
extraFiles,
[]updater.NewUpdater{updater.Generic},

View file

@ -1,2 +0,0 @@
ignore:
- "test"

4
go.mod
View file

@ -1,6 +1,6 @@
module github.com/apricote/releaser-pleaser
go 1.24
go 1.23.2
toolchain go1.24.4
@ -55,4 +55,4 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 => codeberg.org/apricote/forgejo-sdk/forgejo/v2 v2.1.2-0.20250615152743-47d3f0434561
replace codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 => ../../../codeberg.org/mvdkleijn/forgejo-sdk/forgejo

4
go.sum
View file

@ -1,5 +1,5 @@
codeberg.org/apricote/forgejo-sdk/forgejo/v2 v2.1.2-0.20250615152743-47d3f0434561 h1:ZFGmrGQ7cd2mbSLrfjrj3COwPKFfKM6sDO/IsrGDW7w=
codeberg.org/apricote/forgejo-sdk/forgejo/v2 v2.1.2-0.20250615152743-47d3f0434561/go.mod h1:2i9GsyawlJtVMO5pTS/Om5uo2O3JN/eCjGWy5v15NGg=
codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 v2.1.0 h1:BsxZzd3nPPMyv8r6W964OL7XgTpsXvxO8f0vBTWpPok=
codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 v2.1.0/go.mod h1:2i9GsyawlJtVMO5pTS/Om5uo2O3JN/eCjGWy5v15NGg=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/42wim/httpsig v1.2.3 h1:xb0YyWhkYj57SPtfSttIobJUPJZB9as1nsfo7KWVcEs=

View file

@ -8,14 +8,13 @@ import (
"strings"
"codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2"
"github.com/blang/semver/v4"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/apricote/releaser-pleaser/internal/forge"
"github.com/apricote/releaser-pleaser/internal/git"
"github.com/apricote/releaser-pleaser/internal/pointer"
"github.com/apricote/releaser-pleaser/internal/releasepr"
"github.com/blang/semver/v4"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/plumbing/transport/http"
)
const ()
@ -147,7 +146,7 @@ func (f *Forgejo) CommitsSince(ctx context.Context, tag *git.Tag) ([]git.Commit,
return commits, nil
}
func (f *Forgejo) commitsSinceTag(_ context.Context, tag *git.Tag) ([]*forgejo.Commit, error) {
func (f *Forgejo) commitsSinceTag(ctx context.Context, tag *git.Tag) ([]*forgejo.Commit, error) {
head := f.options.BaseBranch
log := f.log.With("base", tag.Hash, "head", head)
log.Debug("comparing commits")
@ -162,7 +161,7 @@ func (f *Forgejo) commitsSinceTag(_ context.Context, tag *git.Tag) ([]*forgejo.C
return compare.Commits, nil
}
func (f *Forgejo) commitsSinceInit(_ context.Context) ([]*forgejo.Commit, error) {
func (f *Forgejo) commitsSinceInit(ctx context.Context) ([]*forgejo.Commit, error) {
head := f.options.BaseBranch
log := f.log.With("head", head)
log.Debug("listing all commits")
@ -183,7 +182,7 @@ func (f *Forgejo) commitsSinceInit(_ context.Context) ([]*forgejo.Commit, error)
return repositoryCommits, nil
}
func (f *Forgejo) prForCommit(_ context.Context, commit git.Commit) (*git.PullRequest, error) {
func (f *Forgejo) prForCommit(ctx context.Context, commit git.Commit) (*git.PullRequest, 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,
@ -196,17 +195,13 @@ func (f *Forgejo) prForCommit(_ context.Context, commit git.Commit) (*git.PullRe
commit.Hash,
)
if err != nil {
if strings.HasPrefix(err.Error(), "pull request does not exist") {
return nil, nil
}
return nil, err
}
return forgejoPRToPullRequest(pullRequest), nil
}
func (f *Forgejo) EnsureLabelsExist(_ context.Context, labels []releasepr.Label) error {
func (f *Forgejo) EnsureLabelsExist(ctx context.Context, labels []releasepr.Label) error {
f.log.Debug("fetching labels on repo")
fLabels, err := all(func(listOptions forgejo.ListOptions) ([]*forgejo.Label, *forgejo.Response, error) {
return f.client.ListRepoLabels(
@ -239,7 +234,7 @@ func (f *Forgejo) EnsureLabelsExist(_ context.Context, labels []releasepr.Label)
return nil
}
func (f *Forgejo) PullRequestForBranch(_ context.Context, branch string) (*releasepr.ReleasePullRequest, error) {
func (f *Forgejo) PullRequestForBranch(ctx context.Context, branch string) (*releasepr.ReleasePullRequest, error) {
prs, err := all(
func(listOptions forgejo.ListOptions) ([]*forgejo.PullRequest, *forgejo.Response, error) {
return f.client.ListRepoPullRequests(
@ -289,7 +284,7 @@ func (f *Forgejo) CreatePullRequest(ctx context.Context, pr *releasepr.ReleasePu
return nil
}
func (f *Forgejo) UpdatePullRequest(_ context.Context, pr *releasepr.ReleasePullRequest) error {
func (f *Forgejo) UpdatePullRequest(ctx context.Context, pr *releasepr.ReleasePullRequest) error {
_, _, err := f.client.EditPullRequest(
f.options.Owner, f.options.Repo,
int64(pr.ID), forgejo.EditPullRequestOption{
@ -304,15 +299,12 @@ func (f *Forgejo) UpdatePullRequest(_ context.Context, pr *releasepr.ReleasePull
return nil
}
func (f *Forgejo) SetPullRequestLabels(_ context.Context, pr *releasepr.ReleasePullRequest, remove, add []releasepr.Label) error {
func (f *Forgejo) SetPullRequestLabels(ctx context.Context, pr *releasepr.ReleasePullRequest, remove, add []releasepr.Label) error {
allLabels, err := all(
func(listOptions forgejo.ListOptions) ([]*forgejo.Label, *forgejo.Response, error) {
return f.client.ListRepoLabels(f.options.Owner, f.options.Repo, forgejo.ListLabelsOptions{ListOptions: listOptions})
},
)
if err != nil {
return err
}
findLabel := func(labelName string) *forgejo.Label {
for _, fLabel := range allLabels {
@ -360,7 +352,7 @@ func (f *Forgejo) SetPullRequestLabels(_ context.Context, pr *releasepr.ReleaseP
return nil
}
func (f *Forgejo) ClosePullRequest(_ context.Context, pr *releasepr.ReleasePullRequest) error {
func (f *Forgejo) ClosePullRequest(ctx context.Context, pr *releasepr.ReleasePullRequest) error {
_, _, err := f.client.EditPullRequest(
f.options.Owner, f.options.Repo,
int64(pr.ID), forgejo.EditPullRequestOption{
@ -374,7 +366,7 @@ func (f *Forgejo) ClosePullRequest(_ context.Context, pr *releasepr.ReleasePullR
return nil
}
func (f *Forgejo) PendingReleases(_ context.Context, pendingLabel releasepr.Label) ([]*releasepr.ReleasePullRequest, error) {
func (f *Forgejo) PendingReleases(ctx context.Context, pendingLabel releasepr.Label) ([]*releasepr.ReleasePullRequest, error) {
fPRs, err := all(func(listOptions forgejo.ListOptions) ([]*forgejo.PullRequest, *forgejo.Response, error) {
return f.client.ListRepoPullRequests(
f.options.Owner, f.options.Repo,
@ -385,7 +377,6 @@ func (f *Forgejo) PendingReleases(_ context.Context, pendingLabel releasepr.Labe
})
})
if err != nil {
// "The target couldn't be found." means that the repo does not have pull requests activated.
return nil, err
}
@ -411,7 +402,7 @@ func (f *Forgejo) PendingReleases(_ context.Context, pendingLabel releasepr.Labe
return prs, nil
}
func (f *Forgejo) CreateRelease(_ context.Context, commit git.Commit, title, changelog string, preRelease, latest bool) error {
func (f *Forgejo) CreateRelease(ctx context.Context, commit git.Commit, title, changelog string, preRelease, latest bool) error {
// latest can not be set through the API
_, _, err := f.client.CreateRelease(

View file

@ -1,7 +1,6 @@
package log
import (
"io"
"log/slog"
"os"
"time"
@ -9,15 +8,11 @@ import (
"github.com/lmittmann/tint"
)
func GetLogger(w io.Writer) *slog.Logger {
return slog.New(
tint.NewHandler(w, &tint.Options{
func init() {
slog.SetDefault(slog.New(
tint.NewHandler(os.Stderr, &tint.Options{
Level: slog.LevelDebug,
TimeFormat: time.RFC3339,
}),
)
}
func init() {
slog.SetDefault(GetLogger(os.Stderr))
))
}

View file

@ -1,19 +0,0 @@
package e2e
import (
"context"
"testing"
)
type TestForge interface {
Init(ctx context.Context, runID string) error
CreateRepo(t *testing.T, opts CreateRepoOpts) (*Repository, error)
RunArguments() []string
}
type CreateRepoOpts struct {
Name string
Description string
DefaultBranch string
}

View file

@ -8,9 +8,5 @@ services:
- data:/data/gitea
- ./app.ini:/data/gitea/conf/app.ini:ro
healthcheck:
test: ["CMD", "curl", "localhost:3000/api/healthz"]
volumes:
data:

View file

@ -1,111 +0,0 @@
package forgejo
import (
"context"
"fmt"
"log/slog"
"os/exec"
"strings"
"testing"
"codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2"
"github.com/apricote/releaser-pleaser/test/e2e"
)
const (
TestAPIURL = "http://localhost:3000"
TestUserNameTemplate = "rp-%s"
TestUserPassword = "releaser-pleaser"
TestUserEmailTemplate = "releaser-pleaser-%s@example.com"
TestTokenName = "rp"
TestTokenScopes = "write:user,write:issue,write:repository"
)
type TestForge struct {
username string
token string
client *forgejo.Client
}
func (f *TestForge) Init(ctx context.Context, runID string) error {
if err := f.initUser(ctx, runID); err != nil {
return err
}
if err := f.initClient(ctx); err != nil {
return err
}
return nil
}
func (f *TestForge) initUser(ctx context.Context, runID string) error {
f.username = fmt.Sprintf(TestUserNameTemplate, runID)
if output, err := exec.CommandContext(ctx,
"docker", "compose", "exec", "--user=1000", "forgejo",
"forgejo", "admin", "user", "create",
"--username", f.username,
"--password", TestUserPassword,
"--email", fmt.Sprintf(TestUserEmailTemplate, runID),
"--must-change-password=false",
).CombinedOutput(); err != nil {
slog.Debug("create forgejo user output", "output", output)
return fmt.Errorf("failed to create forgejo user: %w", err)
}
token, err := exec.CommandContext(ctx,
"docker", "compose", "exec", "--user=1000", "forgejo",
"forgejo", "admin", "user", "generate-access-token",
"--username", f.username,
"--token-name", TestTokenName,
"--scopes", TestTokenScopes,
"--raw",
).Output()
if err != nil {
return fmt.Errorf("failed to create forgejo token: %w", err)
}
f.token = strings.TrimSpace(string(token))
return nil
}
func (f *TestForge) initClient(ctx context.Context) (err error) {
f.client, err = forgejo.NewClient(TestAPIURL,
forgejo.SetToken(f.token),
forgejo.SetUserAgent("releaser-pleaser-e2e-tests"),
forgejo.SetContext(ctx),
// forgejo.SetDebugMode(),
)
return err
}
func (f *TestForge) CreateRepo(t *testing.T, opts e2e.CreateRepoOpts) (*e2e.Repository, error) {
t.Helper()
repo, _, err := f.client.CreateRepo(forgejo.CreateRepoOption{
Name: opts.Name,
Description: opts.Description,
DefaultBranch: opts.DefaultBranch,
Readme: "Default",
AutoInit: true,
})
if err != nil {
return nil, err
}
return &e2e.Repository{
Name: repo.Name,
}, nil
}
func (f *TestForge) RunArguments() []string {
return []string{"--forge=forgejo",
fmt.Sprintf("--owner=%s", f.username),
fmt.Sprintf("--api-url=%s", TestAPIURL),
fmt.Sprintf("--api-token=%s", f.token),
fmt.Sprintf("--username=%s", f.username),
}
}

View file

@ -1,39 +0,0 @@
//go:build e2e_forgejo
package forgejo
import (
"context"
"log/slog"
"os"
"testing"
"github.com/stretchr/testify/require"
"github.com/apricote/releaser-pleaser/test/e2e"
)
var (
f *e2e.Framework
)
func TestMain(m *testing.M) {
ctx := context.Background()
var err error
f, err = e2e.NewFramework(ctx, &TestForge{})
if err != nil {
slog.Error("failed to set up test framework", "err", err)
}
os.Exit(m.Run())
}
func TestCreateRepository(t *testing.T) {
_ = f.NewRepository(t, t.Name())
}
func TestRun(t *testing.T) {
repo := f.NewRepository(t, t.Name())
require.NoError(t, f.Run(t, repo, []string{}))
}

View file

@ -0,0 +1,147 @@
//go:build e2e_forgejo
package forgejo
import (
"bytes"
"context"
"crypto/rand"
"encoding/hex"
"fmt"
"log/slog"
"os/exec"
"strings"
"testing"
"codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2"
"github.com/apricote/releaser-pleaser/cmd/rp/cmd"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const (
TestAPIURL = "http://localhost:3000"
TestUserNameTemplate = "rp-%s"
TestUserPassword = "releaser-pleaser"
TestUserEmailTemplate = "releaser-pleaser-%s@example.com"
TestTokenName = "rp"
TestTokenScopes = "write:user,write:issue,write:repository"
TestDefaultBranch = "main"
)
var (
TestToken string
TestUserName string
TestClient *forgejo.Client
)
func randomSuffix() string {
bytes := make([]byte, 4)
if _, err := rand.Read(bytes); err != nil {
panic(err)
}
return hex.EncodeToString(bytes)
}
func setupTestUser(ctx context.Context, suffix string) string {
TestUserName = fmt.Sprintf(TestUserNameTemplate, suffix)
if output, err := exec.CommandContext(ctx,
"docker", "compose", "exec", "--user=1000", "forgejo",
"forgejo", "admin", "user", "create",
"--username", TestUserName,
"--password", TestUserPassword,
"--email", fmt.Sprintf(TestUserEmailTemplate, suffix),
"--must-change-password=false",
).CombinedOutput(); err != nil {
slog.ErrorContext(ctx, "failed to create forgejo user", "err", err, "output", output)
panic(err)
}
token, err := exec.CommandContext(ctx,
"docker", "compose", "exec", "--user=1000", "forgejo",
"forgejo", "admin", "user", "generate-access-token",
"--username", TestUserName,
"--token-name", TestTokenName,
"--scopes", TestTokenScopes,
"--raw",
).Output()
if err != nil {
slog.ErrorContext(ctx, "failed to create forgejo token", "err", err)
panic(err)
}
return strings.TrimSpace(string(token))
}
func setupTestClient(ctx context.Context, token string) *forgejo.Client {
client, err := forgejo.NewClient(TestAPIURL,
forgejo.SetToken(token),
forgejo.SetUserAgent("releaser-pleaser-e2e-tests"),
forgejo.SetContext(ctx),
// forgejo.SetDebugMode(),
)
if err != nil {
panic(err)
}
return client
}
type Repository struct {
Name string
}
func NewRepository(t *testing.T, name string) *Repository {
t.Helper()
r := &Repository{
Name: fmt.Sprintf("%s-%s", name, randomSuffix())}
repo, _, err := TestClient.CreateRepo(forgejo.CreateRepoOption{
Name: r.Name,
Description: name,
DefaultBranch: TestDefaultBranch,
})
require.NoError(t, err)
require.NotNil(t, repo)
return r
}
func Run(t *testing.T, r *Repository, extraFiles []string) (stdout bytes.Buffer, stderr bytes.Buffer, err error) {
t.Helper()
ctx := t.Context()
rootCmd := cmd.NewRootCmd()
rootCmd.SetArgs([]string{
"run", "--forge=forgejo",
fmt.Sprintf("--branch=%s", TestDefaultBranch),
fmt.Sprintf("--owner=%s", TestUserName),
fmt.Sprintf("--repo=%s", r.Name),
fmt.Sprintf("--extra-files=%q", strings.Join(extraFiles, "\n")),
fmt.Sprintf("--api-url=%s", TestAPIURL),
fmt.Sprintf("--api-token=%s", TestToken),
fmt.Sprintf("--username=%s", TestUserName),
})
rootCmd.SetOut(&stdout)
rootCmd.SetErr(&stderr)
err = rootCmd.ExecuteContext(ctx)
return stdout, stderr, err
}
func MustRun(t *testing.T, r *Repository, extraFiles []string) {
t.Helper()
stdout, stderr, err := Run(t, r, extraFiles)
if !assert.NoError(t, err) {
t.Log(stdout)
t.Log(stderr)
}
}

View file

@ -0,0 +1,39 @@
//go:build e2e_forgejo
package forgejo
import (
"context"
"os"
"testing"
"github.com/stretchr/testify/require"
)
func TestMain(m *testing.M) {
ctx := context.Background()
suffix := randomSuffix()
TestToken = setupTestUser(ctx, suffix)
TestClient = setupTestClient(ctx, TestToken)
os.Exit(m.Run())
}
func TestAPIAccess(t *testing.T) {
user, _, err := TestClient.GetMyUserInfo()
require.NoError(t, err)
require.NotNil(t, user)
}
func TestCreateRepository(t *testing.T) {
_ = NewRepository(t, t.Name())
}
func TestRun(t *testing.T) {
repo := NewRepository(t, t.Name())
MustRun(t, repo, []string{})
}

View file

@ -1,96 +0,0 @@
package e2e
import (
"bytes"
"context"
"crypto/rand"
"encoding/hex"
"fmt"
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/apricote/releaser-pleaser/cmd/rp/cmd"
)
const (
TestDefaultBranch = "main"
)
func randomString() string {
randomBytes := make([]byte, 4)
if _, err := rand.Read(randomBytes); err != nil {
panic(err)
}
return hex.EncodeToString(randomBytes)
}
type Framework struct {
runID string
forge TestForge
}
func NewFramework(ctx context.Context, forge TestForge) (*Framework, error) {
f := &Framework{
runID: randomString(),
forge: forge,
}
err := forge.Init(ctx, f.runID)
if err != nil {
return nil, err
}
return f, nil
}
type Repository struct {
Name string
}
func (f *Framework) NewRepository(t *testing.T, name string) *Repository {
t.Helper()
r := &Repository{
Name: fmt.Sprintf("%s-%s-%s", name, f.runID, randomString()),
}
repo, err := f.forge.CreateRepo(t, CreateRepoOpts{
Name: r.Name,
Description: name,
DefaultBranch: TestDefaultBranch,
})
require.NoError(t, err)
require.NotNil(t, repo)
return r
}
func (f *Framework) Run(t *testing.T, r *Repository, extraFiles []string) error {
t.Helper()
ctx := t.Context()
rootCmd := cmd.NewRootCmd()
rootCmd.SetArgs(append([]string{
"run",
fmt.Sprintf("--repo=%s", r.Name),
fmt.Sprintf("--extra-files=%q", strings.Join(extraFiles, "\n")),
}, f.forge.RunArguments()...))
var stdout, stderr bytes.Buffer
rootCmd.SetOut(&stdout)
rootCmd.SetErr(&stderr)
err := rootCmd.ExecuteContext(ctx)
stdoutString := stdout.String()
stderrString := stderr.String()
t.Log(stdoutString)
t.Log(stderrString)
return err
}