Contributor
- Architecture
- Code of Conduct and moderation team
- Developer Certificate of Origin (DCO)
- Bugs, features and discussions
- Governance
- Release management
- Secrets
- Web browser support
- code.forgejo.org
- Interface customization
- Dependency management
- Development environment
- Finding what to work on in Forgejo
- Compiling from source
- Forgejo Contributor Guide
- Hardware infrastructure
- Managing dependencies between repositories
- Repository Migration
- Static pages
- Testing
- Welcome to Forgejo
- Weblate admin duties
- English base localization
- Localization
- Service Messages
- Service Messages Feature Description
- Threat Analysis
- Threat Analysis: Remote Login Propagation
Architecture
This document serves two purposes: it seeks to offer the reader a high-level grasp of how Forgejo operates, and it shows the reader where to find whatever sort of code. This is not a full explanation of how the code works; rather, it is intended to be concise.
It's encouraged to read this carefully, take a moment to let it sink in, find related code through your preferred symbol finder, and reach out when necessary.
Bird's-eye view
Forgejo serves as a Git server and frontend; users can pull and push to git repositories and view commits and their diff, Forgejo handles the authentication and executes actions based on the context of the request, such as sending a notification if the pushed commit was part of a pull request or getting a diff (and syntax-highlighting it) between two commits if the compare page is requested. Forgejo doesn’t touch the git repository itself; it leaves that to the git binary.
Forgejo also provides a software development environment that does not rely on or interact with Git for the most part, which means that Forgejo handles the logic and stores information in databases. The environment includes features such as user profiles, issue reporting, and packages.
Code map
models/
Files in this directory include type definitions for tables and helper functions that execute SQL queries to retrieve and update data from the database.
modules/
Examples of modules that reside in this directory are markdown rendering, caching, and logging.
services/
This directory contains advanced functions that require database and/or git code and implement a lot of non-database logic; it is a combination of the 'models' and 'modules' functions.
This category contains operations such as user deletion, Git repository mirroring, and mail.
web_src/
The sources used to generate static assets used by the web UI (such as JavaScript, CSS, and SVGs) resides in this directory.
routers/
Web and API routes are in separate files and don't share handlers with each other, but they do have common middleware. Handlers either retrieve data and prepare it for rendering or perform an action and report on its success.
templates/
This directory contains the HTML templates for the frontend.
These templates are server-side rendered and use the Go template syntax.
tests/
This directory contains integration testing code.
The files prefixed with api_ contain integration tests for API routes, while those without are mostly integration tests for web routes.
Integration tests ensure that the behavior of the entire program is correct, which usually means that they indirectly test the behavior of multiple functions and their logic together as one behavior.
Unit tests
Unit tests are implemented in <filename>_test.go for the corresponding <filename>.go code. Not all files will have unit tests.
They test the expected result and behavior of the function.
Code of Conduct and moderation team
Forgejo is committed to creating an inclusive community where everyone feels welcome and safe. The well-being of our members is important to us and if anyone feels unsafe or uncomfortable for any reason, we encourage you to reach out to the Moderation Team.
You can contact the Moderation Team via email at moderation@forgejo.org or via matrix at @forgejo-moderation:matrix.org.
Your conversations will remain confidential and are only disclosed in accordance with our moderation process.
The Moderation Team is there to listen to your concerns and will do its best to find the best solutions, ensuring that everyone feels safe and respected in our community. Actions taken are documented in an anonymized way within the governance repository, ensuring transparency and accountability. The team is committed to addressing any behavior that goes against the Code of Conduct or the law.
Together, we can foster a positive and inclusive environment for all Forgejo community members.
Developer Certificate of Origin (DCO)
Contributions to Forgejo, in all the repositories in the Forgejo organization are accepted provided the author agrees to the following Developer Certificate of Origin (DCO).
By making a contribution to Forgejo, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the Free Software license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate Free Software
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same Free Software license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the Free Software license(s) involved.
Bugs, features and discussions
The Forgejo issue tracker is where bugs should be reported and features requested.
Dedicated repositories in the Forgejo organization cover areas such as:
- the website
- the governance
- the Code of Conduct
- the sustainability and funding.
Other discussions regarding all non technical aspects of Forgejo, happen in a dedicated issue tracker and in the matrix chatroom.
Security
The security team takes care of security vulnerabilities. It handles sensitive security-related issues reported to security@forgejo.org using encryption.
The security team also keeps the content of the security.txt file up to date.
The private GPG key for security@forgejo.org is shared among all members of the security team and not stored online.
Governance
The Forgejo domains are owned by the democratic non-profit dedicated to Free Software Codeberg e.V.. Forgejo is therefore ultimately under the control of Codeberg e.V. and its governance. However, although Codeberg e.V. is committed to use and host Forgejo, it is expected that Forgejo defines its own governance, in a way that is compatible with the Codeberg e.V. governance.
Forgejo Governance
Forgejo was bootstrapped in November 2022 and defines its governance in a dedicated repository.
Release management
See the release schedule.
Stable release support
Bug fixes and security fixes are backported to the latest stable release.
Long Term Support (LTS)
The first quarter release of the year is LTS. Critical bug fixes and security fixes are backported to the latest LTS release.
Experimental releases
Experimental releases are published daily in forgejo-experimental organization. They are built from the tip of the branch of each stable release. For instance:
forgejoisX.Y-testwhereX.Yis the major and minor number of the next stable release.v8.0/forgejois8.0-testv7.0/forgejois7.0-test
Release numbering
The Forgejo release numbers are compliant with Semantic Versioning. They are followed by the Gitea release number with which they compatible. For instance:
- Forgejo v7.0.5+gitea-1.21.0 is compatible with Gitea v1.21.0.
The release candidates are built of the stable branch and published with the -test suffix:
- Forgejo v7.0-test
- Forgejo v8.0-test
- Forgejo v8.1-test
- etc.
Stable release management
Major and patch releases are published with the help of the release-manager.
LTS toolchain upgrades
The Forgejo toolchain relies on Go OCI images based on Alpine. Only the last two Alpine versions are supported and will be EOL before the Forgejo LTS. In order to receive security updates the toolchain needs to be upgraded to a supported Alpine and Go version because it goes EOL.
Automated experimental releases
The Forgejo development and stable branches are pushed daily to the forgejo-integration
repository
(latest workflow runs).
It triggers the release build
workflow
(latest runs)
which creates a new release for each updated branch, based on their latest commit:
- the
forgejobranch creates theX.Y-testrelease whereX.Yis based on the most recent tag. For instance:- the tag
v8.0.0-devwill create the8.0-testrelease - the tag
v8.1.0-devwill create the8.1-testrelease
- the tag
- the
v*/forgejobranches createX.Y-testreleases whereX.Yis based on their name. For instance:- the branch
v7.0/forgejowill create the7.0-testrelease - the branch
v7.1/forgejowill create the7.1-testrelease
- the branch
Forgejo release mirror
The https://code.forgejo.org/forgejo/forgejo repository is a read-only mirror updated daily with the release assets and the branches from https://codeberg.org/forgejo/forgejo.
Forgejo Runner publication
- Push the vX.Y.Z tag to https://code.forgejo.org/forgejo/runner
The release is built on https://code.forgejo.org/forgejo-integration/runner, which is a mirror of https://code.forgejo.org/forgejo/runner.
The release is published by https://invisible.forgejo.org/forgejo/runner, which is a mirror of https://code.forgejo.org/forgejo-integration/runner. Its role is to copy and sign release artifacts.
- Binaries are downloaded from https://code.forgejo.org/forgejo-integration/runner, signed and copied to https://code.forgejo.org/forgejo/runner.
- Container images are copied from https://code.forgejo.org/forgejo-integration to https://code.forgejo.org/forgejo
It can also be done from the CLI with
forgejo-runner execand providing the secrets from the command line. - Once the new version is published, wait 24h for renovate to propose a pull request titled
Update forgejo-runner to vX.Y.Z(e.g. Update forgejo-runner to v11.1.2) - After merging the pull request titled
Update forgejo-runner to vX.Y.Zask a member of the devops team to upgrade the runners hardware with:
which is expected to display a line confirming the upgrade toforgejo-runner-service.sh upgrade https://code.forgejo.org/forgejo/runner/raw/branch/main/examples/lxc-systemd/forgejo-runner-service.shvX.Y.Z. More information on that script can be found in the README.
Release signing keys management
A GPG master key with no expiration date is created and shared with members of the Owners team via encrypted email. A subkey with a one year expiration date is created and stored in the secrets repository (openpgp/20??-release-team.gpg), to be used by the release pipeline. The public master key is stored in the secrets repository and published where relevant (keys.openpgp.org for instance).
Securing the release token and cryptographic keys
For both the Forgejo Runner and Forgejo itself, copying and signing the release artifacts (container images and binaries) happen on a Forgejo instance not publicly accessible to safeguard the token that has write access to the Forgejo repository as well as the cryptographic key used to sign the releases.
Master key creation
- gpg --expert --full-generate-key
- key type: ECC and ECC option with Curve 25519 as curve
- no expiration
- id: Forgejo Releases contact@forgejo.org
- gpg --export --armor EB114F5E6C0DC2BCDD183550A4B61A2DC5923710 > release-team.gpg.pub
- gpg --keyserver keys.openpgp.org --send-keys EB114F5E6C0DC2BCDD183550A4B61A2DC5923710
- gpg --export-secret-keys --armor EB114F5E6C0DC2BCDD183550A4B61A2DC5923710 | gopass cat openpgp/release-team-master.gpg
Subkey creation and renewal
From the root of the secrets directory, assuming the master key for EB114F5E6C0DC2BCDD183550A4B61A2DC5923710 is already imported in the keyring.
There are a lot of contradictory information regarding the management of subkeys, with zillions of ways of doing something that looks like it could work but creates situations that are close to impossible to figure out. Experimenting with the CLI, reading the gpg man page and using common sense is the best way to understand how it works. Reading the documentation or discussions on the net is highly confusing because it is loaded with 20 years of history, most of which is no longer relevant.
Here are a few notions that help understand how it works:
gpg --export-secret-subkeys --armor B3B1F60AC577F2A2!exports the secret key for the subkey B3B1F60AC577F2A2, the exclamation mark meaning "nothing else".- a
keygripis something that each private key has and that can be displayed withgpg --with-keygrip --list-key. It matters because each private key is associated with exactly one file in theprivate-keys-v1.ddirectory which is named after this keygrip. It is the best way to verify an unrelated private key was not accidentally included in the export of the subkey. - when a subkey is created, the public key for the master key must be published again because it includes the public key of this new subkey.
- all the instructions that are published to instruct people to verify the signature of a release use the fingerprint of the master key. It will work although the release really is signed by the subkey and not the master key. This is the main benefit of using subkeys as it hides the rotation of the subkeys and does not require updating instructions everywhere every year.
- whenever gpg starts working with a new directory, it will launch a gpg-agent daemon that will persist. If this directory is removed manually or modified it will confuse the daemon and the gpg command will misbehave in ways that can be very difficult to understand. When experimenting create a new directory but do not modify the files manually, even though some instructions on the net recommend doing so, for instance to remove a private key.
Create
mkdir /tmp/secrets && pushd /tmp/secrets
gopass list openpgp
gopass fscopy openpgp/20??-release-team.gpg 20??-release-team.gpg # for all years
docker run -v ${PWD}:/tmp/secrets -ti --rm data.forgejo.org/oci/debian:bookworm bash
apt update && apt install -y gnupg2
gpg --import 20*-release-team.gpg
gpg --expert --edit-key EB114F5E6C0DC2BCDD183550A4B61A2DC5923710
addkey
key type: ECC (signature only)
elliptic curve Curve 25519
key validity: 18 months
gpg --list-key --with-subkey-fingerprints # set FINGERPRINT to the fingerprint of the new subkey
year=$(date +%Y)
gpg --export-secret-subkeys --armor "${FINGERPRINT}!" > $year-release-team.gpg
gpg --export --armor EB114F5E6C0DC2BCDD183550A4B61A2DC5923710 > release-team.gpg.pub
gopass fscopy $year-release-team.gpg openpgp/$year-release-team.gpg
gopass fscopy release-team.gpg.pub openpgp/release-team.gpg.pub
popd && rm -r /tmp/secrets
Sanity check
mkdir /tmp/secrets && pushd /tmp/secrets
gopass fscopy openpgp/release-team.gpg.pub release-team.gpg.pub
year=$(date +%Y)
gopass fscopy openpgp/$year-release-team.gpg $year-release-team.gpg # year of the new subkey
docker run -e year=$year -v ${PWD}:/tmp/secrets -ti --rm data.forgejo.org/oci/debian:bookworm bash
apt update && apt install -y gnupg2
cd /tmp/secrets
export GNUPGHOME=/tmp/signrelease ; mkdir $GNUPGHOME ; chmod 700 $GNUPGHOME
gpg --import $year-release-team.gpg # year of the new subkey
find $GNUPGHOME/private-keys-v1.d # only has **one** file named after the keygrip
echo bar > /tmp/foo
gpg --detach-sig --output /tmp/foo.asc --sign /tmp/foo
export GNUPGHOME=/tmp/user ; mkdir $GNUPGHOME ; chmod 700 $GNUPGHOME
gpg --import release-team.gpg.pub
gpg --verify /tmp/foo.asc /tmp/foo
exit
popd && rm -r /tmp/secrets
Publish
mkdir /tmp/secrets && pushd /tmp/secrets
gopass fscopy release-team.gpg.pub release-team.gpg.pub
gpg --import release-team.gpg.pub
gpg --keyserver keys.openpgp.org --send-keys EB114F5E6C0DC2BCDD183550A4B61A2DC5923710
popd && rm -r /tmp/secrets
Update the GPG_PRIVATE_KEY secret in the runner and forgejo repositories dedicated to signing the releases with the new private sub-key:
- https://invisible.forgejo.org/forgejo/runner/settings/actions/secrets
- https://invisible.forgejo.org/forgejo/forgejo/settings/actions/secrets
Reminder
- Set the date of the next sub-key creation reminder https://codeberg.org/forgejo/governance/issues/216
Users, organizations and repositories
Shared user: forgejo-cascading-pr
The forgejo-cascading-pr user opens pull requests on behalf of other repositories by way of the cascading-pr action. It is a regular user, not part of any team. It is only used for that purpose for security reasons.
Dedicated user: forgejo-backport-action
The forgejo-backport-action user opens backport pull requests on the forgejo repository. It is a member of the mergers team. The associated email is mailto:forgejo-backport-action@forgejo.org.
Shared user: release-team
The release-team user publishes and signs all releases. The associated email is mailto:release@forgejo.org.
The public GPG key used to sign the releases is EB114F5E6C0DC2BCDD183550A4B61A2DC5923710 Forgejo Releases <release@forgejo.org>
Shared user: forgejo-experimental-ci
The forgejo-experimental-ci user is dedicated to provide the application tokens used by the CI to build releases and publish them to https://codeberg.org/forgejo-experimental. It does not (and must not) have permission to publish releases at https://codeberg.org/forgejo.
Dedicated user: forgejo-renovate-action
The forgejo-renovate-action user opens renovate pull requests on the forgejo repository. It is a member of the mergers team. The associated email is mailto:forgejo-renovate-action@forgejo.org.
Integration and experimental organization
The https://codeberg.org/forgejo-integration organization is dedicated to integration testing. Its purpose is to ensure all artefacts can effectively be published and retrieved by the CI/CD pipelines.
The https://codeberg.org/forgejo-experimental organization is dedicated to publishing experimental Forgejo releases. They are copied from the https://codeberg.org/forgejo-integration organization.
The forgejo-experimental-ci user as well as all Forgejo contributors working on the CI/CD pipeline should be owners of both organizations.
Secrets
All Forgejo credentials are shared among the devops team in a private repository with encrypted content.
Get started
- Make sure you have a GPG Key, or create one
- Send someone else your public key and ask this person to add yourself as a recipient
# Commands for the other person
$ gpg --import public_key.asc
# The following command will open a prompt, with the available public keys.
# Choose the one you just added and all secrets will be re-encrypted with this new key.
$ gopass recipients add
- Install gopass
:warning: When installing on Ubuntu or Debian you can either download the deb package, install manually or build from source or use our APT repository (github comment with more information).
- Clone this repo using
gopass(the name and email are forgit config)
$ gopass clone git@codeberg.org:forgejo/gopass.git
- Check the consistency of the gopass storage
$ gopass fsck
Get a secret
Show the whole secret file:
$ gopass show ovh.com/manager
Copy the password in the clipboard:
$ gopass show -c ovh.com/manager
Copy the user part of the secret in the clipboard:
$ gopass show -c ovh.com/manager user
Insert or edit a secret
$ gopass edit ovh.com/manager
In the editor, insert the password on the first line.
You may then add lines with a key: value syntax (user: username for instance).
Debugging and manual git operations
The following command will show the location and status of the git repo (all git commands are available).
$ gopass git status
Web browser support
New and existing Forgejo web UI features that affect usability of Forgejo should rely on browser features and APIs available in both:
- Latest version of Firefox ESR
- Previous major version of Chromium
For example, forgejo/forgejo#8859 couldn't rely on the new closedBy property of HTMLDialogElement because it was re-implementing an existing significant feature (modal dialogs) and closedBy was not yet supported by Firefox ESR. So not implementing a compatible JS function would break the ability of users to close the dialog by clicking outside of it on a supported browser, which would have been considered a newly introduced bug in Forgejo v13.0.0.
The latest Firefox ESR version can be calculated differently:
- If pull request is proposed to be backported to stable branch of Forgejo, the latest Firefox ESR as of present day should be considered
- If pull request targets the next major Forgejo release, the latest Firefox ESR as of the day of that Forgejo release should be considered. Refer to Forgejo release schedule and Firefox release calendar to find the matching ESR version
New features that do not affect usability can rely on browser features up to Baseline of last year is acceptable.
Feature oldness and non-standard features
It's up to the contributor to work on additional support beyond the minimally required platforms as long as such compatibility doesn't introduce:
- Significant increase in complexity
- Additional dependencies (e.g. compatibility libraries)
However we do not welcome additional code that only exists to maintain compatibility with browser engines that have been completely officially discontinued, such as:
- MSHTML (Microsoft Internet Explorer)
- EdgeHTML (Microsoft Edge)
- Presto (Opera)
- KHTML
Vendor prefixes should be used strictly when necessarily. When a property is supported without vendor prefixes on the minimally required platforms, vendor-prefixed properties should not be used.
code.forgejo.org
https://code.forgejo.org is a Forgejo instance running the latest stable version, starting with the release candidates. It is dedicated to hosting repositories dedicated to Forgejo development, among which:
- setup-forgejo a Forgejo Action to spawn a Forgejo instance and a runner for testing purposes https://code.forgejo.org/actions/setup-forgejo
- Forgejo Runner https://code.forgejo.org/forgejo/runner
Interface customization
This sections documents the Forgejo interface customization that requires an intimate knowledge of the underlying codebase. The user interface customization documented and supported for Forgejo admins to use is found in the corresponding administrator guide section.
Customizing Forgejo is typically done using the CustomPath folder - by default this is
the custom folder from the working directory (WorkPath), but may be different if your build has
set this differently. This is the central place to override configuration settings,
templates, etc. You can check the CustomPath using forgejo help. You can also find
the path on the Configuration tab in the Site Administration page. You can override
the CustomPath by setting either the FORGEJO_CUSTOM environment variable or by
using the --custom-path option on the forgejo binary. (The option will override the
environment variable.)
If Forgejo is deployed from binary, all default paths will be relative to the Forgejo binary.
Application settings can be found in file CustomConf which is by default,
$FORGEJO_CUSTOM/conf/app.ini but may be different if your build has set this differently.
If you are having difficulty with finding the CustomConf directory, you can identify
the variable using forgejo help or in the logs. This variable can be overridden using
the --config option on the forgejo binary.
[!note] Note Forgejo must perform a full restart to see configuration changes.
Serving custom public files
To make Forgejo serve custom public files (like pages and images), use the folder
$FORGEJO_CUSTOM/public/ as the webroot. Symbolic links will be followed.
At the moment, only the following files are served:
public/robots.txtpublic/manifest.json- files in the
public/.well-known/folder - files in the
public/assets/folder
For example, a file image.png stored in $FORGEJO_CUSTOM/public/assets/, can be accessed with
the url http://forgejo.example.com/assets/image.png.
Changing the logo
To build a custom logo and/or favicon clone the Forgejo source repository, replace assets/logo.svg and/or assets/favicon.svg and run
make generate-images.
assets/favicon.svg is used for favicon.png only, all others will be generated from logo.svg. SVG files will be optimized. All source images must also have square dimensions.
The above command will update below output files which you can then place in $FORGEJO_CUSTOM/public/assets/img on your server:
public/assets/img/logo.svg- Used for site icon, app iconpublic/assets/img/logo.png- Used for Open Graph and home pagepublic/assets/img/avatar_default.png- Used as the default avatar imagepublic/assets/img/repo_default.png- Used as the default repository image, see also thepicture.REPOSITORY_AVATAR_FALLBACK_IMAGEsettingpublic/assets/img/apple-touch-icon.png- Used on iOS devices for bookmarkspublic/assets/img/favicon.svg- Used for faviconpublic/assets/img/favicon.png- Used as fallback for browsers that don't support SVG favicons
Customizing Forgejo pages and resources
Forgejo's executable contains all the resources required to run: templates, images, style-sheets
and translations. Any of them can be overridden by placing a replacement in a matching path
inside the custom directory. For example, to replace the default .gitignore provided
for C++ repositories, we want to replace options/gitignore/C++. To do this, a replacement
must be placed in $FORGEJO_CUSTOM/options/gitignore/C++.
Every single page of Forgejo can be changed. Dynamic content is generated using go templates,
which can be modified by placing replacements below the $FORGEJO_CUSTOM/templates directory.
To obtain any embedded file (including templates), the forgejo embedded CLI can be used. Alternatively, they can be found in the templates directory of Forgejo source.
Be aware that any statement contained inside {{ and }} are Forgejo's template syntax and
should not be touched without fully understanding these components.
Forgejo regularly makes backward incompatible changes to its own templates, which makes templates very likely to break when upgrading Forgejo.
Before deploying your changes to production or upgrading a modified Forgejo instance, we urge that you test your custom modifications in a testing environment first.
Warnings on registration and login pages
It is possible to add custom warnings to registration and login pages. For example, to warn about the scopes in which an instance with open registration should be used. Here's how they would look like:
| Registration | Login |
|---|---|
![]() |
![]() |
Steps to add them
- Copy files
signin_mid.tmplandsignup_top.tmplto$FORGEJO_CUSTOM/templates/custom. - Uncomment the code and edit the message to fit the instance
- Restart the Forgejo instance to apply the change
Customizing startpage / homepage
Copy home.tmpl for your version of Forgejo from templates to $FORGEJO_CUSTOM/templates.
Edit as you wish.
Restart the Forgejo instance to apply the changes.
Adding links and tabs
If all you want is to add extra links to the top navigation bar or footer, or extra tabs to the repository view, you can put them in extra_links.tmpl (links added to the navbar), extra_links_footer.tmpl (links added to the right side of footer), and extra_tabs.tmpl inside your $FORGEJO_CUSTOM/templates/custom/ directory.
For instance, let's say you are in Germany and must add the famously legally-required "Impressum"/about page, listing who is responsible for the site's content:
just place it under your "$FORGEJO_CUSTOM/public/assets/" directory (for instance $FORGEJO_CUSTOM/public/assets/impressum.html) and put a link to it in either $FORGEJO_CUSTOM/templates/custom/extra_links.tmpl or $FORGEJO_CUSTOM/templates/custom/extra_links_footer.tmpl.
To match the current style, the link should have the class name "item", and you can use {{AppSubUrl}} to get the base URL:
<a class="item" href="{{AppSubUrl}}/assets/impressum.html">Impressum</a>
You can add new tabs in the same way, putting them in extra_tabs.tmpl.
The exact HTML needed to match the style of other tabs is in the file
templates/repo/header.tmpl.
Other additions to the page
Apart from extra_links.tmpl and extra_tabs.tmpl, there are other useful templates you can put in your $FORGEJO_CUSTOM/templates/custom/ directory:
header.tmpl, just before the end of the<head>tag where you can add custom CSS files for instance.body_outer_pre.tmpl, right after the start of<body>.body_inner_pre.tmpl, before the top navigation bar, but already inside the main container<div class="full height">.body_inner_post.tmpl, before the end of the main container.body_outer_post.tmpl, before the bottom<footer>element.footer.tmpl, right before the end of the<body>tag, a good place for additional JavaScript.
Using Forgejo variables
It's possible to use various Forgejo variables in your custom templates.
First, temporarily enable development mode: in your app.ini change from RUN_MODE = prod to RUN_MODE = dev. Then add {{ $ | DumpVar }} to any of your templates, restart Forgejo and refresh that page; that will dump all available variables.
Find the data that you need, and use the corresponding variable; for example, if you need the name of the repository then you'd use {{.Repository.Name}}.
If you need to transform that data somehow, and aren't familiar with Go, an easy workaround is to add the data to the DOM and add a small JavaScript script block to manipulate the data.
Customizing Forgejo mails
The $FORGEJO_CUSTOM/templates/mail folder allows changing the body of every mail of Forgejo.
Templates to override can be found in the
templates/mail
directory of Forgejo source.
Override by making a copy of the file under $FORGEJO_CUSTOM/templates/mail using a
full path structure matching source.
Any statement contained inside {{ and }} are Forgejo's template
syntax and shouldn't be touched without fully understanding these components.
Customizing gitignores, labels, licenses, locales, and readmes.
Place custom files in corresponding sub-folder under custom/options.
[!note] Note The files should not have a file extension, e.g.
Labelsrather thanLabels.txt.
gitignores
To add custom .gitignore, add a file with existing .gitignore rules in it to $FORGEJO_CUSTOM/options/gitignore
Labels
Starting with Forgejo 1.19, you can add a file that follows the YAML label format to $FORGEJO_CUSTOM/options/label:
labels:
- name: 'foo/bar' # name of the label that will appear in the dropdown
exclusive: true # whether to use the exclusive namespace for scoped labels. scoped delimiter is /
color: aabbcc # hex colour coding
description: Some label # long description of label intent
The legacy file format can still be used following the format below, however we strongly recommend using the newer YAML format instead.
#hex-color label name ; label description
Licenses
To add a custom license, add a file with the license text to $FORGEJO_CUSTOM/options/license
READMEs
To add a custom README, add a markdown formatted file (without an .md extension) to $FORGEJO_CUSTOM/options/readme
[!note] Note README templates support variable expansion. Currently there are
{Name}(name of repository),{Description},{CloneURL.SSH},{CloneURL.HTTPS}and{OwnerName}
Reactions
To change reaction emoji's you can set allowed reactions at app.ini
[ui]
REACTIONS = +1, -1, laugh, confused, heart, hooray, eyes
A full list of supported emoji's is at emoji list
Customizing fonts
Fonts can be customized using CSS variables:
:root {
--fonts-proportional: 'Proportional fonts here' !important;
--fonts-monospace: 'Monospace fonts here' !important;
--fonts-emoji: 'Emoji fonts here' !important;
}
Customizing the PWA (Progressive Web App)
Forgejo provides a Web application manifest, retrievable from the /manifest.json route. It is also included in every HTML response's <head> element.
By default, this manifest is configured to be very minimal, only setting the absolute required properties of name, shortname, start_url and two entries for icons.
The display property is omitted by default but can be set to "standalone" via the setting STANDALONE in the [pwa] section of app.ini.
If this is not enough for your usecase, Forgejo additionally looks for a manifest.json file in your custom public files to serve instead. (See Serving custom public files).
Dependency management
Forgejo relies on hundreds of Free Software components and they all need to be updated on a regular basis, with appropriate tooling and methods.
Releases
Software referenced by a release (even if such a release is the hash of a commit). They are listed in the dependency dashboard which is updated by renovate from the renovate.json configuration file.
Decision to upgrade
Development branch
An upgrade is justified if:
- it is beneficial to Forgejo
- the risk of regression is low compared to the benefit
There is no need to upgrade if there is no indication that it is beneficial to Forgejo.
Stable branch
The dependency dashboard can be used to create a pull request for an upgrade on a stable branch. It is not done automatically.
An upgrade is justified if it follows the restrictions that apply to stable branches (not backporting features for instance).
Release notes
When an upgrade has a user facing impact on Forgejo, it should be included in the release notes.
- Label the pull request with worth a release note
- Add a
release-notes/<pr number>.mdfile that contains one line for each feature or bug fix. For instance:feat!: [new feature](https://example.com/pull_request) with breaking changesfeat: [new feature](https://example.com/pull_request)fix!: [bug fix](https://example.com/pull_request) with breaking changesfix: [bug fix](https://example.com/pull_request)
See this pull request for an example.
Reviewing renovate pull requests
Pull requests are opened when an upgrade is available and the decision to merge (positive review) or not (request for change review) depends on what the upgrade offers. The history of past upgrades can be browsed by looking for PR with the same title (e.g. happy-dom upgrades).
- The PR contains information about the release. If it does not, it has detailed references that can be used to browse the commits in the dependency source repository and figure out what the changes are.
- The comment of the review:
- explains the decision (needed, not needed)
- explains why the change has an impact on Forgejo
- If the upgrade is needed, user visible changes must be included in the draft release notes for the upcoming release. See this upgrade for an example.
- Security fix and important bug fixes are backported to the stable releases.
Note that renovate will keep a few (see prConcurrentLimit in renovate.json) pull request open at any given time. If no decision is made, newer upgrades will accumulate in the backlog visible in the dashboard.
The release team looks after the pull requests, to the extent that they can be tested automatically. If manual testing is required (because there is no test coverage for the part of the code that would be impacted by an upgrade), a review will be requested from the people who have the required expertise to either improve the test coverage or come up with a manual test procedure to be repeated.
Tuning a software upgrade
There is no uniformity in how software is released and they call for different strategies to deal with upgrades:
Soft forks
Temporary
- https://code.forgejo.org/forgejo/download-artifact
- https://code.forgejo.org/forgejo/upload-artifact
Development environment
This page lists a few options to set up a productive development environment for working on Forgejo.
VSCodium
VSCodium is an open source version of the Visual Studio Code IDE. The Go integration for Visual Studio Code works with VSCodium and is a viable tool to work on Forgejo.
First, run cp -r contrib/ide/vscode .vscode to create new directory .vscode with the contents of folder contrib/ide/vscode at the root of the repository. Then, open the project directory in VSCodium.
You can now use Ctrl+Shift+B to build the gitea executable and F5 to run it in debug mode.
Tests can be run by clicking on the run test or debug test button above their declaration.
Go code is formatted automatically when saved.
Emacs
GNU Emacs is an open source implementation of the Emacs text editor. The Emacs Go-mode, in combination with the Go language-server and a LSP client can be used to work on Forgejo's code base.
A prerequisite for working with the language server is installing gopls.
After this, you'll want to install the necessary packages in Emacs using M-x package-install followed by go-mode and your preferred LSP client, so either lsp-mode or eglot.
After the installation, you'll need to activate the packages in your initialization file. Regardless of your choice of LSP client, you'll need to add:
(require 'go-mode)
If you would like a full list of the features of go-mode, have a look at this blogpost by it's creator.
Then, for lsp-mode, add:
(use-package lsp-mode
:ensure
:commands lsp
:custom
(add-hook 'lsp-mode-hook 'lsp-ui-mode) ;; Optional, see below
(add-hook 'go-mode-hook #'lsp-deferred))
;; lsp-ui mode is optional. It shows inline hints.
(use-package lsp-ui
:ensure
:commands lsp-ui-mode
:custom
(lsp-ui-peek-always-show t)
(lsp-ui-sideline-show-hover t)
(lsp-ui-doc-enable nil))
;; Set up before-save hooks to format buffer and add/delete imports.
;; Make sure you don't have other gofmt/goimports hooks enabled.
(defun lsp-go-install-save-hooks ()
(add-hook 'before-save-hook #'lsp-format-buffer t t)
(add-hook 'before-save-hook #'lsp-organize-imports t t))
(add-hook 'go-mode-hook #'lsp-go-install-save-hooks)
Or for eglot:
(require 'project)
(defun project-find-go-module (dir)
(when-let ((root (locate-dominating-file dir "go.mod")))
(cons 'go-module root)))
(cl-defmethod project-root ((project (head go-module)))
(cdr project))
(add-hook 'project-find-functions #'project-find-go-module)
(require 'eglot)
(add-hook 'go-mode-hook 'eglot-ensure)
;; Optional: install eglot-format-buffer as a save hook.
;; The depth of -10 places this before eglot's willSave notification,
;; so that that notification reports the actual contents that will be saved.
(defun eglot-format-buffer-before-save ()
(add-hook 'before-save-hook #'eglot-format-buffer -10 t))
(add-hook 'go-mode-hook #'eglot-format-buffer-before-save)
As additional quality of life improvements, you might consider installing company, flycheck and/or magit. Consider the package website for a complete explanation and installation instructions.
Here is an init example if you just want to use all three packages
;; company
(use-package company
:ensure
:custom
(company-idle-delay 0) ;; how long to wait until popup
(company-minimum-prefix-length 1)
;; (company-begin-commands nil) ;; uncomment to disable popup
:bind
(:map company-active-map
("C-n". company-select-next)
("C-p". company-select-previous)
("M-<". company-select-first)
("M->". company-select-last)
)
;; Tab-override
(:map company-mode-map
("<tab>". tab-indent-or-complete)
("TAB". tab-indent-or-complete)
)
)
(add-hook 'after-init-hook 'global-company-mode)
;; flycheck
(use-package flycheck :ensure)
;; magit
(use-package magit :ensure)
Vim
Vim has a Go plugin that can likely be used to work on Forgejo's code base. Do you know how to configure it properly? Why not document that here?
Neovim
Here's a minimal example that configures gopls and golangci_lint_ls using
the Lazy.nvim plugin manager.
init.lua
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.uv.fs_stat(lazypath) then
vim.fn.system({
"git",
"clone",
"--filter=blob:none",
"https://github.com/folke/lazy.nvim.git",
"--branch=stable", -- latest stable release
lazypath,
})
end
vim.opt.rtp:prepend(lazypath)
require("lazy").setup({
"neovim/nvim-lspconfig",
{
"nvim-telescope/telescope.nvim",
branch = "0.1.x",
dependencies = {
"nvim-lua/plenary.nvim",
{
"nvim-telescope/telescope-fzf-native.nvim",
build = "make",
cond = vim.fn.executable("make") == 1,
},
},
},
})
vim.g.mapleader = " "
vim.g.maplocalleader = " "
local on_attach = function(client, bufno)
-- deprecated since neovim 0.10
-- vim.api.nvim_buf_set_option(bufno, "omnifunc", "v:lua.vim.lsp.omnifunc")
vim.api.nvim_set_option_value("omnifunc", "v:lua.vim.lsp.omnifunc", { buf = bufno })
local ts = require("telescope.builtin")
local opts = { buffer = bufno }
vim.keymap.set("n", "<leader>e", vim.diagnostic.open_float)
vim.keymap.set("n", "K", vim.lsp.buf.hover, opts)
vim.keymap.set("n", "<C-k>", vim.lsp.buf.signature_help, opts)
vim.keymap.set("n", "gD", vim.lsp.buf.declaration, opts)
vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts)
vim.keymap.set("n", "gtd", vim.lsp.buf.type_definition, opts)
vim.keymap.set("n", "gi", vim.lsp.buf.implementation, opts)
vim.keymap.set("n", "gu", ts.lsp_references, opts)
vim.keymap.set("n", "<leader>ca", vim.lsp.buf.code_action, opts)
vim.keymap.set("n", "<leader>cl", vim.lsp.codelens.run, opts)
vim.keymap.set("n", "<leader>r", vim.lsp.buf.rename, opts)
vim.keymap.set("n", "<leader>f", function()
vim.lsp.buf.format({ async = true })
end, opts)
end
local capabilities = vim.lsp.protocol.make_client_capabilities()
require("lspconfig")["gopls"].setup({
capabilities = capabilities,
settings = {},
on_attach = on_attach,
})
require("lspconfig")["golangci_lint_ls"].setup({
capabilities = capabilities,
settings = {},
on_attach = on_attach,
})
Finding what to work on in Forgejo
Assuming that your development environment is ready and that you are aware of how you can reach out to the Forgejo community, this document shall describe how you can find Forgejo issues to work on.
good-first-issue label
Issues are marked using labels. The good-first-issue label is used to explicitly mark issues that may be appealing to potential new first time contributors. Content-wise, the issues can vary significantly, as they can range between simple fixes to user-facing strings, to features that primarily involve backend development or web development.
As such, do keep in mind that good-first-issue does not necessarily imply that fixing them will be easy or quick. Also, there is a possibility that an issue that you may choose may get too time-consuming. If that happens, don't fret! First, this is completely normal when trying to learn your way around a new free and open-source project. Second, you can always ask other contributors for help if you feel lost (and you should!). In the worst case, you will have learned a few things as a direct result of having been exposed to actual source code. Also, you can always choose to pick another issue, and circle back to the more difficult issue at a later point in time.
Click here to discover issues labelled using the good-first-issue label.
valuable code label
As most Forgejo contributors are volunteers, authors might not have the time to address review comments for their submitted pull request. "Work in progress" pull requests that have had unaddressed review comments for a longer period of time get closed, as it helps reviewers avoid checking whether any progress has been made repeatedly since their last review.
However, that does not mean that the features in the pull requests themselves are unwanted: More often than not, the pull requests are closed because of unaddressed review comments (which are sometimes easy to fix) or because of missing tests. The valuable-code is applied to such pull requests to ensure that they do not get lost. This label also intends to signal that there is a desire to have pull requests merged.
The valuable code way could be better tasked to people that have contributed to free and open-source projects before, and has the additional upside of helping you get acquainted with Forgejo's internals and review processes without having to learn everything at once (UI, backend, tests, documentation requirements submitting the PR). Before picking up on such tasks, we suggest pinging the original author to make sure that you would not be working on the same tasks simultaneously, yet separately.
Bug fixes
Another way of contributing to Forgejo is fixing confirmed bugs. Helping us improve Forgejo's stability and reliability is something that we can always use help with, and such efforts are immensely appreciated and prioritized by the reviewers. Fixing bugs is a great way to get acquainted with Forgejo's different components and development procedures without exposing yourself to a lot of complexity.
In this context, confirmed means that an individual (other than the bug reporter) has managed to reproduce the bug described by the reporter. You can also help us with new bug reports by attempting to reproduce the bugs yourself.
Documentation
Administrators, (prospective) contributors and users of Forgejo all share one characteristic: Sooner or later, they will (most likely) have to rely on our documentation.
Improvements to our documentation help everyone involved, and it is unlikely that you will have to touch any code. Should you notice any inaccuracies or topics which are not covered by our documentation, then we encourage you to try authoring a Pull Request. You can also try working on issues that were opened by other community members.
Click here to view issues available in the forgejo/docs repository.
Further notes
- There exist many other Forgejo-related projects that one can contribute to.
- The Pull Requests Agreement lists the conditions for merging Pull Requests in Forgejo repositories.
- Forgejo contributions are subjected to testing requirements. Feel free to ask other contributors for help.
Compiling from source
You should install go and set up your go
environment correctly. In particular, it is recommended to set the $GOPATH
environment variable and to add the go bin directory or directories
${GOPATH//://bin:}/bin to the $PATH. See the Go wiki entry for
GOPATH.
Next, install Node.js with npm which is required to build the JavaScript and CSS files. The minimum supported Node.js version is 20.
[!note] Note When executing make tasks that require external tools, like
make misspell-check, Forgejo will automatically download and build these as necessary. To be able to use these, you must have the"$GOPATH/bin"directory on the executable path.
[!note] Note 2 Go version 1.24 or higher is required. However, it is recommended to obtain the same version as the continuous integration.
[!note] Note 3 If you want to avoid installing build dependencies manually, you can also build the Docker image, which runs the build process in a Docker image containing all the required dependencies.
Download
First, we must retrieve the source code.
git clone https://codeberg.org/forgejo/forgejo
Decide which version of Forgejo to build and install. Currently, there are
multiple options to choose from. The forgejo branch represents the current
development version.
To work with tagged releases, the following commands can be used:
git branch -a
git checkout v7.0.3
To build Forgejo from source at a specific tagged release (like v7.0.3), list the available tags and check out the specific tag.
git tag -l
git checkout v7.0.3
Build
To build from source, the following programs must be present on the system:
gitcoreutils(sed, grep, find, echo, ...)targzipgosee thego.modfile in the root of the repository for the minimum required versionnodeneeds to includenpm, the latest LTS should generally build fine. In doubt, thepackage-lock.jsoncontains node version constrains for every dependency.gccmake
For testing, linting and other build configuration, the following dependencies are also required:
gitwith lfs supportsqliteopenssh
Also see the shell.nix and manifest.scm in the root of the forgejo
repositories for Nix and Guix definitions of all dependencies.
There are a number of useful make targets, only some of which are documented here.
They can all be displayed with:
$ make help
Make Routines:
- "" equivalent to "build"
- build build everything
- frontend build frontend files
- backend build backend files
- watch watch everything and continuously rebuild
- watch-frontend watch frontend files and continuously rebuild
- watch-backend watch backend files and continuously rebuild
...
Depending on requirements, the following build tags can be included.
bindata: Build a single monolithic binary, with all assets included. Required for production build.sqlite sqlite_unlock_notify: Enable support for a SQLite3 database. Suggested only for small installations.pam: Enable support for PAM (Linux Pluggable Authentication Modules). Can be used to authenticate local users or extend authentication to methods available to PAM.
Using the bindata build tag is required for production
deployments. You could exclude bindata when you are
developing/testing Forgejo or able to separate the assets correctly.
To include all assets, use the bindata tag:
TAGS="bindata" make build
In the default release build of the continuous integration system, the build
tags are: TAGS="bindata timetzdata sqlite sqlite_unlock_notify". The simplest
recommended way to build from source is therefore:
TAGS="bindata timetzdata sqlite sqlite_unlock_notify" make build
The build target is split into two sub-targets:
make backendwhich requires Go v1.22 or greater.make frontendwhich requires Node.js 20 or greater.
If pre-built frontend files are present it is possible to only build the backend:
TAGS="bindata" make backend
Webpack source maps are by default enabled in development builds and disabled in production builds. They can be enabled by setting the ENABLE_SOURCEMAP=true environment variable.
[!note] Note If you have the
libvipslibrary installed globally, the frontend part of the build might fail withnpm error sharperror messages. In order to fix this, set theSHARP_IGNORE_GLOBAL_LIBVIPS=trueenvironment variable and rerun the build command.
Build the Docker image
To build Forgejo's Docker image, you need to have Docker and the Docker Buildx plugin installed. You can build the Docker image with:
docker buildx build --output=type=docker --tag forgejo:mybuild .
This will run the entire build process in a Docker container with the required dependencies.
You can also supply a tag during the build process with the -t option, to make it easier to publish or run the image later.
Build using GNU Guix
A GNU Guix package manager manifest.scm
file is also provided. It can be used to create a containerized
environment that has all the needed build and test dependencies, via a
single command:
guix shell -CNF --share=$HOME -m manifest.scm
It's important to set a few environment variables inside the guix shell environment for proper operation:
export GOTOOLCHAIN=local CC=gcc CGO_ENABLED=1
The GOTOOLCHAIN=local environment variable tells the Go build
machinery to prefer the Go binary provided by Guix instead of an
external copy fetched from the Go package server, which would fail to
load needed libraries with messages like error while loading shared libraries: libgcc_s.so.1.
A few sample commands for building and testing in the Guix container
are included in a comment in the manifest.scm file, for your
convenience.
Testing
See the section dedicated to testing.
Forgejo Contributor Guide
This area is targeted to people who want to improve Forgejo and adjust it to their needs.
We encourage you to read our warm welcome first, then get involved.
- For everyone involved
- Translations
- Localization - for translators
- English strings - for developers or people improving strings
- Weblate admin duties - for Forgejo contributors
- For UI/UX designers and user researchers
- For developers
- Compiling from source
- Testing
- Development environment
- Finding what to work on
- Interface customization
- Web browser support
- Architecture overview
- Threat Analysis
- Developer Certificate of Origin (DCO)
- code.forgejo.org
- next.forgejo.org kubernetes infrastructure
- static pages
- Forgejo Runner implementation notes
- Dependency management
- For maintainers
When you've contributed something, you'll have learned new things in the process. Why not share those things with other contributors? Creating a personal article is an option to do so. Once you do, create a Pull Request on this page and extend the list.
- Forgejo Action Notification Development (including exhaustive development and testing setup) by Christopher Besch on 20th November 2025
- The History of Forgejo's Pub-Sub Pattern by Christopher Besch on 30th October 2025
Hardware infrastructure
Moved permanently to https://code.forgejo.org/infrastructure/documentation#readme. A mirror is available at https://git.pub.solar/forgejo/infrastructure-documentation.
Managing dependencies between repositories
Some Forgejo repositories that depend on each other have workflows that will trigger workflows on other repositories using the cascading-pr action.
Use cases and examples
Files copied from other repositories
setup-forgejo contains a copy of the forgejo-curl script. This script does not have numbered releases and the latest version is the one in the main branch.
forgejo-curl has a workflow that will open a pull request to setup-forgejo to update the forgejo-curl.sh script it contains. The forgejo-curl workflow will wait on the setup-forgejo workflow and fail if it does not pass, thus providing additional confirmation that the change under test won't break setup-forgejo once merged.
When the PR is merged in forgejo-curl, the corresponding PR in setup-forgejo is left open and ready to be merged with the latest version of the forgejo-curl.sh script.
Verifying an upgrade would work
setup-forgejo installs a runner by default, as specified in the action.yml file.
The runner has a workflow that will open a pull request to setup-forgejo to update the default version in the action.yml file. The runner workflow will wait on the setup-forgejo workflow and fail if it does not pass, thus providing additional confirmation that the change under test won't break setup-forgejo when it upgrades to using a release that contains the change.
When the PR is merged in forgejo-curl, the corresponding PR in setup-forgejo is closed. It is not meant to upgrade setup-forgejo because there is not yet a tag release published with this change.
Permissions
The cascading-pr action needs a token with write permissions on issues and repositories that will allow it to fork the destination repository and create a pull request from this fork. It also needs read permission on issues and pull requests for the origin repository.
The cascading-pr user is dedicated to providing such tokens.
For instance, a personal token named
https://code.forgejo.org/forgejo/forgejo-curl/ was created by the
cascading-pr user. This token was added as two secrets named
CASCADING_PR_ORIGIN and CASCADING_PR_DESTINATION in the
https://code.forgejo.org/forgejo/forgejo-curl/ repository.
Access to secrets
The workflow that contains the cascading-pr action needs access to the
secrets of the repository and must run on.pull_request_target. For
instance:
on:
pull_request_target:
types:
- opened
- synchronize
- closed
Updating the workflow
When the cascading-pr workflow is added or updated in a repository, it
must be done in a PR from a branch of the repository and not from
a forked repository. It runs on.pull_request_target and if run from
a fork it will use the content of the default branch instead of the
proposed change.
Repository Migration
The sequence diagram below shows the different go packages in the top and bottom boxes and their functions within. You can find the relevant code mostly in the services/migrations folder. The arrows are to be read in sequence from top to bottom, while the description of each arrow shows the function and its simplified signature. Reference this diagram if you need a basic overview of how the packages in services/migrations are connected and how PR/Issue comments are filtered based on migration options.
The diagram was made with mermaid.js, you can find the documentation for it here.
Static pages
LXC container dedicated to hosting static HTML pages.
LXC container
See the static-pages section in the infrastructure documentation.
SSL on the LXC host
Each domain has a /etc/nginx/sites-available/f3.forgefriends.forgejo.org file similar to the following
on the host where the LXC container resides.
server {
listen 80;
listen [::]:80;
server_name f3.forgefriends.forgejo.org;
location / {
proxy_pass http://10.6.83.106:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Obtain the certificate:
ln -sf /etc/nginx/sites-available/f3.forgefriends.forgejo.org /etc/nginx/sites-enabled/f3.forgefriends.forgejo.org
sudo certbot -n --agree-tos --email contact@forgejo.org -d f3.forgefriends.forgejo.org --nginx
Creation in the LXC container
With the example of f3.forgefriends.forgejo.org and
f3.forgefriends.org serving the same content.
login
From the LXC host:
lxc-helpers.sh lxc_container_run static-pages -- sudo --user $USER bash
nginx
$ cat /etc/nginx/sites-enabled/f3.forgefriends.org
server {
listen 80;
listen [::]:80;
server_name f3.forgefriends.org f3.forgefriends.forgejo.org;
root /var/www/f3.forgefriends.org;
location / {
try_files $uri $uri/ =404;
}
}
clone
git clone https://code.forgejo.org/f3/html-documentation /var/www/f3.forgefriends.org
Update in the LXC container
Webhook
Create a POST webhook with the URL https://f3.forgefriends.forgejo.org/.well-known/forgejo/f3.forgefriends.org on https://code.forgejo.org/f3/html-documentation. It is expected to fail with 404, the information will be extracted from the web server logs.
To verify that it works:
journalctl -f --unit static-pagesTest deliveryat https://code.forgejo.org/f3/html-documentation/settings/hooks/4
Service
git pull on change
$ cat /usr/local/bin/static-pages.sh
##!/bin/bash
sudo tail -f /var/log/nginx/access.log | sed --silent --regexp-extended --unbuffered --expression 's|.*.well-known/forgejo/([^ /]+) .*|\1|p' | while read server ; do
d="/var/www/$server"
if test -d "$d" ; then
echo "update $server"
cd "$d"
git pull
else
echo "unknown server $server"
fi
done
service
$ cat /etc/systemd/system/static-pages.service
[Unit]
Description=Static pages
[Service]
User=debian
ExecStart=/usr/local/bin/static-pages.sh
[Install]
WantedBy=multi-user.target
$ sudo systemctl enable static-pages
Testing
If you want to contribute to Forgejo, keep in mind that we ask you to do a reasonable effort to test your changes.
Why would we do this? Your submission to Forgejo could be wasted if you add it, but no one checks that it still works in a few months, or a few years. We want your contribution to be meaningful and to last.
We cannot test all the behaviour of Forgejo. It is complex, and certain aspects are only important to a handful of users. To reduce the burden on the maintainers, you should do your best to make testing easy and automated.
What is a "reasonable" effort?
There is no simple answer to this, but we want to see that you gave your best. It will be decided on a case-by-case basis by the maintainers.
Some changes are easy to write a test for. Other tests can be really hard and challenging. This guide tries to provide you some starting points, and if you don't know how to continue, please submit your contribution and ask for help in writing a test. We will happily assist you.
How to write a good test?
Let's list some basic things when writing software tests. Creating good tests can be a creative challenge, and you'll benefit from experience (both from writing tests and working with the Forgejo codebase).
These basic rules should hopefully guide you the way.
Test the actual change
Some parts of the codebase don't bring extensive testing yet. Asking you to cover all the behaviour with tests would often be too much. If in doubt, try to focus on the change you made:
- Which behaviour did you change?
- Can you demonstrate that something works differently?
- Can you write a test that fails before, and passes after your actual contribution?
Take a look at this example. It tests a very simple change in diff generation. It does not add tests for all the (edge) cases of diff generation, but it provides a simple verification that the diff is generated on larger words, not letter-by-letter, by adding samples that are affected by the change.
Test the edge cases
If you test different input, consider the behaviour at edge cases. For example, what happens with an empty string? If you want to test a size limitation, do not only test content that is "way too large", also test content that is a perfect fit, and the first content size that should be rejected.
Get inspiration or help
Please don't hesitate to reach out for help or ask for feedback. In some cases, good knowledge of the codebase is helpful (e.g. to think of testing your feature's compatibility with another feature you never heard of).
We recommend that you take inspiration from existing tests. Take a look at the Git history around your files (or the kind of tests such as e2e/integration) to see who made recent changes. They have high chances of being more idiomatic and modern than the average test code, and feel free to ping people directly for help.
Getting Started
There are three kinds of tests in Forgejo.
Unit tests
These live next to the code in files suffixed
_test.go (for backend code)
or .test.js (for frontend code).
They test small portions of the code, mostly functions that process data and have no side-effects. Use these where possible.
Run these with:
make test
Integration tests
Find these in tests/integration.
They test more complex behaviour of the app
(such as performing an action and seeing that related activity is recorded).
They are mostly useful when a behaviour is hard to test with isolated unit tests.
There is more detailed information in the integration test README.
End-to-end / browser tests
Find these in tests/e2e.
They run real browsers and test the frontend of Forgejo.
Use them to verify that things look like they should,
or that pressing a specific button does what it is supposed to do.
You can also run an automated accessibility analysis.
There is more detailed information in the e2e test README.
Interactive testing during development
During development, you'll probably want to interact with Forgejo to see if your changes work.
To run and continuously rebuild when the source files change:
TAGS='sqlite sqlite_unlock_notify' make watch
[!note] Note Do not set the
bindatatag such as inTAGS="bindata" make watch. The browser may fail to load pages with an error likeFailed to load asset.
It will create, in the source directory:
- The
dataandlogdirectories - The
custom/conf/app.inifile after you complete the configuration interactively at http://127.0.0.1:3000
[!note] Note To enable the internal SSH server, manually add
START_SSH_SERVER = trueto the[server]section ofcustom/conf/app.ini, and ensure theSSH_PORToption is set to an unprivileged port (any port greater than 1024), such as2222.
Automated tests
In the Forgejo repository
When a pull request (PR) is opened, it will run workflows found in the .forgejo/workflows directory.
A number of checks carried out by these jobs need to pass before a PR can be
merged. The main part of these checks can be run locally by calling make pr-go. It is strongly recommended to do so before opening a PR, and the PR
template reminds about it.
In the end-to-end repository
[!note] Note Do not confuse this kind of testing with the "e2e" (frontend) tests in the Forgejo repository.
Some tests are best served by running Forgejo from a compiled binary,
for instance to verify the result of a workflow run by the Forgejo Runner. They can be
run by adding the run-end-to-end-test label to the pull request. It will:
- compile a binary from the pull request
- open a pull request against the end-to-end repository
- use the compiled binary to run the tests
- report back failure or success
Debugging locally
A workflow can be run locally by installing the Forgejo Runner and using the command line:
forgejo-runner exec --workflows .forgejo/workflows/testing.yml
Manual testing
When the change to be tested lacks the proper framework, we sometimes add manual testing instructions as a last resort. The exact test steps must be documented in the description of the pull request.
Changes that are associated with manual tests must be labeled "test/manual".
Using gotestsum for summaries
By default, the make targets use the built-in go test command.
However, it does not produce clear summaries of test results
which are useful for understanding quickly the extent of failures.
The provided Makefile supports switching to gotestsum
using USE_GOTESTSUM=yes while running tests, for example:
USE_GOTESTSUM=yes make test
gotestsum behaves like the default go test except it reformats the test
results so that they are clearer for human interpretation and include a helpful
summary.
You need to have gotestsum installed (see the link above) to use it as it does not come built-in unlike go test.
Welcome to Forgejo
We are happy to welcome you to the Forgejo project. Please take the time to read this page to the end.
Quality standards
The Forgejo community is motivated to deliver a high quality product to all our users. Please be aware that we:
- Understand our users before solving their problems (also see user research).
- Discuss our implementations prior to starting to code.
- Require that patches are tested well, either manually or automatically.
- Prioritize fixing problems over adding new functionality.
For you, this means that you:
- Are welcome to contribute more than just code (translations, UI/UX discussions etc).
- Should get involved with the community before writing code.
- Should start by fixing problems to help with the maintenance of the project (even if you just want to contribute a feature).
- Are prepared to dedicate enough time to meet the quality standards (e.g. for code review and test coverage).
Getting Started
These steps are recommended for all users:
- Join the Matrix channels (see link in footer) to stay in touch with the project
- Check the list of resources.
- Start by taking small contributions. For code, use the good first issues.
- Reach out via issues or chat if you encounter any problems or get stuck.
Getting into the project
Made your first contributions? Consider yourself a part of the community. Hang around in the chats, have fun with others and feel free to share your feedback, even on things you didn't yet contribute to.
We recommend you make yourself familiar with our governance repository and watch the activity for decisions you might want to participate in. Check out the teams and consider applying to those who match your interests.
Weblate admin duties
Localization team members with admin rights on both Forgejo and Weblate are expected to make sure the translations land in the Forgejo repository and resolve conflicts when they arise.
Merging translations in Forgejo
Weblate is configured to propose pull requests to the Forgejo repository with new translations. These pull requests are normally merged with a merge commit into the Forgejo development branch as follows:
- If the PR title has prefix [skip ci], remove it
- If no translation activity is expected before the merge, update the PR to
trigger a CI run:
- Change some translation(s). You can modify
[translation_meta]testin some language - Commit changes in the repository management of the main component
- Change some translation(s). You can modify
- Wait for a moment when there are no uncommitted translation changes and the CI checks are passed. Commit button will be disabled in repository management when there are no such changes.
- Merge the pending i18n: pull request with a commit message similar to this example:
- Visit Weblate history and make sure there's a message about successful rebase
Merging a pull request that changes translations
When a Forgejo pull request modifies locale files other than base which is en-US, it must be merged after all pending changes in Weblate are merged, while the impacted components are locked.
- lock Weblate component(s)
- merge the pending
i18n:pull request based on the instructions above - rebase the other PR if needed, merge it
- unlock component(s)
- make sure Weblate rebased successfully, you can use
Updatebutton in main component to trigget that
Resolving failures
Weblate locked due to network error
Sometimes a connectivity error with Codeberg or it's unavailability can cause Weblate to lock. The lock error looks like this:
kex_exchange_identification: Connection closed by remote host
Connection closed by 217.197.91.145 port 22
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
(128)
Weblate will retry connection attempts but it takes hours before it does that. If Codeberg is currently available and working, the project can simply be unlocked manually to allow the translators to keep working.
Commit changing non-base locales was merged before Weblate
If a commit changing translation files other than en_US.ini was merged before all changes from Weblate were merged, it could have caused Weblate to lock itself due to a failed rebase.
If the rebase did succeed, everything is ok and no steps need to be taken, just be more cautious with merges next time.
If Weblate failed to rebase, there are two ways to solve this issue.
Revert and merge in correct sequence
- Make a PR that reverts the commit that caused the breakage
- Merge this PR
- Make Weblate commit and push all changes
- Merge the Weblate PR
- Rebase the commit(s) of the PR that caused the breakage on top of the merged Weblate commit, open a new PR
Merge translations manually
- Fetch the branch that Weblate uses:
git remote add weblate https://translate.codeberg.org/git/forgejo/forgejogit fetch -u weblategit checkout weblate/forgejo - Checkout into a new branch:
git checkout -b i18n-weblate-recovery - Rebase it on top of latest commit in main Forgejo branch
git rebase -i <latest-commit> - Propose a new PR with fixed commit and merge it
- Close PR created by Weblate, reset Weblate
Alternatively to rebase you can also interactively cherry-pick the commit on top of a new branch based on latest.
Other merge strategies
Forgejo used to have different merge workflows based on squashing the PR. The old instructions are available in older version of the documentation.
English base localization
Forgejo base localization is English. This means that all translations are derived from it.
Managing strings
English localization strings are stored in two locations:
options/locale_next/locale_en-US.json- preferredoptions/locale/locale_en-US.ini- legacy, doesn't support culture-specific plurals
Strings are translated on Weblate and string management is partially done by it.
Additions and minor edits
Addition of new strings, deletion of obsolete strings and minor changes should be be submitted in pull requests as is. Edits are considered minor unless they include changes to handling of placeholders (%s, %[n]s, %d, %[n]d).
+ "new-string": "Hello, world!",
//
- "obsolete-string": "This string is no longer used",
//
- "edited-string": "Remove this runner",
+ "edited-string": "Remove runner",
When a new string is added, it must be double-checked that its key is unique and is not present already.
"repo.pulls.poster_trust_deny": "Deny",
// <1000 other lines>
"repo.pulls.poster_trust_deny": "Deny access", // Oh, no!
Changes to non-base files (translations) are must only be done on Weblate and not via individual pull requests to
- prevent merge conflicts with Weblate
- not bypass of reviews on Weblate, where translation reviers know better than code reviewers
- avoid wasting CI time and cluttering PR review queue
When a UI change renders some string unused, it must be deleted.
Deletions
Unused strings must only be deleted in base language. They will disappear from Weblate after the PR is merged. Removal of orphaned translations from the files is handled by Weblate for JSON and manually by the Localization team for INI components.
Avoiding deletion in non-localization pull requests prevents merge conflicts with Weblate changes and during backports, and saves time for both authors and reviewers.
Incompatible edits
String placeholders cannot be changed as is.
// This change to base locale
- "initial-key": "Remove this runner?",
+ "initial-key": "Remove runner %s?",
// Breaks string rendering in all other locales
"initial-key": "Изтриване на изпълнител?",
If there's an intention to preserve existing translations, a migration can be prepared in coordination with the Localization team admins:
-
preform the change in all locales - likely to conflict with Weblate, making it a rather complicated way
-
prepare replacement that can be executed in Weblate UI if the change is possible to automate by text replacement
For example, this change can be performed for all strings in Weblate UI avoiding conflicts with parameters:
- filter:
initial-key - search for:
%s - replacement string:
%[1]s
- "initial-key": "Remove %s?", + "initial-key": "Remove %[1]s?", - filter:
If it is deemed fine to replace the string, the key needs to be changed:
- "initial-key": "Remove this runner?",
+ "updated-key": "Remove %[1]s?",
This is the easiest option for those working on the UI changes, but the translators will have to re-do their work.
Refactoring keys
Sometime string keys are named poorly and need to be changed. In such cases they need to be mass-changed for all languages into which the strings have already been translated, so that the existing translations aren't lost. This includes merging the Weblate PR first and then re-applying the rename to avoid race conditions with changes from Weblate. It is a complicated process, so renaming keys is generally not recommended unless there's a good reason.
Localization style
Capitalization
All strings should have regular capitalization. Headers, labels, buttons and such should start with a capital letter. Only names, product names and such should be capitalized after that. Git/Forgejo specific measurement units should not be capitalized.
Follow these examples for string capitalization:
| Context | ❌ Bad | ✅ Good |
|---|---|---|
| Button | edit | Edit |
| Header | Manage Organizations | Manage organizations |
| Option | Use Custom Avatar | Use custom avatar |
| Button | Add Cleanup Rule | Add cleanup rule |
| Label | Integrate matrix into your repository. | Integrate Matrix into your repository. |
| Label | %s Commits | %s commits |
Other stylistic choices
Form labels should not end with any punctuation marks.
Follow these examples:
| Context | ❌ Bad | ✅ Good |
|---|---|---|
| Form label | Username. | Username |
| Form label | Username: | Username |
Ensuring good translatability
Strings expose the ability to edit the entire parts of text shown in the UI.
Using placeholders
When possible, strings should include placeholders for everything displayed within the given context. When there's more than one placeholder, it's best to use numbered placeholders.
Good example:
"event.user_pushed": "%[1]s pushed %[2]s on %[3]s"
{{ctx.Locale.Tr "event.user_pushed" .User .Commit .Commit.Time}}
Bad example:
In this example, the text is constructed in the template rather than with placeholders. This greatly reduces flexibility of translations and is confusing to translate.
"event.push.pushed": "pushed",
"event.push.on": "on"
{{.User}} {{ctx.Locale.Tr "event.push.pushed"}} {{.Commit}} {{ctx.Locale.Tr "event.push.on"}} {{.Commit.Time}}
Using complex plurals
For strings that include a number and an associated noun, a specific plural format must be used so the words in the translated text can be adapted to the appropriate plural forms, which vary in different languages.
Good example:
"n_commits_found": {
"one": "%d commit was found.",
"other": "%d commits were found."
}
{{ctx.Locale.TrPluralString .NumCommits "n_commits_found" .NumCommits}}
[!note] There's no dedicated
zerocase where placeholders would be unused like so:"n_commits_found": { //... "zero": "No commits were found.", }Instead, use Go template logic to handle such cases if needed:
"no_commits_found": "No commits were found.",{{if ne .NumCommits 0}} {{ctx.Locale.TrPluralString .NumCommits "n_commits_found" .NumCommits}} {{else}} <!-- This could also be displayed differently. E.g. more like a placeholder --> {{ctx.Locale.Tr "no_commits_found"}} {{end}}
Bad example:
In this example, no plural router is used. Such text can only be correctly displayed with all different numbers only in few languages.
"commits_found": "commits were found"
{{.NumCommits}} {{ctx.Locale.Tr "commits_found"}}.
Bad example:
In this example, simple plural router is used, but the strings include the numbers. This way, translated variants of the text can be displayed correctly in more languages than in previous example, but still in far from all languages.
"n_commits_found.one": "%d commit was found.",
"n_commits_found.few": "%d commits were found."
{{ctx.Locale.TrN .NumCommits "commits_found.one" "commits_found.few" .NumCommits}}
Using simple plurals
When strings should be written out differently depending on the number of objects they reference, but do not include the number, different variants should not be stored in the dict format as they would not benefit translatability in other languages.
Good example:
"config.parse_errors.one": "There was an error parsing the config:",
"config.parse_errors.few": "There were errors parsing the config:"
{{ctx.Locale.TrN (len .Errors) "config.parse_errors.one" "config.parse_errors.few"}}
<ul>
{{range .Errors}}
<li>{{.}}</li>
{{end}}
</ul>
Bad example:
In this example, usage of dict format and TrPluralString provides no benefit to languages that use more than two plural variants.
"config.parse_errors": {
"one": "There was error parsing the config:",
"other": "There were errors parsing the config:"
}
{{ctx.Locale.TrPluralString (len .Errors) "config.parse_errors"}}
Formatting numbers
Large numbers can be formatted as pretty strings like so:
"n_commits_found": {
"one": "%s commit was found.",
"other": "%s commits were found."
}
{{ctx.Locale.TrPluralString .NumCommits "n_commits_found" (CountFmt .NumCommits)}}
Result: 1,000,000 commits were found.
Styling of placeholders
It is best to avoid wrapping placeholders in HTML tags. This makes translating unnecessarily harder.
Good example:
"n_commits_found": {
"one": "%s commit was found.",
"other": "%s commits were found."
}
For trusted content such as numbers, printf and TrustHTML can be used:
{{ctx.Locale.TrPluralString .CommitsCount "n_commits_found" (printf "<b>%d</b>" .CommitsCount | TrustHTML)}}
Good example:
"user_invited": "You're invited to team %s."
For untrusted content such as user provided strings, HTMLFormat must be used:
{{ctx.Locale.Tr "user_invited" (HTMLFormat "<b>%s</b>" .Team.Name)}}
Bad example:
In this example, HTML tags are polluting the translation file.
"n_commits_found": {
"one": "<b>%s</b> commit was found.",
"other": "<b>%s</b> commits were found."
}
Styling of text
When the intention is to highlight a part the string, or to make a link, tags are welcome in the strings. For links, storing the actual URLs in the strings must be avoided.
Good example:
"warning": "This change is <strong>irreversible</strong>. Proceed?",
"with_link": "See <a href=\"%s\">the documentation</a> for pattern syntax."
{{ctx.Locale.Tr "warning"}}
{{ctx.Locale.Tr "with_link" "https://forgejo.org/docs/latest/"}}
Bad example:
In this example, the word irreversible is moved out of the full string to avoid HTML tags in translations. However, this creates unnecessary complexity in form of multiple strings.
Additionally, the link is stored in the translations, making it more difficult to update.
"warning": "This change is %s. Proceed?",
"irreversible": "irreversible",
"with_link": "See <a href=\"https://forgejo.org/docs/latest/\">the documentation</a> for pattern syntax."
{{ctx.Locale.Tr "warning" (HTMLFormat "<strong>%s</strong>" ctx.Locale.Tr "irreversible")}}
{{ctx.Locale.Tr "with_link"}}
[!note] The link example doesn't apply to links not included in
<a>tags. For example, it is good to leave example addresses for translators.example.comhas localized variants."email.placeholder": "Email (e.g. test@example.com)",
Parts that don't need to be translated
If something doesn't need to be translated, it is best to include it via placeholder.
Good example:
"unsupported_field.permissions": "The %s field is not supported."
{{ctx.Locale.Tr "unsupported_field.permissions" ("<code>permissions</code>" | TrustHTML)}}
Bad example:
In this example, the part that doesn't need to be translated is stored in the string along with tags. Translators will have to copy-paste it. Some will try to translate it.
"unsupported_field.permissions": "The <code>permissions</code> field is not supported."
{{ctx.Locale.Tr "unsupported_field.permissions"}}
Contributing
This section is to help you contribute to the English localization of Forgejo.
Suggesting improvements
You send bugs, suggestions and feedback via Forgejo issue tracker or Forgejo Localization Matrix chatroom. Comments on Weblate also might work but are not recommended because they're checked rarely and might not be noticed.
Proposing changes
Similarly to other projects you can propose changes via a pull request. Read the informative sections for string managing and style guidelines above. You can perform small changes via Forgejo web UI. Otherwise you can make a fork or use AGit workflow, clone, checkout a new branch, make your changes, commit, push and open a pull request. Put some useful explanation of the change in the PR description. Screenshots are also highly welcome.
If it's a small change to the string, you don't need to test it locally. However, if you want to make a larger text refactoring, consider verifying that everything looks good together in live by building and running Forgejo locally. See For contributors for more information how to do this.
If your refactor affects key names, make sure to perform a mass-rename of the affected keys in all code, templates and translations. Sometimes keys are generated in code with conditions. Please check for that as well. With such a refactoring you might be asked to resolve merge conflicts before your PR can be merged. If you have low availability, it is best to avoid renaming translation keys.
Localization
Forgejo is translated via Weblate, a libre web-based translation platform.
Translating via Weblate
The Forgejo project's localization project is publicly available via the Codeberg Translate Weblate instance.
Translation guidelines
- Only suggest changes that benefit all users of the translation. Please do not suggest changes that will only make the translation better in cases specific to any self-hosted Forgejo instance. Instead customize such instances separately from Forgejo upstream.
- Keep the translation as beginner-friendly as possible.
- Users are not obligated to complete any translation. When unsure about the translation, feel free to leave it for others to translate.
Discovering the translation
Go to the Project page for a list of languages that are currently available for translation.
From the language page it is possible to browse all translation strings, as well as untranslated, unfinished and failing translations.
Suggesting changes
Anonymous suggestions for changes and additions to the existing translation can be submitted by finding the string for which to suggest a change, typing the change in, and then clicking "Suggest".
All suggested change will be checked before being accepted. Since most localization members are likewise volunteers, this can take a while.
Making direct changes, accepting suggestions
Direct changes require a Codeberg account which can be used to access the Codeberg Translate account.
If the translation is not approved it is possible to edit the string again and use the "Save" button to save the change. Existing suggestions can either be applied, or rejected by optionally specifying the rejection reason.
Once the string is translated and approved it can only be changed by a Forgejo Localization Team member, though everyone else is still able suggest changes.
To protect the existing translations from vandalism, all strings imported from Gitea were automatically marked as approved.
Adding a new language
If your language is not present among languages in the project, it must be added before you'll be able to start translating.
New languages are added per-request. Make one by contacting the Localization team. The most reliable ways are listed in Discussions.
E-mail privacy
By default, Weblate will use your primary e-mail address for all contributions. If you want to adjust this behavior, go to Weblate settings - Account and select a different e-mail under the "Commit e-mail" section. Select a noreply address to avoid using a real one.
Tips
Finding strings through UI
It is possible to find keys corresponding to translation strings on a page quickly by appending a lang=dummy query parameter to the URL. It will make all texts on a page use their keys instead of the actual translations. For example:
# If no parameters are present:
- https://codeberg.org/user/settings
+ https://codeberg.org/user/settings?lang=dummy
# If other parameters are present:
- https://codeberg.org/translate?tab=stars
+ https://codeberg.org/translate?tab=stars&lang=dummy
Searching for strings on Weblate
The following applies to the forgejo component which uses the INI format.
When searching on Weblate, the first part of keys ("section") may need to be wrapped with []. For example, on Weblate, settings.enable_custom_avatar should be searched with [settings]enable_custom_avatar, issues -> [common]issues, and filter.public -> [common]filter.public. If you are getting no results, wrap the section and search again.
Instance with up-to-date translations
The most up to date translations can be seen in live on a dev instance: https://dev.next.forgejo.org. The translations here may lag behind Weblate for up to a month, but it may still better than a production instance for a quick overview.
Discussions
You can ask for help, discuss translations, make suggestions for source strings, or contact localization maintainers through appropriate channels:
- Dedicated Matrix room
- Forgejo issues - for suggestions to source strings
- Forgejo discussions - for other questions more specific to localization
Joining the Localization Team
Any Codeberg Translate user is able to suggest translations, translate new strings, add comments and discuss existing translations with our Localization Team.
If you would like to maintain the translation, join the Localization Team as a member by sending us an application.
However, before doing that, we recommend working on translations independently before applying. This allows time to get used to the workflow and collaborating within the Localization Team. Members are able to accept their own suggestions.
Applying
In order to become a member of the team apply by opening a new issue at forgejo/governance repository. See previous applications for inspiration.
In the application message, state the following:
- Motivation for becoming a member.
- Experience translating other projects and using Weblate. E.g. link(s) to public translation profile(s) or contribution(s).
The application process will take approximately two (2) weeks (or more) to complete.
Responsibilities
Becoming a part of any of our team comes with a couple of responsibilities:
- Members must act in accordance to the Code of Conduct.
- Members must act in accordance to all other rules and process that Forgejo agrees on through its decision making process.
Translations should aim to target people of different backgrounds across all reasonable end user locales.
Since text is highly subjective, this is simply a goal that should be striven for and not a measurable requirement. Remain receptive to creative feedback from the Localization Team members.
Troubleshooting
When having problems using Codeberg Translate, there are multiple support channels available:
- Weblate documentation
- Weblate issues
- Codeberg Translate chat room
- Codeberg community issues
- Forgejo localization discussions
Service Messages
Service Messages Feature Description
Mastodon instances like mastodon.art provide their community rules via a mandatory dialog that has to be accepted before sign up. Similarly Forgejo instances could benefit from a dedicated "Service Messages" Mechanism that allows organizations to prominently show their guidelines to users on certain occasions in the login flow. The requirement here is that agreement to the guidelines happens after sign up. Also this feature is a requirement for compliance within regulated entities. Here, regulators require that certain legal information reach the user and the user decision to accept the info needs to be recorded.
For example:
- Community Guidelines could be shown to users once on first sign in and after they have been updated with a prominent pop up
- Acceptance of these guidelines may be a condition to continue
See uctkm002-admin-sets-service for an exemplary flow
Requirements
- uctkm002-1: When a user logs in for the first time, an "Service Message" should be shown to the user.
- uctkm002-2: When the Service Message is changed, it should be shown again to all users on their next login.
- uctkm002-3: Due to compliance, it is necessary to document that the user has seen and agreed to the message presented. To continue interacting with forgejo an "Accept" Button needs to be clicked.
- uctkm002-4: When the "Accept" Button named in uctkm002-3 is clicked, the timestamp of the consent action must be saved in the database.
- (Not needed currently) uctkm002-5: If more than one text needs to be shown to the user, the admin can create and manage these texts in Forgejo and set options for them individually.
- (Not needed currently) uctkm002-6: A "Service Message" can be configured so that it either requires interaction to proceed (it can be blocking the user flow) or is shown passively and can be discarded by the user at will.
Feature Description
A Service Message is a notification to all users that can only be set by an instance admin. There is currently one kind of Service Message: "modal".
- A "Modal" is visually prominent and stops the user in their workflow so interaction is mandatory
- To continue, the user must press the Confirm button
- On confirming the Modal, the confirmation time stamp will be saved along with the user id in the
service_messagestruct - On denying the Modal, the user is logged out
- The aim of the Modal is to make sure information is read and acknowledged and the acknowledgment is recorded
The management UI will provide a typed text input field with markdown and preview capability. Saving the field will immediately show the service message to any logged in user (also the admin) after their next request to the UI. With this behaviour we can also reach users who might be logged in for a longer period of time.
Technical Diagrams
The connection between templates and routers/web is described here uctkm002-frontend-backend-connection.
An initial technical view of how a confirmation may be saved: uctkm002-saving-confirmation.
ADR
Web Endpoints for Management
To avoid conflicts with the existing structure the service message was implemented using:
- A dedicated web endpoint
admin/service_messagefor the management of the feature is implemented - A dedicated template
admin/service_messageand a dedicated form is used
POST /admin/service_message/ # Create, Update POST /admin/service_message/delete_modal # Delete GET /admin/service_message/ # Show current service message
Logic for showing the service message
Uctkm002-1&2 have in common, that the user has not accepted an existing modal service message yet. Leveraging that fact, we just need to check the following:
- Did the user accept the service message yet?
- Is the service message timestamp of the user older than the timestamp of the current service message?
If any of these questions are answered yes, the service message is shown.
Distinction by type
Considering the requirements and feature description, it becomes apparent that probably only few types of Service Messages will exist (and be shown at the same time).
So for now it makes sense to have typed input fields in the admin management UI. Those fields will then send a form POST to {{AppSubUrl}}/admin/service_message?sm_type="modal". This saves us creating and managing options while being clearer to the admin as well. Currently modal will be the only type. If more Service Messages need to be shown, the process could include adding types like banner.
Confirm button and saving a timestamp to database
It makes sense to save the confirmation in the user model for the following reasons:
- Saving the confirmation per user in ServiceMessage would require potentially hundreds of thousands of requests to the DB to a single table.
- Cleanup Jobs would need to be done to also delete users from the ServiceMessage table
- RAM usage will increase if we always have a huge list of users loaded when a ServiceMessage is loaded from DB
- The
user/{username}endpoint requires an authenticated request, so we can make sure the user is already logged in. - The User Model is already loaded and interacted upon when a user is logged in
Threat Analysis
Threat Analysis: Remote Login Propagation
See also blog: geballte sicherheit for getting an idea about the analysis.
Technical Background
Control Flow
Data transferred
# NodeInfoWellKnown
{"links":[
{"href":"https://federated-repo.prod.meissa.de/api/v1/nodeinfo",
"rel":"http://nodeinfo.diaspora.software/ns/schema/2.1"}]}
# NodeInfo
{"version":"2.1",
"software":{"name":"gitea",
...}}
# LikeActivity
{"id": "https://repo.prod.meissa.de/api/v1/activitypub/user-id/1/outbox/12345",
"type": "Like",
"actor": "https://repo.prod.meissa.de/api/v1/activitypub/user-id/1",
"object": "https://codeberg.org/api/v1/activitypub/repository-id/12"
"startTime": "2014-12-31T23:00:00-08:00"
}
# Person
{"id":"https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/10",
"type":"Person",
"preferredUsername":"stargoose9",
"name": "goose going to star the repo",
"publicKey":{"id":"https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/10#main-key",
"owner":"https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/10",
"publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBoj...XAgMBAAE=\n-----END PUBLIC KEY-----\n"}}
Data Flow
Analysis
Assets
- Service Availability: The availability of our or foreign servers.
- Instance Reputation: We hope our project does not live on a spam instance.
- Project Reputation: The reputation of an individual project.
Actors
- Script Kiddies: Bored teens, willing to do some illegal stuff without deep knowledge of tech details but broad knowledge across internet discussions. Able to do some bash / python scripting.
- Experienced Hacker: Hacker with deep knowledge.
- Hacker: Hacker with some knowledge.
- Malicious Fediverse Member: Malicious Members of the fediverse, able to operate malicious forge instances.
- Malicious Forge Admin: Admin of good reputation forge instance in the fediverse.
- Federated User: Members of good reputation forge instance in the fediverse.
Threat
- tbd
Mitigations
- tbd
DREAD-Score
| Threat | Damage | Reproducibility | Exploitability | Affected Users | Discoverability | Mitigations |
|---|---|---|---|---|---|---|
| 1. | ... tbd | |||||
| 2. | ... tbd |
Threat Score with values between 1 - 6
- Damage – how severe would the damage be if the attack is successful? 6 is a very bad damage.
- Reproducibility – how easy would the attack be reproducible? 6 is very easy to reproduce.
- Exploitability – How much time, effort and experience are necessary to exploit the threat? 6 is very easy to make.
- Affected Users – if a threat were exploited, how many percentage of users would be affected?
- Discoverability – How easy can an attack be discovered? Does the attacker have to expect prosecution? 6 is very hard to discover / is not illegal

