diff --git a/roles/distgit/files/cgitrc b/roles/distgit/files/cgitrc new file mode 100644 index 0000000000..7276dc333b --- /dev/null +++ b/roles/distgit/files/cgitrc @@ -0,0 +1,71 @@ +# +# See cgitrc(5) or /usr/share/doc/cgit-*/cgitrc.5.html for details +# + +# Enable caching of up to 1000 output entries +cache-size=1000 + +# Specify some default clone prefixes +clone-prefix=git://pkgs.fedoraproject.org ssh://pkgs.fedoraproject.org http://pkgs.fedoraproject.org/git + +# Specify the css url +css=/cgit-data/cgit.css + +# Show extra links for each repository on the index page +enable-index-links=1 + +# Enable ASCII art commit history graph on the log pages +enable-commit-graph=1 + +# Show number of affected files per commit on the log pages +enable-log-filecount=1 + +# Show number of added/removed lines per commit on the log pages +enable-log-linecount=1 + +# Add a cgit favicon +#favicon=/favicon.ico + +# Use a custom logo +logo=/cgit-data/cgit.png + +# Enable statistics per week, month and quarter +max-stats=quarter + +# Set the title and heading of the repository index page +root-title=Fedora Project Packages GIT repositories + +# Set a subheading for the repository index page +#root-desc=tracking the foobar development + +# Include some more info about this site on the index page +#root-readme=/var/www/html/about.html + +# Allow download of tar.gz, tar.bz2 and zip-files +snapshots=tar.gz tar.xz zip + +## +## List of common mimetypes +## + +mimetype.gif=image/gif +mimetype.html=text/html +mimetype.jpg=image/jpeg +mimetype.jpeg=image/jpeg +mimetype.pdf=application/pdf +mimetype.png=image/png +mimetype.svg=image/svg+xml + +# Enable syntax highlighting (requires the highlight package) +#source-filter=/usr/libexec/cgit/filters/syntax-highlighting.sh +email-filter=lua:/usr/libexec/cgit/filters/email-libravatar-korg.lua + + +## +## List of repositories. +## PS: Any repositories listed when section is unset will not be +## displayed under a section heading +## PPS: This list could be kept in a different file (e.g. '/etc/cgitrepos') +## and included like this: +project-list=/srv/git/pkgs-git-repos-list +scan-path=/srv/git/rpms/ diff --git a/roles/distgit/files/fedmsg-genacls-config.py b/roles/distgit/files/fedmsg-genacls-config.py new file mode 100644 index 0000000000..b657de1de8 --- /dev/null +++ b/roles/distgit/files/fedmsg-genacls-config.py @@ -0,0 +1,4 @@ +config = { + 'genacls.consumer.enabled': True, + 'genacls.consumer.delay': 5, # 5 seconds +} diff --git a/roles/distgit/files/genacls.pkgdb b/roles/distgit/files/genacls.pkgdb new file mode 100644 index 0000000000..88f75e7970 --- /dev/null +++ b/roles/distgit/files/genacls.pkgdb @@ -0,0 +1,118 @@ +#!/usr/bin/python -t +# +# Create an /etc/gitolog/conf/getolog.conf file with acls for dist-git +# +# Takes no arguments! +# + +import grp +import sys + +import requests + +if __name__ == '__main__': + # Get the users in various groups + TRUSTED = grp.getgrnam('cvsadmin')[3] + ARM = grp.getgrnam('fedora-arm')[3] + SPARC = grp.getgrnam('fedora-sparc')[3] + IA64 = grp.getgrnam('fedora-ia64')[3] + S390 = grp.getgrnam('fedora-s390')[3] + PPC = grp.getgrnam('fedora-ppc')[3] + PROVEN = grp.getgrnam('provenpackager')[3] + + # Set the active branches to create ACLs for + # Give them the git branch eqiv until pkgdb follows suite + ACTIVE = {'OLPC-2': 'olpc2', 'OLPC-3': 'olpc3', 'EL-4': 'el4', + 'EL-5': 'el5', 'el5': 'el5', 'el6': 'el6', 'EL-6': 'el6', + 'epel7': 'epel7', + 'F-11': 'f11', 'F-12': 'f12', 'F-13': 'f13', 'f14': 'f14', 'f15': + 'f15', 'f16': 'f16', 'f17': 'f17', 'f18': 'f18', 'f19': 'f19', + 'f20': 'f20', 'devel': 'master', 'master': 'master'} + + # Create a "regex"ish list 0f the reserved branches + RESERVED = ['f[0-9][0-9]', 'epel[0-9]', 'epel[0-9][0-9]', 'el[0-9]', 'olpc[0-9]'] + + # Read the ACL information from the packageDB + data = requests.get('https://admin.fedoraproject.org/pkgdb/api/vcs?format=json').json() + + # Get a list of all the packages + acls = data['packageAcls'] + pkglist = data['packageAcls'].keys() + pkglist.sort() + + # sanity check + if len(pkglist) < 2500: + sys.exit(1) + + # print out our user groups + print '@admins = %s' % ' '.join(TRUSTED) + print '@provenpackager = %s' % ' '.join(PROVEN) + print '@fedora-arm = %s' % ' '.join(ARM) + print '@fedora-s390 = %s' % ' '.join(S390) + print '@fedora-ppc = %s' % ' '.join(PPC) + + # print our default permissions + print 'repo @all' + print ' RWC = @admins @fedora-arm @fedora-s390 @fedora-ppc' + print ' R = @all' + #print ' RW private- = @all' + # dont' enable the above until we prevent building for real from private- + + for pkg in pkglist: + branchAcls = {} # Check whether we need to set separate per branch acls + buffer = [] # Buffer the output per package + masters = [] # Folks that have commit to master + writers = [] # Anybody that has write access + + # Examine each branch in the package + branches = acls[pkg].keys() + branches.sort() + for branch in branches: + if not branch in ACTIVE.keys(): + continue + if 'packager' in acls[pkg][branch]['commit']['groups']: + # If the packager group is defined, everyone has access + buffer.append(' RWC %s = @all' % (ACTIVE[branch])) + branchAcls.setdefault('@all', []).append((pkg, + ACTIVE[branch])) + if branch == 'master': + masters.append('@all') + if '@all' not in writers: + writers.append('@all') + else: + # Extract the owners + committers = [] + owners = acls[pkg][branch]['commit']['people'] + owners.sort() + for owner in owners: + committers.append(owner) + if 'provenpackager' in acls[pkg][branch]['commit']['groups']: + committers.append('@provenpackager') + if branch == 'master': + masters.extend(committers) + + # add all the committers to the top writers list + for committer in committers: + if not committer in writers: + writers.append(committer) + + # Print the committers to the acl for this package-branch + committers = ' '.join(committers) + buffer.append(' RWC %s = %s' % + (ACTIVE[branch], committers)) + branchAcls.setdefault(committers, []).append((pkg, + ACTIVE[branch])) + + print + print 'repo %s' % pkg + #if len(branchAcls.keys()) == 1: + # acl = branchAcls.keys()[0] + # print ' RW = %s' % acl + #else: + print '\n'.join(buffer) + for reserved in RESERVED: + print ' - %s = @all' % reserved + print ' RWC refs/tags/ = %s' % ' '.join(writers) + if masters: + print ' RWC = %s' % ' '.join(masters) + sys.exit(0) diff --git a/roles/distgit/files/genacls.sh b/roles/distgit/files/genacls.sh new file mode 100644 index 0000000000..712edb3479 --- /dev/null +++ b/roles/distgit/files/genacls.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +TEMPDIR=`mktemp -d -p /var/tmp genacls.XXXXX` +export GL_RC=/etc/gitolite/gitolite.rc +export GL_BINDIR=/usr/bin + +cd $TEMPDIR +# Only replace the acls if genacls completes successfully +if /usr/local/bin/genacls.pkgdb > gitolite.conf ; then + mv gitolite.conf /etc/gitolite/conf/ + /usr/bin/gl-compile-conf +fi +cd / +rm -rf $TEMPDIR diff --git a/roles/distgit/files/git-smart-http.conf b/roles/distgit/files/git-smart-http.conf new file mode 100644 index 0000000000..5841632b72 --- /dev/null +++ b/roles/distgit/files/git-smart-http.conf @@ -0,0 +1,3 @@ +SetEnv GIT_PROJECT_ROOT /srv/git/rpms +SetEnv GIT_HTTP_EXPORT_ALL +ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/ diff --git a/roles/distgit/files/gitolite.rc b/roles/distgit/files/gitolite.rc new file mode 100644 index 0000000000..03149e3ce5 --- /dev/null +++ b/roles/distgit/files/gitolite.rc @@ -0,0 +1,233 @@ +# paths and configuration variables for gitolite + +# please read comments before editing + +# this file is meant to be pulled into a perl program using "do" or "require". + +# You do NOT need to know perl to edit the paths; it should be fairly +# self-explanatory and easy to maintain perl syntax :-) + +# -------------------------------------- +# Do not uncomment these values unless you know what you're doing +# $GL_PACKAGE_CONF = ""; +# $GL_PACKAGE_HOOKS = ""; + +# -------------------------------------- + +# -------------------------------------- + +# this is where the repos go. If you provide a relative path (not starting +# with "/"), it's relative to your $HOME. You may want to put in something +# like "/bigdisk" or whatever if your $HOME is too small for the repos, for +# example + +$REPO_BASE="/srv/git/rpms/"; + +# the default umask for repositories is 0077; change this if you run stuff +# like gitweb and find it can't read the repos. Please note the syntax; the +# leading 0 is required + +$REPO_UMASK = 0002; +# $REPO_UMASK = 0027; # gets you 'rwxr-x---' +# $REPO_UMASK = 0022; # gets you 'rwxr-xr-x' + +# part of the setup of gitweb is a variable called $projects_list (please see +# gitweb documentation for more on this). Set this to the same value: + +$PROJECTS_LIST = $ENV{HOME} . "/projects.list"; + +# -------------------------------------- + +# I see no reason anyone may want to change the gitolite admin directory, but +# feel free to do so. However, please note that it *must* be an *absolute* +# path (i.e., starting with a "/" character) + +# gitolite admin directory, files, etc + +$GL_ADMINDIR="/etc/gitolite"; + +# -------------------------------------- + +# templates for location of the log files and format of their names + +# I prefer this template (note the %y and %m placeholders) +# it produces files like `~/.gitolite/logs/gitolite-2009-09.log` + +$GL_LOGT="/var/log/gitolite/gitolite-%y-%m.log"; + +# other choices are below, or you can make your own -- but PLEASE MAKE SURE +# the directory exists and is writable; gitolite won't do that for you (unless +# it is the default, which is "$GL_ADMINDIR/logs") + +# $GL_LOGT="$GL_ADMINDIR/logs/gitolite-%y-%m-%d.log"; +# $GL_LOGT="$GL_ADMINDIR/logs/gitolite-%y.log"; + +# -------------------------------------- + +# Please DO NOT change these three paths + +$GL_CONF="$GL_ADMINDIR/conf/gitolite.conf"; +$GL_KEYDIR="$GL_ADMINDIR/keydir"; +$GL_CONF_COMPILED="$GL_ADMINDIR/conf/gitolite.conf-compiled.pm"; + +# -------------------------------------- + +# if git on your server is on a standard path (that is +# ssh git@server git --version +# works), leave this setting as is. Otherwise, choose one of the +# alternatives, or write your own + +$GIT_PATH=""; +# $GIT_PATH="/opt/bin/"; + +# -------------------------------------- + +# ---------------------------------------------------------------------- +# BIG CONFIG SETTINGS + +# Please read doc/big-config.mkd for details + +$GL_BIG_CONFIG = 1; +$GL_NO_DAEMON_NO_GITWEB = 1; +$GL_NO_CREATE_REPOS = 1; +$GL_NO_SETUP_AUTHKEYS = 1; + + +# ---------------------------------------------------------------------- +# SECURITY SENSITIVE SETTINGS +# +# Settings below this point may have security implications. That +# usually means that I have not thought hard enough about all the +# possible ways to crack security if these settings are enabled. + +# Please see details on each setting for specifics, if any. +# ---------------------------------------------------------------------- + + + +# -------------------------------------- +# ALLOW REPO ADMIN TO SET GITCONFIG KEYS +# +# Gitolite allows you to set git repo options using the "config" keyword; see +# conf/example.conf for details and syntax. +# +# However, if you are in an installation where the repo admin does not (and +# should not) have shell access to the server, then allowing him to set +# arbitrary repo config options *may* be a security risk -- some config +# settings may allow executing arbitrary commands. +# +# You have 3 choices. By default $GL_GITCONFIG_KEYS is left empty, which +# completely disables this feature (meaning you cannot set git configs from +# the repo config). + +$GL_GITCONFIG_KEYS = ""; + +# The second choice is to give it a space separated list of settings you +# consider safe. (These are actually treated as a set of regular expression +# patterns, and any one of them must match). For example: +# $GL_GITCONFIG_KEYS = "core\.logAllRefUpdates core\..*compression"; +# allows repo admins to set one of those 3 config keys (yes, that second +# pattern matches two settings from "man git-config", if you look) +# +# The third choice (which you may have guessed already if you're familiar with +# regular expressions) is to allow anything and everything: +# $GL_GITCONFIG_KEYS = ".*"; + +# -------------------------------------- +# EXTERNAL COMMAND HELPER -- HTPASSWD + +# security note: runs an external command (htpasswd) with specific arguments, +# including a user-chosen "password". + +# if you want to enable the "htpasswd" command, give this the absolute path to +# whatever file apache (etc) expect to find the passwords in. + +$HTPASSWD_FILE = ""; + +# Look in doc/3 ("easier to link gitweb authorisation with gitolite" section) +# for more details on using this feature. + +# -------------------------------------- +# EXTERNAL COMMAND HELPER -- RSYNC + +# security note: runs an external command (rsync) with specific arguments, all +# presumably filled in correctly by the client-side rsync. + +# base path of all the files that are accessible via rsync. Must be an +# absolute path. Leave it undefined or set to the empty string to disable the +# rsync helper. + +$RSYNC_BASE = ""; + +# $RSYNC_BASE = "/home/git/up-down"; +# $RSYNC_BASE = "/tmp/up-down"; + +# -------------------------------------- +# EXTERNAL COMMAND HELPER -- SVNSERVE + +# security note: runs an external command (svnserve) with specific arguments, +# as specified below. %u is substituted with the username. + +# This setting allows launching svnserve when requested by the ssh client. +# This allows using the same SSH setup (hostname/username/public key) for both +# SVN and git access. Leave it undefined or set to the empty string to disable +# svnserve access. + +$SVNSERVE = ""; +# $SVNSERVE = "/usr/bin/svnserve -r /var/svn/ -t --tunnel-user=%u"; + +# -------------------------------------- +# ALLOW REPO CONFIG TO USE WILDCARDS + +# security note: this used to in a separate "wildrepos" branch. You can +# create repositories based on wild cards, give "ownership" to the specific +# user who created it, allow him/her to hand out R and RW permissions to other +# users to collaborate, etc. This is powerful stuff, and I've made it as +# secure as I can, but it hasn't had the kind of rigorous line-by-line +# analysis that the old "master" branch had. + +# This has now been rolled into master, with all the functionality gated by +# this variable. Set this to 1 if you want to enable the wildrepos features. +# Please see doc/4-wildcard-repositories.mkd for details. + +$GL_WILDREPOS = 0; + +# -------------------------------------- +# DEFAULT WILDCARD PERMISSIONS + +# If set, this value will be used as the default user-level permission rule of +# new wildcard repositories. The user can change this value with the setperms command +# as desired after repository creation; it is only a default. Note that @all can be +# used here but is special; no other groups can be used in user-level permissions. + +# $GL_WILDREPOS_DEFPERMS = 'R = @all'; + +# -------------------------------------- +# HOOK CHAINING + +# by default, the update hook in every repo chains to "update.secondary". +# Similarly, the post-update hook in the admin repo chains to +# "post-update.secondary". If you're fine with the defaults, there's no need +# to do anything here. However, if you want to use different names or paths, +# change these variables + +# $UPDATE_CHAINS_TO = "hooks/update.secondary"; +# $ADMIN_POST_UPDATE_CHAINS_TO = "hooks/post-update.secondary"; + +# -------------------------------------- +# ADMIN DEFINED COMMANDS + +# WARNING: Use this feature only if (a) you really really know what you're +# doing or (b) you really don't care too much about security. Please read +# doc/admin-defined-commands.mkd for details. + +# $GL_ADC_PATH = ""; + +# -------------------------------------- +# per perl rules, this should be the last line in such a file: +1; + +# Local variables: +# mode: perl +# End: +# vim: set syn=perl: diff --git a/roles/distgit/files/lookaside-upload.conf b/roles/distgit/files/lookaside-upload.conf new file mode 100644 index 0000000000..a5948d7398 --- /dev/null +++ b/roles/distgit/files/lookaside-upload.conf @@ -0,0 +1,69 @@ +Alias /repo/ /srv/cache/lookaside/ + +# default SSL configuration... +Listen 443 + +SSLSessionCache shmcb:/var/cache/mod_ssl/scache(512000) +SSLSessionCacheTimeout 300 + +SSLMutex default + +SSLRandomSeed startup file:/dev/urandom 256 +SSLRandomSeed connect builtin +SSLCryptoDevice builtin + +# SSL host + + # This alias must come before the /repo/ one to avoid being overridden. + ScriptAlias /repo/pkgs/upload.cgi /srv/web/upload.cgi + + Alias /repo/ /srv/cache/lookaside/ + ServerName pkgs.fedoraproject.org + ServerAdmin webmaster@fedoraproject.org + + SSLEngine on + + SSLCertificateFile conf/pkgs.fedoraproject.org_key_and_cert.pem + SSLCertificateKeyFile conf/pkgs.fedoraproject.org_key_and_cert.pem + SSLCACertificateFile conf/cacert.pem + SSLCARevocationFile /etc/pki/tls/crl.pem + + SSLCipherSuite RSA:!EXPORT:!DH:!LOW:!NULL:+MEDIUM:+HIGH + +# Must be 'optional' everywhere in order to have POST operations work to upload.cgi + SSLVerifyClient optional +# Must be here for POST operations to upload.cgi + SSLOptions +OptRenegotiate + ErrorLog logs/ssl_error_log + CustomLog logs/ssl_access_log \ + "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%{SSL_CLIENT_S_DN_OU}x\" %{SSL_CLIENT_S_DN_CN}x %{SSL_CLIENT_S_DN_emailAddress}x \"%r\" %b" + + + SSLVerifyClient optional + SSLVerifyDepth 1 + SSLOptions +StrictRequire +StdEnvVars +OptRenegotiate + # require that the client auth cert was created by us and signed by us + SSLRequire ( %{SSL_CIPHER} !~ m/^(EXP|NULL)/ \ + and %{SSL_CLIENT_S_DN_O} eq "Fedora Project" \ + and %{SSL_CLIENT_S_DN_OU} eq "Fedora User Cert" \ + and %{SSL_CLIENT_I_DN_O} eq "Fedora Project" \ + and %{SSL_CLIENT_I_DN_OU} eq "Fedora Project CA" ) + + + + SSLRequireSSL + + SSLVerifyClient optional + SSLVerifyDepth 1 + SSLOptions +StrictRequire +StdEnvVars +OptRenegotiate + # require that the access comes from internal or that + # the client auth cert was created by us and signed by us + SSLRequire ( %{SSL_CIPHER} !~ m/^(EXP|NULL)/ \ + and %{SSL_CLIENT_S_DN_O} eq "Fedora Project" \ + and %{SSL_CLIENT_S_DN_OU} eq "Fedora User Cert" \ + and %{SSL_CLIENT_I_DN_O} eq "Fedora Project" \ + and %{SSL_CLIENT_I_DN_OU} eq "Fedora Project CA" ) + + + + diff --git a/roles/distgit/files/lookaside.conf b/roles/distgit/files/lookaside.conf new file mode 100644 index 0000000000..d7d3defccc --- /dev/null +++ b/roles/distgit/files/lookaside.conf @@ -0,0 +1,6 @@ +Alias /lookaside /srv/cache/lookaside + + Options Indexes FollowSymLinks + AllowOverride None + + diff --git a/roles/distgit/files/mkbranch b/roles/distgit/files/mkbranch new file mode 100644 index 0000000000..4e73495bdb --- /dev/null +++ b/roles/distgit/files/mkbranch @@ -0,0 +1,181 @@ +#!/bin/bash +# +# Create a new development branch for a module. +# THIS HAS TO BE RUN ON THE GIT SERVER! + +# WARNING: +# This file is maintained within puppet? +# All local changes will be lost. + + +# Figure out the environment we're running in +RUNDIR=$(cd $(dirname $0) && pwd) +GITROOT=/srv/git/rpms + +# check if a moron is driving me +if [ ! -d $GITROOT ] ; then + # we're not on the git server (this check is fragile) + echo "ERROR: This script has to be run on the git server." + echo "ERROR: Homer sez 'Duh'." + exit -9 +fi + +# where are the packages kept +TOPLEVEL=rpms + +# Local variables +VERBOSE=0 +TEST= +IGNORE= +BRANCH="" +PACKAGES="" +SRC_BRANCH="master" +AUTHOR="Fedora Release Engineering " + +Usage() { + cat <] ... + + Creates a new branch for the list of s. + The /master suffix on branch names is assumed. + +Options: + -s,--source= Use as the source branch. + Defaults is master + /master suffix on other branches assumed + -n,--test Don't do nothing, only test + -i,--ignore Ignore erroneous modules + -h,--help This help message + -v,--verbose Increase verbosity +EOF +} + +# parse the arguments +while [ -n "$1" ] ; do + case "$1" in + -h | --help ) + Usage + exit 0 + ;; + + -v | --verbose ) + VERBOSE=$(($VERBOSE + 1)) + ;; + + -i | --ignore ) + IGNORE="yes" + ;; + + -n | --test ) + TEST="yes" + ;; + + -s | --source ) + shift + SRC_BRANCH=$1 + ;; + + -b | --branch ) + shift + BRANCH=$1/master + ;; + + * ) + if [ -z "$BRANCH" ] ; then + BRANCH="$1" + else + PACKAGES="$PACKAGES $1" + fi + ;; + esac + shift +done + +# check the arguments +if [ -z "$BRANCH" -o -z "$PACKAGES" ] ; then + Usage + exit -1 +fi + + +# Sanity checks before we start doing damage +NEWP= +for p in $PACKAGES ; do + [ $VERBOSE -gt 1 ] && echo "Checking package $p..." + if [ ! -d $GITROOT/$p.git ] ; then + echo "ERROR: Package module $p is invalid" >&2 + [ "$IGNORE" = "yes" ] && continue || exit -1 + fi + if [ -z "$(GIT_DIR=$GITROOT/$p.git git rev-parse -q --verify $SRC_BRANCH)" ] ; then \ + echo "ERROR: Invalid source branch '$SRC_BRANCH' for package $p" >&2; \ + if [ $SRC_BRANCH == 'master' ]; then + [ "$IGNORE" = "yes" ] && continue + else + SRC_BRANCH=master + fi + fi + $(GIT_DIR=$GITROOT/$p.git git rev-parse -q --verify \ + $BRANCH >/dev/null) && \ + (echo "IGNORING: Package module $p already has a branch $BRANCH" >&2; \ + [ "$IGNORE" = "yes" ] && continue || exit -1) + NEWP="$NEWP $p" +done +PACKAGES="$(echo $NEWP)" +if [ -z "$PACKAGES" ] ; then + echo "NOOP: no valid packages found to process" + exit -1 +fi + +if [ -n "$TEST" ] ; then + echo "Branch $BRANCH valid for $PACKAGES" + exit 0 +fi + +# This account must have the proper permissions as to not screw up the +# repository work. +if [ "$(id -un)" = "root" ] ; then + echo "Please run this script as yourself" + exit -3 +fi +#### Change this to check for proper git-admin rights + +# "global" permissions check +if [ ! -w $GITROOT ] ; then + echo "ERROR: You can not write to $GITROOT" + echo "ERROR: You can not perform branching operations" + exit -1 +fi + +# Now start working on creating those branches + +# For every module, "create" the branch +for NAME in $PACKAGES ; do + echo + echo "Creating new module branch '$BRANCH' for '$NAME' from branch '$SRC_BRANCH'..." + + # permissions checks for this particular module + if [ ! -w $GITROOT/$NAME.git/refs/heads/ ] ; then + echo "ERROR: You can not write to $d" + echo "ERROR: $NAME can not be branched by you" + continue + fi + #### Replace the above with a gitolite permission check + #[ $VERBOSE -gt 0 ] && echo "Creating $BRANCH-split tag for $NAME/$SRC_BRANCH..." + # Is the above needed? + #cvs -Q rtag -f "$BRANCH-split" $TOPLEVEL/$NAME/$SRC_BRANCH || { + #echo "ERROR: Branch split tag for $NAME/$SRC_BRANCH could not be created" >&2 + #exit -2 + #} + [ $VERBOSE -gt 0 ] && echo "Creating $NAME $BRANCH from $NAME $SRC_BRANCH..." + $(pushd $GITROOT/$NAME.git >/dev/null && \ + git branch --no-track $BRANCH $SRC_BRANCH && \ + popd >/dev/null) || { + echo "ERROR: Branch $NAME $BRANCH could not be created" >&2 + popd >/dev/null + exit -2 + } +done + +echo +echo "Done." diff --git a/roles/distgit/files/pkgdb2-clone b/roles/distgit/files/pkgdb2-clone new file mode 100644 index 0000000000..49c8a8f736 --- /dev/null +++ b/roles/distgit/files/pkgdb2-clone @@ -0,0 +1,152 @@ +#!/usr/bin/env python + +import re +import requests +import sys +import getpass +import pkgdb2client +import subprocess + +#PAGE_URL = 'https://fedoraproject.org/w/api.php?format=json&action=query&rvprop=content&prop=revisions&titles=User:Codeblock/RequestsSANDBOX' +PAGE_URL = 'https://fedoraproject.org/w/api.php?format=json&action=query&rvprop=content&prop=revisions&titles=EPEL/epel7/Requests' +NEW_EPEL_VERSION = '7' +NEW_EPEL_SOURCE_BRANCH = 'f19' +RHEL_PKGS_PATH = '/var/lib/rhel/rhel' + NEW_EPEL_VERSION + +# parse_page :: String -> IO (Map String String) +# This returns a dictionary of {"pkg_name": "branch"} +def parse_page(url): + r = requests.get(url).json() + text = r['query']['pages'][r['query']['pages'].keys()[0]]['revisions'][0]['*'] + lines = text.split("\n") + pkgs = filter(lambda y: y.startswith('| '), lines) + __pkgs_list__ = map(lambda y: ''.join(y.split())[1:].split('||'), pkgs) + pkgs_list = filter(lambda y: y[0] != 'foo', __pkgs_list__) + pkgs_dict = dict(pkgs_list) + return pkgs_dict + +# is_in_rhel :: String -> IO Bool +def is_in_rhel(pkg): + with open(RHEL_PKGS_PATH) as f: + pkgs = map(lambda x: x.strip(), f.readlines()) + return (pkg in pkgs) + +# These tuples will be used to substitute one pattern for another. +# Every transform will be run on every branch name so be sure the +# pattern cannot match if you don't want it to be triggered. +transforms = ( + (re.compile(r'^devel$'), 'master'), + (re.compile(r'-'), ''), + (re.compile(r'^fc([0-9])'), r'f\1'), + (re.compile(r'^epel([456])$'), r'el\1'), + (re.compile(r'^el([789]|[1-9][0-9])'), r'epel\1'), +) +branch_replacements = {'devel': (re.compile(r'^devel$'), 'master'),} + +# generate_collection_cache :: PkgDB -> IO [String] +def generate_collection_cache(pkgdb): + raw_collections = pkgdb.get_collections(clt_status=( + 'Active', + 'Under Development')) + collection_cache = frozenset(map(lambda y: y['branchname'], + raw_collections['collections'])) + return collection_cache + +# normalize_branch :: [String] -> String -> IO (Option String) +def normalize_branch(collection_cache, branch): + # I originally had this implemented as a foldRight (which it really is). + # But Python doesn't eliminate tail calls. It probably would have been fine + # because "transforms" above is only 5 elements, but instead I will deal + # with the local mutation and wish that I had a type system to reason with. + # -rbe + norm_branch = branch.lower() + for transform in transforms: + norm_branch = re.sub(transform[0], transform[1], norm_branch) + + + # Ugh, here we break purity. Where is the option type when you need it? + if not (norm_branch in collection_cache): + print('Unknown collection specified: {0}'.format(branch)) + return None + + return norm_branch + +# process_package :: PkgDB -> String -> String -> IO Bool +def process_package(pkgdb, pkg, src, dest): + data = pkgdb.get_package(pkg) + pkg_list = data['packages'] + + maybe_source = filter(lambda y: y['collection']['branchname'] == src, + pkg_list) + maybe_dest = filter(lambda y: y['collection']['branchname'] == dest, + pkg_list) + if len(maybe_source) == 0: + print "Source branch `" + src + "' not found. Please "\ + "branch" + pkg + "manually." + return False + + if len(maybe_dest) != 0: + print "Package `" + pkg + "' was already branched for `" + dest + "'."\ + " Not overwriting branch." + return False + + acls = filter(lambda y: y['fas_name'] != 'group::provenpackager', + maybe_source[0]['acls']) + map(lambda acl: pkgdb.update_acl(pkg, dest, acl['acl'], acl['status'], + acl['fas_name']), acls) + return True + +# main :: [String] -> IO Unit +def main(args): + new_epel_requests = "epel" + NEW_EPEL_VERSION + "-requests" + if len(args) < 1 or (len(args) < 3 and args[0] != new_epel_requests) or\ + len(args) > 3 or (len(args) > 1 and args[0] == new_epel_requests): + print "Usage: pkgdb2-clone " + new_epel_requests + print " - OR -" + print " pkgdb2-clone " + sys.exit(1) + + pkgdb = pkgdb2client.PkgDB() + + username = raw_input('Username: ') + password = getpass.getpass() + pkgdb.login(username, password, True) + + collection_cache = generate_collection_cache(pkgdb) + + if args[0] == new_epel_requests: + pkgs = parse_page(PAGE_URL) + for key in pkgs: + if is_in_rhel(key): + continue + src_branchname = normalize_branch(collection_cache, pkgs[key]) + dest_branchname = normalize_branch(collection_cache, + 'epel' + NEW_EPEL_VERSION) + if not src_branchname or not dest_branchname: + print "[" + key + "] Invalid source or destination branch "\ + "name, " + src_branchname + " -> " + dest_branchname + else: + if process_package(pkgdb, key, src_branchname, dest_branchname): + subprocess.call(["mkbranch", + "-s", + NEW_EPEL_SOURCE_BRANCH, + "epel" + NEW_EPEL_VERSION, + key]) + print "[" + key + "] Success" + else: + print "[" + key + "] Error" + print "Done." + else: + src_branchname = normalize_branch(collection_cache, args[0]) + dest_branchname = normalize_branch(collection_cache, args[1]) + if not src_branchname or not dest_branchname: + print "[" + key + "] Invalid source or destination branch "\ + "name, " + src_branchname + " -> " + dest_branchname + for pkg in args[2:]: + if process_package(pkgdb, key, src_branchname, dest_branchname): + print "[" + key + "] Success" + else: + print "[" + key + "] Error" + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/roles/distgit/files/pkgdb2branch.py b/roles/distgit/files/pkgdb2branch.py new file mode 100644 index 0000000000..507fdd120b --- /dev/null +++ b/roles/distgit/files/pkgdb2branch.py @@ -0,0 +1,362 @@ +#!/usr/bin/python -t +# Author: Toshio Kuratomi +# Copyright: 2007-2008 Red Hat Software +# License: GPLv2 +# This needs a proper license and copyright here +__version__ = '0.3' + +import sys +import os +import optparse + +import subprocess + +import fedmsg + +# Do some off-the-bat configuration of fedmsg. +# 1) since this is a one-off script and not a daemon, it needs to connect to +# the fedmsg-relay process running on another node (or noone will hear it) +# 2) its going to use the 'shell' certificate which only 'sysadmin' has read +# access to. Contrast that with the 'scm' certificate which everyone in +# the 'packager' group has access to. +config = fedmsg.config.load_config([], None) +config['active'] = True +config['endpoints']['relay_inbound'] = config['relay_inbound'] +fedmsg.init(name='relay_inbound', cert_prefix='shell', **config) + +from fedora.client import FedoraServiceError +from pkgdb2client import PkgDB + +GITDIR='/srv/git/rpms' +BASEURL = os.environ.get('PACKAGEDBURL') or 'https://admin.fedoraproject.org/pkgdb/' +MKBRANCH='/usr/local/bin/mkbranch' +SETUP_PACKAGE='/usr/local/bin/setup_git_package' +BRANCHES = {'el4': 'master', 'el5': 'master', 'el6': 'master', 'epel7': 'f19', + 'olpc2': 'f7', + 'olpc3': 'f11', + 'master': None, + 'fc6': 'master', + 'f7': 'master', + 'f8': 'master', + 'f9': 'master', + 'f10': 'master', + 'f11': 'master', + 'f12': 'master', + 'f13': 'master', 'f14': 'master', + 'f15': 'master', 'f16': 'master', + 'f17': 'master', 'f18': 'master', + 'f19': 'master', 'f20': 'master' + } + +# The branch names we get out of pkgdb have to be translated to git +GITBRANCHES = {'el4': 'el4', 'el5': 'el5', 'el6': 'el6', 'epel7': 'epel7', + 'OLPC-2': 'olpc2', + 'FC-6': 'fc6', 'F-7': 'f7', 'F-8': 'f8', 'F-9': 'f9', + 'F-10': 'f10', 'OLPC-3': 'olpc3', + 'F-11': 'f11', 'F-12': 'f12', 'F-13': 'f13', 'f14': 'f14', 'f15': 'f15', 'f16': 'f16', 'f17': 'f17', + 'f18': 'f18', 'f19': 'f19', 'f20': 'f20', + 'devel': 'master'} + +# The branch options we get from the CLI have to be translated to pkgdb +BRANCHBYGIT = dict([(v, k) for (k, v) in GITBRANCHES.iteritems()]) + +class InternalError(Exception): + pass + +class PackageDBError(InternalError): + pass + +class ProcessError(InternalError): + pass + +class ArgumentsError(InternalError): + pass + +class InvalidBranchError(PackageDBError): + pass + +class PackageDBClient(PkgDB): + def __init__(self, baseURL): + '''Initialize the connection. + + Args: + :baseURL: URL from which the packageDB is accessed + ''' + # We're only performing read operations so we don't need a username + super(PackageDBClient, self).__init__(baseURL) + + def get_package_branches(self, pkgname): + '''Return the branches to which a package belongs. + + Args: + :pkgname: The package to retrieve branch information about + ''' + + data = self.get_package(pkgname) + return map(lambda x: x['collection']['branchname'], data['packages']) + + def get_package_list(self, branchName): + '''Retrieve all the packages in a specific branch. + + Args: + :branchName: to return the packages for + ''' + pkgs = map(lambda l: l['name'], self.get_packages('*', branchName, page=0)['packages']) + return pkgs + +class Brancher(object): + ''' Make branches in the GIT Repository.''' + + def __init__(self, pkgdburl, cache, verbose): + # Connect to the package database + self.verbose = verbose + self.client = PackageDBClient(BASEURL) + + def _invoke(self, program, args): + '''Run a command and raise an exception if an error occurred. + + Args: + :program: The program to invoke + :args: List of arguments to pass to the program + + raises ProcessError if there's a problem. + ''' + cmdLine = [program] + cmdLine.extend(args) + print ' '.join(cmdLine) + + stdoutfd = subprocess.PIPE + if self.verbose: + program = subprocess.Popen(cmdLine, stderr=subprocess.STDOUT) + else: + program = subprocess.Popen(cmdLine, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + retCode = program.wait() + if retCode != 0: + e = ProcessError() + e.returnCode = retCode + e.cmd = ' '.join(cmdLine) + if self.verbose: + output = program.stdout.read() + e.message = 'Error, "%s" returned %s: %s' % (e.cmd, e.returnCode, output) + else: + e.message = 'Error, "%s" returned %s' % (e.cmd, e.returnCode) + raise e + + def _create_branch(self, pkgname, branch): + '''Create a specific branch for a package. + + Args: + :pkgname: Name of the package to branch + :branch: Name of the branch to create + + raises InvalidBranchError if a branchname is unknown. + + Will ignore a branch which is EOL. + ''' + try: + branchFrom = '%s/master' % BRANCHES[branch] + except KeyError: + raise InvalidBranchError( + 'PackageDB returned an invalid branch %s for %s' % + (branch, pkgname)) + + # Add the master to the branch + # No longer add this after the new branching setup. + #branch = '%s/master' % branch + # If branchFrom is None, this is an EOL release + # If the directory already exists, no need to invoke mkbranch + if branchFrom: + # Fall back to branching from master. + frombranchpath = os.path.join(GITDIR, '%s.git' % pkgname, + 'refs/heads', branchFrom) + if not os.path.exists(frombranchpath): + branchFrom = 'master' + + branchpath = os.path.join(GITDIR, '%s.git' % pkgname, + 'refs/heads', branch) + if not os.path.exists(branchpath): + try: + self._invoke(MKBRANCH, ['-s', branchFrom, branch, pkgname]) + except ProcessError, e: + if e.returnCode == 255: + # This is a warning, not an error + return + raise + finally: + fedmsg.publish( + topic='branch', + modname='git', + msg=dict( + agent=os.getlogin(), + name=pkgname, + branch=branch, + ), + ) + + def branch_package(self, pkgname): + '''Create all the branches that are listed in the pkgdb for a package. + + Args: + :pkgname: The package to create branches for + + Note: this will ignore branches which are EOL. + + raises PackageDBError if the package is not present in the Package + Database. + ''' + # Retrieve branch information + try: + branches = self.client.get_package_branches(pkgname) + except FedoraServiceError, e: + raise PackageDBError( + 'Unable to retrieve information about %s: %s' % + (pkgname, str(e))) + + # Create the devel branch if necessary + if not os.path.exists(os.path.join(GITDIR, + '%s.git' % pkgname)): + self._invoke(SETUP_PACKAGE, [pkgname]) + # Create all the required branches for the package + # Use the translated branch name until pkgdb falls inline + for branch in branches: + if branch == 'devel': + continue + if not branch in GITBRANCHES.keys(): + print 'Skipping unknown branch %s' % branch + continue + self._create_branch(pkgname, GITBRANCHES[branch]) + + def mass_branch(self, branchName): + '''Make sure all packages listed for a specific branch in the PackageDB + have a CVS branch. + + Args: + :branchName: The branch to ensure. + ''' + fedmsg.publish( + topic='mass_branch.start', + modname='git', + msg=dict(agent=os.getlogin()), + ) + # Retrieve all the packages in this branch + pkglist = self.client.get_package_list(branchName) + pkglist.sort() + for pkg in pkglist: + # Create a branch for this release for each of them + # Use the translated branch name until pkgdb falls inline + self._create_branch(pkg, GITBRANCHES[branchName]) + + fedmsg.publish( + topic='mass_branch.complete', + modname='git', + msg=dict(agent=os.getlogin()), + ) + +def parse_commands(): + parser = optparse.OptionParser(version=__version__, usage='''pkgdb2branch.py [options] PACKAGENAME [packagename, ...] [-] + pkgdb2branch.py [options] --branchfor BRANCH + +pkgdb2branch reads package information from the packagedb and creates branches +on the git server based on what branches are listed there. pkgdb2branch can +read the list of packages from stdin if you specify '-' as an argument. + +pkgdb2branch has two modes of operation. In the first mode, you specify which +packages you want to branch. This mode is more efficient for a small number +of packages. + +In the second mode, pkgdb2branch will find every package lacking a BRANCH and +will create one if the pkgdb says it's needed. This mode is very efficient for +mass branching. This implies --cache-branches. + +For those with a moderate number of packages, using a list of packages and +--cache-branches may be fastest.''') + parser.add_option('-b', '--branch-for', + dest='branchFor', + action='store', + help='Make sure all the packages have been branched for BRANCHFOR. Implies -c.') + parser.add_option('-c', '--cache-branches', + dest='enableCache', + action='store_true', + help='Download a complete cache of packages') + parser.add_option('--verbose', + dest='verbose', + action='store_true', + help='Enable verbose output') + (opts, args) = parser.parse_args() + + if opts.branchFor: + if args: + raise ArgumentsError('Cannot specify packages with --branchFor') + opts.enableCache = True + + if '-' in args: + opts.fromStdin = True + del (args[args.index('-')]) + else: + opts.fromStdin = False + + if not (args or opts.fromStdin or opts.branchFor): + raise ArgumentsError('You must list packages to operate on') + + return opts, args + +if __name__ == '__main__': + try: + options, packages = parse_commands() + except ArgumentsError, e: + print e + sys.exit(1) + + branchedPackages, unbranchedPackages = [], [] + brancher = Brancher(BASEURL, options.enableCache, options.verbose) + fedmsg.publish( + topic='pkgdb2branch.start', + modname='git', + msg=dict(agent=os.getlogin()), + ) + + if options.branchFor: + try: + unbranchedPackages = \ + brancher.mass_branch(BRANCHBYGIT[options.branchFor]) + except PackageDBError, e: + print 'Unable contact the PackageDB. Error: %s' % str(e) + sys.exit(1) + else: + # Process packages specified on the cmdline + for pkgname in packages: + try: + brancher.branch_package(pkgname) + branchedPackages.append(pkgname) + except InternalError, e: + print str(e) + unbranchedPackages.append(pkgname) + + # Process packages from stdin + if options.fromStdin: + for pkgname in sys.stdin: + pkgname = pkgname.strip() + try: + brancher.branch_package(pkgname) + branchedPackages.append(pkgname) + except InternalError, e: + print str(e) + unbranchedPackages.append(pkgname) + + fedmsg.publish( + topic='pkgdb2branch.complete', + modname='git', + msg=dict( + agent=os.getlogin(), + branchedPackages=branchedPackages, + unbranchedPackages=unbranchedPackages, + ), + ) + + if unbranchedPackages: + print 'The following packages were unbranched:' + print '\n'.join(unbranchedPackages) + sys.exit(100) + + sys.exit(0) diff --git a/roles/distgit/files/pkgs.fedoraproject.org.conf b/roles/distgit/files/pkgs.fedoraproject.org.conf new file mode 100644 index 0000000000..cf4e0181c3 --- /dev/null +++ b/roles/distgit/files/pkgs.fedoraproject.org.conf @@ -0,0 +1 @@ +include "conf.d/pkgs.fedoraproject.org/*.conf" diff --git a/roles/distgit/files/process-git-requests b/roles/distgit/files/process-git-requests new file mode 100644 index 0000000000..c2bb9b2d4b --- /dev/null +++ b/roles/distgit/files/process-git-requests @@ -0,0 +1,654 @@ +#!/usr/bin/python -t +VERSION = "1.0" + +# $Id: process-cvs-requests.py,v 1.25 2010/07/01 04:51:23 tibbs Exp $ + +# TODO: +# Extract fedora-review flag setter if possible. +# Display last linked spec file. +# Download (and process?) last linked srpm + +# Checks to add: +# Package already exists in pkgdb. +# fedora-review flag isn't set (especially if it's still set to '?' +# Catch common misspellings? +# Any owner contains '@' or other invalid character +# Maybe verify owners in pkgdb/FAS. +# SSH into cvs.fedoraproject.org to run pkg2branch.py directly +# or just run on cvs.fedoraproject.org +# Try to do some checking on the ~/.bugzillacookies file and suggest "bugzilla login" + +import bugzilla +import codecs +import datetime +import getpass +import glob +import operator +import os +import re +import readline +import sys +import subprocess +import tempfile +import time +import xmlrpclib +from configobj import ConfigObj, flatten_errors +from fedora.client import AuthError, AppError, PackageDB +from optparse import OptionParser +from validate import Validator + +# Red Hat's bugzilla +url = 'https://bugzilla.redhat.com/xmlrpc.cgi' + +# Users who indicated that they're OK with EPEL branches. Some request that +# they be made comaintainers. +# Taken from http://fedoraproject.org/wiki/EPEL/ContributorStatusNo +epel_ok = ['abompart', 'athimm', 'corsepiu', 'ecik', 'faucamp', 'konradm', + 'monnerat', 'mtasaka', 'nim', 'rafalzaq', 'rineau', 'rstrode', + 'sgrubb', 'shishz', 'terjeros', 'zkota'] +epel_ok_comaint = ['alexlan', 'guidograzioli', 'jwrdegoede', 'kkofler', + 'mebourne', 'overholt', 'pgordon', 'rishi', 'snirkel'] + +PAGER = os.environ.get('PAGER') or '/usr/bin/less' +EDITOR = os.environ.get('EDITOR') or '/usr/bin/vim' + +# Override a method in xmlrpclib so it doesn't blow up when getting crap data +# from Red Hat's bugzilla. +# Bugfixes seem to have rendered this unnecessary +#def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search): +# # decode non-ascii string (if possible) +# if unicode and encoding and is8bit(data): +# data = unicode(data, encoding, 'replace') +# return data +#xmlrpclib._decode = _decode + +def parse_commandline(): + usage = 'usage: %prog [options]' + parser = OptionParser(usage) + parser.add_option('--url', dest='url', + help='bugzilla URL to query', + default=url) + parser.add_option('-u', '--user', + help='Username for PackageDB connection', + dest='user', + default=getpass.getuser()) + parser.add_option('--debug', + action='store_true', + dest='debug', + default=False, + help='Turn on some debugging statements') + + (options, args) = parser.parse_args() + return options + +def parse_pkgdb_config(): + vldtr = Validator() + # configspec to validate types and set defaults + configspec = ''' + [global] + pkgdb.url = string(default = 'https://admin.fedoraproject.org/pkgdb') + pkgdb.retries = integer(default = 5) + pkgdb.knowngroups = list(default = list()) + '''.splitlines() + + cfg = ConfigObj('/etc/pkgdb-client.cfg', configspec=configspec) + user = ConfigObj(os.path.expanduser('~/.fedora/pkgdb-client.cfg'), + configspec=configspec) + cfg.merge(user) + res = cfg.validate(vldtr, preserve_errors=True) + + for entry in flatten_errors(cfg, res): + section_list, key, error = entry + section_list.append(key) + section_string = ','.join(section_list) + if error == False: + error = 'Missing value or section.' + print ','.join(section_list), '=', error + sys.exit(1) + + cfg['global']['pkgdb.url'] = os.environ.get('PACKAGEDBURL') or cfg['global']['pkgdb.url'] + return cfg['global'] + +def encode_utf8(object, encoding='utf8', errors='replace'): + if isinstance(object, basestring): + if isinstance(object, str): + return unicode(object, encoding, errors) + else: + return object + return u'' + +def add_package(pkgdb, request): + for retry in range(1, config['pkgdb.retries'] + 1): + try: + pkgdb.add_package(pkg=request['pkg'], + owner=request['owner'], + description=request['description'], + branches=request['branches'], + cc_list=request['cc_list'], + comaintainers=request['comaintainers']) + except AuthError, e: + if sys.stdin.isatty(): + if retry >= config['pkgdb.retries']: + break + pkgdb.password = getpass.getpass('PackageDB Password: ') + else: + # Don't retry if we're reading the password from stdin + break + else: + break + +def edit_package(pkgdb, request): + for retry in range(1, config['pkgdb.retries'] + 1): + try: + pkgdb.edit_package(pkg=request['pkg'], + owner=request['owner'], + branches=request['newbranches'], + cc_list=request['cc_list'], + comaintainers=request['comaintainers']) + except AuthError, e: + if retry >= config['pkgdb.retries']: + break + pkgdb.password = getpass.getpass('PackageDB Password: ') + else: + break + +def run_query(bz): + querydata = {} + querydata['column_list'] = ['opendate', 'changeddate', 'bug_severity', + 'alias', 'assigned_to', 'reporter', 'bug_status', 'resolution', + 'component', 'blockedby', 'dependson', 'short_desc', + 'status_whiteboard', 'flag_types'] + querydata['product'] = ['Fedora'] + + querydata['field0-0-0'] = 'flagtypes.name' + querydata['type0-0-0'] = 'equals' + querydata['value0-0-0'] = 'fedora-cvs?' + + bugs = bz.query(querydata) + bugs.sort(key=operator.attrgetter('bug_id')) + + ids = map(lambda x: x.bug_id, bugs) + comments = bz._proxy.Bug.comments({"ids": ids}) + + return [bugs, comments] + +def display_bug(bug, comments): + '''Show the complete ticket in a pager.''' + comment = 0 + b = [] + b.append('https://bugzilla.redhat.com/%d' % bug.bug_id) + b.append('Bug %d - %s' % (bug.bug_id, bug.short_desc)) + b.append('Reported by: %s at %s' % (bug.reporter, bug.opendate)) + b.append('Assigned to: %s' % (bug.assigned_to)) + for i in comments: + b.append('-'*40) + #b.append('Comment %d by %s at %s\n' % (comment, i['author'], time.strftime('%F %T',i['time'].timetuple()))) + #b.append('Comment %d by %s at %04d-%02d-%02d %02d:%02d%02d\n' % ( + b.append('Comment %d by %s at %s\n' % ( + comment, i['author'], i['time'])) + b.append(i['text']) + b.append('') + comment += 1 + + p = subprocess.Popen(PAGER, stdin=subprocess.PIPE) + p.communicate('\n'.join(b).encode('utf8')) + + +def edit_string(s): + '''Edit the contents of a string in the user's preferred editor.''' + (fd, f) = tempfile.mkstemp() + fh=os.fdopen(fd, 'w+') + fh.write(s) + fh.close() + p = subprocess.Popen([EDITOR, f]); + sts = os.waitpid(p.pid, 0)[1] + if not sts: + try: + fh = open(f, 'r') + s = fh.read() + finally: + fh.close() + + return s + + +def parse_prefixed_lines(s): + lastitem = '' + items = {} + items['Branches'] = '' + lines = s.splitlines() + + # Skip until the Request line + while 1: + if (lines[0].find('New Package CVS Request') == 0 + or lines[0].find('Package Change Request') == 0): + break + lines.pop(0) + + # Skip until a line containing a colon + while 1: + if lines[0].find(':') >= 0: + break + lines.pop(0) + + # Now parse + while 1: + if not len(lines): + break + + line = lines.pop(0) + line.strip() + if len(line) == 0: + break + + pos = line.find(':') + + # Line-wrapped? + if pos < 0: + items[lastitem] += " " + line.strip() + continue + + lastitem = line[:pos] + items[lastitem] = line[pos+1:].strip() + + return items + +def clean_request(items): + '''Clean up various bits that can be passed in a CVS request.''' + request = {} + + if not 'InitialCC' in items: + items['InitialCC'] = '' + if not 'Owners' in items: + items['Owners'] = '' + if not 'Short Description' in items: + items['Short Description'] = '' + + branches = items['Branches'].strip() + branches = re.sub(r',', ' ', branches) + branches = re.sub(r'f', 'F', branches) + branches = re.sub(r'devel', ' ', branches) + branches = re.sub(r'F([1-9][0-9])', r'F-\1', branches) + branches = re.sub(r'EL([1-9])', r'EL-\1', branches) + branches = re.sub(r'F-14', r'f14', branches) + branches = re.sub(r' +', ' ', branches) + branches = branches.strip() + branches += ' devel' + items['Branches'] = branches + request['branches'] = branches.split() + + if 'New Branches' in items: + branches = items['New Branches'].strip() + branches = re.sub(r',', ' ', branches) + branches = re.sub(r'f', 'F', branches) + branches = re.sub(r'F([1-9][0-9])', r'F-\1', branches) + branches = re.sub(r'F-14', r'f14', branches) + branches = re.sub(r' +', ' ', branches) + branches = branches.strip() + items['New Branches'] = branches + request['newbranches'] = branches.split() + + owners = items['Owners'].strip() + owners = re.sub(r',', ' ', owners) + if len(owners): + request['owner'] = owners.split()[0] + request['comaintainers'] = owners.split()[1:] + + request['cc_list'] = items['InitialCC'].split() + request['pkg'] = items['Package Name'] + request['description'] = items['Short Description'] + + return request + +def new_request_string(items, bug): + r = [] + r.append("Bug URL: http://bugzilla.redhat.com/%d " % bug.bug_id) + r.append("Bug summary: " + bug.short_desc) + r.append('') + r.append("New Package CVS Request") + r.append("=======================") + r.append("Package Name: " + items['Package Name']) + r.append("Short Description: " + items['Short Description']) + r.append("Owners: " + items['Owners']) + r.append("Branches: " + items['Branches']) + r.append("InitialCC: " + items['InitialCC']) + r.append('') + return '\n'.join(r) + +def change_request_string(items, bug): + r = [] + r.append("Bug URL: http://bugzilla.redhat.com/%d" % bug.bug_id) + r.append("Bug summary: " + bug.short_desc) + r.append('') + r.append("Package Change Request") + r.append("======================") + r.append("Package Name: " + items['Package Name']) + r.append("Owners: " + items['Owners']) + r.append("New Branches: " + items['New Branches']) + r.append("InitialCC: " + items['InitialCC']) + r.append('') + return '\n'.join(r) + +def get_pkgdb_owners(pkgdb, pkg): + owners = {} + o = '' + for i in pkgdb.get_owners(pkg)['packageListings']: + branch = i['collection']['branchname'] + if branch not in branches: + continue + + owners[branch] = {} + owners[branch]['primary'] = i['owner'] + owners[branch]['comaint'] = [] + for j in i['people']: + #if 'commit' in j['aclOrder']: + if j['aclOrder']['commit'] != None and j['username'] != owners[branch]: + owners[branch]['comaint'].append(j['username']) + + for i in sorted(branches, reverse=True): + if i in owners: + o += "%s: %s" % (i, owners[i]['primary']) + if len(owners[i]['comaint']): + o += ' - %s' % ','.join(sorted(owners[i]['comaint'])) + o += '\n' + + return (owners, o) + +def process_no_request(bug, allcomments): + '''Deal with a ticket where no request was found.''' + while 1: + os.system('clear') + print "No CVS request found in bug %d\nhttp://bugzilla.redhat.com/%d." % (bug.bug_id, bug.bug_id) + ok = raw_input('\nWhat do? (n=Next, s=Show ticket, c=Comment, q=Quit):') + if ok == 'c': + bug_comment = edit_string('') + print bug_comment + ok = raw_input("\nPost this comment to the ticket (y/n)?") + if ok == 'y': + print "Updating bugzilla..." + bug.addcomment(bug_comment) + ok = raw_input("\nClear the fedora-cvs flag (y/n)?") + if ok == 'y': + print "Clearing the flag..." + bug.updateflags({'fedora-cvs':'X', 'nomail':1}) + break + elif ok == 'n': + return True + elif ok == 'q': + return False + elif ok == 's': + print + display_bug(bug, allcomments) + return True + +def process_new_request(bug, comment, allcomments, firstfound, pkgdb, branches): + '''Parse a new package request, try to repair line wrapping, and do some + basic validity checks.''' + warned = False + warnings = [] + items = parse_prefixed_lines(comment['text']) + request = clean_request(items) + + if not firstfound: + warnings.append("WARNING: CVS request was not the last comment.") + warned = True + if not 'Package Name' in items: + warnings.append("WARNING: No package name supplied.") + warned = True + if not 'Owners' in items: + warnings.append("WARNING: No owners provided.") + warned = True + if not 'Short Description' in items: + warnings.append("WARNING: No description provided.") + warned = True + for i in request['branches']: + if i not in branches: + warnings.append("WARNING: Invalid branch %s requested" % i) + warned = True + + short_desc = bug.short_desc + m=re.search('Review Request:\s([a-zA-Z0-9_+.-]+)\s+', short_desc, re.I) + if not m: + warnings.append("WARNING: Couldn't parse package name out of bug summary.") + warned = True + elif m.group(1) != items['Package Name']: + warnings.append("WARNING: Requested package name %s doesn't match bug summary %s" % (items['Package Name'], m.group(1))) + warned = True + + req_string = new_request_string(items, bug) + bug_comment = 'GIT done (by process-git-requests).\n' + + okprompt = 'Do it (yes=Yes, n=No, e=Edit request, s=Show ticket, c=Comment, q=Quit)?' + if warned: + prompt = 'Warnings present!\nDo it (a=Accept warnings, n=No, e=Edit request, s=Show ticket, c=Comment, q=Quit)?' + else: + prompt = okprompt + + # We have to loop until the user accepts the request + while 1: + # We have to loop until the user enters something that works + while 1: + os.system('clear') + if len(warnings): + print '\n'.join(warnings), "\n" + print "Currently assigned to: %s" % bug.assigned_to + print req_string + ok = raw_input(prompt) + if ok == 'a': + prompt = okprompt + warned = False + if ok == 'c': + bug_comment = edit_string('') + print bug_comment + ok = raw_input("\nPost this comment to the ticket (y/n)?") + if ok == 'y': + print "Updating bugzilla..." + bug.addcomment(bug_comment) + ok = raw_input("\nClear the fedora-cvs flag (y/n)?") + if ok == 'y': + print "Clearing the flag..." + bug.updateflags({'fedora-cvs':'X', 'nomail':1}) + return (False, True) + elif ok == 'e': + req_string = edit_string(req_string) + items=parse_prefixed_lines(req_string) + request = clean_request(items) + req_string = new_request_string(items, bug) + break + elif ok == 'n': + return (False, True) + elif ok == 'q': + return (False, False) + elif ok == 's': + print + display_bug(bug, allcomments) + elif ok == 'yes' and not warned: + bug_comment = edit_string(bug_comment) + print '\n', bug_comment + ok = raw_input('Go ahead (y/n)?') + if ok != 'y': + break + print 'Calling pkgdb...' + try: + add_package(pkgdb, request) + except Exception, e: + print "Pkgdb call failed:" + print e + raw_input('\nPress enter to continue to the next ticket.') + return (False, True) + + print 'Updating bugzilla...' + # XXX Need to handle errors here + bug.updateflags({'fedora-cvs':'+', 'nomail':1}) + bug.addcomment(bug_comment) + return (request['pkg'], True) + else: + pass + +def process_change_request(bug, comment, allcomments, firstfound, pkgdb, branches): + '''Parse a change request, try to repair line wrapping, and do some + basic validity checks.''' + owned = False + warned = False + warnings = [] + items = parse_prefixed_lines(comment['text']) + request = clean_request(items) + print "Looking up owners in pkgdb..." + (owners, owner_string) = get_pkgdb_owners(pkgdb, items['Package Name']) + + # Try to enforce EPEL branch rules + for i in owners.keys(): + if request['owner'] == owners[i]['primary'] or request['owner'] in owners[i]['comaint']: + owned = True + if not owned and items['New Branches'].find('EL') >= 0 and owners['devel']['primary'] in epel_ok: + warnings.append("NOTE: new branch owner not owner of other branches,\n but primary devel owner is OK with EPEL branches.") + elif not owned and items['New Branches'].find('EL') >= 0 and owners['devel']['primary'] in epel_ok_comaint: + warnings.append("NOTE: new branch owner not owner of other branches,\n but primary devel owner is OK with EPEL branches\n as long as they comaintain.") + elif not owned: + warnings.append("WARNING: new branch owner not owner of other branches.") + warned = True + + if not firstfound: + warnings.append("WARNING: GIT request was not the last comment.") + warned = True + if not 'Package Name' in items: + warnings.append("WARNING: No package name supplied.") + warned = True + if not 'Owners' in items: + warnings.append("WARNING: No owners provided.") + warned = True + if not 'New Branches' in items: + warnings.append("WARNING: No new branches requested.") + for i in request['branches']: + if i not in branches: + warnings.append("WARNING: Invalid branch %s requested" % i) + warned = True + + short_desc = bug.short_desc + req_string = change_request_string(items, bug) + bug_comment = 'GIT done (by process-git-requests).\n' + + okprompt = 'Do it (yes=Yes, n=No, e=Edit request, s=Show ticket, c=Comment, q=Quit)?' + if warned: + prompt = 'Warnings present!\nDo it (a=Accept warnings, n=No, e=Edit request, s=Show ticket, c=Comment, q=Quit)?' + else: + prompt = okprompt + + # We have to loop until the user accepts the request + while 1: + # We have to loop until the user enters something that works + while 1: + os.system('clear') + if len(warnings): + print '\n'.join(warnings), "\n" + print req_string + "\nCurrent branch owners - comaintainers:\n" + owner_string + ok = raw_input(prompt) + if ok == 'a': + prompt = okprompt + warned = False + if ok == 'c': + bug_comment = edit_string('') + print bug_comment + ok = raw_input("\nPost this comment to the ticket (y/n)?") + if ok == 'y': + print "Updating bugzilla..." + bug.addcomment(bug_comment) + ok = raw_input("\nClear the fedora-cvs flag (y/n)?") + if ok == 'y': + print "Clearing the flag..." + bug.updateflags({'fedora-cvs':'X', 'nomail':1}) + return (False, True) + elif ok == 'e': + req_string = edit_string(req_string) + items=parse_prefixed_lines(req_string) + request = clean_request(items) + req_string = change_request_string(items, bug) + break + elif ok == 'n': + return (False, True) + elif ok == 'q': + return (False, False) + elif ok == 's': + print + display_bug(bug, allcomments) + elif ok == 'yes' and not warned: + bug_comment = edit_string(bug_comment) + print '\n', bug_comment + ok = raw_input('Go ahead (y/n)?') + if ok != 'y': + break + print 'Calling pkgdb...' + try: + edit_package(pkgdb, request) + except Exception, e: + print "Pkgdb call failed:" + print e + raw_input('\nPress enter to continue to the next ticket.') + return (False, True) + + print 'Updating bugzilla...' + # XXX Need to handle errors here + bug.updateflags({'fedora-cvs':'+', 'nomail':1}) + bug.addcomment(bug_comment) + return (request['pkg'], True) + else: + pass + +if __name__ == '__main__': + branches = {} + processed = [] + options = parse_commandline() + print "Connecting to bugzilla..." + bz = bugzilla.Bugzilla(url=options.url) + print "Querying bugzilla..." + (bugs, comments) = run_query(bz) + print "Done; got %d." % len(bugs) + if not len(bugs): + print "No requests to process!" + exit(0) + + print "Connecting to pkgdb..." + config = parse_pkgdb_config() + pkgdb = PackageDB(config['pkgdb.url'], username=options.user, + debug=options.debug) + print "Getting valid branches...." + for i in pkgdb.get_collection_list(eol=False): + branches[i[0]['branchname']] = 1 + print "Done." + print + + # Iterate over bugs + for i in bugs: + firstfound = True + type = '' + print "Parsing bug %d - https://bugzilla.redhat.com/%d" % (i.bug_id, i.bug_id) + for j in reversed(comments['bugs'][str(i.bug_id)]['comments']): + if 'New Package CVS Request' in j['text']: + type = 'new' + break + if 'Package Change Request' in j['text']: + type = 'change' + break + firstfound = False + else: + if not process_no_request(i, comments['bugs'][str(i.bug_id)]['comments']): + break + + if type == 'new': + (package, more) = process_new_request(i, j, comments['bugs'][str(i.bug_id)]['comments'], firstfound, pkgdb, branches) + if package: + processed.append(package) + if not more: + break + elif type == 'change': + (package, more) = process_change_request(i, j, comments['bugs'][str(i.bug_id)]['comments'], firstfound, pkgdb, branches) + if package: + processed.append(package) + if not more: + break + + if len(processed): + print '\nYou must now run this on the git server\nto set up the git repository:' + print '/usr/local/bin/pkgdb2branch.py ' + ' '.join(processed) + + sys.exit(0) diff --git a/roles/distgit/files/redirect.conf b/roles/distgit/files/redirect.conf new file mode 100644 index 0000000000..dc1b6a1133 --- /dev/null +++ b/roles/distgit/files/redirect.conf @@ -0,0 +1,160 @@ +RedirectMatch permanent ^/$ http://pkgs.fedoraproject.org/cgit/ + +RewriteEngine on + +# rewrite log +# set level to 0 to turn off and speed things up +# when debugging - set to 3 and you can see what the hell is going on +RewriteLogLevel 0 +RewriteLog "/var/log/httpd/git-rewrite.log" + +# Use cgit and redirect (some) old gitweb-caching things +RewriteRule ^/cgit-data/(.*)$ /cgit-data/$1 [L,PT] +RewriteRule ^/cgit/(.*)$ /cgit/$1 [L,PT] + +# blob +RewriteCond %{REQUEST_URI} /(.+)(\.git)/blob/(.+)/(.+):/(.+) [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=blob;h=(.+);hb=(.+);f=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/tree/%5?id=%3;id2=%4 [R,L,NE] + +RewriteCond %{REQUEST_URI} /(.+)(\.git)/blob/(.+):/(.+) [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=blob;hb=(.+);f=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/tree/%4?id=%3 [R,L,NE] + +RewriteCond %{query_string} p=(.+)(\.git);a=blob;f=(.+);h=(.+);hb=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/tree/%3?id=%4;id2=%5 [R,L,NE] + +RewriteCond %{query_string} p=(.+)(\.git);a=blob;f=(.+);h=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/tree/%3?id=%4 [R,L,NE] + +# tree +RewriteCond %{REQUEST_URI} /(.+)(\.git)/tree/(.+)/(.+):/(.+) [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=tree;h=(.+);hb=(.+);f=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/tree/%5?id=%4 [R,L,NE] + +RewriteCond %{REQUEST_URI} /(.+)(\.git)/tree/(.+):/(.+) [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=tree;hb=(.+);f=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/tree/%4?id=%3 [R,L,NE] + +RewriteCond %{REQUEST_URI} /(.+)(\.git)/tree/(.+)/(.+) [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=tree;h=(.+);hb=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/tree/?id=%4 [R,L,NE] + +RewriteCond %{REQUEST_URI} /(.+)(\.git)/tree/(.+) [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=tree;hb=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/tree/?id=%3 [R,L,NE] + +RewriteCond %{REQUEST_URI} /(.+)(\.git)/tree [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=tree +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/tree/? [R,L,NE] + +# commitdiff +RewriteCond %{REQUEST_URI} /(.+)(\.git)/commitdiff/(.+)/(.+):/(.+) [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=blobdiff;h=(.+);hp=(.+);hb=(.+);f=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/diff/%6?id2=%4;id=%3;id3=%5 [R,L,NE] + +RewriteCond %{REQUEST_URI} /(.+)(\.git)/commitdiff/(.+)/(.+) [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=commitdiff;h=(.+);hp=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/diff/?id=%4;id2=%3 [R,L,NE] + +RewriteCond %{REQUEST_URI} /(.+)(\.git)/commitdiff/(.+) [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=commitdiff;h=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/diff/?id=%3 [R,L,NE] + +# commit +RewriteCond %{REQUEST_URI} /(.+)(\.git)/commit/(.+) [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=commit;h=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/commit/?id=%3 [R,L,NE] + +# summary +RewriteCond %{REQUEST_URI} /(.+)(\.git)/summary [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=summary +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/? [R,L,NE] + +# shortlog +RewriteCond %{REQUEST_URI} /(.+)(\.git)/shortlog/(.+) [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=shortlog;h=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/log/?id=%3 [R,L,NE] + +RewriteCond %{REQUEST_URI} /(.+)(\.git)/shortlog [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=shortlog +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/log/? [R,L,NE] + +# log +RewriteCond %{REQUEST_URI} /(.+)(\.git)/log/(.+) [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=log;h=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/log/?id=%3 [R,L,NE] + +RewriteCond %{REQUEST_URI} /(.+)(\.git)/log [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=log +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/log? [R,L,NE] + +# history +RewriteCond %{REQUEST_URI} /(.+)(\.git)/history/(.+)/(.+):/(.+) [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=history;h=(.+);hb=(.+);f=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/log/%5?id=%4 [R,L,NE] + +RewriteCond %{query_string} p=(.+)(\.git);a=history;f=(.+);h=(.+);hb=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/log/%3?id=%4;id2=%5 [R,L,NE] + +RewriteCond %{REQUEST_URI} /(.+)(\.git)/history/(.+):/(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/log/%4?id=%3 [R,L,NE] + +RewriteCond %{query_string} p=(.+)(\.git);a=history;f=(.+);h=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/log/%3?id=%4 [R,L,NE] + +RewriteCond %{REQUEST_URI} /(.+)(\.git)/history/(.+)/(.+) [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=history;h=(.+);hb=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/log/?id=%4 [R,L,NE] + +RewriteCond %{REQUEST_URI} /(.+)(\.git)/history/(.+):/(.+) [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=history;hb=(.+);f=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/log/%4?id=%3 [R,L,NE] + +RewriteCond %{REQUEST_URI} /(.+)(\.git)/history/(.+) [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=history;hb=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/log/?id=%3 [R,L,NE] + +# tag +RewriteCond %{REQUEST_URI} /(.+)(\.git)/tag/(.+) [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=tag;h=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/tag/?id=%3 [R,L,NE] + +# blob_plain +RewriteCond %{REQUEST_URI} /(.+)(\.git)/blob_plain/(.+):/(.+) [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=blob_plain;h=(.+);f=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/plain/%4?id=%3 [R,L,NE] + +RewriteCond %{query_string} p=(.+)(\.git);a=blob_plain;f=(.+);hb=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/plain/%3?id2=%4 [R,L,NE] + +RewriteCond %{REQUEST_URI} /(.+)(\.git)/blob_plain/(.+) [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=blob_plain;f=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/plain/%3 [R,L,NE] + +# rss|atom +RewriteCond %{REQUEST_URI} /(.+)(\.git)/(rss|atom)/refs/heads/(.+) [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=(rss|atom);h=refsheads/(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/atom?h=%4 [R,L,NE] + +RewriteCond %{REQUEST_URI} /(.+)(\.git)/(rss|atom) [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=(rss|atom) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/atom? [R,L,NE] + +# snapshot +RewriteCond %{REQUEST_URI} /(.+)(\.git)/snapshot/(.+)(\.tar\.gz|\.tar\.bz2) [OR] +RewriteCond %{query_string} p=(.+)(\.git);a=snapshot;h=(.+);sf=(.+) +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/snapshot/%3.tar.gz [R,L,NE] + +# base old gitweb project +RewriteCond %{REQUEST_URI} /gitweb/(.+)\.git.* [OR] +RewriteCond %{query_string} p=(.+)\.git.* +RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/? [R,L,NE] + +# Fail safes incase nothing above matches, try at least to put the person in the project +#RewriteCond %{REQUEST_URI} /(.+)\.git.* [OR] +#RewriteCond %{query_string} p=(.+)\.git.* +#RewriteRule ^/.*$ http://pkgs.fedoraproject.org/cgit/%1.git/? [R,L,NE] + +# Or else in the root of cgit +#RewriteRule ^.* http://pkgs.fedoraproject.org/cgit/ [R,L,NE] diff --git a/roles/distgit/files/setup_git_package b/roles/distgit/files/setup_git_package new file mode 100644 index 0000000000..73f5d28bc5 --- /dev/null +++ b/roles/distgit/files/setup_git_package @@ -0,0 +1,125 @@ +#!/bin/bash +# +# Create a new repo. +# THIS HAS TO BE RUN ON THE GIT SERVER! + +# WARNING: +# This file is maintained within ansible +# All local changes will be lost. + + +# Figure out the environment we're running in +GITROOT=/srv/git/rpms + +# check if a moron is driving me +if [ ! -d $GITROOT ] ; then + # we're not on the git server (this check is fragile) + echo "ERROR: This script has to be run on the git server." + echo "ERROR: Homer sez 'Duh'." + exit -9 +fi + +# Local variables +VERBOSE=0 +TEST= +IGNORE= +AUTHOR="Fedora Release Engineering " +GIT_SSH_URL="ssh://localhost" + +Usage() { + cat < + + Creates a new repo for + +Options: + -h,--help This help message +EOF +} + +if [ $# -gt 2 ]; then + Usage + exit -1 +fi + +# parse the arguments +while [ -n "$1" ] ; do + case "$1" in + -h | --help ) + Usage + exit 0 + ;; + + * ) + PACKAGE="$1" + ;; + esac + shift +done + +# I hate shell scripting. I'm sure the above is totally wrong + +# check the arguments +if [ -z "$PACKAGE" ] ; then + Usage + exit -1 +fi + +# Sanity checks before we start doing damage +[ $VERBOSE -gt 1 ] && echo "Checking package $PACKAGE..." +if [ -d $GITROOT/$PACKAGE.git ] ; then + echo "ERROR: Package module $PACKAGE already exists!" >&2 + exit -1 +fi + +# Just don't run as root, mmkey? +if [ "$(id -un)" = "root" ] ; then + echo "Please run this script as yourself" + exit -3 +fi + +# "global" permissions check +if [ ! -w $GITROOT ] ; then + echo "ERROR: You can not write to $GITROOT" + echo "ERROR: You can not create repos" + exit -1 +fi + +# Now start working on creating those branches +# Create a tmpdir to do some git work in +TMPDIR=$(mktemp -d /tmp/tmpXXXXXX) + +# First create the master repo +mkdir $GITROOT/$PACKAGE.git +pushd $GITROOT/$PACKAGE.git >/dev/null +git init -q --shared --bare +echo "$PACKAGE" > description # This is used to figure out who to send mail to. +git config --add hooks.mailinglist "$PACKAGE-owner@fedoraproject.org,scm-commits@lists.fedoraproject.org" +git config --add hooks.maildomain fedoraproject.org +popd >/dev/null + +# Now clone that repo and create the .gitignore and sources file +git clone -q /srv/git/rpms/$PACKAGE.git $TMPDIR/$PACKAGE +pushd $TMPDIR/$PACKAGE >/dev/null +touch .gitignore sources +git add . +git commit -q -m 'Initial setup of the repo' --author "$AUTHOR" +git push -q origin master +popd >/dev/null + +# Put our special update hooks in place +ln -s /usr/share/gitolite/hooks/common/update $GITROOT/$PACKAGE.git/hooks/ + +mkdir -p $GITROOT/$PACKAGE.git/hooks/post-receive-chained.d +ln -s /usr/share/git-core/mail-hooks/gnome-post-receive-email \ + $GITROOT/$PACKAGE.git/hooks/post-receive-chained.d/post-receive-email +ln -s /usr/share/git-core/post-receive-fedmsg \ + $GITROOT/$PACKAGE.git/hooks/post-receive-chained.d/post-receive-fedmsg + +# This one kicks off all the others in post-receive-chained.d +ln -s /usr/share/git-core/post-receive-chained \ + $GITROOT/$PACKAGE.git/hooks/post-receive + +rm -rf $TMPDIR +echo "Done." diff --git a/roles/distgit/files/ssl.conf b/roles/distgit/files/ssl.conf new file mode 100644 index 0000000000..e42dd66b2c --- /dev/null +++ b/roles/distgit/files/ssl.conf @@ -0,0 +1,16 @@ +# +# This is the Apache server configuration file providing SSL support. +# It contains the configuration directives to instruct the server how to +# serve pages over an https connection. For detailing information about these +# directives see +# +# For the moment, see for this info. +# The documents are still being prepared from material donated by the +# modssl project. +# +# Do NOT simply read the instructions in here without understanding +# what they do. They're here only as hints or reminders. If you are unsure +# consult the online docs. You have been warned. +# + +LoadModule ssl_module modules/mod_ssl.so diff --git a/roles/distgit/files/updatecrl.sh b/roles/distgit/files/updatecrl.sh new file mode 100644 index 0000000000..c7649b3b9d --- /dev/null +++ b/roles/distgit/files/updatecrl.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +URL=https://admin.fedoraproject.org/ca/crl.pem +OLD=/etc/pki/tls/crl.pem +NEW=/tmp/crl.pem + +wget $URL -O $NEW +OLDUPDATE=`openssl crl -in $OLD -noout -lastupdate` +NEWUPDATE=`openssl crl -in $NEW -noout -lastupdate` + +if [ "$OLDUPDATE" != "$NEWUPDATE" ]; then + mv $NEW $OLD + restorecon $OLD + /etc/init.d/httpd graceful + echo "updated to $NEWUPDATE" +fi diff --git a/roles/distgit/tasks/main.yml b/roles/distgit/tasks/main.yml new file mode 100644 index 0000000000..336439f7b6 --- /dev/null +++ b/roles/distgit/tasks/main.yml @@ -0,0 +1,147 @@ +--- +# tasklist for setting up Dist Git +# +# This is a bit complex, so I'm dividing it into sections. + +# -- Common ---------------------------------------------- +# This is very basic stuff that is needed by multiple of the next sections. +- name: install the needed packages + yum: pkg={{item}} state=present + with_items: + - git + - httpd + - mod_ssl + - python-fedmsg-genacls + +- name: set some sysctl options + sysctl: name=vm.zone_reclaim_mode value=1 state=present + when: env != "staging" + +- name: create the packager group + group: name=packager gid=415 state=present + +- name: install the httpd config directory + copy: src=pkgs.fedoraproject.org.conf dest=/etc/httpd/conf.d/pkgs.fedoraproject.org.conf + file: dest=/etc/httpd/conf.d/pkgs.fedoraproject.org state=directory + notify: + - restart httpd + +- name: install the mod_ssl configuration + copy: src=ssl.conf dest=/etc/httpd/conf.d/ssl.conf + notify: + - restart httpd + +- name: allow httpd to access the files on NFS + seboolean: name=httpd_use_nfs state=yes persistent=yes + +# -- Dist Git -------------------------------------------- +# This is the Git setup itself: group, root directory, scripts,... +# +# Requires: roles/git/hooks +# Requires: roles/git/make_checkout_seed +# Requires: roles/git/server +- name: create the distgit root directory) + file: dest=/srv/git state=directory mode=0755 + file: dest=/srv/git/rpms state=directory mode=2775 group=packager + +- name: install the distgit scripts + copy: src={{item}} dest=/usr/local/bin/{{item}} owner=root group=root mode=0755 + with_items: + - setup_git_package + - mkbranch + - pkgdb2-clone + - pkgdb2branch.py + - process-git-requests + +- name: install the Dist Git-related httpd config + copy: src=git-smart-http.conf dest=/etc/httpd/conf.d/pkgs.fedoraproject.org/git-smart-http.conf + notify: + - restart httpd + + +# -- Gitolite -------------------------------------------- +# This is the permission management for package maintainers, using Gitolite. +# +# Requires: roles/fedmsg/base +# Requires: roles/fedmsg/hub +# Requires: roles/gitolite/base +# Requires: roles/gitolite/check_fedmsg_hooks +- name: mount the lookaside path + mount: > + src=vtap-fedora-nfs01.storage.phx2.redhat.com:/vol/fedora_sourcecache + name=/srv/cache/lookaside + fstype=nfs + opts=rw,hard,bg,intr,noatime,nodev,nosuid,nfsvers=3 + state=mounted + when: env != "staging" + +- name: create the /var/log/gitolite directory + file: path=/var/log/gitolite owner=root group=packager state=directory mode=2775 + +- name: create the gen-acls user + group: name=gen-acls gid=417 state=present + user: name=gen-acls comment="dummy system account for the gen-acls fedmsg job" uid=417 group=gen-acls shell=/bin/bash home=/ + +- name: create the /etc/gitolite/conf directory + file: path=/etc/gitolite/conf owner=gen-acls group=gen-acls state=directory mode=0755 + +- name: create /etc/gitolite/gitolite.rc + copy: src=gitolite.rc dest=/etc/gitolite/gitolite.rc owner=root group=root mode=0755 + +- name: install the gitolite scripts + copy: src={{item}} dest=/usr/local/bin/{{item}} mode=0755 + with_items: + - genacls.pkgdb + - genacls.sh + +- name: install the fedmsg configuration + copy: src=fedmsg-genacls-config.py dest=/etc/fedmsg.d/genacls.py owner=root group=root mode=0644 + + +# -- CGit ------------------------------------------------ +# This is the pretty web view of the repositories, using CGit. +# +# Requires: roles/cgit/base +# Requires: roles/cgit/clean_lock_cron +# Requires: roles/cgit/make_pkgs_list +- name: install the cgitrc file + copy: src=cgitrc dest=/etc/cgitrc + +- name: install the CGit-related httpd config + copy: src=redirect.conf dest=/etc/httpd/conf.d/pkgs.fedoraproject.org/redirect.conf + notify: + - restart httpd + + +# -- Lookaside Cache ------------------------------------- +# This is the annex to Dist Git, where we host source tarballs. +# +# Requires: clamav +- name: install the Lookaside Cache httpd configs + copy: src={{item}} dest=/etc/httpd/conf.d/pkgs.fedoraproject.org/{{item}} + with_items: + - lookaside.conf + - lookaside-upload.conf + notify: + - restart httpd + +- name: create the Lookaside Cache root directory + file: dest=/srv/cache/lookaside/pkgs state=directory + +- name: install the certificates + copy: src={{private}}/fedora-ca.cert dest=/etc/httpd/conf/cacert.pem + copy: src={{private}}/pkgs.fedoraproject.org_key_and_cert.pem dest=/etc/httpd/conf/pkgs.fedoraproject.org_key_and_cert.pem owner=apache mode=0400 + +- name: install, run, and schedule the updatecrl.sh script + copy: src=updatecrl.sh dest=/usr/local/bin/updatecrl.sh owner=root mode=755 + command: /usr/local/bin/updatecrl.sh creates=/etc/pki/tls/crl.pem + cron: > + name="updatecrl" cron_file="ansible-updatecrl" + minute=0 + user=root + job="/usr/local/bin/updatecrl.sh" + +- name: install the upload CGI script + copy: src=dist-git-upload.cgi dest=/srv/web/upload.cgi owner=root group=root mode=0755 + notify: + - restart httpd