feat(actions): show inherited org secrets/variables in repo settings #81

Merged
jmiller merged 1 commits from feature/inherited-org-secrets into main 2026-05-13 01:17:19 +00:00
5 changed files with 126 additions and 0 deletions
+2
View File
@@ -3692,6 +3692,7 @@
"secrets.deletion.success": "The secret has been removed.",
"secrets.deletion.failed": "Failed to remove secret.",
"secrets.management": "Secrets Management",
"secrets.overridden": "overridden by repository",
"actions.actions": "Actions",
"actions.unit.desc": "Manage actions",
"actions.status.unknown": "Unknown",
@@ -3796,6 +3797,7 @@
"actions.approve_all_success": "All workflow runs are approved successfully.",
"actions.variables": "Variables",
"actions.variables.management": "Variables Management",
"actions.variables.overridden": "overridden by repository",
"actions.variables.creation": "Add Variable",
"actions.variables.none": "There are no variables yet.",
"actions.variables.deletion": "Remove variable",
+21
View File
@@ -107,6 +107,27 @@ func Variables(ctx *context.Context) {
ctx.Data["Variables"] = variables
ctx.Data["DataMaxLength"] = actions_model.VariableDataMaxLength
ctx.Data["DescriptionMaxLength"] = actions_model.VariableDescriptionMaxLength
// MokoGitea: when viewing repo variables, also show inherited org variables
if vCtx.IsRepo && ctx.Repo != nil && ctx.Repo.Repository != nil {
orgVars, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{
OwnerID: ctx.Repo.Repository.OwnerID,
})
if err != nil {
log.Error("FindOrgVariables failed: %v", err)
} else if len(orgVars) > 0 {
repoVarNames := make(map[string]bool, len(variables))
for _, v := range variables {
repoVarNames[v.Name] = true
}
ctx.Data["OrgVariables"] = orgVars
ctx.Data["RepoVariableNames"] = repoVarNames
if err := ctx.Repo.Repository.LoadOwner(ctx); err == nil {
ctx.Data["OrgVariablesLink"] = ctx.Repo.Repository.Owner.HTMLURL(ctx) + "/settings/actions/variables"
}
}
}
ctx.HTML(http.StatusOK, vCtx.VariablesTemplate)
}
+19
View File
@@ -24,6 +24,25 @@ func SetSecretsContext(ctx *context.Context, ownerID, repoID int64) {
ctx.Data["Secrets"] = secrets
ctx.Data["DataMaxLength"] = secret_model.SecretDataMaxLength
ctx.Data["DescriptionMaxLength"] = secret_model.SecretDescriptionMaxLength
// MokoGitea: when viewing repo secrets, also show inherited org secrets
if repoID > 0 && ctx.Repo != nil && ctx.Repo.Repository != nil {
orgSecrets, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{OwnerID: ctx.Repo.Repository.OwnerID})
if err != nil {
log.Error("FindOrgSecrets failed: %v", err)
} else if len(orgSecrets) > 0 {
// Build a set of repo secret names to detect overrides
repoSecretNames := make(map[string]bool, len(secrets))
for _, s := range secrets {
repoSecretNames[s.Name] = true
}
ctx.Data["OrgSecrets"] = orgSecrets
ctx.Data["RepoSecretNames"] = repoSecretNames
if err := ctx.Repo.Repository.LoadOwner(ctx); err == nil {
ctx.Data["OrgSecretsLink"] = ctx.Repo.Repository.Owner.HTMLURL(ctx) + "/settings/actions/secrets"
}
}
}
}
func PerformSecretsPost(ctx *context.Context, ownerID, repoID int64, redirectURL string) {
+42
View File
@@ -65,6 +65,48 @@
{{end}}
</div>
{{/* Inherited organization secrets */}}
{{if .OrgSecrets}}
<h4 class="ui top attached header tw-mt-4">
{{ctx.Locale.Tr "secrets.management"}}{{ctx.Locale.Tr "org.name"}}
{{if .OrgSecretsLink}}
<div class="ui right">
<a class="ui tiny button" href="{{.OrgSecretsLink}}">{{ctx.Locale.Tr "settings.manage"}}</a>
</div>
{{end}}
</h4>
<div class="ui attached segment">
<div class="flex-divided-list items-with-main">
{{range .OrgSecrets}}
<div class="item tw-items-center">
<div class="item-leading">
{{svg "octicon-organization" 32}}
</div>
<div class="item-main">
<div class="item-title">
{{.Name}}
{{if index $.RepoSecretNames .Name}}
<span class="ui label mini">{{ctx.Locale.Tr "secrets.overridden"}}</span>
{{end}}
</div>
<div class="item-body">
{{if .Description}}{{.Description}}{{else}}-{{end}}
</div>
<div class="item-body">
******
</div>
</div>
<div class="item-trailing">
<span class="color-text-light-2">
{{ctx.Locale.Tr "settings.added_on" (DateUtils.AbsoluteShort .CreatedUnix)}}
</span>
</div>
</div>
{{end}}
</div>
</div>
{{end}}
{{/* Add secret dialog */}}
<div class="ui small modal" id="add-secret-modal">
<div class="header"></div>
@@ -63,6 +63,48 @@
{{end}}
</div>
{{/* Inherited organization variables */}}
{{if .OrgVariables}}
<h4 class="ui top attached header tw-mt-4">
{{ctx.Locale.Tr "actions.variables.management"}}{{ctx.Locale.Tr "org.name"}}
{{if .OrgVariablesLink}}
<div class="ui right">
<a class="ui tiny button" href="{{.OrgVariablesLink}}">{{ctx.Locale.Tr "settings.manage"}}</a>
</div>
{{end}}
</h4>
<div class="ui attached segment">
<div class="flex-divided-list items-with-main">
{{range .OrgVariables}}
<div class="item tw-items-center">
<div class="item-leading">
{{svg "octicon-organization" 32}}
</div>
<div class="item-main">
<div class="item-title">
{{.Name}}
{{if index $.RepoVariableNames .Name}}
<span class="ui label mini">{{ctx.Locale.Tr "actions.variables.overridden"}}</span>
{{end}}
</div>
<div class="item-body">
{{if .Description}}{{.Description}}{{else}}-{{end}}
</div>
<div class="item-body">
{{.Data}}
</div>
</div>
<div class="item-trailing">
<span class="color-text-light-2">
{{ctx.Locale.Tr "settings.added_on" (DateUtils.AbsoluteShort .CreatedUnix)}}
</span>
</div>
</div>
{{end}}
</div>
</div>
{{end}}
{{/** Edit variable dialog */}}
<div class="ui small modal" id="edit-variable-modal">
<div class="header"></div>