~edwargix/git.sr.ht

17a7f707dc3cd36606eaf6045f1c62b0680da7af — Drew DeVault 7 years ago ef0dde8
Create repos if they don't exist on push
M git-srht-keys => git-srht-keys +17 -2
@@ 5,9 5,11 @@ import sys
import os
import shlex
import requests
import time
from datetime import datetime
from pwd import getpwnam
from grp import getgrnam
from srht.validation import Validation
from srht.config import cfg, cfgi, load_config
load_config("git")



@@ 110,6 112,7 @@ def shell():
    db = DbSession(cfg("sr.ht", "connection-string"))
    from gitsrht.types import User, Repository, RepoVisibility, Redirect
    from gitsrht.access import has_access, UserAccess
    from gitsrht.repos import create_repo
    db.init()

    user = User.query.filter(User.id == user_id).first()


@@ 137,13 140,25 @@ def shell():
        repo = Redirect.query.filter(Redirect.path == path).first()
        if repo:
            repo = repo.new_repo
            sys.stderr.write("\n\033[93m\tNOTICE\033[0m\n")
            sys.stderr.write("\n\t\033[93mNOTICE\033[0m\n")
            sys.stderr.write("\tThis repository has moved:\n")
            # TODO: orgs
            sys.stderr.write("\t{}/~{}/{}\n".format(
                root, repo.owner.username, repo.name))
            sys.stderr.write("\tPlease update your remote.\n\n")
        sys.exit(128)
            sys.exit(128)

        _path, repo_name = os.path.split(path)
        owner = os.path.basename(_path)
        if "~" + user.username != owner:
            sys.exit(128)

        valid = Validation({ "name": repo_name })
        repo = create_repo(valid, user)
        if not valid.ok:
            sys.exit(128)
        repo.visibility = RepoVisibility.autocreated
        db.session.commit()
    
    if cmd[0] == "git-receive-pack":
        if not has_access(repo, UserAccess.write, user):

A git-srht-periodic => git-srht-periodic +18 -0
@@ 0,0 1,18 @@
#!/usr/bin/env python3
from srht.config import cfg, cfgi, load_config
load_config("git")
from srht.database import DbSession
db = DbSession(cfg("sr.ht", "connection-string"))
from gitsrht.types import Repository, RepoVisibility
db.init()
from datetime import datetime, timedelta

def cleanup_autocreated():
    due = datetime.utcnow() - timedelta(minutes=20)
    repos = (Repository.query
            .filter(Repository.visibility == RepoVisibility.autocreated)
            .filter(Repository.created < due)).all()
    db.session.delete(r) for r in repos
    db.session.commit()

cleanup_autocreated()

M git-srht-update-hook => git-srht-update-hook +13 -1
@@ 3,7 3,7 @@ from srht.config import cfg, cfgi, load_config
load_config("git")
from srht.database import DbSession
db = DbSession(cfg("sr.ht", "connection-string"))
from gitsrht.types import User, Repository
from gitsrht.types import User, Repository, RepoVisibility
db.init()
from configparser import ConfigParser
from datetime import datetime


@@ 15,6 15,11 @@ import re

op = sys.argv[0]
builds_sr_ht = cfg("network", "builds", default=None)
root = (
    cfg("server", "protocol") +
    "://" +
    cfg("server", "domain")
)

if op == "hooks/post-update":
    refs = sys.argv[1:]


@@ 32,6 37,13 @@ if op == "hooks/post-update":
    if not repo:
        sys.exit(0)

    if repo.visibility == RepoVisibility.autocreated:
        print("\n\t\033[93mNOTICE\033[0m")
        print("\tWe saved your changes, but this repository does not exist.")
        print("\tClick here to create it:")
        print("\t{}/create?name={}".format(root, repo.name))
        print("\tYour changes will be discarded in 20 minutes.\n")

    repo.updated = datetime.utcnow()
    db.session.commit()


M gitsrht/blueprints/manage.py => gitsrht/blueprints/manage.py +6 -6
@@ 7,7 7,7 @@ from srht.validation import Validation
from gitsrht.types import Repository, RepoVisibility, Redirect
from gitsrht.decorators import loginrequired
from gitsrht.access import check_access, UserAccess
from gitsrht.repos import create_repo, rename_repo
from gitsrht.repos import create_repo, rename_repo, delete_repo
import shutil

manage = Blueprint('manage', __name__)


@@ 18,7 18,8 @@ post_update = cfg("git.sr.ht", "post-update-script")
@loginrequired
def index():
    another = request.args.get("another")
    return render_template("create.html", another=another)
    name = request.args.get("name")
    return render_template("create.html", another=another, repo_name=name)

@manage.route("/create", methods=["POST"])
@loginrequired


@@ 99,10 100,9 @@ def settings_delete(owner_name, repo_name):
def settings_delete_POST(owner_name, repo_name):
    owner, repo = check_access(owner_name, repo_name, UserAccess.manage)
    if isinstance(repo, Redirect):
        repo = repo.new_repo
    shutil.rmtree(repo.path)
    db.session.delete(repo)
    db.session.commit()
        # Normally we'd redirect but we don't want to fuck up some other repo
        abort(404)
    delete_repo(repo)
    session["notice"] = "{}/{} was deleted.".format(
        owner.canonical_name, repo.name)
    return redirect("/" + owner.canonical_name)

M gitsrht/blueprints/public.py => gitsrht/blueprints/public.py +5 -3
@@ 15,9 15,11 @@ meta_uri = cfg("network", "meta")
@public.route("/")
def index():
    if current_user:
        repos = Repository.query.filter(Repository.owner_id == current_user.id)\
                .order_by(Repository.updated.desc())\
                .limit(5).all()
        repos = (Repository.query
                .filter(Repository.owner_id == current_user.id)
                .filter(Repository.visibility != RepoVisibility.autocreated)
                .order_by(Repository.updated.desc())
                .limit(5)).all()
    else:
        repos = None
    return render_template("index.html", repos=repos)

M gitsrht/repos.py => gitsrht/repos.py +34 -16
@@ 2,6 2,7 @@ import subprocess
from srht.database import db
from srht.config import cfg
from gitsrht.types import Repository, RepoVisibility, Redirect
import shutil
import re
import os



@@ 10,14 11,17 @@ post_update = cfg("git.sr.ht", "post-update-script")

def validate_name(valid, owner, repo_name):
    if not valid.ok:
        return
        return None
    valid.expect(re.match(r'^[a-z._-][a-z0-9._-]*$', repo_name),
            "Name must match [a-z._-][a-z0-9._-]*", field="name")
    existing = (Repository.query
            .filter(Repository.owner_id == owner.id)
            .filter(Repository.name.ilike("%" + repo_name + "%"))
            .first())
    if existing and existing.visibility == RepoVisibility.autocreated:
        return existing
    valid.expect(not existing, "This name is already in use.", field="name")
    return None

def create_repo(valid, owner):
    repo_name = valid.require("name", friendly_name="Name")


@@ 25,26 29,36 @@ def create_repo(valid, owner):
    visibility = valid.optional("visibility",
            default="public",
            cls=RepoVisibility)
    validate_name(valid, owner, repo_name)
    repo = validate_name(valid, owner, repo_name)
    if not valid.ok:
        return None

    repo = Repository()
    repo.name = repo_name
    repo.description = description
    repo.owner_id = owner.id
    repo.visibility = visibility
    repo.path = os.path.join(repos_path, "~" + owner.username, repo.name)
    db.session.add(repo)
    if not repo:
        repo = Repository()
        repo.name = repo_name
        repo.description = description
        repo.owner_id = owner.id
        repo.path = os.path.join(repos_path, "~" + owner.username, repo.name)
        db.session.add(repo)
        db.session.flush()

    subprocess.run(["mkdir", "-p", repo.path])
    subprocess.run(["git", "init", "--bare"], cwd=repo.path)
        subprocess.run(["mkdir", "-p", repo.path],
            stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        subprocess.run(["git", "init", "--bare"], cwd=repo.path,
            stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        subprocess.run(["git", "config", "srht.repo-id", str(repo.id)],
            cwd=repo.path, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        subprocess.run(["ln", "-s",
                post_update,
                os.path.join(repo.path, "hooks", "update")
            ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        subprocess.run(["ln", "-s",
                post_update,
                os.path.join(repo.path, "hooks", "post-update")
            ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

    repo.visibility = visibility
    db.session.commit()

    subprocess.run(["git", "config", "srht.repo-id", str(repo.id)], cwd=repo.path)
    subprocess.run(["ln", "-s", post_update, os.path.join(repo.path, "hooks", "update")])
    subprocess.run(["ln", "-s", post_update, os.path.join(repo.path, "hooks", "post-update")])
    return repo

def rename_repo(owner, repo, valid):


@@ 71,5 85,9 @@ def rename_repo(owner, repo, valid):
    repo.path = new_path
    repo.name = repo_name
    db.session.commit()

    return repo

def delete_repo(repo):
    shutil.rmtree(repo.path)
    db.session.delete(repo)
    db.session.commit()

M gitsrht/types/repository.py => gitsrht/types/repository.py +2 -0
@@ 4,6 4,8 @@ from srht.database import Base
from enum import Enum

class RepoVisibility(Enum):
    autocreated = 'autocreated'
    """Used for repositories that were created automatically on push"""
    public = 'public'
    private = 'private'
    unlisted = 'unlisted'

M setup.py => setup.py +5 -1
@@ 32,5 32,9 @@ setup(
          'hooks/*'
      ]
  },
  scripts = ['git-srht-keys', 'git-srht-update-hook']
  scripts = [
      'git-srht-keys',
      'git-srht-update-hook',
      'git-srht-periodic'
  ]
)