summaryrefslogtreecommitdiffstats
path: root/contrib
diff options
context:
space:
mode:
Diffstat (limited to 'contrib')
-rw-r--r--contrib/README1
-rw-r--r--contrib/autocompletion/README17
-rwxr-xr-xcontrib/autocompletion/bash_autocomplete30
-rw-r--r--contrib/autocompletion/zsh_autocomplete30
-rw-r--r--contrib/backport/README41
-rw-r--r--contrib/backport/backport.go474
-rw-r--r--contrib/environment-to-ini/README47
-rw-r--r--contrib/environment-to-ini/environment-to-ini.go111
-rwxr-xr-xcontrib/fhs-compliant-script/gitea40
-rw-r--r--contrib/fixtures/fixture_generation.go80
-rw-r--r--contrib/gitea-monitoring-mixin/.gitignore2
-rw-r--r--contrib/gitea-monitoring-mixin/Makefile31
-rw-r--r--contrib/gitea-monitoring-mixin/README.md33
-rw-r--r--contrib/gitea-monitoring-mixin/config.libsonnet99
-rw-r--r--contrib/gitea-monitoring-mixin/dashboards/dashboards.libsonnet1
-rw-r--r--contrib/gitea-monitoring-mixin/dashboards/overview.libsonnet467
-rw-r--r--contrib/gitea-monitoring-mixin/jsonnetfile.json15
-rw-r--r--contrib/gitea-monitoring-mixin/jsonnetfile.lock.json16
-rw-r--r--contrib/gitea-monitoring-mixin/lib/alerts.jsonnet1
-rw-r--r--contrib/gitea-monitoring-mixin/lib/dashboards.jsonnet6
-rw-r--r--contrib/gitea-monitoring-mixin/lib/rules.jsonnet1
-rw-r--r--contrib/gitea-monitoring-mixin/mixin.libsonnet2
-rw-r--r--contrib/ide/README.md12
-rw-r--r--contrib/ide/vscode/launch.json31
-rw-r--r--contrib/ide/vscode/settings.json4
-rw-r--r--contrib/ide/vscode/tasks.json49
-rw-r--r--contrib/init/centos/gitea93
-rw-r--r--contrib/init/debian/gitea89
-rw-r--r--contrib/init/freebsd/gitea48
-rw-r--r--contrib/init/gentoo/gitea44
-rwxr-xr-xcontrib/init/openbsd/gitea19
-rw-r--r--contrib/init/openwrt/gitea35
-rw-r--r--contrib/init/sunos/gitea.xml46
-rw-r--r--contrib/init/suse/gitea115
-rw-r--r--contrib/init/ubuntu/gitea84
-rw-r--r--contrib/launchd/io.gitea.web.plist39
-rw-r--r--contrib/legal/privacy.html.sample196
-rw-r--r--contrib/legal/tos.html.sample245
-rw-r--r--contrib/options/label/Advanced26
-rw-r--r--contrib/supervisor/gitea16
-rw-r--r--contrib/systemd/forgejo.service86
-rwxr-xr-xcontrib/update_dependencies.sh8
-rwxr-xr-xcontrib/upgrade.sh135
43 files changed, 2965 insertions, 0 deletions
diff --git a/contrib/README b/contrib/README
new file mode 100644
index 0000000..fddd5ac
--- /dev/null
+++ b/contrib/README
@@ -0,0 +1 @@
+All files in subdirectories are templates, do modifications based on your environment first. \ No newline at end of file
diff --git a/contrib/autocompletion/README b/contrib/autocompletion/README
new file mode 100644
index 0000000..1defd21
--- /dev/null
+++ b/contrib/autocompletion/README
@@ -0,0 +1,17 @@
+Bash and Zsh completion
+=======================
+
+From within the gitea root run:
+
+```bash
+source contrib/autocompletion/bash_autocomplete
+```
+
+or for zsh run:
+
+```bash
+source contrib/autocompletion/zsh_autocomplete
+```
+
+These scripts will check if gitea is on the path and if so add autocompletion for `gitea`. Or if not autocompletion will work for `./gitea`.
+If gitea has been installed as a different program pass in the `PROG` environment variable to set the correct program name.
diff --git a/contrib/autocompletion/bash_autocomplete b/contrib/autocompletion/bash_autocomplete
new file mode 100755
index 0000000..5cb62f2
--- /dev/null
+++ b/contrib/autocompletion/bash_autocomplete
@@ -0,0 +1,30 @@
+#! /bin/bash
+# Heavily inspired by https://github.com/urfave/cli
+
+_cli_bash_autocomplete() {
+ if [[ "${COMP_WORDS[0]}" != "source" ]]; then
+ local cur opts base
+ COMPREPLY=()
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ if [[ "$cur" == "-"* ]]; then
+ opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --generate-bash-completion )
+ else
+ opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion )
+ fi
+ COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+ return 0
+ fi
+}
+
+if [ -z "$PROG" ] && [ ! "$(command -v gitea &> /dev/null)" ] ; then
+ complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete gitea
+elif [ -z "$PROG" ]; then
+ complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete ./gitea
+ complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete "$PWD/gitea"
+else
+ complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete "$PROG"
+ unset PROG
+fi
+
+
+
diff --git a/contrib/autocompletion/zsh_autocomplete b/contrib/autocompletion/zsh_autocomplete
new file mode 100644
index 0000000..b3b40df
--- /dev/null
+++ b/contrib/autocompletion/zsh_autocomplete
@@ -0,0 +1,30 @@
+#compdef ${PROG:=gitea}
+
+
+# Heavily inspired by https://github.com/urfave/cli
+
+_cli_zsh_autocomplete() {
+
+ local -a opts
+ local cur
+ cur=${words[-1]}
+ if [[ "$cur" == "-"* ]]; then
+ opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} ${cur} --generate-bash-completion)}")
+ else
+ opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} --generate-bash-completion)}")
+ fi
+
+ if [[ "${opts[1]}" != "" ]]; then
+ _describe 'values' opts
+ else
+ _files
+ fi
+
+ return
+}
+
+if [ -z $PROG ] ; then
+ compdef _cli_zsh_autocomplete gitea
+else
+ compdef _cli_zsh_autocomplete $(basename $PROG)
+fi
diff --git a/contrib/backport/README b/contrib/backport/README
new file mode 100644
index 0000000..466b79c
--- /dev/null
+++ b/contrib/backport/README
@@ -0,0 +1,41 @@
+`backport`
+==========
+
+`backport` is a command to help create backports of PRs. It backports a
+provided PR from main on to a released version.
+
+It will create a backport branch, cherry-pick the PR's merge commit, adjust
+the commit message and then push this back up to your fork's remote.
+
+The default version will read from `docs/config.yml`. You can override this
+using the option `--version`.
+
+The upstream branches will be fetched, using the remote `origin`. This can
+be overridden using `--upstream`, and fetching can be avoided using
+`--no-fetch`.
+
+By default the branch created will be called `backport-$PR-$VERSION`. You
+can override this using the option `--backport-branch`. This branch will
+be created from `--release-branch` which is `release/$(VERSION)`
+by default and will be pulled from `$(UPSTREAM)`.
+
+The merge-commit as determined by the github API will be used as the SHA to
+cherry-pick. You can override this using `--cherry-pick`.
+
+The commit message will be amended to add the `Backport` header.
+`--no-amend-message` can be set to stop this from happening.
+
+If cherry-pick is successful the backported branch will be pushed up to your
+fork using your remote. These will be determined using `git remote -v`. You
+can set your fork name using `--fork-user` and your remote name using
+`--remote`. You can avoid pushing using `--no-push`.
+
+If the push is successful, `xdg-open` will be called to open a backport url.
+You can stop this using `--no-xdg-open`.
+
+Installation
+============
+
+```bash
+go install contrib/backport/backport.go
+```
diff --git a/contrib/backport/backport.go b/contrib/backport/backport.go
new file mode 100644
index 0000000..dd6b412
--- /dev/null
+++ b/contrib/backport/backport.go
@@ -0,0 +1,474 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+//nolint:forbidigo
+package main
+
+import (
+ "context"
+ "fmt"
+ "log"
+ "net/http"
+ "os"
+ "os/exec"
+ "os/signal"
+ "path"
+ "strconv"
+ "strings"
+ "syscall"
+
+ "github.com/google/go-github/v64/github"
+ "github.com/urfave/cli/v2"
+ "gopkg.in/yaml.v3"
+)
+
+const defaultVersion = "v1.18" // to backport to
+
+func main() {
+ app := cli.NewApp()
+ app.Name = "backport"
+ app.Usage = "Backport provided PR-number on to the current or previous released version"
+ app.Description = `Backport will look-up the PR in Gitea's git log and attempt to cherry-pick it on the current version`
+ app.ArgsUsage = "<PR-to-backport>"
+
+ app.Flags = []cli.Flag{
+ &cli.StringFlag{
+ Name: "version",
+ Usage: "Version branch to backport on to",
+ },
+ &cli.StringFlag{
+ Name: "upstream",
+ Value: "origin",
+ Usage: "Upstream remote for the Gitea upstream",
+ },
+ &cli.StringFlag{
+ Name: "release-branch",
+ Value: "",
+ Usage: "Release branch to backport on. Will default to release/<version>",
+ },
+ &cli.StringFlag{
+ Name: "cherry-pick",
+ Usage: "SHA to cherry-pick as backport",
+ },
+ &cli.StringFlag{
+ Name: "backport-branch",
+ Usage: "Backport branch to backport on to (default: backport-<pr>-<version>",
+ },
+ &cli.StringFlag{
+ Name: "remote",
+ Value: "",
+ Usage: "Remote for your fork of the Gitea upstream",
+ },
+ &cli.StringFlag{
+ Name: "fork-user",
+ Value: "",
+ Usage: "Forked user name on Github",
+ },
+ &cli.BoolFlag{
+ Name: "no-fetch",
+ Usage: "Set this flag to prevent fetch of remote branches",
+ },
+ &cli.BoolFlag{
+ Name: "no-amend-message",
+ Usage: "Set this flag to prevent automatic amendment of the commit message",
+ },
+ &cli.BoolFlag{
+ Name: "no-push",
+ Usage: "Set this flag to prevent pushing the backport up to your fork",
+ },
+ &cli.BoolFlag{
+ Name: "no-xdg-open",
+ Usage: "Set this flag to not use xdg-open to open the PR URL",
+ },
+ &cli.BoolFlag{
+ Name: "continue",
+ Usage: "Set this flag to continue from a git cherry-pick that has broken",
+ },
+ }
+ cli.AppHelpTemplate = `NAME:
+ {{.Name}} - {{.Usage}}
+USAGE:
+ {{.HelpName}} {{if .VisibleFlags}}[options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
+ {{if len .Authors}}
+AUTHOR:
+ {{range .Authors}}{{ . }}{{end}}
+ {{end}}{{if .Commands}}
+OPTIONS:
+ {{range .VisibleFlags}}{{.}}
+ {{end}}{{end}}
+`
+
+ app.Action = runBackport
+
+ if err := app.Run(os.Args); err != nil {
+ fmt.Fprintf(os.Stderr, "Unable to backport: %v\n", err)
+ }
+}
+
+func runBackport(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
+ continuing := c.Bool("continue")
+
+ var pr string
+
+ version := c.String("version")
+ if version == "" && continuing {
+ // determine version from current branch name
+ var err error
+ pr, version, err = readCurrentBranch(ctx)
+ if err != nil {
+ return err
+ }
+ }
+ if version == "" {
+ version = readVersion()
+ }
+ if version == "" {
+ version = defaultVersion
+ }
+
+ upstream := c.String("upstream")
+ if upstream == "" {
+ upstream = "origin"
+ }
+
+ forkUser := c.String("fork-user")
+ remote := c.String("remote")
+ if remote == "" && !c.Bool("--no-push") {
+ var err error
+ remote, forkUser, err = determineRemote(ctx, forkUser)
+ if err != nil {
+ return err
+ }
+ }
+
+ upstreamReleaseBranch := c.String("release-branch")
+ if upstreamReleaseBranch == "" {
+ upstreamReleaseBranch = path.Join("release", version)
+ }
+
+ localReleaseBranch := path.Join(upstream, upstreamReleaseBranch)
+
+ args := c.Args().Slice()
+ if len(args) == 0 && pr == "" {
+ return fmt.Errorf("no PR number provided\nProvide a PR number to backport")
+ } else if len(args) != 1 && pr == "" {
+ return fmt.Errorf("multiple PRs provided %v\nOnly a single PR can be backported at a time", args)
+ }
+ if pr == "" {
+ pr = args[0]
+ }
+
+ backportBranch := c.String("backport-branch")
+ if backportBranch == "" {
+ backportBranch = "backport-" + pr + "-" + version
+ }
+
+ fmt.Printf("* Backporting %s to %s as %s\n", pr, localReleaseBranch, backportBranch)
+
+ sha := c.String("cherry-pick")
+ if sha == "" {
+ var err error
+ sha, err = determineSHAforPR(ctx, pr)
+ if err != nil {
+ return err
+ }
+ }
+ if sha == "" {
+ return fmt.Errorf("unable to determine sha for cherry-pick of %s", pr)
+ }
+
+ if !c.Bool("no-fetch") {
+ if err := fetchRemoteAndMain(ctx, upstream, upstreamReleaseBranch); err != nil {
+ return err
+ }
+ }
+
+ if !continuing {
+ if err := checkoutBackportBranch(ctx, backportBranch, localReleaseBranch); err != nil {
+ return err
+ }
+ }
+
+ if err := cherrypick(ctx, sha); err != nil {
+ return err
+ }
+
+ if !c.Bool("no-amend-message") {
+ if err := amendCommit(ctx, pr); err != nil {
+ return err
+ }
+ }
+
+ if !c.Bool("no-push") {
+ url := "https://github.com/go-gitea/gitea/compare/" + upstreamReleaseBranch + "..." + forkUser + ":" + backportBranch
+
+ if err := gitPushUp(ctx, remote, backportBranch); err != nil {
+ return err
+ }
+
+ if !c.Bool("no-xdg-open") {
+ if err := xdgOpen(ctx, url); err != nil {
+ return err
+ }
+ } else {
+ fmt.Printf("* Navigate to %s to open PR\n", url)
+ }
+ }
+ return nil
+}
+
+func xdgOpen(ctx context.Context, url string) error {
+ fmt.Printf("* `xdg-open %s`\n", url)
+ out, err := exec.CommandContext(ctx, "xdg-open", url).Output()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s", string(out))
+ return fmt.Errorf("unable to xdg-open to %s: %w", url, err)
+ }
+ return nil
+}
+
+func gitPushUp(ctx context.Context, remote, backportBranch string) error {
+ fmt.Printf("* `git push -u %s %s`\n", remote, backportBranch)
+ out, err := exec.CommandContext(ctx, "git", "push", "-u", remote, backportBranch).Output()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s", string(out))
+ return fmt.Errorf("unable to push up to %s: %w", remote, err)
+ }
+ return nil
+}
+
+func amendCommit(ctx context.Context, pr string) error {
+ fmt.Printf("* Amending commit to prepend `Backport #%s` to body\n", pr)
+ out, err := exec.CommandContext(ctx, "git", "log", "-1", "--pretty=format:%B").Output()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s", string(out))
+ return fmt.Errorf("unable to get last log message: %w", err)
+ }
+
+ parts := strings.SplitN(string(out), "\n", 2)
+
+ if len(parts) != 2 {
+ return fmt.Errorf("unable to interpret log message:\n%s", string(out))
+ }
+ subject, body := parts[0], parts[1]
+ if !strings.HasSuffix(subject, " (#"+pr+")") {
+ subject = subject + " (#" + pr + ")"
+ }
+
+ out, err = exec.CommandContext(ctx, "git", "commit", "--amend", "-m", subject+"\n\nBackport #"+pr+"\n"+body).Output()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s", string(out))
+ return fmt.Errorf("unable to amend last log message: %w", err)
+ }
+ return nil
+}
+
+func cherrypick(ctx context.Context, sha string) error {
+ // Check if a CHERRY_PICK_HEAD exists
+ if _, err := os.Stat(".git/CHERRY_PICK_HEAD"); err == nil {
+ // Assume that we are in the middle of cherry-pick - continue it
+ fmt.Println("* Attempting git cherry-pick --continue")
+ out, err := exec.CommandContext(ctx, "git", "cherry-pick", "--continue").Output()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "git cherry-pick --continue failed:\n%s\n", string(out))
+ return fmt.Errorf("unable to continue cherry-pick: %w", err)
+ }
+ return nil
+ }
+
+ fmt.Printf("* Attempting git cherry-pick %s\n", sha)
+ out, err := exec.CommandContext(ctx, "git", "cherry-pick", sha).Output()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "git cherry-pick %s failed:\n%s\n", sha, string(out))
+ return fmt.Errorf("git cherry-pick %s failed: %w", sha, err)
+ }
+ return nil
+}
+
+func checkoutBackportBranch(ctx context.Context, backportBranch, releaseBranch string) error {
+ out, err := exec.CommandContext(ctx, "git", "branch", "--show-current").Output()
+ if err != nil {
+ return fmt.Errorf("unable to check current branch %w", err)
+ }
+
+ currentBranch := strings.TrimSpace(string(out))
+ fmt.Printf("* Current branch is %s\n", currentBranch)
+ if currentBranch == backportBranch {
+ fmt.Printf("* Current branch is %s - not checking out\n", currentBranch)
+ return nil
+ }
+
+ if _, err := exec.CommandContext(ctx, "git", "rev-list", "-1", backportBranch).Output(); err == nil {
+ fmt.Printf("* Branch %s already exists. Checking it out...\n", backportBranch)
+ return exec.CommandContext(ctx, "git", "checkout", "-f", backportBranch).Run()
+ }
+
+ fmt.Printf("* `git checkout -b %s %s`\n", backportBranch, releaseBranch)
+ return exec.CommandContext(ctx, "git", "checkout", "-b", backportBranch, releaseBranch).Run()
+}
+
+func fetchRemoteAndMain(ctx context.Context, remote, releaseBranch string) error {
+ fmt.Printf("* `git fetch %s main`\n", remote)
+ out, err := exec.CommandContext(ctx, "git", "fetch", remote, "main").Output()
+ if err != nil {
+ fmt.Println(string(out))
+ return fmt.Errorf("unable to fetch %s from %s: %w", "main", remote, err)
+ }
+ fmt.Println(string(out))
+
+ fmt.Printf("* `git fetch %s %s`\n", remote, releaseBranch)
+ out, err = exec.CommandContext(ctx, "git", "fetch", remote, releaseBranch).Output()
+ if err != nil {
+ fmt.Println(string(out))
+ return fmt.Errorf("unable to fetch %s from %s: %w", releaseBranch, remote, err)
+ }
+ fmt.Println(string(out))
+
+ return nil
+}
+
+func determineRemote(ctx context.Context, forkUser string) (string, string, error) {
+ out, err := exec.CommandContext(ctx, "git", "remote", "-v").Output()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Unable to list git remotes:\n%s\n", string(out))
+ return "", "", fmt.Errorf("unable to determine forked remote: %w", err)
+ }
+ lines := strings.Split(string(out), "\n")
+ for _, line := range lines {
+ fields := strings.Split(line, "\t")
+ name, remote := fields[0], fields[1]
+ // only look at pushers
+ if !strings.HasSuffix(remote, " (push)") {
+ continue
+ }
+ // only look at github.com pushes
+ if !strings.Contains(remote, "github.com") {
+ continue
+ }
+ // ignore go-gitea/gitea
+ if strings.Contains(remote, "go-gitea/gitea") {
+ continue
+ }
+ if !strings.Contains(remote, forkUser) {
+ continue
+ }
+ if strings.HasPrefix(remote, "git@github.com:") {
+ forkUser = strings.TrimPrefix(remote, "git@github.com:")
+ } else if strings.HasPrefix(remote, "https://github.com/") {
+ forkUser = strings.TrimPrefix(remote, "https://github.com/")
+ } else if strings.HasPrefix(remote, "https://www.github.com/") {
+ forkUser = strings.TrimPrefix(remote, "https://www.github.com/")
+ } else if forkUser == "" {
+ return "", "", fmt.Errorf("unable to extract forkUser from remote %s: %s", name, remote)
+ }
+ idx := strings.Index(forkUser, "/")
+ if idx >= 0 {
+ forkUser = forkUser[:idx]
+ }
+ return name, forkUser, nil
+ }
+ return "", "", fmt.Errorf("unable to find appropriate remote in:\n%s", string(out))
+}
+
+func readCurrentBranch(ctx context.Context) (pr, version string, err error) {
+ out, err := exec.CommandContext(ctx, "git", "branch", "--show-current").Output()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Unable to read current git branch:\n%s\n", string(out))
+ return "", "", fmt.Errorf("unable to read current git branch: %w", err)
+ }
+ parts := strings.Split(strings.TrimSpace(string(out)), "-")
+
+ if len(parts) != 3 || parts[0] != "backport" {
+ fmt.Fprintf(os.Stderr, "Unable to continue from git branch:\n%s\n", string(out))
+ return "", "", fmt.Errorf("unable to continue from git branch:\n%s", string(out))
+ }
+
+ return parts[1], parts[2], nil
+}
+
+func readVersion() string {
+ bs, err := os.ReadFile("docs/config.yaml")
+ if err != nil {
+ if err == os.ErrNotExist {
+ log.Println("`docs/config.yaml` not present")
+ return ""
+ }
+ fmt.Fprintf(os.Stderr, "Unable to read `docs/config.yaml`: %v\n", err)
+ return ""
+ }
+
+ type params struct {
+ Version string
+ }
+ type docConfig struct {
+ Params params
+ }
+ dc := &docConfig{}
+ if err := yaml.Unmarshal(bs, dc); err != nil {
+ fmt.Fprintf(os.Stderr, "Unable to read `docs/config.yaml`: %v\n", err)
+ return ""
+ }
+
+ if dc.Params.Version == "" {
+ fmt.Fprintf(os.Stderr, "No version in `docs/config.yaml`")
+ return ""
+ }
+
+ version := dc.Params.Version
+ if version[0] != 'v' {
+ version = "v" + version
+ }
+
+ split := strings.SplitN(version, ".", 3)
+
+ return strings.Join(split[:2], ".")
+}
+
+func determineSHAforPR(ctx context.Context, prStr string) (string, error) {
+ prNum, err := strconv.Atoi(prStr)
+ if err != nil {
+ return "", err
+ }
+
+ client := github.NewClient(http.DefaultClient)
+
+ pr, _, err := client.PullRequests.Get(ctx, "go-gitea", "gitea", prNum)
+ if err != nil {
+ return "", err
+ }
+
+ if pr.Merged == nil || !*pr.Merged {
+ return "", fmt.Errorf("PR #%d is not yet merged - cannot determine sha to backport", prNum)
+ }
+
+ if pr.MergeCommitSHA != nil {
+ return *pr.MergeCommitSHA, nil
+ }
+
+ return "", nil
+}
+
+func installSignals() (context.Context, context.CancelFunc) {
+ ctx, cancel := context.WithCancel(context.Background())
+ go func() {
+ // install notify
+ signalChannel := make(chan os.Signal, 1)
+
+ signal.Notify(
+ signalChannel,
+ syscall.SIGINT,
+ syscall.SIGTERM,
+ )
+ select {
+ case <-signalChannel:
+ case <-ctx.Done():
+ }
+ cancel()
+ signal.Reset()
+ }()
+
+ return ctx, cancel
+}
diff --git a/contrib/environment-to-ini/README b/contrib/environment-to-ini/README
new file mode 100644
index 0000000..f1d3f2a
--- /dev/null
+++ b/contrib/environment-to-ini/README
@@ -0,0 +1,47 @@
+Environment To Ini
+==================
+
+Multiple docker users have requested that the Gitea docker is changed
+to permit arbitrary configuration via environment variables.
+
+Gitea needs to use an ini file for configuration because the running
+environment that starts the docker may not be the same as that used
+by the hooks. An ini file also gives a good default and means that
+users do not have to completely provide a full environment.
+
+With those caveats above, this command provides a generic way of
+converting suitably structured environment variables into any ini
+value.
+
+To use the command is very simple just run it and the default gitea
+app.ini will be rewritten to take account of the variables provided,
+however there are various options to give slightly different
+behavior and these can be interrogated with the `-h` option.
+
+The environment variables should be of the form:
+
+ GITEA__SECTION_NAME__KEY_NAME
+
+Note, SECTION_NAME in the notation above is case-insensitive.
+
+Environment variables are usually restricted to a reduced character
+set "0-9A-Z_" - in order to allow the setting of sections with
+characters outside of that set, they should be escaped as following:
+"_0X2E_" for "." and "_0X2D_" for "-". The entire section and key names
+can be escaped as a UTF8 byte string if necessary. E.g. to configure:
+
+ """
+ ...
+ [log.console]
+ COLORIZE=false
+ STDERR=true
+ ...
+ """
+
+You would set the environment variables: "GITEA__LOG_0x2E_CONSOLE__COLORIZE=false"
+and "GITEA__LOG_0x2E_CONSOLE__STDERR=false". Other examples can be found
+on the configuration cheat sheet.
+
+To build locally, run:
+
+ go build contrib/environment-to-ini/environment-to-ini.go
diff --git a/contrib/environment-to-ini/environment-to-ini.go b/contrib/environment-to-ini/environment-to-ini.go
new file mode 100644
index 0000000..f8593e4
--- /dev/null
+++ b/contrib/environment-to-ini/environment-to-ini.go
@@ -0,0 +1,111 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package main
+
+import (
+ "os"
+
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+
+ "github.com/urfave/cli/v2"
+)
+
+func main() {
+ app := cli.NewApp()
+ app.Name = "environment-to-ini"
+ app.Usage = "Use provided environment to update configuration ini"
+ app.Description = `As a helper to allow docker users to update the forgejo configuration
+ through the environment, this command allows environment variables to
+ be mapped to values in the ini.
+
+ Environment variables of the form "FORGEJO__SECTION_NAME__KEY_NAME"
+ will be mapped to the ini section "[section_name]" and the key
+ "KEY_NAME" with the value as provided.
+
+ Environment variables of the form "FORGEJO__SECTION_NAME__KEY_NAME__FILE"
+ will be mapped to the ini section "[section_name]" and the key
+ "KEY_NAME" with the value loaded from the specified file.
+
+ Environment variables are usually restricted to a reduced character
+ set "0-9A-Z_" - in order to allow the setting of sections with
+ characters outside of that set, they should be escaped as following:
+ "_0X2E_" for ".". The entire section and key names can be escaped as
+ a UTF8 byte string if necessary. E.g. to configure:
+
+ """
+ ...
+ [log.console]
+ COLORIZE=false
+ STDERR=true
+ ...
+ """
+
+ You would set the environment variables: "FORGEJO__LOG_0x2E_CONSOLE__COLORIZE=false"
+ and "FORGEJO__LOG_0x2E_CONSOLE__STDERR=false". Other examples can be found
+ on the configuration cheat sheet.`
+ app.Flags = []cli.Flag{
+ &cli.StringFlag{
+ Name: "custom-path",
+ Aliases: []string{"C"},
+ Value: setting.CustomPath,
+ Usage: "Custom path file path",
+ },
+ &cli.StringFlag{
+ Name: "config",
+ Aliases: []string{"c"},
+ Value: setting.CustomConf,
+ Usage: "Custom configuration file path",
+ },
+ &cli.StringFlag{
+ Name: "work-path",
+ Aliases: []string{"w"},
+ Value: setting.AppWorkPath,
+ Usage: "Set the forgejo working path",
+ },
+ &cli.StringFlag{
+ Name: "out",
+ Aliases: []string{"o"},
+ Value: "",
+ Usage: "Destination file to write to",
+ },
+ }
+ app.Action = runEnvironmentToIni
+ err := app.Run(os.Args)
+ if err != nil {
+ log.Fatal("Failed to run app with %s: %v", os.Args, err)
+ }
+}
+
+func runEnvironmentToIni(c *cli.Context) error {
+ // the config system may change the environment variables, so get a copy first, to be used later
+ env := append([]string{}, os.Environ()...)
+ setting.InitWorkPathAndCfgProvider(os.Getenv, setting.ArgWorkPathAndCustomConf{
+ WorkPath: c.String("work-path"),
+ CustomPath: c.String("custom-path"),
+ CustomConf: c.String("config"),
+ })
+
+ cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
+ if err != nil {
+ log.Fatal("Failed to load custom conf '%s': %v", setting.CustomConf, err)
+ }
+
+ changed := setting.EnvironmentToConfig(cfg, env)
+
+ // try to save the config file
+ destination := c.String("out")
+ if len(destination) == 0 {
+ destination = setting.CustomConf
+ }
+ if destination != setting.CustomConf || changed {
+ log.Info("Settings saved to: %q", destination)
+ err = cfg.SaveTo(destination)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/contrib/fhs-compliant-script/gitea b/contrib/fhs-compliant-script/gitea
new file mode 100755
index 0000000..ea033b0
--- /dev/null
+++ b/contrib/fhs-compliant-script/gitea
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+#############################################################################
+# This script sets some defaults for gitea to run in a FHS compliant manner #
+#############################################################################
+
+# It assumes that you place this script as gitea in /usr/bin
+#
+# And place the original in /usr/lib/gitea with working files in /var/lib/gitea
+# and main configuration in /etc/gitea/app.ini
+GITEA="/usr/lib/gitea/gitea"
+WORK_DIR="/var/lib/gitea"
+APP_INI="/etc/gitea/app.ini"
+
+APP_INI_SET=""
+for i in "$@"; do
+ case "$i" in
+ "-c")
+ APP_INI_SET=1
+ ;;
+ "-c="*)
+ APP_INI_SET=1
+ ;;
+ "--config")
+ APP_INI_SET=1
+ ;;
+ "--config="*)
+ APP_INI_SET=1
+ ;;
+ *)
+ ;;
+ esac
+done
+
+if [ -z "$APP_INI_SET" ]; then
+ CONF_ARG=("-c" "${GITEA_APP_INI:-$APP_INI}")
+fi
+
+# Provide FHS compliant defaults
+GITEA_WORK_DIR="${GITEA_WORK_DIR:-$WORK_DIR}" exec -a "$0" "$GITEA" "${CONF_ARG[@]}" "$@"
diff --git a/contrib/fixtures/fixture_generation.go b/contrib/fixtures/fixture_generation.go
new file mode 100644
index 0000000..31797cc
--- /dev/null
+++ b/contrib/fixtures/fixture_generation.go
@@ -0,0 +1,80 @@
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+//nolint:forbidigo
+package main
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "code.gitea.io/gitea/models"
+ "code.gitea.io/gitea/models/unittest"
+)
+
+// To generate derivative fixtures, execute the following from Gitea's repository base dir:
+// go run -tags 'sqlite sqlite_unlock_notify' contrib/fixtures/fixture_generation.go [fixture...]
+
+var (
+ generators = []struct {
+ gen func(ctx context.Context) (string, error)
+ name string
+ }{
+ {
+ models.GetYamlFixturesAccess, "access",
+ },
+ }
+ fixturesDir string
+)
+
+func main() {
+ pathToGiteaRoot := "."
+ fixturesDir = filepath.Join(pathToGiteaRoot, "models", "fixtures")
+ if err := unittest.CreateTestEngine(unittest.FixturesOptions{
+ Dir: fixturesDir,
+ }); err != nil {
+ fmt.Printf("CreateTestEngine: %+v", err)
+ os.Exit(1)
+ }
+ if err := unittest.PrepareTestDatabase(); err != nil {
+ fmt.Printf("PrepareTestDatabase: %+v\n", err)
+ os.Exit(1)
+ }
+ ctx := context.Background()
+ if len(os.Args) == 0 {
+ for _, r := range os.Args {
+ if err := generate(ctx, r); err != nil {
+ fmt.Printf("generate '%s': %+v\n", r, err)
+ os.Exit(1)
+ }
+ }
+ } else {
+ for _, g := range generators {
+ if err := generate(ctx, g.name); err != nil {
+ fmt.Printf("generate '%s': %+v\n", g.name, err)
+ os.Exit(1)
+ }
+ }
+ }
+}
+
+func generate(ctx context.Context, name string) error {
+ for _, g := range generators {
+ if g.name == name {
+ data, err := g.gen(ctx)
+ if err != nil {
+ return err
+ }
+ path := filepath.Join(fixturesDir, name+".yml")
+ if err := os.WriteFile(path, []byte(data), 0o644); err != nil {
+ return fmt.Errorf("%s: %+v", path, err)
+ }
+ fmt.Printf("%s created.\n", path)
+ return nil
+ }
+ }
+
+ return fmt.Errorf("generator not found")
+}
diff --git a/contrib/gitea-monitoring-mixin/.gitignore b/contrib/gitea-monitoring-mixin/.gitignore
new file mode 100644
index 0000000..f8472b0
--- /dev/null
+++ b/contrib/gitea-monitoring-mixin/.gitignore
@@ -0,0 +1,2 @@
+dashboards_out
+vendor
diff --git a/contrib/gitea-monitoring-mixin/Makefile b/contrib/gitea-monitoring-mixin/Makefile
new file mode 100644
index 0000000..429dfc4
--- /dev/null
+++ b/contrib/gitea-monitoring-mixin/Makefile
@@ -0,0 +1,31 @@
+JSONNET_FMT := jsonnetfmt -n 2 --max-blank-lines 1 --string-style s --comment-style s
+
+.PHONY: all
+all: build dashboards_out
+
+vendor: jsonnetfile.json
+ jb install
+
+.PHONY: build
+build: vendor
+
+.PHONY: fmt
+fmt:
+ find . -name 'vendor' -prune -o -name '*.libsonnet' -print -o -name '*.jsonnet' -print | \
+ xargs -n 1 -- $(JSONNET_FMT) -i
+
+.PHONY: lint
+lint: build
+ find . -name 'vendor' -prune -o -name '*.libsonnet' -print -o -name '*.jsonnet' -print | \
+ while read f; do \
+ $(JSONNET_FMT) "$$f" | diff -u "$$f" -; \
+ done
+ mixtool lint mixin.libsonnet
+
+dashboards_out: mixin.libsonnet config.libsonnet $(wildcard dashboards/*)
+ @mkdir -p dashboards_out
+ jsonnet -J vendor -m dashboards_out lib/dashboards.jsonnet
+
+.PHONY: clean
+clean:
+ rm -rf dashboards_out
diff --git a/contrib/gitea-monitoring-mixin/README.md b/contrib/gitea-monitoring-mixin/README.md
new file mode 100644
index 0000000..2e11706
--- /dev/null
+++ b/contrib/gitea-monitoring-mixin/README.md
@@ -0,0 +1,33 @@
+# Gitea Mixin
+
+Gitea Mixin is a set of configurable Grafana dashboards based on the metrics exported by the Gitea built-in metrics endpoint.
+
+## Generate config files
+
+You can manually generate dashboards, but first you should install some tools:
+
+```bash
+go install github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb@latest
+go install github.com/google/go-jsonnet/cmd/jsonnet@latest
+# or in brew: brew install go-jsonnet
+```
+
+For linting and formatting, you would also need `mixtool` and `jsonnetfmt` installed. If you
+have a working Go development environment, it's easiest to run the following:
+
+```bash
+go install github.com/monitoring-mixins/mixtool/cmd/mixtool@latest
+go install github.com/google/go-jsonnet/cmd/jsonnetfmt@latest
+```
+
+The files in `dashboards_out` need to be imported
+into your Grafana server. The exact details will be depending on your environment.
+
+Edit `config.libsonnet` if required and then build JSON dashboard files for Grafana:
+
+```bash
+make
+```
+
+For more advanced uses of mixins, see
+https://github.com/monitoring-mixins/docs.
diff --git a/contrib/gitea-monitoring-mixin/config.libsonnet b/contrib/gitea-monitoring-mixin/config.libsonnet
new file mode 100644
index 0000000..446fc09
--- /dev/null
+++ b/contrib/gitea-monitoring-mixin/config.libsonnet
@@ -0,0 +1,99 @@
+{
+ _config+:: {
+ local c = self,
+ dashboardNamePrefix: 'Gitea',
+ dashboardTags: ['gitea'],
+ dashboardPeriod: 'now-1h',
+ dashboardTimezone: 'default',
+ dashboardRefresh: '1m',
+
+ // please see https://docs.gitea.com/administration/config-cheat-sheet#metrics-metrics
+ // Show issue by repository metrics with format gitea_issues_by_repository{repository="org/repo"} 5.
+ // Requires Gitea 1.16.0 with ENABLED_ISSUE_BY_REPOSITORY set to true.
+ showIssuesByRepository: true,
+ // Show graphs for issue by label metrics with format gitea_issues_by_label{label="bug"} 2.
+ // Requires Gitea 1.16.0 with ENABLED_ISSUE_BY_LABEL set to true.
+ showIssuesByLabel: true,
+
+ // Requires Gitea 1.16.0.
+ showIssuesOpenClose: true,
+
+ // add or remove metrics from dashboard
+ giteaStatMetrics:
+ [
+ {
+ name: 'gitea_organizations',
+ description: 'Organizations',
+ },
+ {
+ name: 'gitea_teams',
+ description: 'Teams',
+ },
+ {
+ name: 'gitea_users',
+ description: 'Users',
+ },
+ {
+ name: 'gitea_repositories',
+ description: 'Repositories',
+ },
+ {
+ name: 'gitea_milestones',
+ description: 'Milestones',
+ },
+ {
+ name: 'gitea_stars',
+ description: 'Stars',
+ },
+ {
+ name: 'gitea_releases',
+ description: 'Releases',
+ },
+ ]
+ +
+ if c.showIssuesOpenClose then
+ [
+ {
+ name: 'gitea_issues_open',
+ description: 'Issues opened',
+ },
+ {
+ name: 'gitea_issues_closed',
+ description: 'Issues closed',
+ },
+ ] else
+ [
+ {
+ name: 'gitea_issues',
+ description: 'Issues',
+ },
+ ],
+ //set this for using label colors on graphs
+ issueLabels: [
+ {
+ label: 'bug',
+ color: '#ee0701',
+ },
+ {
+ label: 'duplicate',
+ color: '#cccccc',
+ },
+ {
+ label: 'invalid',
+ color: '#e6e6e6',
+ },
+ {
+ label: 'enhancement',
+ color: '#84b6eb',
+ },
+ {
+ label: 'help wanted',
+ color: '#128a0c',
+ },
+ {
+ label: 'question',
+ color: '#cc317c',
+ },
+ ],
+ },
+}
diff --git a/contrib/gitea-monitoring-mixin/dashboards/dashboards.libsonnet b/contrib/gitea-monitoring-mixin/dashboards/dashboards.libsonnet
new file mode 100644
index 0000000..800feec
--- /dev/null
+++ b/contrib/gitea-monitoring-mixin/dashboards/dashboards.libsonnet
@@ -0,0 +1 @@
+(import 'overview.libsonnet')
diff --git a/contrib/gitea-monitoring-mixin/dashboards/overview.libsonnet b/contrib/gitea-monitoring-mixin/dashboards/overview.libsonnet
new file mode 100644
index 0000000..31b7d4f
--- /dev/null
+++ b/contrib/gitea-monitoring-mixin/dashboards/overview.libsonnet
@@ -0,0 +1,467 @@
+local grafana = import 'github.com/grafana/grafonnet-lib/grafonnet/grafana.libsonnet';
+local prometheus = grafana.prometheus;
+
+local addIssueLabelsOverrides(labels) =
+ {
+ fieldConfig+: {
+ overrides+: [
+ {
+ matcher: {
+ id: 'byRegexp',
+ options: label.label,
+ },
+ properties: [
+ {
+ id: 'color',
+ value: {
+ fixedColor: label.color,
+ mode: 'fixed',
+ },
+ },
+ ],
+ }
+ for label in labels
+ ],
+ },
+ };
+
+{
+
+ grafanaDashboards+:: {
+
+ local giteaSelector = 'job=~"$job", instance=~"$instance"',
+ local giteaStatsPanel =
+ grafana.statPanel.new(
+ 'Gitea stats',
+ datasource='$datasource',
+ reducerFunction='lastNotNull',
+ graphMode='none',
+ colorMode='value',
+ )
+ .addTargets(
+ [
+ prometheus.target(expr='%s{%s}' % [metric.name, giteaSelector], legendFormat=metric.description, intervalFactor=10)
+ for metric in $._config.giteaStatMetrics
+ ]
+ )
+ + {
+ fieldConfig+: {
+ defaults+: {
+ color: {
+ fixedColor: 'blue',
+ mode: 'fixed',
+ },
+ },
+ },
+ },
+
+ local giteaUptimePanel =
+ grafana.statPanel.new(
+ 'Uptime',
+ datasource='$datasource',
+ reducerFunction='last',
+ graphMode='area',
+ colorMode='value',
+ )
+ .addTarget(prometheus.target(expr='time()-process_start_time_seconds{%s}' % giteaSelector, intervalFactor=1))
+ + {
+ fieldConfig+: {
+ defaults+: {
+ color: {
+ fixedColor: 'blue',
+ mode: 'fixed',
+ },
+ unit: 's',
+ },
+ },
+ },
+
+ local giteaMemoryPanel =
+ grafana.graphPanel.new(
+ 'Memory usage',
+ datasource='$datasource'
+ )
+ .addTarget(prometheus.target(expr='process_resident_memory_bytes{%s}' % giteaSelector, intervalFactor=2))
+ + {
+ type: 'timeseries',
+ options+: {
+ tooltip: {
+ mode: 'multi',
+ },
+ legend+: {
+ displayMode: 'hidden',
+ },
+ },
+ fieldConfig+: {
+ defaults+: {
+ custom+: {
+ lineInterpolation: 'smooth',
+ fillOpacity: 15,
+ },
+ color: {
+ fixedColor: 'green',
+ mode: 'fixed',
+ },
+ unit: 'decbytes',
+ },
+ },
+ },
+
+ local giteaCpuPanel =
+ grafana.graphPanel.new(
+ 'CPU usage',
+ datasource='$datasource'
+ )
+ .addTarget(prometheus.target(expr='rate(process_cpu_seconds_total{%s}[$__rate_interval])*100' % giteaSelector, intervalFactor=2))
+ + {
+ type: 'timeseries',
+ options+: {
+ tooltip: {
+ mode: 'multi',
+ },
+ legend+: {
+ displayMode: 'hidden',
+ },
+ },
+ fieldConfig+: {
+ defaults+: {
+ custom+: {
+ lineInterpolation: 'smooth',
+ gradientMode: 'scheme',
+ fillOpacity: 15,
+ axisSoftMin: 0,
+ axisSoftMax: 0,
+ },
+ color: {
+ mode: 'continuous-GrYlRd', // from green to red (100%)
+ },
+ unit: 'percent',
+ },
+ overrides: [
+ {
+ matcher: {
+ id: 'byRegexp',
+ options: '.+',
+ },
+ properties: [
+ {
+ id: 'max',
+ value: 100,
+ },
+ {
+ id: 'min',
+ value: 0,
+ },
+ ],
+ },
+ ],
+ },
+ },
+
+ local giteaFileDescriptorsPanel =
+ grafana.graphPanel.new(
+ 'File descriptors usage',
+ datasource='$datasource',
+ )
+ .addTarget(prometheus.target(expr='process_open_fds{%s}' % giteaSelector, intervalFactor=2))
+ .addTarget(prometheus.target(expr='process_max_fds{%s}' % giteaSelector, intervalFactor=2))
+ .addSeriesOverride(
+ {
+ alias: '/process_max_fds.+/',
+ color: '#F2495C', // red
+ dashes: true,
+ fill: 0,
+ },
+ )
+ + {
+ type: 'timeseries',
+ options+: {
+ tooltip: {
+ mode: 'multi',
+ },
+ legend+: {
+ displayMode: 'hidden',
+ },
+ },
+ fieldConfig+: {
+ defaults+: {
+ custom+: {
+ lineInterpolation: 'smooth',
+ gradientMode: 'scheme',
+ fillOpacity: 0,
+ },
+ color: {
+ fixedColor: 'green',
+ mode: 'fixed',
+ },
+ unit: '',
+ },
+ overrides: [
+ {
+ matcher: {
+ id: 'byFrameRefID',
+ options: 'B',
+ },
+ properties: [
+ {
+ id: 'custom.lineStyle',
+ value: {
+ fill: 'dash',
+ dash: [
+ 10,
+ 10,
+ ],
+ },
+ },
+ {
+ id: 'color',
+ value: {
+ mode: 'fixed',
+ fixedColor: 'red',
+ },
+ },
+ ],
+ },
+ ],
+ },
+ },
+
+ local giteaChangesPanelPrototype =
+ grafana.graphPanel.new(
+ '',
+ datasource='$datasource',
+ interval='$agg_interval',
+ maxDataPoints=10000,
+ )
+ + {
+ type: 'timeseries',
+ options+: {
+ tooltip: {
+ mode: 'multi',
+ },
+ legend+: {
+ calcs+: [
+ 'sum',
+ ],
+ },
+ },
+ fieldConfig+: {
+ defaults+: {
+ noValue: '0',
+ custom+: {
+ drawStyle: 'bars',
+ barAlignment: -1,
+ fillOpacity: 50,
+ gradientMode: 'hue',
+ pointSize: 1,
+ lineWidth: 0,
+ stacking: {
+ group: 'A',
+ mode: 'normal',
+ },
+ },
+ },
+ },
+ },
+
+ local giteaChangesPanelAll =
+ giteaChangesPanelPrototype
+ .addTarget(prometheus.target(expr='changes(process_start_time_seconds{%s}[$__interval]) > 0' % [giteaSelector], legendFormat='Restarts', intervalFactor=1))
+ .addTargets(
+ [
+ prometheus.target(expr='floor(delta(%s{%s}[$__interval])) > 0' % [metric.name, giteaSelector], legendFormat=metric.description, intervalFactor=1)
+ for metric in $._config.giteaStatMetrics
+ ]
+ ) + { id: 200 }, // some unique number, beyond the maximum number of panels in the dashboard,
+
+ local giteaChangesPanelTotal =
+ grafana.statPanel.new(
+ 'Changes',
+ datasource='-- Dashboard --',
+ reducerFunction='sum',
+ graphMode='none',
+ textMode='value_and_name',
+ colorMode='value',
+ )
+ + {
+ targets+: [
+ {
+ panelId: giteaChangesPanelAll.id,
+ refId: 'A',
+ },
+ ],
+ }
+ + {
+ fieldConfig+: {
+ defaults+: {
+ color: {
+ mode: 'palette-classic',
+ },
+ },
+ },
+ },
+
+ local giteaChangesByRepositories =
+ giteaChangesPanelPrototype
+ .addTarget(prometheus.target(expr='floor(increase(gitea_issues_by_repository{%s}[$__interval])) > 0' % [giteaSelector], legendFormat='{{ repository }}', intervalFactor=1))
+ + { id: 210 }, // some unique number, beyond the maximum number of panels in the dashboard,
+
+ local giteaChangesByRepositoriesTotal =
+ grafana.statPanel.new(
+ 'Issues by repository',
+ datasource='-- Dashboard --',
+ reducerFunction='sum',
+ graphMode='none',
+ textMode='value_and_name',
+ colorMode='value',
+ )
+ + {
+ id: 211,
+ targets+: [
+ {
+ panelId: giteaChangesByRepositories.id,
+ refId: 'A',
+ },
+ ],
+ }
+ + {
+ fieldConfig+: {
+ defaults+: {
+ color: {
+ mode: 'palette-classic',
+ },
+ },
+ },
+ },
+
+ local giteaChangesByLabel =
+ giteaChangesPanelPrototype
+ .addTarget(prometheus.target(expr='floor(increase(gitea_issues_by_label{%s}[$__interval])) > 0' % [giteaSelector], legendFormat='{{ label }}', intervalFactor=1))
+ + addIssueLabelsOverrides($._config.issueLabels)
+ + { id: 220 }, // some unique number, beyond the maximum number of panels in the dashboard,
+
+ local giteaChangesByLabelTotal =
+ grafana.statPanel.new(
+ 'Issues by labels',
+ datasource='-- Dashboard --',
+ reducerFunction='sum',
+ graphMode='none',
+ textMode='value_and_name',
+ colorMode='value',
+ )
+ + addIssueLabelsOverrides($._config.issueLabels)
+ + {
+ id: 221,
+ targets+: [
+ {
+ panelId: giteaChangesByLabel.id,
+ refId: 'A',
+ },
+ ],
+ }
+ + {
+ fieldConfig+: {
+ defaults+: {
+ color: {
+ mode: 'palette-classic',
+ },
+ },
+ },
+ },
+
+ 'gitea-overview.json':
+ grafana.dashboard.new(
+ '%s Overview' % $._config.dashboardNamePrefix,
+ time_from='%s' % $._config.dashboardPeriod,
+ editable=false,
+ tags=($._config.dashboardTags),
+ timezone='%s' % $._config.dashboardTimezone,
+ refresh='%s' % $._config.dashboardRefresh,
+ graphTooltip='shared_crosshair',
+ uid='gitea-overview'
+ )
+ .addTemplate(
+ {
+ current: {
+ text: 'Prometheus',
+ value: 'Prometheus',
+ },
+ hide: 0,
+ label: 'Data Source',
+ name: 'datasource',
+ options: [],
+ query: 'prometheus',
+ refresh: 1,
+ regex: '',
+ type: 'datasource',
+ },
+ )
+ .addTemplate(
+ {
+ hide: 0,
+ label: 'job',
+ name: 'job',
+ options: [],
+ datasource: '$datasource',
+ query: 'label_values(gitea_organizations, job)',
+ refresh: 1,
+ regex: '',
+ type: 'query',
+ multi: true,
+ allValue: '.+'
+ },
+ )
+ .addTemplate(
+ {
+ hide: 0,
+ label: 'instance',
+ name: 'instance',
+ options: [],
+ datasource: '$datasource',
+ query: 'label_values(gitea_organizations{job="$job"}, instance)',
+ refresh: 1,
+ regex: '',
+ type: 'query',
+ multi: true,
+ allValue: '.+'
+ },
+ )
+ .addTemplate(
+ {
+ hide: 0,
+ label: 'aggregation interval',
+ name: 'agg_interval',
+ auto_min: '1m',
+ auto: true,
+ query: '1m,10m,1h,1d,7d',
+ type: 'interval',
+ },
+ )
+ .addPanel(grafana.row.new(title='General'), gridPos={ x: 0, y: 0, w: 0, h: 0 },)
+ .addPanel(giteaStatsPanel, gridPos={ x: 0, y: 0, w: 16, h: 4 })
+ .addPanel(giteaUptimePanel, gridPos={ x: 16, y: 0, w: 8, h: 4 })
+ .addPanel(giteaMemoryPanel, gridPos={ x: 0, y: 4, w: 8, h: 6 })
+ .addPanel(giteaCpuPanel, gridPos={ x: 8, y: 4, w: 8, h: 6 })
+ .addPanel(giteaFileDescriptorsPanel, gridPos={ x: 16, y: 4, w: 8, h: 6 })
+ .addPanel(grafana.row.new(title='Changes', collapse=false), gridPos={ x: 0, y: 10, w: 24, h: 8 })
+ .addPanel(giteaChangesPanelTotal, gridPos={ x: 0, y: 12, w: 6, h: 8 })
+ + // use patching instead of .addPanel() to keep static ids
+ {
+ panels+: std.flattenArrays([
+ [
+ giteaChangesPanelAll { gridPos: { x: 6, y: 12, w: 18, h: 8 } },
+ ],
+ if $._config.showIssuesByRepository then
+ [
+ giteaChangesByRepositoriesTotal { gridPos: { x: 0, y: 20, w: 6, h: 8 } },
+ giteaChangesByRepositories { gridPos: { x: 6, y: 20, w: 18, h: 8 } },
+ ] else [],
+ if $._config.showIssuesByLabel then
+ [
+ giteaChangesByLabelTotal { gridPos: { x: 0, y: 28, w: 6, h: 8 } },
+ giteaChangesByLabel { gridPos: { x: 6, y: 28, w: 18, h: 8 } },
+ ] else [],
+ ]),
+ },
+ },
+}
diff --git a/contrib/gitea-monitoring-mixin/jsonnetfile.json b/contrib/gitea-monitoring-mixin/jsonnetfile.json
new file mode 100644
index 0000000..5e9bae2
--- /dev/null
+++ b/contrib/gitea-monitoring-mixin/jsonnetfile.json
@@ -0,0 +1,15 @@
+{
+ "version": 1,
+ "dependencies": [
+ {
+ "source": {
+ "git": {
+ "remote": "https://github.com/grafana/grafonnet-lib.git",
+ "subdir": "grafonnet"
+ }
+ },
+ "version": "master"
+ }
+ ],
+ "legacyImports": false
+ } \ No newline at end of file
diff --git a/contrib/gitea-monitoring-mixin/jsonnetfile.lock.json b/contrib/gitea-monitoring-mixin/jsonnetfile.lock.json
new file mode 100644
index 0000000..4804382
--- /dev/null
+++ b/contrib/gitea-monitoring-mixin/jsonnetfile.lock.json
@@ -0,0 +1,16 @@
+{
+ "version": 1,
+ "dependencies": [
+ {
+ "source": {
+ "git": {
+ "remote": "https://github.com/grafana/grafonnet-lib.git",
+ "subdir": "grafonnet"
+ }
+ },
+ "version": "a1d61cce1da59c71409b99b5c7568511fec661ea",
+ "sum": "342u++/7rViR/zj2jeJOjshzglkZ1SY+hFNuyCBFMdc="
+ }
+ ],
+ "legacyImports": false
+}
diff --git a/contrib/gitea-monitoring-mixin/lib/alerts.jsonnet b/contrib/gitea-monitoring-mixin/lib/alerts.jsonnet
new file mode 100644
index 0000000..d396a38
--- /dev/null
+++ b/contrib/gitea-monitoring-mixin/lib/alerts.jsonnet
@@ -0,0 +1 @@
+std.manifestYamlDoc((import '../mixin.libsonnet').prometheusAlerts)
diff --git a/contrib/gitea-monitoring-mixin/lib/dashboards.jsonnet b/contrib/gitea-monitoring-mixin/lib/dashboards.jsonnet
new file mode 100644
index 0000000..dadaebe
--- /dev/null
+++ b/contrib/gitea-monitoring-mixin/lib/dashboards.jsonnet
@@ -0,0 +1,6 @@
+local dashboards = (import '../mixin.libsonnet').grafanaDashboards;
+
+{
+ [name]: dashboards[name]
+ for name in std.objectFields(dashboards)
+}
diff --git a/contrib/gitea-monitoring-mixin/lib/rules.jsonnet b/contrib/gitea-monitoring-mixin/lib/rules.jsonnet
new file mode 100644
index 0000000..2d7fa91
--- /dev/null
+++ b/contrib/gitea-monitoring-mixin/lib/rules.jsonnet
@@ -0,0 +1 @@
+std.manifestYamlDoc((import '../mixin.libsonnet').prometheusRules)
diff --git a/contrib/gitea-monitoring-mixin/mixin.libsonnet b/contrib/gitea-monitoring-mixin/mixin.libsonnet
new file mode 100644
index 0000000..bb56a6c
--- /dev/null
+++ b/contrib/gitea-monitoring-mixin/mixin.libsonnet
@@ -0,0 +1,2 @@
+(import 'dashboards/dashboards.libsonnet') +
+(import 'config.libsonnet')
diff --git a/contrib/ide/README.md b/contrib/ide/README.md
new file mode 100644
index 0000000..a9abfa3
--- /dev/null
+++ b/contrib/ide/README.md
@@ -0,0 +1,12 @@
+# IDE and code editor configuration
+
+## Table of Contents
+- [IDE and code editor configuration](#ide-and-code-editor-configuration)
+ - [Microsoft Visual Studio Code](#microsoft-visual-studio-code)
+
+## Microsoft Visual Studio Code
+Download Microsoft Visual Studio Code at https://code.visualstudio.com/ and follow instructions at https://code.visualstudio.com/docs/languages/go to setup Go extension for it.
+
+Create new directory `.vscode` in Gitea root folder and copy contents of folder [contrib/ide/vscode](vscode/) to it. You can now use `Ctrl`+`Shift`+`B` to build gitea executable and `F5` to run it in debug mode.
+
+Supported on Debian, Ubuntu, Red Hat, Fedora, SUSE Linux, MacOS and Microsoft Windows.
diff --git a/contrib/ide/vscode/launch.json b/contrib/ide/vscode/launch.json
new file mode 100644
index 0000000..b80b826
--- /dev/null
+++ b/contrib/ide/vscode/launch.json
@@ -0,0 +1,31 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Launch",
+ "type": "go",
+ "request": "launch",
+ "mode": "debug",
+ "buildFlags": "",
+ "program": "${workspaceRoot}/main.go",
+ "env": {
+ "GITEA_WORK_DIR": "${workspaceRoot}",
+ },
+ "args": ["web"],
+ "showLog": true
+ },
+ {
+ "name": "Launch (with SQLite3)",
+ "type": "go",
+ "request": "launch",
+ "mode": "debug",
+ "buildFlags": "-tags='sqlite sqlite_unlock_notify'",
+ "program": "${workspaceRoot}/main.go",
+ "env": {
+ "GITEA_WORK_DIR": "${workspaceRoot}",
+ },
+ "args": ["web"],
+ "showLog": true
+ }
+ ]
+}
diff --git a/contrib/ide/vscode/settings.json b/contrib/ide/vscode/settings.json
new file mode 100644
index 0000000..2ec666f
--- /dev/null
+++ b/contrib/ide/vscode/settings.json
@@ -0,0 +1,4 @@
+{
+ "go.buildTags": "sqlite,sqlite_unlock_notify",
+ "go.testFlags": ["-v"]
+}
diff --git a/contrib/ide/vscode/tasks.json b/contrib/ide/vscode/tasks.json
new file mode 100644
index 0000000..e35ae30
--- /dev/null
+++ b/contrib/ide/vscode/tasks.json
@@ -0,0 +1,49 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "Build",
+ "type": "shell",
+ "command": "go",
+ "group": "build",
+ "presentation": {
+ "echo": true,
+ "reveal": "always",
+ "focus": false,
+ "panel": "shared"
+ },
+ "linux": {
+ "args": ["build", "-o", "gitea", "${workspaceRoot}/main.go" ]
+ },
+ "osx": {
+ "args": ["build", "-o", "gitea", "${workspaceRoot}/main.go" ]
+ },
+ "windows": {
+ "args": ["build", "-o", "gitea.exe", "\"${workspaceRoot}\\main.go\""]
+ },
+ "problemMatcher": ["$go"]
+ },
+ {
+ "label": "Build (with SQLite3)",
+ "type": "shell",
+ "command": "go",
+ "group": "build",
+ "presentation": {
+ "echo": true,
+ "reveal": "always",
+ "focus": false,
+ "panel": "shared"
+ },
+ "linux": {
+ "args": ["build", "-tags=\"sqlite sqlite_unlock_notify\"", "-o", "gitea", "${workspaceRoot}/main.go"]
+ },
+ "osx": {
+ "args": ["build", "-tags=\"sqlite sqlite_unlock_notify\"", "-o", "gitea", "${workspaceRoot}/main.go"]
+ },
+ "windows": {
+ "args": ["build", "-tags=\"sqlite sqlite_unlock_notify\"", "-o", "gitea.exe", "\"${workspaceRoot}\\main.go\""]
+ },
+ "problemMatcher": ["$go"]
+ }
+ ]
+}
diff --git a/contrib/init/centos/gitea b/contrib/init/centos/gitea
new file mode 100644
index 0000000..4f0d0d9
--- /dev/null
+++ b/contrib/init/centos/gitea
@@ -0,0 +1,93 @@
+#!/bin/sh
+#
+# /etc/rc.d/init.d/gitea
+#
+# Runs the Gitea Git with a cup of tea.
+#
+#
+# chkconfig: - 85 15
+#
+
+### BEGIN INIT INFO
+# Provides: gitea
+# Required-Start: $remote_fs $syslog
+# Required-Stop: $remote_fs $syslog
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: Start gitea at boot time.
+# Description: Control gitea.
+### END INIT INFO
+
+# Source function library.
+. /etc/init.d/functions
+
+# Default values
+
+NAME=gitea
+GITEA_HOME=/var/lib/${NAME}
+GITEA_PATH=/usr/local/bin/${NAME}
+GITEA_USER=git
+SERVICENAME="Gitea - Git with a cup of tea"
+LOCKFILE=/var/lock/subsys/gitea
+LOGPATH=${GITEA_HOME}/log
+LOGFILE=${LOGPATH}/gitea.log
+RETVAL=0
+
+# Read configuration from /etc/sysconfig/gitea to override defaults
+[ -r /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME
+
+# Don't do anything if nothing is installed
+[ -x ${GITEA_PATH} ] || exit 0
+# exit if logpath dir is not created.
+[ -x ${LOGPATH} ] || exit 0
+
+DAEMON_OPTS="--check $NAME"
+
+# Set additional options, if any
+[ ! -z "$GITEA_USER" ] && DAEMON_OPTS="$DAEMON_OPTS --user=${GITEA_USER}"
+
+start() {
+ cd ${GITEA_HOME}
+ echo -n "Starting ${SERVICENAME}: "
+ daemon $DAEMON_OPTS "${GITEA_PATH} web -c /etc/${NAME}/app.ini > ${LOGFILE} 2>&1 &"
+ RETVAL=$?
+ echo
+ [ $RETVAL = 0 ] && touch ${LOCKFILE}
+
+ return $RETVAL
+}
+
+stop() {
+ cd ${GITEA_HOME}
+ echo -n "Shutting down ${SERVICENAME}: "
+ killproc ${NAME}
+ RETVAL=$?
+ echo
+ [ $RETVAL = 0 ] && rm -f ${LOCKFILE}
+}
+
+case "$1" in
+ start)
+ status ${NAME} > /dev/null 2>&1 && exit 0
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ status)
+ status ${NAME}
+ ;;
+ restart)
+ stop
+ start
+ ;;
+ reload)
+ stop
+ start
+ ;;
+ *)
+ echo "Usage: ${NAME} {start|stop|status|restart}"
+ exit 1
+ ;;
+esac
+exit $RETVAL
diff --git a/contrib/init/debian/gitea b/contrib/init/debian/gitea
new file mode 100644
index 0000000..2246309
--- /dev/null
+++ b/contrib/init/debian/gitea
@@ -0,0 +1,89 @@
+#!/bin/sh
+### BEGIN INIT INFO
+# Provides: gitea
+# Required-Start: $syslog $network
+# Required-Stop: $syslog
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: A self-hosted Git service written in Go.
+# Description: A self-hosted Git service written in Go.
+### END INIT INFO
+
+# Author: Danny Boisvert
+
+# Do NOT "set -e"
+
+# PATH should only include /usr/* if it runs after the mountnfs.sh script
+PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
+DESC="Gitea - Git with a cup of tea"
+NAME=gitea
+SERVICEVERBOSE=yes
+PIDFILE=/run/$NAME.pid
+SCRIPTNAME=/etc/init.d/$NAME
+WORKINGDIR=/var/lib/$NAME
+DAEMON=/usr/local/bin/$NAME
+DAEMON_ARGS="web -c /etc/$NAME/app.ini"
+USER=git
+USERBIND=""
+# If you want to bind Gitea to a port below 1024 uncomment
+# the line below
+#USERBIND="setcap cap_net_bind_service=+ep"
+STOP_SCHEDULE="${STOP_SCHEDULE:-QUIT/5/TERM/1/KILL/5}"
+
+# Read configuration variable file if it is present
+[ -r /etc/default/$NAME ] && . /etc/default/$NAME
+
+# Exit if the package is not installed
+[ -x "$DAEMON" ] || exit 0
+
+do_start()
+{
+ $USERBIND $DAEMON
+ sh -c "USER=$USER HOME=/home/$USER GITEA_WORK_DIR=$WORKINGDIR start-stop-daemon --start --quiet --pidfile $PIDFILE --make-pidfile \\
+ --background --chdir $WORKINGDIR --chuid $USER \\
+ --exec $DAEMON -- $DAEMON_ARGS"
+}
+
+do_stop()
+{
+ start-stop-daemon --stop --quiet --retry=$STOP_SCHEDULE --pidfile $PIDFILE --name $NAME --oknodo
+ rm -f $PIDFILE
+}
+
+do_status()
+{
+ if [ -f $PIDFILE ]; then
+ if kill -0 $(cat "$PIDFILE"); then
+ echo "$NAME is running, PID is $(cat $PIDFILE)"
+ else
+ echo "$NAME process is dead, but pidfile exists"
+ fi
+ else
+ echo "$NAME is not running"
+ fi
+}
+
+case "$1" in
+ start)
+ echo "Starting $DESC" "$NAME"
+ do_start
+ ;;
+ stop)
+ echo "Stopping $DESC" "$NAME"
+ do_stop
+ ;;
+ status)
+ do_status
+ ;;
+ restart)
+ echo "Restarting $DESC" "$NAME"
+ do_stop
+ do_start
+ ;;
+ *)
+ echo "Usage: $SCRIPTNAME {start|stop|status|restart}" >&2
+ exit 2
+ ;;
+esac
+
+exit 0
diff --git a/contrib/init/freebsd/gitea b/contrib/init/freebsd/gitea
new file mode 100644
index 0000000..2c034ce
--- /dev/null
+++ b/contrib/init/freebsd/gitea
@@ -0,0 +1,48 @@
+#!/bin/sh
+#
+# $FreeBSD$
+#
+# PROVIDE: gitea
+# REQUIRE: NETWORKING SYSLOG
+# KEYWORD: shutdown
+#
+# Add the following lines to /etc/rc.conf to enable gitea:
+#
+#gitea_enable="YES"
+
+. /etc/rc.subr
+
+name="gitea"
+rcvar="gitea_enable"
+
+load_rc_config $name
+
+: ${gitea_user:="git"}
+: ${gitea_enable:="NO"}
+: ${gitea_directory:="/var/lib/gitea"}
+
+command="/usr/local/bin/gitea web -c /etc/gitea/app.ini"
+procname="$(echo $command |cut -d' ' -f1)"
+
+pidfile="${gitea_directory}/${name}.pid"
+
+start_cmd="${name}_start"
+stop_cmd="${name}_stop"
+
+gitea_start() {
+ cd ${gitea_directory}
+ export USER=${gitea_user}
+ export HOME=/usr/home/${gitea_user}
+ export GITEA_WORK_DIR=${gitea_directory}
+ /usr/sbin/daemon -f -u ${gitea_user} -p ${pidfile} $command
+}
+
+gitea_stop() {
+ if [ ! -f $pidfile ]; then
+ echo "GITEA PID File not found. Maybe GITEA is not running?"
+ else
+ kill $(cat $pidfile)
+ fi
+}
+
+run_rc_command "$1"
diff --git a/contrib/init/gentoo/gitea b/contrib/init/gentoo/gitea
new file mode 100644
index 0000000..db904e7
--- /dev/null
+++ b/contrib/init/gentoo/gitea
@@ -0,0 +1,44 @@
+#!/sbin/openrc-run
+
+DIR=/var/lib/gitea
+USER=git
+HOME=/home/${USER}
+GITEA_WORK_DIR=${DIR}
+EXECUTABLE=/usr/local/bin/gitea
+
+export USER
+export HOME
+export GITEA_WORK_DIR
+
+name=$RC_SVCNAME
+cfgfile="/etc/$RC_SVCNAME/app.ini"
+command="${EXECUTABLE}"
+command_user="${USER}"
+command_args="web -c /etc/$RC_SVCNAME/app.ini"
+command_background="yes"
+pidfile="/run/$RC_SVCNAME/$RC_SVCNAME.pid"
+start_stop_daemon_args="--user ${USER} --chdir ${DIR}"
+
+depend()
+{
+ need net
+ ###
+ # Don't forget to add the database service requirements
+ ###
+ #after postgresql
+ #after mysql
+ #after mariadb
+ #after memcached
+ #after redis
+}
+
+start_pre()
+{
+ checkpath --directory --owner $command_user:$command_user --mode 0750 \
+ /run/$RC_SVCNAME /var/log/$RC_SVCNAME
+ ##
+ # If you want to bind Gitea to a port below 1024, uncomment
+ # the value below
+ ##
+ #setcap cap_net_bind_service=+ep "${EXECUTABLE}"
+}
diff --git a/contrib/init/openbsd/gitea b/contrib/init/openbsd/gitea
new file mode 100755
index 0000000..89d4f68
--- /dev/null
+++ b/contrib/init/openbsd/gitea
@@ -0,0 +1,19 @@
+#!/bin/sh
+#
+# $OpenBSD$
+
+daemon="/usr/local/bin/gitea"
+daemon_user="git"
+daemon_flags="web -c /etc/gitea/app.ini"
+
+gitea_directory="/var/lib/gitea"
+
+rc_bg=YES
+
+. /etc/rc.d/rc.subr
+
+rc_start() {
+ ${rcexec} "cd ${gitea_directory}; ${daemon} ${daemon_flags} ${_bg}"
+}
+
+rc_cmd $1
diff --git a/contrib/init/openwrt/gitea b/contrib/init/openwrt/gitea
new file mode 100644
index 0000000..67b4495
--- /dev/null
+++ b/contrib/init/openwrt/gitea
@@ -0,0 +1,35 @@
+#!/bin/sh /etc/rc.common
+
+USE_PROCD=1
+
+# PROCD_DEBUG=1
+
+START=90
+STOP=10
+
+PROG=/opt/gitea/gitea
+GITEA_WORK_DIR=/opt/gitea
+CONF_FILE=$GITEA_WORK_DIR/app.ini
+
+start_service(){
+ procd_open_instance gitea
+ procd_set_param env GITEA_WORK_DIR=$GITEA_WORK_DIR
+ procd_set_param env HOME=$GITEA_WORK_DIR
+ procd_set_param command $PROG web -c $CONF_FILE
+ procd_set_param file $CONF_FILE
+ procd_set_param user git
+ procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5} # respawn automatically if something died, be careful if you have an alternative process supervisor
+ procd_close_instance
+}
+
+start(){
+ service_start $PROG
+}
+
+stop(){
+ service_stop $PROG
+}
+
+reload(){
+ service_reload $PROG
+}
diff --git a/contrib/init/sunos/gitea.xml b/contrib/init/sunos/gitea.xml
new file mode 100644
index 0000000..4b8cc3a
--- /dev/null
+++ b/contrib/init/sunos/gitea.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<service_bundle type="manifest" name="export">
+ <service name="gitea" type="service" version="1">
+ <create_default_instance enabled="false"/>
+
+ <dependency name="network" grouping="require_all" restart_on="refresh" type="service">
+ <service_fmri value="svc:/milestone/network:default"/>
+ </dependency>
+
+ <dependency name="filesystem" grouping="require_all" restart_on="refresh" type="service">
+ <service_fmri value="svc:/system/filesystem/local"/>
+ </dependency>
+
+ <exec_method
+ type="method"
+ name="start"
+ exec="/opt/local/bin/gitea web"
+ timeout_seconds="60">
+ <method_context>
+ <method_credential user="git" group="git" />
+ <method_environment>
+ <envvar name='GITEA_WORK_DIR' value='/opt/local/share/gitea'/>
+ <envvar name='GITEA_CUSTOM' value='/opt/local/etc/gitea'/>
+ <envvar name='HOME' value='/var/db/gitea'/>
+ <envvar name='PATH' value='/opt/local/bin:${PATH}'/>
+ <envvar name='USER' value='git'/>
+ </method_environment>
+ </method_context>
+ </exec_method>
+ <exec_method type="method" name="stop" exec=":kill" timeout_seconds="60"/>
+
+ <property_group name="application" type="application"></property_group>
+ <property_group name="startd" type="framework">
+ <propval name="duration" type="astring" value="child"/>
+ <propval name="ignore_error" type="astring" value="core,signal"/>
+ </property_group>
+
+ <template>
+ <common_name>
+ <loctext xml:lang="C">A painless, self-hosted Git service</loctext>
+ </common_name>
+ </template>
+
+ </service>
+</service_bundle>
diff --git a/contrib/init/suse/gitea b/contrib/init/suse/gitea
new file mode 100644
index 0000000..6391bed
--- /dev/null
+++ b/contrib/init/suse/gitea
@@ -0,0 +1,115 @@
+#!/bin/sh
+#
+# /etc/init.d/gitea
+#
+# Runs the Gitea Git with a cup of tea.
+#
+
+### BEGIN INIT INFO
+# Provides: gitea
+# Required-Start: $remote_fs
+# Required-Stop: $remote_fs
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: Start gitea at boot time.
+# Description: Control gitea.
+### END INIT INFO
+
+# Default values
+
+NAME=gitea
+GITEA_HOME=/var/lib/$NAME
+GITEA_PATH=/usr/local/bin/$NAME
+GITEA_USER=git
+SERVICENAME="Gitea - Git with a cup of tea"
+LOCKFILE=/var/lock/subsys/gitea
+LOGPATH=${GITEA_HOME}/log
+LOGFILE=${LOGPATH}/error.log
+# gitea creates its own gitea.log from stdout
+RETVAL=0
+
+# Read configuration from /etc/sysconfig/gitea to override defaults
+[ -r /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME
+
+# Don't do anything if nothing is installed
+test -x ${GITEA_PATH} || { echo "$NAME not installed";
+ if [ "$1" = "stop" ]; then exit 0;
+ else exit 5; fi; }
+
+# exit if logpath dir is not created.
+test -r ${LOGPATH} || { echo "$LOGPATH not existing";
+ if [ "$1" = "stop" ]; then exit 0;
+ else exit 6; fi; }
+
+# Source function library.
+. /etc/rc.status
+
+# Reset status of this service
+rc_reset
+
+
+case "$1" in
+ start)
+ echo -n "Starting ${SERVICENAME} "
+
+ # As we can't use startproc, we have to check ourselves if the service is already running
+ /sbin/checkproc ${GITEA_PATH}
+ if [ $? -eq 0 ]; then
+ # return skipped as service is already running
+ (exit 5)
+ else
+ su - ${GITEA_USER} -c "USER=${GITEA_USER} GITEA_WORK_DIR=${GITEA_HOME} ${GITEA_PATH} web -c /etc/${NAME}/app.ini 2>&1 >>${LOGFILE} &"
+ fi
+
+ # Remember status and be verbose
+ rc_status -v
+ ;;
+
+ stop)
+ echo -n "Shutting down ${SERVICENAME} "
+
+ ## Stop daemon with killproc(8) and if this fails
+ ## killproc sets the return value according to LSB.
+ /sbin/killproc ${GITEA_PATH}
+
+ # Remember status and be verbose
+ rc_status -v
+ ;;
+
+ restart)
+ ## Stop the service and regardless of whether it was
+ ## running or not, start it again.
+ $0 stop
+ $0 start
+
+ # Remember status and be quiet
+ rc_status
+ ;;
+
+ status)
+ echo -n "Checking for ${SERVICENAME} "
+ ## Check status with checkproc(8), if process is running
+ ## checkproc will return with exit status 0.
+
+ # Return value is slightly different for the status command:
+ # 0 - service up and running
+ # 1 - service dead, but /run/ pid file exists
+ # 2 - service dead, but /var/lock/ lock file exists
+ # 3 - service not running (unused)
+ # 4 - service status unknown :-(
+ # 5--199 reserved (5--99 LSB, 100--149 distro, 150--199 appl.)
+
+ # NOTE: checkproc returns LSB compliant status values.
+ /sbin/checkproc ${GITEA_PATH}
+ # NOTE: rc_status knows that we called this init script with
+ # "status" option and adapts its messages accordingly.
+ rc_status -v
+ ;;
+
+ *)
+ echo "Usage: $0 {start|stop|status|restart}"
+ exit 1
+ ;;
+
+esac
+rc_exit
diff --git a/contrib/init/ubuntu/gitea b/contrib/init/ubuntu/gitea
new file mode 100644
index 0000000..da56b6e
--- /dev/null
+++ b/contrib/init/ubuntu/gitea
@@ -0,0 +1,84 @@
+#!/bin/sh
+### BEGIN INIT INFO
+# Provides: gitea
+# Required-Start: $syslog $network
+# Required-Stop: $syslog
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: A self-hosted Git service written in Go.
+# Description: A self-hosted Git service written in Go.
+### END INIT INFO
+
+# Do NOT "set -e"
+
+# PATH should only include /usr/* if it runs after the mountnfs.sh script
+PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
+DESC="Gitea - Git with a cup of tea"
+NAME=gitea
+SERVICEVERBOSE=yes
+PIDFILE=/run/$NAME.pid
+SCRIPTNAME=/etc/init.d/$NAME
+WORKINGDIR=/var/lib/$NAME
+DAEMON=/usr/local/bin/$NAME
+DAEMON_ARGS="web -c /etc/$NAME/app.ini"
+USER=git
+STOP_SCHEDULE="${STOP_SCHEDULE:-QUIT/5/TERM/1/KILL/5}"
+
+# Read configuration variable file if it is present
+[ -r /etc/default/$NAME ] && . /etc/default/$NAME
+
+# Exit if the package is not installed
+[ -x "$DAEMON" ] || exit 0
+
+do_start()
+{
+ GITEA_ENVS="USER=$USER GITEA_WORK_DIR=$WORKINGDIR HOME=/home/$USER"
+ GITEA_EXEC="$DAEMON -- $DAEMON_ARGS"
+ sh -c "start-stop-daemon --start --quiet --pidfile $PIDFILE --make-pidfile \\
+ --background --chdir $WORKINGDIR --chuid $USER \\
+ --exec /bin/bash -- -c '/usr/bin/env $GITEA_ENVS $GITEA_EXEC'"
+}
+
+do_stop()
+{
+ start-stop-daemon --stop --quiet --retry=$STOP_SCHEDULE --pidfile $PIDFILE --name $NAME --oknodo
+ rm -f $PIDFILE
+}
+
+do_status()
+{
+ if [ -f $PIDFILE ]; then
+ if kill -0 $(cat "$PIDFILE"); then
+ echo "$NAME is running, PID is $(cat $PIDFILE)"
+ else
+ echo "$NAME process is dead, but pidfile exists"
+ fi
+ else
+ echo "$NAME is not running"
+ fi
+}
+
+case "$1" in
+ start)
+ echo "Starting $DESC" "$NAME"
+ do_start
+ ;;
+ stop)
+ echo "Stopping $DESC" "$NAME"
+ do_stop
+ ;;
+ status)
+ do_status
+ ;;
+ restart)
+ echo "Restarting $DESC" "$NAME"
+ do_stop
+ do_start
+ ;;
+ *)
+ echo "Usage: $SCRIPTNAME {start|stop|status|restart}" >&2
+ exit 2
+ ;;
+esac
+
+exit 0
diff --git a/contrib/launchd/io.gitea.web.plist b/contrib/launchd/io.gitea.web.plist
new file mode 100644
index 0000000..43ec612
--- /dev/null
+++ b/contrib/launchd/io.gitea.web.plist
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>Label</key>
+ <string>io.gitea.web</string>
+ <!-- assumes Gitea is running under 'git' account -->
+ <!-- modify below to reflect your settings -->
+ <key>UserName</key>
+ <string>git</string>
+ <key>GroupName</key>
+ <string>git</string>
+ <key>ProgramArguments</key>
+ <array>
+ <!-- assumes Gitea is installed in /Users/git/gitea -->
+ <!-- modify below to reflect your settings -->
+ <string>/Users/git/gitea/gitea</string>
+ <string>web</string>
+ </array>
+ <key>RunAtLoad</key>
+ <true/>
+ <key>KeepAlive</key>
+ <true/>
+ <!-- assumes Gitea is installed in /Users/git/gitea -->
+ <!-- modify below to reflect your settings -->
+ <key>WorkingDirectory</key>
+ <string>/Users/git/gitea/</string>
+ <key>StandardOutPath</key>
+ <string>/Users/git/gitea/log/stdout.log</string>
+ <key>StandardErrorPath</key>
+ <string>/Users/git/gitea/log/stderr.log</string>
+ <!-- default 256 is too low for Gitea needs using parallel pipes -->
+ <key>SoftResourceLimits</key>
+ <dict>
+ <key>NumberOfFiles</key>
+ <integer>8192</integer>
+ </dict>
+ </dict>
+</plist>
diff --git a/contrib/legal/privacy.html.sample b/contrib/legal/privacy.html.sample
new file mode 100644
index 0000000..adb3ea7
--- /dev/null
+++ b/contrib/legal/privacy.html.sample
@@ -0,0 +1,196 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Privacy Policy</title>
+ </head>
+
+ <body>
+ <h1>Privacy Policy</h1>
+
+ <h4>Last updated: January 29, 2020</h4>
+
+ <h2>Who We Are?</h2>
+
+ <p>Your Gitea Instance</p>
+
+ <h2>What Personal Data We Collect?</h2>
+
+ <p>We collect following personal data (collectively called User Personal Information):</p>
+
+ <ol>
+ <li>Registration information (username, email, password, etc.)</li>
+ <li>Profile information for your Account (such as your full name, biography, website, gpg key, and location.)</li>
+ <li>Usage information (pages you view, your IP address, referring site, session information, and request date and time.)</li>
+ <li>Device information (its IP address, client application information, language preference, operating system and application version, device type, ID, model and manufacturer.)</li>
+ <li>Git data that you upload to a repository</li>
+ <li>Cookies and Similar Technologies</li>
+ </ol>
+
+ <p>We may also collect User Personal Information from third-parties (vendors, partners, or affiliates). We don't purchase them from third-party data brokers, though.</p>
+
+ <p>However, we don't intentionally collect sensitive information (such as racial or ethnic origin, political affiliations, religious/philosophical beliefs, biometric data, etc.)</p>
+<!--If you choose to store any of such data on our servers, you are responsible for complying with any regulations regarding them.-->
+
+ <h2>How We Share Information We Collect?</h2>
+
+ <p>We may share your User Personal Information with third-parties under following circumstances:</p>
+
+ <h3>With your Consent</h3>
+
+ <p>We share your User Personal Information, if you consent, after letting you know what information will be shared, with whom, and why. For example, if you allow third party applications to access your Account using <a href="https://docs.gitea.com/development/oauth2-provider">OAuth2 providers</a>, we share all information associated with your Account, including private repos and organizations. You may also direct us through your action on Your Gitea Instance to share your User Personal Information, such as when joining an Organization.</p>
+
+ <h3>With Service Providers</h3>
+
+ <p>We share User Personal Information with a limited number of service providers who process it on our behalf to provide or improve our Service, and who have agreed to privacy restrictions similar to the ones in our Privacy Statement by signing data protection agreements or making similar commitments. Our service providers perform payment processing, customer support ticketing, network data transmission, security, and other similar services. While Your Gitea Instance processes all User Personal Information in the (country/state where Gitea is deployed), our service providers may process data outside of (country/state where Gitea is deployed), the United States or the European Union.</p>
+
+ <h3>For Security Purposes</h3>
+
+ <p>If you are a member of an Organization, Your Gitea Instance may share your username, Usage Information, and Device Information associated with that Organization with an owner and/or administrator of the Organization who has agreed to the Corporate Terms of Service or applicable customer agreements, to the extent that such information is provided only to investigate or respond to a security incident that affects or compromises the security of that particular Organization.</p>
+
+ <h3>For Legal Disclosure</h3>
+
+ <p>Your Gitea Instance strives for transparency in complying with legal process and legal obligations. Unless prevented from doing so by law or court order, or in rare, exigent circumstances, we make a reasonable effort to notify users of any legally compelled or required disclosure of their information. Your Gitea Instance may disclose User Personal Information or other information we collect about you to law enforcement if required in response to a valid subpoena, court order, search warrant, a similar government order, or when we believe in good faith that disclosure is necessary to comply with our legal obligations, to protect our property or rights, or those of third parties or the public at large.</p>
+
+ <h3>Change in Control or Sale</h3>
+
+ <p>We may share User Personal Information if we are involved in a merger, sale, or acquisition of corporate entities or business units. If any such change of ownership happens, we will ensure that it is under terms that preserve the confidentiality of User Personal Information, and we will notify you on our Website or by email before any transfer of your User Personal Information. The organization receiving any User Personal Information will have to honor any promises we made in our Privacy Statement or Terms of Service.</p>
+
+ <h3>Aggregate, Non-Personally Identifying Information</h3>
+
+ <p>We share certain aggregated, non-personally identifying information with others about how our users, collectively, use Your Gitea Instance, or how our users respond to our other offerings, such as our conferences or events. For example, we may compile statistics on the open source activity across Your Gitea Instance.</p>
+
+ <p>We <b>don't</b> sell your User Personal Information for monetary or other consideration. </p>
+
+ <h2>How We Use Your Information?</h2>
+
+ <p>We may use your information for following purposes:</p>
+
+ <ol>
+ <li>We use your Registration Information to create your account, and to provide you the Service.</li>
+ <li>We use your User Personal Information, specifically your username, to identify you on Your Gitea Instance.</li>
+ <li>We use your Profile Information to fill out your Account profile and to share that profile with other users if you ask us to.</li>
+ <li>We use your email address to communicate with you, if you've said that's okay, and only for the reasons you’ve said that’s okay.</li>
+ <li>We use User Personal Information and other data to make recommendations for you, such as to suggest projects you may want to follow or contribute to. We learn from your public behavior on Your Gitea Instance—such as the projects you star—to determine your coding interests, and we recommend similar projects. These recommendations are automated decisions, but they have no legal impact on your rights.</li>
+ <li>We use Usage Information and Device Information to better understand how our Users use Your Gitea Instance and to improve our Website and Service.</li>
+ <li>We may use your User Personal Information if it is necessary for security purposes or to investigate possible fraud or attempts to harm Your Gitea Instance or our Users.</li>
+ <li>We may use your User Personal Information to comply with our legal obligations, protect our intellectual property, and enforce our Terms of Service.</li>
+
+ <li>We limit our use of your User Personal Information to the purposes listed in this Privacy Statement. If we need to use your User Personal Information for other purposes, we will ask your permission first. You can always see what information we have, how we're using it, and what permissions you have given us in your user profile.</li>
+ </ol>
+
+ <h2>How Your Gitea Instance Secures Your Information?</h2>
+
+ <p>Your Gitea Instance takes all measures reasonably necessary to protect User Personal Information from unauthorized access, alteration, or destruction; maintain data accuracy; and help ensure the appropriate use of User Personal Information.</p>
+
+ <p>To the extent above, we enforce a written security information program, which:</p>
+
+ <ul>
+ <li>aligns with industry recognized frameworks;</li>
+ <li>includes security safeguards reasonably designed to protect the confidentiality, integrity, availability, and resilience of our Users' data;</li>
+ <li>is appropriate to the nature, size, and complexity of Your Gitea Instance’s business operations;</li>
+ <li>includes incident response and data breach notification processes; and</li>
+ <li>complies with applicable information security-related laws and regulations in the geographic regions where Your Gitea Instance does business.</li>
+ </ul>
+
+ <p>In the event of a data breach that affects your User Personal Information, we will act promptly to mitigate the impact of a breach and notify any affected Users without undue delay.</p>
+
+ <p>Transmission of data on Your Gitea Instance is encrypted using SSH, HTTPS (TLS), and git repository content is encrypted at rest. We host Your Gitea Instance at our hosting partner, which they provide data centers with high level of physical and network security.</p>
+
+ <p><b>Disclaimer:</b> No method of transmission, or method of electronic storage, is 100% secure, therefore, we cannot guarantee absolute security.</p>
+
+ <h2>Cookies and Tracking Usage</h2>
+
+ <h3>Cookies</h3>
+
+ <p>We uses cookies to make interactions with our service easy and meaningful. Cookies are small text files that websites often store on computer hard drives or mobile devices of visitors. We use cookies (and similar technologies, like HTML5 localStorage) to keep you logged in, remember your preferences, and provide information for future development of Your Gitea Instance. For security purposes, we use cookies to identify a device. By using our Website, you agree that we can place these types of cookies on your computer or device. If you disable your browser or device’s ability to accept these cookies, you will not be able to log in or use our services.</p>
+
+ <h3>Tracking and Analytics</h3>
+
+ <p>Out of the box, Gitea doesn't use third-party analytics. In case when we opt in to their usage, we do that to help us evaluate our Users' use of Your Gitea Instance, compile statistical reports on activity, and improve our content and Website performance. We only use these third-party analytics providers on certain areas of our Website, and all of them have signed data protection agreements with us that limit the type of User Personal Information they can collect and the purpose for which they can process the information. In addition, we may also deploy internal analytics software to provide similar functionality.</p>
+
+ <p>Some browsers have incorporated "Do Not Track" (DNT) features that can send a signal to the websites you visit indicating you do not wish to be tracked. Your Gitea Instance responds to browser DNT signals and follows the <a href="https://www.w3.org/TR/tracking-dnt/">W3C standard for responding to DNT signals</a>. If you have not enabled DNT on a browser that supports it, cookies on some parts of our Website will track your online browsing activity on other online services over time, though we do not permit third parties other than our analytics and service providers to track Your Gitea Instance Users' activity over time on Your Gitea Instance.</p>
+
+ <h2>Repository Contents</h2>
+
+ <p>Our employees do not access private repositories unless required to for security purposes, for support, to maintain integrity of the Service, or to comply with our legal obligations. While we don't generally search for content in your repositories, we may scan our servers and your content to detect tokens or security signatures, known malwares, or child exploitation imagery.</p>
+
+ <p>If your repository is public, anyone may view its contents. If you include private, confidential or Sensitive Personal Information, such as email addresses or passwords, in your public repository, that information may be indexed by search engines or used by third parties.</p>
+
+ <h2>Public Information</h2>
+
+ <p>Many of our services and feature are public-facing. If your content is public-facing, third parties may access and use it in compliance with our Terms of Service, such as by viewing your profile or repositories or pulling data via our API. We do not sell that content; it is yours. However, we do allow third parties, such as research organizations or archives, to compile public-facing Your Gitea Instance information. Other third parties, such as data brokers, have been known to scrape Your Gitea Instance and compile data as well.</p>
+
+ <p>Your User Personal Information associated with your content could be gathered by third parties in these compilations of Your Gitea Instance data. If you do not want your User Personal Information to appear in third parties’ compilations of Your Gitea Instance data, please do not make your User Personal Information publicly available and be sure to configure your email address to be private in your user profile and in your git commit settings.</p>
+
+ <p>If you would like to compile Your Gitea Instance data, you must comply with our Terms of Service regarding scraping and privacy, and you may only use any public-facing User Personal Information you gather for the purpose for which our user authorized it. For example, where a Your Gitea Instance user has made an email address public-facing for the purpose of identification and attribution, do not use that email address for commercial advertising. We expect you to reasonably secure any User Personal Information you have gathered from Your Gitea Instance, and to respond promptly to complaints, removal requests, and "do not contact" requests from Your Gitea Instance or Your Gitea Instance users.</p>
+
+ <p>In similar fashion, projects on Your Gitea Instance may include publicly available User Personal Information collected as part of the collaborative events.</p>
+
+ <h2>Organizations</h2>
+
+ <p>If you collaborate on or become a member of an Organization, then its Account owners may receive your User Personal Information. When you accept an invitation to an Organization, you will be notified of the types of information owners may be able to see. If you accept an invitation to an Organization with a verified domain, then the owners of that Organization will be able to see your full email address(es) within that Organization's verified domain(s).</p>
+
+ <p>Please note, Your Gitea Instance may share your username, Usage Information, and Device Information with the owner of the Organization you are a member of, to the extent that your User Personal Information is provided only to investigate or respond to a security incident that affects or compromises the security of that particular Organization.</p>
+
+ <p>If you collaborate with or become a member of an Account that has agreed to a Data Protection Addendum (DPA) to this Privacy Policy, then that DPA governs in the event of conflicts between this Privacy Policy and DPA with respect to your activity in the Account.</p>
+
+ <p>Please contact the Account owners for more information about how they might process your User Personal Information in their Organization and the ways for you to access, update, alter, or delete the User Personal Information stored in the Account.</p>
+
+ <h2>How You Can Access and Control the Information We Collect?</h2>
+
+ <p>If you're already a Your Gitea Instance user, you may access, update, alter, or delete your basic user information by editing your user profile. You can control the information we collect about you by limiting what information is in your profile, or by keeping your information current.</p>
+
+ <p>If Your Gitea Instance processes information about you, such as information receives from third parties, and you do not have an account, then you may, subject to applicable law, access, update, alter, delete, or object to the processing of your personal information by contacting our support.</p>
+
+ <h3>Data Portability</h3>
+
+ <p>As a Your Gitea Instance User, you can always take your data with you. You can clone your repositories to your computer, or you can <a href="https://docs.gitea.com/development/migrations-interfaces">perform migrations using the provided interfaces</a>, for example.</p>
+
+ <h3>Data Retention and Deletion of Data</h3>
+
+ <p>In general, Your Gitea Instance retains User Personal Information for as long as your account is active, or as needed to provide you service.</p>
+
+ <p>If you would like to cancel your account or delete your User Personal Information, you may do so in your user profile. We retain and use your information as necessary to comply with our legal obligations, resolve disputes, and enforce our agreements, but barring legal requirements, we will delete your full profile (within reason) within 90 days of your request. Feel free to contact our support to request erasure of the data we process on the basis of consent within 30 days.</p>
+
+ <p>After an account has been deleted, certain data, such as contributions to other Users' repositories and comments in others' issues, will remain. However, we will delete or de-identify your User Personal Information, including your username and email address, from the author field of issues, pull requests, and comments by associating them with a ghost user.</p>
+
+ <p>That said, the email address you have supplied via your Git commit settings will always be associated with your commits in the Git system. If you choose to make your email address private, you should also update your Git commit settings. We are unable to change or delete data in the Git commit history — the Git software is designed to maintain a record — but we do enable you to control what information you put in that record.</p>
+
+ <h2>Our Global Privacy Practices</h2>
+
+ <p>We store and process the information that we collect in the (country/state where Gitea is deployed) in accordance with this Privacy Statement though our service providers may store and process data outside the (country/state where Gitea is deployed). However, we understand that we have Users from different countries and regions with different privacy expectations, and we try to meet those needs even when the (country/state where Gitea is deployed) does not have the same privacy framework as other countries.</p>
+
+ <p>We provide a high standard of privacy protection—as described in this Privacy Statement—to all our users around the world, regardless of their country of origin or location, and we are proud of the levels of notice, choice, accountability, security, data integrity, access, and recourse we provide. We work hard to comply with the applicable data privacy laws wherever we do business, working with our Data Protection Officer as part of a cross-functional team that oversees our privacy compliance efforts. Additionally, if our vendors or affiliates have access to User Personal Information, they must sign agreements that require them to comply with our privacy policies and with applicable data privacy laws.</p>
+
+ <p>In particular:</p>
+
+ <ul>
+ <li>Your Gitea Instance provides clear methods of unambiguous, informed, specific, and freely given consent at the time of data collection, when we collect your User Personal Information using consent as a basis.</li>
+ <li>We collect only the minimum amount of User Personal Information necessary for our purposes, unless you choose to provide more. We encourage you to only give us the amount of data you are comfortable sharing.</li>
+ <li>We offer you simple methods of accessing, altering, or deleting the User Personal Information we have collected, where legally permitted.</li>
+ <li>We provide our Users notice, choice, accountability, security, and access regarding their User Personal Information, and we limit the purpose for processing it. We also provide our Users a method of recourse and enforcement. These are the Privacy Shield Principles, but they are also just good practices.</li>
+ </ul>
+
+ <h2>How We Communicate with You?</h2>
+
+ <p>We use your email address to communicate with you, if you've said that's okay, and only for the reasons you’ve said that’s okay. For example, if you contact our support with a request, we respond to you via email. You have a lot of control over how your email address is used and shared on and through Your Gitea instance. You may manage your communication preferences in your user profile.</p>
+
+ <p>By design, the Git version control system associates many actions with a User's email address, such as commit messages. We are not able to change many aspects of the Git system. If you would like your email address to remain private, even when you’re commenting on public repositories, you can create a private email address in your user profile. You should also update your local Git configuration to use your private email address. This will not change how we contact you, but it will affect how others see you.</p>
+
+ <p>Depending on your email settings, Your Gitea instance may occasionally send notification emails about changes in a repository you’re watching, new features, requests for feedback, important policy changes, or to offer customer support. We also send marketing emails, based on your choices and in accordance with applicable laws and regulations. There's an “unsubscribe” link located at the bottom of each of the marketing emails we send you. Note that you can opt out of any communications with us, except the important ones (like from our support and system emails).</p>
+
+ <p>Our emails may contain a pixel tag, which is a small, clear image that can tell us whether or not you have opened an email and what your IP address is. We use this pixel tag to make our email more effective for you and to make sure we’re not sending you unwanted email.</p>
+
+ <h2>Changes to this Privacy Policy</h2>
+
+ <p>Although most changes are likely to be minor, Your Gitea Instance may change our Privacy Statement from time to time. We will provide notification to Users of material changes to this Privacy Statement through our Website at least 30 days prior to the change taking effect by posting a notice on our home page or sending email to the primary email address specified in your account.</p>
+
+ <h2>Contact</h2>
+
+ <p>If you have any concerns about privacy, please contact us at <a href="mailto:privacy@your-gitea-instance">privacy@your-gitea-instance</a>. We will respond promptly, within 45 days.</p>
+
+ <h2>COPYING</h2>
+
+ <p>This document is licensed under CC0 Public Domain License. See <a href="https://creativecommons.org/publicdomain/zero/1.0/legalcode">full legal code here</a>.</p>
+ </body>
+</html>
diff --git a/contrib/legal/tos.html.sample b/contrib/legal/tos.html.sample
new file mode 100644
index 0000000..d390829
--- /dev/null
+++ b/contrib/legal/tos.html.sample
@@ -0,0 +1,245 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Terms of Service</title>
+ </head>
+
+ <body>
+ <h1>Terms of Service</h1>
+
+ <h4>Last updated: January 29, 2020</h4>
+
+ <p>Thank you for choosing Your Gitea Instance! Before you use it, please read this Terms of Service agreement carefully, which contains important contract between us and our users.</p>
+
+ <h2>Definitions</h2>
+
+ <ol>
+ <li>An "Account" represents your legal relationship with Your Gitea Instance. A “User Account” represents an individual User’s authorization to log in to and use the Service and serves as a User’s identity on Your Gitea Instance. “Organizations” are shared workspaces that may be associated with a single entity or with one or more Users where multiple Users can collaborate across many projects at once. A User Account can be a member of any number of Organizations.</li>
+
+ <li>The "Agreement" collectively refers to all terms, conditions, and notices referenced or contained in this document and other operating rules, policies (including Privacy Policy) and procedures that we may publish from time to time on this Website.</li>
+
+ <li>“Content” refers to content featured or displayed through the Website, including without limitation code, text, data, articles, images, photographs, graphics, software, applications, packages, designs, features, and other materials that are available on the Website or otherwise available through the Service. "Content" also includes Services. “User-Generated Content” is Content, written or otherwise, created or uploaded by our Users. "Your Content" is Content that you create or own.</li>
+
+ <li>"Your Gitea Instance", "We", and "Us" refers to Your Gitea Instance, as well as our affiliates, directors, subsidiaries, contractors, licensors, officers, agents, and employees.</li>
+
+ <li>The "Service" refers to applications/software, products, and services provided by Your Gitea Instance.</li>
+
+ <li>The "User", "You", and "Your" refers to individual person or institution (organizations or company) that has visited or using the Service; that have access or use any part of the Account; or that directs to use the Account to perform its function. Please note that additional terms may apply for Accounts related to business or government.</li>
+
+ <li>The "Website" refers to Your Gitea Instance's website at <a href="https://your-gitea-instance">your-gitea-instance</a>, including its subdomains and other websites owned by Your Gitea Instance.</li>
+ </ol>
+
+ <h2>Account Terms</h2>
+
+ <h3>Account Controls</h3>
+
+ <ul>
+ <li>Users: Subject to these Terms, you retain ultimate administrative control over your User Account and the Content within it.</li>
+
+ <li>Organizations. The "owner" of an Organization that was created under these Terms has ultimate administrative control over that Organization and the Content within it. Within the Service, an owner can manage User access to the Organization’s data and projects. An Organization may have multiple owners, but there must be at least one User Account designated as an owner of an Organization. If you are the owner of an Organization under these Terms, we consider you responsible for the actions that are performed on or through that Organization.</li>
+ </ul>
+
+ <h3>Required Information</h3>
+
+ <p>You must provide a valid email address in order to complete the signup process. Any other information requested, such as your real name, is optional, unless you are accepting these terms on behalf of a legal entity (in which case we need more information about the legal entity).</p>
+
+ <h3>Account Requirements</h3>
+
+ <ul>
+ <li>You must be a human to create an Account. Accounts registered by "bots" or other automated methods are not permitted. We do permit machine accounts:</li>
+ <li>A machine account is an Account set up by an individual human who accepts the Terms on behalf of the Account, provides a valid email address, and is responsible for its actions. A machine account is used exclusively for performing automated tasks. Multiple users may direct the actions of a machine account, but the owner of the Account is ultimately responsible for the machine's actions.</li>
+ <li>You must be age 13 or older. If we learn of any User under that age, we will immediately terminate that User's Account. Different countries may have different minimum age; in such cases you are responsible for complying with your country's regulation. By using Your Gitea Instance, you agree to comply with <a href="https://www.ftc.gov/enforcement/rules/rulemaking-regulatory-reform-proceedings/childrens-online-privacy-protection-rule">COPPA</a> and/or similar law in your country.</li>
+ </ul>
+
+ <h3>User Account Security</h3>
+
+ <p>You are responsible for keeping your Account secure while you use our Service. We offer tools such as two-factor authentication to help you maintain your Account's security, but the content of your Account and its security are up to you.</p>
+
+ <h3>Additional Terms</h3>
+
+ <p>In some situations, third parties' terms may apply to your use of Your Gitea Instance. For example, you may be a member of an organization on Your Gitea Instance with its own terms or license agreements; you may download an application that integrates with Your Gitea Instance; or you may use Your Gitea Instance to authenticate to another service. Please be aware that while these Terms are our full agreement with you, other parties' terms govern their relationships with you.</p>
+
+ <h2>Acceptable Use</h2>
+
+ <p>Your use of the Website and Service must not violate any applicable laws, including copyright or trademark laws, export control or sanctions laws, or other laws in your jurisdiction. You are responsible for making sure that your use of the Service is in compliance with laws and any applicable regulations.</p>
+
+ <h2>User-Generated Content</h2>
+
+ <ol>
+ <li>You may create or upload User-Generated Content while using the Service. You are solely responsible for the content of, and for any harm resulting from, any User-Generated Content that you post, upload, link to or otherwise make available via the Service, regardless of the form of that Content. We are not responsible for any public display or misuse of your User-Generated Content.</li>
+
+ <li>We do not pre-screen User-Generated Content, but we have the right (though not the obligation) to refuse or remove any User-Generated Content that, in our sole discretion, violates any our terms or policies.</li>
+
+ <li>
+ <p>You retain ownership of and responsibility for Your Content. If you're posting anything you did not create yourself or do not own the rights to, you agree that you are responsible for any Content you post; that you will only submit Content that you have the right to post; and that you will fully comply with any third party licenses relating to Content you post.</p>
+
+ <p>Because of above, we need you to grant us -- and other Your Gitea Instance users -- certain legal permissions, listed below in this section. If you upload Content that already comes with a license granting Your Gitea Instance the permissions we need to run our Service, no additional license is required. You understand that you will not receive any payment for any of the rights granted below. The licenses you grant to us will end when you remove Your Content from our servers, unless other Users have forked it.</p>
+ </li>
+
+ <li>
+ <p>We need the legal right to do things like host Your Content, publish it, and share it. You grant us and our legal successors the right to store, parse, and display Your Content, and make incidental copies as necessary to render the Website and provide the Service. This includes the right to do things like copy it to our database and make backups; show it to you and other users; parse it into a search index or otherwise analyze it on our servers; share it with other users; and perform it, in case Your Content is something like music or video.</p>
+
+ <p>This license, however, doesn't grant Your Gitea Instance the right to sell Your Content or otherwise distribute or use it outside of our provision of the Service.</p>
+ </li>
+
+ <li>
+ <p>Any User-Generated Content you post publicly, including issues, comments, and contributions to other Users' repositories, may be viewed by others. By setting your repositories to be viewed publicly, you agree to allow others to view and "fork" your repositories (this means that others may make their own copies of Content from your repositories in repositories they control).</p>
+
+ <p>If you set your pages and repositories to be viewed publicly, you grant each User of Your Gitea Instance a nonexclusive, worldwide license to use, display, and perform Your Content through the Your Gitea Instance Service and to reproduce Your Content solely on Your Gitea Instance as permitted through Your Gitea Instance's functionality (for example, through forking). You may grant further rights if you adopt a license. If you are uploading Content you did not create or own, you are responsible for ensuring that the Content you upload is licensed under terms that grant these permissions to other Your Gitea Instance Users.</p>
+ </li>
+
+ <li>
+ <p>Whenever you make a contribution to a repository containing notice of a license, you license your contribution under the same terms, and you agree that you have the right to license your contribution under those terms. If you have a separate agreement to license your contributions under different terms, such as a contributor license agreement, that agreement will supersede.</p>
+
+ <p><i>Isn't this just how it works already? Yep. This is widely accepted as the norm in the open-source community; it's commonly referred to by the shorthand "inbound=outbound". We're just making it explicit.</i></p>
+ </li>
+
+ <li>
+ <p>You retain all moral rights to Your Content that you upload, publish, or submit to any part of the Service, including the rights of integrity and attribution. However, you waive these rights and agree not to assert them against us, to enable us to reasonably exercise the rights granted above, but not otherwise.</p>
+
+ <p>To the extent this agreement is not enforceable by applicable law, you grant Your Gitea Instance the rights we need to use Your Content without attribution and to make reasonable adaptations of Your Content as necessary to render the Website and provide the Service.</p>
+ </li>
+ </ol>
+
+ <h2>Private Repositories</h2>
+
+ <ol>
+ <li>Some Accounts may have private repositories, which allow the User to control access to Content.</li>
+
+ <li>Your Gitea Instance considers the contents of private repositories to be confidential to you. Your Gitea Instance will protect the contents of private repositories from unauthorized use, access, or disclosure in the same manner that we would use to protect our own confidential information of a similar nature and in no event with less than a reasonable degree of care.</li>
+
+ <li>
+ <p>Your Gitea Instance employees may only access the content of your private repositories in the following situations:</p>
+
+ <ul>
+ <li>With your consent and knowledge, for support reasons. If Your Gitea Instance accesses a private repository for support reasons, we will only do so with the owner’s consent and knowledge.</li>
+ <li>When access is required for security reasons, including when access is required to maintain ongoing confidentiality, integrity, availability and resilience of Your Gitea Instance's systems and Service.</li>
+ </ul>
+ </li>
+
+ <li>You may choose to enable additional access to your private repositories. For example: You may enable various Your Gitea Instance services or features that require additional rights to Your Content in private repositories. These rights may vary depending on the service or feature, but Your Gitea Instance will continue to treat your private repository Content as confidential. If those services or features require rights in addition to those we need to provide the Your Gitea Instance Service, we will provide an explanation of those rights.</li>
+ </ol>
+
+ <h2>Copyright Infringement and DMCA Policy</h2>
+
+ <p>If you are copyright owner and believe that content on our website violates your copyright, please contact us at <a href="mailto:copyright@your-gitea-instance">copyright@your-gitea-instance</a>. Please note that before sending a takedown notice, consider legal uses (such as fair use and licensed use); and legal consequences for sending false notices.</p>
+
+ <h2>Intellectual Properties and COPYING</h2>
+
+ <p>Your Gitea Instance and our licensors, vendors, agents, and/or our content providers retain ownership of all intellectual property rights of any kind related to the Website and Service. We reserve all rights that are not expressly granted to you under this Agreement or by law. The look and feel of the Website and Service is copyright © Your Gitea Instance. All rights reserved.</p>
+
+ <p>If you'd like to use our trademarks, you must follow all of our trademark guidelines.</p>
+
+ <p>This Agreement is licensed under <a href="https://creativecommons.org/publicdomain/zero/1.0/legalcode">CCO Public Domain License</a>.</p>
+
+ <h2>API Terms</h2>
+
+ <p>Abuse or excessively frequent requests to Your Gitea Instance via the API may result in the temporary or permanent suspension of your Account's access to the API. Your Gitea Instance, in our sole discretion, will determine abuse or excessive usage of the API. We will make a reasonable attempt to warn you via email prior to suspension.</p>
+
+ <p>You may not share API tokens to exceed Your Gitea Instance's rate limitations.</p>
+
+ <p>You may not use the API to download data or Content from Your Gitea Instance for spamming purposes, including for the purposes of selling Your Gitea Instance users' personal information, such as to recruiters, headhunters, and job boards.</p>
+
+ <p>All use of the Your Gitea Instance API is subject to these Terms of Service and the Your Gitea Instance Privacy Statement.</p>
+
+ <p>However, we may provide subscription-based access to our API for Users who need high-throughput access or reselling our Service.</p>
+
+
+ <h2>Cancellation and Termination</h2>
+
+ <h3>Account Cancellation</h3>
+
+ <p>It is your responsibility to properly cancel your Account with Your Gitea Instance. You can cancel your Account at any time by going into your Settings in the global navigation bar at the top of the screen. The Account screen provides a simple, no questions asked cancellation link. We are not able to cancel Accounts in response to an email or phone request.</p>
+
+ <h3>Upon Cancellation</h3>
+
+ <p>We will retain and use your information as necessary to comply with our legal obligations, resolve disputes, and enforce our agreements, but barring legal requirements, we will delete your full profile and the Content of your repositories within 90 days of cancellation or termination (though some information may remain in encrypted backups). This information can not be recovered once your Account is cancelled.</p>
+
+ <p>We will not delete Content that you have contributed to other Users' repositories or that other Users have forked.</p>
+
+ <p>Upon request, we will make a reasonable effort to provide an Account owner with a copy of your lawful, non-infringing Account contents after Account cancellation or termination. You must make this request within 90 days of cancellation or termination.</p>
+
+ <h3>We May Terminate</h3>
+
+ <p>Your Gitea Instance has the right to suspend or terminate your access to all or any part of the Website at any time, with or without cause, with or without notice, effective immediately. Your Gitea Instance reserves the right to refuse service to anyone for any reason at any time.</p>
+
+ <h3>Survival</h3>
+
+ <p>All provisions of this Agreement which, by their nature, should survive termination will survive termination — including, without limitation: ownership provisions, warranty disclaimers, indemnity, and limitations of liability.</p>
+
+ <h2>Communications with Us</h2>
+
+ <h3>Electronic Communications Required</h3>
+
+ <p>For contractual purposes, you (1) consent to receive communications from us in an electronic form via the email address you have submitted or via the Service; and (2) agree that all Terms of Service, agreements, notices, disclosures, and other communications that we provide to you electronically satisfy any legal requirement that those communications would satisfy if they were on paper. This section does not affect your non-waivable rights.</p>
+
+ <h3>Legal Notices to Us Must Be in Writing</h3>
+
+ <p>Communications made through email or Your Gitea Instance Support's messaging system will not constitute legal notice to Your Gitea Instance or any of its officers, employees, agents or representatives in any situation where notice to Your Gitea Instance is required by contract or any law or regulation. Legal notice to Your Gitea Instance must be in writing and served on Your Gitea Instance's legal agent.</p>
+
+ <h3>No Phone Support</h3>
+
+ <p>We only offer support via email, in-Service communications, and electronic messages. We do not offer telephone support.</p>
+
+ <h2>Disclaimer of Warranties</h2>
+
+ <p>Your Gitea Instance provides the Website and the Service “as is” and “as available,” without warranty of any kind. Without limiting this, we expressly disclaim all warranties, whether express, implied or statutory, regarding the Website and the Service including without limitation any warranty of merchantability, fitness for a particular purpose, title, security, accuracy and non-infringement.</p>
+
+ <p>Your Gitea Instance does not warrant that the Service will meet your requirements; that the Service will be uninterrupted, timely, secure, or error-free; that the information provided through the Service is accurate, reliable or correct; that any defects or errors will be corrected; that the Service will be available at any particular time or location; or that the Service is free of viruses or other harmful components. You assume full responsibility and risk of loss resulting from your downloading and/or use of files, information, content or other material obtained from the Service.</p>
+
+ <h2>Limitation of Liability</h2>
+
+ <p>You understand and agree that we will not be liable to you or any third party for any loss of profits, use, goodwill, or data, or for any incidental, indirect, special, consequential or exemplary damages, however arising, that result from:</p>
+
+ <ul>
+ <li>the use, disclosure, or display of your User-Generated Content;</li>
+
+ <li>your use or inability to use the Service;</li>
+
+ <li>any modification, price change, suspension or discontinuance of the Service;</li>
+
+ <li>the Service generally or the software or systems that make the Service available;</li>
+
+ <li>unauthorized access to or alterations of your transmissions or data;</li>
+
+ <li>statements or conduct of any third party on the Service;</li>
+
+ <li>any other user interactions that you input or receive through your use of the Service; or
+ any other matter relating to the Service.</li>
+ </ul>
+
+ <p>Our liability is limited whether or not we have been informed of the possibility of such damages, and even if a remedy set forth in this Agreement is found to have failed of its essential purpose. We will have no liability for any failure or delay due to matters beyond our reasonable control.</p>
+
+ <h2>Release and Indemnification</h2>
+
+ <p>If you have a dispute with one or more Users, you agree to release Your Gitea Instance from any and all claims, demands and damages (actual and consequential) of every kind and nature, known and unknown, arising out of or in any way connected with such disputes.</p>
+
+ <p>You agree to indemnify us, defend us, and hold us harmless from and against any and all claims, liabilities, and expenses, including attorneys’ fees, arising out of your use of the Website and the Service, including but not limited to your violation of this Agreement, provided that Your Gitea Instance (1) promptly gives you written notice of the claim, demand, suit or proceeding; (2) gives you sole control of the defense and settlement of the claim, demand, suit or proceeding (provided that you may not settle any claim, demand, suit or proceeding unless the settlement unconditionally releases Your Gitea Instance of all liability); and (3) provides to you all reasonable assistance, at your expense.</p>
+
+ <h2>Changes to These Terms</h2>
+
+ <p>We reserve the right, at our sole discretion, to amend these Terms of Service at any time and will update these Terms of Service in the event of any such amendments. We will notify our Users of material changes to this Agreement, such as price changes, at least 30 days prior to the change taking effect by posting a notice on our Website. For non-material modifications, your continued use of the Website constitutes agreement to our revisions of these Terms of Service.</p>
+
+ <h2>Miscellaneous</h2>
+
+ <h3>Governing Law</h3>
+
+ <p>Except to the extent applicable law provides otherwise, this Agreement between you and us and any access to or use of the Website or the Service are governed by (national laws of country/state where Gitea is deployed) and (regional laws of locality where Gitea is deployed), without regard to conflict of law provisions. You and Your Gitea Instance agree to submit to the exclusive jurisdiction and venue of the courts located in (locality where Gitea is deployed).</p>
+
+ <h3>Non-Assignability</h3>
+
+ <p>Your Gitea Instance may assign or delegate these Terms of Service and/or our Privacy Policy in whole or in part, to any person or entity at any time with or without your consent, including the license granted in <i>User-Generated Content</i>. You may not assign or delegate any rights or obligations under the Terms of Service or Privacy Statement without our prior written consent, and any unauthorized assignment and delegation by you is void.</p>
+
+ <h3>Severablity, No Waiver, and Survival</h3>
+
+ <p>If any part of this Agreement is held invalid or unenforceable, that portion of the Agreement will be construed to reflect the parties’ original intent. The remaining portions will remain in full force and effect. Any failure on the part of Your Gitea Instance to enforce any provision of this Agreement will not be considered a waiver of our right to enforce such provision. Our rights under this Agreement will survive any termination of this Agreement.</p>
+
+ <h3>Amendments and Complete Agreement</h3>
+
+ <p>This Agreement may only be modified by a written amendment signed by an authorized representative of Your Gitea Instance, or by the posting by Your Gitea Instance of a revised version in accordance with <i>Changes to These Terms</i>. These Terms of Service, together with the Your Gitea Instance Privacy Policy, represent the complete and exclusive statement of the agreement between you and us. This Agreement supersedes any proposal or prior agreement oral or written, and any other communications between you and Your Gitea Instance relating to the subject matter of these terms including any confidentiality or nondisclosure agreements.</p>
+
+ <h3>Contact</h3>
+
+ <p>If you have questions about these Terms of Service, you can <a href="mailto:support@your-gitea-instance">contact our support</a>.</p>
+ </body>
+</html>
diff --git a/contrib/options/label/Advanced b/contrib/options/label/Advanced
new file mode 100644
index 0000000..e413236
--- /dev/null
+++ b/contrib/options/label/Advanced
@@ -0,0 +1,26 @@
+#424242 Status: Needs feedback ; Feedback is needed
+#fbc02d Status: In progress ; Work is in progress
+#43a047 Status: Completed ; Work is complete
+#00796b Status: Help wanted ;
+#880e4f Status: Blocked ;
+#b0bec5 Status: Stale ;
+
+#d32f2f Kind: Bug ;
+#607d8b Kind: Question ;
+#9c27b0 Kind: Security ;
+#795548 Kind: Testing ;
+#c62828 Kind: Breaking ;
+#37474f Kind: Documentation ;
+#0288d1 Kind: Feature ;
+#2e7d32 Kind: Enhancement ;
+#f4511e Kind: Maintenance ;
+
+#546e7a Reviewed: Invalid ; Something was marked as invalid
+#616161 Reviewed: Duplicate; Something exists already
+#795548 Reviewed: Confirmed ; Something has been confirmed
+#eeeeee Reviewed: Wontfix ; Something won't be fixed
+
+#d32f2f Priority: High ; The priority is high
+#e64a19 Priority: Medium ; The priority is medium
+#4caf50 Priority: Low ; The priority is low
+#b71c1c Priority: Critical ; The priority is critical
diff --git a/contrib/supervisor/gitea b/contrib/supervisor/gitea
new file mode 100644
index 0000000..a8a908d
--- /dev/null
+++ b/contrib/supervisor/gitea
@@ -0,0 +1,16 @@
+[program:gitea]
+directory=/home/git/go/src/github.com/go-gitea/gitea/
+command=/home/git/go/src/github.com/go-gitea/gitea/gitea web
+autostart=true
+autorestart=true
+startsecs=10
+stdout_logfile=/var/log/gitea/stdout.log
+stdout_logfile_maxbytes=1MB
+stdout_logfile_backups=10
+stdout_capture_maxbytes=1MB
+stderr_logfile=/var/log/gitea/stderr.log
+stderr_logfile_maxbytes=1MB
+stderr_logfile_backups=10
+stderr_capture_maxbytes=1MB
+user = git
+environment = HOME="/home/git", USER="git" \ No newline at end of file
diff --git a/contrib/systemd/forgejo.service b/contrib/systemd/forgejo.service
new file mode 100644
index 0000000..ee019e1
--- /dev/null
+++ b/contrib/systemd/forgejo.service
@@ -0,0 +1,86 @@
+[Unit]
+Description=Forgejo (Beyond coding. We forge.)
+After=syslog.target
+After=network.target
+###
+# Don't forget to add the database service dependencies
+###
+#
+#Wants=mysql.service
+#After=mysql.service
+#
+#Wants=mariadb.service
+#After=mariadb.service
+#
+#Wants=postgresql.service
+#After=postgresql.service
+#
+#Wants=memcached.service
+#After=memcached.service
+#
+#Wants=redis.service
+#After=redis.service
+#
+###
+# If using socket activation for main http/s
+###
+#
+#After=forgejo.main.socket
+#Requires=forgejo.main.socket
+#
+###
+# (You can also provide forgejo an http fallback and/or ssh socket too)
+#
+# An example of /etc/systemd/system/forgejo.main.socket
+###
+##
+## [Unit]
+## Description=Forgejo Web Socket
+## PartOf=forgejo.service
+##
+## [Socket]
+## Service=forgejo.service
+## ListenStream=<some_port>
+## NoDelay=true
+##
+## [Install]
+## WantedBy=sockets.target
+##
+###
+
+[Service]
+# Uncomment the next line if you have repos with lots of files and get a HTTP 500 error because of that
+# LimitNOFILE=524288:524288
+RestartSec=2s
+Type=simple
+User=git
+Group=git
+WorkingDirectory=/var/lib/forgejo/
+# If using Unix socket: tells systemd to create the /run/forgejo folder, which will contain the forgejo.sock file
+# (manually creating /run/forgejo doesn't work, because it would not persist across reboots)
+#RuntimeDirectory=forgejo
+ExecStart=/usr/local/bin/forgejo web --config /etc/forgejo/app.ini
+Restart=always
+Environment=USER=git HOME=/home/git FORGEJO_WORK_DIR=/var/lib/forgejo
+# If you install Git to directory prefix other than default PATH (which happens
+# for example if you install other versions of Git side-to-side with
+# distribution version), uncomment below line and add that prefix to PATH
+# Don't forget to place git-lfs binary on the PATH below if you want to enable
+# Git LFS support
+#Environment=PATH=/path/to/git/bin:/bin:/sbin:/usr/bin:/usr/sbin
+# If you want to bind Forgejo to a port below 1024, uncomment
+# the two values below, or use socket activation to pass Forgejo its ports as above
+###
+#CapabilityBoundingSet=CAP_NET_BIND_SERVICE
+#AmbientCapabilities=CAP_NET_BIND_SERVICE
+###
+# In some cases, when using CapabilityBoundingSet and AmbientCapabilities option, you may want to
+# set the following value to false to allow capabilities to be applied on Forgejo process. The following
+# value if set to true sandboxes Forgejo service and prevent any processes from running with privileges
+# in the host user namespace.
+###
+#PrivateUsers=false
+###
+
+[Install]
+WantedBy=multi-user.target
diff --git a/contrib/update_dependencies.sh b/contrib/update_dependencies.sh
new file mode 100755
index 0000000..5700a19
--- /dev/null
+++ b/contrib/update_dependencies.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+grep 'git' go.mod | grep '\.com' | grep -v indirect | grep -v replace | cut -f 2 | cut -d ' ' -f 1 | while read line; do
+ go get -u "$line"
+ make vendor
+ git add .
+ git commit -m "update $line"
+done
diff --git a/contrib/upgrade.sh b/contrib/upgrade.sh
new file mode 100755
index 0000000..4240b56
--- /dev/null
+++ b/contrib/upgrade.sh
@@ -0,0 +1,135 @@
+#!/usr/bin/env bash
+# This is an update script for forgejo installed via the binary distribution
+# from codeberg.org/forgejo/forgejo on linux as systemd service. It
+# performs a backup and updates Forgejo in place.
+# NOTE: This adds the GPG Signing Key of the Forgejo maintainers to the keyring.
+# Depends on: bash, curl, xz, sha256sum. optionally jq, gpg
+# See section below for available environment vars.
+# When no version is specified, updates to the latest release.
+# Examples:
+# upgrade.sh 1.15.10
+# forgejohome=/opt/forgejo forgejoconf=$forgejohome/app.ini upgrade.sh
+
+# Check if forgejo service is running
+if ! pidof forgejo &> /dev/null; then
+ echo "Error: forgejo is not running."
+ exit 1
+fi
+
+# Continue with rest of the script if forgejo is running
+echo "Forgejo is running. Continuing with rest of script..."
+
+# apply variables from environment
+: "${forgejobin:="/usr/local/bin/forgejo"}"
+: "${forgejohome:="/var/lib/forgejo"}"
+: "${forgejoconf:="/etc/forgejo/app.ini"}"
+: "${forgejouser:="git"}"
+: "${sudocmd:="sudo"}"
+: "${arch:="linux-amd64"}"
+: "${service_start:="$sudocmd systemctl start forgejo"}"
+: "${service_stop:="$sudocmd systemctl stop forgejo"}"
+: "${service_status:="$sudocmd systemctl status forgejo"}"
+: "${backupopts:=""}" # see `forgejo dump --help` for available options
+
+function forgejocmd {
+ if [[ $sudocmd = "su" ]]; then
+ # `-c` only accept one string as argument.
+ "$sudocmd" - "$forgejouser" -c "$(printf "%q " "$forgejobin" "--config" "$forgejoconf" "--work-path" "$forgejohome" "$@")"
+ else
+ "$sudocmd" --user "$forgejouser" "$forgejobin" --config "$forgejoconf" --work-path "$forgejohome" "$@"
+ fi
+}
+
+function require {
+ for exe in "$@"; do
+ command -v "$exe" &>/dev/null || (echo "missing dependency '$exe'"; exit 1)
+ done
+}
+
+# parse command line arguments
+while true; do
+ case "$1" in
+ -v | --version ) forgejoversion="$2"; shift 2 ;;
+ -y | --yes ) no_confirm="yes"; shift ;;
+ --ignore-gpg) ignore_gpg="yes"; shift ;;
+ "" | -- ) shift; break ;;
+ * ) echo "Usage: [<environment vars>] upgrade.sh [-v <version>] [-y] [--ignore-gpg]"; exit 1;;
+ esac
+done
+
+# exit once any command fails. this means that each step should be idempotent!
+set -euo pipefail
+
+if [[ -f /etc/os-release ]]; then
+ os_release=$(cat /etc/os-release)
+
+ if [[ "$os_release" =~ "OpenWrt" ]]; then
+ sudocmd="su"
+ service_start="/etc/init.d/forgejo start"
+ service_stop="/etc/init.d/forgejo stop"
+ service_status="/etc/init.d/forgejo status"
+ else
+ require systemctl
+ fi
+fi
+
+require curl xz sha256sum "$sudocmd"
+
+# select version to install
+if [[ -z "${forgejoversion:-}" ]]; then
+ require jq
+ forgejoversion=$(curl --connect-timeout 10 -sL 'https://codeberg.org/api/v1/repos/forgejo/forgejo/releases?draft=false&pre-release=false&limit=1' -H 'accept: application/json' | jq -r '.[0].tag_name | sub("v"; "")')
+ echo "Latest available version is $forgejoversion"
+fi
+
+# confirm update
+echo "Checking currently installed version..."
+current=$(forgejocmd --version | cut -d ' ' -f 3)
+[[ "$current" == "$forgejoversion" ]] && echo "$current is already installed, stopping." && exit 1
+if [[ -z "${no_confirm:-}" ]]; then
+ echo "Make sure to read the changelog first: https://codeberg.org/forgejo/forgejo/src/branch/forgejo/CHANGELOG.md"
+ echo "Are you ready to update forgejo from ${current} to ${forgejoversion}? (y/N)"
+ read -r confirm
+ [[ "$confirm" == "y" ]] || [[ "$confirm" == "Y" ]] || exit 1
+fi
+
+echo "Upgrading forgejo from $current to $forgejoversion ..."
+
+pushd "$(pwd)" &>/dev/null
+cd "$forgejohome" # needed for forgejo dump later
+
+# download new binary
+binname="forgejo-${forgejoversion}-${arch}"
+binurl="https://codeberg.org/forgejo/forgejo/releases/download/v${forgejoversion}/${binname}.xz"
+echo "Downloading $binurl..."
+curl --connect-timeout 10 --silent --show-error --fail --location -O "$binurl{,.sha256,.asc}"
+
+# validate checksum & gpg signature
+sha256sum -c "${binname}.xz.sha256"
+if [[ -z "${ignore_gpg:-}" ]]; then
+ require gpg
+ gpg --keyserver keys.openpgp.org --recv EB114F5E6C0DC2BCDD183550A4B61A2DC5923710
+ gpg --verify "${binname}.xz.asc" "${binname}.xz" || { echo 'Signature does not match'; exit 1; }
+fi
+rm "${binname}".xz.{sha256,asc}
+
+# unpack binary + make executable
+xz --decompress --force "${binname}.xz"
+chown "$forgejouser" "$binname"
+chmod +x "$binname"
+
+# stop forgejo, create backup, replace binary, restart forgejo
+echo "Flushing forgejo queues at $(date)"
+forgejocmd manager flush-queues
+echo "Stopping forgejo at $(date)"
+$service_stop
+echo "Creating backup in $forgejohome"
+forgejocmd dump $backupopts
+echo "Updating binary at $forgejobin"
+cp -f "$forgejobin" "$forgejobin.bak" && mv -f "$binname" "$forgejobin"
+$service_start
+$service_status
+
+echo "Upgrade to $forgejoversion successful!"
+
+popd