Skip to content
Merged
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
3 changes: 2 additions & 1 deletion config/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ func defaultBranch(rules []BranchRule, fallback string) string {
func issues(assignmentKey string) *IssueReplication {
replicate := viper.GetBool(assignmentKey + ".issues.replicateFromStartercode")
numbers := viper.GetIntSlice(assignmentKey + ".issues.issueNumbers")
includeChildTasks := viper.GetBool(assignmentKey + ".issues.includeChildTasks")

// Legacy compatibility for old startercode issue replication config.
if !replicate && !viper.IsSet(assignmentKey+".issues") {
Expand All @@ -196,7 +197,7 @@ func issues(assignmentKey string) *IssueReplication {
numbers = []int{1}
}

return &IssueReplication{ReplicateFromStartercode: true, IssueNumbers: numbers}
return &IssueReplication{ReplicateFromStartercode: true, IssueNumbers: numbers, IncludeChildTasks: includeChildTasks}
}

func clone(assignmentKey, defaultBranch string) *Clone {
Expand Down
7 changes: 7 additions & 0 deletions config/repo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,20 @@ func TestIssues_DefaultsAndLegacyFallback(t *testing.T) {
if !reflect.DeepEqual(i.IssueNumbers, []int{1}) {
t.Fatalf("IssueNumbers = %#v, want [1]", i.IssueNumbers)
}
if i.IncludeChildTasks {
t.Fatalf("IncludeChildTasks = %v, want false", i.IncludeChildTasks)
}

viper.Set("course.a1.issues.replicateFromStartercode", true)
viper.Set("course.a1.issues.issueNumbers", []int{4, 7})
viper.Set("course.a1.issues.includeChildTasks", true)
i = issues("course.a1")
if !reflect.DeepEqual(i.IssueNumbers, []int{4, 7}) {
t.Fatalf("IssueNumbers = %#v", i.IssueNumbers)
}
if !i.IncludeChildTasks {
t.Fatalf("IncludeChildTasks = %v, want true", i.IncludeChildTasks)
}
}

func TestCloneDefaultsAndOverrides(t *testing.T) {
Expand Down
3 changes: 3 additions & 0 deletions config/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ func (cfg *AssignmentConfig) Show() {
fieldCandidate(2, "AdditionalBranches"),
fieldCandidate(2, "ReplicateFromStartercode"),
fieldCandidate(2, "IssueNumbers"),
fieldCandidate(2, "IncludeChildTasks"),
fieldCandidate(2, "MergeMethod"),
fieldCandidate(2, "SquashOption"),
fieldCandidate(2, "PipelineMustSucceed"),
Expand Down Expand Up @@ -182,8 +183,10 @@ func (cfg *AssignmentConfig) Show() {
writeSectionField("ReplicateFromStartercode", cfg.Issues.ReplicateFromStartercode)
if cfg.Issues.ReplicateFromStartercode {
writeSectionField("IssueNumbers", cfg.Issues.IssueNumbers)
writeSectionField("IncludeChildTasks", cfg.Issues.IncludeChildTasks)
} else {
writeSectionField("IssueNumbers", "not used")
writeSectionField("IncludeChildTasks", "not used")
}
}

Expand Down
1 change: 1 addition & 0 deletions config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ type BranchRule struct {
type IssueReplication struct {
ReplicateFromStartercode bool
IssueNumbers []int
IncludeChildTasks bool
}

type Clone struct {
Expand Down
2 changes: 2 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ Configure issue replication separately from startercode:
issues:
replicateFromStartercode: true
issueNumbers: [1, 3]
includeChildTasks: false
```

### Issue keys
Expand All @@ -254,6 +255,7 @@ issues:
|---|---|---|---|
| `replicateFromStartercode` | Copy issues from starter project | `false` | Requires `startercode` |
| `issueNumbers` | Which issue numbers to copy | `[1]` | Used only when replication is enabled |
| `includeChildTasks` | Expand issueNumbers by linked child tasks (GraphQL) | `false` | Resolves nested child tasks recursively |

**Example: Merge-only development branch**

Expand Down
82 changes: 23 additions & 59 deletions git/sourcerepo.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,29 @@ func PrepareSourceRepo(url, fromBranch string, singleCommit bool, commitMessage
log.Debug().Err(err).Msg("cannot start spinner")
}

cloneSucceeded := false
defer func() {
if spinner == nil {
return
}

if cloneSucceeded {
errs := spinner.Stop()
if errs != nil {
log.Debug().Err(errs).Msg("cannot stop spinner")
}
return
}

err := spinner.StopFail()
if err != nil {
log.Debug().Err(err).Msg("cannot stop spinner")
}
}()

auth, err := GetAuth()
if err != nil {
spinner.StopFailMessage(fmt.Sprintf("problem: %v", err))
fmt.Printf("error: %v", err)
return nil, err
}
Expand All @@ -60,22 +81,12 @@ func PrepareSourceRepo(url, fromBranch string, singleCommit bool, commitMessage
})
if err != nil {
spinner.StopFailMessage(fmt.Sprintf("problem: %v", err))

err := spinner.StopFail()
if err != nil {
log.Debug().Err(err).Msg("cannot stop spinner")
}
return nil, err
}

wt, err := repo.Worktree()
if err != nil {
spinner.StopFailMessage(fmt.Sprintf("problem: %v", err))

err := spinner.StopFail()
if err != nil {
log.Debug().Err(err).Msg("cannot stop spinner")
}
return nil, err
}

Expand All @@ -84,15 +95,11 @@ func PrepareSourceRepo(url, fromBranch string, singleCommit bool, commitMessage
Force: true,
}); err != nil {
spinner.StopFailMessage(fmt.Sprintf("problem: %v", err))

err := spinner.StopFail()
if err != nil {
log.Debug().Err(err).Msg("cannot stop spinner")
}
return nil, err
}

if !singleCommit {
cloneSucceeded = true
return &SourceRepo{
Repo: repo,
Ref: sourceRef,
Expand All @@ -103,33 +110,18 @@ func PrepareSourceRepo(url, fromBranch string, singleCommit bool, commitMessage
headRef, err := repo.Head()
if err != nil {
spinner.StopFailMessage(fmt.Sprintf("problem: %v", err))

err := spinner.StopFail()
if err != nil {
log.Debug().Err(err).Msg("cannot stop spinner")
}
return nil, err
}

headCommit, err := repo.CommitObject(headRef.Hash())
if err != nil {
spinner.StopFailMessage(fmt.Sprintf("problem: %v", err))

err := spinner.StopFail()
if err != nil {
log.Debug().Err(err).Msg("cannot stop spinner")
}
return nil, err
}

tree, err := headCommit.Tree()
if err != nil {
spinner.StopFailMessage(fmt.Sprintf("problem: %v", err))

err := spinner.StopFail()
if err != nil {
log.Debug().Err(err).Msg("cannot stop spinner")
}
return nil, err
}

Expand Down Expand Up @@ -163,32 +155,17 @@ func PrepareSourceRepo(url, fromBranch string, singleCommit bool, commitMessage
encoded := repo.Storer.NewEncodedObject()
if err := commit.Encode(encoded); err != nil {
spinner.StopFailMessage(fmt.Sprintf("problem: %v", err))

err := spinner.StopFail()
if err != nil {
log.Debug().Err(err).Msg("cannot stop spinner")
}
return nil, err
}

commitHash, err := repo.Storer.SetEncodedObject(encoded)
if err != nil {
spinner.StopFailMessage(fmt.Sprintf("problem: %v", err))

err := spinner.StopFail()
if err != nil {
log.Debug().Err(err).Msg("cannot stop spinner")
}
return nil, err
}

if err := repo.Storer.SetReference(plumbing.NewHashReference(refName, commitHash)); err != nil {
spinner.StopFailMessage(fmt.Sprintf("problem: %v", err))

err := spinner.StopFail()
if err != nil {
log.Debug().Err(err).Msg("cannot stop spinner")
}
return nil, err
}

Expand All @@ -197,11 +174,6 @@ func PrepareSourceRepo(url, fromBranch string, singleCommit bool, commitMessage
Merge: refName,
}); err != nil && err != git.ErrBranchExists {
spinner.StopFailMessage(fmt.Sprintf("problem: %v", err))

err := spinner.StopFail()
if err != nil {
log.Debug().Err(err).Msg("cannot stop spinner")
}
return nil, err
}

Expand All @@ -210,21 +182,13 @@ func PrepareSourceRepo(url, fromBranch string, singleCommit bool, commitMessage
Force: true,
}); err != nil {
spinner.StopFailMessage(fmt.Sprintf("problem: %v", err))

err := spinner.StopFail()
if err != nil {
log.Debug().Err(err).Msg("cannot stop spinner")
}
return nil, err
}

if singleCommit {
spinner.StopMessage(fmt.Sprintf("using branch '%s' with single commit '%s'", refName.Short(), commitMessage))
}
errs := spinner.Stop()
if errs != nil {
log.Debug().Err(err).Msg("cannot stop spinner")
}
cloneSucceeded = true

return &SourceRepo{
Repo: repo,
Expand Down
45 changes: 43 additions & 2 deletions gitlab/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,26 @@ func (c *Client) generate(assignmentCfg *config.AssignmentConfig, assignmentGrou
// Replicate issues from startercode repo if configured
if generated && assignmentCfg.Startercode != nil && assignmentCfg.Issues != nil && assignmentCfg.Issues.ReplicateFromStartercode {
starterProject, starterProjectErr := c.getStartercodeProject(assignmentCfg)
issueNumbers := assignmentCfg.Issues.IssueNumbers
parentByChild := make(map[int]int)

if starterProjectErr == nil {
plan, planErr := c.resolveIssuePlanForReplication(
starterProject,
assignmentCfg.Issues.IssueNumbers,
assignmentCfg.Issues.IncludeChildTasks,
)
if planErr != nil {
starterProjectErr = planErr
} else {
issueNumbers = plan.OrderedIssues
parentByChild = plan.ParentByChild
}
}

for _, issueNumber := range assignmentCfg.Issues.IssueNumbers {
createdIssueMap := make(map[int]int, len(issueNumbers))

for _, issueNumber := range issueNumbers {
cfg.Suffix = aurora.Sprintf(
aurora.Cyan(" ↪ replicating issue #%d from startercode"),
aurora.Yellow(issueNumber),
Expand All @@ -203,7 +221,9 @@ func (c *Client) generate(assignmentCfg *config.AssignmentConfig, assignmentGrou
continue
}

err = c.replicateIssue(starterProject, project, issueNumber)
_, isChildTask := parentByChild[issueNumber]
createdIssueIID, replicateErr := c.replicateIssue(starterProject, project, issueNumber, isChildTask)
err = replicateErr
if err != nil {
spinner.StopFailMessage(fmt.Sprintf("problem: %v", err))

Expand All @@ -214,11 +234,32 @@ func (c *Client) generate(assignmentCfg *config.AssignmentConfig, assignmentGrou
continue
}

createdIssueMap[issueNumber] = createdIssueIID

err = spinner.Stop()
if err != nil {
log.Debug().Err(err).Msg("cannot stop spinner")
}
}

if starterProjectErr == nil && assignmentCfg.Issues.IncludeChildTasks {
for childSource, parentSource := range parentByChild {
parentTarget, hasParent := createdIssueMap[parentSource]
childTarget, hasChild := createdIssueMap[childSource]
if !hasParent || !hasChild {
continue
}

if err = c.attachChildTaskToParent(project, parentTarget, childTarget); err != nil {
log.Error().Err(err).
Int("sourceParentIssue", parentSource).
Int("sourceChildIssue", childSource).
Int("targetParentIssue", parentTarget).
Int("targetChildIssue", childTarget).
Msg("cannot attach replicated child task to parent")
}
}
}
}

c.setaccess(assignmentCfg, project, members, &cfg)
Expand Down
Loading
Loading