diff --git a/capytal/TRADEMARK b/capytal/TRADEMARK new file mode 100644 index 0000000..c3c6eac --- /dev/null +++ b/capytal/TRADEMARK @@ -0,0 +1,2 @@ +"Capytal", "Capytal Company", "Capytal Code", "Capytal Creators", the Capytal Logo, +the Capytal Code Logo, are trademarks of Gustavo L. de Mello. diff --git a/capytal/default.nix b/capytal/default.nix new file mode 100644 index 0000000..a86d834 --- /dev/null +++ b/capytal/default.nix @@ -0,0 +1,5 @@ +{...}: { + imports = [ + ./forgejo + ]; +} diff --git a/capytal/forgejo/assets/icon.png b/capytal/forgejo/assets/icon.png new file mode 100644 index 0000000..491a401 Binary files /dev/null and b/capytal/forgejo/assets/icon.png differ diff --git a/capytal/forgejo/assets/icon.svg b/capytal/forgejo/assets/icon.svg new file mode 100644 index 0000000..7f479ff --- /dev/null +++ b/capytal/forgejo/assets/icon.svg @@ -0,0 +1,288 @@ + + + + diff --git a/capytal/forgejo/assets/logo.png b/capytal/forgejo/assets/logo.png new file mode 100644 index 0000000..6b10717 Binary files /dev/null and b/capytal/forgejo/assets/logo.png differ diff --git a/capytal/forgejo/assets/logo.svg b/capytal/forgejo/assets/logo.svg new file mode 100644 index 0000000..5e19548 --- /dev/null +++ b/capytal/forgejo/assets/logo.svg @@ -0,0 +1,288 @@ + + + + diff --git a/capytal/forgejo/default.nix b/capytal/forgejo/default.nix new file mode 100644 index 0000000..27aa9ad --- /dev/null +++ b/capytal/forgejo/default.nix @@ -0,0 +1,103 @@ +{ + config, + inputs, + pkgs, + pkgs-unstable, + ... +}: +with builtins; let + secrets = config.spacestation-secrets.lesser; + + frappurccino-theme = + readFile + "${inputs.frappurccino-forgejo.packages.${pkgs.system}.default}/css/theme-frappurccino-mocha-sky.css"; + + cal-sans = pkgs.fetchzip { + url = "https://github.com/calcom/font/releases/download/v1.0.0/CalSans_Semibold_v1.0.0.zip"; + stripRoot = false; + hash = "sha256-JqU64JUgWimJgrKX3XYcml8xsvy//K7O5clNKJRGaTM="; + }; + fonts-css = pkgs.writeText "custom.css" '' + @font-face { + family: 'Cal Sans'; + src: + url('assets/fonts/CalSans-SemiBold.woff2') format('woff2'), + url('assets/fonts/CalSans-SemiBold.woff') format('woff'), + url('assets/fonts/CalSans-SemiBold.ttf') format('truetype'); + }; + ''; +in { + imports = [ + ../../modules/forgejo + ]; + services.forgejo = { + enable = true; + package = pkgs-unstable.forgejo; + actions = { + enable = true; + token = secrets.services.forgejo.actions-token; + url = "https://forgejo.capytal.company"; + labels = secrets.services.forgejo.actions-labels; + }; + customization = { + assets = { + "fonts" = { + source = "${cal-sans}/fonts/webfonts"; + recursive = true; + }; + "fonts.css" = { + source = fonts-css; + }; + "img/home-logo.png" = { + source = ./assets/logo.png; + }; + "img/home-logo.svg" = { + source = ./assets/logo.svg; + }; + }; + templates = { + header = '' + + ${readFile ./templates/custom/header.tmpl} + ''; + home = ./templates/home.tmpl; + }; + theme = { + "frappurccino" = frappurccino-theme; + }; + favicon.png = ./assets/icon.png; + favicon.svg = ./assets/icon.svg; + logo.png = ./assets/icon.png; + logo.svg = ./assets/icon.svg; + }; + # users = { + # user1 = { + # name = /. + config.sops.secrets."forgejo/user1/name".path; + # password = /. + config.sops.secrets."forgejo/user1/password".path; + # email = /. + config.sops.secrets."forgejo/user1/email".path; + # admin = true; + # }; + # }; + settings = { + DEFAULT = { + APP_NAME = "Capytal Code"; + }; + server = rec { + HTTP_PORT = secrets.services.forgejo.port; + DOMAIN = "forgejo.capytal.company"; + ROOT_URL = "https://${DOMAIN}"; + }; + admin = { + DISABLE_REGULAR_ORG_CREATION = true; + USER_DISABLED_FEATURES = "deletion manage_ssh_keys manage_gpg_keys"; + EXTERNAL_USER_DISABLED_FEATURES = "deletion manage_ssh_keys manage_gpg_keys"; + }; + service = { + DISABLE_REGISTRATION = true; + }; + ui = { + DEFAULT_THEME = "frappurccino"; + }; + }; + }; +} diff --git a/capytal/forgejo/templates/custom/header.tmpl b/capytal/forgejo/templates/custom/header.tmpl new file mode 100644 index 0000000..645a4e0 --- /dev/null +++ b/capytal/forgejo/templates/custom/header.tmpl @@ -0,0 +1,55 @@ + + + + + + diff --git a/capytal/forgejo/templates/explore/repo_list.tmpl b/capytal/forgejo/templates/explore/repo_list.tmpl new file mode 100644 index 0000000..63cd955 --- /dev/null +++ b/capytal/forgejo/templates/explore/repo_list.tmpl @@ -0,0 +1,73 @@ +
+ {{range .Repos}} +
+
+ {{template "repo/icon" .}} +
+
+
+
+ {{if and (or $.PageIsExplore $.PageIsProfileStarList) .Owner}} + {{.Owner.Name}}/ + {{end}} + {{.Name}} + + {{if .IsArchived}} + {{ctx.Locale.Tr "repo.desc.archived"}} + {{end}} + {{if .IsPrivate}} + {{ctx.Locale.Tr "repo.desc.private"}} + {{else}} + {{if .Owner.Visibility.IsPrivate}} + {{ctx.Locale.Tr "repo.desc.internal"}} + {{end}} + {{end}} + {{if .IsTemplate}} + {{ctx.Locale.Tr "repo.desc.template"}} + {{end}} + {{if eq .ObjectFormatName "sha256"}} + {{ctx.Locale.Tr "repo.desc.sha256"}} + {{end}} + +
+ +
+ {{$description := .DescriptionHTML $.Context}} + {{if $description}} +
{{$description}}
+ {{end}} + {{if .Topics}} +
+ {{range .Topics}} + {{if ne . ""}}{{.}}{{end}} + {{end}} +
+ {{end}} +
{{ctx.Locale.Tr "org.repo_updated" (TimeSinceUnix .UpdatedUnix ctx.Locale)}}
+
+
+ {{else}} +
+ {{ctx.Locale.Tr "search.no_results"}} +
+ {{end}} +
diff --git a/capytal/forgejo/templates/explore/user_list.tmpl b/capytal/forgejo/templates/explore/user_list.tmpl new file mode 100644 index 0000000..a9f53b3 --- /dev/null +++ b/capytal/forgejo/templates/explore/user_list.tmpl @@ -0,0 +1,33 @@ +
+ {{range .Users}} +
+
+ {{ctx.AvatarUtils.Avatar . 48}} +
+
+
+ {{template "shared/user/name" .}} + {{if .Visibility.IsPrivate}} + {{ctx.Locale.Tr "repo.desc.private"}} + {{end}} +
+
+ {{if .Location}} + {{svg "octicon-location"}}{{.Location}} + {{end}} + {{if and .Email (or (and $.ShowUserEmail $.IsSigned (not .KeepEmailPrivate)) $.PageIsAdminUsers)}} + + {{svg "octicon-mail"}} + {{.Email}} + + {{end}} + {{svg "octicon-calendar"}}{{ctx.Locale.Tr "user.joined_on" (DateTime "short" .CreatedUnix)}} +
+
+
+ {{else}} +
+ {{ctx.Locale.Tr "search.no_results"}} +
+ {{end}} +
diff --git a/capytal/forgejo/templates/home.tmpl b/capytal/forgejo/templates/home.tmpl new file mode 100644 index 0000000..25510f9 --- /dev/null +++ b/capytal/forgejo/templates/home.tmpl @@ -0,0 +1,51 @@ +{{template "base/head" .}} +
+
+
+ +
+ +

From dreams, to code.

+
+
+
+ +
+{{template "base/footer" .}} diff --git a/capytal/forgejo/templates/org/header.tmpl b/capytal/forgejo/templates/org/header.tmpl new file mode 100644 index 0000000..2fad97f --- /dev/null +++ b/capytal/forgejo/templates/org/header.tmpl @@ -0,0 +1,37 @@ +
+ {{ctx.AvatarUtils.Avatar .Org 100 "org-avatar"}} +
+
+
+ {{.Org.DisplayName}} + + {{if .Org.Visibility.IsLimited}}{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}{{end}} + {{if .Org.Visibility.IsPrivate}}{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}{{end}} + +
+ + {{if .EnableFeed}} + + {{svg "octicon-rss" 24}} + + {{end}} + {{if .IsSigned}} + {{template "org/follow_unfollow" .}} + {{end}} + {{if .IsOrganizationMember}} + {{ctx.Locale.Tr "org.open_dashboard"}} + {{end}} + +
+ {{if .RenderedDescription}}
{{.RenderedDescription}}
{{end}} +
+ {{if .Org.Location}}
{{svg "octicon-location"}} {{.Org.Location}}
{{end}} + {{if .Org.Website}}
{{svg "octicon-link"}} {{.Org.Website}}
{{end}} + {{if .IsSigned}} + {{if .Org.Email}}
{{svg "octicon-mail"}} {{.Org.Email}}
{{end}} + {{end}} +
+
+
+ +{{template "org/menu" .}} diff --git a/capytal/forgejo/templates/repo/home.tmpl b/capytal/forgejo/templates/repo/home.tmpl new file mode 100644 index 0000000..fc4f0c9 --- /dev/null +++ b/capytal/forgejo/templates/repo/home.tmpl @@ -0,0 +1,164 @@ +{{template "base/head" .}} +
+ {{template "repo/header" .}} +
+ {{template "base/alert" .}} + {{template "repo/code/recently_pushed_new_branches" .}} + {{if and (not .HideRepoInfo) (not .IsBlame)}} +
+
+ {{$description := .Repository.DescriptionHTML $.Context}} + {{if $description}}{{$description | RenderCodeBlock}}{{else}}{{ctx.Locale.Tr "repo.no_desc"}}{{end}} + {{if .Repository.Website}}{{.Repository.Website}}{{end}} +
+
+
+ + {{template "shared/search/button"}} +
+
+
+
+ {{/* it should match the code in issue-home.js */}} + {{range .Topics}}{{.Name}}{{end}} + {{if and .Permission.IsAdmin (not .Repository.IsArchived)}}{{end}} +
+ {{end}} + {{if and .Permission.IsAdmin (not .Repository.IsArchived)}} +
+ +
+ + +
+
+ {{end}} + + {{if RepoFlagsEnabled}} + {{template "custom/repo_flag_banners" .}} + {{if .SignedUser.IsAdmin}} + {{template "repo/admin_flags" .}} + {{end}} + {{end}} + + {{if .Repository.IsArchived}} +
+ {{if .Repository.ArchivedUnix.IsZero}} + {{ctx.Locale.Tr "repo.archive.title"}} + {{else}} + {{ctx.Locale.Tr "repo.archive.title_date" (DateTime "long" .Repository.ArchivedUnix)}} + {{end}} +
+ {{end}} + {{template "repo/sub_menu" .}} + {{$n := len .TreeNames}} + {{$l := Eval $n "-" 1}} + {{$isHomepage := (eq $n 0)}} +
+
+ {{template "repo/branch_dropdown" dict "root" . "ContainerClasses" "tw-mr-1"}} + {{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}} + {{$cmpBranch := ""}} + {{if ne .Repository.ID .BaseRepo.ID}} + {{$cmpBranch = printf "%s/%s:" (.Repository.OwnerName|PathEscape) (.Repository.Name|PathEscape)}} + {{end}} + {{$cmpBranch = print $cmpBranch (.BranchName|PathEscapeSegments)}} + {{$compareLink := printf "%s/compare/%s...%s" .BaseRepo.Link (.BaseRepo.DefaultBranch|PathEscapeSegments) $cmpBranch}} + + {{svg "octicon-git-pull-request"}} + + {{end}} + + {{if $isHomepage}} + {{ctx.Locale.Tr "repo.find_file.go_to_file"}} + {{end}} + + {{if and .CanWriteCode .IsViewBranch (not .Repository.IsMirror) (not .Repository.IsArchived) (not .IsViewFile)}} + + {{end}} + + {{if and $isHomepage (.Repository.IsTemplate)}} + + {{ctx.Locale.Tr "repo.use_template"}} + + {{end}} + {{if (not $isHomepage)}} + + {{StringUtils.EllipsisString .Repository.Name 30}} + {{- range $i, $v := .TreeNames -}} + / + {{- if eq $i $l -}} + {{$v}} + {{- else -}} + {{$p := index $.Paths $i}}{{$v}} + {{- end -}} + {{- end -}} + + {{end}} +
+
+ + {{if $isHomepage}} +
+ {{template "repo/clone_buttons" .}} + + {{template "repo/clone_script" .}}{{/* the script will update `.js-clone-url` and related elements */}} +
+ {{template "repo/cite/cite_modal" .}} + {{end}} + {{if and (not $isHomepage) (not .IsViewFile) (not .IsBlame)}}{{/* IsViewDirectory (not home), TODO: split the templates, avoid using "if" tricks */}} + + {{svg "octicon-history" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.file_history"}} + + {{end}} +
+
+ {{if .IsViewFile}} + {{template "repo/view_file" .}} + {{else if .IsBlame}} + {{template "repo/blame" .}} + {{else}}{{/* IsViewDirectory */}} + {{template "repo/view_list" .}} + {{end}} +
+
+{{template "base/footer" .}} diff --git a/capytal/forgejo/templates/repo/issue/list.tmpl b/capytal/forgejo/templates/repo/issue/list.tmpl new file mode 100644 index 0000000..9123509 --- /dev/null +++ b/capytal/forgejo/templates/repo/issue/list.tmpl @@ -0,0 +1,55 @@ +{{template "base/head" .}} +
+ {{template "repo/header" .}} +
+ {{template "base/alert" .}} + + {{if .PinnedIssues}} +
+ {{range .PinnedIssues}} +
+ {{template "repo/issue/card" (dict "Issue" . "Page" $ "isPinnedIssueCard" true)}} +
+ {{end}} +
+ {{end}} + +
+ {{template "repo/issue/navbar" .}} + {{template "repo/issue/search" .}} + {{if not .Repository.IsArchived}} + {{if .PageIsIssueList}} + {{ctx.Locale.Tr "repo.issues.new"}} + {{else}} + {{ctx.Locale.Tr "repo.pulls.new"}} + {{end}} + {{else}} + {{if not .PageIsIssueList}} + {{ctx.Locale.Tr "action.compare_commits_general"}} + {{end}} + {{end}} +
+ + {{template "repo/issue/filters" .}} + +
+
+ {{template "repo/issue/openclose" .}} + + {{if .TotalTrackedTime}} + + {{end}} +
+
+ {{template "repo/issue/filter_actions" .}} +
+
+ {{template "shared/issuelist" dict "." . "listType" "repo"}} +
+
+{{template "base/footer" .}} diff --git a/capytal/forgejo/templates/shared/issuelist.tmpl b/capytal/forgejo/templates/shared/issuelist.tmpl new file mode 100644 index 0000000..086f1b4 --- /dev/null +++ b/capytal/forgejo/templates/shared/issuelist.tmpl @@ -0,0 +1,163 @@ +
+ {{$approvalCounts := .ApprovalCounts}} + {{range .Issues}} +
+ +
+ {{if $.CanWriteIssuesOrPulls}} + + {{end}} + {{template "shared/issueicon" .}} +
+ +
+
+
+ {{RenderEmoji $.Context .Title | RenderCodeBlock}} + {{if .IsPull}} + {{if (index $.CommitStatuses .PullRequest.ID)}} + {{template "repo/commit_statuses" dict "Status" (index $.CommitLastStatus .PullRequest.ID) "Statuses" (index $.CommitStatuses .PullRequest.ID)}} + {{end}} + {{end}} + + {{range .Labels}} + {{RenderLabel $.Context ctx.Locale .}} + {{end}} + +
+ {{if or .TotalTrackedTime .Assignees .NumComments}} +
+ {{if .TotalTrackedTime}} +
+ {{svg "octicon-clock" 16}} + {{.TotalTrackedTime | Sec2Time}} +
+ {{end}} + {{if .Assignees}} +
+ {{range .Assignees}} + + {{ctx.AvatarUtils.Avatar . 20}} + + {{end}} +
+ {{end}} + {{if .NumComments}} + + {{end}} +
+ {{end}} +
+
+ + {{if eq $.listType "dashboard"}} + {{.Repo.FullName}}#{{.Index}} + {{else}} + #{{.Index}} + {{end}} + + {{$timeStr := TimeSinceUnix .GetLastEventTimestamp ctx.Locale}} + {{if .OriginalAuthor}} + {{ctx.Locale.Tr .GetLastEventLabelFake $timeStr .OriginalAuthor}} + {{else if gt .Poster.ID 0}} + {{ctx.Locale.Tr .GetLastEventLabel $timeStr .Poster.HomeLink .Poster.GetDisplayName}} + {{else}} + {{ctx.Locale.Tr .GetLastEventLabelFake $timeStr .Poster.GetDisplayName}} + {{end}} + {{if .IsPull}} + + {{end}} + {{if and .Milestone (ne $.listType "milestone")}} + + {{svg "octicon-milestone" 14}} + {{.Milestone.Name}} + + {{end}} + {{if .Project}} + + {{svg .Project.IconName 14}} + {{.Project.Title}} + + {{end}} + {{if .Ref}} + + {{svg "octicon-git-branch" 14}} + {{index $.IssueRefEndNames .ID}} + + {{end}} + {{$tasks := .GetTasks}} + {{if gt $tasks 0}} + {{$tasksDone := .GetTasksDone}} + + {{svg "octicon-checklist" 14}}{{$tasksDone}} / {{$tasks}} + + + {{end}} + {{if ne .DeadlineUnix 0}} + + + {{svg "octicon-calendar" 14}} + {{DateTime "short" (.DeadlineUnix.FormatDate)}} + + + {{end}} + {{if .IsPull}} + {{$approveOfficial := call $approvalCounts .ID "approve"}} + {{$rejectOfficial := call $approvalCounts .ID "reject"}} + {{$waitingOfficial := call $approvalCounts .ID "waiting"}} + {{if gt $approveOfficial 0}} + + {{svg "octicon-check" 14}} + {{ctx.Locale.TrN $approveOfficial "repo.pulls.approve_count_1" "repo.pulls.approve_count_n" $approveOfficial}} + + {{end}} + {{if gt $rejectOfficial 0}} + + {{svg "octicon-diff" 14}} + {{ctx.Locale.TrN $rejectOfficial "repo.pulls.reject_count_1" "repo.pulls.reject_count_n" $rejectOfficial}} + + {{end}} + {{if gt $waitingOfficial 0}} + + {{svg "octicon-eye" 14}} + {{ctx.Locale.TrN $waitingOfficial "repo.pulls.waiting_count_1" "repo.pulls.waiting_count_n" $waitingOfficial}} + + {{end}} + {{if and (not .PullRequest.HasMerged) .PullRequest.ConflictedFiles}} + + {{svg "octicon-x" 14}} + {{ctx.Locale.TrN (len .PullRequest.ConflictedFiles) "repo.pulls.num_conflicting_files_1" "repo.pulls.num_conflicting_files_n" (len .PullRequest.ConflictedFiles)}} + + {{end}} + {{end}} +
+
+
+ {{end}} + {{if .IssueIndexerUnavailable}} +
+

{{ctx.Locale.Tr "search.keyword_search_unavailable"}}

+
+ {{end}} +
+{{template "base/paginate" .}} diff --git a/capytal/forgejo/templates/user/dashboard/feeds.tmpl b/capytal/forgejo/templates/user/dashboard/feeds.tmpl new file mode 100644 index 0000000..bf33526 --- /dev/null +++ b/capytal/forgejo/templates/user/dashboard/feeds.tmpl @@ -0,0 +1,127 @@ +
+ {{range .Feeds}} +
+
+ {{ctx.AvatarUtils.AvatarByAction .}} +
+
+
+ {{if gt .ActUser.ID 0}} + {{.GetActDisplayName ctx}} + {{else}} + {{.ShortActUserName ctx}} + {{end}} + {{if .GetOpType.InActions "create_repo"}} + {{ctx.Locale.Tr "action.create_repo" (.GetRepoLink ctx) (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "rename_repo"}} + {{ctx.Locale.Tr "action.rename_repo" .GetContent (.GetRepoLink ctx) (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "commit_repo"}} + {{if .Content}} + {{ctx.Locale.Tr "action.commit_repo" (.GetRepoLink ctx) (.GetRefLink ctx) .GetBranch (.ShortRepoPath ctx)}} + {{else}} + {{ctx.Locale.Tr "action.create_branch" (.GetRepoLink ctx) (.GetRefLink ctx) .GetBranch (.ShortRepoPath ctx)}} + {{end}} + {{else if .GetOpType.InActions "create_issue"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.create_issue" (printf "%s/issues/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "create_pull_request"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.create_pull_request" (printf "%s/pulls/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "transfer_repo"}} + {{ctx.Locale.Tr "action.transfer_repo" .GetContent (.GetRepoLink ctx) (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "push_tag"}} + {{ctx.Locale.Tr "action.push_tag" (.GetRepoLink ctx) (.GetRefLink ctx) .GetTag (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "comment_issue"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.comment_issue" (printf "%s/issues/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "merge_pull_request"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.merge_pull_request" (printf "%s/pulls/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "close_issue"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.close_issue" (printf "%s/issues/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "reopen_issue"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.reopen_issue" (printf "%s/issues/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "close_pull_request"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.close_pull_request" (printf "%s/pulls/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "reopen_pull_request"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.reopen_pull_request" (printf "%s/pulls/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "delete_tag"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.delete_tag" (.GetRepoLink ctx) .GetTag (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "delete_branch"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.delete_branch" (.GetRepoLink ctx) .GetBranch (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "mirror_sync_push"}} + {{ctx.Locale.Tr "action.mirror_sync_push" (.GetRepoLink ctx) (.GetRefLink ctx) .GetBranch (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "mirror_sync_create"}} + {{ctx.Locale.Tr "action.mirror_sync_create" (.GetRepoLink ctx) (.GetRefLink ctx) .GetBranch (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "mirror_sync_delete"}} + {{ctx.Locale.Tr "action.mirror_sync_delete" (.GetRepoLink ctx) .GetBranch (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "approve_pull_request"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.approve_pull_request" (printf "%s/pulls/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "reject_pull_request"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.reject_pull_request" (printf "%s/pulls/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "comment_pull"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.comment_pull" (printf "%s/pulls/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "publish_release"}} + {{$linkText := .Content | RenderEmoji $.Context}} + {{ctx.Locale.Tr "action.publish_release" (.GetRepoLink ctx) (printf "%s/releases/tag/%s" (.GetRepoLink ctx) .GetTag) (.ShortRepoPath ctx) $linkText}} + {{else if .GetOpType.InActions "review_dismissed"}} + {{$index := index .GetIssueInfos 0}} + {{$reviewer := index .GetIssueInfos 1}} + {{ctx.Locale.Tr "action.review_dismissed" (printf "%s/pulls/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx) $reviewer}} + {{end}} + {{TimeSince .GetCreate ctx.Locale}} +
+ {{if .GetOpType.InActions "commit_repo" "mirror_sync_push"}} + {{$push := ActionContent2Commits .}} + {{$repoLink := (.GetRepoLink ctx)}} + {{$repo := .Repo}} +
+ {{range $push.Commits}} + {{$commitLink := printf "%s/commit/%s" $repoLink .Sha1}} +
+ + {{ShortSha .Sha1}} + + {{RenderCommitMessage $.Context .Message ($repo.ComposeMetas ctx)}} + +
+ {{end}} +
+ {{if and (gt $push.Len 1) $push.CompareURL}} + {{ctx.Locale.Tr "action.compare_commits" $push.Len}} ยป + {{end}} + {{else if .GetOpType.InActions "create_issue"}} + {{index .GetIssueInfos 1 | RenderEmoji $.Context | RenderCodeBlock}} + {{else if .GetOpType.InActions "create_pull_request"}} + {{index .GetIssueInfos 1 | RenderEmoji $.Context | RenderCodeBlock}} + {{else if .GetOpType.InActions "comment_issue" "approve_pull_request" "reject_pull_request" "comment_pull"}} + {{(.GetIssueTitle ctx) | RenderEmoji $.Context | RenderCodeBlock}} + {{$comment := index .GetIssueInfos 1}} + {{if $comment}} +
{{RenderMarkdownToHtml ctx $comment}}
+ {{end}} + {{else if .GetOpType.InActions "merge_pull_request"}} +
{{index .GetIssueInfos 1}}
+ {{else if .GetOpType.InActions "close_issue" "reopen_issue" "close_pull_request" "reopen_pull_request"}} + {{(.GetIssueTitle ctx) | RenderEmoji $.Context | RenderCodeBlock}} + {{else if .GetOpType.InActions "pull_review_dismissed"}} +
{{ctx.Locale.Tr "action.review_dismissed_reason"}}
+
{{index .GetIssueInfos 2 | RenderEmoji $.Context}}
+ {{end}} +
+
+ {{svg (printf "octicon-%s" (ActionIcon .GetOpType)) 32 "text grey tw-mr-1"}} +
+
+ {{end}} + {{template "base/paginate" .}} +
diff --git a/configuration.nix b/configuration.nix index 98b9422..d85abfd 100644 --- a/configuration.nix +++ b/configuration.nix @@ -3,14 +3,16 @@ lib, inputs, pkgs, + pkgs-unstable, ... }: { imports = [ - ./hardware-configuration.nix inputs.dot013-environment.nixosModules.default + ./hardware-configuration.nix ./services ./modules ./secrets.nix + ./capytal ]; programs.nh.enable = true; @@ -19,7 +21,11 @@ profiles.locale.enable = true; home-manager.backupFileExtension = "backup~"; - home-manager.extraSpecialArgs = {inherit inputs;}; + home-manager.extraSpecialArgs = { + inherit inputs; + inherit pkgs; + inherit pkgs-unstable; + }; users.users."guz" = { shell = pkgs.zsh; hashedPasswordFile = builtins.toString config.sops.secrets."guz/password".path; diff --git a/flake.lock b/flake.lock index 50770f3..885092a 100644 --- a/flake.lock +++ b/flake.lock @@ -2,14 +2,16 @@ "nodes": { "dot013-environment": { "inputs": { - "nixpkgs": "nixpkgs" + "nixpkgs": [ + "nixpkgs-unstable" + ] }, "locked": { - "lastModified": 1725634253, - "narHash": "sha256-GbBtwR/rfu9nmfP9O87XitYRbJEQgxkxvHe6nzcHrYA=", + "lastModified": 1725918868, + "narHash": "sha256-XYZiGWFT8r9GrIIHyhN/3dYbkUCcAy4v5PyKYUzPOiI=", "owner": "dot013", "repo": "environment", - "rev": "91185158c5aca939661a128c50d1df55417e8dd7", + "rev": "f340ff699bd735d617148cd023885f1acd5b615f", "type": "github" }, "original": { @@ -18,6 +20,48 @@ "type": "github" } }, + "frappurccino-forgejo": { + "inputs": { + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1726239025, + "narHash": "sha256-9INgfUmWSE4kT/0niKvpqhuesP4FR7lBplaKfHm/q+g=", + "ref": "refs/heads/main", + "rev": "b24a35a56013a760515198fae58ce8711a22d05c", + "revCount": 54, + "type": "git", + "url": "file:///home/guz/.projects/capytal/frappurccino-forgejo" + }, + "original": { + "type": "git", + "url": "file:///home/guz/.projects/capytal/frappurccino-forgejo" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "frappurccino-forgejo", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, "home-manager": { "inputs": { "nixpkgs": [ @@ -61,16 +105,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1724224976, - "narHash": "sha256-Z/ELQhrSd7bMzTO8r7NZgi9g5emh+aRKoCdaAv5fiO0=", + "lastModified": 1725930920, + "narHash": "sha256-RVhD9hnlTT2nJzPHlAqrWqCkA7T6CYrP41IoVRkciZM=", "owner": "nixos", "repo": "nixpkgs", - "rev": "c374d94f1536013ca8e92341b540eba4c22f9c62", + "rev": "44a71ff39c182edaf25a7ace5c9454e7cba2c658", "type": "github" }, "original": { "owner": "nixos", - "ref": "nixos-unstable", + "ref": "nixos-24.05", "repo": "nixpkgs", "type": "github" } @@ -91,18 +135,18 @@ "type": "github" } }, - "nixpkgs_2": { + "nixpkgs-unstable": { "locked": { - "lastModified": 1725693463, - "narHash": "sha256-ZPzhebbWBOr0zRWW10FfqfbJlan3G96/h3uqhiFqmwg=", + "lastModified": 1726062873, + "narHash": "sha256-IiA3jfbR7K/B5+9byVi9BZGWTD4VSbWe8VLpp9B/iYk=", "owner": "nixos", "repo": "nixpkgs", - "rev": "68e7dce0a6532e876980764167ad158174402c6f", + "rev": "4f807e8940284ad7925ebd0a0993d2a1791acb2f", "type": "github" }, "original": { "owner": "nixos", - "ref": "nixos-24.05", + "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } @@ -110,9 +154,11 @@ "root": { "inputs": { "dot013-environment": "dot013-environment", + "frappurccino-forgejo": "frappurccino-forgejo", "home-manager": "home-manager", "nix-index-database": "nix-index-database", - "nixpkgs": "nixpkgs_2", + "nixpkgs": "nixpkgs", + "nixpkgs-unstable": "nixpkgs-unstable", "sops-nix": "sops-nix" } }, @@ -124,11 +170,11 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1725765163, - "narHash": "sha256-rfd2c47iVSFI6bRYy5l8wRijRBaYDeU7dM8XCDUGqlA=", + "lastModified": 1726218807, + "narHash": "sha256-z7CoWbSOtsOz8TmRKDnobURkKfv6nPZCo3ayolNuQGc=", "owner": "Mic92", "repo": "sops-nix", - "rev": "b68757cd2c3fa66d6ccaa0d046ce42a9324e0070", + "rev": "f30b1bac192e2dc252107ac8a59a03ad25e1b96e", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index b656e56..8a7d0c4 100644 --- a/flake.nix +++ b/flake.nix @@ -22,6 +22,12 @@ dot013-environment.url = "github:dot013/environment"; dot013-environment.inputs.nixpkgs.follows = "nixpkgs-unstable"; + + frappurccino-forgejo = { + url = "git+file:///home/guz/.projects/capytal/frappurccino-forgejo"; + # url = "git+https://forgejo.capytal.company/capytal/frappurccino-forgejo"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = { nixpkgs, diff --git a/modules/forgejo/customization.nix b/modules/forgejo/customization.nix new file mode 100644 index 0000000..265a337 --- /dev/null +++ b/modules/forgejo/customization.nix @@ -0,0 +1,136 @@ +{ + config, + inputs, + lib, + pkgs, + ... +}: let + cfg = config.services.forgejo.customization; + forgejo = config.services.forgejo; + + fileType = with lib.types; either str (either lines path); +in { + imports = [ + inputs.home-manager.nixosModules.default + ]; + options.services.forgejo.customization = with lib; + with lib.types; { + enable = mkOption { + type = bool; + default = true; + }; + assets = mkOption { + type = attrsOf anything; + default = {}; + }; + templates = { + header = mkOption { + type = nullOr fileType; + default = null; + }; + home = mkOption { + type = nullOr fileType; + default = null; + }; + }; + logo.svg = mkOption { + type = nullOr fileType; + default = null; + }; + logo.png = mkOption { + type = nullOr fileType; + default = null; + }; + favicon.svg = mkOption { + type = nullOr fileType; + default = null; + }; + favicon.png = mkOption { + type = nullOr fileType; + default = null; + }; + theme = mkOption { + type = nullOr (either (attrsOf fileType) fileType); + default = null; + }; + }; + config = with lib; + mkIf cfg.enable { + home-manager.users."${forgejo.user}" = let + home = { + lib, + forgejoConfig, + customization, + ... + }: + with lib; + with builtins; { + imports = []; + + programs.home-manager.enable = true; + + home.username = "${forgejoConfig.user}"; + home.homeDirectory = "${forgejoConfig.stateDir}"; + + home.file = let + fileTypeToHomeFile = theme: + if (isString theme) + then { + text = theme; + } + else {source = theme;}; + assetsDir = "${forgejoConfig.customDir}/public/assets"; + templatesDir = "${forgejoConfig.customDir}/templates"; + in + { + "${templatesDir}/custom/header.tmpl" = + mkIf (!(isNull customization.templates.header)) + (fileTypeToHomeFile customization.templates.header); + + "${templatesDir}/home.tmpl" = + mkIf (!(isNull customization.templates.home)) + (fileTypeToHomeFile customization.templates.home); + + "${assetsDir}/img/logo.svg" = + mkIf (!(isNull customization.logo.svg)) + (fileTypeToHomeFile customization.logo.svg); + + "${assetsDir}/img/logo.png" = + mkIf (!(isNull customization.logo.png)) + (fileTypeToHomeFile customization.logo.png); + + "${assetsDir}/img/favicon.svg" = + mkIf (!(isNull customization.favicon.svg)) + (fileTypeToHomeFile customization.favicon.svg); + + "${assetsDir}/img/favicon.png" = + mkIf (!(isNull customization.favicon.png)) + (fileTypeToHomeFile customization.favicon.png); + } + // (lib.attrsets.mapAttrs' + (n: v: lib.attrsets.nameValuePair "${assetsDir}/${n}" v) + customization.assets) + // ( + if (!(isNull customization.theme)) + then + if isAttrs customization.theme + then + (lib.attrsets.mapAttrs' + (n: v: + lib.attrsets.nameValuePair "${assetsDir}/css/theme-${n}.css" (fileTypeToHomeFile v)) + customization.theme) + else { + "${assetsDir}/css/theme-custom.css" = fileTypeToHomeFile customization.theme; + } + else {} + ); + home.stateVersion = "23.11"; # DO NOT CHANGE + }; + in + home { + inherit lib; + forgejoConfig = forgejo; + customization = cfg; + }; + }; +} diff --git a/modules/forgejo/default.nix b/modules/forgejo/default.nix index c313f0a..76400a7 100644 --- a/modules/forgejo/default.nix +++ b/modules/forgejo/default.nix @@ -2,57 +2,94 @@ config, lib, pkgs, - utils, ... }: let cfg = config.services.forgejo; - yamlFormat = pkgs.formats.yaml {}; - users = builtins.attrValues (builtins.mapAttrs - (username: info: { - name = - if isNull info.name - then username - else info.name; - email = info.email; - password = info.password; - admin = info.admin; - }) - cfg.users); - initList = l: lib.strings.concatStringsSep "," l; + + initList = l: (lib.strings.concatStringsSep "," l); + + customThemes = with builtins; + with lib.attrsets; + if !(isNull cfg.customization.theme) + then + ( + if (isAttrs cfg.customization.theme) + then (mapAttrsToList (n: v: n) cfg.customization.theme) + else ["custom"] + ) + else []; in { - imports = []; + imports = [ + ./users.nix + ./customization.nix + ]; options.services.forgejo = with lib; with lib.types; { - handleUndeclaredUsers = mkOption { - type = bool; - default = false; - }; - users = mkOption { - type = attrsOf (submodule ({ - config, - lib, - ... - }: - with lib; - with lib.types; { - options = { - name = mkOption { - type = nullOr (either str path); - default = null; - }; - password = mkOption { - type = either str path; - }; - email = mkOption { - type = either str path; - }; - admin = mkOption { - type = bool; - default = false; - }; - }; - })); - default = {}; + settings = { + DEFAULT = { + APP_NAME = mkOption { + type = str; + default = "Forgejo: Beyond code. We forge."; + }; + }; + actions = { + ENABLED = mkOption { + type = bool; + default = cfg.actions.enable; + }; + DEFAULT_ACTIONS_URL = mkOption { + type = str; + default = "https://localhost:${toString cfg.settings.server.HTTP_PORT}"; + }; + }; + repository = { + DEFAULT_REPO_UNITS = mkOption { + type = listOf str; + default = ["repo.code"]; + apply = t: initList t; + }; + DISABLED_REPO_UNITS = mkOption { + type = listOf str; + default = []; + apply = t: + initList (t + ++ ( + if !cfg.actions.enable + then ["repo.actions"] + else [] + )); + }; + }; + ui = { + DEFAULT_THEME = mkOption { + type = str; + default = with builtins; + if (!(isNull cfg.customization.theme)) + then elemAt customThemes 0 + else "forgejo-auto"; + }; + THEMES = mkOption { + type = listOf str; + default = [ + "forgejo-auto" + "forgejo-light" + "forgejo-dark" + "gitea-auto" + "gitea-light" + "gitea-dark" + "forgejo-auto-deuteranopania-protanopia" + "forgejo-light-deuteranopania-protanopia" + "forgejo-dark-deuteranopania-protanopia" + "forgejo-auto-tritanopia" + "forgejo-light-tritanopia" + "forgejo-dark-tritanopia" + ]; + apply = t: let + list = t ++ customThemes; + in + initList list; + }; + }; }; actions = { enable = mkOption { @@ -100,27 +137,7 @@ in { services.forgejo = { user = mkDefault "git"; - group = mkDefault cfg.user; - settings = { - DEFAULT = { - APP_NAME = mkDefault "Forgejo: Beyond coding. We forge."; - }; - actions = { - ENABLED = mkDefault cfg.actions.enable; - DEFAULT_ACTIONS_URL = mkDefault "http://localhost:${toString cfg.settings.server.HTTP_PORT}"; - }; - repository = { - DEFAULT_REPO_UNITS = mkDefault (initList [ - "repo.code" - ]); - DISABLED_REPO_UNITS = mkIf (!cfg.actions.enable) (mkDefault (initList [ - "repo.actions" - ])); - }; - service = { - # DISABLE_REGISTRARION = mkDefault true; - }; - }; + group = cfg.user; }; virtualisation.docker.enable = mkIf cfg.actions.enable (mkDefault true); @@ -142,64 +159,5 @@ in { }; }; }; - - systemd.services."forgejo-users-setup" = with builtins; { - script = '' - function gum() { ${pkgs.gum}/bin/gum "$@"; } - function forgejo() { - # local config_file="${toString cfg.stateDir}/custom/conf/app.ini"; - # touch $config_file - ${cfg.package}/bin/gitea \ - --work-path ${cfg.stateDir} \ - "$@" - } - function fjuser() { forgejo admin user "$@"; } - function awk() { ${pkgs.gawk}/bin/awk "$@"; } - - handle_undeclared_users="${ - if cfg.handleUndeclaredUsers - then "true" - else "false" - }"; - - declared_users=(${toString (map (user: "${ - if isPath user.name - then "$(cat ${toString user.name})" - else user.name - }") - users)}); - - ${readFile ./user-handler.sh} - - ${toString (map (user: '' - set-user "${ - if isPath user.name - then "$(cat ${toString user.name})" - else user.name - }" "${ - if isPath user.email - then "$(cat ${toString user.email})" - else user.email - }" "${ - if isPath user.password - then "$(cat ${toString user.password})" - else user.password - }" \ - "${ - if user.admin - then "true" - else "false" - }" - '') - users)} - ''; - wantedBy = ["multi-user.target"]; - after = ["forgejo.service"]; - serviceConfig = { - Type = "oneshot"; - User = cfg.user; - Group = cfg.group; - }; - }; }; } diff --git a/modules/forgejo/users.nix b/modules/forgejo/users.nix new file mode 100644 index 0000000..a3d8221 --- /dev/null +++ b/modules/forgejo/users.nix @@ -0,0 +1,122 @@ +{ + config, + lib, + pkgs, + ... +}: let + cfg = config.services.forgejo.users; + forgejo = config.services.forgejo; +in { + imports = []; + options.services.forgejo.users = with lib; + with lib.types; { + enable = mkOption { + type = bool; + default = true; + }; + handleUndeclaredUsers = mkOption { + type = bool; + default = false; + }; + users = mkOption { + type = attrsOf (submodule ({ + config, + lib, + ... + }: + with lib; + with lib.types; { + options = { + name = mkOption { + type = nullOr (either str path); + default = null; + }; + password = mkOption { + type = either str path; + }; + email = mkOption { + type = either str path; + }; + admin = mkOption { + type = bool; + default = false; + }; + }; + })); + default = {}; + }; + }; + config = with lib; + mkIf cfg.enable { + systemd.services."forgejo-users-setup" = with builtins; let + users = builtins.attrValues (builtins.mapAttrs + (username: info: { + name = + if isNull info.name + then username + else info.name; + email = info.email; + password = info.password; + admin = info.admin; + }) + cfg.users); + in { + script = '' + function gum() { ${pkgs.gum}/bin/gum "$@"; } + function forgejo() { + # local config_file="${toString forgejo.stateDir}/custom/conf/app.ini"; + # touch $config_file + ${forgejo.package}/bin/gitea \ + --work-path ${forgejo.stateDir} \ + "$@" + } + function fjuser() { forgejo admin user "$@"; } + function awk() { ${pkgs.gawk}/bin/awk "$@"; } + + handle_undeclared_users="${ + if cfg.handleUndeclaredUsers + then "true" + else "false" + }"; + + declared_users=(${toString (map (user: "${ + if isPath user.name + then "$(cat ${toString user.name})" + else user.name + }") + users)}); + + ${readFile ./users.sh} + + ${toString (map (user: '' + set-user "${ + if isPath user.name + then "$(cat ${toString user.name})" + else user.name + }" "${ + if isPath user.email + then "$(cat ${toString user.email})" + else user.email + }" "${ + if isPath user.password + then "$(cat ${toString user.password})" + else user.password + }" \ + "${ + if user.admin + then "true" + else "false" + }" + '') + users)} + ''; + wantedBy = ["multi-user.target"]; + after = ["forgejo.service"]; + serviceConfig = { + Type = "oneshot"; + User = forgejo.user; + Group = forgejo.group; + }; + }; + }; +} diff --git a/modules/forgejo/user-handler.sh b/modules/forgejo/users.sh similarity index 100% rename from modules/forgejo/user-handler.sh rename to modules/forgejo/users.sh diff --git a/services/default.nix b/services/default.nix index cadca88..31ac042 100644 --- a/services/default.nix +++ b/services/default.nix @@ -3,7 +3,6 @@ ./adguardhome.nix ./caddy.nix ./containers - ./forgejo.nix ./tailscale.nix ]; } diff --git a/services/forgejo.nix b/services/forgejo.nix deleted file mode 100644 index 9c8f7e2..0000000 --- a/services/forgejo.nix +++ /dev/null @@ -1,44 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: let - secrets = config.spacestation-secrets.lesser; -in { - imports = [ - ../modules/forgejo - ]; - services.forgejo = { - enable = true; - actions = { - enable = true; - token = secrets.services.forgejo.actions-token; - url = "http://192.168.1.10:${toString secrets.services.forgejo.port}"; - labels = secrets.services.forgejo.actions-labels; - }; - users = { - user1 = { - name = /. + config.sops.secrets."forgejo/user1/name".path; - password = /. + config.sops.secrets."forgejo/user1/password".path; - email = /. + config.sops.secrets."forgejo/user1/email".path; - admin = true; - }; - }; - settings = { - server = { - HTTP_PORT = secrets.services.forgejo.port; - DOMAIN = "forgejo.capytal.company"; - ROOT_URL = "https://forgejo.capytal.company"; - }; - admin = { - DISABLE_REGULAR_ORG_CREATION = true; - USER_DISABLED_FEATURES = "deletion manage_ssh_keys manage_gpg_keys"; - EXTERNAL_USER_DISABLED_FEATURES = "deletion manage_ssh_keys manage_gpg_keys"; - }; - service = { - DISABLE_REGISTRATION = true; - }; - }; - }; -}