@@ -79,12 +79,6 @@ See [Workflow syntax for GitHub Actions](https://docs.github.com/en/actions/usin | |||
Gitea Actions only supports `runs-on: xyz` or `runs-on: [xyz]` now. | |||
### `workflow_dispatch` | |||
See [Workflow syntax for GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#onworkflow_dispatch). | |||
It's ignored by Gitea Actions now. | |||
### `hashFiles` expression | |||
See [Expressions](https://docs.github.com/en/actions/learn-github-actions/expressions#hashfiles) |
@@ -79,12 +79,6 @@ Gitea Actions 目前不支持此功能。 | |||
Gitea Actions目前只支持`runs-on: xyz`或`runs-on: [xyz]`。 | |||
### `workflow_dispatch` | |||
请参阅[GitHub Actions的工作流语法](https://docs.github.com/zh/actions/using-workflows/workflow-syntax-for-github-actions#onworkflow_dispatch)。 | |||
Gitea Actions目前不支持此功能。 | |||
### `hashFiles`表达式 | |||
请参阅[表达式](https://docs.github.com/en/actions/learn-github-actions/expressions#hashfiles)。 |
@@ -494,3 +494,17 @@ type PackagePayload struct { | |||
func (p *PackagePayload) JSONPayload() ([]byte, error) { | |||
return json.MarshalIndent(p, "", " ") | |||
} | |||
// WorkflowDispatchPayload represents a workflow dispatch payload | |||
type WorkflowDispatchPayload struct { | |||
Workflow string `json:"workflow"` | |||
Ref string `json:"ref"` | |||
Inputs map[string]any `json:"inputs"` | |||
Repository *Repository `json:"repository"` | |||
Sender *User `json:"sender"` | |||
} | |||
// JSONPayload implements Payload | |||
func (p *WorkflowDispatchPayload) JSONPayload() ([]byte, error) { | |||
return json.MarshalIndent(p, "", " ") | |||
} |
@@ -624,6 +624,7 @@ org_still_own_repo = "This organization still owns one or more repositories, del | |||
org_still_own_packages = "This organization still owns one or more packages, delete them first." | |||
target_branch_not_exist = Target branch does not exist. | |||
target_ref_not_exist = Target ref does not exist %s | |||
admin_cannot_delete_self = You cannot delete yourself when you are an admin. Please remove your admin privileges first. | |||
@@ -3651,6 +3652,11 @@ workflow.disable_success = Workflow '%s' disabled successfully. | |||
workflow.enable = Enable Workflow | |||
workflow.enable_success = Workflow '%s' enabled successfully. | |||
workflow.disabled = Workflow is disabled. | |||
workflow.run = Run Workflow | |||
workflow.not_found = Workflow '%s' not found. | |||
workflow.run_success = Workflow '%s' run successfully. | |||
workflow.from_ref = Use workflow from | |||
workflow.has_workflow_dispatch = This workflow has a workflow_dispatch event trigger. | |||
need_approval_desc = Need approval to run workflows for fork pull request. | |||
@@ -7,10 +7,13 @@ import ( | |||
"bytes" | |||
"fmt" | |||
"net/http" | |||
"slices" | |||
"strings" | |||
actions_model "code.gitea.io/gitea/models/actions" | |||
"code.gitea.io/gitea/models/db" | |||
git_model "code.gitea.io/gitea/models/git" | |||
repo_model "code.gitea.io/gitea/models/repo" | |||
"code.gitea.io/gitea/models/unit" | |||
"code.gitea.io/gitea/modules/actions" | |||
"code.gitea.io/gitea/modules/base" | |||
@@ -18,6 +21,7 @@ import ( | |||
"code.gitea.io/gitea/modules/git" | |||
"code.gitea.io/gitea/modules/optional" | |||
"code.gitea.io/gitea/modules/setting" | |||
"code.gitea.io/gitea/modules/util" | |||
"code.gitea.io/gitea/routers/web/repo" | |||
"code.gitea.io/gitea/services/context" | |||
"code.gitea.io/gitea/services/convert" | |||
@@ -58,8 +62,13 @@ func MustEnableActions(ctx *context.Context) { | |||
func List(ctx *context.Context) { | |||
ctx.Data["Title"] = ctx.Tr("actions.actions") | |||
ctx.Data["PageIsActions"] = true | |||
workflowID := ctx.FormString("workflow") | |||
actorID := ctx.FormInt64("actor") | |||
status := ctx.FormInt("status") | |||
ctx.Data["CurWorkflow"] = workflowID | |||
var workflows []Workflow | |||
var curWorkflow *model.Workflow | |||
if empty, err := ctx.Repo.GitRepo.IsEmpty(); err != nil { | |||
ctx.ServerError("IsEmpty", err) | |||
return | |||
@@ -132,6 +141,10 @@ func List(ctx *context.Context) { | |||
workflow.ErrMsg = ctx.Locale.TrString("actions.runs.no_job_without_needs") | |||
} | |||
workflows = append(workflows, workflow) | |||
if workflow.Entry.Name() == workflowID { | |||
curWorkflow = wf | |||
} | |||
} | |||
} | |||
ctx.Data["workflows"] = workflows | |||
@@ -142,17 +155,46 @@ func List(ctx *context.Context) { | |||
page = 1 | |||
} | |||
workflow := ctx.FormString("workflow") | |||
actorID := ctx.FormInt64("actor") | |||
status := ctx.FormInt("status") | |||
ctx.Data["CurWorkflow"] = workflow | |||
actionsConfig := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions).ActionsConfig() | |||
ctx.Data["ActionsConfig"] = actionsConfig | |||
if len(workflow) > 0 && ctx.Repo.IsAdmin() { | |||
if len(workflowID) > 0 && ctx.Repo.IsAdmin() { | |||
ctx.Data["AllowDisableOrEnableWorkflow"] = true | |||
ctx.Data["CurWorkflowDisabled"] = actionsConfig.IsWorkflowDisabled(workflow) | |||
isWorkflowDisabled := actionsConfig.IsWorkflowDisabled(workflowID) | |||
ctx.Data["CurWorkflowDisabled"] = isWorkflowDisabled | |||
if !isWorkflowDisabled && curWorkflow != nil { | |||
workflowDispatchConfig := curWorkflow.WorkflowDispatchConfig() | |||
if workflowDispatchConfig != nil { | |||
ctx.Data["WorkflowDispatchConfig"] = workflowDispatchConfig | |||
branchOpts := git_model.FindBranchOptions{ | |||
RepoID: ctx.Repo.Repository.ID, | |||
IsDeletedBranch: optional.Some(false), | |||
ListOptions: db.ListOptions{ | |||
ListAll: true, | |||
}, | |||
} | |||
branches, err := git_model.FindBranchNames(ctx, branchOpts) | |||
if err != nil { | |||
ctx.ServerError("FindBranchNames", err) | |||
return | |||
} | |||
// always put default branch on the top if it exists | |||
if slices.Contains(branches, ctx.Repo.Repository.DefaultBranch) { | |||
branches = util.SliceRemoveAll(branches, ctx.Repo.Repository.DefaultBranch) | |||
branches = append([]string{ctx.Repo.Repository.DefaultBranch}, branches...) | |||
} | |||
ctx.Data["Branches"] = branches | |||
tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID) | |||
if err != nil { | |||
ctx.ServerError("GetTagNamesByRepoID", err) | |||
return | |||
} | |||
ctx.Data["Tags"] = tags | |||
} | |||
} | |||
} | |||
// if status or actor query param is not given to frontend href, (href="/<repoLink>/actions") | |||
@@ -169,7 +211,7 @@ func List(ctx *context.Context) { | |||
PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")), | |||
}, | |||
RepoID: ctx.Repo.Repository.ID, | |||
WorkflowID: workflow, | |||
WorkflowID: workflowID, | |||
TriggerUserID: actorID, | |||
} | |||
@@ -206,7 +248,7 @@ func List(ctx *context.Context) { | |||
pager := context.NewPagination(int(total), opts.PageSize, opts.Page, 5) | |||
pager.SetDefaultParams(ctx) | |||
pager.AddParamString("workflow", workflow) | |||
pager.AddParamString("workflow", workflowID) | |||
pager.AddParamString("actor", fmt.Sprint(actorID)) | |||
pager.AddParamString("status", fmt.Sprint(status)) | |||
ctx.Data["Page"] = pager |
@@ -18,18 +18,26 @@ import ( | |||
actions_model "code.gitea.io/gitea/models/actions" | |||
"code.gitea.io/gitea/models/db" | |||
"code.gitea.io/gitea/models/perm" | |||
access_model "code.gitea.io/gitea/models/perm/access" | |||
repo_model "code.gitea.io/gitea/models/repo" | |||
"code.gitea.io/gitea/models/unit" | |||
"code.gitea.io/gitea/modules/actions" | |||
"code.gitea.io/gitea/modules/base" | |||
"code.gitea.io/gitea/modules/git" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/setting" | |||
"code.gitea.io/gitea/modules/storage" | |||
api "code.gitea.io/gitea/modules/structs" | |||
"code.gitea.io/gitea/modules/timeutil" | |||
"code.gitea.io/gitea/modules/util" | |||
"code.gitea.io/gitea/modules/web" | |||
actions_service "code.gitea.io/gitea/services/actions" | |||
context_module "code.gitea.io/gitea/services/context" | |||
"code.gitea.io/gitea/services/convert" | |||
"github.com/nektos/act/pkg/jobparser" | |||
"github.com/nektos/act/pkg/model" | |||
"xorm.io/builder" | |||
) | |||
@@ -715,3 +723,164 @@ func disableOrEnableWorkflowFile(ctx *context_module.Context, isEnable bool) { | |||
url.QueryEscape(ctx.FormString("actor")), url.QueryEscape(ctx.FormString("status"))) | |||
ctx.JSONRedirect(redirectURL) | |||
} | |||
func Run(ctx *context_module.Context) { | |||
redirectURL := fmt.Sprintf("%s/actions?workflow=%s&actor=%s&status=%s", ctx.Repo.RepoLink, url.QueryEscape(ctx.FormString("workflow")), | |||
url.QueryEscape(ctx.FormString("actor")), url.QueryEscape(ctx.FormString("status"))) | |||
workflowID := ctx.FormString("workflow") | |||
if len(workflowID) == 0 { | |||
ctx.ServerError("workflow", nil) | |||
return | |||
} | |||
ref := ctx.FormString("ref") | |||
if len(ref) == 0 { | |||
ctx.ServerError("ref", nil) | |||
return | |||
} | |||
// can not rerun job when workflow is disabled | |||
cfgUnit := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions) | |||
cfg := cfgUnit.ActionsConfig() | |||
if cfg.IsWorkflowDisabled(workflowID) { | |||
ctx.Flash.Error(ctx.Tr("actions.workflow.disabled")) | |||
ctx.Redirect(redirectURL) | |||
return | |||
} | |||
// get target commit of run from specified ref | |||
refName := git.RefName(ref) | |||
var runTargetCommit *git.Commit | |||
var err error | |||
if refName.IsTag() { | |||
runTargetCommit, err = ctx.Repo.GitRepo.GetTagCommit(refName.TagName()) | |||
} else if refName.IsBranch() { | |||
runTargetCommit, err = ctx.Repo.GitRepo.GetBranchCommit(refName.BranchName()) | |||
} else { | |||
ctx.Flash.Error(ctx.Tr("form.git_ref_name_error", ref)) | |||
ctx.Redirect(redirectURL) | |||
return | |||
} | |||
if err != nil { | |||
ctx.Flash.Error(ctx.Tr("form.target_ref_not_exist", ref)) | |||
ctx.Redirect(redirectURL) | |||
return | |||
} | |||
// get workflow entry from default branch commit | |||
defaultBranchCommit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch) | |||
if err != nil { | |||
ctx.Error(http.StatusInternalServerError, err.Error()) | |||
return | |||
} | |||
entries, err := actions.ListWorkflows(defaultBranchCommit) | |||
if err != nil { | |||
ctx.Error(http.StatusInternalServerError, err.Error()) | |||
return | |||
} | |||
// find workflow from commit | |||
var workflows []*jobparser.SingleWorkflow | |||
for _, entry := range entries { | |||
if entry.Name() == workflowID { | |||
content, err := actions.GetContentFromEntry(entry) | |||
if err != nil { | |||
ctx.Error(http.StatusInternalServerError, err.Error()) | |||
return | |||
} | |||
workflows, err = jobparser.Parse(content) | |||
if err != nil { | |||
ctx.ServerError("workflow", err) | |||
return | |||
} | |||
break | |||
} | |||
} | |||
if len(workflows) == 0 { | |||
ctx.Flash.Error(ctx.Tr("actions.workflow.not_found", workflowID)) | |||
ctx.Redirect(redirectURL) | |||
return | |||
} | |||
// get inputs from post | |||
workflow := &model.Workflow{ | |||
RawOn: workflows[0].RawOn, | |||
} | |||
inputs := make(map[string]any) | |||
if workflowDispatch := workflow.WorkflowDispatchConfig(); workflowDispatch != nil { | |||
for name, config := range workflowDispatch.Inputs { | |||
value := ctx.Req.PostForm.Get(name) | |||
if config.Type == "boolean" { | |||
// https://www.w3.org/TR/html401/interact/forms.html | |||
// https://stackoverflow.com/questions/11424037/do-checkbox-inputs-only-post-data-if-theyre-checked | |||
// Checkboxes (and radio buttons) are on/off switches that may be toggled by the user. | |||
// A switch is "on" when the control element's checked attribute is set. | |||
// When a form is submitted, only "on" checkbox controls can become successful. | |||
inputs[name] = strconv.FormatBool(value == "on") | |||
} else if value != "" { | |||
inputs[name] = value | |||
} else { | |||
inputs[name] = config.Default | |||
} | |||
} | |||
} | |||
// ctx.Req.PostForm -> WorkflowDispatchPayload.Inputs -> ActionRun.EventPayload -> runner: ghc.Event | |||
// https://docs.github.com/en/actions/learn-github-actions/contexts#github-context | |||
// https://docs.github.com/en/webhooks/webhook-events-and-payloads#workflow_dispatch | |||
workflowDispatchPayload := &api.WorkflowDispatchPayload{ | |||
Workflow: workflowID, | |||
Ref: ref, | |||
Repository: convert.ToRepo(ctx, ctx.Repo.Repository, access_model.Permission{AccessMode: perm.AccessModeNone}), | |||
Inputs: inputs, | |||
Sender: convert.ToUserWithAccessMode(ctx, ctx.Doer, perm.AccessModeNone), | |||
} | |||
var eventPayload []byte | |||
if eventPayload, err = workflowDispatchPayload.JSONPayload(); err != nil { | |||
ctx.ServerError("JSONPayload", err) | |||
return | |||
} | |||
run := &actions_model.ActionRun{ | |||
Title: strings.SplitN(runTargetCommit.CommitMessage, "\n", 2)[0], | |||
RepoID: ctx.Repo.Repository.ID, | |||
OwnerID: ctx.Repo.Repository.OwnerID, | |||
WorkflowID: workflowID, | |||
TriggerUserID: ctx.Doer.ID, | |||
Ref: ref, | |||
CommitSHA: runTargetCommit.ID.String(), | |||
IsForkPullRequest: false, | |||
Event: "workflow_dispatch", | |||
TriggerEvent: "workflow_dispatch", | |||
EventPayload: string(eventPayload), | |||
Status: actions_model.StatusWaiting, | |||
} | |||
// cancel running jobs of the same workflow | |||
if err := actions_model.CancelPreviousJobs( | |||
ctx, | |||
run.RepoID, | |||
run.Ref, | |||
run.WorkflowID, | |||
run.Event, | |||
); err != nil { | |||
log.Error("CancelRunningJobs: %v", err) | |||
} | |||
// Insert the action run and its associated jobs into the database | |||
if err := actions_model.InsertRun(ctx, run, workflows); err != nil { | |||
ctx.ServerError("workflow", err) | |||
return | |||
} | |||
alljobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: run.ID}) | |||
if err != nil { | |||
log.Error("FindRunJobs: %v", err) | |||
} | |||
actions_service.CreateCommitStatus(ctx, alljobs...) | |||
ctx.Flash.Success(ctx.Tr("actions.workflow.run_success", workflowID)) | |||
ctx.Redirect(redirectURL) | |||
} |
@@ -1375,6 +1375,7 @@ func registerRoutes(m *web.Route) { | |||
m.Get("", actions.List) | |||
m.Post("/disable", reqRepoAdmin, actions.DisableWorkflowFile) | |||
m.Post("/enable", reqRepoAdmin, actions.EnableWorkflowFile) | |||
m.Post("/run", reqRepoAdmin, actions.Run) | |||
m.Group("/runs/{run}", func() { | |||
m.Combo(""). |
@@ -76,6 +76,11 @@ | |||
</button> | |||
{{end}} | |||
</div> | |||
{{if .WorkflowDispatchConfig}} | |||
{{template "repo/actions/workflow_dispatch" .}} | |||
{{end}} | |||
{{template "repo/actions/runs_list" .}} | |||
</div> | |||
</div> |
@@ -0,0 +1,85 @@ | |||
<div class="ui blue info message tw-flex tw-justify-between tw-items-center"> | |||
<span class="ui text middle">{{ctx.Locale.Tr "actions.workflow.has_workflow_dispatch"}}</span> | |||
<button class="ui mini button show-modal" data-modal="#runWorkflowDispatchModal">{{ctx.Locale.Tr "actions.workflow.run"}}{{svg "octicon-triangle-down" 14 "dropdown icon"}}</button> | |||
</div> | |||
<div id="runWorkflowDispatchModal" class="ui mini modal"> | |||
<div class="content"> | |||
<form id="runWorkflowDispatchForm" class="ui form" action="{{$.Link}}/run?workflow={{$.CurWorkflow}}&actor={{$.CurActor}}&status={{.Status}}" method="post"> | |||
{{.CsrfTokenHtml}} | |||
<div class="ui field required"> | |||
<label>{{ctx.Locale.Tr "actions.workflow.from_ref"}}:</label> | |||
<div class="ui filter dropdown button select-branch"> | |||
<input type="hidden" name="ref" value="refs/heads/{{index .Branches 0}}"> | |||
{{svg "octicon-git-branch"}} | |||
<span class="text branch-name gt-ellipsis">{{index .Branches 0}}</span> | |||
<div class="menu"> | |||
<div class="ui icon search input tw-w-auto"> | |||
<i class="search icon">{{svg "octicon-search" 16 "search icon"}}</i> | |||
<input type="text" placeholder="{{ctx.Locale.Tr "repo.filter_branch_and_tag"}}"> | |||
</div> | |||
<div class="header"> | |||
<div class="ui grid"> | |||
<div class="two column row"> | |||
<a class="reference column muted" href="#" data-target="#branch-list"> | |||
<span class="text black"> | |||
{{svg "octicon-git-branch" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.branches"}} | |||
</span> | |||
</a> | |||
<a class="reference column muted" href="#" data-target="#tag-list"> | |||
<span class="text"> | |||
{{svg "octicon-tag" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.tags"}} | |||
</span> | |||
</a> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="branch-tag-divider"></div> | |||
<div id="branch-list" class="scrolling menu reference-list-menu"> | |||
{{range .Branches}} | |||
<div class="item" data-value="refs/heads/{{.}}" title="{{.}}">{{.}}</div> | |||
{{else}} | |||
<div class="item">{{ctx.Locale.Tr "no_results_found"}}</div> | |||
{{end}} | |||
</div> | |||
<div id="tag-list" class="scrolling menu reference-list-menu tw-hidden"> | |||
{{range .Tags}} | |||
<div class="item" data-value="refs/tags/{{.}}" title="{{.}}">{{.}}</div> | |||
{{else}} | |||
<div class="item">{{ctx.Locale.Tr "no_results_found"}}</div> | |||
{{end}} | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
{{range $key, $item := .WorkflowDispatchConfig.Inputs}} | |||
<div class="ui field {{if .Required}}required{{end}}"> | |||
{{if eq .Type "choice"}} | |||
<label>{{$key}}:</label> | |||
<select class="ui fluid dropdown" name="{{$key}}"> | |||
{{range .Options}} | |||
<option value="{{.}}" {{if eq $item.Default .}}selected{{end}} >{{.}}</option> | |||
{{end}} | |||
</select> | |||
<span class="help">{{.Description}}</span> | |||
{{else if eq .Type "boolean"}} | |||
<div class="ui inline toggle checkbox"> | |||
<label>{{$key}}</label> | |||
<input type="checkbox" name="{{$key}}" {{if eq .Default "true"}}checked{{end}}> | |||
<span class="help">{{.Description}}</span> | |||
</div> | |||
{{else if eq .Type "number"}} | |||
<label>{{$key}}:</label> | |||
<input name="{{$key}}" value="{{.Default}}" {{if .Required}}required{{end}}> | |||
<span class="help">{{.Description}}</span> | |||
{{else}} | |||
<label>{{$key}}:</label> | |||
<input name="{{$key}}" value="{{.Default}}" {{if .Required}}required{{end}}> | |||
<span class="help">{{.Description}}</span> | |||
{{end}} | |||
</div> | |||
{{end}} | |||
<button class="ui tiny primary button" type="submit">Submit</button> | |||
</form> | |||
</div> | |||
</div> |
@@ -5,15 +5,15 @@ | |||
{{$.CsrfTokenHtml}} | |||
</form> | |||
{{/* TODO: share this branch selector dropdown with the same in repo page */}} | |||
<div class="ui {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}} floating filter select-branch dropdown tw-max-w-full" data-no-results="{{ctx.Locale.Tr "no_results_found"}}"> | |||
<div class="ui {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}} floating filter select-branch issue-select-branch dropdown tw-max-w-full" data-no-results="{{ctx.Locale.Tr "no_results_found"}}"> | |||
<div class="ui basic small button"> | |||
<span class="text branch-name gt-ellipsis">{{if .Reference}}{{$.RefEndName}}{{else}}{{ctx.Locale.Tr "repo.issues.no_ref"}}{{end}}</span> | |||
{{if .HasIssuesOrPullsWritePermission}}{{svg "octicon-triangle-down" 14 "dropdown icon"}}{{end}} | |||
</div> | |||
<div class="menu"> | |||
<div class="ui icon search input"> | |||
<i class="icon">{{svg "octicon-filter" 16}}</i> | |||
<input name="search" placeholder="{{ctx.Locale.Tr "repo.filter_branch_and_tag"}}..."> | |||
<i class="search icon">{{svg "octicon-search" 16 "search icon"}}</i> | |||
<input type="text" placeholder="{{ctx.Locale.Tr "repo.filter_branch_and_tag"}}"> | |||
</div> | |||
<div class="header"> | |||
<div class="ui grid"> |
@@ -2810,14 +2810,14 @@ tbody.commit-list { | |||
} | |||
/* Branch tag selector - TODO: Merge this into the same selector on repo page */ | |||
.repository .issue-content .issue-content-right .ui.grid .column.row { | |||
.select-branch .ui.grid .column.row { | |||
padding: 10px; | |||
padding-bottom: 0; | |||
} | |||
.repository .issue-content .issue-content-right .ui.grid .column.muted { | |||
.select-branch .ui.grid .column.muted { | |||
padding: 0; | |||
} | |||
.repository .issue-content .issue-content-right .ui.grid .column.muted .text { | |||
.select-branch .ui.grid .column.muted .text { | |||
display: inline-block; | |||
padding: 10px; | |||
width: 100%; | |||
@@ -2825,16 +2825,16 @@ tbody.commit-list { | |||
border: 1px solid transparent; | |||
border-bottom: none; | |||
} | |||
.repository .issue-content .issue-content-right .ui.grid .column.muted .text.black { | |||
.select-branch .ui.grid .column.muted .text.black { | |||
border-color: var(--color-secondary); | |||
background: var(--color-menu); | |||
border-top-left-radius: var(--border-radius); | |||
border-top-right-radius: var(--border-radius); | |||
} | |||
.repository .issue-content .issue-content-right .ui.dropdown .scrolling.menu { | |||
border-top: none; | |||
.select-branch .scrolling.menu { | |||
border-top: none !important; | |||
} | |||
.repository .issue-content .issue-content-right .branch-tag-divider { | |||
.select-branch .branch-tag-divider { | |||
margin-top: -1px; | |||
border-top: 1px solid var(--color-secondary); | |||
} |
@@ -43,6 +43,50 @@ function reloadConfirmDraftComment() { | |||
window.location.reload(); | |||
} | |||
// code specific to the issue branch selector | |||
export function initBranchSelectorIssue() { | |||
const $selectBranch = $('.ui.issue-select-branch'); | |||
if (!$selectBranch.length) return; | |||
const $branchMenu = $selectBranch.find('.reference-list-menu'); | |||
const $isNewIssue = $branchMenu.hasClass('new-issue'); | |||
$branchMenu.find('.item:not(.no-select)').on('click', async function () { | |||
const selectedValue = $(this).data('id'); | |||
const editMode = $('#editing_mode').val(); | |||
$($(this).data('id-selector')).val(selectedValue); | |||
if ($isNewIssue) { | |||
$selectBranch.find('.ui .branch-name').text($(this).data('name')); | |||
return; | |||
} | |||
if (editMode === 'true') { | |||
const form = document.getElementById('update_issueref_form'); | |||
const params = new URLSearchParams(); | |||
params.append('ref', selectedValue); | |||
try { | |||
await POST(form.getAttribute('action'), {data: params}); | |||
window.location.reload(); | |||
} catch (error) { | |||
console.error(error); | |||
} | |||
} else if (editMode === '') { | |||
$selectBranch.find('.ui .branch-name').text(selectedValue); | |||
} | |||
}); | |||
} | |||
// code for all branch selectors | |||
export function initBranchSelectorTabs() { | |||
const $selectBranch = $('.ui.select-branch'); | |||
if (!$selectBranch.length) return; | |||
$selectBranch.find('.reference.column').on('click', function () { | |||
hideElem($selectBranch.find('.scrolling.reference-list-menu')); | |||
$selectBranch.find('.reference .text').removeClass('black'); | |||
showElem($($(this).data('target'))); | |||
$(this).find('.text').addClass('black'); | |||
return false; | |||
}); | |||
} | |||
export function initRepoCommentForm() { | |||
const $commentForm = $('.comment.form'); | |||
if (!$commentForm.length) return; | |||
@@ -55,44 +99,6 @@ export function initRepoCommentForm() { | |||
initSingleCommentEditor($commentForm); | |||
} | |||
function initBranchSelector() { | |||
const $selectBranch = $('.ui.select-branch'); | |||
const $branchMenu = $selectBranch.find('.reference-list-menu'); | |||
const $isNewIssue = $branchMenu.hasClass('new-issue'); | |||
$branchMenu.find('.item:not(.no-select)').on('click', async function () { | |||
const selectedValue = $(this).data('id'); | |||
const editMode = $('#editing_mode').val(); | |||
$($(this).data('id-selector')).val(selectedValue); | |||
if ($isNewIssue) { | |||
$selectBranch.find('.ui .branch-name').text($(this).data('name')); | |||
return; | |||
} | |||
if (editMode === 'true') { | |||
const form = document.getElementById('update_issueref_form'); | |||
const params = new URLSearchParams(); | |||
params.append('ref', selectedValue); | |||
try { | |||
await POST(form.getAttribute('action'), {data: params}); | |||
window.location.reload(); | |||
} catch (error) { | |||
console.error(error); | |||
} | |||
} else if (editMode === '') { | |||
$selectBranch.find('.ui .branch-name').text(selectedValue); | |||
} | |||
}); | |||
$selectBranch.find('.reference.column').on('click', function () { | |||
hideElem($selectBranch.find('.scrolling.reference-list-menu')); | |||
$selectBranch.find('.reference .text').removeClass('black'); | |||
showElem($($(this).data('target'))); | |||
$(this).find('.text').addClass('black'); | |||
return false; | |||
}); | |||
} | |||
initBranchSelector(); | |||
// List submits | |||
function initListSubmits(selector, outerSelector) { | |||
const $list = $(`.ui.${outerSelector}.list`); |
@@ -71,7 +71,12 @@ import {initCompWebHookEditor} from './features/comp/WebHookEditor.js'; | |||
import {initRepoBranchButton} from './features/repo-branch.js'; | |||
import {initCommonOrganization} from './features/common-organization.js'; | |||
import {initRepoWikiForm} from './features/repo-wiki.js'; | |||
import {initRepoCommentForm, initRepository} from './features/repo-legacy.js'; | |||
import { | |||
initRepoCommentForm, | |||
initRepository, | |||
initBranchSelectorIssue, | |||
initBranchSelectorTabs, | |||
} from './features/repo-legacy.js'; | |||
import {initCopyContent} from './features/copycontent.js'; | |||
import {initCaptcha} from './features/captcha.js'; | |||
import {initRepositoryActionView} from './components/RepoActionView.vue'; | |||
@@ -146,6 +151,8 @@ onDomReady(() => { | |||
initRepoBranchButton(); | |||
initRepoCodeView(); | |||
initRepoCommentForm(); | |||
initBranchSelectorIssue(); | |||
initBranchSelectorTabs(); | |||
initRepoEllipsisButton(); | |||
initRepoDiffCommitBranchesAndTags(); | |||
initRepoEditor(); |