feat: add repo creation

This commit is contained in:
Максим Слипенко 2024-12-13 13:52:15 +03:00
parent 0fedac8e93
commit 26c751ccb7
7 changed files with 267 additions and 35 deletions

View file

@ -18,7 +18,7 @@ func New() (*App, error) {
return nil, err return nil, err
} }
db.AutoMigrate(&models.Task{}, &models.GitRepoAltRepoTask{}) db.AutoMigrate(&models.Task{}, &models.GitRepoAltRepoTask{}, &models.RPMFile{})
db.FirstOrCreate(&models.ALTRepo{ db.FirstOrCreate(&models.ALTRepo{
Name: "Sisyphus", Name: "Sisyphus",

View file

@ -14,7 +14,7 @@ type CreateTaskDTO struct {
} }
type CreateTaskResponse struct { type CreateTaskResponse struct {
TaskID int `json:"taskID"` TaskID uint `json:"taskID"`
Status models.TaskStatus `json:"status"` Status models.TaskStatus `json:"status"`
} }

View file

@ -7,17 +7,21 @@ import (
type ALTRepo struct { type ALTRepo struct {
gorm.Model gorm.Model
ID int
Name string `gorm:"uniqueIndex"` Name string `gorm:"uniqueIndex"`
} }
type GitRepo struct { type GitRepo struct {
ID int gorm.Model
Name string `gorm:"uniqueIndex"` Name string `gorm:"uniqueIndex"`
} }
type RPMFile struct { type RPMFile struct {
gorm.Model
TaskID int TaskID int
Task Task
Name string Name string
Arch string Arch string
} }
@ -45,27 +49,29 @@ const (
type Task struct { type Task struct {
gorm.Model gorm.Model
ID int
Status TaskStatus Status TaskStatus
Type TaskType Type TaskType
RepoID int RepoID uint
Repo GitRepo Repo *GitRepo
ALTRepoID int ALTRepoID uint
ALTRepo ALTRepo ALTRepo ALTRepo
FilesRemoved bool
Files []RPMFile Files []RPMFile
} }
type GitRepoAltRepoTask struct { type GitRepoAltRepoTask struct {
gorm.Model gorm.Model
ID int RepoID uint `gorm:"uniqueIndex:idx_gr_ar_gitaltrepotask"`
Repo *GitRepo
ALTRepoID uint `gorm:"uniqueIndex:idx_gr_ar_gitaltrepotask"`
ALTRepo *ALTRepo
RepoID int `gorm:"uniqueIndex:idx_gr_ar_gitaltrepotask"` LastTaskID uint
Repo GitRepo LastTask *Task
ALTRepoID int `gorm:"uniqueIndex:idx_gr_ar_gitaltrepotask"`
ALTRepo ALTRepo CurrentTaskID uint
TaskID int CurrentTask Task
Task Task
} }

View file

@ -7,6 +7,7 @@ import (
"code.alt-gnome.ru/aides-infra/aides-repo-api/internal/app" "code.alt-gnome.ru/aides-infra/aides-repo-api/internal/app"
"code.alt-gnome.ru/aides-infra/aides-repo-api/internal/controllers/taskcontroller" "code.alt-gnome.ru/aides-infra/aides-repo-api/internal/controllers/taskcontroller"
"code.alt-gnome.ru/aides-infra/aides-repo-api/internal/middlewares" "code.alt-gnome.ru/aides-infra/aides-repo-api/internal/middlewares"
"code.alt-gnome.ru/aides-infra/aides-repo-api/internal/services/reposervice"
"code.alt-gnome.ru/aides-infra/aides-repo-api/internal/services/taskservice" "code.alt-gnome.ru/aides-infra/aides-repo-api/internal/services/taskservice"
) )
@ -28,6 +29,9 @@ func (r *Router) Setup() *chi.Mux {
r.app, r.app,
) )
repoService := reposervice.New(r.app)
repoService.ForceUpdate()
taskController := taskcontroller.New( taskController := taskcontroller.New(
r.app, r.app,
taskService, taskService,

View file

@ -0,0 +1,134 @@
package reposervice
import (
"fmt"
"os"
"os/exec"
"path"
"strconv"
"code.alt-gnome.ru/aides-infra/aides-repo-api/internal/app"
"code.alt-gnome.ru/aides-infra/aides-repo-api/internal/models"
"gorm.io/gorm"
)
type Service struct {
app *app.App
}
func New(app *app.App) *Service {
return &Service{
app: app,
}
}
func createSymlink(target, link string) error {
if _, err := os.Lstat(link); err == nil {
if err := os.Remove(link); err != nil {
return fmt.Errorf("failed to remove existing file or symlink: %w", err)
}
}
if err := os.Symlink(target, link); err != nil {
return fmt.Errorf("failed to create symlink: %w", err)
}
return nil
}
func runGenbasedir(repoDir, arch, repoName string) {
cmd := exec.Command("genbasedir", "--bloat", "--progress", fmt.Sprintf("--topdir=%s", repoDir), arch, repoName)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
fmt.Printf("Failed to run genbasedir for %s: %v\n", arch, err)
os.Exit(1)
}
fmt.Printf("Successfully ran genbasedir for %s\n", arch)
}
func createRepoDirs(repoDir, repoName, arch string) {
// Create the 'base' directory
baseDir := path.Join(repoDir, arch, "base")
err := os.MkdirAll(baseDir, os.ModePerm)
if err != nil {
fmt.Printf("Failed to create directory %s: %v\n", baseDir, err)
os.Exit(1)
}
// Create the 'RPMS.<REPO_NAME>' directory
rpmsDir := path.Join(repoDir, arch, fmt.Sprintf("RPMS.%s", repoName))
err = os.MkdirAll(rpmsDir, os.ModePerm)
if err != nil {
fmt.Printf("Failed to create directory %s: %v\n", rpmsDir, err)
os.Exit(1)
}
}
func (s *Service) ForceUpdate() {
var tasks []models.Task
altRepo := models.ALTRepo{
Name: "Sisyphus",
}
s.app.Db.
Where(&altRepo).
First(&altRepo)
s.app.Db.Debug().
Model(&models.GitRepoAltRepoTask{}).
Select("tasks.*").
Joins("JOIN tasks ON tasks.id = git_repo_alt_repo_tasks.last_task_id").
Where(&models.GitRepoAltRepoTask{
ALTRepoID: altRepo.ID,
}).
Preload("Files").
Find(&tasks)
repoPath := path.Join(s.app.Config.UploadDir, "future_repo", "Sisyphus")
os.MkdirAll(
repoPath,
os.ModePerm,
)
repoName := "aides"
architectures := []string{"x86_64", "noarch"}
for _, arch := range architectures {
createRepoDirs(repoPath, repoName, arch)
}
for _, el := range tasks {
fmt.Println(el.Files)
taskPath := path.Join(
s.app.Config.UploadDir, "tasks", strconv.FormatUint(uint64(el.ID), 10),
)
for _, fileInfo := range el.Files {
localFilePath := path.Join(
strconv.FormatUint(uint64(el.ID), 10), fileInfo.Name,
)
symLink := path.Join(s.app.Config.UploadDir, "future_repo", "Sisyphus", fileInfo.Arch, "RPMS.aides", fileInfo.Name)
targetPath := path.Join("../../../../tasks/", localFilePath)
createSymlink(targetPath, symLink)
fmt.Println(path.Join(taskPath, fileInfo.Name))
}
}
for _, arch := range architectures {
runGenbasedir(repoPath, arch, repoName)
}
s.app.Db.Debug().
Model(&models.GitRepoAltRepoTask{}).
Where(&models.GitRepoAltRepoTask{
ALTRepoID: altRepo.ID,
}).
Update(
"current_task_id", gorm.Expr("last_task_id"),
)
}

View file

@ -1,24 +1,99 @@
package taskservice package taskservice
import ( import (
"mime/multipart" "os"
"path"
"strconv"
"code.alt-gnome.ru/aides-infra/aides-repo-api/internal/app" "code.alt-gnome.ru/aides-infra/aides-repo-api/internal/app"
"code.alt-gnome.ru/aides-infra/aides-repo-api/internal/models"
) )
type Service struct { type Service struct {
app *app.App app *app.App
} }
type TaskUploadInput struct {
TaskID string
Repo string
Files []*multipart.FileHeader
}
func New(app *app.App) *Service { func New(app *app.App) *Service {
return &Service{ return &Service{
app: app, app: app,
} }
} }
func (s *Service) onTaskComplete(task *models.Task) error {
if err := s.app.Db.Save(&task).Error; err != nil {
return err
}
grart := models.GitRepoAltRepoTask{
ALTRepoID: 1,
Repo: task.Repo,
}
s.app.Db.Debug().Where(
&grart,
).FirstOrCreate(&grart)
grart.LastTaskID = task.ID
if err := s.app.Db.Save(&grart).Error; err != nil {
return err
}
s.tasksCleanup(&grart, 0)
return nil
}
func (s *Service) tasksCleanup(r *models.GitRepoAltRepoTask, N int) {
excludedTaskIDs := []uint{}
if r.CurrentTaskID != 0 {
excludedTaskIDs = append(excludedTaskIDs, r.CurrentTaskID)
}
if r.LastTaskID != 0 {
excludedTaskIDs = append(excludedTaskIDs, r.LastTaskID)
}
var lastNTaskIDs []uint
s.app.Db.
Debug().
Table("tasks").
Select("id").
Where("repo_id = ?", r.ID).
Where("status = ?", models.StatusCompleted).
Where("id NOT IN ?", excludedTaskIDs).
Order("created_at DESC").
Limit(N).
Pluck("id", &lastNTaskIDs)
excludedTaskIDs = append(excludedTaskIDs, lastNTaskIDs...)
var taskIDsToDelete []int
s.app.Db.
Debug().
Model(&models.Task{}).
Select("id").
Where("repo_id = ?", r.RepoID).
Where("files_removed = ?", false).
Where("id NOT IN ?", excludedTaskIDs).
Pluck("id", &taskIDsToDelete)
if len(taskIDsToDelete) > 0 {
s.app.Db.
Debug().
Model(&models.Task{}).
Where("id IN ?", taskIDsToDelete).
Update("files_removed", true)
for _, id := range taskIDsToDelete {
s.removeTaskFiles(id)
}
}
}
func (s *Service) removeTaskFiles(taskId int) {
taskFolderPath := path.Join(
s.app.Config.UploadDir,
"tasks",
strconv.Itoa(taskId),
)
os.RemoveAll(taskFolderPath)
}

View file

@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"mime/multipart"
"os" "os"
"os/exec" "os/exec"
"path" "path"
@ -14,6 +15,13 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
type TaskUploadInput struct {
TaskID string
Repo string
Files []*multipart.FileHeader
}
func getRPMArchitecture(filePath string) (string, error) { func getRPMArchitecture(filePath string) (string, error) {
cmd := exec.Command("rpm", "-qp", "--queryformat", "%{ARCH}", filePath) cmd := exec.Command("rpm", "-qp", "--queryformat", "%{ARCH}", filePath)
@ -34,11 +42,12 @@ func (s *Service) Upload(input *TaskUploadInput) error {
files := input.Files files := input.Files
task := models.Task{} task := models.Task{}
result := s.app.Db.Where( result := s.app.Db.Preload("Repo").Where(
"id = ?", taskID, "id = ?", taskID,
).Where(
"status = ?", models.StatusPending,
).First(&task) ).First(&task)
/*.Where(
"status = ?", models.StatusPending,
)*/
if errors.Is(result.Error, gorm.ErrRecordNotFound) { if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return result.Error return result.Error
@ -47,8 +56,6 @@ func (s *Service) Upload(input *TaskUploadInput) error {
return result.Error return result.Error
} }
fmt.Printf("%v", task.Status)
localPath := path.Join(input.TaskID) localPath := path.Join(input.TaskID)
taskFolderPath := path.Join(s.app.Config.UploadDir, "tasks", localPath) taskFolderPath := path.Join(s.app.Config.UploadDir, "tasks", localPath)
os.MkdirAll(taskFolderPath, os.ModePerm) os.MkdirAll(taskFolderPath, os.ModePerm)
@ -91,7 +98,15 @@ func (s *Service) Upload(input *TaskUploadInput) error {
return err return err
} }
fmt.Println(arch) f := models.RPMFile{
Name: fileHeader.Filename,
Arch: arch,
Task: task,
}
if err := s.app.Db.Save(&f).Error; err != nil {
return err
}
// Символическая ссылка // Символическая ссылка
/* /*
@ -106,9 +121,7 @@ func (s *Service) Upload(input *TaskUploadInput) error {
} }
task.Status = models.StatusCompleted task.Status = models.StatusCompleted
if err := s.app.Db.Save(&task).Error; err != nil { s.onTaskComplete(&task)
return err
}
return nil return nil
} }