ronantakizawa/codereview-bench huggingface dataset:
Repository: go-gitea/gitea
File: models/user/user.go (Go)

Code before:
func (u *User) CanCreateRepoIn(owner *User) bool {
	if u.IsAdmin {
		return true
	}
	const noLimit = -1
	if owner.MaxRepoCreation == noLimit {
		if setting.Repository.MaxCreationLimit == noLimit {
			return true
		}
		return owner.NumRepos < setting.Repository.MaxCreationLimit
	}
	return owner.NumRepos < owner.MaxRepoCreation
}

// CanCreateOrganization returns true if user can create organisation.
func (u *User) CanCreateOrganization() bool {
	return u.IsAdmin || (u.AllowCreateOrganization && !setting.Admin.DisableRegularOrgCreation)
}

// CanEditGitHook returns true if user can edit Git hooks.
func (u *User) CanEditGitHook() bool {
	return !setting.DisableGitHooks && (u.IsAdmin || u.AllowGitHook)
}

func (u *User) CanForkRepoIn(owner *User) bool {
	if setting.Repository.AllowForkWithoutMaximumLimit {
		return true
	}
	return u.CanCreateRepoIn(owner)
}

// CanImportLocal returns true if user can migrate repository by local path.
func (u *User) CanImportLocal() bool {
	if !setting.ImportLocalPaths || u == nil {
		return false
	}
	return u.IsAdmin || u.AllowImportLocal
}

// DashboardLink returns the user dashboard page link.
func (u *User) DashboardLink() string {
	if u.IsOrganization() {
		return u.OrganisationLink() + "/dashboard"
	}
	return setting.AppSubURL + "/"
}

// HomeLink returns the user or organization home page link.
func (u *User) HomeLink() string {
	return setting.AppSubURL + "/" + url.PathEscape(u.Name)
}

Review comment (suggestion):
```suggestion
// CanForkRepoIn ONLY checks repository count limit
func (u *User) CanForkRepoIn(owner *User) bool {
```

Code after:
func (u *User) CanCreateRepoIn(owner *User) bool {
	if u.IsAdmin {
		return true
	}
	const noLimit = -1
	if owner.MaxRepoCreation == noLimit {
		if setting.Repository.MaxCreationLimit == noLimit {
			return true
		}
		return owner.NumRepos < setting.Repository.MaxCreationLimit
	}
	return owner.NumRepos < owner.MaxRepoCreation
}

// CanCreateOrganization returns true if user can create organisation.
func (u *User) CanCreateOrganization() bool {
	return u.IsAdmin || (u.AllowCreateOrganization && !setting.Admin.DisableRegularOrgCreation)
}

// CanEditGitHook returns true if user can edit Git hooks.
func (u *User) CanEditGitHook() bool {
	return !setting.DisableGitHooks && (u.IsAdmin || u.AllowGitHook)
}

// CanForkRepoIn ONLY checks repository count limit
func (u *User) CanForkRepoIn(owner *User) bool {
	if setting.Repository.AllowForkWithoutMaximumLimit {
		return true
	}
	return u.CanCreateRepoIn(owner)
}

// CanImportLocal returns true if user can migrate repository by local path.
func (u *User) CanImportLocal() bool {
	if !setting.ImportLocalPaths || u == nil {
		return false
	}
	return u.IsAdmin || u.AllowImportLocal
}

// DashboardLink returns the user dashboard page link.
func (u *User) DashboardLink() string {
	if u.IsOrganization() {
		return u.OrganisationLink() + "/dashboard"
	}
	return setting.AppSubURL + "/"
}

// HomeLink returns the user or organization home page link.
func (u *User) HomeLink() string {
	return setting.AppSubURL + "/" + url.PathEscape(u.Name)
Repository: go-gitea/gitea
File: routers/api/v1/repo/issue_lock.go (Go)

Code before:
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package repo

import (
	"errors"
	"net/http"

	issues_model "code.gitea.io/gitea/models/issues"
	api "code.gitea.io/gitea/modules/structs"
	"code.gitea.io/gitea/modules/web"
	"code.gitea.io/gitea/services/context"
)

// LockIssue lock an issue
func LockIssue(ctx *context.APIContext) {
	// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/lock issue issueLockIssue
	// ---
	// summary: Lock an issue
	// consumes:
	// - application/json
	// produces:
	// - application/json
	// parameters:
	// - name: owner

Review comment (security):
```suggestion
// Copyright 2025 The Gitea Authors. All rights reserved.
```

Code after:
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package repo

import (
	"errors"
	"net/http"

	issues_model "code.gitea.io/gitea/models/issues"
	api "code.gitea.io/gitea/modules/structs"
	"code.gitea.io/gitea/modules/web"
	"code.gitea.io/gitea/services/context"
)

// LockIssue lock an issue
func LockIssue(ctx *context.APIContext) {
	// swagger:operation PUT /repos/{owner}/{repo}/issues/{index}/lock issue issueLockIssue
	// ---
	// summary: Lock an issue
	// consumes:
	// - application/json
	// produces:
	// - application/json
	// parameters:
	// - name: owner
Repository: go-gitea/gitea
File: tests/integration/api_issue_lock_test.go (Go)

Code before:
// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package integration

import (
	"fmt"
	"net/http"
	"testing"

	auth_model "code.gitea.io/gitea/models/auth"
	issues_model "code.gitea.io/gitea/models/issues"
	repo_model "code.gitea.io/gitea/models/repo"
	"code.gitea.io/gitea/models/unittest"
	user_model "code.gitea.io/gitea/models/user"
	api "code.gitea.io/gitea/modules/structs"
	"code.gitea.io/gitea/tests"

	"github.com/stretchr/testify/assert"
)

func TestAPILockIssue(t *testing.T) {
	defer tests.PrepareTestEnv(t)()

	issueBefore := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
	assert.False(t, issueBefore.IsLocked)

Review comment (security):
```suggestion
// Copyright 2025 The Gitea Authors. All rights reserved.
```

Code after:
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package integration

import (
	"fmt"
	"net/http"
	"testing"

	auth_model "code.gitea.io/gitea/models/auth"
	issues_model "code.gitea.io/gitea/models/issues"
	repo_model "code.gitea.io/gitea/models/repo"
	"code.gitea.io/gitea/models/unittest"
	user_model "code.gitea.io/gitea/models/user"
	api "code.gitea.io/gitea/modules/structs"
	"code.gitea.io/gitea/tests"

	"github.com/stretchr/testify/assert"
)

func TestAPILockIssue(t *testing.T) {
	defer tests.PrepareTestEnv(t)()

	t.Run("Lock", func(t *testing.T) {
		issueBefore := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
Repository: go-gitea/gitea
File: models/issues/issue_lock.go (Go)

Code before:
	if err != nil {
		return err
	}
	defer committer.Close()

	if err := UpdateIssueCols(ctx, opts.Issue, "is_locked"); err != nil {
		return err
	}

	opt := &CreateCommentOptions{
		Doer:    opts.Doer,
		Issue:   opts.Issue,
		Repo:    opts.Issue.Repo,
		Type:    commentType,
		Content: opts.Reason,
	}
	if _, err := CreateComment(ctx, opt); err != nil {
		return err
	}

	return committer.Commit()
}

// IsValidReason checks to make sure that the reason submitted
// matches any of the values in the config
func IsValidReason(reason string) bool {
	if strings.TrimSpace(reason) == "" {
		return true
	}

	return slices.Contains(setting.Repository.Issue.LockReasons, reason)
}


Review comment (performance):
Not sure if it adds too much complexity, but it might be worth looking into integrating `strings.EqualFold` as then you don't need to worry about converting cases.

Code after:
	}

	ctx, committer, err := db.TxContext(ctx)
	if err != nil {
		return err
	}
	defer committer.Close()

	if err := UpdateIssueCols(ctx, opts.Issue, "is_locked"); err != nil {
		return err
	}

	opt := &CreateCommentOptions{
		Doer:    opts.Doer,
		Issue:   opts.Issue,
		Repo:    opts.Issue.Repo,
		Type:    commentType,
		Content: opts.Reason,
	}
	if _, err := CreateComment(ctx, opt); err != nil {
		return err
	}

	return committer.Commit()
}
Repository: go-gitea/gitea
File: routers/api/actions/artifactsv4.go (Go)

Code before:
	}
	r.sendProtbufBody(ctx, &respData)
}

func (r *artifactV4Routes) getSignedArtifactURL(ctx *ArtifactContext) {
	var req GetSignedArtifactURLRequest

	if ok := r.parseProtbufBody(ctx, &req); !ok {
		return
	}
	_, runID, ok := validateRunIDV4(ctx, req.WorkflowRunBackendId)
	if !ok {
		return
	}

	artifactName := req.Name

	// get artifact by name
	artifact, err := r.getArtifactByName(ctx, runID, artifactName)
	if err != nil {
		log.Error("Error artifact not found: %v", err)
		ctx.HTTPError(http.StatusNotFound, "Error artifact not found")
		return
	}
	if artifact.Status != actions.ArtifactStatusUploadConfirmed {
		log.Error("Error artifact not found: unconfirmed")
		ctx.HTTPError(http.StatusNotFound, "Error artifact not found")
		return
	}

	respData := GetSignedArtifactURLResponse{}

	if setting.Actions.ArtifactStorage.ServeDirect() {
		u, err := storage.ActionsArtifacts.URL(artifact.StoragePath, artifact.ArtifactPath, nil)
		if u != nil && err == nil {
			respData.SignedUrl = u.String()
		}
	}
	if respData.SignedUrl == "" {
		respData.SignedUrl = r.buildArtifactURL(ctx, "DownloadArtifact", artifactName, ctx.ActionTask.ID, artifact.ID)
	}
	r.sendProtbufBody(ctx, &respData)
}

func (r *artifactV4Routes) downloadArtifact(ctx *ArtifactContext) {
	task, artifactName, ok := r.verifySignature(ctx, "DownloadArtifact")
	if !ok {
		return
	}

	// get artifact by name

Review comment (suggestion):
```suggestion
		log.Error("Error artifact not found: upload is not yet completed")
```

Code after:
	}
	r.sendProtbufBody(ctx, &respData)
}

func (r *artifactV4Routes) getSignedArtifactURL(ctx *ArtifactContext) {
	var req GetSignedArtifactURLRequest

	if ok := r.parseProtbufBody(ctx, &req); !ok {
		return
	}
	_, runID, ok := validateRunIDV4(ctx, req.WorkflowRunBackendId)
	if !ok {
		return
	}

	artifactName := req.Name

	// get artifact by name
	artifact, err := r.getArtifactByName(ctx, runID, artifactName)
	if err != nil {
		log.Error("Error artifact not found: %v", err)
		ctx.HTTPError(http.StatusNotFound, "Error artifact not found")
		return
	}
	if artifact.Status != actions.ArtifactStatusUploadConfirmed {
		log.Error("Error artifact not found: %s", artifact.Status.ToString())
		ctx.HTTPError(http.StatusNotFound, "Error artifact not found")
		return
	}

	respData := GetSignedArtifactURLResponse{}

	if setting.Actions.ArtifactStorage.ServeDirect() {
		u, err := storage.ActionsArtifacts.URL(artifact.StoragePath, artifact.ArtifactPath, nil)
		if u != nil && err == nil {
			respData.SignedUrl = u.String()
		}
	}
	if respData.SignedUrl == "" {
		respData.SignedUrl = r.buildArtifactURL(ctx, "DownloadArtifact", artifactName, ctx.ActionTask.ID, artifact.ID)
	}
	r.sendProtbufBody(ctx, &respData)
}

func (r *artifactV4Routes) downloadArtifact(ctx *ArtifactContext) {
	task, artifactName, ok := r.verifySignature(ctx, "DownloadArtifact")
	if !ok {
		return
	}

	// get artifact by name
Repository: go-gitea/gitea
File: routers/web/repo/issue.go (Go)

Code before:
		ctx.HTTPError(http.StatusInternalServerError, "UpdateIssueDeadline", err.Error())
		return
	}

	ctx.JSONRedirect("")
}

// UpdateIssueMilestone change issue's milestone
func UpdateIssueMilestone(ctx *context.Context) {
	issues := getActionIssues(ctx)
	if ctx.Written() {
		return
	}

	milestoneID := ctx.FormInt64("id")
	for _, issue := range issues {
		oldMilestoneID := issue.MilestoneID
		if oldMilestoneID == milestoneID {
			continue
		}
		issue.MilestoneID = milestoneID
		if milestoneID > 0 {
			var err error
			issue.Milestone, err = issues_model.GetMilestoneByRepoID(ctx, ctx.Repo.Repository.ID, milestoneID)
			if err != nil {
				ctx.ServerError("GetMileStoneByID", err)
				return
			}
		} else {
			issue.Milestone = nil
		}
		if err := issue_service.ChangeMilestoneAssign(ctx, issue, ctx.Doer, oldMilestoneID); err != nil {
			ctx.ServerError("ChangeMilestoneAssign", err)
			return
		}
	}

	ctx.JSONOK()
}

// UpdateIssueAssignee change issue's or pull's assignee
func UpdateIssueAssignee(ctx *context.Context) {
	issues := getActionIssues(ctx)
	if ctx.Written() {
		return
	}

	assigneeID := ctx.FormInt64("id")
	action := ctx.FormString("action")

	for _, issue := range issues {

Review comment (suggestion):
```suggestion
				ctx.ServerError("GetMilestoneByRepoID", err)
```

Code after:
		ctx.HTTPError(http.StatusInternalServerError, "UpdateIssueDeadline", err.Error())
		return
	}

	ctx.JSONRedirect("")
}

// UpdateIssueMilestone change issue's milestone
func UpdateIssueMilestone(ctx *context.Context) {
	issues := getActionIssues(ctx)
	if ctx.Written() {
		return
	}

	milestoneID := ctx.FormInt64("id")
	for _, issue := range issues {
		oldMilestoneID := issue.MilestoneID
		if oldMilestoneID == milestoneID {
			continue
		}
		issue.MilestoneID = milestoneID
		if milestoneID > 0 {
			var err error
			issue.Milestone, err = issues_model.GetMilestoneByRepoID(ctx, ctx.Repo.Repository.ID, milestoneID)
			if err != nil {
				ctx.ServerError("GetMilestoneByRepoID", err)
				return
			}
		} else {
			issue.Milestone = nil
		}
		if err := issue_service.ChangeMilestoneAssign(ctx, issue, ctx.Doer, oldMilestoneID); err != nil {
			ctx.ServerError("ChangeMilestoneAssign", err)
			return
		}
	}

	ctx.JSONOK()
}

// UpdateIssueAssignee change issue's or pull's assignee
func UpdateIssueAssignee(ctx *context.Context) {
	issues := getActionIssues(ctx)
	if ctx.Written() {
		return
	}

	assigneeID := ctx.FormInt64("id")
	action := ctx.FormString("action")

	for _, issue := range issues {
Repository: go-gitea/gitea
File: modules/storage/local.go (Go)

Code before:
		if err != nil {
			return err
		}
		select {
		case <-l.ctx.Done():
			return l.ctx.Err()
		default:
		}
		if path == l.dir {
			return nil
		}
		if d.IsDir() {
			return nil
		}
		relPath, err := filepath.Rel(l.dir, path)
		if err != nil {
			return err
		}
		obj, err := os.Open(path)
		if err != nil {
			return err
		}
		defer obj.Close()
		return fn(relPath, obj)
	})
	if os.IsNotExist(err) {
		return nil
	}
	return err
}

func init() {
	RegisterStorageType(setting.LocalStorageType, NewLocalStorage)
}


Review comment (bug):
I had a test failure due to lstat not found after deleting the user and repository in a cleanup artifacts_storage, while all other storage backends did not return any error for the same test sequence.

This applies to v1.24 cleanly as well

> lstat /home/runner/work/gitea/gitea/tests/integration/gitea-integration-sqlite/data/actions_artifacts: no such file or directory

ref: https://github.com/go-gitea/gitea/actions/runs/14994010613/job/42123851803#step:7:105

If I run the added tests then the failing artifacts test, nothing happens locally. I believe the deletion has a side effect / the local storage backend diverts too much from the other storage backends.

Code after:
		if err != nil {
			return err
		}
		select {
		case <-l.ctx.Done():
			return l.ctx.Err()
		default:
		}
		if path == l.dir {
			return nil
		}
		if d.IsDir() {
			return nil
		}
		relPath, err := filepath.Rel(l.dir, path)
		if err != nil {
			return err
		}
		obj, err := os.Open(path)
		if err != nil {
			return err
		}
		defer obj.Close()
		return fn(relPath, obj)
	})
}

func init() {
	RegisterStorageType(setting.LocalStorageType, NewLocalStorage)
}
Repository: go-gitea/gitea
File: services/org/user.go (Go)

Code before:
				return organization.ErrLastOrgOwner{UID: user.ID}
			}
		}
	}

	ctx, committer, err := db.TxContext(ctx)
	if err != nil {
		return err
	}
	defer committer.Close()

	if _, err := db.DeleteByID[organization.OrgUser](ctx, ou.ID); err != nil {
		return err
	} else if _, err = db.Exec(ctx, "UPDATE `user` SET num_members=num_members-1 WHERE id=?", org.ID); err != nil {
		return err
	}

	// Delete all repository accesses and unwatch them.
	env, err := repo_model.AccessibleReposEnv(ctx, org, user.ID)
	if err != nil {
		return fmt.Errorf("AccessibleReposEnv: %w", err)
	}
	var repoIDs []int64
	// mssql does not support fetching 0 repositories
	if org.NumRepos > 0 {
		repoIDs, err = env.RepoIDs(ctx, 1, org.NumRepos)
		if err != nil {
			return fmt.Errorf("GetUserRepositories [%d]: %w", user.ID, err)
		}
	}

	for _, repoID := range repoIDs {
		repo, err := repo_model.GetRepositoryByID(ctx, repoID)
		if err != nil {
			return err
		}
		if err = repo_model.WatchRepo(ctx, user, repo, false); err != nil {
			return err
		}
	}

	if len(repoIDs) > 0 {
		if _, err = db.GetEngine(ctx).
			Where("user_id = ?", user.ID).
			In("repo_id", repoIDs).
			Delete(new(access_model.Access)); err != nil {
			return err
		}
	}

	// Delete member in their teams.

Review comment (suggestion):
I don't think the fix is right. What if `NumRepos` is out-of-sync? Such problems happened a lot.

The root problem is not related to `org.NumRepos`, the real problem is that `RepoIDs` should have a correct behavior

Code after:
				return organization.ErrLastOrgOwner{UID: user.ID}
			}
		}
	}

	ctx, committer, err := db.TxContext(ctx)
	if err != nil {
		return err
	}
	defer committer.Close()

	if _, err := db.DeleteByID[organization.OrgUser](ctx, ou.ID); err != nil {
		return err
	} else if _, err = db.Exec(ctx, "UPDATE `user` SET num_members=num_members-1 WHERE id=?", org.ID); err != nil {
		return err
	}

	// Delete all repository accesses and unwatch them.
	env, err := repo_model.AccessibleReposEnv(ctx, org, user.ID)
	if err != nil {
		return fmt.Errorf("AccessibleReposEnv: %w", err)
	}
	repoIDs, err := env.RepoIDs(ctx)
	if err != nil {
		return fmt.Errorf("GetUserRepositories [%d]: %w", user.ID, err)
	}

	for _, repoID := range repoIDs {
		repo, err := repo_model.GetRepositoryByID(ctx, repoID)
		if err != nil {
			return err
		}
		if err = repo_model.WatchRepo(ctx, user, repo, false); err != nil {
			return err
		}
	}

	if len(repoIDs) > 0 {
		if _, err = db.GetEngine(ctx).
			Where("user_id = ?", user.ID).
			In("repo_id", repoIDs).
			Delete(new(access_model.Access)); err != nil {
			return err
		}
	}

	// Delete member in their teams.
	teams, err := organization.GetUserOrgTeams(ctx, org.ID, user.ID)
	if err != nil {
		return err
	}
Repository: go-gitea/gitea
File: modules/git/blame.go (Go)

Code before:
		done <- err
		_ = stdout.Close()
		if err != nil {
			log.Error("Error running git blame (dir: %v): %v, stderr: %v", repoPath, err, stderr.String())
		}
	}()

	bufferedReader := bufio.NewReader(reader)
	return &BlameReader{
		output:         stdout,
		reader:         reader,
		bufferedReader: bufferedReader,
		done:           done,
		ignoreRevsFile: ignoreRevsFileName,
		objectFormat:   objectFormat,
		cleanupFuncs:   []func(){ignoreRevsFileCleanup},
	}, nil
}

func tryCreateBlameIgnoreRevsFile(commit *Commit) (string, func(), error) {
	entry, err := commit.GetTreeEntryByPath(".git-blame-ignore-revs")
	if err != nil {
		if IsErrNotExist(err) {
			return "", nil, nil
		}
		return "", nil, err
	}

	r, err := entry.Blob().DataAsync()
	if err != nil {
		return "", nil, err
	}
	defer r.Close()

	f, cleanup, err := setting.AppDataTempDir("git-repo-content").CreateTempFileRandom("git-blame-ignore-revs")
	if err != nil {
		return "", nil, err
	}
	filename := f.Name()
	_, err = io.Copy(f, r)
	_ = f.Close()
	if err != nil {
		cleanup()
		return "", nil, err
	}

	return filename, cleanup, nil
}


Review comment (suggestion):
It breaks the design of Golang&Gitea error system

In most cases:

* Return "err" if error occurs
* Return err=nil if no error occurs and then the returned value can't be nil.

But your change breaks this constraint.

----

To make the error handling consistent, do not call `IsErrNotExist` here, let caller check, just like most XORM methods do.

Code after:
		_ = stdout.Close()
		if err != nil {
			log.Error("Error running git blame (dir: %v): %v, stderr: %v", repoPath, err, stderr.String())
		}
	}()

	bufferedReader := bufio.NewReader(reader)
	return &BlameReader{
		output:         stdout,
		reader:         reader,
		bufferedReader: bufferedReader,
		done:           done,
		ignoreRevsFile: ignoreRevsFileName,
		objectFormat:   objectFormat,
		cleanupFuncs:   []func(){ignoreRevsFileCleanup},
	}, nil
}

func tryCreateBlameIgnoreRevsFile(commit *Commit) (string, func(), error) {
	entry, err := commit.GetTreeEntryByPath(".git-blame-ignore-revs")
	if err != nil {
		return "", nil, err
	}

	r, err := entry.Blob().DataAsync()
	if err != nil {
		return "", nil, err
	}
	defer r.Close()

	f, cleanup, err := setting.AppDataTempDir("git-repo-content").CreateTempFileRandom("git-blame-ignore-revs")
	if err != nil {
		return "", nil, err
	}
	filename := f.Name()
	_, err = io.Copy(f, r)
	_ = f.Close()
	if err != nil {
		cleanup()
		return "", nil, err
	}

	return filename, cleanup, nil
}
Repository: go-gitea/gitea
File: routers/api/v1/org/action.go (Go)

Code before:
func (Action) CreateVariable(ctx *context.APIContext) {
	// swagger:operation POST /orgs/{org}/actions/variables/{variablename} organization createOrgVariable
	// ---
	// summary: Create an org-level variable
	// consumes:
	// - application/json
	// produces:
	// - application/json
	// parameters:
	// - name: org
	//   in: path
	//   description: name of the organization
	//   type: string
	//   required: true
	// - name: variablename
	//   in: path
	//   description: name of the variable
	//   type: string
	//   required: true
	// - name: body
	//   in: body
	//   schema:
	//     "$ref": "#/definitions/CreateVariableOption"
	// responses:
	//   "201":
	//     description: response when creating a repo-level variable
	//   "400":
	//     "$ref": "#/responses/error"
	//   "409":
	//     description: variable name already exists.

	opt := web.GetForm(ctx).(*api.CreateVariableOption)

	ownerID := ctx.Org.Organization.ID
	variableName := ctx.PathParam("variablename")

	v, err := actions_service.GetVariable(ctx, actions_model.FindVariablesOpts{
		OwnerID: ownerID,
		Name:    variableName,
	})
	if err != nil && !errors.Is(err, util.ErrNotExist) {
		ctx.APIErrorInternal(err)
		return
	}
	if v != nil && v.ID > 0 {
		ctx.APIError(http.StatusConflict, util.NewAlreadyExistErrorf("variable name %s already exists", variableName))
		return
	}

	if _, err := actions_service.CreateVariable(ctx, ownerID, 0, variableName, opt.Value, opt.Description); err != nil {
		if errors.Is(err, util.ErrInvalidArgument) {

Review comment (suggestion):
Wait, why `repo-level` when it was org-level above?
```suggestion
	//     description: successfully created the org-level variable
```

Code after:
func (Action) CreateVariable(ctx *context.APIContext) {
	// swagger:operation POST /orgs/{org}/actions/variables/{variablename} organization createOrgVariable
	// ---
	// summary: Create an org-level variable
	// consumes:
	// - application/json
	// produces:
	// - application/json
	// parameters:
	// - name: org
	//   in: path
	//   description: name of the organization
	//   type: string
	//   required: true
	// - name: variablename
	//   in: path
	//   description: name of the variable
	//   type: string
	//   required: true
	// - name: body
	//   in: body
	//   schema:
	//     "$ref": "#/definitions/CreateVariableOption"
	// responses:
	//   "201":
	//     description: successfully created the org-level variable
	//   "400":
	//     "$ref": "#/responses/error"
	//   "409":
	//     description: variable name already exists.
	//   "500":
	//     "$ref": "#/responses/error"

	opt := web.GetForm(ctx).(*api.CreateVariableOption)

	ownerID := ctx.Org.Organization.ID
	variableName := ctx.PathParam("variablename")

	v, err := actions_service.GetVariable(ctx, actions_model.FindVariablesOpts{
		OwnerID: ownerID,
		Name:    variableName,
	})
	if err != nil && !errors.Is(err, util.ErrNotExist) {
		ctx.APIErrorInternal(err)
		return
	}
	if v != nil && v.ID > 0 {
		ctx.APIError(http.StatusConflict, util.NewAlreadyExistErrorf("variable name %s already exists", variableName))
		return
	}