package migrations import ( "bytes" "fmt" "strings" "code.gitea.io/sdk/gitea" "github.com/google/go-github/github" "github.com/sirupsen/logrus" ) // Job manages all migrations of a "migartion job" type Job struct { Repositories []string Options *Options Client *gitea.Client GHClient *github.Client UseStdErr bool migratories map[string]*FetchMigratory } // JobReport represents the current status of a Job type JobReport struct { Pending []string `json:"pending"` Running map[string]*MigratoryStatus `json:"running"` Finished map[string]*MigratoryStatus `json:"finished"` Failed map[string]string `json:"failed"` } // NewJob returns an instance of initialized instance of Job func NewJob(options *Options, client *gitea.Client, githubClient *github.Client, repos ...string) *Job { return &Job{Repositories: repos, Options: options, Client: client, GHClient: githubClient} } // StatusReport generates a JobReport indicating which state the job is func (job *Job) StatusReport() *JobReport { report := &JobReport{ Pending: make([]string, 0), Finished: make(map[string]*MigratoryStatus), Running: make(map[string]*MigratoryStatus), Failed: make(map[string]string), } for _, repo := range job.Repositories { if migratory, ok := job.migratories[repo]; ok { migratory.Status.Log = migratory.LogOutput.String() switch migratory.Status.Stage { case Finished: report.Finished[repo] = migratory.Status case Importing: case Migrating: report.Running[repo] = migratory.Status case Failed: report.Failed[repo] = migratory.Status.FatalError.Error() default: report.Pending = append(report.Pending, repo) fmt.Printf("unknown status %d\n", migratory.Status.Stage) } } else { report.Pending = append(report.Pending, repo) } } return report } // StartMigration migrates all repos from Repositories func (job *Job) StartMigration() chan error { errs := make(chan error, len(job.Repositories)) var pendingRepos = len(job.Repositories) autoclose := func() { pendingRepos-- if pendingRepos <= 0 { close(errs) } } job.migratories = make(map[string]*FetchMigratory, pendingRepos) for _, repo := range job.Repositories { mig, err := job.initFetchMigratory(repo) job.migratories[repo] = mig if err != nil { mig.Status = &MigratoryStatus{ Stage: Failed, FatalError: err, } errs <- err autoclose() continue } go func() { err := mig.MigrateFromGitHub() errs <- err autoclose() }() } return errs } func (job *Job) initFetchMigratory(repo string) (*FetchMigratory, error) { res := strings.Split(repo, "/") if len(res) != 2 { return nil, fmt.Errorf("invalid repo name: %s", repo) } fm := &FetchMigratory{ Migratory: Migratory{ Client: job.Client, Options: *job.Options, }, RepoName: res[1], RepoOwner: res[0], GHClient: job.GHClient, Logger: logrus.New(), LogOutput: new(bytes.Buffer), } if !job.UseStdErr { fm.Logger.Formatter = &logrus.TextFormatter{ DisableColors: true, DisableLevelTruncation: true, DisableTimestamp: true, } fm.Logger.SetOutput(fm.LogOutput) } else { fm.LogOutput = nil } return fm, nil } // Finished indicates if the job is finished or not func (job *Job) Finished() bool { return (len(job.StatusReport().Failed) + len(job.StatusReport().Finished)) >= len(job.Repositories) }