2014-03-15 20:03:23 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2017-09-12 09:48:13 +03:00
// Copyright 2017 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2014-03-15 20:03:23 +04:00
2016-03-11 19:56:52 +03:00
package context
2014-03-15 20:03:23 +04:00
import (
2021-05-06 02:30:25 +03:00
"context"
2014-03-20 08:12:33 +04:00
"fmt"
2022-04-01 11:47:50 +03:00
"html"
2021-12-15 09:59:57 +03:00
"net/http"
2019-01-31 00:04:19 +03:00
"net/url"
2015-10-31 19:04:04 +03:00
"path"
2014-03-17 12:47:42 +04:00
"strings"
2014-03-16 10:28:24 +04:00
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/models"
2022-05-20 17:08:52 +03:00
"code.gitea.io/gitea/models/db"
2022-06-12 18:51:54 +03:00
git_model "code.gitea.io/gitea/models/git"
2022-06-13 12:37:59 +03:00
issues_model "code.gitea.io/gitea/models/issues"
2022-05-11 13:09:36 +03:00
access_model "code.gitea.io/gitea/models/perm/access"
2021-12-10 04:27:50 +03:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-09 22:57:58 +03:00
unit_model "code.gitea.io/gitea/models/unit"
2021-11-24 12:49:20 +03:00
user_model "code.gitea.io/gitea/models/user"
2017-10-26 04:37:33 +03:00
"code.gitea.io/gitea/modules/cache"
2019-03-27 12:33:00 +03:00
"code.gitea.io/gitea/modules/git"
2022-01-27 11:30:51 +03:00
code_indexer "code.gitea.io/gitea/modules/indexer/code"
2017-10-30 05:04:25 +03:00
"code.gitea.io/gitea/modules/log"
2022-06-06 11:01:49 +03:00
repo_module "code.gitea.io/gitea/modules/repository"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/setting"
2020-11-25 23:07:39 +03:00
"code.gitea.io/gitea/modules/util"
2021-12-10 11:14:24 +03:00
asymkey_service "code.gitea.io/gitea/services/asymkey"
2017-10-26 04:37:33 +03:00
2019-10-16 00:24:16 +03:00
"github.com/editorconfig/editorconfig-core-go/v2"
2014-03-15 20:03:23 +04:00
)
2021-07-08 14:38:13 +03:00
// PullRequest contains information to make a pull request
2016-03-14 00:37:44 +03:00
type PullRequest struct {
2021-12-10 04:27:50 +03:00
BaseRepo * repo_model . Repository
2021-11-16 21:18:25 +03:00
Allowed bool
SameRepo bool
HeadInfoSubURL string // [<user>:]<branch> url segment
2016-03-14 00:37:44 +03:00
}
2017-03-15 03:52:01 +03:00
// Repository contains information to operate a repository
2016-03-14 00:37:44 +03:00
type Repository struct {
2022-05-11 13:09:36 +03:00
access_model . Permission
2016-03-14 00:37:44 +03:00
IsWatching bool
IsViewBranch bool
IsViewTag bool
IsViewCommit bool
2021-12-10 04:27:50 +03:00
Repository * repo_model . Repository
2021-11-24 12:49:20 +03:00
Owner * user_model . User
2016-03-14 00:37:44 +03:00
Commit * git . Commit
Tag * git . Tag
GitRepo * git . Repository
2021-11-18 02:50:17 +03:00
RefName string
2016-03-14 00:37:44 +03:00
BranchName string
TagName string
2016-08-25 07:35:03 +03:00
TreePath string
2016-03-14 00:37:44 +03:00
CommitID string
RepoLink string
2021-12-10 04:27:50 +03:00
CloneLink repo_model . CloneLink
2016-03-14 00:37:44 +03:00
CommitsCount int64
2016-08-30 12:08:38 +03:00
PullRequest * PullRequest
2016-03-14 00:37:44 +03:00
}
2022-05-11 13:09:36 +03:00
// CanWriteToBranch checks if the branch is writable by the user
2023-07-22 17:14:27 +03:00
func ( r * Repository ) CanWriteToBranch ( ctx context . Context , user * user_model . User , branch string ) bool {
return issues_model . CanMaintainerWriteToBranch ( ctx , r . Permission , branch , user )
2022-05-11 13:09:36 +03:00
}
2016-08-30 12:08:38 +03:00
// CanEnableEditor returns true if repository is editable and user has proper access level.
2023-07-22 17:14:27 +03:00
func ( r * Repository ) CanEnableEditor ( ctx context . Context , user * user_model . User ) bool {
return r . IsViewBranch && r . CanWriteToBranch ( ctx , user , r . BranchName ) && r . Repository . CanEnableEditor ( ) && ! r . Repository . IsArchived
2016-08-30 12:08:38 +03:00
}
2017-10-15 22:59:24 +03:00
// CanCreateBranch returns true if repository is editable and user has proper access level.
func ( r * Repository ) CanCreateBranch ( ) bool {
2021-11-09 22:57:58 +03:00
return r . Permission . CanWrite ( unit_model . TypeCode ) && r . Repository . CanCreateBranch ( )
2017-10-15 22:59:24 +03:00
}
2019-01-23 21:58:38 +03:00
// RepoMustNotBeArchived checks if a repo is archived
2021-01-26 18:36:53 +03:00
func RepoMustNotBeArchived ( ) func ( ctx * Context ) {
2019-01-23 21:58:38 +03:00
return func ( ctx * Context ) {
if ctx . Repo . Repository . IsArchived {
ctx . NotFound ( "IsArchived" , fmt . Errorf ( ctx . Tr ( "repo.archive.title" ) ) )
}
}
}
2020-01-15 11:32:57 +03:00
// CanCommitToBranchResults represents the results of CanCommitToBranch
type CanCommitToBranchResults struct {
CanCommitToBranch bool
EditorEnabled bool
UserCanPush bool
RequireSigned bool
WillSign bool
SigningKey string
WontSignReason string
}
2017-05-02 03:49:55 +03:00
// CanCommitToBranch returns true if repository is editable and user has proper access level
2022-08-25 05:31:57 +03:00
//
// and branch is not protected for push
2022-01-20 02:26:57 +03:00
func ( r * Repository ) CanCommitToBranch ( ctx context . Context , doer * user_model . User ) ( CanCommitToBranchResults , error ) {
2023-01-16 11:00:22 +03:00
protectedBranch , err := git_model . GetFirstMatchProtectedBranchRule ( ctx , r . Repository . ID , r . BranchName )
2017-05-02 03:49:55 +03:00
if err != nil {
2020-01-15 11:32:57 +03:00
return CanCommitToBranchResults { } , err
2017-05-02 03:49:55 +03:00
}
2020-01-15 11:32:57 +03:00
userCanPush := true
requireSigned := false
if protectedBranch != nil {
2023-01-16 11:00:22 +03:00
protectedBranch . Repo = r . Repository
userCanPush = protectedBranch . CanUserPush ( ctx , doer )
2020-01-15 11:32:57 +03:00
requireSigned = protectedBranch . RequireSignedCommits
}
2022-01-20 02:26:57 +03:00
sign , keyID , _ , err := asymkey_service . SignCRUDAction ( ctx , r . Repository . RepoPath ( ) , doer , r . Repository . RepoPath ( ) , git . BranchPrefix + r . BranchName )
2020-01-15 11:32:57 +03:00
2023-07-22 17:14:27 +03:00
canCommit := r . CanEnableEditor ( ctx , doer ) && userCanPush
2020-01-15 11:32:57 +03:00
if requireSigned {
canCommit = canCommit && sign
}
wontSignReason := ""
if err != nil {
2021-12-10 11:14:24 +03:00
if asymkey_service . IsErrWontSign ( err ) {
wontSignReason = string ( err . ( * asymkey_service . ErrWontSign ) . Reason )
2020-01-15 11:32:57 +03:00
err = nil
} else {
wontSignReason = "error"
}
}
return CanCommitToBranchResults {
CanCommitToBranch : canCommit ,
2023-07-22 17:14:27 +03:00
EditorEnabled : r . CanEnableEditor ( ctx , doer ) ,
2020-01-15 11:32:57 +03:00
UserCanPush : userCanPush ,
RequireSigned : requireSigned ,
WillSign : sign ,
SigningKey : keyID ,
WontSignReason : wontSignReason ,
} , err
2017-05-02 03:49:55 +03:00
}
2017-09-12 09:48:13 +03:00
// CanUseTimetracker returns whether or not a user can use the timetracker.
2022-06-13 12:37:59 +03:00
func ( r * Repository ) CanUseTimetracker ( issue * issues_model . Issue , user * user_model . User ) bool {
2017-09-12 09:48:13 +03:00
// Checking for following:
// 1. Is timetracker enabled
// 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this?
2022-06-13 12:37:59 +03:00
isAssigned , _ := issues_model . IsUserAssignedToIssue ( db . DefaultContext , issue , user )
2022-12-10 05:46:31 +03:00
return r . Repository . IsTimetrackerEnabled ( db . DefaultContext ) && ( ! r . Repository . AllowOnlyContributorsToTrackTime ( db . DefaultContext ) ||
2020-01-20 15:00:32 +03:00
r . Permission . CanWriteIssuesOrPulls ( issue . IsPull ) || issue . IsPoster ( user . ID ) || isAssigned )
2017-09-12 09:48:13 +03:00
}
2018-07-18 00:23:58 +03:00
// CanCreateIssueDependencies returns whether or not a user can create dependencies.
2021-11-24 12:49:20 +03:00
func ( r * Repository ) CanCreateIssueDependencies ( user * user_model . User , isPull bool ) bool {
2022-12-10 05:46:31 +03:00
return r . Repository . IsDependenciesEnabled ( db . DefaultContext ) && r . Permission . CanWriteIssuesOrPulls ( isPull )
2018-07-18 00:23:58 +03:00
}
2017-10-26 04:37:33 +03:00
// GetCommitsCount returns cached commit count for current view
func ( r * Repository ) GetCommitsCount ( ) ( int64 , error ) {
2023-04-19 16:40:42 +03:00
if r . Commit == nil {
return 0 , nil
}
2017-10-26 04:37:33 +03:00
var contextName string
if r . IsViewBranch {
contextName = r . BranchName
} else if r . IsViewTag {
contextName = r . TagName
} else {
contextName = r . CommitID
}
return cache . GetInt64 ( r . Repository . GetCommitsCountCacheKey ( contextName , r . IsViewBranch || r . IsViewTag ) , func ( ) ( int64 , error ) {
return r . Commit . CommitsCount ( )
} )
}
2020-11-08 20:21:54 +03:00
// GetCommitGraphsCount returns cached commit count for current view
2022-01-20 02:26:57 +03:00
func ( r * Repository ) GetCommitGraphsCount ( ctx context . Context , hidePRRefs bool , branches , files [ ] string ) ( int64 , error ) {
2020-11-08 20:21:54 +03:00
cacheKey := fmt . Sprintf ( "commits-count-%d-graph-%t-%s-%s" , r . Repository . ID , hidePRRefs , branches , files )
return cache . GetInt64 ( cacheKey , func ( ) ( int64 , error ) {
if len ( branches ) == 0 {
2022-01-20 02:26:57 +03:00
return git . AllCommitsCount ( ctx , r . Repository . RepoPath ( ) , hidePRRefs , files ... )
2020-11-08 20:21:54 +03:00
}
2023-05-08 10:10:53 +03:00
return git . CommitsCount ( ctx ,
git . CommitsCountOptions {
RepoPath : r . Repository . RepoPath ( ) ,
Revision : branches ,
RelPath : files ,
} )
2020-11-08 20:21:54 +03:00
} )
}
2017-10-30 05:04:25 +03:00
// BranchNameSubURL sub-URL for the BranchName field
func ( r * Repository ) BranchNameSubURL ( ) string {
switch {
case r . IsViewBranch :
2021-11-16 21:18:25 +03:00
return "branch/" + util . PathEscapeSegments ( r . BranchName )
2017-10-30 05:04:25 +03:00
case r . IsViewTag :
2021-11-18 02:50:17 +03:00
return "tag/" + util . PathEscapeSegments ( r . TagName )
2017-10-30 05:04:25 +03:00
case r . IsViewCommit :
2021-11-18 02:50:17 +03:00
return "commit/" + util . PathEscapeSegments ( r . CommitID )
2017-10-30 05:04:25 +03:00
}
2019-04-02 10:48:31 +03:00
log . Error ( "Unknown view type for repo: %v" , r )
2017-10-30 05:04:25 +03:00
return ""
}
2019-04-17 19:06:35 +03:00
// FileExists returns true if a file exists in the given repo branch
2021-12-20 07:41:31 +03:00
func ( r * Repository ) FileExists ( path , branch string ) ( bool , error ) {
2019-04-17 19:06:35 +03:00
if branch == "" {
branch = r . Repository . DefaultBranch
}
commit , err := r . GitRepo . GetBranchCommit ( branch )
if err != nil {
return false , err
}
if _ , err := commit . GetTreeEntryByPath ( path ) ; err != nil {
return false , err
}
return true , nil
}
2016-08-12 03:07:09 +03:00
// GetEditorconfig returns the .editorconfig definition if found in the
// HEAD of the default repo branch.
2023-04-06 23:01:20 +03:00
func ( r * Repository ) GetEditorconfig ( optCommit ... * git . Commit ) ( cfg * editorconfig . Editorconfig , warning , err error ) {
2019-10-13 16:23:14 +03:00
if r . GitRepo == nil {
2023-04-06 23:01:20 +03:00
return nil , nil , nil
2019-10-13 16:23:14 +03:00
}
2023-04-06 23:01:20 +03:00
var commit * git . Commit
2022-04-21 18:17:57 +03:00
if len ( optCommit ) != 0 {
commit = optCommit [ 0 ]
} else {
commit , err = r . GitRepo . GetBranchCommit ( r . Repository . DefaultBranch )
if err != nil {
2023-04-06 23:01:20 +03:00
return nil , nil , err
2022-04-21 18:17:57 +03:00
}
2016-08-12 03:07:09 +03:00
}
treeEntry , err := commit . GetTreeEntryByPath ( ".editorconfig" )
if err != nil {
2023-04-06 23:01:20 +03:00
return nil , nil , err
2016-08-12 03:07:09 +03:00
}
2017-11-29 04:50:39 +03:00
if treeEntry . Blob ( ) . Size ( ) >= setting . UI . MaxDisplayFileSize {
2023-04-06 23:01:20 +03:00
return nil , nil , git . ErrNotExist { ID : "" , RelPath : ".editorconfig" }
2017-11-29 04:50:39 +03:00
}
2019-04-19 15:17:27 +03:00
reader , err := treeEntry . Blob ( ) . DataAsync ( )
2016-08-12 03:07:09 +03:00
if err != nil {
2023-04-06 23:01:20 +03:00
return nil , nil , err
2016-08-12 03:07:09 +03:00
}
2019-04-19 15:17:27 +03:00
defer reader . Close ( )
2023-04-06 23:01:20 +03:00
return editorconfig . ParseGraceful ( reader )
2016-08-12 03:07:09 +03:00
}
2016-11-25 09:51:01 +03:00
// RetrieveBaseRepo retrieves base repository
2021-12-10 04:27:50 +03:00
func RetrieveBaseRepo ( ctx * Context , repo * repo_model . Repository ) {
2015-09-01 18:43:53 +03:00
// Non-fork repository will not return error in this method.
2022-12-03 05:48:26 +03:00
if err := repo . GetBaseRepo ( ctx ) ; err != nil {
2021-12-10 04:27:50 +03:00
if repo_model . IsErrRepoNotExist ( err ) {
2015-09-01 18:43:53 +03:00
repo . IsFork = false
repo . ForkID = 0
return
}
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetBaseRepo" , err )
2015-09-01 18:43:53 +03:00
return
2023-02-18 15:11:03 +03:00
} else if err = repo . BaseRepo . LoadOwner ( ctx ) ; err != nil {
ctx . ServerError ( "BaseRepo.LoadOwner" , err )
2015-09-01 18:43:53 +03:00
return
}
}
2019-11-11 18:15:29 +03:00
// RetrieveTemplateRepo retrieves template repository used to generate this repository
2021-12-10 04:27:50 +03:00
func RetrieveTemplateRepo ( ctx * Context , repo * repo_model . Repository ) {
2019-11-11 18:15:29 +03:00
// Non-generated repository will not return error in this method.
2022-05-20 17:08:52 +03:00
templateRepo , err := repo_model . GetTemplateRepo ( ctx , repo )
2021-12-10 04:27:50 +03:00
if err != nil {
if repo_model . IsErrRepoNotExist ( err ) {
2019-11-11 18:15:29 +03:00
repo . TemplateID = 0
return
}
ctx . ServerError ( "GetTemplateRepo" , err )
return
2023-02-18 15:11:03 +03:00
} else if err = templateRepo . LoadOwner ( ctx ) ; err != nil {
ctx . ServerError ( "TemplateRepo.LoadOwner" , err )
2019-11-11 18:15:29 +03:00
return
}
2022-05-11 13:09:36 +03:00
perm , err := access_model . GetUserRepoPermission ( ctx , templateRepo , ctx . Doer )
2019-11-20 22:44:35 +03:00
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
return
}
2021-11-09 22:57:58 +03:00
if ! perm . CanRead ( unit_model . TypeCode ) {
2019-11-11 18:15:29 +03:00
repo . TemplateID = 0
}
}
2017-09-23 16:24:24 +03:00
// ComposeGoGetImport returns go-get-import meta content.
func ComposeGoGetImport ( owner , repo string ) string {
2019-05-28 00:08:38 +03:00
/// setting.AppUrl is guaranteed to be parse as url
appURL , _ := url . Parse ( setting . AppURL )
return path . Join ( appURL . Host , setting . AppSubURL , url . PathEscape ( owner ) , url . PathEscape ( repo ) )
2016-08-08 00:29:16 +03:00
}
2017-09-23 16:24:24 +03:00
// EarlyResponseForGoGetMeta responses appropriate go-get meta with status 200
2016-08-08 00:29:16 +03:00
// if user does not have actual access to the requested repository,
// or the owner or repository does not exist at all.
// This is particular a workaround for "go get" command which does not respect
// .netrc file.
2017-09-23 16:24:24 +03:00
func EarlyResponseForGoGetMeta ( ctx * Context ) {
username := ctx . Params ( ":username" )
2019-09-06 16:44:59 +03:00
reponame := strings . TrimSuffix ( ctx . Params ( ":reponame" ) , ".git" )
if username == "" || reponame == "" {
2021-12-15 09:59:57 +03:00
ctx . PlainText ( http . StatusBadRequest , "invalid repository path" )
2019-09-06 16:44:59 +03:00
return
}
2023-05-12 12:44:37 +03:00
var cloneURL string
if setting . Repository . GoGetCloneURLProtocol == "ssh" {
cloneURL = repo_model . ComposeSSHCloneURL ( username , reponame )
} else {
cloneURL = repo_model . ComposeHTTPSCloneURL ( username , reponame )
}
goImportContent := fmt . Sprintf ( "%s git %s" , ComposeGoGetImport ( username , reponame ) , cloneURL )
2022-04-01 11:47:50 +03:00
htmlMeta := fmt . Sprintf ( ` <meta name="go-import" content="%s"> ` , html . EscapeString ( goImportContent ) )
ctx . PlainText ( http . StatusOK , htmlMeta )
2016-08-08 00:29:16 +03:00
}
2017-02-05 17:35:03 +03:00
// RedirectToRepo redirect to a differently-named repository
2023-05-21 04:50:53 +03:00
func RedirectToRepo ( ctx * Base , redirectRepoID int64 ) {
2017-02-05 17:35:03 +03:00
ownerName := ctx . Params ( ":username" )
previousRepoName := ctx . Params ( ":reponame" )
2022-12-03 05:48:26 +03:00
repo , err := repo_model . GetRepositoryByID ( ctx , redirectRepoID )
2017-02-05 17:35:03 +03:00
if err != nil {
2023-05-21 04:50:53 +03:00
log . Error ( "GetRepositoryByID: %v" , err )
ctx . Error ( http . StatusInternalServerError , "GetRepositoryByID" )
2017-02-05 17:35:03 +03:00
return
}
redirectPath := strings . Replace (
2021-11-16 21:18:25 +03:00
ctx . Req . URL . EscapedPath ( ) ,
url . PathEscape ( ownerName ) + "/" + url . PathEscape ( previousRepoName ) ,
url . PathEscape ( repo . OwnerName ) + "/" + url . PathEscape ( repo . Name ) ,
2017-02-05 17:35:03 +03:00
1 ,
)
2019-04-25 08:51:40 +03:00
if ctx . Req . URL . RawQuery != "" {
redirectPath += "?" + ctx . Req . URL . RawQuery
}
2022-03-23 07:54:07 +03:00
ctx . Redirect ( path . Join ( setting . AppSubURL , redirectPath ) , http . StatusTemporaryRedirect )
2017-02-05 17:35:03 +03:00
}
2017-09-19 14:44:49 +03:00
2021-12-10 04:27:50 +03:00
func repoAssignment ( ctx * Context , repo * repo_model . Repository ) {
2018-11-28 14:26:14 +03:00
var err error
2023-02-18 15:11:03 +03:00
if err = repo . LoadOwner ( ctx ) ; err != nil {
ctx . ServerError ( "LoadOwner" , err )
2019-03-05 23:15:24 +03:00
return
}
2022-05-11 13:09:36 +03:00
ctx . Repo . Permission , err = access_model . GetUserRepoPermission ( ctx , repo , ctx . Doer )
2018-11-28 14:26:14 +03:00
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
return
2017-10-05 10:32:25 +03:00
}
// Check access.
2021-10-28 05:54:40 +03:00
if ! ctx . Repo . Permission . HasAccess ( ) {
2021-08-11 03:31:13 +03:00
if ctx . FormString ( "go-get" ) == "1" {
2017-10-05 10:32:25 +03:00
EarlyResponseForGoGetMeta ( ctx )
return
}
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "no access right" , nil )
2017-10-05 10:32:25 +03:00
return
}
ctx . Data [ "HasAccess" ] = true
2018-11-28 14:26:14 +03:00
ctx . Data [ "Permission" ] = & ctx . Repo . Permission
2017-10-05 10:32:25 +03:00
if repo . IsMirror {
2023-05-15 22:02:10 +03:00
pullMirror , err := repo_model . GetMirrorByRepoID ( ctx , repo . ID )
2022-06-13 19:12:59 +03:00
if err == nil {
2023-05-15 22:02:10 +03:00
ctx . Data [ "PullMirror" ] = pullMirror
2022-06-13 19:12:59 +03:00
} else if err != repo_model . ErrMirrorNotExist {
ctx . ServerError ( "GetMirrorByRepoID" , err )
return
2022-05-03 16:55:17 +03:00
}
2017-10-05 10:32:25 +03:00
}
2021-12-10 04:27:50 +03:00
2022-07-30 19:45:59 +03:00
pushMirrors , _ , err := repo_model . GetPushMirrorsByRepoID ( ctx , repo . ID , db . ListOptions { } )
2021-12-10 04:27:50 +03:00
if err != nil {
ctx . ServerError ( "GetPushMirrorsByRepoID" , err )
2021-06-14 20:20:43 +03:00
return
}
2017-10-05 10:32:25 +03:00
ctx . Repo . Repository = repo
2021-12-10 04:27:50 +03:00
ctx . Data [ "PushMirrors" ] = pushMirrors
2017-10-05 10:32:25 +03:00
ctx . Data [ "RepoName" ] = ctx . Repo . Repository . Name
2019-01-18 03:01:04 +03:00
ctx . Data [ "IsEmptyRepo" ] = ctx . Repo . Repository . IsEmpty
2017-10-05 10:32:25 +03:00
}
2021-01-29 18:35:30 +03:00
// RepoIDAssignment returns a handler which assigns the repo to the context.
2021-01-26 18:36:53 +03:00
func RepoIDAssignment ( ) func ( ctx * Context ) {
2017-09-18 17:52:20 +03:00
return func ( ctx * Context ) {
repoID := ctx . ParamsInt64 ( ":repoid" )
// Get repository.
2022-12-03 05:48:26 +03:00
repo , err := repo_model . GetRepositoryByID ( ctx , repoID )
2017-09-18 17:52:20 +03:00
if err != nil {
2021-12-10 04:27:50 +03:00
if repo_model . IsErrRepoNotExist ( err ) {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "GetRepositoryByID" , nil )
2017-09-18 17:52:20 +03:00
} else {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetRepositoryByID" , err )
2017-09-18 17:52:20 +03:00
}
return
}
2017-10-05 10:32:25 +03:00
repoAssignment ( ctx , repo )
2017-09-18 17:52:20 +03:00
}
}
2017-02-05 17:35:03 +03:00
2021-01-29 18:35:30 +03:00
// RepoAssignment returns a middleware to handle repository assignment
2023-07-07 08:31:56 +03:00
func RepoAssignment ( ctx * Context ) context . CancelFunc {
2022-04-21 18:17:57 +03:00
if _ , repoAssignmentOnce := ctx . Data [ "repoAssignmentExecuted" ] ; repoAssignmentOnce {
log . Trace ( "RepoAssignment was exec already, skipping second call ..." )
2023-07-07 08:31:56 +03:00
return nil
2022-04-21 18:17:57 +03:00
}
ctx . Data [ "repoAssignmentExecuted" ] = true
2021-04-10 03:26:08 +03:00
var (
2021-11-24 12:49:20 +03:00
owner * user_model . User
2021-04-10 03:26:08 +03:00
err error
)
userName := ctx . Params ( ":username" )
repoName := ctx . Params ( ":reponame" )
repoName = strings . TrimSuffix ( repoName , ".git" )
2023-04-23 02:38:25 +03:00
if setting . Other . EnableFeed {
2022-11-21 08:14:58 +03:00
repoName = strings . TrimSuffix ( repoName , ".rss" )
repoName = strings . TrimSuffix ( repoName , ".atom" )
}
2021-04-10 03:26:08 +03:00
// Check if the user is the same as the repository owner
2022-03-22 10:03:22 +03:00
if ctx . IsSigned && ctx . Doer . LowerName == strings . ToLower ( userName ) {
owner = ctx . Doer
2021-04-10 03:26:08 +03:00
} else {
2022-05-20 17:08:52 +03:00
owner , err = user_model . GetUserByName ( ctx , userName )
2021-04-10 03:26:08 +03:00
if err != nil {
2021-11-24 12:49:20 +03:00
if user_model . IsErrUserNotExist ( err ) {
2022-10-11 14:54:44 +03:00
// go-get does not support redirects
// https://github.com/golang/go/issues/19760
2021-08-11 03:31:13 +03:00
if ctx . FormString ( "go-get" ) == "1" {
2021-04-10 03:26:08 +03:00
EarlyResponseForGoGetMeta ( ctx )
2023-07-07 08:31:56 +03:00
return nil
2021-01-26 18:36:53 +03:00
}
2022-10-11 14:54:44 +03:00
if redirectUserID , err := user_model . LookupUserRedirect ( userName ) ; err == nil {
2023-05-21 04:50:53 +03:00
RedirectToUser ( ctx . Base , userName , redirectUserID )
2022-10-11 14:54:44 +03:00
} else if user_model . IsErrUserRedirectNotExist ( err ) {
ctx . NotFound ( "GetUserByName" , nil )
} else {
ctx . ServerError ( "LookupUserRedirect" , err )
}
2021-04-10 03:26:08 +03:00
} else {
ctx . ServerError ( "GetUserByName" , err )
2021-01-26 18:36:53 +03:00
}
2023-07-07 08:31:56 +03:00
return nil
2021-04-10 03:26:08 +03:00
}
}
ctx . Repo . Owner = owner
2022-03-26 12:04:22 +03:00
ctx . ContextUser = owner
2023-09-06 09:38:14 +03:00
ctx . Data [ "ContextUser" ] = ctx . ContextUser
2021-04-10 03:26:08 +03:00
ctx . Data [ "Username" ] = ctx . Repo . Owner . Name
2014-03-30 06:09:59 +04:00
2022-03-23 16:29:18 +03:00
// redirect link to wiki
if strings . HasSuffix ( repoName , ".wiki" ) {
// ctx.Req.URL.Path does not have the preceding appSubURL - any redirect must have this added
// Now we happen to know that all of our paths are: /:username/:reponame/whatever_else
originalRepoName := ctx . Params ( ":reponame" )
redirectRepoName := strings . TrimSuffix ( repoName , ".wiki" )
redirectRepoName += originalRepoName [ len ( redirectRepoName ) + 5 : ]
redirectPath := strings . Replace (
ctx . Req . URL . EscapedPath ( ) ,
url . PathEscape ( userName ) + "/" + url . PathEscape ( originalRepoName ) ,
url . PathEscape ( userName ) + "/" + url . PathEscape ( redirectRepoName ) + "/wiki" ,
1 ,
)
if ctx . Req . URL . RawQuery != "" {
redirectPath += "?" + ctx . Req . URL . RawQuery
}
ctx . Redirect ( path . Join ( setting . AppSubURL , redirectPath ) )
2023-07-07 08:31:56 +03:00
return nil
2022-03-23 16:29:18 +03:00
}
2021-04-10 03:26:08 +03:00
// Get repository.
2021-12-10 04:27:50 +03:00
repo , err := repo_model . GetRepositoryByName ( owner . ID , repoName )
2021-04-10 03:26:08 +03:00
if err != nil {
2021-12-10 04:27:50 +03:00
if repo_model . IsErrRepoNotExist ( err ) {
2021-12-12 18:48:20 +03:00
redirectRepoID , err := repo_model . LookupRedirect ( owner . ID , repoName )
2021-04-10 03:26:08 +03:00
if err == nil {
2023-05-21 04:50:53 +03:00
RedirectToRepo ( ctx . Base , redirectRepoID )
2021-12-12 18:48:20 +03:00
} else if repo_model . IsErrRedirectNotExist ( err ) {
2021-08-11 03:31:13 +03:00
if ctx . FormString ( "go-get" ) == "1" {
2021-04-10 03:26:08 +03:00
EarlyResponseForGoGetMeta ( ctx )
2023-07-07 08:31:56 +03:00
return nil
2014-03-15 20:03:23 +04:00
}
2021-04-10 03:26:08 +03:00
ctx . NotFound ( "GetRepositoryByName" , nil )
} else {
ctx . ServerError ( "LookupRepoRedirect" , err )
2014-03-15 20:03:23 +04:00
}
2021-04-10 03:26:08 +03:00
} else {
ctx . ServerError ( "GetRepositoryByName" , err )
}
2023-07-07 08:31:56 +03:00
return nil
2021-04-10 03:26:08 +03:00
}
repo . Owner = owner
2014-03-15 20:03:23 +04:00
2021-04-10 03:26:08 +03:00
repoAssignment ( ctx , repo )
if ctx . Written ( ) {
2023-07-07 08:31:56 +03:00
return nil
2021-04-10 03:26:08 +03:00
}
2014-04-12 05:47:39 +04:00
2021-04-10 03:26:08 +03:00
ctx . Repo . RepoLink = repo . Link ( )
ctx . Data [ "RepoLink" ] = ctx . Repo . RepoLink
ctx . Data [ "RepoRelPath" ] = ctx . Repo . Owner . Name + "/" + ctx . Repo . Repository . Name
2014-03-30 09:30:17 +04:00
2023-04-23 02:38:25 +03:00
if setting . Other . EnableFeed {
2023-04-07 11:48:23 +03:00
ctx . Data [ "EnableFeed" ] = true
ctx . Data [ "FeedURL" ] = ctx . Repo . RepoLink
}
2022-12-10 05:46:31 +03:00
unit , err := ctx . Repo . Repository . GetUnit ( ctx , unit_model . TypeExternalTracker )
2021-04-10 03:26:08 +03:00
if err == nil {
ctx . Data [ "RepoExternalIssuesLink" ] = unit . ExternalTrackerConfig ( ) . ExternalTrackerURL
}
2014-03-30 07:38:41 +04:00
2022-12-10 05:46:31 +03:00
ctx . Data [ "NumTags" ] , err = repo_model . GetReleaseCountByRepoID ( ctx , ctx . Repo . Repository . ID , repo_model . FindReleasesOptions {
2022-10-03 15:05:53 +03:00
IncludeDrafts : true ,
IncludeTags : true ,
HasSha1 : util . OptionalBoolTrue , // only draft releases which are created with existing tags
2021-04-10 03:26:08 +03:00
} )
if err != nil {
ctx . ServerError ( "GetReleaseCountByRepoID" , err )
2023-07-07 08:31:56 +03:00
return nil
2021-04-10 03:26:08 +03:00
}
2022-12-10 05:46:31 +03:00
ctx . Data [ "NumReleases" ] , err = repo_model . GetReleaseCountByRepoID ( ctx , ctx . Repo . Repository . ID , repo_model . FindReleasesOptions { } )
2021-04-10 03:26:08 +03:00
if err != nil {
ctx . ServerError ( "GetReleaseCountByRepoID" , err )
2023-07-07 08:31:56 +03:00
return nil
2021-04-10 03:26:08 +03:00
}
2019-02-20 02:09:47 +03:00
2021-04-10 03:26:08 +03:00
ctx . Data [ "Title" ] = owner . Name + "/" + repo . Name
ctx . Data [ "Repository" ] = repo
ctx . Data [ "Owner" ] = ctx . Repo . Repository . Owner
ctx . Data [ "IsRepositoryOwner" ] = ctx . Repo . IsOwner ( )
ctx . Data [ "IsRepositoryAdmin" ] = ctx . Repo . IsAdmin ( )
ctx . Data [ "RepoOwnerIsOrganization" ] = repo . Owner . IsOrganization ( )
2021-11-09 22:57:58 +03:00
ctx . Data [ "CanWriteCode" ] = ctx . Repo . CanWrite ( unit_model . TypeCode )
ctx . Data [ "CanWriteIssues" ] = ctx . Repo . CanWrite ( unit_model . TypeIssues )
ctx . Data [ "CanWritePulls" ] = ctx . Repo . CanWrite ( unit_model . TypePullRequests )
2014-04-14 05:00:12 +04:00
2023-09-14 20:09:32 +03:00
canSignedUserFork , err := repo_module . CanUserForkRepo ( ctx , ctx . Doer , ctx . Repo . Repository )
2021-12-13 04:59:09 +03:00
if err != nil {
ctx . ServerError ( "CanUserForkRepo" , err )
2023-07-07 08:31:56 +03:00
return nil
2021-12-13 04:59:09 +03:00
}
ctx . Data [ "CanSignedUserFork" ] = canSignedUserFork
2022-06-06 11:01:49 +03:00
userAndOrgForks , err := repo_model . GetForksByUserAndOrgs ( ctx , ctx . Doer , ctx . Repo . Repository )
2021-12-13 04:59:09 +03:00
if err != nil {
ctx . ServerError ( "GetForksByUserAndOrgs" , err )
2023-07-07 08:31:56 +03:00
return nil
2021-04-10 03:26:08 +03:00
}
2021-12-13 04:59:09 +03:00
ctx . Data [ "UserAndOrgForks" ] = userAndOrgForks
// canSignedUserFork is true if the current user doesn't have a fork of this repo yet or
// if he owns an org that doesn't have a fork of this repo yet
// If multiple forks are available or if the user can fork to another account, but there is already a fork: open selection dialog
ctx . Data [ "ShowForkModal" ] = len ( userAndOrgForks ) > 1 || ( canSignedUserFork && len ( userAndOrgForks ) > 0 )
2017-10-15 18:06:07 +03:00
2022-03-29 06:21:30 +03:00
ctx . Data [ "RepoCloneLink" ] = repo . CloneLink ( )
cloneButtonShowHTTPS := ! setting . Repository . DisableHTTPGit
cloneButtonShowSSH := ! setting . SSH . Disabled && ( ctx . IsSigned || setting . SSH . ExposeAnonymous )
if ! cloneButtonShowHTTPS && ! cloneButtonShowSSH {
// We have to show at least one link, so we just show the HTTPS
cloneButtonShowHTTPS = true
}
ctx . Data [ "CloneButtonShowHTTPS" ] = cloneButtonShowHTTPS
ctx . Data [ "CloneButtonShowSSH" ] = cloneButtonShowSSH
ctx . Data [ "CloneButtonOriginLink" ] = ctx . Data [ "RepoCloneLink" ] // it may be rewritten to the WikiCloneLink by the router middleware
2021-04-10 03:26:08 +03:00
ctx . Data [ "RepoSearchEnabled" ] = setting . Indexer . RepoIndexerEnabled
2022-01-27 11:30:51 +03:00
if setting . Indexer . RepoIndexerEnabled {
Refactor indexer (#25174)
Refactor `modules/indexer` to make it more maintainable. And it can be
easier to support more features. I'm trying to solve some of issue
searching, this is a precursor to making functional changes.
Current supported engines and the index versions:
| engines | issues | code |
| - | - | - |
| db | Just a wrapper for database queries, doesn't need version | - |
| bleve | The version of index is **2** | The version of index is **6**
|
| elasticsearch | The old index has no version, will be treated as
version **0** in this PR | The version of index is **1** |
| meilisearch | The old index has no version, will be treated as version
**0** in this PR | - |
## Changes
### Split
Splited it into mutiple packages
```text
indexer
├── internal
│ ├── bleve
│ ├── db
│ ├── elasticsearch
│ └── meilisearch
├── code
│ ├── bleve
│ ├── elasticsearch
│ └── internal
└── issues
├── bleve
├── db
├── elasticsearch
├── internal
└── meilisearch
```
- `indexer/interanal`: Internal shared package for indexer.
- `indexer/interanal/[engine]`: Internal shared package for each engine
(bleve/db/elasticsearch/meilisearch).
- `indexer/code`: Implementations for code indexer.
- `indexer/code/internal`: Internal shared package for code indexer.
- `indexer/code/[engine]`: Implementation via each engine for code
indexer.
- `indexer/issues`: Implementations for issues indexer.
### Deduplication
- Combine `Init/Ping/Close` for code indexer and issues indexer.
- ~Combine `issues.indexerHolder` and `code.wrappedIndexer` to
`internal.IndexHolder`.~ Remove it, use dummy indexer instead when the
indexer is not ready.
- Duplicate two copies of creating ES clients.
- Duplicate two copies of `indexerID()`.
### Enhancement
- [x] Support index version for elasticsearch issues indexer, the old
index without version will be treated as version 0.
- [x] Fix spell of `elastic_search/ElasticSearch`, it should be
`Elasticsearch`.
- [x] Improve versioning of ES index. We don't need `Aliases`:
- Gitea does't need aliases for "Zero Downtime" because it never delete
old indexes.
- The old code of issues indexer uses the orignal name to create issue
index, so it's tricky to convert it to an alias.
- [x] Support index version for meilisearch issues indexer, the old
index without version will be treated as version 0.
- [x] Do "ping" only when `Ping` has been called, don't ping
periodically and cache the status.
- [x] Support the context parameter whenever possible.
- [x] Fix outdated example config.
- [x] Give up the requeue logic of issues indexer: When indexing fails,
call Ping to check if it was caused by the engine being unavailable, and
only requeue the task if the engine is unavailable.
- It is fragile and tricky, could cause data losing (It did happen when
I was doing some tests for this PR). And it works for ES only.
- Just always requeue the failed task, if it caused by bad data, it's a
bug of Gitea which should be fixed.
---------
Co-authored-by: Giteabot <teabot@gitea.io>
2023-06-23 15:37:56 +03:00
ctx . Data [ "CodeIndexerUnavailable" ] = ! code_indexer . IsAvailable ( ctx )
2022-01-27 11:30:51 +03:00
}
2014-03-30 07:38:41 +04:00
2021-04-10 03:26:08 +03:00
if ctx . IsSigned {
2023-09-15 09:13:19 +03:00
ctx . Data [ "IsWatchingRepo" ] = repo_model . IsWatching ( ctx , ctx . Doer . ID , repo . ID )
2022-05-20 17:08:52 +03:00
ctx . Data [ "IsStaringRepo" ] = repo_model . IsStaring ( ctx , ctx . Doer . ID , repo . ID )
2021-04-10 03:26:08 +03:00
}
2015-10-03 02:58:36 +03:00
2021-04-10 03:26:08 +03:00
if repo . IsFork {
RetrieveBaseRepo ( ctx , repo )
if ctx . Written ( ) {
2023-07-07 08:31:56 +03:00
return nil
2021-04-10 03:26:08 +03:00
}
}
2019-04-15 23:48:35 +03:00
2021-04-10 03:26:08 +03:00
if repo . IsGenerated ( ) {
RetrieveTemplateRepo ( ctx , repo )
if ctx . Written ( ) {
2023-07-07 08:31:56 +03:00
return nil
2021-04-10 03:26:08 +03:00
}
}
2014-03-15 20:03:23 +04:00
2021-11-23 01:32:16 +03:00
isHomeOrSettings := ctx . Link == ctx . Repo . RepoLink || ctx . Link == ctx . Repo . RepoLink + "/settings" || strings . HasPrefix ( ctx . Link , ctx . Repo . RepoLink + "/settings/" )
2021-04-10 03:26:08 +03:00
// Disable everything when the repo is being created
2021-11-23 01:32:16 +03:00
if ctx . Repo . Repository . IsBeingCreated ( ) || ctx . Repo . Repository . IsBroken ( ) {
2021-04-10 03:26:08 +03:00
ctx . Data [ "BranchName" ] = ctx . Repo . Repository . DefaultBranch
2021-11-23 01:32:16 +03:00
if ! isHomeOrSettings {
ctx . Redirect ( ctx . Repo . RepoLink )
}
2023-07-07 08:31:56 +03:00
return nil
2021-04-10 03:26:08 +03:00
}
2019-11-13 10:01:19 +03:00
2022-03-29 22:13:41 +03:00
gitRepo , err := git . OpenRepository ( ctx , repo_model . RepoPath ( userName , repoName ) )
2021-04-10 03:26:08 +03:00
if err != nil {
2021-11-23 01:32:16 +03:00
if strings . Contains ( err . Error ( ) , "repository does not exist" ) || strings . Contains ( err . Error ( ) , "no such file or directory" ) {
log . Error ( "Repository %-v has a broken repository on the file system: %s Error: %v" , ctx . Repo . Repository , ctx . Repo . Repository . RepoPath ( ) , err )
2023-04-19 16:40:42 +03:00
ctx . Repo . Repository . MarkAsBrokenEmpty ( )
2021-11-23 01:32:16 +03:00
ctx . Data [ "BranchName" ] = ctx . Repo . Repository . DefaultBranch
// Only allow access to base of repo or settings
if ! isHomeOrSettings {
ctx . Redirect ( ctx . Repo . RepoLink )
}
2023-07-07 08:31:56 +03:00
return nil
2021-11-23 01:32:16 +03:00
}
2021-12-10 04:27:50 +03:00
ctx . ServerError ( "RepoAssignment Invalid repo " + repo_model . RepoPath ( userName , repoName ) , err )
2023-07-07 08:31:56 +03:00
return nil
2021-04-10 03:26:08 +03:00
}
2022-04-21 18:17:57 +03:00
if ctx . Repo . GitRepo != nil {
ctx . Repo . GitRepo . Close ( )
}
2021-04-10 03:26:08 +03:00
ctx . Repo . GitRepo = gitRepo
2019-10-16 16:08:01 +03:00
2021-04-10 03:26:08 +03:00
// We opened it, we should close it
2023-07-07 08:31:56 +03:00
cancel := func ( ) {
2021-04-10 03:26:08 +03:00
// If it's been set to nil then assume someone else has closed it.
if ctx . Repo . GitRepo != nil {
ctx . Repo . GitRepo . Close ( )
}
2021-05-06 02:30:25 +03:00
}
2019-10-13 16:23:14 +03:00
2021-04-10 03:26:08 +03:00
// Stop at this point when the repo is empty.
if ctx . Repo . Repository . IsEmpty {
ctx . Data [ "BranchName" ] = ctx . Repo . Repository . DefaultBranch
2023-07-07 08:31:56 +03:00
return cancel
2021-04-10 03:26:08 +03:00
}
2016-03-07 07:57:46 +03:00
2023-06-29 13:03:20 +03:00
branchOpts := git_model . FindBranchOptions {
RepoID : ctx . Repo . Repository . ID ,
IsDeletedBranch : util . OptionalBoolFalse ,
ListOptions : db . ListOptions {
ListAll : true ,
} ,
}
branchesTotal , err := git_model . CountBranches ( ctx , branchOpts )
if err != nil {
ctx . ServerError ( "CountBranches" , err )
2023-07-07 08:31:56 +03:00
return cancel
2023-06-29 13:03:20 +03:00
}
2023-07-21 14:20:04 +03:00
// non-empty repo should have at least 1 branch, so this repository's branches haven't been synced yet
2023-06-29 13:03:20 +03:00
if branchesTotal == 0 { // fallback to do a sync immediately
branchesTotal , err = repo_module . SyncRepoBranches ( ctx , ctx . Repo . Repository . ID , 0 )
if err != nil {
ctx . ServerError ( "SyncRepoBranches" , err )
2023-07-07 08:31:56 +03:00
return cancel
2023-06-29 13:03:20 +03:00
}
}
ctx . Data [ "BranchesCount" ] = branchesTotal
2021-03-01 03:47:30 +03:00
2023-07-21 14:20:04 +03:00
// If no branch is set in the request URL, try to guess a default one.
2021-04-10 03:26:08 +03:00
if len ( ctx . Repo . BranchName ) == 0 {
if len ( ctx . Repo . Repository . DefaultBranch ) > 0 && gitRepo . IsBranchExist ( ctx . Repo . Repository . DefaultBranch ) {
ctx . Repo . BranchName = ctx . Repo . Repository . DefaultBranch
2023-07-21 14:20:04 +03:00
} else {
ctx . Repo . BranchName , _ = gitRepo . GetDefaultBranch ( )
if ctx . Repo . BranchName == "" {
// If it still can't get a default branch, fall back to default branch from setting.
// Something might be wrong. Either site admin should fix the repo sync or Gitea should fix a potential bug.
ctx . Repo . BranchName = setting . Repository . DefaultBranch
}
2021-04-10 03:26:08 +03:00
}
2021-11-18 02:50:17 +03:00
ctx . Repo . RefName = ctx . Repo . BranchName
2021-04-10 03:26:08 +03:00
}
ctx . Data [ "BranchName" ] = ctx . Repo . BranchName
// People who have push access or have forked repository can propose a new pull request.
2021-11-22 18:21:55 +03:00
canPush := ctx . Repo . CanWrite ( unit_model . TypeCode ) ||
2023-09-14 20:09:32 +03:00
( ctx . IsSigned && repo_model . HasForkedRepo ( ctx , ctx . Doer . ID , ctx . Repo . Repository . ID ) )
2021-04-10 03:26:08 +03:00
canCompare := false
// Pull request is allowed if this is a fork repository
// and base repository accepts pull requests.
if repo . BaseRepo != nil && repo . BaseRepo . AllowsPulls ( ) {
canCompare = true
ctx . Data [ "BaseRepo" ] = repo . BaseRepo
ctx . Repo . PullRequest . BaseRepo = repo . BaseRepo
ctx . Repo . PullRequest . Allowed = canPush
2021-11-16 21:18:25 +03:00
ctx . Repo . PullRequest . HeadInfoSubURL = url . PathEscape ( ctx . Repo . Owner . Name ) + ":" + util . PathEscapeSegments ( ctx . Repo . BranchName )
2021-04-10 03:26:08 +03:00
} else if repo . AllowsPulls ( ) {
// Or, this is repository accepts pull requests between branches.
canCompare = true
ctx . Data [ "BaseRepo" ] = repo
ctx . Repo . PullRequest . BaseRepo = repo
ctx . Repo . PullRequest . Allowed = canPush
ctx . Repo . PullRequest . SameRepo = true
2021-11-16 21:18:25 +03:00
ctx . Repo . PullRequest . HeadInfoSubURL = util . PathEscapeSegments ( ctx . Repo . BranchName )
2021-04-10 03:26:08 +03:00
}
ctx . Data [ "CanCompareOrPull" ] = canCompare
ctx . Data [ "PullRequestCtx" ] = ctx . Repo . PullRequest
2021-12-10 04:27:50 +03:00
if ctx . Repo . Repository . Status == repo_model . RepositoryPendingTransfer {
2022-12-10 05:46:31 +03:00
repoTransfer , err := models . GetPendingRepositoryTransfer ( ctx , ctx . Repo . Repository )
2021-04-10 03:26:08 +03:00
if err != nil {
ctx . ServerError ( "GetPendingRepositoryTransfer" , err )
2023-07-07 08:31:56 +03:00
return cancel
2021-04-10 03:26:08 +03:00
}
2021-03-01 03:47:30 +03:00
2022-12-10 05:46:31 +03:00
if err := repoTransfer . LoadAttributes ( ctx ) ; err != nil {
2021-04-10 03:26:08 +03:00
ctx . ServerError ( "LoadRecipient" , err )
2023-07-07 08:31:56 +03:00
return cancel
2021-04-10 03:26:08 +03:00
}
ctx . Data [ "RepoTransfer" ] = repoTransfer
2022-03-22 10:03:22 +03:00
if ctx . Doer != nil {
2023-09-16 17:39:12 +03:00
ctx . Data [ "CanUserAcceptTransfer" ] = repoTransfer . CanUserAcceptTransfer ( ctx , ctx . Doer )
2021-04-10 03:26:08 +03:00
}
}
2021-08-11 03:31:13 +03:00
if ctx . FormString ( "go-get" ) == "1" {
2021-04-10 03:26:08 +03:00
ctx . Data [ "GoGetImport" ] = ComposeGoGetImport ( owner . Name , repo . Name )
2023-02-11 09:34:11 +03:00
fullURLPrefix := repo . HTMLURL ( ) + "/src/branch/" + util . PathEscapeSegments ( ctx . Repo . BranchName )
ctx . Data [ "GoDocDirectory" ] = fullURLPrefix + "{/dir}"
ctx . Data [ "GoDocFile" ] = fullURLPrefix + "{/dir}/{file}#L{line}"
2014-03-15 20:03:23 +04:00
}
2022-06-20 13:02:49 +03:00
return cancel
2014-03-15 20:03:23 +04:00
}
2014-05-06 03:58:13 +04:00
2017-10-30 05:04:25 +03:00
// RepoRefType type of repo reference
type RepoRefType int
const (
// RepoRefLegacy unknown type, make educated guess and redirect.
// for backward compatibility with previous URL scheme
RepoRefLegacy RepoRefType = iota
2017-11-04 02:23:59 +03:00
// RepoRefAny is for usage where educated guess is needed
// but redirect can not be made
RepoRefAny
2017-10-30 05:04:25 +03:00
// RepoRefBranch branch
RepoRefBranch
// RepoRefTag tag
RepoRefTag
// RepoRefCommit commit
RepoRefCommit
2018-11-18 21:45:40 +03:00
// RepoRefBlob blob
RepoRefBlob
2017-10-30 05:04:25 +03:00
)
// RepoRef handles repository reference names when the ref name is not
// explicitly given
2021-05-06 02:30:25 +03:00
func RepoRef ( ) func ( * Context ) context . CancelFunc {
2017-10-30 05:04:25 +03:00
// since no ref name is explicitly specified, ok to just use branch
return RepoRefByType ( RepoRefBranch )
}
2019-11-11 10:37:28 +03:00
// RefTypeIncludesBranches returns true if ref type can be a branch
func ( rt RepoRefType ) RefTypeIncludesBranches ( ) bool {
if rt == RepoRefLegacy || rt == RepoRefAny || rt == RepoRefBranch {
return true
}
return false
}
// RefTypeIncludesTags returns true if ref type can be a tag
func ( rt RepoRefType ) RefTypeIncludesTags ( ) bool {
if rt == RepoRefLegacy || rt == RepoRefAny || rt == RepoRefTag {
return true
}
return false
}
2023-05-21 04:50:53 +03:00
func getRefNameFromPath ( ctx * Base , repo * Repository , path string , isExist func ( string ) bool ) string {
2017-10-30 05:04:25 +03:00
refName := ""
parts := strings . Split ( path , "/" )
for i , part := range parts {
refName = strings . TrimPrefix ( refName + "/" + part , "/" )
if isExist ( refName ) {
2023-05-21 04:50:53 +03:00
repo . TreePath = strings . Join ( parts [ i + 1 : ] , "/" )
2017-10-30 05:04:25 +03:00
return refName
}
}
return ""
}
2023-05-21 04:50:53 +03:00
func getRefName ( ctx * Base , repo * Repository , pathType RepoRefType ) string {
2017-10-30 05:04:25 +03:00
path := ctx . Params ( "*" )
switch pathType {
2017-11-04 02:23:59 +03:00
case RepoRefLegacy , RepoRefAny :
2023-05-21 04:50:53 +03:00
if refName := getRefName ( ctx , repo , RepoRefBranch ) ; len ( refName ) > 0 {
2017-10-30 05:04:25 +03:00
return refName
}
2023-05-21 04:50:53 +03:00
if refName := getRefName ( ctx , repo , RepoRefTag ) ; len ( refName ) > 0 {
2017-10-30 05:04:25 +03:00
return refName
}
2020-11-25 23:07:39 +03:00
// For legacy and API support only full commit sha
parts := strings . Split ( path , "/" )
2022-12-27 16:12:49 +03:00
if len ( parts ) > 0 && len ( parts [ 0 ] ) == git . SHAFullLength {
2023-05-21 04:50:53 +03:00
repo . TreePath = strings . Join ( parts [ 1 : ] , "/" )
2020-11-25 23:07:39 +03:00
return parts [ 0 ]
2017-11-04 20:26:38 +03:00
}
2023-05-21 04:50:53 +03:00
if refName := getRefName ( ctx , repo , RepoRefBlob ) ; len ( refName ) > 0 {
2018-11-18 21:45:40 +03:00
return refName
}
2023-05-21 04:50:53 +03:00
repo . TreePath = path
return repo . Repository . DefaultBranch
2017-10-30 05:04:25 +03:00
case RepoRefBranch :
2023-05-21 04:50:53 +03:00
ref := getRefNameFromPath ( ctx , repo , path , repo . GitRepo . IsBranchExist )
2021-10-08 20:03:04 +03:00
if len ( ref ) == 0 {
// maybe it's a renamed branch
2023-05-21 04:50:53 +03:00
return getRefNameFromPath ( ctx , repo , path , func ( s string ) bool {
b , exist , err := git_model . FindRenamedBranch ( ctx , repo . Repository . ID , s )
2021-10-08 20:03:04 +03:00
if err != nil {
log . Error ( "FindRenamedBranch" , err )
return false
}
if ! exist {
return false
}
ctx . Data [ "IsRenamedBranch" ] = true
ctx . Data [ "RenamedBranchName" ] = b . To
return true
} )
}
return ref
2017-10-30 05:04:25 +03:00
case RepoRefTag :
2023-05-21 04:50:53 +03:00
return getRefNameFromPath ( ctx , repo , path , repo . GitRepo . IsTagExist )
2017-10-30 05:04:25 +03:00
case RepoRefCommit :
parts := strings . Split ( path , "/" )
2022-12-27 16:12:49 +03:00
if len ( parts ) > 0 && len ( parts [ 0 ] ) >= 7 && len ( parts [ 0 ] ) <= git . SHAFullLength {
2023-05-21 04:50:53 +03:00
repo . TreePath = strings . Join ( parts [ 1 : ] , "/" )
2017-10-30 05:04:25 +03:00
return parts [ 0 ]
}
2018-11-18 21:45:40 +03:00
case RepoRefBlob :
2023-05-21 04:50:53 +03:00
_ , err := repo . GitRepo . GetBlob ( path )
2018-11-18 21:45:40 +03:00
if err != nil {
return ""
}
return path
2017-10-30 05:04:25 +03:00
default :
2019-04-02 10:48:31 +03:00
log . Error ( "Unrecognized path type: %v" , path )
2017-10-30 05:04:25 +03:00
}
return ""
}
// RepoRefByType handles repository reference name for a specific type
// of repository reference
2021-05-06 06:12:50 +03:00
func RepoRefByType ( refType RepoRefType , ignoreNotExistErr ... bool ) func ( * Context ) context . CancelFunc {
2021-05-06 02:30:25 +03:00
return func ( ctx * Context ) ( cancel context . CancelFunc ) {
2021-04-10 03:26:08 +03:00
// Empty repository does not have reference information.
if ctx . Repo . Repository . IsEmpty {
2023-04-19 16:40:42 +03:00
// assume the user is viewing the (non-existent) default branch
ctx . Repo . IsViewBranch = true
ctx . Repo . BranchName = ctx . Repo . Repository . DefaultBranch
ctx . Data [ "TreePath" ] = ""
2023-07-07 08:31:56 +03:00
return nil
2021-04-10 03:26:08 +03:00
}
var (
refName string
err error
)
if ctx . Repo . GitRepo == nil {
2021-12-10 04:27:50 +03:00
repoPath := repo_model . RepoPath ( ctx . Repo . Owner . Name , ctx . Repo . Repository . Name )
2022-03-29 22:13:41 +03:00
ctx . Repo . GitRepo , err = git . OpenRepository ( ctx , repoPath )
2021-04-10 03:26:08 +03:00
if err != nil {
ctx . ServerError ( "RepoRef Invalid repo " + repoPath , err )
2023-07-07 08:31:56 +03:00
return nil
2015-12-05 01:20:23 +03:00
}
2021-04-10 03:26:08 +03:00
// We opened it, we should close it
2021-05-06 02:30:25 +03:00
cancel = func ( ) {
2021-04-10 03:26:08 +03:00
// If it's been set to nil then assume someone else has closed it.
if ctx . Repo . GitRepo != nil {
ctx . Repo . GitRepo . Close ( )
}
2021-05-06 02:30:25 +03:00
}
2021-04-10 03:26:08 +03:00
}
2015-12-05 01:20:23 +03:00
2021-04-10 03:26:08 +03:00
// Get default branch.
if len ( ctx . Params ( "*" ) ) == 0 {
refName = ctx . Repo . Repository . DefaultBranch
if ! ctx . Repo . GitRepo . IsBranchExist ( refName ) {
2023-06-29 13:03:20 +03:00
brs , _ , err := ctx . Repo . GitRepo . GetBranches ( 0 , 1 )
2023-04-19 16:40:42 +03:00
if err == nil && len ( brs ) != 0 {
2023-06-29 13:03:20 +03:00
refName = brs [ 0 ] . Name
2021-04-10 03:26:08 +03:00
} else if len ( brs ) == 0 {
2023-04-19 16:40:42 +03:00
log . Error ( "No branches in non-empty repository %s" , ctx . Repo . GitRepo . Path )
ctx . Repo . Repository . MarkAsBrokenEmpty ( )
} else {
log . Error ( "GetBranches error: %v" , err )
ctx . Repo . Repository . MarkAsBrokenEmpty ( )
2015-12-05 01:20:23 +03:00
}
2021-04-10 03:26:08 +03:00
}
2021-11-18 02:50:17 +03:00
ctx . Repo . RefName = refName
ctx . Repo . BranchName = refName
2021-04-10 03:26:08 +03:00
ctx . Repo . Commit , err = ctx . Repo . GitRepo . GetBranchCommit ( refName )
2023-04-19 16:40:42 +03:00
if err == nil {
ctx . Repo . CommitID = ctx . Repo . Commit . ID . String ( )
} else if strings . Contains ( err . Error ( ) , "fatal: not a git repository" ) || strings . Contains ( err . Error ( ) , "object does not exist" ) {
// if the repository is broken, we can continue to the handler code, to show "Settings -> Delete Repository" for end users
log . Error ( "GetBranchCommit: %v" , err )
ctx . Repo . Repository . MarkAsBrokenEmpty ( )
} else {
2021-04-10 03:26:08 +03:00
ctx . ServerError ( "GetBranchCommit" , err )
2023-07-07 08:31:56 +03:00
return cancel
2015-12-05 01:20:23 +03:00
}
2021-04-10 03:26:08 +03:00
ctx . Repo . IsViewBranch = true
} else {
2023-05-21 04:50:53 +03:00
refName = getRefName ( ctx . Base , ctx . Repo , refType )
2021-11-18 02:50:17 +03:00
ctx . Repo . RefName = refName
2021-10-08 20:03:04 +03:00
isRenamedBranch , has := ctx . Data [ "IsRenamedBranch" ] . ( bool )
if isRenamedBranch && has {
renamedBranchName := ctx . Data [ "RenamedBranchName" ] . ( string )
ctx . Flash . Info ( ctx . Tr ( "repo.branch.renamed" , refName , renamedBranchName ) )
2021-11-16 21:18:25 +03:00
link := setting . AppSubURL + strings . Replace ( ctx . Req . URL . EscapedPath ( ) , util . PathEscapeSegments ( refName ) , util . PathEscapeSegments ( renamedBranchName ) , 1 )
2021-10-08 20:03:04 +03:00
ctx . Redirect ( link )
2023-07-07 08:31:56 +03:00
return cancel
2021-10-08 20:03:04 +03:00
}
2021-04-10 03:26:08 +03:00
if refType . RefTypeIncludesBranches ( ) && ctx . Repo . GitRepo . IsBranchExist ( refName ) {
ctx . Repo . IsViewBranch = true
2021-11-18 02:50:17 +03:00
ctx . Repo . BranchName = refName
2015-12-05 01:20:23 +03:00
2015-12-10 04:46:05 +03:00
ctx . Repo . Commit , err = ctx . Repo . GitRepo . GetBranchCommit ( refName )
2015-12-05 01:20:23 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetBranchCommit" , err )
2023-07-07 08:31:56 +03:00
return cancel
2015-12-05 01:20:23 +03:00
}
ctx . Repo . CommitID = ctx . Repo . Commit . ID . String ( )
2021-04-10 03:26:08 +03:00
} else if refType . RefTypeIncludesTags ( ) && ctx . Repo . GitRepo . IsTagExist ( refName ) {
ctx . Repo . IsViewTag = true
2021-11-18 02:50:17 +03:00
ctx . Repo . TagName = refName
2021-04-10 03:26:08 +03:00
ctx . Repo . Commit , err = ctx . Repo . GitRepo . GetTagCommit ( refName )
if err != nil {
2022-06-18 20:08:34 +03:00
if git . IsErrNotExist ( err ) {
ctx . NotFound ( "GetTagCommit" , err )
2023-07-07 08:31:56 +03:00
return cancel
2022-06-18 20:08:34 +03:00
}
2021-04-10 03:26:08 +03:00
ctx . ServerError ( "GetTagCommit" , err )
2023-07-07 08:31:56 +03:00
return cancel
2015-12-05 01:20:23 +03:00
}
2021-04-10 03:26:08 +03:00
ctx . Repo . CommitID = ctx . Repo . Commit . ID . String ( )
2022-12-27 16:12:49 +03:00
} else if len ( refName ) >= 7 && len ( refName ) <= git . SHAFullLength {
2021-04-10 03:26:08 +03:00
ctx . Repo . IsViewCommit = true
ctx . Repo . CommitID = refName
2015-12-05 01:20:23 +03:00
2021-04-10 03:26:08 +03:00
ctx . Repo . Commit , err = ctx . Repo . GitRepo . GetCommit ( refName )
if err != nil {
ctx . NotFound ( "GetCommit" , err )
2023-07-07 08:31:56 +03:00
return cancel
2015-12-05 01:20:23 +03:00
}
2021-04-10 03:26:08 +03:00
// If short commit ID add canonical link header
2022-12-27 16:12:49 +03:00
if len ( refName ) < git . SHAFullLength {
2021-12-15 09:59:57 +03:00
ctx . RespHeader ( ) . Set ( "Link" , fmt . Sprintf ( "<%s>; rel=\"canonical\"" ,
2021-11-16 21:18:25 +03:00
util . URLJoin ( setting . AppURL , strings . Replace ( ctx . Req . URL . RequestURI ( ) , util . PathEscapeSegments ( refName ) , url . PathEscape ( ctx . Repo . Commit . ID . String ( ) ) , 1 ) ) ) )
2021-04-10 03:26:08 +03:00
}
} else {
2021-05-06 06:12:50 +03:00
if len ( ignoreNotExistErr ) > 0 && ignoreNotExistErr [ 0 ] {
2023-07-07 08:31:56 +03:00
return cancel
2021-05-06 06:12:50 +03:00
}
2021-04-10 03:26:08 +03:00
ctx . NotFound ( "RepoRef invalid repo" , fmt . Errorf ( "branch or tag not exist: %s" , refName ) )
2023-07-07 08:31:56 +03:00
return cancel
2015-12-05 01:20:23 +03:00
}
2017-10-30 05:04:25 +03:00
2021-04-10 03:26:08 +03:00
if refType == RepoRefLegacy {
// redirect from old URL scheme to new URL scheme
2022-02-17 19:11:27 +03:00
prefix := strings . TrimPrefix ( setting . AppSubURL + strings . ToLower ( strings . TrimSuffix ( ctx . Req . URL . Path , ctx . Params ( "*" ) ) ) , strings . ToLower ( ctx . Repo . RepoLink ) )
2021-11-16 21:18:25 +03:00
2021-04-10 03:26:08 +03:00
ctx . Redirect ( path . Join (
2021-11-16 21:18:25 +03:00
ctx . Repo . RepoLink ,
util . PathEscapeSegments ( prefix ) ,
2021-04-10 03:26:08 +03:00
ctx . Repo . BranchNameSubURL ( ) ,
2021-11-16 21:18:25 +03:00
util . PathEscapeSegments ( ctx . Repo . TreePath ) ) )
2023-07-07 08:31:56 +03:00
return cancel
2017-10-30 05:04:25 +03:00
}
2021-04-10 03:26:08 +03:00
}
2015-12-05 01:20:23 +03:00
2021-04-10 03:26:08 +03:00
ctx . Data [ "BranchName" ] = ctx . Repo . BranchName
2022-08-26 16:07:06 +03:00
ctx . Data [ "RefName" ] = ctx . Repo . RefName
2021-04-10 03:26:08 +03:00
ctx . Data [ "BranchNameSubURL" ] = ctx . Repo . BranchNameSubURL ( )
2021-11-18 02:50:17 +03:00
ctx . Data [ "TagName" ] = ctx . Repo . TagName
2021-04-10 03:26:08 +03:00
ctx . Data [ "CommitID" ] = ctx . Repo . CommitID
ctx . Data [ "TreePath" ] = ctx . Repo . TreePath
ctx . Data [ "IsViewBranch" ] = ctx . Repo . IsViewBranch
ctx . Data [ "IsViewTag" ] = ctx . Repo . IsViewTag
ctx . Data [ "IsViewCommit" ] = ctx . Repo . IsViewCommit
ctx . Data [ "CanCreateBranch" ] = ctx . Repo . CanCreateBranch ( )
ctx . Repo . CommitsCount , err = ctx . Repo . GetCommitsCount ( )
if err != nil {
ctx . ServerError ( "GetCommitsCount" , err )
2023-07-07 08:31:56 +03:00
return cancel
2021-04-10 03:26:08 +03:00
}
ctx . Data [ "CommitsCount" ] = ctx . Repo . CommitsCount
2022-07-25 18:39:42 +03:00
ctx . Repo . GitRepo . LastCommitCache = git . NewLastCommitCache ( ctx . Repo . CommitsCount , ctx . Repo . Repository . FullName ( ) , ctx . Repo . GitRepo , cache . GetCache ( ) )
2022-06-20 13:02:49 +03:00
return cancel
2015-12-05 01:20:23 +03:00
}
}
2014-12-07 04:22:48 +03:00
// GitHookService checks if repository Git hooks service has been enabled.
2021-01-26 18:36:53 +03:00
func GitHookService ( ) func ( ctx * Context ) {
2014-10-07 01:50:00 +04:00
return func ( ctx * Context ) {
2022-03-22 10:03:22 +03:00
if ! ctx . Doer . CanEditGitHook ( ) {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "GitHookService" , nil )
2014-10-07 01:50:00 +04:00
return
}
}
}
2017-02-04 18:53:46 +03:00
2021-01-29 18:35:30 +03:00
// UnitTypes returns a middleware to set unit types to context variables.
2021-01-26 18:36:53 +03:00
func UnitTypes ( ) func ( ctx * Context ) {
2017-02-04 18:53:46 +03:00
return func ( ctx * Context ) {
2021-11-09 22:57:58 +03:00
ctx . Data [ "UnitTypeCode" ] = unit_model . TypeCode
ctx . Data [ "UnitTypeIssues" ] = unit_model . TypeIssues
ctx . Data [ "UnitTypePullRequests" ] = unit_model . TypePullRequests
ctx . Data [ "UnitTypeReleases" ] = unit_model . TypeReleases
ctx . Data [ "UnitTypeWiki" ] = unit_model . TypeWiki
ctx . Data [ "UnitTypeExternalWiki" ] = unit_model . TypeExternalWiki
ctx . Data [ "UnitTypeExternalTracker" ] = unit_model . TypeExternalTracker
ctx . Data [ "UnitTypeProjects" ] = unit_model . TypeProjects
2022-05-08 18:51:50 +03:00
ctx . Data [ "UnitTypePackages" ] = unit_model . TypePackages
Implement actions (#21937)
Close #13539.
Co-authored by: @lunny @appleboy @fuxiaohei and others.
Related projects:
- https://gitea.com/gitea/actions-proto-def
- https://gitea.com/gitea/actions-proto-go
- https://gitea.com/gitea/act
- https://gitea.com/gitea/act_runner
### Summary
The target of this PR is to bring a basic implementation of "Actions",
an internal CI/CD system of Gitea. That means even though it has been
merged, the state of the feature is **EXPERIMENTAL**, and please note
that:
- It is disabled by default;
- It shouldn't be used in a production environment currently;
- It shouldn't be used in a public Gitea instance currently;
- Breaking changes may be made before it's stable.
**Please comment on #13539 if you have any different product design
ideas**, all decisions reached there will be adopted here. But in this
PR, we don't talk about **naming, feature-creep or alternatives**.
### ⚠️ Breaking
`gitea-actions` will become a reserved user name. If a user with the
name already exists in the database, it is recommended to rename it.
### Some important reviews
- What is `DEFAULT_ACTIONS_URL` in `app.ini` for?
- https://github.com/go-gitea/gitea/pull/21937#discussion_r1055954954
- Why the api for runners is not under the normal `/api/v1` prefix?
- https://github.com/go-gitea/gitea/pull/21937#discussion_r1061173592
- Why DBFS?
- https://github.com/go-gitea/gitea/pull/21937#discussion_r1061301178
- Why ignore events triggered by `gitea-actions` bot?
- https://github.com/go-gitea/gitea/pull/21937#discussion_r1063254103
- Why there's no permission control for actions?
- https://github.com/go-gitea/gitea/pull/21937#discussion_r1090229868
### What it looks like
<details>
#### Manage runners
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205870657-c72f590e-2e08-4cd4-be7f-2e0abb299bbf.png">
#### List runs
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205872794-50fde990-2b45-48c1-a178-908e4ec5b627.png">
#### View logs
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205872501-9b7b9000-9542-4991-8f55-18ccdada77c3.png">
</details>
### How to try it
<details>
#### 1. Start Gitea
Clone this branch and [install from
source](https://docs.gitea.io/en-us/install-from-source).
Add additional configurations in `app.ini` to enable Actions:
```ini
[actions]
ENABLED = true
```
Start it.
If all is well, you'll see the management page of runners:
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205877365-8e30a780-9b10-4154-b3e8-ee6c3cb35a59.png">
#### 2. Start runner
Clone the [act_runner](https://gitea.com/gitea/act_runner), and follow
the
[README](https://gitea.com/gitea/act_runner/src/branch/main/README.md)
to start it.
If all is well, you'll see a new runner has been added:
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205878000-216f5937-e696-470d-b66c-8473987d91c3.png">
#### 3. Enable actions for a repo
Create a new repo or open an existing one, check the `Actions` checkbox
in settings and submit.
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205879705-53e09208-73c0-4b3e-a123-2dcf9aba4b9c.png">
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205879383-23f3d08f-1a85-41dd-a8b3-54e2ee6453e8.png">
If all is well, you'll see a new tab "Actions":
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205881648-a8072d8c-5803-4d76-b8a8-9b2fb49516c1.png">
#### 4. Upload workflow files
Upload some workflow files to `.gitea/workflows/xxx.yaml`, you can
follow the [quickstart](https://docs.github.com/en/actions/quickstart)
of GitHub Actions. Yes, Gitea Actions is compatible with GitHub Actions
in most cases, you can use the same demo:
```yaml
name: GitHub Actions Demo
run-name: ${{ github.actor }} is testing out GitHub Actions 🚀
on: [push]
jobs:
Explore-GitHub-Actions:
runs-on: ubuntu-latest
steps:
- run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event."
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!"
- run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}."
- name: Check out repository code
uses: actions/checkout@v3
- run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
- name: List files in the repository
run: |
ls ${{ github.workspace }}
- run: echo "🍏 This job's status is ${{ job.status }}."
```
If all is well, you'll see a new run in `Actions` tab:
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205884473-79a874bc-171b-4aaf-acd5-0241a45c3b53.png">
#### 5. Check the logs of jobs
Click a run and you'll see the logs:
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205884800-994b0374-67f7-48ff-be9a-4c53f3141547.png">
#### 6. Go on
You can try more examples in [the
documents](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions)
of GitHub Actions, then you might find a lot of bugs.
Come on, PRs are welcome.
</details>
See also: [Feature Preview: Gitea
Actions](https://blog.gitea.io/2022/12/feature-preview-gitea-actions/)
---------
Co-authored-by: a1012112796 <1012112796@qq.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: ChristopherHX <christopher.homberger@web.de>
Co-authored-by: John Olheiser <john.olheiser@gmail.com>
2023-01-31 04:45:19 +03:00
ctx . Data [ "UnitTypeActions" ] = unit_model . TypeActions
2017-02-04 18:53:46 +03:00
}
}