mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	Implement action artifacts server api. This change is used for supporting https://github.com/actions/upload-artifact and https://github.com/actions/download-artifact in gitea actions. It can run sample workflow from doc https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts. The api design is inspired by https://github.com/nektos/act/blob/master/pkg/artifacts/server.go and includes some changes from gitea internal structs and methods. Actions artifacts contains two parts: - Gitea server api and storage (this pr implement basic design without some complex cases supports) - Runner communicate with gitea server api (in comming) Old pr https://github.com/go-gitea/gitea/pull/22345 is outdated after actions merged. I create new pr from main branch.  Add artifacts list in actions workflow page.
		
			
				
	
	
		
			123 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			123 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// 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"
 | 
						|
 | 
						|
	"code.gitea.io/gitea/models/db"
 | 
						|
	"code.gitea.io/gitea/modules/timeutil"
 | 
						|
	"code.gitea.io/gitea/modules/util"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	// ArtifactStatusUploadPending is the status of an artifact upload that is pending
 | 
						|
	ArtifactStatusUploadPending = 1
 | 
						|
	// ArtifactStatusUploadConfirmed is the status of an artifact upload that is confirmed
 | 
						|
	ArtifactStatusUploadConfirmed = 2
 | 
						|
	// ArtifactStatusUploadError is the status of an artifact upload that is errored
 | 
						|
	ArtifactStatusUploadError = 3
 | 
						|
)
 | 
						|
 | 
						|
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)"` // 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             // The path to the artifact when runner uploads it
 | 
						|
	ArtifactName       string             `xorm:"UNIQUE(runid_name)"` // The name of the artifact when runner uploads it
 | 
						|
	Status             int64              `xorm:"index"`              // The status of the artifact, uploading, expired or need-delete
 | 
						|
	CreatedUnix        timeutil.TimeStamp `xorm:"created"`
 | 
						|
	UpdatedUnix        timeutil.TimeStamp `xorm:"updated index"`
 | 
						|
}
 | 
						|
 | 
						|
// CreateArtifact create a new artifact with task info or get same named artifact in the same run
 | 
						|
func CreateArtifact(ctx context.Context, t *ActionTask, artifactName string) (*ActionArtifact, error) {
 | 
						|
	if err := t.LoadJob(ctx); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	artifact, err := getArtifactByArtifactName(ctx, t.Job.RunID, artifactName)
 | 
						|
	if errors.Is(err, util.ErrNotExist) {
 | 
						|
		artifact := &ActionArtifact{
 | 
						|
			RunID:     t.Job.RunID,
 | 
						|
			RunnerID:  t.RunnerID,
 | 
						|
			RepoID:    t.RepoID,
 | 
						|
			OwnerID:   t.OwnerID,
 | 
						|
			CommitSHA: t.CommitSHA,
 | 
						|
			Status:    ArtifactStatusUploadPending,
 | 
						|
		}
 | 
						|
		if _, err := db.GetEngine(ctx).Insert(artifact); err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		return artifact, nil
 | 
						|
	} else if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return artifact, nil
 | 
						|
}
 | 
						|
 | 
						|
func getArtifactByArtifactName(ctx context.Context, runID int64, name string) (*ActionArtifact, error) {
 | 
						|
	var art ActionArtifact
 | 
						|
	has, err := db.GetEngine(ctx).Where("run_id = ? AND artifact_name = ?", runID, name).Get(&art)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	} else if !has {
 | 
						|
		return nil, util.ErrNotExist
 | 
						|
	}
 | 
						|
	return &art, nil
 | 
						|
}
 | 
						|
 | 
						|
// GetArtifactByID returns an artifact by id
 | 
						|
func GetArtifactByID(ctx context.Context, id int64) (*ActionArtifact, error) {
 | 
						|
	var art ActionArtifact
 | 
						|
	has, err := db.GetEngine(ctx).ID(id).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
 | 
						|
}
 | 
						|
 | 
						|
// ListArtifactsByRunID returns all artifacts of a run
 | 
						|
func ListArtifactsByRunID(ctx context.Context, runID int64) ([]*ActionArtifact, error) {
 | 
						|
	arts := make([]*ActionArtifact, 0, 10)
 | 
						|
	return arts, db.GetEngine(ctx).Where("run_id=?", runID).Find(&arts)
 | 
						|
}
 | 
						|
 | 
						|
// ListUploadedArtifactsByRunID returns all uploaded artifacts of a run
 | 
						|
func ListUploadedArtifactsByRunID(ctx context.Context, runID int64) ([]*ActionArtifact, error) {
 | 
						|
	arts := make([]*ActionArtifact, 0, 10)
 | 
						|
	return arts, db.GetEngine(ctx).Where("run_id=? AND status=?", runID, ArtifactStatusUploadConfirmed).Find(&arts)
 | 
						|
}
 | 
						|
 | 
						|
// ListArtifactsByRepoID returns all artifacts of a repo
 | 
						|
func ListArtifactsByRepoID(ctx context.Context, repoID int64) ([]*ActionArtifact, error) {
 | 
						|
	arts := make([]*ActionArtifact, 0, 10)
 | 
						|
	return arts, db.GetEngine(ctx).Where("repo_id=?", repoID).Find(&arts)
 | 
						|
}
 |