diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index c899ccf3358..e67e6ae32b9 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -297,6 +297,7 @@
./services/misc/fstrim.nix
./services/misc/gammu-smsd.nix
./services/misc/geoip-updater.nix
+ ./services/misc/gitea.nix
#./services/misc/gitit.nix
./services/misc/gitlab.nix
./services/misc/gitolite.nix
diff --git a/nixos/modules/services/misc/gitea.nix b/nixos/modules/services/misc/gitea.nix
new file mode 100644
index 00000000000..f0b44b7bede
--- /dev/null
+++ b/nixos/modules/services/misc/gitea.nix
@@ -0,0 +1,270 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.services.gitea;
+ configFile = pkgs.writeText "app.ini" ''
+ APP_NAME = ${cfg.appName}
+ RUN_USER = ${cfg.user}
+ RUN_MODE = prod
+
+ [database]
+ DB_TYPE = ${cfg.database.type}
+ HOST = ${cfg.database.host}:${toString cfg.database.port}
+ NAME = ${cfg.database.name}
+ USER = ${cfg.database.user}
+ PASSWD = #dbpass#
+ PATH = ${cfg.database.path}
+
+ [repository]
+ ROOT = ${cfg.repositoryRoot}
+
+ [server]
+ DOMAIN = ${cfg.domain}
+ HTTP_ADDR = ${cfg.httpAddress}
+ HTTP_PORT = ${toString cfg.httpPort}
+ ROOT_URL = ${cfg.rootUrl}
+ STATIC_ROOT_PATH = ${cfg.staticRootPath}
+
+ [session]
+ COOKIE_NAME = session
+ COOKIE_SECURE = ${boolToString cfg.cookieSecure}
+
+ [security]
+ SECRET_KEY = #secretkey#
+ INSTALL_LOCK = true
+
+ ${cfg.extraConfig}
+ '';
+in
+
+{
+ options = {
+ services.gitea = {
+ enable = mkOption {
+ default = false;
+ type = types.bool;
+ description = "Enable Gitea Service.";
+ };
+
+ useWizard = mkOption {
+ default = false;
+ type = types.bool;
+ description = "Do not generate a configuration and use gitea' installation wizard instead. The first registered user will be administrator.";
+ };
+
+ stateDir = mkOption {
+ default = "/var/lib/gitea";
+ type = types.str;
+ description = "gitea data directory.";
+ };
+
+ user = mkOption {
+ type = types.str;
+ default = "gitea";
+ description = "User account under which gitea runs.";
+ };
+
+ database = {
+ type = mkOption {
+ type = types.enum [ "sqlite3" "mysql" "postgres" ];
+ example = "mysql";
+ default = "sqlite3";
+ description = "Database engine to use.";
+ };
+
+ host = mkOption {
+ type = types.str;
+ default = "127.0.0.1";
+ description = "Database host address.";
+ };
+
+ port = mkOption {
+ type = types.int;
+ default = 3306;
+ description = "Database host port.";
+ };
+
+ name = mkOption {
+ type = types.str;
+ default = "gitea";
+ description = "Database name.";
+ };
+
+ user = mkOption {
+ type = types.str;
+ default = "gitea";
+ description = "Database user.";
+ };
+
+ password = mkOption {
+ type = types.str;
+ default = "";
+ description = ''
+ The password corresponding to .
+ Warning: this is stored in cleartext in the Nix store!
+ Use instead.
+ '';
+ };
+
+ passwordFile = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ example = "/run/keys/gitea-dbpassword";
+ description = ''
+ A file containing the password corresponding to
+ .
+ '';
+ };
+
+ path = mkOption {
+ type = types.str;
+ default = "${cfg.stateDir}/data/gitea.db";
+ description = "Path to the sqlite3 database file.";
+ };
+ };
+
+ appName = mkOption {
+ type = types.str;
+ default = "gitea: Gitea Service";
+ description = "Application name.";
+ };
+
+ repositoryRoot = mkOption {
+ type = types.str;
+ default = "${cfg.stateDir}/repositories";
+ description = "Path to the git repositories.";
+ };
+
+ domain = mkOption {
+ type = types.str;
+ default = "localhost";
+ description = "Domain name of your server.";
+ };
+
+ rootUrl = mkOption {
+ type = types.str;
+ default = "http://localhost:3000/";
+ description = "Full public URL of gitea server.";
+ };
+
+ httpAddress = mkOption {
+ type = types.str;
+ default = "0.0.0.0";
+ description = "HTTP listen address.";
+ };
+
+ httpPort = mkOption {
+ type = types.int;
+ default = 3000;
+ description = "HTTP listen port.";
+ };
+
+ cookieSecure = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Marks session cookies as "secure" as a hint for browsers to only send
+ them via HTTPS. This option is recommend, if gitea is being served over HTTPS.
+ '';
+ };
+
+ staticRootPath = mkOption {
+ type = types.str;
+ default = "${pkgs.gitea.data}";
+ example = "/var/lib/gitea/data";
+ description = "Upper level of template and static files path.";
+ };
+
+ extraConfig = mkOption {
+ type = types.str;
+ default = "";
+ description = "Configuration lines appended to the generated gitea configuration file.";
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+
+ systemd.services.gitea = {
+ description = "gitea";
+ after = [ "network.target" ];
+ wantedBy = [ "multi-user.target" ];
+ path = [ pkgs.gitea.bin ];
+
+ preStart = let
+ runConfig = "${cfg.stateDir}/custom/conf/app.ini";
+ secretKey = "${cfg.stateDir}/custom/conf/secret_key";
+ in ''
+ mkdir -p ${cfg.stateDir}
+
+ # copy custom configuration and generate a random secret key if needed
+ ${optionalString (cfg.useWizard == false) ''
+ mkdir -p ${cfg.stateDir}/custom/conf
+ cp -f ${configFile} ${runConfig}
+
+ if [ ! -e ${secretKey} ]; then
+ head -c 16 /dev/urandom | base64 > ${secretKey}
+ fi
+
+ KEY=$(head -n1 ${secretKey})
+ DBPASS=$(head -n1 ${cfg.database.passwordFile})
+ sed -e "s,#secretkey#,$KEY,g" \
+ -e "s,#dbpass#,$DBPASS,g" \
+ -i ${runConfig}
+ chmod 640 ${runConfig} ${secretKey}
+ ''}
+
+ mkdir -p ${cfg.repositoryRoot}
+ # update all hooks' binary paths
+ HOOKS=$(find ${cfg.repositoryRoot} -mindepth 4 -maxdepth 4 -type f -wholename "*git/hooks/*")
+ if [ "$HOOKS" ]
+ then
+ sed -ri 's,/nix/store/[a-z0-9.-]+/bin/gitea,${pkgs.gitea.bin}/bin/gitea,g' $HOOKS
+ sed -ri 's,/nix/store/[a-z0-9.-]+/bin/env,${pkgs.coreutils}/bin/env,g' $HOOKS
+ sed -ri 's,/nix/store/[a-z0-9.-]+/bin/bash,${pkgs.bash}/bin/bash,g' $HOOKS
+ sed -ri 's,/nix/store/[a-z0-9.-]+/bin/perl,${pkgs.perl}/bin/perl,g' $HOOKS
+ fi
+ if [ ! -d ${cfg.stateDir}/conf/locale ]
+ then
+ mkdir -p ${cfg.stateDir}/conf
+ cp -r ${pkgs.gitea.out}/locale ${cfg.stateDir}/conf/locale
+ fi
+ '';
+
+ serviceConfig = {
+ Type = "simple";
+ User = cfg.user;
+ WorkingDirectory = cfg.stateDir;
+ ExecStart = "${pkgs.gitea.bin}/bin/gitea web";
+ Restart = "always";
+ };
+
+ environment = {
+ USER = cfg.user;
+ HOME = cfg.stateDir;
+ GITEA_WORK_DIR = cfg.stateDir;
+ };
+ };
+
+ users = mkIf (cfg.user == "gitea") {
+ extraUsers.gitea = {
+ description = "Gitea Service";
+ home = cfg.stateDir;
+ createHome = true;
+ };
+ };
+
+ warnings = optional (cfg.database.password != "")
+ ''config.services.gitea.database.password will be stored as plaintext
+ in the Nix store. Use database.passwordFile instead.'';
+
+ # Create database passwordFile default when password is configured.
+ services.gitea.database.passwordFile =
+ (mkDefault (toString (pkgs.writeTextFile {
+ name = "gitea-database-password";
+ text = cfg.database.password;
+ })));
+ };
+}
diff --git a/pkgs/applications/version-management/gitea/default.nix b/pkgs/applications/version-management/gitea/default.nix
new file mode 100644
index 00000000000..734b9d3629c
--- /dev/null
+++ b/pkgs/applications/version-management/gitea/default.nix
@@ -0,0 +1,50 @@
+{ stdenv, buildGoPackage, fetchFromGitHub, makeWrapper
+, git, coreutils, bash, gzip, openssh
+, sqliteSupport ? true
+}:
+
+with stdenv.lib;
+
+buildGoPackage rec {
+ name = "gitea-${version}";
+ version = "1.2.1";
+
+ src = fetchFromGitHub {
+ owner = "go-gitea";
+ repo = "gitea";
+ rev = "v${version}";
+ sha256 = "15zw4b6hnx4hmzn2xlsi4p7jvh6jx4g4smbdidnrzrykzyq4rmpp";
+ };
+
+ patches = [ ./static-root-path.patch ];
+
+ postPatch = ''
+ patchShebangs .
+ substituteInPlace modules/setting/setting.go --subst-var data
+ '';
+
+ nativeBuildInputs = [ makeWrapper ];
+
+ buildFlags = optionalString sqliteSupport "-tags sqlite";
+
+ outputs = [ "bin" "out" "data" ];
+
+ postInstall = ''
+ mkdir $data
+ cp -R $src/{public,templates} $data
+ mkdir -p $out
+ cp -R $src/options/locale $out/locale
+
+ wrapProgram $bin/bin/gitea \
+ --prefix PATH : ${makeBinPath [ bash git gzip openssh ]}
+ '';
+
+ goPackagePath = "code.gitea.io/gitea";
+
+ meta = {
+ description = "Git with a cup of tea";
+ homepage = http://gitea.io;
+ license = licenses.mit;
+ maintainers = [ maintainers.disassembler ];
+ };
+}
diff --git a/pkgs/applications/version-management/gitea/static-root-path.patch b/pkgs/applications/version-management/gitea/static-root-path.patch
new file mode 100644
index 00000000000..06ce521e9e8
--- /dev/null
+++ b/pkgs/applications/version-management/gitea/static-root-path.patch
@@ -0,0 +1,13 @@
+diff --git i/modules/setting/setting.go w/modules/setting/setting.go
+index aafe2d1b..1e4a8064 100644
+--- i/modules/setting/setting.go
++++ w/modules/setting/setting.go
+@@ -683,7 +683,7 @@ func NewContext() {
+ LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(defaultLocalURL)
+ OfflineMode = sec.Key("OFFLINE_MODE").MustBool()
+ DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool()
+- StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(workDir)
++ StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString("@data@")
+ AppDataPath = sec.Key("APP_DATA_PATH").MustString("data")
+ EnableGzip = sec.Key("ENABLE_GZIP").MustBool()
+ EnablePprof = sec.Key("ENABLE_PPROF").MustBool(false)
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index da1c6c7e1ae..2ced90b2d0f 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -2241,6 +2241,8 @@ with pkgs;
git-latexdiff = callPackage ../tools/typesetting/git-latexdiff { };
+ gitea = callPackage ../applications/version-management/gitea { };
+
glusterfs = callPackage ../tools/filesystems/glusterfs { };
glmark2 = callPackage ../tools/graphics/glmark2 { };