| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- // Copyright 2023 The Gitea Authors. All rights reserved.
- // SPDX-License-Identifier: MIT
-
- // This artifact server is inspired by https://github.com/nektos/act/blob/master/pkg/artifacts/server.go.
- // It updates url setting and uses ObjectStore to handle artifacts persistence.
-
- package actions
-
- import (
- "context"
- "errors"
- "time"
-
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/util"
-
- "xorm.io/builder"
- )
-
- // ArtifactStatus is the status of an artifact, uploading, expired or need-delete
- type ArtifactStatus int64
-
- const (
- ArtifactStatusUploadPending ArtifactStatus = iota + 1 // 1, ArtifactStatusUploadPending is the status of an artifact upload that is pending
- ArtifactStatusUploadConfirmed // 2, ArtifactStatusUploadConfirmed is the status of an artifact upload that is confirmed
- ArtifactStatusUploadError // 3, ArtifactStatusUploadError is the status of an artifact upload that is errored
- ArtifactStatusExpired // 4, ArtifactStatusExpired is the status of an artifact that is expired
- ArtifactStatusPendingDeletion // 5, ArtifactStatusPendingDeletion is the status of an artifact that is pending deletion
- ArtifactStatusDeleted // 6, ArtifactStatusDeleted is the status of an artifact that is deleted
- )
-
- func (status ArtifactStatus) ToString() string {
- switch status {
- case ArtifactStatusUploadPending:
- return "upload is not yet completed"
- case ArtifactStatusUploadConfirmed:
- return "upload is completed"
- case ArtifactStatusUploadError:
- return "upload failed"
- case ArtifactStatusExpired:
- return "expired"
- case ArtifactStatusPendingDeletion:
- return "pending deletion"
- case ArtifactStatusDeleted:
- return "deleted"
- default:
- return "unknown"
- }
- }
-
- func init() {
- db.RegisterModel(new(ActionArtifact))
- }
-
- // ActionArtifact is a file that is stored in the artifact storage.
- type ActionArtifact struct {
- ID int64 `xorm:"pk autoincr"`
- RunID int64 `xorm:"index unique(runid_name_path)"` // The run id of the artifact
- RunnerID int64
- RepoID int64 `xorm:"index"`
- OwnerID int64
- CommitSHA string
- StoragePath string // The path to the artifact in the storage
- FileSize int64 // The size of the artifact in bytes
- FileCompressedSize int64 // The size of the artifact in bytes after gzip compression
- ContentEncoding string // The content encoding of the artifact
- ArtifactPath string `xorm:"index unique(runid_name_path)"` // The path to the artifact when runner uploads it
- ArtifactName string `xorm:"index unique(runid_name_path)"` // The name of the artifact when runner uploads it
- Status ArtifactStatus `xorm:"index"` // The status of the artifact, uploading, expired or need-delete
- CreatedUnix timeutil.TimeStamp `xorm:"created"`
- UpdatedUnix timeutil.TimeStamp `xorm:"updated index"`
- ExpiredUnix timeutil.TimeStamp `xorm:"index"` // The time when the artifact will be expired
- }
-
- func CreateArtifact(ctx context.Context, t *ActionTask, artifactName, artifactPath string, expiredDays int64) (*ActionArtifact, error) {
- if err := t.LoadJob(ctx); err != nil {
- return nil, err
- }
- artifact, err := getArtifactByNameAndPath(ctx, t.Job.RunID, artifactName, artifactPath)
- if errors.Is(err, util.ErrNotExist) {
- artifact := &ActionArtifact{
- ArtifactName: artifactName,
- ArtifactPath: artifactPath,
- RunID: t.Job.RunID,
- RunnerID: t.RunnerID,
- RepoID: t.RepoID,
- OwnerID: t.OwnerID,
- CommitSHA: t.CommitSHA,
- Status: ArtifactStatusUploadPending,
- ExpiredUnix: timeutil.TimeStamp(time.Now().Unix() + timeutil.Day*expiredDays),
- }
- if _, err := db.GetEngine(ctx).Insert(artifact); err != nil {
- return nil, err
- }
- return artifact, nil
- } else if err != nil {
- return nil, err
- }
-
- if _, err := db.GetEngine(ctx).ID(artifact.ID).Cols("expired_unix").Update(&ActionArtifact{
- ExpiredUnix: timeutil.TimeStamp(time.Now().Unix() + timeutil.Day*expiredDays),
- }); err != nil {
- return nil, err
- }
-
- return artifact, nil
- }
-
- func getArtifactByNameAndPath(ctx context.Context, runID int64, name, fpath string) (*ActionArtifact, error) {
- var art ActionArtifact
- has, err := db.GetEngine(ctx).Where("run_id = ? AND artifact_name = ? AND artifact_path = ?", runID, name, fpath).Get(&art)
- if err != nil {
- return nil, err
- } else if !has {
- return nil, util.ErrNotExist
- }
- return &art, nil
- }
-
- // UpdateArtifactByID updates an artifact by id
- func UpdateArtifactByID(ctx context.Context, id int64, art *ActionArtifact) error {
- art.ID = id
- _, err := db.GetEngine(ctx).ID(id).AllCols().Update(art)
- return err
- }
-
- type FindArtifactsOptions struct {
- db.ListOptions
- RepoID int64
- RunID int64
- ArtifactName string
- Status int
- FinalizedArtifactsV4 bool
- }
-
- func (opts FindArtifactsOptions) ToOrders() string {
- return "id"
- }
-
- var _ db.FindOptionsOrder = (*FindArtifactsOptions)(nil)
-
- func (opts FindArtifactsOptions) ToConds() builder.Cond {
- cond := builder.NewCond()
- if opts.RepoID > 0 {
- cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
- }
- if opts.RunID > 0 {
- cond = cond.And(builder.Eq{"run_id": opts.RunID})
- }
- if opts.ArtifactName != "" {
- cond = cond.And(builder.Eq{"artifact_name": opts.ArtifactName})
- }
- if opts.Status > 0 {
- cond = cond.And(builder.Eq{"status": opts.Status})
- }
- if opts.FinalizedArtifactsV4 {
- cond = cond.And(builder.Eq{"status": ArtifactStatusUploadConfirmed}.Or(builder.Eq{"status": ArtifactStatusExpired}))
- cond = cond.And(builder.Eq{"content_encoding": "application/zip"})
- }
-
- return cond
- }
-
- // ActionArtifactMeta is the meta-data of an artifact
- type ActionArtifactMeta struct {
- ArtifactName string
- FileSize int64
- Status ArtifactStatus
- }
-
- // ListUploadedArtifactsMeta returns all uploaded artifacts meta of a run
- func ListUploadedArtifactsMeta(ctx context.Context, runID int64) ([]*ActionArtifactMeta, error) {
- arts := make([]*ActionArtifactMeta, 0, 10)
- return arts, db.GetEngine(ctx).Table("action_artifact").
- Where("run_id=? AND (status=? OR status=?)", runID, ArtifactStatusUploadConfirmed, ArtifactStatusExpired).
- GroupBy("artifact_name").
- Select("artifact_name, sum(file_size) as file_size, max(status) as status").
- Find(&arts)
- }
-
- // ListNeedExpiredArtifacts returns all need expired artifacts but not deleted
- func ListNeedExpiredArtifacts(ctx context.Context) ([]*ActionArtifact, error) {
- arts := make([]*ActionArtifact, 0, 10)
- return arts, db.GetEngine(ctx).
- Where("expired_unix < ? AND status = ?", timeutil.TimeStamp(time.Now().Unix()), ArtifactStatusUploadConfirmed).Find(&arts)
- }
-
- // ListPendingDeleteArtifacts returns all artifacts in pending-delete status.
- // limit is the max number of artifacts to return.
- func ListPendingDeleteArtifacts(ctx context.Context, limit int) ([]*ActionArtifact, error) {
- arts := make([]*ActionArtifact, 0, limit)
- return arts, db.GetEngine(ctx).
- Where("status = ?", ArtifactStatusPendingDeletion).Limit(limit).Find(&arts)
- }
-
- // SetArtifactExpired sets an artifact to expired
- func SetArtifactExpired(ctx context.Context, artifactID int64) error {
- _, err := db.GetEngine(ctx).Where("id=? AND status = ?", artifactID, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: ArtifactStatusExpired})
- return err
- }
-
- // SetArtifactNeedDelete sets an artifact to need-delete, cron job will delete it
- func SetArtifactNeedDelete(ctx context.Context, runID int64, name string) error {
- _, err := db.GetEngine(ctx).Where("run_id=? AND artifact_name=? AND status = ?", runID, name, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: ArtifactStatusPendingDeletion})
- return err
- }
-
- // SetArtifactDeleted sets an artifact to deleted
- func SetArtifactDeleted(ctx context.Context, artifactID int64) error {
- _, err := db.GetEngine(ctx).ID(artifactID).Cols("status").Update(&ActionArtifact{Status: ArtifactStatusDeleted})
- return err
- }
|