Compare commits
No commits in common. "ab7fdd50d72ef59ff24bb48abb6bd583d20d5c54" and "47fbd4bb11b7574ebaf700871226ce542acc5a5c" have entirely different histories.
ab7fdd50d7
...
47fbd4bb11
35
.env.example
35
.env.example
@ -1,35 +0,0 @@
|
|||||||
# Copy to .env (./kit.sh init does it for you) and edit. .env is git-ignored.
|
|
||||||
|
|
||||||
# How to run: "docker" (docker compose, official image) or "binary" (official binary, no Docker).
|
|
||||||
KIT_MODE=docker
|
|
||||||
|
|
||||||
APP_NAME="My Gitea"
|
|
||||||
# Public hostname and URL (what people type in the browser). Trailing slash required.
|
|
||||||
DOMAIN=localhost
|
|
||||||
ROOT_URL=http://localhost:3000/
|
|
||||||
HTTP_PORT=3000
|
|
||||||
# Docker: host interface the port is published on. 127.0.0.1 = only reachable through your proxy/tunnel.
|
|
||||||
BIND_ADDR=127.0.0.1
|
|
||||||
# Set to true when ROOT_URL is https.
|
|
||||||
COOKIE_SECURE=false
|
|
||||||
|
|
||||||
# Sign-up policy
|
|
||||||
DISABLE_REGISTRATION=false
|
|
||||||
MAX_REPOS_PER_USER=10
|
|
||||||
REGISTER_EMAIL_CONFIRM=false
|
|
||||||
MAILER_ENABLED=false
|
|
||||||
|
|
||||||
# First admin (created by ./kit.sh setup). Leave ADMIN_PASSWORD empty to generate one
|
|
||||||
# (saved to secrets/admin.password, mode 0600).
|
|
||||||
ADMIN_USER=admin
|
|
||||||
ADMIN_EMAIL=admin@example.com
|
|
||||||
ADMIN_PASSWORD=
|
|
||||||
|
|
||||||
# Default branch protection applied by ./kit.sh protect / protect-all.
|
|
||||||
PROTECT_BRANCH=main
|
|
||||||
# Approvals needed to merge a PR (0 lets a solo admin merge their own PRs; 1+ for teams).
|
|
||||||
REQUIRED_APPROVALS=0
|
|
||||||
|
|
||||||
# Binary mode only: where Gitea keeps its data and config.
|
|
||||||
GITEA_HOME=/var/lib/gitea
|
|
||||||
GITEA_CONF=/etc/gitea/app.ini
|
|
||||||
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,7 +0,0 @@
|
|||||||
.env
|
|
||||||
config/app.ini
|
|
||||||
data/
|
|
||||||
secrets/
|
|
||||||
bin/
|
|
||||||
backups/
|
|
||||||
*.zip
|
|
||||||
21
LICENSE
21
LICENSE
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2026 The gitea-selfhost contributors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
178
README.md
178
README.md
@ -1,179 +1,3 @@
|
|||||||
# gitea-selfhost
|
# gitea-selfhost
|
||||||
|
|
||||||
Run your own [Gitea](https://about.gitea.com/) code host, set up the way the new internet's
|
Self-host kit: run your own Gitea with PR-only main, open sign-up behind a captcha, and per-agent tokens. Docker or binary + systemd.
|
||||||
hosted Gitea is: public repos browsable without an account, open sign-up behind a captcha,
|
|
||||||
`main` protected so every change lands through a pull request, and one Gitea user plus one
|
|
||||||
scoped token per agent.
|
|
||||||
|
|
||||||
Two ways to run it, same config:
|
|
||||||
|
|
||||||
| | Docker (default) | No Docker |
|
|
||||||
|---|---|---|
|
|
||||||
| What runs | official image `gitea/gitea:28.1.0-rootless` | official binary 28.1.0 (sha256-verified download) |
|
|
||||||
| Start | `./kit.sh up` | `KIT_MODE=binary` in `.env`, then `./kit.sh up` (or a systemd service) |
|
|
||||||
| Data | `./data` | `GITEA_HOME` (default `/var/lib/gitea`) |
|
|
||||||
| Config | `./config/app.ini` | `GITEA_CONF` (default `/etc/gitea/app.ini`) |
|
|
||||||
|
|
||||||
## One-command start
|
|
||||||
|
|
||||||
```sh
|
|
||||||
git clone <this repo> gitea-selfhost && cd gitea-selfhost
|
|
||||||
./kit.sh up
|
|
||||||
```
|
|
||||||
|
|
||||||
The first run creates `.env` from `.env.example`. Edit it (at least `DOMAIN` and `ROOT_URL`), then
|
|
||||||
run `./kit.sh up` again. It will:
|
|
||||||
|
|
||||||
1. render `config/app.ini` from `config/app.ini.template` with fresh random secrets (generated on
|
|
||||||
your machine, never committed);
|
|
||||||
2. start Gitea (Docker, or the downloaded binary) and wait for `/api/healthz`;
|
|
||||||
3. create the admin user (password generated into `secrets/admin.password` unless you set
|
|
||||||
`ADMIN_PASSWORD`) and an admin API token in `secrets/admin.token` (mode 0600).
|
|
||||||
|
|
||||||
Then open `ROOT_URL`. Needs: `bash`, `curl`, `python3` (for `protect-all`), and Docker with the
|
|
||||||
compose plugin (Docker mode).
|
|
||||||
|
|
||||||
### No Docker: binary + systemd
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sed -i 's/^KIT_MODE=.*/KIT_MODE=binary/' .env
|
|
||||||
sudo ./kit.sh install-service
|
|
||||||
```
|
|
||||||
|
|
||||||
As root, that downloads and verifies the binary, creates a `git` system user, installs
|
|
||||||
`/usr/local/bin/gitea`, renders the config into `GITEA_CONF`, installs and starts
|
|
||||||
`systemd/gitea.service`, and creates the admin + token. Run later kit commands with `sudo`.
|
|
||||||
|
|
||||||
Without systemd, `./kit.sh up` in binary mode runs Gitea in the background with a pid file under
|
|
||||||
`GITEA_HOME` (set `GITEA_HOME` / `GITEA_CONF` in `.env` to paths you can write).
|
|
||||||
|
|
||||||
## Commands
|
|
||||||
|
|
||||||
```
|
|
||||||
./kit.sh up init + start + admin setup
|
|
||||||
./kit.sh protect OWNER/REPO [BRANCH] block direct pushes to BRANCH (default main); PRs only
|
|
||||||
./kit.sh protect-all protect the default branch of every non-empty repo
|
|
||||||
./kit.sh agent NAME one Gitea user + one scoped token for an agent
|
|
||||||
./kit.sh backup gitea dump into ./backups
|
|
||||||
./kit.sh status | down
|
|
||||||
```
|
|
||||||
|
|
||||||
## What the config does (`config/app.ini.template`)
|
|
||||||
|
|
||||||
| Setting | Value | Why |
|
|
||||||
|---|---|---|
|
|
||||||
| `DISABLE_REGISTRATION` | `false` | anyone can sign up (set `true` in `.env` for invite-only) |
|
|
||||||
| `ENABLE_CAPTCHA`, `CAPTCHA_TYPE` | `true`, `image` | built-in image captcha on sign-up, no third party |
|
|
||||||
| `REGISTER_EMAIL_CONFIRM` | `false` | turn on only after `[mailer]` really delivers mail |
|
|
||||||
| `REQUIRE_SIGNIN_VIEW` | `false` | public repos are browsable without an account |
|
|
||||||
| `DEFAULT_ALLOW_CREATE_ORGANIZATION` | `false` | new users cannot create organizations |
|
|
||||||
| `MAX_CREATION_LIMIT` | `10` | repos per regular user (admins are exempt) |
|
|
||||||
| `DISABLE_MIGRATIONS`, `[mirror] DISABLE_NEW_PULL` | `true` | no importing or mirroring from arbitrary URLs |
|
|
||||||
| `[security] EGRESS_MODE` | `lax` | webhooks may call public hosts only, not your LAN or loopback |
|
|
||||||
| `DEFAULT_KEEP_EMAIL_PRIVATE` | `true` | new users' emails hidden by default |
|
|
||||||
| `DEFAULT_BRANCH` | `main` | |
|
|
||||||
| `DISABLE_SSH` | `true` | Git over HTTPS with a token; nothing to expose but one HTTP port |
|
|
||||||
| `SSH_CREATE_AUTHORIZED_KEYS_FILE` | `false` | Gitea never touches the host user's `~/.ssh` |
|
|
||||||
|
|
||||||
Gitea has no instance-wide "protect main in every new repo" switch, so the kit applies the rule
|
|
||||||
per repo: `./kit.sh protect OWNER/REPO`, or `./kit.sh protect-all` from a timer
|
|
||||||
(`systemd/gitea-protect.timer` runs it every 5 minutes; with cron:
|
|
||||||
`*/5 * * * * cd /opt/gitea-selfhost && ./kit.sh protect-all`). The rule blocks direct pushes for
|
|
||||||
everyone, admins included; changes land through a PR. `REQUIRED_APPROVALS` in `.env` sets how many
|
|
||||||
approvals a PR needs (0 lets a solo admin merge their own PR; Gitea never lets you approve your own).
|
|
||||||
|
|
||||||
## Agents and tokens
|
|
||||||
|
|
||||||
- One Gitea user per agent: `./kit.sh agent my-agent`. The token (in `secrets/agents/my-agent.token`)
|
|
||||||
has `write:repository`, `write:issue` and `write:user`: it can create its own repos, push branches and
|
|
||||||
open PRs, but has no admin scope.
|
|
||||||
- Git remote: `https://<your host>/<owner>/<repo>.git`, username = agent name, password = token.
|
|
||||||
Keep it out of URLs and shell history: `git config credential.helper store` with a 0600 file, or
|
|
||||||
`GIT_ASKPASS`.
|
|
||||||
- API: `Authorization: token <token>`.
|
|
||||||
- Give an agent access to someone else's repo by adding it as a collaborator (write), not by sharing
|
|
||||||
tokens. Never hand out the admin token. Rotate a token by generating a new one and deleting the old
|
|
||||||
one in the user's settings (Applications).
|
|
||||||
|
|
||||||
## Put it behind a tunnel or reverse proxy
|
|
||||||
|
|
||||||
Keep Gitea on loopback (`BIND_ADDR=127.0.0.1`) and publish it through a proxy that terminates TLS.
|
|
||||||
Set `ROOT_URL` to the public URL and `COOKIE_SECURE=true` once it is https, then restart.
|
|
||||||
|
|
||||||
**Caddy** (automatic HTTPS):
|
|
||||||
|
|
||||||
```
|
|
||||||
git.example.com {
|
|
||||||
reverse_proxy 127.0.0.1:3000
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**nginx**:
|
|
||||||
|
|
||||||
```nginx
|
|
||||||
server {
|
|
||||||
server_name git.example.com;
|
|
||||||
client_max_body_size 512m;
|
|
||||||
location / {
|
|
||||||
proxy_pass /gitea;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Under a sub-path (`https://example.com/git/`), set `ROOT_URL = https://example.com/git/` and strip
|
|
||||||
the prefix in the proxy (`location /git/ { proxy_pass /gitea/; ... }`).
|
|
||||||
|
|
||||||
**Cloudflare Tunnel** (no open ports, no public IP needed):
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# quick, temporary URL (changes when cloudflared restarts; update ROOT_URL each time)
|
|
||||||
cloudflared tunnel --url /gitea
|
|
||||||
# stable hostname on your own domain
|
|
||||||
cloudflared tunnel login
|
|
||||||
cloudflared tunnel create gitea
|
|
||||||
cloudflared tunnel route dns gitea git.example.com
|
|
||||||
cloudflared tunnel run --url /gitea gitea
|
|
||||||
```
|
|
||||||
|
|
||||||
## Backups
|
|
||||||
|
|
||||||
```sh
|
|
||||||
./kit.sh backup # backups/gitea-dump-YYYYmmdd-HHMMSS.zip: repos, database, config
|
|
||||||
```
|
|
||||||
|
|
||||||
The dump contains your `app.ini` secrets and every repo: store it encrypted and off the machine.
|
|
||||||
Also keep a copy of `.env` and `secrets/`. Restore = stop Gitea, unzip the dump, put the repos,
|
|
||||||
`gitea.db` and `app.ini` back in place (see the upstream
|
|
||||||
[backup and restore guide](https://docs.gitea.com/administration/backup-and-restore)), start Gitea.
|
|
||||||
|
|
||||||
## Upgrades
|
|
||||||
|
|
||||||
1. `./kit.sh backup`.
|
|
||||||
2. Read the [release notes](https://blog.gitea.com/) for breaking config changes.
|
|
||||||
3. Docker: change the image tag in `docker-compose.yml`, then `docker compose pull && docker compose up -d`.
|
|
||||||
Binary: change `GITEA_VERSION` in `kit.sh`, `rm bin/gitea && ./kit.sh up`
|
|
||||||
(systemd: `sudo install -m 0755 bin/gitea /usr/local/bin/gitea && sudo systemctl restart gitea`).
|
|
||||||
4. Gitea migrates the database on start. Check `./kit.sh status` and the log.
|
|
||||||
|
|
||||||
## Test the kit
|
|
||||||
|
|
||||||
```sh
|
|
||||||
tests/e2e-binary.sh [PORT]
|
|
||||||
```
|
|
||||||
|
|
||||||
Runs the whole flow in a throwaway directory on a spare port (default 3990): download and verify the
|
|
||||||
binary, start, create the admin, create a repo, protect `main`, check that a direct push to `main` is
|
|
||||||
rejected (even for the admin) while a PR merges, create an agent token (own repo yes, admin API no),
|
|
||||||
check the new-user defaults (no org creation, 10-repo limit), migrations off, captcha on sign-up,
|
|
||||||
backup. Then it stops Gitea and deletes everything.
|
|
||||||
|
|
||||||
## Licenses
|
|
||||||
|
|
||||||
- **Gitea** is free software under the MIT License, copyright the Gitea authors
|
|
||||||
(<https://github.com/go-gitea/gitea>). This kit does not include or modify Gitea: it downloads the
|
|
||||||
official binary or image.
|
|
||||||
- **This kit** (scripts, templates, docs) is MIT-licensed too; see [LICENSE](LICENSE).
|
|
||||||
@ -1,99 +0,0 @@
|
|||||||
; Gitea app.ini template for gitea-selfhost.
|
|
||||||
; Rendered by ./kit.sh init into config/app.ini with fresh random secrets.
|
|
||||||
; Placeholders (dollar + braces) are filled from .env (see .env.example). Do not commit the rendered file.
|
|
||||||
|
|
||||||
APP_NAME = ${APP_NAME}
|
|
||||||
RUN_MODE = prod
|
|
||||||
WORK_PATH = ${WORK_PATH}
|
|
||||||
|
|
||||||
[server]
|
|
||||||
PROTOCOL = http
|
|
||||||
DOMAIN = ${DOMAIN}
|
|
||||||
HTTP_ADDR = ${HTTP_ADDR}
|
|
||||||
HTTP_PORT = ${HTTP_PORT}
|
|
||||||
; The public URL people type in the browser (behind your tunnel / reverse proxy).
|
|
||||||
ROOT_URL = ${ROOT_URL}
|
|
||||||
; How Gitea reaches itself (hooks, internal API). Always loopback.
|
|
||||||
LOCAL_ROOT_URL = http://127.0.0.1:${HTTP_PORT}/
|
|
||||||
APP_DATA_PATH = ${WORK_PATH}/data
|
|
||||||
; HTTP(S) only: Git over smart HTTP with a username + access token.
|
|
||||||
DISABLE_SSH = true
|
|
||||||
START_SSH_SERVER = false
|
|
||||||
; Never touch the host user's ~/.ssh/authorized_keys.
|
|
||||||
SSH_CREATE_AUTHORIZED_KEYS_FILE = false
|
|
||||||
OFFLINE_MODE = true
|
|
||||||
LFS_START_SERVER = false
|
|
||||||
|
|
||||||
[database]
|
|
||||||
DB_TYPE = sqlite3
|
|
||||||
PATH = ${WORK_PATH}/data/gitea.db
|
|
||||||
|
|
||||||
[repository]
|
|
||||||
ROOT = ${WORK_PATH}/git/repositories
|
|
||||||
DEFAULT_BRANCH = main
|
|
||||||
DEFAULT_PRIVATE = public
|
|
||||||
; Max repos per regular user (-1 = unlimited). Admins are exempt.
|
|
||||||
MAX_CREATION_LIMIT = ${MAX_REPOS_PER_USER}
|
|
||||||
; No importing / mirroring from arbitrary URLs.
|
|
||||||
DISABLE_MIGRATIONS = true
|
|
||||||
ENABLE_PUSH_CREATE_USER = false
|
|
||||||
ENABLE_PUSH_CREATE_ORG = false
|
|
||||||
|
|
||||||
[mirror]
|
|
||||||
; No new pull mirrors either.
|
|
||||||
DISABLE_NEW_PULL = true
|
|
||||||
|
|
||||||
[security]
|
|
||||||
INSTALL_LOCK = true
|
|
||||||
SECRET_KEY = ${SECRET_KEY}
|
|
||||||
INTERNAL_TOKEN = ${INTERNAL_TOKEN}
|
|
||||||
MIN_PASSWORD_LENGTH = 10
|
|
||||||
PASSWORD_CHECK_PWN = false
|
|
||||||
; Outgoing requests (webhooks, OAuth2 clients) may reach public hosts only; loopback / LAN
|
|
||||||
; targets need an explicit [security] ALLOWED_HOST_LIST entry. Blocks SSRF into your network.
|
|
||||||
EGRESS_MODE = lax
|
|
||||||
|
|
||||||
[oauth2]
|
|
||||||
JWT_SECRET = ${JWT_SECRET}
|
|
||||||
|
|
||||||
[service]
|
|
||||||
; Public self-registration (set DISABLE_REGISTRATION=true in .env for invite-only).
|
|
||||||
DISABLE_REGISTRATION = ${DISABLE_REGISTRATION}
|
|
||||||
SHOW_REGISTRATION_BUTTON = true
|
|
||||||
ENABLE_CAPTCHA = true
|
|
||||||
CAPTCHA_TYPE = image
|
|
||||||
REQUIRE_CAPTCHA_FOR_LOGIN = false
|
|
||||||
; Only turn this on once [mailer] really delivers mail.
|
|
||||||
REGISTER_EMAIL_CONFIRM = ${REGISTER_EMAIL_CONFIRM}
|
|
||||||
REGISTER_MANUAL_CONFIRM = false
|
|
||||||
ALLOW_ONLY_EXTERNAL_REGISTRATION = false
|
|
||||||
; Public repos stay browsable without an account.
|
|
||||||
REQUIRE_SIGNIN_VIEW = false
|
|
||||||
DEFAULT_ALLOW_CREATE_ORGANIZATION = false
|
|
||||||
DEFAULT_KEEP_EMAIL_PRIVATE = true
|
|
||||||
DEFAULT_ENABLE_TIMINGS = false
|
|
||||||
ENABLE_NOTIFY_MAIL = false
|
|
||||||
NO_REPLY_ADDRESS = noreply.${DOMAIN}
|
|
||||||
ENABLE_TIMELINE = true
|
|
||||||
ENABLE_USER_HEATMAP = false
|
|
||||||
AUTO_WATCH_NEW_REPOS = false
|
|
||||||
AUTO_WATCH_ON_CHANGES = false
|
|
||||||
|
|
||||||
[mailer]
|
|
||||||
ENABLED = ${MAILER_ENABLED}
|
|
||||||
|
|
||||||
[openid]
|
|
||||||
ENABLE_OPENID_SIGNIN = false
|
|
||||||
ENABLE_OPENID_SIGNUP = false
|
|
||||||
|
|
||||||
[session]
|
|
||||||
PROVIDER = file
|
|
||||||
COOKIE_SECURE = ${COOKIE_SECURE}
|
|
||||||
|
|
||||||
[log]
|
|
||||||
MODE = console
|
|
||||||
LEVEL = Info
|
|
||||||
|
|
||||||
[other]
|
|
||||||
SHOW_FOOTER_VERSION = false
|
|
||||||
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
# gitea-selfhost: official Gitea image, pinned. Start with ./kit.sh up (it renders config first).
|
|
||||||
services:
|
|
||||||
gitea:
|
|
||||||
image: gitea/gitea:28.1.0-rootless
|
|
||||||
container_name: gitea
|
|
||||||
restart: unless-stopped
|
|
||||||
user: "${KIT_UID:-1000}:${KIT_GID:-1000}"
|
|
||||||
environment:
|
|
||||||
- GITEA_APP_INI=/etc/gitea/app.ini
|
|
||||||
- GITEA_WORK_DIR=/var/lib/gitea
|
|
||||||
volumes:
|
|
||||||
- ./data:/var/lib/gitea
|
|
||||||
- ./config:/etc/gitea
|
|
||||||
ports:
|
|
||||||
- "${BIND_ADDR:-127.0.0.1}:${HTTP_PORT:-3000}:${HTTP_PORT:-3000}"
|
|
||||||
221
kit.sh
221
kit.sh
@ -1,221 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# gitea-selfhost: run your own Gitea with PR-only main branches and per-agent tokens.
|
|
||||||
# Usage: ./kit.sh up | init | setup | protect OWNER/REPO [BRANCH] | protect-all | agent NAME
|
|
||||||
# | backup | upgrade | status | down | install-service
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
KIT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
cd "$KIT_DIR"
|
|
||||||
|
|
||||||
GITEA_VERSION="28.1.0" # keep in step with docker-compose.yml
|
|
||||||
SECRETS="$KIT_DIR/secrets"
|
|
||||||
|
|
||||||
die() { echo "error: $*" >&2; exit 1; }
|
|
||||||
log() { echo "==> $*"; }
|
|
||||||
|
|
||||||
load_env() {
|
|
||||||
[ -f .env ] || { cp .env.example .env; log "created .env from .env.example (edit it, then re-run)"; }
|
|
||||||
set -a; . ./.env; set +a
|
|
||||||
KIT_MODE="${KIT_MODE:-docker}"
|
|
||||||
HTTP_PORT="${HTTP_PORT:-3000}"
|
|
||||||
PROTECT_BRANCH="${PROTECT_BRANCH:-main}"
|
|
||||||
REQUIRED_APPROVALS="${REQUIRED_APPROVALS:-0}"
|
|
||||||
case "$KIT_MODE" in
|
|
||||||
docker) WORK_PATH=/var/lib/gitea; CONF_HOST="$KIT_DIR/config/app.ini" ;;
|
|
||||||
binary) WORK_PATH="${GITEA_HOME:-/var/lib/gitea}"; CONF_HOST="${GITEA_CONF:-/etc/gitea/app.ini}" ;;
|
|
||||||
*) die "KIT_MODE must be docker or binary" ;;
|
|
||||||
esac
|
|
||||||
API="http://127.0.0.1:${HTTP_PORT}/api/v1"
|
|
||||||
export KIT_UID="$(id -u)" KIT_GID="$(id -g)" WORK_PATH HTTP_ADDR="${HTTP_ADDR:-$([ "$KIT_MODE" = docker ] && echo 0.0.0.0 || echo 127.0.0.1)}"
|
|
||||||
}
|
|
||||||
|
|
||||||
compose() {
|
|
||||||
if docker compose version >/dev/null 2>&1; then docker compose "$@"
|
|
||||||
elif command -v docker-compose >/dev/null 2>&1; then docker-compose "$@"
|
|
||||||
else die "docker compose not found (or set KIT_MODE=binary)"; fi
|
|
||||||
}
|
|
||||||
|
|
||||||
bin_path() { if [ -x "$KIT_DIR/bin/gitea" ]; then echo "$KIT_DIR/bin/gitea"; else command -v gitea || echo /usr/local/bin/gitea; fi; }
|
|
||||||
|
|
||||||
# Run the gitea CLI against this instance.
|
|
||||||
gitea_cli() {
|
|
||||||
if [ "$KIT_MODE" = docker ]; then
|
|
||||||
compose exec -T gitea gitea --config /etc/gitea/app.ini --work-path /var/lib/gitea "$@"
|
|
||||||
elif [ "$(id -u)" = 0 ]; then
|
|
||||||
# systemd install: Gitea refuses to run as root, so act as the service user.
|
|
||||||
runuser -u git -- "$(bin_path)" --config "$CONF_HOST" --work-path "$WORK_PATH" "$@"
|
|
||||||
else
|
|
||||||
"$(bin_path)" --config "$CONF_HOST" --work-path "$WORK_PATH" "$@"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
rand() { head -c 64 /dev/urandom | base64 | tr -dc 'A-Za-z0-9' | head -c "${1:-32}"; }
|
|
||||||
|
|
||||||
render_config() {
|
|
||||||
# Fresh random secrets on first render; an existing config keeps its secrets.
|
|
||||||
if [ -f "$CONF_HOST" ]; then log "config exists: $CONF_HOST (not overwritten)"; return; fi
|
|
||||||
local s; s="$(cat config/app.ini.template)"
|
|
||||||
SECRET_KEY="$(rand 64)"; INTERNAL_TOKEN="$(rand 105)"; JWT_SECRET="$(head -c 32 /dev/urandom | base64 | tr '+/' '-_' | tr -d '=')"
|
|
||||||
local v
|
|
||||||
for v in APP_NAME WORK_PATH DOMAIN HTTP_ADDR HTTP_PORT ROOT_URL MAX_REPOS_PER_USER DISABLE_REGISTRATION \
|
|
||||||
REGISTER_EMAIL_CONFIRM MAILER_ENABLED COOKIE_SECURE SECRET_KEY INTERNAL_TOKEN JWT_SECRET; do
|
|
||||||
s="${s//\$\{$v\}/${!v-}}"
|
|
||||||
done
|
|
||||||
case "$s" in *'${'*) die "unrendered placeholder in template: $(grep -o '\${[A-Z_]*}' <<<"$s" | head -1)";; esac
|
|
||||||
mkdir -p "$(dirname "$CONF_HOST")"
|
|
||||||
umask 077; printf '%s\n' "$s" > "$CONF_HOST"; umask 022
|
|
||||||
log "wrote $CONF_HOST (secrets generated locally, never commit it)"
|
|
||||||
}
|
|
||||||
|
|
||||||
install_binary() {
|
|
||||||
[ -x "$KIT_DIR/bin/gitea" ] && "$KIT_DIR/bin/gitea" --version 2>/dev/null | grep -q " $GITEA_VERSION " && return
|
|
||||||
local arch; case "$(uname -m)" in x86_64) arch=amd64;; aarch64|arm64) arch=arm64;; armv7l) arch=arm-6;; *) die "unsupported arch $(uname -m)";; esac
|
|
||||||
local url="https://dl.gitea.com/gitea/${GITEA_VERSION}/gitea-${GITEA_VERSION}-linux-${arch}"
|
|
||||||
mkdir -p bin; log "downloading $url"
|
|
||||||
curl -fsSL -o bin/gitea.tmp "$url"
|
|
||||||
local want got; want="$(curl -fsSL "$url.sha256" | awk '{print $1}')"; got="$(sha256sum bin/gitea.tmp | awk '{print $1}')"
|
|
||||||
[ -n "$want" ] && [ "$want" = "$got" ] || { rm -f bin/gitea.tmp; die "sha256 mismatch for gitea binary"; }
|
|
||||||
chmod +x bin/gitea.tmp; mv bin/gitea.tmp bin/gitea; log "gitea $GITEA_VERSION verified (sha256 $got)"
|
|
||||||
}
|
|
||||||
|
|
||||||
wait_healthy() {
|
|
||||||
local i; for i in $(seq 1 60); do
|
|
||||||
curl -fsS "http://127.0.0.1:${HTTP_PORT}/api/healthz" >/dev/null 2>&1 && { log "gitea is up on 127.0.0.1:${HTTP_PORT}"; return; }
|
|
||||||
sleep 1; done
|
|
||||||
die "gitea did not become healthy on port ${HTTP_PORT}"
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd_init() {
|
|
||||||
mkdir -p "$SECRETS"; chmod 700 "$SECRETS"
|
|
||||||
if [ "$KIT_MODE" = docker ]; then mkdir -p data config; else install_binary; mkdir -p "$WORK_PATH"; fi
|
|
||||||
render_config
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
|
||||||
if [ "$KIT_MODE" = docker ]; then compose up -d
|
|
||||||
elif systemctl is-enabled gitea >/dev/null 2>&1; then sudo systemctl start gitea
|
|
||||||
else
|
|
||||||
mkdir -p "$WORK_PATH/log"
|
|
||||||
if [ -f "$WORK_PATH/gitea.pid" ] && kill -0 "$(cat "$WORK_PATH/gitea.pid")" 2>/dev/null; then log "already running"; return; fi
|
|
||||||
nohup "$(bin_path)" web --config "$CONF_HOST" --work-path "$WORK_PATH" >> "$WORK_PATH/log/gitea.out" 2>&1 < /dev/null &
|
|
||||||
echo $! > "$WORK_PATH/gitea.pid"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
api() { # api METHOD PATH [JSON]
|
|
||||||
local tok; tok="$(cat "$SECRETS/admin.token")"
|
|
||||||
curl -fsS -X "$1" -H "Authorization: token $tok" -H "Content-Type: application/json" "$API$2" ${3:+-d "$3"}
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd_setup() {
|
|
||||||
mkdir -p "$SECRETS"; chmod 700 "$SECRETS"
|
|
||||||
local user="${ADMIN_USER:-admin}" pass="${ADMIN_PASSWORD:-}"
|
|
||||||
if gitea_cli admin user list --admin 2>/dev/null | awk 'NR>1{print $2}' | grep -qx "$user"; then
|
|
||||||
log "admin '$user' already exists"
|
|
||||||
else
|
|
||||||
if [ -z "$pass" ]; then pass="$(rand 24)"; (umask 077; printf '%s\n' "$pass" > "$SECRETS/admin.password"); fi
|
|
||||||
gitea_cli admin user create --admin --username "$user" --password "$pass" \
|
|
||||||
--email "${ADMIN_EMAIL:-admin@example.com}" --must-change-password=false >/dev/null
|
|
||||||
log "created admin '$user' (password in secrets/admin.password if generated)"
|
|
||||||
fi
|
|
||||||
if [ ! -s "$SECRETS/admin.token" ]; then
|
|
||||||
gitea_cli admin user generate-access-token --username "$user" --token-name "kit-admin-$(date +%s)" \
|
|
||||||
--scopes "write:admin,write:repository,write:user,write:organization,write:issue" --raw \
|
|
||||||
| tail -n1 | (umask 077; cat > "$SECRETS/admin.token")
|
|
||||||
log "admin API token saved to secrets/admin.token (0600)"
|
|
||||||
fi
|
|
||||||
api GET /user >/dev/null && log "admin token works"
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd_protect() { # protect OWNER/REPO [BRANCH]
|
|
||||||
local repo="$1" branch="${2:-$PROTECT_BRANCH}"
|
|
||||||
local body; body=$(printf '{"rule_name":"%s","enable_push":false,"enable_merge_whitelist":false,"required_approvals":%s,"block_on_rejected_reviews":true,"dismiss_stale_approvals":true,"block_admin_merge_override":false}' "$branch" "$REQUIRED_APPROVALS")
|
|
||||||
if api GET "/repos/$repo/branch_protections/$branch" >/dev/null 2>&1; then
|
|
||||||
api PATCH "/repos/$repo/branch_protections/$branch" "$body" >/dev/null
|
|
||||||
else
|
|
||||||
api POST "/repos/$repo/branch_protections" "$body" >/dev/null
|
|
||||||
fi
|
|
||||||
echo "protected $repo:$branch (no direct pushes; merge through a PR; approvals=$REQUIRED_APPROVALS)"
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd_protect_all() {
|
|
||||||
local page=1 names
|
|
||||||
while :; do
|
|
||||||
names=$(api GET "/repos/search?limit=50&page=$page" | python3 -c 'import json,sys; [print(r["full_name"], r["default_branch"] or "main", r["empty"]) for r in json.load(sys.stdin)["data"]]')
|
|
||||||
[ -n "$names" ] || break
|
|
||||||
while read -r full branch empty; do
|
|
||||||
[ "$empty" = "True" ] && { echo "skip $full (empty)"; continue; }
|
|
||||||
cmd_protect "$full" "$branch"
|
|
||||||
done <<<"$names"
|
|
||||||
page=$((page+1))
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd_agent() { # agent NAME : one Gitea user + one scoped token per agent
|
|
||||||
local name="$1"; [[ "$name" =~ ^[a-z0-9][a-z0-9-]{1,38}$ ]] || die "agent name: lowercase letters, digits, dashes"
|
|
||||||
mkdir -p "$SECRETS/agents"; chmod 700 "$SECRETS/agents"
|
|
||||||
if ! gitea_cli admin user list 2>/dev/null | awk 'NR>1{print $2}' | grep -qx "$name"; then
|
|
||||||
gitea_cli admin user create --username "$name" --password "$(rand 32)" --email "$name@agents.invalid" \
|
|
||||||
--must-change-password=false >/dev/null
|
|
||||||
fi
|
|
||||||
gitea_cli admin user generate-access-token --username "$name" --token-name "agent-$(date +%s)" \
|
|
||||||
--scopes "write:repository,write:issue,write:user" --raw | tail -n1 | (umask 077; cat > "$SECRETS/agents/$name.token")
|
|
||||||
echo "agent '$name' ready: token in secrets/agents/$name.token (scopes: write:repository, write:issue, write:user; no admin)"
|
|
||||||
echo "git remote: ${ROOT_URL%/}/<owner>/<repo>.git (username $name, token as password)"
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd_backup() {
|
|
||||||
mkdir -p backups
|
|
||||||
local f="gitea-dump-$(date +%Y%m%d-%H%M%S).zip"
|
|
||||||
if [ "$KIT_MODE" = docker ]; then
|
|
||||||
compose exec -T gitea sh -c "cd /tmp && gitea dump --config /etc/gitea/app.ini --work-path /var/lib/gitea --file /tmp/$f >/dev/null && cat /tmp/$f && rm -f /tmp/$f" > "backups/$f"
|
|
||||||
else
|
|
||||||
gitea_cli dump --file "$KIT_DIR/backups/$f" >/dev/null
|
|
||||||
fi
|
|
||||||
chmod 600 "backups/$f"; log "backup: backups/$f (contains secrets; store it encrypted)"
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd_down() {
|
|
||||||
if [ "$KIT_MODE" = docker ]; then compose down
|
|
||||||
elif systemctl is-active gitea >/dev/null 2>&1; then sudo systemctl stop gitea
|
|
||||||
elif [ -f "$WORK_PATH/gitea.pid" ]; then kill "$(cat "$WORK_PATH/gitea.pid")" 2>/dev/null || true; rm -f "$WORK_PATH/gitea.pid"; log "stopped"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd_status() {
|
|
||||||
curl -fsS "http://127.0.0.1:${HTTP_PORT}/api/v1/version" && echo || echo "not reachable on 127.0.0.1:${HTTP_PORT}"
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd_install_service() { # binary mode, as root: git user, binary, config, systemd unit, admin
|
|
||||||
[ "$KIT_MODE" = binary ] || die "install-service is for KIT_MODE=binary"
|
|
||||||
[ "$(id -u)" = 0 ] || die "run with sudo"
|
|
||||||
install_binary
|
|
||||||
id git >/dev/null 2>&1 || useradd --system --create-home --home-dir /home/git --shell /bin/bash git
|
|
||||||
install -m 0755 "$KIT_DIR/bin/gitea" /usr/local/bin/gitea
|
|
||||||
mkdir -p "$WORK_PATH" "$(dirname "$CONF_HOST")" "$SECRETS"; chmod 700 "$SECRETS"
|
|
||||||
render_config
|
|
||||||
chown -R git:git "$WORK_PATH" "$(dirname "$CONF_HOST")"; chmod 750 "$(dirname "$CONF_HOST")"; chmod 640 "$CONF_HOST"
|
|
||||||
sed -e "s#/var/lib/gitea#$WORK_PATH#g" -e "s#/etc/gitea/app.ini#$CONF_HOST#g" -e "s#/etc/gitea#$(dirname "$CONF_HOST")#g" \
|
|
||||||
systemd/gitea.service > /etc/systemd/system/gitea.service
|
|
||||||
systemctl daemon-reload; systemctl enable --now gitea
|
|
||||||
wait_healthy; cmd_setup
|
|
||||||
log "systemd service 'gitea' enabled; run later kit commands with sudo"
|
|
||||||
}
|
|
||||||
|
|
||||||
load_env
|
|
||||||
case "${1:-help}" in
|
|
||||||
init) cmd_init ;;
|
|
||||||
up) cmd_init; start; wait_healthy; cmd_setup
|
|
||||||
log "open ${ROOT_URL} (admin: ${ADMIN_USER:-admin})"
|
|
||||||
log "protect a repo: ./kit.sh protect OWNER/REPO (or protect-all)" ;;
|
|
||||||
setup) cmd_setup ;;
|
|
||||||
protect) [ $# -ge 2 ] || die "usage: ./kit.sh protect OWNER/REPO [BRANCH]"; cmd_protect "$2" "${3:-}" ;;
|
|
||||||
protect-all) cmd_protect_all ;;
|
|
||||||
agent) [ $# -ge 2 ] || die "usage: ./kit.sh agent NAME"; cmd_agent "$2" ;;
|
|
||||||
backup) cmd_backup ;;
|
|
||||||
upgrade) die "edit GITEA_VERSION here and the image tag in docker-compose.yml, run ./kit.sh backup, then: docker compose pull && docker compose up -d (binary: rm bin/gitea && ./kit.sh up). See README." ;;
|
|
||||||
status) cmd_status ;;
|
|
||||||
down) cmd_down ;;
|
|
||||||
install-service) cmd_install_service ;;
|
|
||||||
*) sed -n '2,4p' "$0" ;;
|
|
||||||
esac
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
# Re-applies default branch protection to every repo (new repos included).
|
|
||||||
[Unit]
|
|
||||||
Description=gitea-selfhost: protect default branches
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=oneshot
|
|
||||||
WorkingDirectory=/opt/gitea-selfhost
|
|
||||||
ExecStart=/opt/gitea-selfhost/kit.sh protect-all
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=gitea-selfhost: protect default branches every 5 minutes
|
|
||||||
|
|
||||||
[Timer]
|
|
||||||
OnBootSec=2min
|
|
||||||
OnUnitActiveSec=5min
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=timers.target
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
# Binary mode: install with sudo ./kit.sh install-service
|
|
||||||
# Assumes a "git" system user, binary at /usr/local/bin/gitea, data in /var/lib/gitea,
|
|
||||||
# config in /etc/gitea/app.ini (the defaults in .env.example).
|
|
||||||
[Unit]
|
|
||||||
Description=Gitea (gitea-selfhost)
|
|
||||||
After=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
User=git
|
|
||||||
Group=git
|
|
||||||
WorkingDirectory=/var/lib/gitea
|
|
||||||
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini --work-path /var/lib/gitea
|
|
||||||
Restart=always
|
|
||||||
RestartSec=5
|
|
||||||
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
|
|
||||||
# Hardening
|
|
||||||
NoNewPrivileges=true
|
|
||||||
PrivateTmp=true
|
|
||||||
ProtectSystem=full
|
|
||||||
ReadWritePaths=/var/lib/gitea /etc/gitea
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
@ -1,73 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# End-to-end check of the kit in binary mode, in a throwaway directory on a spare port.
|
|
||||||
# Usage: tests/e2e-binary.sh [PORT] (default 3990). Leaves nothing behind.
|
|
||||||
set -euo pipefail
|
|
||||||
PORT="${1:-3990}"
|
|
||||||
SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
||||||
TMP="$(mktemp -d)"; WORK="$TMP/kit"; CLONE="$TMP/clone"
|
|
||||||
pass=0; fail=0
|
|
||||||
ok() { echo "PASS $*"; pass=$((pass+1)); }
|
|
||||||
bad() { echo "FAIL $*"; fail=$((fail+1)); }
|
|
||||||
cleanup() { (cd "$WORK" 2>/dev/null && ./kit.sh down >/dev/null 2>&1) || true; rm -rf "$TMP"; }
|
|
||||||
trap cleanup EXIT
|
|
||||||
|
|
||||||
ss -ltn 2>/dev/null | grep -q ":$PORT " && { echo "port $PORT is busy"; exit 2; }
|
|
||||||
mkdir -p "$WORK"; cp -r "$SRC"/. "$WORK"/; rm -rf "$WORK/.git" "$WORK/.env" "$WORK/secrets" "$WORK/data"
|
|
||||||
cd "$WORK"
|
|
||||||
cat > .env <<ENV
|
|
||||||
KIT_MODE=binary
|
|
||||||
APP_NAME="Kit E2E"
|
|
||||||
DOMAIN=127.0.0.1
|
|
||||||
ROOT_URL=http://127.0.0.1:$PORT/
|
|
||||||
HTTP_PORT=$PORT
|
|
||||||
COOKIE_SECURE=false
|
|
||||||
DISABLE_REGISTRATION=false
|
|
||||||
MAX_REPOS_PER_USER=10
|
|
||||||
REGISTER_EMAIL_CONFIRM=false
|
|
||||||
MAILER_ENABLED=false
|
|
||||||
ADMIN_USER=kitadmin
|
|
||||||
ADMIN_EMAIL=kitadmin@example.com
|
|
||||||
ADMIN_PASSWORD=
|
|
||||||
PROTECT_BRANCH=main
|
|
||||||
REQUIRED_APPROVALS=0
|
|
||||||
GITEA_HOME=$TMP/home
|
|
||||||
GITEA_CONF=$TMP/home/conf/app.ini
|
|
||||||
ENV
|
|
||||||
./kit.sh up && ok "kit.sh up (download + sha256, render config, start, admin + token)" || { bad "kit.sh up"; exit 1; }
|
|
||||||
|
|
||||||
A="http://127.0.0.1:$PORT/api/v1"; T="$(cat secrets/admin.token)"; P="$(cat secrets/admin.password)"
|
|
||||||
H=(-H "Authorization: token $T" -H "Content-Type: application/json")
|
|
||||||
curl -fsS "${H[@]}" -X POST "$A/user/repos" -d '{"name":"demo","auto_init":true,"default_branch":"main"}' >/dev/null && ok "admin created repo kitadmin/demo" || bad "create repo"
|
|
||||||
./kit.sh protect kitadmin/demo >/dev/null && ok "branch protection applied to main" || bad "protect"
|
|
||||||
|
|
||||||
git -c credential.helper= clone -q "http://kitadmin:$P@127.0.0.1:$PORT/kitadmin/demo.git" "$CLONE"
|
|
||||||
cd "$CLONE"; git config user.name e2e; git config user.email e2e@example.com
|
|
||||||
echo direct > direct.txt; git add direct.txt; git commit -qm direct
|
|
||||||
if git -c credential.helper= push -q origin main 2>/dev/null; then bad "direct push to main was ACCEPTED"; else ok "direct push to main rejected (even for the admin)"; fi
|
|
||||||
git reset -q --hard origin/main; git checkout -qb feature; echo via-pr > pr.txt; git add pr.txt; git commit -qm "via PR"
|
|
||||||
git -c credential.helper= push -q origin feature 2>/dev/null && ok "push to a feature branch accepted" || bad "feature push"
|
|
||||||
cd "$WORK"
|
|
||||||
N=$(curl -fsS "${H[@]}" -X POST "$A/repos/kitadmin/demo/pulls" -d '{"head":"feature","base":"main","title":"via PR"}' | python3 -c 'import json,sys;print(json.load(sys.stdin)["number"])')
|
|
||||||
curl -fsS "${H[@]}" -X POST "$A/repos/kitadmin/demo/pulls/$N/merge" -d '{"Do":"merge"}' >/dev/null && ok "PR #$N merged into main" || bad "merge"
|
|
||||||
curl -fsS "${H[@]}" "$A/repos/kitadmin/demo/contents/pr.txt?ref=main" >/dev/null && ok "merged change is on main" || bad "merged file missing"
|
|
||||||
|
|
||||||
./kit.sh agent e2e-agent >/dev/null && ok "agent user + scoped token created" || bad "agent"
|
|
||||||
AT="$(cat secrets/agents/e2e-agent.token)"
|
|
||||||
curl -fsS -H "Authorization: token $AT" -H "Content-Type: application/json" -X POST "$A/user/repos" -d '{"name":"agent-repo","auto_init":true}' >/dev/null && ok "agent token can create its own repo" || bad "agent create repo"
|
|
||||||
code=$(curl -s -o /dev/null -w '%{http_code}' -H "Authorization: token $AT" "$A/admin/users"); [ "$code" = 403 ] && ok "agent token has no admin access (403)" || bad "agent admin access: $code"
|
|
||||||
code=$(curl -s -o /dev/null -w '%{http_code}' "${H[@]}" -H "Sudo: e2e-agent" -X POST "$A/orgs" -d '{"username":"e2e-org"}'); [ "$code" = 403 ] && ok "new users cannot create organizations (403)" || bad "org creation by new user: $code"
|
|
||||||
st=$(curl -fsS "${H[@]}" "$A/settings/repository"); grep -q '"migrations_disabled":true' <<<"$st" && ok "migrations disabled" || bad "migrations"
|
|
||||||
page=$(curl -fsS "http://127.0.0.1:$PORT/user/sign_up"); grep -q 'captcha' <<<"$page" && ok "public sign-up page shows a captcha" || bad "sign-up captcha"
|
|
||||||
./kit.sh backup >/dev/null 2>&1 && ls backups/*.zip >/dev/null && ok "backup (gitea dump) written" || bad "backup"
|
|
||||||
./kit.sh protect-all >/dev/null && ok "protect-all ran" || bad "protect-all"
|
|
||||||
|
|
||||||
made=1; for i in $(seq 2 11); do
|
|
||||||
c=$(curl -s -o /dev/null -w '%{http_code}' -H "Authorization: token $AT" -H "Content-Type: application/json" -X POST "$A/user/repos" -d "{\"name\":\"r$i\"}")
|
|
||||||
[ "$c" = 201 ] && made=$((made+1))
|
|
||||||
done
|
|
||||||
[ "$made" = 10 ] && ok "repo limit: regular user stops at 10 repos" || bad "repo limit: user made $made repos"
|
|
||||||
if grep -q "Deprecation" "$TMP/home/log/gitea.out"; then bad "config uses deprecated keys: $(grep -o 'config option `[^`]*`' "$TMP/home/log/gitea.out" | sort -u | tr '\n' ' ')"; else ok "no deprecated config keys in the Gitea log"; fi
|
|
||||||
|
|
||||||
echo; echo "$pass passed, $fail failed"
|
|
||||||
[ "$fail" = 0 ]
|
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user