code.gitea.io/gitea@v1.21.7/models/actions/artifact.go (about)

     1  // Copyright 2023 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  // This artifact server is inspired by https://github.com/nektos/act/blob/master/pkg/artifacts/server.go.
     5  // It updates url setting and uses ObjectStore to handle artifacts persistence.
     6  
     7  package actions
     8  
     9  import (
    10  	"context"
    11  	"errors"
    12  	"time"
    13  
    14  	"code.gitea.io/gitea/models/db"
    15  	"code.gitea.io/gitea/modules/timeutil"
    16  	"code.gitea.io/gitea/modules/util"
    17  )
    18  
    19  // ArtifactStatus is the status of an artifact, uploading, expired or need-delete
    20  type ArtifactStatus int64
    21  
    22  const (
    23  	ArtifactStatusUploadPending   ArtifactStatus = iota + 1 // 1, ArtifactStatusUploadPending is the status of an artifact upload that is pending
    24  	ArtifactStatusUploadConfirmed                           // 2, ArtifactStatusUploadConfirmed is the status of an artifact upload that is confirmed
    25  	ArtifactStatusUploadError                               // 3, ArtifactStatusUploadError is the status of an artifact upload that is errored
    26  	ArtifactStatusExpired                                   // 4, ArtifactStatusExpired is the status of an artifact that is expired
    27  )
    28  
    29  func init() {
    30  	db.RegisterModel(new(ActionArtifact))
    31  }
    32  
    33  // ActionArtifact is a file that is stored in the artifact storage.
    34  type ActionArtifact struct {
    35  	ID                 int64 `xorm:"pk autoincr"`
    36  	RunID              int64 `xorm:"index unique(runid_name_path)"` // The run id of the artifact
    37  	RunnerID           int64
    38  	RepoID             int64 `xorm:"index"`
    39  	OwnerID            int64
    40  	CommitSHA          string
    41  	StoragePath        string             // The path to the artifact in the storage
    42  	FileSize           int64              // The size of the artifact in bytes
    43  	FileCompressedSize int64              // The size of the artifact in bytes after gzip compression
    44  	ContentEncoding    string             // The content encoding of the artifact
    45  	ArtifactPath       string             `xorm:"index unique(runid_name_path)"` // The path to the artifact when runner uploads it
    46  	ArtifactName       string             `xorm:"index unique(runid_name_path)"` // The name of the artifact when runner uploads it
    47  	Status             int64              `xorm:"index"`                         // The status of the artifact, uploading, expired or need-delete
    48  	CreatedUnix        timeutil.TimeStamp `xorm:"created"`
    49  	UpdatedUnix        timeutil.TimeStamp `xorm:"updated index"`
    50  	ExpiredUnix        timeutil.TimeStamp `xorm:"index"` // The time when the artifact will be expired
    51  }
    52  
    53  func CreateArtifact(ctx context.Context, t *ActionTask, artifactName, artifactPath string, expiredDays int64) (*ActionArtifact, error) {
    54  	if err := t.LoadJob(ctx); err != nil {
    55  		return nil, err
    56  	}
    57  	artifact, err := getArtifactByNameAndPath(ctx, t.Job.RunID, artifactName, artifactPath)
    58  	if errors.Is(err, util.ErrNotExist) {
    59  		artifact := &ActionArtifact{
    60  			ArtifactName: artifactName,
    61  			ArtifactPath: artifactPath,
    62  			RunID:        t.Job.RunID,
    63  			RunnerID:     t.RunnerID,
    64  			RepoID:       t.RepoID,
    65  			OwnerID:      t.OwnerID,
    66  			CommitSHA:    t.CommitSHA,
    67  			Status:       int64(ArtifactStatusUploadPending),
    68  			ExpiredUnix:  timeutil.TimeStamp(time.Now().Unix() + 3600*24*expiredDays),
    69  		}
    70  		if _, err := db.GetEngine(ctx).Insert(artifact); err != nil {
    71  			return nil, err
    72  		}
    73  		return artifact, nil
    74  	} else if err != nil {
    75  		return nil, err
    76  	}
    77  	return artifact, nil
    78  }
    79  
    80  func getArtifactByNameAndPath(ctx context.Context, runID int64, name, fpath string) (*ActionArtifact, error) {
    81  	var art ActionArtifact
    82  	has, err := db.GetEngine(ctx).Where("run_id = ? AND artifact_name = ? AND artifact_path = ?", runID, name, fpath).Get(&art)
    83  	if err != nil {
    84  		return nil, err
    85  	} else if !has {
    86  		return nil, util.ErrNotExist
    87  	}
    88  	return &art, nil
    89  }
    90  
    91  // GetArtifactByID returns an artifact by id
    92  func GetArtifactByID(ctx context.Context, id int64) (*ActionArtifact, error) {
    93  	var art ActionArtifact
    94  	has, err := db.GetEngine(ctx).ID(id).Get(&art)
    95  	if err != nil {
    96  		return nil, err
    97  	} else if !has {
    98  		return nil, util.ErrNotExist
    99  	}
   100  
   101  	return &art, nil
   102  }
   103  
   104  // UpdateArtifactByID updates an artifact by id
   105  func UpdateArtifactByID(ctx context.Context, id int64, art *ActionArtifact) error {
   106  	art.ID = id
   107  	_, err := db.GetEngine(ctx).ID(id).AllCols().Update(art)
   108  	return err
   109  }
   110  
   111  // ListArtifactsByRunID returns all artifacts of a run
   112  func ListArtifactsByRunID(ctx context.Context, runID int64) ([]*ActionArtifact, error) {
   113  	arts := make([]*ActionArtifact, 0, 10)
   114  	return arts, db.GetEngine(ctx).Where("run_id=?", runID).Find(&arts)
   115  }
   116  
   117  // ListArtifactsByRunIDAndArtifactName returns an artifacts of a run by artifact name
   118  func ListArtifactsByRunIDAndArtifactName(ctx context.Context, runID int64, artifactName string) ([]*ActionArtifact, error) {
   119  	arts := make([]*ActionArtifact, 0, 10)
   120  	return arts, db.GetEngine(ctx).Where("run_id=? AND artifact_name=?", runID, artifactName).Find(&arts)
   121  }
   122  
   123  // ListUploadedArtifactsByRunID returns all uploaded artifacts of a run
   124  func ListUploadedArtifactsByRunID(ctx context.Context, runID int64) ([]*ActionArtifact, error) {
   125  	arts := make([]*ActionArtifact, 0, 10)
   126  	return arts, db.GetEngine(ctx).Where("run_id=? AND status=?", runID, ArtifactStatusUploadConfirmed).Find(&arts)
   127  }
   128  
   129  // ActionArtifactMeta is the meta data of an artifact
   130  type ActionArtifactMeta struct {
   131  	ArtifactName string
   132  	FileSize     int64
   133  	Status       int64
   134  }
   135  
   136  // ListUploadedArtifactsMeta returns all uploaded artifacts meta of a run
   137  func ListUploadedArtifactsMeta(ctx context.Context, runID int64) ([]*ActionArtifactMeta, error) {
   138  	arts := make([]*ActionArtifactMeta, 0, 10)
   139  	return arts, db.GetEngine(ctx).Table("action_artifact").
   140  		Where("run_id=? AND (status=? OR status=?)", runID, ArtifactStatusUploadConfirmed, ArtifactStatusExpired).
   141  		GroupBy("artifact_name").
   142  		Select("artifact_name, sum(file_size) as file_size, max(status) as status").
   143  		Find(&arts)
   144  }
   145  
   146  // ListArtifactsByRepoID returns all artifacts of a repo
   147  func ListArtifactsByRepoID(ctx context.Context, repoID int64) ([]*ActionArtifact, error) {
   148  	arts := make([]*ActionArtifact, 0, 10)
   149  	return arts, db.GetEngine(ctx).Where("repo_id=?", repoID).Find(&arts)
   150  }
   151  
   152  // ListArtifactsByRunIDAndName returns artifacts by name of a run
   153  func ListArtifactsByRunIDAndName(ctx context.Context, runID int64, name string) ([]*ActionArtifact, error) {
   154  	arts := make([]*ActionArtifact, 0, 10)
   155  	return arts, db.GetEngine(ctx).Where("run_id=? AND artifact_name=?", runID, name).Find(&arts)
   156  }
   157  
   158  // ListNeedExpiredArtifacts returns all need expired artifacts but not deleted
   159  func ListNeedExpiredArtifacts(ctx context.Context) ([]*ActionArtifact, error) {
   160  	arts := make([]*ActionArtifact, 0, 10)
   161  	return arts, db.GetEngine(ctx).
   162  		Where("expired_unix < ? AND status = ?", timeutil.TimeStamp(time.Now().Unix()), ArtifactStatusUploadConfirmed).Find(&arts)
   163  }
   164  
   165  // SetArtifactExpired sets an artifact to expired
   166  func SetArtifactExpired(ctx context.Context, artifactID int64) error {
   167  	_, err := db.GetEngine(ctx).Where("id=? AND status = ?", artifactID, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusExpired)})
   168  	return err
   169  }