1
0
Fork 0
forgejo/services/migrations/restore.go
zeripath e6b3be4608
Add more checks in migration code (#21011)
When migrating add several more important sanity checks:

* SHAs must be SHAs
* Refs must be valid Refs
* URLs must be reasonable

Signed-off-by: Andrew Thornton <art27@cantab.net>

Signed-off-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: techknowlogick <matti@mdranta.net>
2022-09-04 13:47:56 +03:00

273 lines
6.1 KiB
Go

// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package migrations
import (
"context"
"fmt"
"os"
"path/filepath"
"strconv"
base "code.gitea.io/gitea/modules/migration"
"gopkg.in/yaml.v2"
)
// RepositoryRestorer implements an Downloader from the local directory
type RepositoryRestorer struct {
base.NullDownloader
ctx context.Context
baseDir string
repoOwner string
repoName string
validation bool
}
// NewRepositoryRestorer creates a repository restorer which could restore repository from a dumped folder
func NewRepositoryRestorer(ctx context.Context, baseDir, owner, repoName string, validation bool) (*RepositoryRestorer, error) {
baseDir, err := filepath.Abs(baseDir)
if err != nil {
return nil, err
}
return &RepositoryRestorer{
ctx: ctx,
baseDir: baseDir,
repoOwner: owner,
repoName: repoName,
validation: validation,
}, nil
}
func (r *RepositoryRestorer) commentDir() string {
return filepath.Join(r.baseDir, "comments")
}
func (r *RepositoryRestorer) reviewDir() string {
return filepath.Join(r.baseDir, "reviews")
}
// SetContext set context
func (r *RepositoryRestorer) SetContext(ctx context.Context) {
r.ctx = ctx
}
func (r *RepositoryRestorer) getRepoOptions() (map[string]string, error) {
p := filepath.Join(r.baseDir, "repo.yml")
bs, err := os.ReadFile(p)
if err != nil {
return nil, err
}
opts := make(map[string]string)
err = yaml.Unmarshal(bs, &opts)
if err != nil {
return nil, err
}
return opts, nil
}
// GetRepoInfo returns a repository information
func (r *RepositoryRestorer) GetRepoInfo() (*base.Repository, error) {
opts, err := r.getRepoOptions()
if err != nil {
return nil, err
}
isPrivate, _ := strconv.ParseBool(opts["is_private"])
return &base.Repository{
Owner: r.repoOwner,
Name: r.repoName,
IsPrivate: isPrivate,
Description: opts["description"],
OriginalURL: opts["original_url"],
CloneURL: filepath.Join(r.baseDir, "git"),
DefaultBranch: opts["default_branch"],
}, nil
}
// GetTopics return github topics
func (r *RepositoryRestorer) GetTopics() ([]string, error) {
p := filepath.Join(r.baseDir, "topic.yml")
topics := struct {
Topics []string `yaml:"topics"`
}{}
bs, err := os.ReadFile(p)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, err
}
err = yaml.Unmarshal(bs, &topics)
if err != nil {
return nil, err
}
return topics.Topics, nil
}
// GetMilestones returns milestones
func (r *RepositoryRestorer) GetMilestones() ([]*base.Milestone, error) {
milestones := make([]*base.Milestone, 0, 10)
p := filepath.Join(r.baseDir, "milestone.yml")
err := base.Load(p, &milestones, r.validation)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, err
}
return milestones, nil
}
// GetReleases returns releases
func (r *RepositoryRestorer) GetReleases() ([]*base.Release, error) {
releases := make([]*base.Release, 0, 10)
p := filepath.Join(r.baseDir, "release.yml")
_, err := os.Stat(p)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, err
}
bs, err := os.ReadFile(p)
if err != nil {
return nil, err
}
err = yaml.Unmarshal(bs, &releases)
if err != nil {
return nil, err
}
for _, rel := range releases {
for _, asset := range rel.Assets {
if asset.DownloadURL != nil {
*asset.DownloadURL = "file://" + filepath.Join(r.baseDir, *asset.DownloadURL)
}
}
}
return releases, nil
}
// GetLabels returns labels
func (r *RepositoryRestorer) GetLabels() ([]*base.Label, error) {
labels := make([]*base.Label, 0, 10)
p := filepath.Join(r.baseDir, "label.yml")
_, err := os.Stat(p)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, err
}
bs, err := os.ReadFile(p)
if err != nil {
return nil, err
}
err = yaml.Unmarshal(bs, &labels)
if err != nil {
return nil, err
}
return labels, nil
}
// GetIssues returns issues according start and limit
func (r *RepositoryRestorer) GetIssues(page, perPage int) ([]*base.Issue, bool, error) {
issues := make([]*base.Issue, 0, 10)
p := filepath.Join(r.baseDir, "issue.yml")
err := base.Load(p, &issues, r.validation)
if err != nil {
if os.IsNotExist(err) {
return nil, true, nil
}
return nil, false, err
}
return issues, true, nil
}
// GetComments returns comments according issueNumber
func (r *RepositoryRestorer) GetComments(commentable base.Commentable) ([]*base.Comment, bool, error) {
comments := make([]*base.Comment, 0, 10)
p := filepath.Join(r.commentDir(), fmt.Sprintf("%d.yml", commentable.GetForeignIndex()))
_, err := os.Stat(p)
if err != nil {
if os.IsNotExist(err) {
return nil, false, nil
}
return nil, false, err
}
bs, err := os.ReadFile(p)
if err != nil {
return nil, false, err
}
err = yaml.Unmarshal(bs, &comments)
if err != nil {
return nil, false, err
}
return comments, false, nil
}
// GetPullRequests returns pull requests according page and perPage
func (r *RepositoryRestorer) GetPullRequests(page, perPage int) ([]*base.PullRequest, bool, error) {
pulls := make([]*base.PullRequest, 0, 10)
p := filepath.Join(r.baseDir, "pull_request.yml")
_, err := os.Stat(p)
if err != nil {
if os.IsNotExist(err) {
return nil, true, nil
}
return nil, false, err
}
bs, err := os.ReadFile(p)
if err != nil {
return nil, false, err
}
err = yaml.Unmarshal(bs, &pulls)
if err != nil {
return nil, false, err
}
for _, pr := range pulls {
pr.PatchURL = "file://" + filepath.Join(r.baseDir, pr.PatchURL)
CheckAndEnsureSafePR(pr, "", r)
}
return pulls, true, nil
}
// GetReviews returns pull requests review
func (r *RepositoryRestorer) GetReviews(reviewable base.Reviewable) ([]*base.Review, error) {
reviews := make([]*base.Review, 0, 10)
p := filepath.Join(r.reviewDir(), fmt.Sprintf("%d.yml", reviewable.GetForeignIndex()))
_, err := os.Stat(p)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, err
}
bs, err := os.ReadFile(p)
if err != nil {
return nil, err
}
err = yaml.Unmarshal(bs, &reviews)
if err != nil {
return nil, err
}
return reviews, nil
}