feat: capytal forgejo configuration

This commit is contained in:
Guz
2024-09-18 09:58:09 -03:00
parent a0745fecec
commit 1412b4d484
25 changed files with 1862 additions and 189 deletions

2
capytal/TRADEMARK Normal file
View File

@@ -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.

5
capytal/default.nix Normal file
View File

@@ -0,0 +1,5 @@
{...}: {
imports = [
./forgejo
];
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 200 KiB

103
capytal/forgejo/default.nix Normal file
View File

@@ -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 = ''
<link rel="stylesheet" type="text/css" href="assets/fonts.css">
${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";
};
};
};
}

View File

@@ -0,0 +1,55 @@
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=red-hat-display:300,300i,400,400i,500,500i,600,600i,700,700i,800,800i,900,900i|roboto:100" rel="stylesheet" />
<script src="/assets/js/htmx.js"></script>
<script src="/assets/js/header.js" defer></script>
<style type="text/css">
:root * {
--fonts-proportional: "Red Hat Display", "Noto Sans", "Liberation Sans", sans-serif, var(--fonts-emoji) !important;
}
@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');
};
</style>
<style type="text/css">
@keyframes enter {
0% {
opacity: 0;
transform: translateY(10px);
}
100% {
opacity: 1;
transform: none;
}
}
.slide-enter-content > * {
--stagger: 0;
--delay: 150ms;
--start: 0ms;
animation: enter 0.5s both 1;
animation-delay: calc(var(--start) + var(--stagger) * var(--delay));
}
.slide-enter-content > *:nth-child(1) { --stagger: 1; }
.slide-enter-content > *:nth-child(2) { --stagger: 2; }
.slide-enter-content > *:nth-child(3) { --stagger: 3; }
.slide-enter-content > *:nth-child(4) { --stagger: 4; }
.slide-enter-content > *:nth-child(5) { --stagger: 5; }
.slide-enter-content > *:nth-child(6) { --stagger: 6; }
.slide-enter-content > *:nth-child(7) { --stagger: 7; }
.slide-enter-content > *:nth-child(8) { --stagger: 8; }
.slide-enter-content > *:nth-child(9) { --stagger: 9; }
.slide-enter-content > *:nth-child(10) { --stagger: 10; }
.slide-enter-content > *:nth-child(11) { --stagger: 11; }
.slide-enter-content > *:nth-child(12) { --stagger: 12; }
.slide-enter-content > *:nth-child(13) { --stagger: 13; }
.slide-enter-content > *:nth-child(14) { --stagger: 14; }
.slide-enter-content > *:nth-child(15) { --stagger: 15; }
.slide-enter-content > *:nth-child(16) { --stagger: 16; }
.slide-enter-content > *:nth-child(17) { --stagger: 17; }
.slide-enter-content > *:nth-child(18) { --stagger: 18; }
.slide-enter-content > *:nth-child(19) { --stagger: 19; }
.slide-enter-content > *:nth-child(20) { --stagger: 20; }
</style>

View File

@@ -0,0 +1,73 @@
<div class="flex-list slide-enter-content">
{{range .Repos}}
<div class="flex-item">
<div class="flex-item-leading">
{{template "repo/icon" .}}
</div>
<div class="flex-item-main">
<div class="flex-item-header">
<div class="flex-item-title">
{{if and (or $.PageIsExplore $.PageIsProfileStarList) .Owner}}
<a class="text primary name" href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a>/
{{end}}
<a class="text primary name" href="{{.Link}}">{{.Name}}</a>
<span class="label-list">
{{if .IsArchived}}
<span class="ui basic label">{{ctx.Locale.Tr "repo.desc.archived"}}</span>
{{end}}
{{if .IsPrivate}}
<span class="ui basic label">{{ctx.Locale.Tr "repo.desc.private"}}</span>
{{else}}
{{if .Owner.Visibility.IsPrivate}}
<span class="ui basic label">{{ctx.Locale.Tr "repo.desc.internal"}}</span>
{{end}}
{{end}}
{{if .IsTemplate}}
<span class="ui basic label">{{ctx.Locale.Tr "repo.desc.template"}}</span>
{{end}}
{{if eq .ObjectFormatName "sha256"}}
<span class="ui basic label">{{ctx.Locale.Tr "repo.desc.sha256"}}</span>
{{end}}
</span>
</div>
<div class="flex-item-trailing muted-links">
{{if .PrimaryLanguage}}
<a class="flex-text-inline" href="?q={{$.Keyword}}&sort={{$.SortType}}&language={{.PrimaryLanguage.Language}}{{if $.TabName}}&tab={{$.TabName}}{{end}}">
<i class="color-icon tw-mr-2" style="background-color: {{.PrimaryLanguage.Color}}"></i>
{{.PrimaryLanguage.Language}}
</a>
{{end}}
{{if not $.DisableStars}}
<a class="flex-text-inline" href="{{.Link}}/stars" aria-label="{{ctx.Locale.TrN .NumStars "explore.stars_one" "explore.stars_few" .NumStars}}" {{if ge .NumStars 1000}}data-tooltip-content="{{.NumStars}}"{{end}}>
{{svg "octicon-star" 16}}
{{CountFmt .NumStars}}
</a>
{{end}}
{{if not $.DisableForks}}
<a class="flex-text-inline" href="{{.Link}}/forks" aria-label="{{ctx.Locale.TrN .NumForks "explore.forks_one" "explore.forks_few" .NumForks}}" {{if ge .NumForks 1000}}data-tooltip-content="{{.NumForks}}"{{end}}>
{{svg "octicon-git-branch" 16}}
{{CountFmt .NumForks}}
</a>
{{end}}
</div>
</div>
{{$description := .DescriptionHTML $.Context}}
{{if $description}}
<div class="flex-item-body">{{$description}}</div>
{{end}}
{{if .Topics}}
<div class="label-list">
{{range .Topics}}
{{if ne . ""}}<a class="ui label" href="{{AppSubUrl}}/explore/repos?q={{.}}&topic=1">{{.}}</a>{{end}}
{{end}}
</div>
{{end}}
<div class="flex-item-body">{{ctx.Locale.Tr "org.repo_updated" (TimeSinceUnix .UpdatedUnix ctx.Locale)}}</div>
</div>
</div>
{{else}}
<div>
{{ctx.Locale.Tr "search.no_results"}}
</div>
{{end}}
</div>

View File

@@ -0,0 +1,33 @@
<div class="flex-list slide-enter-content">
{{range .Users}}
<div class="flex-item tw-items-center">
<div class="flex-item-leading">
{{ctx.AvatarUtils.Avatar . 48}}
</div>
<div class="flex-item-main">
<div class="flex-item-title">
{{template "shared/user/name" .}}
{{if .Visibility.IsPrivate}}
<span class="ui basic tiny label">{{ctx.Locale.Tr "repo.desc.private"}}</span>
{{end}}
</div>
<div class="flex-item-body">
{{if .Location}}
<span class="flex-text-inline">{{svg "octicon-location"}}{{.Location}}</span>
{{end}}
{{if and .Email (or (and $.ShowUserEmail $.IsSigned (not .KeepEmailPrivate)) $.PageIsAdminUsers)}}
<span class="flex-text-inline">
{{svg "octicon-mail"}}
<a href="mailto:{{.Email}}">{{.Email}}</a>
</span>
{{end}}
<span class="flex-text-inline">{{svg "octicon-calendar"}}{{ctx.Locale.Tr "user.joined_on" (DateTime "short" .CreatedUnix)}}</span>
</div>
</div>
</div>
{{else}}
<div class="flex-item">
{{ctx.Locale.Tr "search.no_results"}}
</div>
{{end}}
</div>

View File

@@ -0,0 +1,51 @@
{{template "base/head" .}}
<div role="main" aria-label="{{if .IsSigned}}{{ctx.Locale.Tr "dashboard"}}{{else}}{{ctx.Locale.Tr "home"}}{{end}}" class="page-content home">
<div class="tw-mb-8 tw-px-8">
<div class="center">
<img class="logo" style="max-width:70%;max-height:100%;" src="{{AssetUrlPrefix}}/img/home-logo.svg" alt="{{ctx.Locale.Tr "logo"}}">
<div class="hero">
<!-- <h1 class="ui icon header title cal-sans">
{{AppDisplayName}}
</h1> -->
<h2 style="font-family:'Cal Sans';">From dreams, to code.</h2>
</div>
</div>
</div>
<!-- <div class="ui stackable middle very relaxed page grid">
<div class="eight wide center column">
<h1 class="hero ui icon header">
{{svg "octicon-flame"}} {{ctx.Locale.Tr "startpage.install"}}
</h1>
<p class="large">
{{ctx.Locale.Tr "startpage.install_desc" "https://forgejo.org/download/#installation-from-binary" "https://forgejo.org/download/#container-image" "https://forgejo.org/download"}}
</p>
</div>
<div class="eight wide center column">
<h1 class="hero ui icon header">
{{svg "octicon-device-desktop"}} {{ctx.Locale.Tr "startpage.platform"}}
</h1>
<p class="large">
{{ctx.Locale.Tr "startpage.platform_desc" "https://go.dev/"}}
</p>
</div>
</div>
<div class="ui stackable middle very relaxed page grid">
<div class="eight wide center column">
<h1 class="hero ui icon header">
{{svg "octicon-rocket"}} {{ctx.Locale.Tr "startpage.lightweight"}}
</h1>
<p class="large">
{{ctx.Locale.Tr "startpage.lightweight_desc"}}
</p>
</div>
<div class="eight wide center column">
<h1 class="hero ui icon header">
{{svg "octicon-code"}} {{ctx.Locale.Tr "startpage.license"}}
</h1>
<p class="large">
{{ctx.Locale.Tr "startpage.license_desc" "https://forgejo.org/download" "https://codeberg.org/forgejo/forgejo"}}
</p>
</div>
</div> -->
</div>
{{template "base/footer" .}}

View File

@@ -0,0 +1,37 @@
<div class="ui container tw-flex">
{{ctx.AvatarUtils.Avatar .Org 100 "org-avatar"}}
<div id="org-info" class="tw-flex tw-flex-col">
<div class="ui header">
<div style="font-family:'Cal Sans'" class="org-title">
{{.Org.DisplayName}}
<span class="org-visibility">
{{if .Org.Visibility.IsLimited}}<span class="ui large basic horizontal label">{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}</span>{{end}}
{{if .Org.Visibility.IsPrivate}}<span class="ui large basic horizontal label">{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}</span>{{end}}
</span>
</div>
<span class="tw-flex tw-items-center button-row tw-ml-auto tw-text-16 tw-whitespace-nowrap">
{{if .EnableFeed}}
<a class="ui basic label button tw-mr-0" href="{{.Org.HomeLink}}.rss" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">
{{svg "octicon-rss" 24}}
</a>
{{end}}
{{if .IsSigned}}
{{template "org/follow_unfollow" .}}
{{end}}
{{if .IsOrganizationMember}}
<a class="ui basic button tw-mr-0" href="{{.OrgLink}}/dashboard">{{ctx.Locale.Tr "org.open_dashboard"}}</a>
{{end}}
</span>
</div>
{{if .RenderedDescription}}<div class="render-content markup">{{.RenderedDescription}}</div>{{end}}
<div class="text light meta tw-mt-1">
{{if .Org.Location}}<div class="flex-text-block">{{svg "octicon-location"}} <span>{{.Org.Location}}</span></div>{{end}}
{{if .Org.Website}}<div class="flex-text-block">{{svg "octicon-link"}} <a class="muted" target="_blank" rel="noopener noreferrer me" href="{{.Org.Website}}">{{.Org.Website}}</a></div>{{end}}
{{if .IsSigned}}
{{if .Org.Email}}<div class="flex-text-block">{{svg "octicon-mail"}} <a class="muted" href="mailto:{{.Org.Email}}">{{.Org.Email}}</a></div>{{end}}
{{end}}
</div>
</div>
</div>
{{template "org/menu" .}}

View File

@@ -0,0 +1,164 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content repository file list {{if .IsBlame}}blame{{end}}">
{{template "repo/header" .}}
<div class="ui container slide-enter-content {{if .IsBlame}}fluid padded{{end}}">
{{template "base/alert" .}}
{{template "repo/code/recently_pushed_new_branches" .}}
{{if and (not .HideRepoInfo) (not .IsBlame)}}
<div class="repo-description">
<div id="repo-desc" class="tw-break-anywhere tw-text-16">
{{$description := .Repository.DescriptionHTML $.Context}}
{{if $description}}<span class="description">{{$description | RenderCodeBlock}}</span>{{else}}<span class="no-description text-italic">{{ctx.Locale.Tr "repo.no_desc"}}</span>{{end}}
{{if .Repository.Website}}<a class="link" href="{{.Repository.Website}}">{{.Repository.Website}}</a>{{end}}
</div>
<form class="ignore-dirty" action="{{.RepoLink}}/search/{{if .CodeIndexerDisabled}}{{.BranchNameSubURL}}{{end}}" method="get" data-test-tag="codesearch">
<div class="ui small action input">
<input name="q" value="{{.Keyword}}" placeholder="{{ctx.Locale.Tr "search.code_kind"}}">
{{template "shared/search/button"}}
</div>
</form>
</div>
<div class="tw-flex tw-items-center tw-flex-wrap tw-gap-2 tw-my-2" id="repo-topics">
{{/* it should match the code in issue-home.js */}}
{{range .Topics}}<a class="repo-topic ui large label" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=1">{{.Name}}</a>{{end}}
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<button id="manage_topic" class="btn interact-fg tw-text-12">{{ctx.Locale.Tr "repo.topic.manage_topics"}}</button>{{end}}
</div>
{{end}}
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}
<div class="ui form tw-hidden tw-flex tw-gap-2 tw-my-2" id="topic_edit">
<div class="ui fluid multiple search selection dropdown tw-flex-wrap tw-flex-1">
<input type="hidden" name="topics" value="{{range $i, $v := .Topics}}{{.Name}}{{if Eval $i "+" 1 "<" (len $.Topics)}},{{end}}{{end}}">
{{range .Topics}}
{{/* keep the same layout as Fomantic UI generated labels */}}
<a class="ui label transition visible tw-cursor-default tw-inline-block" data-value="{{.Name}}">{{.Name}}{{svg "octicon-x" 16 "delete icon"}}</a>
{{end}}
<div class="text"></div>
</div>
<div>
<button class="ui basic button" id="cancel_topic_edit">{{ctx.Locale.Tr "cancel"}}</button>
<button class="ui primary button" id="save_topic" data-link="{{.RepoLink}}/topics">{{ctx.Locale.Tr "save"}}</button>
</div>
</div>
{{end}}
{{if RepoFlagsEnabled}}
{{template "custom/repo_flag_banners" .}}
{{if .SignedUser.IsAdmin}}
{{template "repo/admin_flags" .}}
{{end}}
{{end}}
{{if .Repository.IsArchived}}
<div class="ui warning message tw-text-center">
{{if .Repository.ArchivedUnix.IsZero}}
{{ctx.Locale.Tr "repo.archive.title"}}
{{else}}
{{ctx.Locale.Tr "repo.archive.title_date" (DateTime "long" .Repository.ArchivedUnix)}}
{{end}}
</div>
{{end}}
{{template "repo/sub_menu" .}}
{{$n := len .TreeNames}}
{{$l := Eval $n "-" 1}}
{{$isHomepage := (eq $n 0)}}
<div class="repo-button-row">
<div class="tw-flex tw-items-center tw-gap-y-2">
{{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}}
<a id="new-pull-request" role="button" class="ui compact basic button" href="{{$compareLink}}"
data-tooltip-content="{{if .PullRequestCtx.Allowed}}{{ctx.Locale.Tr "repo.pulls.compare_changes"}}{{else}}{{ctx.Locale.Tr "action.compare_branch"}}{{end}}">
{{svg "octicon-git-pull-request"}}
</a>
{{end}}
<!-- Show go to file and breadcrumbs if not on home page -->
{{if $isHomepage}}
<a href="{{.Repository.Link}}/find/{{.BranchNameSubURL}}" class="ui compact basic button">{{ctx.Locale.Tr "repo.find_file.go_to_file"}}</a>
{{end}}
{{if and .CanWriteCode .IsViewBranch (not .Repository.IsMirror) (not .Repository.IsArchived) (not .IsViewFile)}}
<button class="ui dropdown basic compact jump button tw-mr-1"{{if not .Repository.CanEnableEditor}} disabled{{end}}>
{{ctx.Locale.Tr "repo.editor.add_file"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu">
<a class="item" href="{{.RepoLink}}/_new/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">
{{ctx.Locale.Tr "repo.editor.new_file"}}
</a>
{{if .RepositoryUploadEnabled}}
<a class="item" href="{{.RepoLink}}/_upload/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">
{{ctx.Locale.Tr "repo.editor.upload_file"}}
</a>
{{end}}
<a class="item" href="{{.RepoLink}}/_diffpatch/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">
{{ctx.Locale.Tr "repo.editor.patch"}}
</a>
</div>
</button>
{{end}}
{{if and $isHomepage (.Repository.IsTemplate)}}
<a role="button" class="ui primary compact button" href="{{AppSubUrl}}/repo/create?template_id={{.Repository.ID}}">
{{ctx.Locale.Tr "repo.use_template"}}
</a>
{{end}}
{{if (not $isHomepage)}}
<span class="breadcrumb repo-path tw-ml-1">
<a class="section" href="{{.RepoLink}}/src/{{.BranchNameSubURL}}" title="{{.Repository.Name}}">{{StringUtils.EllipsisString .Repository.Name 30}}</a>
{{- range $i, $v := .TreeNames -}}
<span class="breadcrumb-divider">/</span>
{{- if eq $i $l -}}
<span class="active section" title="{{$v}}">{{$v}}</span>
{{- else -}}
{{$p := index $.Paths $i}}<span class="section"><a href="{{$.BranchLink}}/{{PathEscapeSegments $p}}" title="{{$v}}">{{$v}}</a></span>
{{- end -}}
{{- end -}}
</span>
{{end}}
</div>
<div class="tw-flex tw-items-center">
<!-- Only show clone panel in repository home page -->
{{if $isHomepage}}
<div class="clone-panel ui action tiny input">
{{template "repo/clone_buttons" .}}
<button class="ui small jump dropdown icon button" data-tooltip-content="{{ctx.Locale.Tr "repo.more_operations"}}">
{{svg "octicon-kebab-horizontal"}}
<div class="menu">
{{if not $.DisableDownloadSourceArchives}}
<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.zip" rel="nofollow">{{svg "octicon-file-zip" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.download_zip"}}</a>
<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.download_tar"}}</a>
<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.bundle" rel="nofollow">{{svg "octicon-package" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.download_bundle"}}</a>
{{end}}
{{if .CitationExist}}
<a class="item" id="cite-repo-button">{{svg "octicon-cross-reference" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.cite_this_repo"}}</a>
{{end}}
{{range .OpenWithEditorApps}}
<a class="item js-clone-url-editor" data-href-template="{{.OpenURL}}">{{.IconHTML}}{{ctx.Locale.Tr "repo.open_with_editor" .DisplayName}}</a>
{{end}}
</div>
</button>
{{template "repo/clone_script" .}}{{/* the script will update `.js-clone-url` and related elements */}}
</div>
{{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 */}}
<a class="ui button" href="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/{{.TreePath | PathEscapeSegments}}">
{{svg "octicon-history" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.file_history"}}
</a>
{{end}}
</div>
</div>
{{if .IsViewFile}}
{{template "repo/view_file" .}}
{{else if .IsBlame}}
{{template "repo/blame" .}}
{{else}}{{/* IsViewDirectory */}}
{{template "repo/view_list" .}}
{{end}}
</div>
</div>
{{template "base/footer" .}}

View File

@@ -0,0 +1,55 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content repository issue-list">
{{template "repo/header" .}}
<div class="ui container slide-enter-content">
{{template "base/alert" .}}
{{if .PinnedIssues}}
<div id="issue-pins" {{if .IsRepoAdmin}}data-is-repo-admin{{end}}>
{{range .PinnedIssues}}
<div class="issue-card tw-break-anywhere {{if $.IsRepoAdmin}}tw-cursor-grab{{end}}" data-move-url="{{$.Link}}/move_pin" data-issue-id="{{.ID}}">
{{template "repo/issue/card" (dict "Issue" . "Page" $ "isPinnedIssueCard" true)}}
</div>
{{end}}
</div>
{{end}}
<div class="list-header list-header-issues">
{{template "repo/issue/navbar" .}}
{{template "repo/issue/search" .}}
{{if not .Repository.IsArchived}}
{{if .PageIsIssueList}}
<a class="ui small primary button issue-list-new" href="{{.RepoLink}}/issues/new{{if .NewIssueChooseTemplate}}/choose{{end}}">{{ctx.Locale.Tr "repo.issues.new"}}</a>
{{else}}
<a class="ui small primary button new-pr-button issue-list-new{{if not .PullRequestCtx.Allowed}} disabled{{end}}" href="{{if .PullRequestCtx.Allowed}}{{.Repository.Link}}/compare/{{.Repository.DefaultBranch | PathEscapeSegments}}...{{if ne .Repository.Owner.Name .PullRequestCtx.BaseRepo.Owner.Name}}{{PathEscape .Repository.Owner.Name}}:{{end}}{{.Repository.DefaultBranch | PathEscapeSegments}}{{end}}">{{ctx.Locale.Tr "repo.pulls.new"}}</a>
{{end}}
{{else}}
{{if not .PageIsIssueList}}
<a class="ui small primary small button issue-list-new{{if not .PullRequestCtx.Allowed}} disabled{{end}}" href="{{if .PullRequestCtx.Allowed}}{{.PullRequestCtx.BaseRepo.Link}}/compare/{{.PullRequestCtx.BaseRepo.DefaultBranch | PathEscapeSegments}}...{{if ne .Repository.Owner.Name .PullRequestCtx.BaseRepo.Owner.Name}}{{PathEscape .Repository.Owner.Name}}:{{end}}{{.Repository.DefaultBranch | PathEscapeSegments}}{{end}}">{{ctx.Locale.Tr "action.compare_commits_general"}}</a>
{{end}}
{{end}}
</div>
{{template "repo/issue/filters" .}}
<div id="issue-actions" class="issue-list-toolbar tw-hidden">
<div class="issue-list-toolbar-left">
{{template "repo/issue/openclose" .}}
<!-- Total Tracked Time -->
{{if .TotalTrackedTime}}
<div class="ui compact tiny secondary menu">
<span class="item" data-tooltip-content='{{ctx.Locale.Tr "tracked_time_summary"}}'>
{{svg "octicon-clock"}}
{{.TotalTrackedTime | Sec2Time}}
</span>
</div>
{{end}}
</div>
<div class="issue-list-toolbar-right">
{{template "repo/issue/filter_actions" .}}
</div>
</div>
{{template "shared/issuelist" dict "." . "listType" "repo"}}
</div>
</div>
{{template "base/footer" .}}

View File

@@ -0,0 +1,163 @@
<div id="issue-list" class="flex-list slide-enter-content">
{{$approvalCounts := .ApprovalCounts}}
{{range .Issues}}
<div class="flex-item">
<div class="flex-item-icon">
{{if $.CanWriteIssuesOrPulls}}
<input type="checkbox" autocomplete="off" class="issue-checkbox tw-mr-4" data-issue-id={{.ID}} aria-label="{{ctx.Locale.Tr "repo.issues.action_check"}} &quot;{{.Title}}&quot;">
{{end}}
{{template "shared/issueicon" .}}
</div>
<div class="flex-item-main">
<div class="flex-item-header">
<div class="flex-item-title">
<a class="tw-no-underline issue-title" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}">{{RenderEmoji $.Context .Title | RenderCodeBlock}}</a>
{{if .IsPull}}
{{if (index $.CommitStatuses .PullRequest.ID)}}
{{template "repo/commit_statuses" dict "Status" (index $.CommitLastStatus .PullRequest.ID) "Statuses" (index $.CommitStatuses .PullRequest.ID)}}
{{end}}
{{end}}
<span class="labels-list tw-ml-1">
{{range .Labels}}
<a href="?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}{{if ne $.listType "milestone"}}&milestone={{$.MilestoneID}}{{end}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}" rel="nofollow">{{RenderLabel $.Context ctx.Locale .}}</a>
{{end}}
</span>
</div>
{{if or .TotalTrackedTime .Assignees .NumComments}}
<div class="flex-item-trailing">
{{if .TotalTrackedTime}}
<div class="text grey flex-text-block">
{{svg "octicon-clock" 16}}
{{.TotalTrackedTime | Sec2Time}}
</div>
{{end}}
{{if .Assignees}}
<div class="text grey">
{{range .Assignees}}
<a class="ui assignee tw-no-underline" href="{{.HomeLink}}" data-tooltip-content="{{.GetDisplayName}}">
{{ctx.AvatarUtils.Avatar . 20}}
</a>
{{end}}
</div>
{{end}}
{{if .NumComments}}
<div class="text grey">
<a class="tw-no-underline muted flex-text-block" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}">
{{svg "octicon-comment" 16}}{{.NumComments}}
</a>
</div>
{{end}}
</div>
{{end}}
</div>
<div class="flex-item-body">
<a class="index" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}">
{{if eq $.listType "dashboard"}}
{{.Repo.FullName}}#{{.Index}}
{{else}}
#{{.Index}}
{{end}}
</a>
{{$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}}
<div class="branches flex-text-inline">
<div class="branch">
<a href="{{.PullRequest.BaseRepo.Link}}/src/branch/{{PathEscapeSegments .PullRequest.BaseBranch}}">
{{/* inline to remove the spaces between spans */}}
{{if ne .RepoID .PullRequest.BaseRepoID}}<span class="truncated-name">{{.PullRequest.BaseRepo.OwnerName}}</span>:{{end}}<span class="truncated-name">{{.PullRequest.BaseBranch}}</span>
</a>
</div>
{{svg "gitea-double-chevron-left" 12}}
{{if .PullRequest.HeadRepo}}
<div class="branch">
<a href="{{.PullRequest.HeadRepo.Link}}/src/branch/{{PathEscapeSegments .PullRequest.HeadBranch}}">
{{/* inline to remove the spaces between spans */}}
{{if ne .RepoID .PullRequest.HeadRepoID}}<span class="truncated-name">{{.PullRequest.HeadRepo.OwnerName}}</span>:{{end}}<span class="truncated-name">{{.PullRequest.HeadBranch}}</span>
</a>
</div>
{{end}}
</div>
{{end}}
{{if and .Milestone (ne $.listType "milestone")}}
<a class="milestone flex-text-inline tw-max-w-[300px]" {{if $.RepoLink}}href="{{$.RepoLink}}/milestone/{{.Milestone.ID}}"{{else}}href="{{.Repo.Link}}/milestone/{{.Milestone.ID}}"{{end}}>
{{svg "octicon-milestone" 14}}
<span class="gt-ellipsis">{{.Milestone.Name}}</span>
</a>
{{end}}
{{if .Project}}
<a class="project flex-text-inline tw-max-w-[300px]" href="{{.Project.Link ctx}}">
{{svg .Project.IconName 14}}
<span class="gt-ellipsis">{{.Project.Title}}</span>
</a>
{{end}}
{{if .Ref}}
<a class="ref flex-text-inline tw-max-w-[300px]" {{if $.RepoLink}}href="{{index $.IssueRefURLs .ID}}"{{else}}href="{{.Repo.Link}}{{index $.IssueRefURLs .ID}}"{{end}}>
{{svg "octicon-git-branch" 14}}
<span class="gt-ellipsis">{{index $.IssueRefEndNames .ID}}</span>
</a>
{{end}}
{{$tasks := .GetTasks}}
{{if gt $tasks 0}}
{{$tasksDone := .GetTasksDone}}
<span class="checklist flex-text-inline">
{{svg "octicon-checklist" 14}}{{$tasksDone}} / {{$tasks}}
<progress value="{{$tasksDone}}" max="{{$tasks}}"></progress>
</span>
{{end}}
{{if ne .DeadlineUnix 0}}
<span class="due-date flex-text-inline" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.due_date"}}">
<span{{if .IsOverdue}} class="text red"{{end}}>
{{svg "octicon-calendar" 14}}
{{DateTime "short" (.DeadlineUnix.FormatDate)}}
</span>
</span>
{{end}}
{{if .IsPull}}
{{$approveOfficial := call $approvalCounts .ID "approve"}}
{{$rejectOfficial := call $approvalCounts .ID "reject"}}
{{$waitingOfficial := call $approvalCounts .ID "waiting"}}
{{if gt $approveOfficial 0}}
<span class="approvals green flex-text-inline">
{{svg "octicon-check" 14}}
{{ctx.Locale.TrN $approveOfficial "repo.pulls.approve_count_1" "repo.pulls.approve_count_n" $approveOfficial}}
</span>
{{end}}
{{if gt $rejectOfficial 0}}
<span class="rejects red flex-text-inline">
{{svg "octicon-diff" 14}}
{{ctx.Locale.TrN $rejectOfficial "repo.pulls.reject_count_1" "repo.pulls.reject_count_n" $rejectOfficial}}
</span>
{{end}}
{{if gt $waitingOfficial 0}}
<span class="waiting flex-text-inline">
{{svg "octicon-eye" 14}}
{{ctx.Locale.TrN $waitingOfficial "repo.pulls.waiting_count_1" "repo.pulls.waiting_count_n" $waitingOfficial}}
</span>
{{end}}
{{if and (not .PullRequest.HasMerged) .PullRequest.ConflictedFiles}}
<span class="conflicting flex-text-inline">
{{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)}}
</span>
{{end}}
{{end}}
</div>
</div>
</div>
{{end}}
{{if .IssueIndexerUnavailable}}
<div class="ui error message">
<p>{{ctx.Locale.Tr "search.keyword_search_unavailable"}}</p>
</div>
{{end}}
</div>
{{template "base/paginate" .}}

View File

@@ -0,0 +1,127 @@
<div id="activity-feed" class="flex-list slide-enter-content">
{{range .Feeds}}
<div class="flex-item">
<div class="flex-item-leading">
{{ctx.AvatarUtils.AvatarByAction .}}
</div>
<div class="flex-item-main tw-gap-2">
<div>
{{if gt .ActUser.ID 0}}
<a href="{{AppSubUrl}}/{{(.GetActUserName ctx) | PathEscape}}" title="{{.GetActDisplayNameTitle ctx}}">{{.GetActDisplayName ctx}}</a>
{{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}}
</div>
{{if .GetOpType.InActions "commit_repo" "mirror_sync_push"}}
{{$push := ActionContent2Commits .}}
{{$repoLink := (.GetRepoLink ctx)}}
{{$repo := .Repo}}
<div class="tw-flex tw-flex-col tw-gap-1">
{{range $push.Commits}}
{{$commitLink := printf "%s/commit/%s" $repoLink .Sha1}}
<div class="flex-text-block">
<img class="ui avatar" src="{{$push.AvatarLink $.Context .AuthorEmail}}" title="{{.AuthorName}}" width="16" height="16">
<a class="ui sha label" href="{{$commitLink}}">{{ShortSha .Sha1}}</a>
<span class="text truncate">
{{RenderCommitMessage $.Context .Message ($repo.ComposeMetas ctx)}}
</span>
</div>
{{end}}
</div>
{{if and (gt $push.Len 1) $push.CompareURL}}
<a href="{{AppSubUrl}}/{{$push.CompareURL}}">{{ctx.Locale.Tr "action.compare_commits" $push.Len}} »</a>
{{end}}
{{else if .GetOpType.InActions "create_issue"}}
<span class="text truncate issue title">{{index .GetIssueInfos 1 | RenderEmoji $.Context | RenderCodeBlock}}</span>
{{else if .GetOpType.InActions "create_pull_request"}}
<span class="text truncate issue title">{{index .GetIssueInfos 1 | RenderEmoji $.Context | RenderCodeBlock}}</span>
{{else if .GetOpType.InActions "comment_issue" "approve_pull_request" "reject_pull_request" "comment_pull"}}
<a href="{{.GetCommentLink ctx}}" class="text truncate issue title">{{(.GetIssueTitle ctx) | RenderEmoji $.Context | RenderCodeBlock}}</a>
{{$comment := index .GetIssueInfos 1}}
{{if $comment}}
<div class="markup tw-text-14">{{RenderMarkdownToHtml ctx $comment}}</div>
{{end}}
{{else if .GetOpType.InActions "merge_pull_request"}}
<div class="flex-item-body text black">{{index .GetIssueInfos 1}}</div>
{{else if .GetOpType.InActions "close_issue" "reopen_issue" "close_pull_request" "reopen_pull_request"}}
<span class="text truncate issue title">{{(.GetIssueTitle ctx) | RenderEmoji $.Context | RenderCodeBlock}}</span>
{{else if .GetOpType.InActions "pull_review_dismissed"}}
<div class="flex-item-body text black">{{ctx.Locale.Tr "action.review_dismissed_reason"}}</div>
<div class="flex-item-body text black">{{index .GetIssueInfos 2 | RenderEmoji $.Context}}</div>
{{end}}
</div>
<div class="flex-item-trailing">
{{svg (printf "octicon-%s" (ActionIcon .GetOpType)) 32 "text grey tw-mr-1"}}
</div>
</div>
{{end}}
{{template "base/paginate" .}}
</div>

View File

@@ -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;

80
flake.lock generated
View File

@@ -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": {

View File

@@ -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,

View File

@@ -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;
};
};
}

View File

@@ -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 {
settings = {
DEFAULT = {
APP_NAME = mkOption {
type = str;
default = "Forgejo: Beyond code. We forge.";
};
};
actions = {
ENABLED = mkOption {
type = bool;
default = false;
default = cfg.actions.enable;
};
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_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;
};
};
}));
default = {};
};
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;
};
};
};
}

122
modules/forgejo/users.nix Normal file
View File

@@ -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;
};
};
};
}

View File

@@ -3,7 +3,6 @@
./adguardhome.nix
./caddy.nix
./containers
./forgejo.nix
./tailscale.nix
];
}

View File

@@ -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;
};
};
};
}