From 49c7e53572be8be7848ff99aa5e60ffd4dc316eb Mon Sep 17 00:00:00 2001 From: Lukas Fleischer Date: Tue, 20 Sep 2016 20:53:44 +0200 Subject: Reorganize tests Move and rename the existing git-interface tests such that tests for other scripts can be added easily. In particular, the following changes are made: * Move the existing tests from git-interface/test/ to test/. * Rename t0001-auth.sh to t1100-git-auth.sh. * Rename t0002-serve.sh to t1200-git-serve.sh. * Rename t0003-update.sh to t1300-git-update.sh. Signed-off-by: Lukas Fleischer --- test/setup.sh | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 test/setup.sh (limited to 'test/setup.sh') diff --git a/test/setup.sh b/test/setup.sh new file mode 100644 index 0000000..232b14d --- /dev/null +++ b/test/setup.sh @@ -0,0 +1,195 @@ +TEST_DIRECTORY="$(pwd)" +TOPLEVEL="$(cd .. && pwd)" + +. ./sharness.sh + +# Configure python search path. +PYTHONPATH="$TOPLEVEL" +export PYTHONPATH + +# Configure paths to the Git interface scripts. +GIT_AUTH="$TOPLEVEL/git-interface/git-auth.py" +GIT_SERVE="$TOPLEVEL/git-interface/git-serve.py" +GIT_UPDATE="$TOPLEVEL/git-interface/git-update.py" + +# Create the configuration file and a dummy notification script. +cat >config <<-EOF +[database] +backend = sqlite +name = aur.db + +[options] +enable-maintenance = 0 +maintenance-exceptions = 127.0.0.1 + +[notifications] +notify-cmd = ./notify.sh + +[auth] +valid-keytypes = ssh-rsa ssh-dss ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521 ssh-ed25519 +username-regex = [a-zA-Z0-9]+[.\-_]?[a-zA-Z0-9]+$ +git-serve-cmd = /srv/http/aurweb/git-interface/git-serve.py +ssh-options = restrict + +[serve] +repo-path = ./aur.git/ +repo-regex = [a-z0-9][a-z0-9.+_-]*$ +git-shell-cmd = ./git-shell.sh +git-update-cmd = ./update.sh +ssh-cmdline = ssh aur@aur.archlinux.org + +[update] +max-blob-size = 256000 +EOF + +cat >notify.sh <<-EOF +#!/bin/sh +EOF +chmod +x notify.sh + +cat >git-shell.sh <<-\EOF +#!/bin/sh +echo $AUR_USER +echo $AUR_PKGBASE +echo $GIT_NAMESPACE +EOF +chmod +x git-shell.sh + +cat >update.sh <<-\EOF +#!/bin/sh +echo $AUR_USER +echo $AUR_PKGBASE +EOF +chmod +x update.sh + +AUR_CONFIG=config +export AUR_CONFIG + +# Create SSH public keys which will be used by the test users later. +AUTH_KEYTYPE_USER=ssh-rsa +AUTH_KEYTEXT_USER=AAAAB3NzaC1yc2EAAAADAQABAAABAQCeUafDK4jqUiRHNQfwHcYjBKLZ4Rc1sNUofHApBP6j91nIvDHZe2VUqeBmFUhBz7kXK4VbXD9nlHMun2HeshL8hXnMzymZ8Wk7+IKefj61pajJkIdttw9Tnayfg7uhg5RbFy9zpEjmGjnIVjSzOXKCwppNT+CNujpKM5FD8gso/Z+l3fD+IwrPwS1SzF1Z99nqI9n2FM/JWZqluvTqnW9WdAvBDfutXxp0R5ZiLI5TAKL2Ssp5rpL70pkLXhv+9sK545zKKlXUFmw6Pi2iVBdqdRsk9ocl49dLiNIh8CYDCO3CRQn+8EnpBhTor2TKQxGJI3mzoBwWJJxoKhD/XlYJ +AUTH_FINGERPRINT_USER=SHA256:F/OFtYAy0JCytAGUi4RUZnOsThhQtFMK7fH1YvFBCpo + +AUTH_KEYTYPE_TU=ssh-rsa +AUTH_KEYTEXT_TU=AAAAB3NzaC1yc2EAAAADAQABAAABAQC4Q2Beg6jf2r1LZ4vwT5y10dK8+/c5RaNyTwv77wF2OSLXh32xW0ovhE2lW2gqoakdGsxgM2fTtqMTl29WOsAxlGF7x9XbWhFXFUT88Daq1fAeuihkiRjfBbInSW/WcrFZ+biLBch67addtfkkd4PmAafDeeCtszAXqza+ltBG1oxAGiTXgI3LOhA1/GtLLxsi5sPUO3ZlhvwDn4Sy0aXYx8l9hop/PU4Cjn82hyRa9r+SRxQ3KtjKxcVMnZ8IyXOrBwXTukgSBR/6nSdEmO0JPkYUFuNwh3UGFKuNkrPguL5T+4YDym6czYmZJzQ7NNl2pLKYmYgBwBe5rORlWfN5 +AUTH_FINGERPRINT_TU=SHA256:xQGC6j/U1Q3NDXLl04pm+Shr1mjYUXbGMUzlm9vby4k + +AUTH_KEYTYPE_MISSING=sha-rsa +AUTH_KEYTEXT_MISSING=AAAAB3NzaC1yc2EAAAADAQABAAABAQC9UTpssBunuTBCT3KFtv+yb+cN0VmI2C9O9U7wHlkEZWxNBK8is6tnDHXBxRuvRk0LHILkTidLLFX22ZF0+TFgSz7uuEvGZVNpa2Fn2+vKJJYMvZEvb/f8VHF5/Jddt21VOyu23royTN/duiT7WIZdCtEmq5C9Y43NPfsB8FbUc+FVSYT2Lq7g1/bzvFF+CZxwCrGjC3qC7p3pshICfFR8bbWgRN33ClxIQ7MvkcDtfNu38dLotJqdfEa7NdQgba5/S586f1A4OWKc/mQJFyTaGhRBxw/cBSjqonvO0442VYLHFxlrTHoUunKyOJ8+BJfKgjWmfENC9ESY3mL/IEn5 +AUTH_FINGERPRINT_MISSING=SHA256:uB0B+30r2WA1TDMUmFcaEBjosjnFGzn33XFhiyvTL9w + +# Initialize the test database. +rm -f aur.db +sed \ + -e '/^DROP DATABASE /d' \ + -e '/^CREATE DATABASE /d' \ + -e '/^USE /d' \ + -e 's/ ENGINE = InnoDB//' \ + -e 's/ [A-Z]* UNSIGNED NOT NULL AUTO_INCREMENT/ INTEGER NOT NULL/' \ + -e 's/([0-9, ]*) UNSIGNED / UNSIGNED /' \ + "$TOPLEVEL/schema/aur-schema.sql" | sqlite3 aur.db + +echo "INSERT INTO Users (ID, UserName, Passwd, Email, AccountTypeID) VALUES (1, 'user', '!', 'user@localhost', 1);" | sqlite3 aur.db +echo "INSERT INTO Users (ID, UserName, Passwd, Email, AccountTypeID) VALUES (2, 'tu', '!', 'tu@localhost', 2);" | sqlite3 aur.db +echo "INSERT INTO Users (ID, UserName, Passwd, Email, AccountTypeID) VALUES (3, 'dev', '!', 'dev@localhost', 3);" | sqlite3 aur.db +echo "INSERT INTO Users (ID, UserName, Passwd, Email, AccountTypeID) VALUES (4, 'user2', '!', 'user2@localhost', 1);" | sqlite3 aur.db +echo "INSERT INTO Users (ID, UserName, Passwd, Email, AccountTypeID) VALUES (5, 'user3', '!', 'user3@localhost', 1);" | sqlite3 aur.db +echo "INSERT INTO Users (ID, UserName, Passwd, Email, AccountTypeID) VALUES (6, 'user4', '!', 'user4@localhost', 1);" | sqlite3 aur.db + +echo "INSERT INTO SSHPubKeys (UserID, Fingerprint, PubKey) VALUES (1, '$AUTH_FINGERPRINT_USER', '$AUTH_KEYTYPE_USER $AUTH_KEYTEXT_USER');" | sqlite3 aur.db +echo "INSERT INTO SSHPubKeys (UserID, Fingerprint, PubKey) VALUES (2, '$AUTH_FINGERPRINT_TU', '$AUTH_KEYTYPE_TU $AUTH_KEYTEXT_TU');" | sqlite3 aur.db + +echo "INSERT INTO PackageBlacklist (Name) VALUES ('forbidden');" | sqlite3 aur.db +echo "INSERT INTO OfficialProviders (Name, Repo, Provides) VALUES ('official', 'core', 'official');" | sqlite3 aur.db + +# Initialize a Git repository and test packages. +GIT_AUTHOR_EMAIL=author@example.com +GIT_AUTHOR_NAME='A U Thor' +GIT_COMMITTER_EMAIL=committer@example.com +GIT_COMMITTER_NAME='C O Mitter' +export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME +export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME + +( + mkdir aur.git + cd aur.git + git init -q + + git checkout -q --orphan refs/namespaces/foobar/refs/heads/master + + cat >PKGBUILD <<-EOF + pkgname=foobar + pkgver=1 + pkgrel=1 + pkgdesc='aurweb test package.' + url='https://aur.archlinux.org/' + license=('GPL') + arch=('any') + depends=('python-pygit2') + source=() + md5sums=() + + package() { + echo 'Hello world!' + } + EOF + + cat >.SRCINFO <<-EOF + pkgbase = foobar + pkgdesc = aurweb test package. + pkgver = 1 + pkgrel = 1 + url = https://aur.archlinux.org/ + arch = any + license = GPL + depends = python-pygit2 + + pkgname = foobar + EOF + + git add PKGBUILD .SRCINFO + git commit -q -m 'Initial import' + + sed 's/\(pkgrel.*\)1/\12/' PKGBUILD >PKGBUILD.new + sed 's/\(pkgrel.*\)1/\12/' .SRCINFO >.SRCINFO.new + mv PKGBUILD.new PKGBUILD + mv .SRCINFO.new .SRCINFO + git commit -q -am 'Bump pkgrel' + + git checkout -q --orphan refs/namespaces/foobar2/refs/heads/master + + cat >PKGBUILD <<-EOF + pkgname=foobar2 + pkgver=1 + pkgrel=1 + pkgdesc='aurweb test package.' + url='https://aur.archlinux.org/' + license=('MIT') + arch=('any') + depends=('python-pygit2') + source=() + md5sums=() + + package() { + echo 'Hello world!' + } + EOF + + cat >.SRCINFO <<-EOF + pkgbase = foobar2 + pkgdesc = aurweb test package. + pkgver = 1 + pkgrel = 1 + url = https://aur.archlinux.org/ + arch = any + license = MIT + depends = python-pygit2 + + pkgname = foobar2 + EOF + + git add PKGBUILD .SRCINFO + git commit -q -m 'Initial import' + + git checkout -q refs/namespaces/foobar/refs/heads/master +) -- cgit v1.2.3-54-g00ecf From a48f8ccb131b059e6878efc647a8ebd5f887d7a7 Mon Sep 17 00:00:00 2001 From: Lukas Fleischer Date: Wed, 21 Sep 2016 08:36:50 +0200 Subject: Add tests for mkpkglists Signed-off-by: Lukas Fleischer --- test/setup.sh | 5 +++++ test/t2100-mkpkglists.sh | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100755 test/t2100-mkpkglists.sh (limited to 'test/setup.sh') diff --git a/test/setup.sh b/test/setup.sh index 232b14d..85faba0 100644 --- a/test/setup.sh +++ b/test/setup.sh @@ -11,6 +11,7 @@ export PYTHONPATH GIT_AUTH="$TOPLEVEL/git-interface/git-auth.py" GIT_SERVE="$TOPLEVEL/git-interface/git-serve.py" GIT_UPDATE="$TOPLEVEL/git-interface/git-update.py" +MKPKGLISTS="$TOPLEVEL/scripts/mkpkglists.py" # Create the configuration file and a dummy notification script. cat >config <<-EOF @@ -40,6 +41,10 @@ ssh-cmdline = ssh aur@aur.archlinux.org [update] max-blob-size = 256000 + +[mkpkglists] +packagesfile = packages.gz +pkgbasefile = pkgbase.gz EOF cat >notify.sh <<-EOF diff --git a/test/t2100-mkpkglists.sh b/test/t2100-mkpkglists.sh new file mode 100755 index 0000000..a84a1b6 --- /dev/null +++ b/test/t2100-mkpkglists.sh @@ -0,0 +1,47 @@ +#!/bin/sh + +test_description='mkpkglists tests' + +. ./setup.sh + +test_expect_success 'Test package list generation with no packages.' ' + echo "DELETE FROM Packages;" | sqlite3 aur.db && + echo "DELETE FROM PackageBases;" | sqlite3 aur.db && + "$MKPKGLISTS" && + test $(zcat packages.gz | wc -l) -eq 1 && + test $(zcat pkgbase.gz | wc -l) -eq 1 +' + +test_expect_success 'Test package list generation.' ' + cat <<-EOD | sqlite3 aur.db && + INSERT INTO PackageBases (ID, Name, PackagerUID, SubmittedTS, ModifiedTS) VALUES (1, "foobar", 1, 0, 0); + INSERT INTO PackageBases (ID, Name, PackagerUID, SubmittedTS, ModifiedTS) VALUES (2, "foobar2", 2, 0, 0); + INSERT INTO PackageBases (ID, Name, PackagerUID, SubmittedTS, ModifiedTS) VALUES (3, "foobar3", NULL, 0, 0); + INSERT INTO PackageBases (ID, Name, PackagerUID, SubmittedTS, ModifiedTS) VALUES (4, "foobar4", 1, 0, 0); + INSERT INTO Packages (ID, PackageBaseID, Name) VALUES (1, 1, "pkg1"); + INSERT INTO Packages (ID, PackageBaseID, Name) VALUES (2, 1, "pkg2"); + INSERT INTO Packages (ID, PackageBaseID, Name) VALUES (3, 1, "pkg3"); + INSERT INTO Packages (ID, PackageBaseID, Name) VALUES (4, 2, "pkg4"); + INSERT INTO Packages (ID, PackageBaseID, Name) VALUES (5, 3, "pkg5"); + EOD + "$MKPKGLISTS" && + cat <<-EOD >expected && + foobar + foobar2 + foobar4 + EOD + gunzip pkgbase.gz && + sed "/^#/d" pkgbase >actual && + test_cmp actual expected && + cat <<-EOD >expected && + pkg1 + pkg2 + pkg3 + pkg4 + EOD + gunzip packages.gz && + sed "/^#/d" packages >actual && + test_cmp actual expected +' + +test_done -- cgit v1.2.3-54-g00ecf From cd2d90612b63711f9d92bff686698b6c37b79b2b Mon Sep 17 00:00:00 2001 From: Lukas Fleischer Date: Wed, 21 Sep 2016 08:49:18 +0200 Subject: Add tests for tuvotereminder Signed-off-by: Lukas Fleischer --- test/setup.sh | 4 +++- test/t2200-tuvotereminder.sh | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100755 test/t2200-tuvotereminder.sh (limited to 'test/setup.sh') diff --git a/test/setup.sh b/test/setup.sh index 85faba0..e6a9a16 100644 --- a/test/setup.sh +++ b/test/setup.sh @@ -12,6 +12,7 @@ GIT_AUTH="$TOPLEVEL/git-interface/git-auth.py" GIT_SERVE="$TOPLEVEL/git-interface/git-serve.py" GIT_UPDATE="$TOPLEVEL/git-interface/git-update.py" MKPKGLISTS="$TOPLEVEL/scripts/mkpkglists.py" +TUVOTEREMINDER="$TOPLEVEL/scripts/tuvotereminder.py" # Create the configuration file and a dummy notification script. cat >config <<-EOF @@ -47,8 +48,9 @@ packagesfile = packages.gz pkgbasefile = pkgbase.gz EOF -cat >notify.sh <<-EOF +cat >notify.sh <<-\EOF #!/bin/sh +echo $* >>notify.out EOF chmod +x notify.sh diff --git a/test/t2200-tuvotereminder.sh b/test/t2200-tuvotereminder.sh new file mode 100755 index 0000000..438c0c3 --- /dev/null +++ b/test/t2200-tuvotereminder.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +test_description='tuvotereminder tests' + +. ./setup.sh + +test_expect_success 'Test Trusted User vote reminders.' ' + now=$(date -d now +%s) && + tomorrow=$(date -d tomorrow +%s) && + threedays=$(date -d "3 days" +%s) && + cat <<-EOD | sqlite3 aur.db && + INSERT INTO TU_VoteInfo (ID, Agenda, User, Submitted, End, Quorum, SubmitterID) VALUES (1, "Lorem ipsum.", "user", 0, $now, 0.00, 2); + INSERT INTO TU_VoteInfo (ID, Agenda, User, Submitted, End, Quorum, SubmitterID) VALUES (2, "Lorem ipsum.", "user", 0, $tomorrow, 0.00, 2); + INSERT INTO TU_VoteInfo (ID, Agenda, User, Submitted, End, Quorum, SubmitterID) VALUES (3, "Lorem ipsum.", "user", 0, $tomorrow, 0.00, 2); + INSERT INTO TU_VoteInfo (ID, Agenda, User, Submitted, End, Quorum, SubmitterID) VALUES (4, "Lorem ipsum.", "user", 0, $threedays, 0.00, 2); + EOD + >notify.out && + "$TUVOTEREMINDER" && + cat <<-EOD >expected && + tu-vote-reminder 2 + tu-vote-reminder 3 + EOD + test_cmp notify.out expected +' + +test_done -- cgit v1.2.3-54-g00ecf From d00f4c5197652563a61628461a39fd799264c9c4 Mon Sep 17 00:00:00 2001 From: Lukas Fleischer Date: Wed, 21 Sep 2016 09:13:49 +0200 Subject: Add tests for pkgmaint Signed-off-by: Lukas Fleischer --- test/setup.sh | 1 + test/t2300-pkgmaint.sh | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100755 test/t2300-pkgmaint.sh (limited to 'test/setup.sh') diff --git a/test/setup.sh b/test/setup.sh index e6a9a16..622aab0 100644 --- a/test/setup.sh +++ b/test/setup.sh @@ -13,6 +13,7 @@ GIT_SERVE="$TOPLEVEL/git-interface/git-serve.py" GIT_UPDATE="$TOPLEVEL/git-interface/git-update.py" MKPKGLISTS="$TOPLEVEL/scripts/mkpkglists.py" TUVOTEREMINDER="$TOPLEVEL/scripts/tuvotereminder.py" +PKGMAINT="$TOPLEVEL/scripts/pkgmaint.py" # Create the configuration file and a dummy notification script. cat >config <<-EOF diff --git a/test/t2300-pkgmaint.sh b/test/t2300-pkgmaint.sh new file mode 100755 index 0000000..5c55aaf --- /dev/null +++ b/test/t2300-pkgmaint.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +test_description='pkgmaint tests' + +. ./setup.sh + +test_expect_success 'Test package base cleanup script.' ' + now=$(date -d now +%s) && + threedaysago=$(date -d "3 days ago" +%s) && + cat <<-EOD | sqlite3 aur.db && + INSERT INTO PackageBases (ID, Name, PackagerUID, SubmittedTS, ModifiedTS) VALUES (1, "foobar", 1, $now, 0); + INSERT INTO PackageBases (ID, Name, PackagerUID, SubmittedTS, ModifiedTS) VALUES (2, "foobar2", 2, $threedaysago, 0); + INSERT INTO PackageBases (ID, Name, PackagerUID, SubmittedTS, ModifiedTS) VALUES (3, "foobar3", NULL, $now, 0); + INSERT INTO PackageBases (ID, Name, PackagerUID, SubmittedTS, ModifiedTS) VALUES (4, "foobar4", NULL, $threedaysago, 0); + EOD + "$PKGMAINT" && + cat <<-EOD >expected && + foobar + foobar2 + foobar3 + EOD + echo "SELECT Name FROM PackageBases;" | sqlite3 aur.db >actual && + test_cmp actual expected +' + +test_done -- cgit v1.2.3-54-g00ecf From c8c37477864638ca37088433f0061e5b62f6986f Mon Sep 17 00:00:00 2001 From: Lukas Fleischer Date: Wed, 21 Sep 2016 21:50:28 +0200 Subject: Add tests for aurblup Signed-off-by: Lukas Fleischer --- test/setup.sh | 6 ++++++ test/t2400-aurblup.sh | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100755 test/t2400-aurblup.sh (limited to 'test/setup.sh') diff --git a/test/setup.sh b/test/setup.sh index 622aab0..e379ada 100644 --- a/test/setup.sh +++ b/test/setup.sh @@ -14,6 +14,7 @@ GIT_UPDATE="$TOPLEVEL/git-interface/git-update.py" MKPKGLISTS="$TOPLEVEL/scripts/mkpkglists.py" TUVOTEREMINDER="$TOPLEVEL/scripts/tuvotereminder.py" PKGMAINT="$TOPLEVEL/scripts/pkgmaint.py" +AURBLUP="$TOPLEVEL/scripts/aurblup.py" # Create the configuration file and a dummy notification script. cat >config <<-EOF @@ -44,6 +45,11 @@ ssh-cmdline = ssh aur@aur.archlinux.org [update] max-blob-size = 256000 +[aurblup] +db-path = $(pwd)/sync/ +sync-dbs = test +server = file://$(pwd)/remote/ + [mkpkglists] packagesfile = packages.gz pkgbasefile = pkgbase.gz diff --git a/test/t2400-aurblup.sh b/test/t2400-aurblup.sh new file mode 100755 index 0000000..708281c --- /dev/null +++ b/test/t2400-aurblup.sh @@ -0,0 +1,53 @@ +#!/bin/sh + +test_description='aurblup tests' + +. ./setup.sh + +test_expect_success 'Test official provider update script.' ' + mkdir -p remote/test/foobar-1.0-1 && + cat <<-EOD >remote/test/foobar-1.0-1/desc && + %FILENAME% + foobar-1.0-any.pkg.tar.xz + + %NAME% + foobar + + %VERSION% + 1.0-1 + + %ARCH% + any + EOD + mkdir -p remote/test/foobar2-1.0-1 && + cat <<-EOD >remote/test/foobar2-1.0-1/desc && + %FILENAME% + foobar2-1.0-any.pkg.tar.xz + + %NAME% + foobar2 + + %VERSION% + 1.0-1 + + %ARCH% + any + + %PROVIDES% + foobar3 + foobar4 + EOD + ( cd remote/test && bsdtar -czf ../test.db * ) && + mkdir sync && + "$AURBLUP" && + cat <<-EOD >expected && + foobar|test|foobar + foobar2|test|foobar2 + foobar2|test|foobar3 + foobar2|test|foobar4 + EOD + echo "SELECT Name, Repo, Provides FROM OfficialProviders ORDER BY Provides;" | sqlite3 aur.db >actual && + test_cmp actual expected +' + +test_done -- cgit v1.2.3-54-g00ecf From eb367d97e28d157036c1bf4f1ee65b0d17d756ea Mon Sep 17 00:00:00 2001 From: Lukas Fleischer Date: Thu, 29 Sep 2016 20:45:58 +0200 Subject: Use the notify script in tests Instead of only checking whether the notification script is called with the correct parameters, actually invoke the real notify script and check whether proper notifications are generated. Signed-off-by: Lukas Fleischer --- test/setup.sh | 14 ++++++++++---- test/t2200-tuvotereminder.sh | 11 +++++------ 2 files changed, 15 insertions(+), 10 deletions(-) (limited to 'test/setup.sh') diff --git a/test/setup.sh b/test/setup.sh index e379ada..cd42479 100644 --- a/test/setup.sh +++ b/test/setup.sh @@ -15,6 +15,7 @@ MKPKGLISTS="$TOPLEVEL/scripts/mkpkglists.py" TUVOTEREMINDER="$TOPLEVEL/scripts/tuvotereminder.py" PKGMAINT="$TOPLEVEL/scripts/pkgmaint.py" AURBLUP="$TOPLEVEL/scripts/aurblup.py" +NOTIFY="$TOPLEVEL/scripts/notify.py" # Create the configuration file and a dummy notification script. cat >config <<-EOF @@ -23,11 +24,16 @@ backend = sqlite name = aur.db [options] +aur_location = https://aur.archlinux.org +aur_request_ml = aur-requests@archlinux.org enable-maintenance = 0 maintenance-exceptions = 127.0.0.1 [notifications] -notify-cmd = ./notify.sh +notify-cmd = $NOTIFY +sendmail = ./sendmail.sh +sender = notify@aur.archlinux.org +reply-to = noreply@aur.archlinux.org [auth] valid-keytypes = ssh-rsa ssh-dss ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521 ssh-ed25519 @@ -55,11 +61,11 @@ packagesfile = packages.gz pkgbasefile = pkgbase.gz EOF -cat >notify.sh <<-\EOF +cat >sendmail.sh <<-\EOF #!/bin/sh -echo $* >>notify.out +cat >>sendmail.out EOF -chmod +x notify.sh +chmod +x sendmail.sh cat >git-shell.sh <<-\EOF #!/bin/sh diff --git a/test/t2200-tuvotereminder.sh b/test/t2200-tuvotereminder.sh index 438c0c3..8477c92 100755 --- a/test/t2200-tuvotereminder.sh +++ b/test/t2200-tuvotereminder.sh @@ -14,13 +14,12 @@ test_expect_success 'Test Trusted User vote reminders.' ' INSERT INTO TU_VoteInfo (ID, Agenda, User, Submitted, End, Quorum, SubmitterID) VALUES (3, "Lorem ipsum.", "user", 0, $tomorrow, 0.00, 2); INSERT INTO TU_VoteInfo (ID, Agenda, User, Submitted, End, Quorum, SubmitterID) VALUES (4, "Lorem ipsum.", "user", 0, $threedays, 0.00, 2); EOD - >notify.out && + >sendmail.out && "$TUVOTEREMINDER" && - cat <<-EOD >expected && - tu-vote-reminder 2 - tu-vote-reminder 3 - EOD - test_cmp notify.out expected + grep -q "Proposal 2" sendmail.out && + grep -q "Proposal 3" sendmail.out && + test_must_fail grep -q "Proposal 1" sendmail.out && + test_must_fail grep -q "Proposal 4" sendmail.out ' test_done -- cgit v1.2.3-54-g00ecf From bc3a4f348d8d73821a80a8033c48e7e1eb020e5c Mon Sep 17 00:00:00 2001 From: Lukas Fleischer Date: Thu, 29 Sep 2016 22:04:54 +0200 Subject: t2200: Check that only non-voters get reminders Add a test to make sure that Trusted Users, who already voted on a proposal, do not receive any reminders. Signed-off-by: Lukas Fleischer --- test/setup.sh | 3 +++ test/t2200-tuvotereminder.sh | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) (limited to 'test/setup.sh') diff --git a/test/setup.sh b/test/setup.sh index cd42479..dc9cff2 100644 --- a/test/setup.sh +++ b/test/setup.sh @@ -115,6 +115,9 @@ echo "INSERT INTO Users (ID, UserName, Passwd, Email, AccountTypeID) VALUES (3, echo "INSERT INTO Users (ID, UserName, Passwd, Email, AccountTypeID) VALUES (4, 'user2', '!', 'user2@localhost', 1);" | sqlite3 aur.db echo "INSERT INTO Users (ID, UserName, Passwd, Email, AccountTypeID) VALUES (5, 'user3', '!', 'user3@localhost', 1);" | sqlite3 aur.db echo "INSERT INTO Users (ID, UserName, Passwd, Email, AccountTypeID) VALUES (6, 'user4', '!', 'user4@localhost', 1);" | sqlite3 aur.db +echo "INSERT INTO Users (ID, UserName, Passwd, Email, AccountTypeID) VALUES (7, 'tu2', '!', 'tu2@localhost', 2);" | sqlite3 aur.db +echo "INSERT INTO Users (ID, UserName, Passwd, Email, AccountTypeID) VALUES (8, 'tu3', '!', 'tu3@localhost', 2);" | sqlite3 aur.db +echo "INSERT INTO Users (ID, UserName, Passwd, Email, AccountTypeID) VALUES (9, 'tu4', '!', 'tu4@localhost', 2);" | sqlite3 aur.db echo "INSERT INTO SSHPubKeys (UserID, Fingerprint, PubKey) VALUES (1, '$AUTH_FINGERPRINT_USER', '$AUTH_KEYTYPE_USER $AUTH_KEYTEXT_USER');" | sqlite3 aur.db echo "INSERT INTO SSHPubKeys (UserID, Fingerprint, PubKey) VALUES (2, '$AUTH_FINGERPRINT_TU', '$AUTH_KEYTYPE_TU $AUTH_KEYTEXT_TU');" | sqlite3 aur.db diff --git a/test/t2200-tuvotereminder.sh b/test/t2200-tuvotereminder.sh index 8477c92..c82ce87 100755 --- a/test/t2200-tuvotereminder.sh +++ b/test/t2200-tuvotereminder.sh @@ -22,4 +22,32 @@ test_expect_success 'Test Trusted User vote reminders.' ' test_must_fail grep -q "Proposal 4" sendmail.out ' +test_expect_success 'Check that only TUs who did not vote receive reminders.' ' + cat <<-EOD | sqlite3 aur.db && + INSERT INTO TU_Votes (VoteID, UserID) VALUES (1, 2); + INSERT INTO TU_Votes (VoteID, UserID) VALUES (2, 2); + INSERT INTO TU_Votes (VoteID, UserID) VALUES (3, 2); + INSERT INTO TU_Votes (VoteID, UserID) VALUES (4, 2); + INSERT INTO TU_Votes (VoteID, UserID) VALUES (1, 7); + INSERT INTO TU_Votes (VoteID, UserID) VALUES (3, 7); + INSERT INTO TU_Votes (VoteID, UserID) VALUES (2, 8); + INSERT INTO TU_Votes (VoteID, UserID) VALUES (4, 8); + INSERT INTO TU_Votes (VoteID, UserID) VALUES (1, 9); + EOD + >sendmail.out && + "$TUVOTEREMINDER" && + cat <<-EOD >expected && + Subject: TU Vote Reminder: Proposal 2 + To: tu2@localhost + Subject: TU Vote Reminder: Proposal 2 + To: tu4@localhost + Subject: TU Vote Reminder: Proposal 3 + To: tu3@localhost + Subject: TU Vote Reminder: Proposal 3 + To: tu4@localhost + EOD + grep "^\(Subject\|To\)" sendmail.out >sendmail.parts && + test_cmp sendmail.parts expected +' + test_done -- cgit v1.2.3-54-g00ecf From d4fe77ac572ef0e60c9ffa5f987c9cda448cf9f2 Mon Sep 17 00:00:00 2001 From: Lukas Fleischer Date: Sat, 8 Oct 2016 14:19:11 +0200 Subject: Reorganize Git interface scripts Move the Git interface scripts from git-interface/ to aurweb/git/. Use setuptools to automatically create wrappers which can be installed using `python3 setup.py install`. Update the configuration files, the test suite as well as the INSTALL and README files to reflect these changes. Signed-off-by: Lukas Fleischer --- INSTALL | 28 ++- README | 5 +- aurweb/git/auth.py | 62 +++++++ aurweb/git/serve.py | 409 +++++++++++++++++++++++++++++++++++++++++ aurweb/git/update.py | 419 +++++++++++++++++++++++++++++++++++++++++++ conf/config.proto | 4 +- git-interface/Makefile | 18 -- git-interface/__init__.py | 0 git-interface/config.mk | 1 - git-interface/git-auth.py | 62 ------- git-interface/git-auth.sh.in | 3 - git-interface/git-serve.py | 409 ----------------------------------------- git-interface/git-update.py | 419 ------------------------------------------- setup.py | 7 + test/setup.sh | 8 +- 15 files changed, 916 insertions(+), 938 deletions(-) create mode 100755 aurweb/git/auth.py create mode 100755 aurweb/git/serve.py create mode 100755 aurweb/git/update.py delete mode 100644 git-interface/Makefile delete mode 100644 git-interface/__init__.py delete mode 100644 git-interface/config.mk delete mode 100755 git-interface/git-auth.py delete mode 100644 git-interface/git-auth.sh.in delete mode 100755 git-interface/git-serve.py delete mode 100755 git-interface/git-update.py (limited to 'test/setup.sh') diff --git a/INSTALL b/INSTALL index dab48cc..395915a 100644 --- a/INSTALL +++ b/INSTALL @@ -37,11 +37,16 @@ Setup on Arch Linux $ mysql -uaur -p AUR 1 else '0', + } + key = keytype + ' ' + keytext + + print(format_command(env_vars, git_serve_cmd, ssh_opts, key)) + + +if __name__ == '__main__': + main() diff --git a/aurweb/git/serve.py b/aurweb/git/serve.py new file mode 100755 index 0000000..ebfef94 --- /dev/null +++ b/aurweb/git/serve.py @@ -0,0 +1,409 @@ +#!/usr/bin/python3 + +import os +import re +import shlex +import subprocess +import sys +import time + +import aurweb.config +import aurweb.db + +notify_cmd = aurweb.config.get('notifications', 'notify-cmd') + +repo_path = aurweb.config.get('serve', 'repo-path') +repo_regex = aurweb.config.get('serve', 'repo-regex') +git_shell_cmd = aurweb.config.get('serve', 'git-shell-cmd') +git_update_cmd = aurweb.config.get('serve', 'git-update-cmd') +ssh_cmdline = aurweb.config.get('serve', 'ssh-cmdline') + +enable_maintenance = aurweb.config.getboolean('options', 'enable-maintenance') +maintenance_exc = aurweb.config.get('options', 'maintenance-exceptions').split() + + +def pkgbase_from_name(pkgbase): + conn = aurweb.db.Connection() + cur = conn.execute("SELECT ID FROM PackageBases WHERE Name = ?", [pkgbase]) + + row = cur.fetchone() + return row[0] if row else None + + +def pkgbase_exists(pkgbase): + return pkgbase_from_name(pkgbase) is not None + + +def list_repos(user): + conn = aurweb.db.Connection() + + cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", [user]) + userid = cur.fetchone()[0] + if userid == 0: + die('{:s}: unknown user: {:s}'.format(action, user)) + + cur = conn.execute("SELECT Name, PackagerUID FROM PackageBases " + + "WHERE MaintainerUID = ?", [userid]) + for row in cur: + print((' ' if row[1] else '*') + row[0]) + conn.close() + + +def create_pkgbase(pkgbase, user): + if not re.match(repo_regex, pkgbase): + die('{:s}: invalid repository name: {:s}'.format(action, pkgbase)) + if pkgbase_exists(pkgbase): + die('{:s}: package base already exists: {:s}'.format(action, pkgbase)) + + conn = aurweb.db.Connection() + + cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", [user]) + userid = cur.fetchone()[0] + if userid == 0: + die('{:s}: unknown user: {:s}'.format(action, user)) + + now = int(time.time()) + cur = conn.execute("INSERT INTO PackageBases (Name, SubmittedTS, " + + "ModifiedTS, SubmitterUID, MaintainerUID) VALUES " + + "(?, ?, ?, ?, ?)", [pkgbase, now, now, userid, userid]) + pkgbase_id = cur.lastrowid + + cur = conn.execute("INSERT INTO PackageNotifications " + + "(PackageBaseID, UserID) VALUES (?, ?)", + [pkgbase_id, userid]) + + conn.commit() + conn.close() + + +def pkgbase_adopt(pkgbase, user, privileged): + pkgbase_id = pkgbase_from_name(pkgbase) + if not pkgbase_id: + die('{:s}: package base not found: {:s}'.format(action, pkgbase)) + + conn = aurweb.db.Connection() + + cur = conn.execute("SELECT ID FROM PackageBases WHERE ID = ? AND " + + "MaintainerUID IS NULL", [pkgbase_id]) + if not privileged and not cur.fetchone(): + die('{:s}: permission denied: {:s}'.format(action, user)) + + cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", [user]) + userid = cur.fetchone()[0] + if userid == 0: + die('{:s}: unknown user: {:s}'.format(action, user)) + + cur = conn.execute("UPDATE PackageBases SET MaintainerUID = ? " + + "WHERE ID = ?", [userid, pkgbase_id]) + + cur = conn.execute("SELECT COUNT(*) FROM PackageNotifications WHERE " + + "PackageBaseID = ? AND UserID = ?", + [pkgbase_id, userid]) + if cur.fetchone()[0] == 0: + cur = conn.execute("INSERT INTO PackageNotifications " + + "(PackageBaseID, UserID) VALUES (?, ?)", + [pkgbase_id, userid]) + conn.commit() + + subprocess.Popen((notify_cmd, 'adopt', str(pkgbase_id), str(userid))) + + conn.close() + + +def pkgbase_get_comaintainers(pkgbase): + conn = aurweb.db.Connection() + + cur = conn.execute("SELECT UserName FROM PackageComaintainers " + + "INNER JOIN Users " + + "ON Users.ID = PackageComaintainers.UsersID " + + "INNER JOIN PackageBases " + + "ON PackageBases.ID = PackageComaintainers.PackageBaseID " + + "WHERE PackageBases.Name = ? " + + "ORDER BY Priority ASC", [pkgbase]) + + return [row[0] for row in cur.fetchall()] + + +def pkgbase_set_comaintainers(pkgbase, userlist, user, privileged): + pkgbase_id = pkgbase_from_name(pkgbase) + if not pkgbase_id: + die('{:s}: package base not found: {:s}'.format(action, pkgbase)) + + if not privileged and not pkgbase_has_full_access(pkgbase, user): + die('{:s}: permission denied: {:s}'.format(action, user)) + + conn = aurweb.db.Connection() + + userlist_old = set(pkgbase_get_comaintainers(pkgbase)) + + uids_old = set() + for olduser in userlist_old: + cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", + [olduser]) + userid = cur.fetchone()[0] + if userid == 0: + die('{:s}: unknown user: {:s}'.format(action, user)) + uids_old.add(userid) + + uids_new = set() + for newuser in userlist: + cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", + [newuser]) + userid = cur.fetchone()[0] + if userid == 0: + die('{:s}: unknown user: {:s}'.format(action, user)) + uids_new.add(userid) + + uids_add = uids_new - uids_old + uids_rem = uids_old - uids_new + + i = 1 + for userid in uids_new: + if userid in uids_add: + cur = conn.execute("INSERT INTO PackageComaintainers " + + "(PackageBaseID, UsersID, Priority) " + + "VALUES (?, ?, ?)", [pkgbase_id, userid, i]) + subprocess.Popen((notify_cmd, 'comaintainer-add', str(pkgbase_id), + str(userid))) + else: + cur = conn.execute("UPDATE PackageComaintainers " + + "SET Priority = ? " + + "WHERE PackageBaseID = ? AND UsersID = ?", + [i, pkgbase_id, userid]) + i += 1 + + for userid in uids_rem: + cur = conn.execute("DELETE FROM PackageComaintainers " + + "WHERE PackageBaseID = ? AND UsersID = ?", + [pkgbase_id, userid]) + subprocess.Popen((notify_cmd, 'comaintainer-remove', + str(pkgbase_id), str(userid))) + + conn.commit() + conn.close() + + +def pkgbase_disown(pkgbase, user, privileged): + pkgbase_id = pkgbase_from_name(pkgbase) + if not pkgbase_id: + die('{:s}: package base not found: {:s}'.format(action, pkgbase)) + + initialized_by_owner = pkgbase_has_full_access(pkgbase, user) + if not privileged and not initialized_by_owner: + die('{:s}: permission denied: {:s}'.format(action, user)) + + # TODO: Support disowning package bases via package request. + # TODO: Scan through pending orphan requests and close them. + + comaintainers = [] + new_maintainer_userid = None + + conn = aurweb.db.Connection() + + # Make the first co-maintainer the new maintainer, unless the action was + # enforced by a Trusted User. + if initialized_by_owner: + comaintainers = pkgbase_get_comaintainers(pkgbase) + if len(comaintainers) > 0: + new_maintainer = comaintainers[0] + cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", + [new_maintainer]) + new_maintainer_userid = cur.fetchone()[0] + comaintainers.remove(new_maintainer) + + pkgbase_set_comaintainers(pkgbase, comaintainers, user, privileged) + cur = conn.execute("UPDATE PackageBases SET MaintainerUID = ? " + + "WHERE ID = ?", [new_maintainer_userid, pkgbase_id]) + + conn.commit() + + cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", [user]) + userid = cur.fetchone()[0] + if userid == 0: + die('{:s}: unknown user: {:s}'.format(action, user)) + + subprocess.Popen((notify_cmd, 'disown', str(pkgbase_id), str(userid))) + + conn.close() + + +def pkgbase_set_keywords(pkgbase, keywords): + pkgbase_id = pkgbase_from_name(pkgbase) + if not pkgbase_id: + die('{:s}: package base not found: {:s}'.format(action, pkgbase)) + + conn = aurweb.db.Connection() + + conn.execute("DELETE FROM PackageKeywords WHERE PackageBaseID = ?", + [pkgbase_id]) + for keyword in keywords: + conn.execute("INSERT INTO PackageKeywords (PackageBaseID, Keyword) " + + "VALUES (?, ?)", [pkgbase_id, keyword]) + + conn.commit() + conn.close() + + +def pkgbase_has_write_access(pkgbase, user): + conn = aurweb.db.Connection() + + cur = conn.execute("SELECT COUNT(*) FROM PackageBases " + + "LEFT JOIN PackageComaintainers " + + "ON PackageComaintainers.PackageBaseID = PackageBases.ID " + + "INNER JOIN Users " + + "ON Users.ID = PackageBases.MaintainerUID " + + "OR PackageBases.MaintainerUID IS NULL " + + "OR Users.ID = PackageComaintainers.UsersID " + + "WHERE Name = ? AND Username = ?", [pkgbase, user]) + return cur.fetchone()[0] > 0 + + +def pkgbase_has_full_access(pkgbase, user): + conn = aurweb.db.Connection() + + cur = conn.execute("SELECT COUNT(*) FROM PackageBases " + + "INNER JOIN Users " + + "ON Users.ID = PackageBases.MaintainerUID " + + "WHERE Name = ? AND Username = ?", [pkgbase, user]) + return cur.fetchone()[0] > 0 + + +def die(msg): + sys.stderr.write("{:s}\n".format(msg)) + exit(1) + + +def die_with_help(msg): + die(msg + "\nTry `{:s} help` for a list of commands.".format(ssh_cmdline)) + + +def warn(msg): + sys.stderr.write("warning: {:s}\n".format(msg)) + + +def usage(cmds): + sys.stderr.write("Commands:\n") + colwidth = max([len(cmd) for cmd in cmds.keys()]) + 4 + for key in sorted(cmds): + sys.stderr.write(" " + key.ljust(colwidth) + cmds[key] + "\n") + exit(0) + + +def main(): + user = os.environ.get('AUR_USER') + privileged = (os.environ.get('AUR_PRIVILEGED', '0') == '1') + ssh_cmd = os.environ.get('SSH_ORIGINAL_COMMAND') + ssh_client = os.environ.get('SSH_CLIENT') + + if not ssh_cmd: + die_with_help("Interactive shell is disabled.") + cmdargv = shlex.split(ssh_cmd) + action = cmdargv[0] + remote_addr = ssh_client.split(' ')[0] if ssh_client else None + + if enable_maintenance: + if remote_addr not in maintenance_exc: + die("The AUR is down due to maintenance. We will be back soon.") + + if action == 'git' and cmdargv[1] in ('upload-pack', 'receive-pack'): + action = action + '-' + cmdargv[1] + del cmdargv[1] + + if action == 'git-upload-pack' or action == 'git-receive-pack': + if len(cmdargv) < 2: + die_with_help("{:s}: missing path".format(action)) + + path = cmdargv[1].rstrip('/') + if not path.startswith('/'): + path = '/' + path + if not path.endswith('.git'): + path = path + '.git' + pkgbase = path[1:-4] + if not re.match(repo_regex, pkgbase): + die('{:s}: invalid repository name: {:s}'.format(action, pkgbase)) + + if action == 'git-receive-pack' and pkgbase_exists(pkgbase): + if not privileged and not pkgbase_has_write_access(pkgbase, user): + die('{:s}: permission denied: {:s}'.format(action, user)) + + os.environ["AUR_USER"] = user + os.environ["AUR_PKGBASE"] = pkgbase + os.environ["GIT_NAMESPACE"] = pkgbase + cmd = action + " '" + repo_path + "'" + os.execl(git_shell_cmd, git_shell_cmd, '-c', cmd) + elif action == 'set-keywords': + if len(cmdargv) < 2: + die_with_help("{:s}: missing repository name".format(action)) + pkgbase_set_keywords(cmdargv[1], cmdargv[2:]) + elif action == 'list-repos': + if len(cmdargv) > 1: + die_with_help("{:s}: too many arguments".format(action)) + list_repos(user) + elif action == 'setup-repo': + if len(cmdargv) < 2: + die_with_help("{:s}: missing repository name".format(action)) + if len(cmdargv) > 2: + die_with_help("{:s}: too many arguments".format(action)) + warn('{:s} is deprecated. ' + 'Use `git push` to create new repositories.'.format(action)) + create_pkgbase(cmdargv[1], user) + elif action == 'restore': + if len(cmdargv) < 2: + die_with_help("{:s}: missing repository name".format(action)) + if len(cmdargv) > 2: + die_with_help("{:s}: too many arguments".format(action)) + + pkgbase = cmdargv[1] + if not re.match(repo_regex, pkgbase): + die('{:s}: invalid repository name: {:s}'.format(action, pkgbase)) + + if pkgbase_exists(pkgbase): + die('{:s}: package base exists: {:s}'.format(action, pkgbase)) + create_pkgbase(pkgbase, user) + + os.environ["AUR_USER"] = user + os.environ["AUR_PKGBASE"] = pkgbase + os.execl(git_update_cmd, git_update_cmd, 'restore') + elif action == 'adopt': + if len(cmdargv) < 2: + die_with_help("{:s}: missing repository name".format(action)) + if len(cmdargv) > 2: + die_with_help("{:s}: too many arguments".format(action)) + + pkgbase = cmdargv[1] + pkgbase_adopt(pkgbase, user, privileged) + elif action == 'disown': + if len(cmdargv) < 2: + die_with_help("{:s}: missing repository name".format(action)) + if len(cmdargv) > 2: + die_with_help("{:s}: too many arguments".format(action)) + + pkgbase = cmdargv[1] + pkgbase_disown(pkgbase, user, privileged) + elif action == 'set-comaintainers': + if len(cmdargv) < 2: + die_with_help("{:s}: missing repository name".format(action)) + + pkgbase = cmdargv[1] + userlist = cmdargv[2:] + pkgbase_set_comaintainers(pkgbase, userlist, user, privileged) + elif action == 'help': + cmds = { + "adopt ": "Adopt a package base.", + "disown ": "Disown a package base.", + "help": "Show this help message and exit.", + "list-repos": "List all your repositories.", + "restore ": "Restore a deleted package base.", + "set-comaintainers [...]": "Set package base co-maintainers.", + "set-keywords [...]": "Change package base keywords.", + "setup-repo ": "Create a repository (deprecated).", + "git-receive-pack": "Internal command used with Git.", + "git-upload-pack": "Internal command used with Git.", + } + usage(cmds) + else: + die_with_help("invalid command: {:s}".format(action)) + + +if __name__ == '__main__': + main() diff --git a/aurweb/git/update.py b/aurweb/git/update.py new file mode 100755 index 0000000..7337341 --- /dev/null +++ b/aurweb/git/update.py @@ -0,0 +1,419 @@ +#!/usr/bin/python3 + +import os +import pygit2 +import re +import subprocess +import sys +import time + +import srcinfo.parse +import srcinfo.utils + +import aurweb.config +import aurweb.db + +notify_cmd = aurweb.config.get('notifications', 'notify-cmd') + +repo_path = aurweb.config.get('serve', 'repo-path') +repo_regex = aurweb.config.get('serve', 'repo-regex') + +max_blob_size = aurweb.config.getint('update', 'max-blob-size') + + +def size_humanize(num): + for unit in ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB']: + if abs(num) < 2048.0: + if isinstance(num, int): + return "{}{}".format(num, unit) + else: + return "{:.2f}{}".format(num, unit) + num /= 1024.0 + return "{:.2f}{}".format(num, 'YiB') + + +def extract_arch_fields(pkginfo, field): + values = [] + + if field in pkginfo: + for val in pkginfo[field]: + values.append({"value": val, "arch": None}) + + for arch in ['i686', 'x86_64']: + if field + '_' + arch in pkginfo: + for val in pkginfo[field + '_' + arch]: + values.append({"value": val, "arch": arch}) + + return values + + +def parse_dep(depstring): + dep, _, desc = depstring.partition(': ') + depname = re.sub(r'(<|=|>).*', '', dep) + depcond = dep[len(depname):] + + if (desc): + return (depname + ': ' + desc, depcond) + else: + return (depname, depcond) + + +def create_pkgbase(conn, pkgbase, user): + cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", [user]) + userid = cur.fetchone()[0] + + now = int(time.time()) + cur = conn.execute("INSERT INTO PackageBases (Name, SubmittedTS, " + + "ModifiedTS, SubmitterUID, MaintainerUID) VALUES " + + "(?, ?, ?, ?, ?)", [pkgbase, now, now, userid, userid]) + pkgbase_id = cur.lastrowid + + cur = conn.execute("INSERT INTO PackageNotifications " + + "(PackageBaseID, UserID) VALUES (?, ?)", + [pkgbase_id, userid]) + + conn.commit() + + return pkgbase_id + + +def save_metadata(metadata, conn, user): + # Obtain package base ID and previous maintainer. + pkgbase = metadata['pkgbase'] + cur = conn.execute("SELECT ID, MaintainerUID FROM PackageBases " + "WHERE Name = ?", [pkgbase]) + (pkgbase_id, maintainer_uid) = cur.fetchone() + was_orphan = not maintainer_uid + + # Obtain the user ID of the new maintainer. + cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", [user]) + user_id = int(cur.fetchone()[0]) + + # Update package base details and delete current packages. + now = int(time.time()) + conn.execute("UPDATE PackageBases SET ModifiedTS = ?, " + + "PackagerUID = ?, OutOfDateTS = NULL WHERE ID = ?", + [now, user_id, pkgbase_id]) + conn.execute("UPDATE PackageBases SET MaintainerUID = ? " + + "WHERE ID = ? AND MaintainerUID IS NULL", + [user_id, pkgbase_id]) + for table in ('Sources', 'Depends', 'Relations', 'Licenses', 'Groups'): + conn.execute("DELETE FROM Package" + table + " WHERE EXISTS (" + + "SELECT * FROM Packages " + + "WHERE Packages.PackageBaseID = ? AND " + + "Package" + table + ".PackageID = Packages.ID)", + [pkgbase_id]) + conn.execute("DELETE FROM Packages WHERE PackageBaseID = ?", [pkgbase_id]) + + for pkgname in srcinfo.utils.get_package_names(metadata): + pkginfo = srcinfo.utils.get_merged_package(pkgname, metadata) + + if 'epoch' in pkginfo and int(pkginfo['epoch']) > 0: + ver = '{:d}:{:s}-{:s}'.format(int(pkginfo['epoch']), + pkginfo['pkgver'], + pkginfo['pkgrel']) + else: + ver = '{:s}-{:s}'.format(pkginfo['pkgver'], pkginfo['pkgrel']) + + for field in ('pkgdesc', 'url'): + if field not in pkginfo: + pkginfo[field] = None + + # Create a new package. + cur = conn.execute("INSERT INTO Packages (PackageBaseID, Name, " + + "Version, Description, URL) " + + "VALUES (?, ?, ?, ?, ?)", + [pkgbase_id, pkginfo['pkgname'], ver, + pkginfo['pkgdesc'], pkginfo['url']]) + conn.commit() + pkgid = cur.lastrowid + + # Add package sources. + for source_info in extract_arch_fields(pkginfo, 'source'): + conn.execute("INSERT INTO PackageSources (PackageID, Source, " + + "SourceArch) VALUES (?, ?, ?)", + [pkgid, source_info['value'], source_info['arch']]) + + # Add package dependencies. + for deptype in ('depends', 'makedepends', + 'checkdepends', 'optdepends'): + cur = conn.execute("SELECT ID FROM DependencyTypes WHERE Name = ?", + [deptype]) + deptypeid = cur.fetchone()[0] + for dep_info in extract_arch_fields(pkginfo, deptype): + depname, depcond = parse_dep(dep_info['value']) + deparch = dep_info['arch'] + conn.execute("INSERT INTO PackageDepends (PackageID, " + + "DepTypeID, DepName, DepCondition, DepArch) " + + "VALUES (?, ?, ?, ?, ?)", + [pkgid, deptypeid, depname, depcond, deparch]) + + # Add package relations (conflicts, provides, replaces). + for reltype in ('conflicts', 'provides', 'replaces'): + cur = conn.execute("SELECT ID FROM RelationTypes WHERE Name = ?", + [reltype]) + reltypeid = cur.fetchone()[0] + for rel_info in extract_arch_fields(pkginfo, reltype): + relname, relcond = parse_dep(rel_info['value']) + relarch = rel_info['arch'] + conn.execute("INSERT INTO PackageRelations (PackageID, " + + "RelTypeID, RelName, RelCondition, RelArch) " + + "VALUES (?, ?, ?, ?, ?)", + [pkgid, reltypeid, relname, relcond, relarch]) + + # Add package licenses. + if 'license' in pkginfo: + for license in pkginfo['license']: + cur = conn.execute("SELECT ID FROM Licenses WHERE Name = ?", + [license]) + row = cur.fetchone() + if row: + licenseid = row[0] + else: + cur = conn.execute("INSERT INTO Licenses (Name) " + + "VALUES (?)", [license]) + conn.commit() + licenseid = cur.lastrowid + conn.execute("INSERT INTO PackageLicenses (PackageID, " + + "LicenseID) VALUES (?, ?)", + [pkgid, licenseid]) + + # Add package groups. + if 'groups' in pkginfo: + for group in pkginfo['groups']: + cur = conn.execute("SELECT ID FROM Groups WHERE Name = ?", + [group]) + row = cur.fetchone() + if row: + groupid = row[0] + else: + cur = conn.execute("INSERT INTO Groups (Name) VALUES (?)", + [group]) + conn.commit() + groupid = cur.lastrowid + conn.execute("INSERT INTO PackageGroups (PackageID, " + "GroupID) VALUES (?, ?)", [pkgid, groupid]) + + # Add user to notification list on adoption. + if was_orphan: + cur = conn.execute("SELECT COUNT(*) FROM PackageNotifications WHERE " + + "PackageBaseID = ? AND UserID = ?", + [pkgbase_id, user_id]) + if cur.fetchone()[0] == 0: + conn.execute("INSERT INTO PackageNotifications " + + "(PackageBaseID, UserID) VALUES (?, ?)", + [pkgbase_id, user_id]) + + conn.commit() + + +def update_notify(conn, user, pkgbase_id): + # Obtain the user ID of the new maintainer. + cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", [user]) + user_id = int(cur.fetchone()[0]) + + # Execute the notification script. + subprocess.Popen((notify_cmd, 'update', str(user_id), str(pkgbase_id))) + + +def die(msg): + sys.stderr.write("error: {:s}\n".format(msg)) + exit(1) + + +def warn(msg): + sys.stderr.write("warning: {:s}\n".format(msg)) + + +def die_commit(msg, commit): + sys.stderr.write("error: The following error " + + "occurred when parsing commit\n") + sys.stderr.write("error: {:s}:\n".format(commit)) + sys.stderr.write("error: {:s}\n".format(msg)) + exit(1) + + +def main(): + repo = pygit2.Repository(repo_path) + + user = os.environ.get("AUR_USER") + pkgbase = os.environ.get("AUR_PKGBASE") + privileged = (os.environ.get("AUR_PRIVILEGED", '0') == '1') + warn_or_die = warn if privileged else die + + if len(sys.argv) == 2 and sys.argv[1] == "restore": + if 'refs/heads/' + pkgbase not in repo.listall_references(): + die('{:s}: repository not found: {:s}'.format(sys.argv[1], + pkgbase)) + refname = "refs/heads/master" + branchref = 'refs/heads/' + pkgbase + sha1_old = sha1_new = repo.lookup_reference(branchref).target + elif len(sys.argv) == 4: + refname, sha1_old, sha1_new = sys.argv[1:4] + else: + die("invalid arguments") + + if refname != "refs/heads/master": + die("pushing to a branch other than master is restricted") + + conn = aurweb.db.Connection() + + # Detect and deny non-fast-forwards. + if sha1_old != "0" * 40 and not privileged: + walker = repo.walk(sha1_old, pygit2.GIT_SORT_TOPOLOGICAL) + walker.hide(sha1_new) + if next(walker, None) is not None: + die("denying non-fast-forward (you should pull first)") + + # Prepare the walker that validates new commits. + walker = repo.walk(sha1_new, pygit2.GIT_SORT_TOPOLOGICAL) + if sha1_old != "0" * 40: + walker.hide(sha1_old) + + # Validate all new commits. + for commit in walker: + for fname in ('.SRCINFO', 'PKGBUILD'): + if fname not in commit.tree: + die_commit("missing {:s}".format(fname), str(commit.id)) + + for treeobj in commit.tree: + blob = repo[treeobj.id] + + if isinstance(blob, pygit2.Tree): + die_commit("the repository must not contain subdirectories", + str(commit.id)) + + if not isinstance(blob, pygit2.Blob): + die_commit("not a blob object: {:s}".format(treeobj), + str(commit.id)) + + if blob.size > max_blob_size: + die_commit("maximum blob size ({:s}) exceeded".format( + size_humanize(max_blob_size)), str(commit.id)) + + metadata_raw = repo[commit.tree['.SRCINFO'].id].data.decode() + (metadata, errors) = srcinfo.parse.parse_srcinfo(metadata_raw) + if errors: + sys.stderr.write("error: The following errors occurred " + "when parsing .SRCINFO in commit\n") + sys.stderr.write("error: {:s}:\n".format(str(commit.id))) + for error in errors: + for err in error['error']: + sys.stderr.write("error: line {:d}: {:s}\n".format( + error['line'], err)) + exit(1) + + metadata_pkgbase = metadata['pkgbase'] + if not re.match(repo_regex, metadata_pkgbase): + die_commit('invalid pkgbase: {:s}'.format(metadata_pkgbase), + str(commit.id)) + + for pkgname in set(metadata['packages'].keys()): + pkginfo = srcinfo.utils.get_merged_package(pkgname, metadata) + + for field in ('pkgver', 'pkgrel', 'pkgname'): + if field not in pkginfo: + die_commit('missing mandatory field: {:s}'.format(field), + str(commit.id)) + + if 'epoch' in pkginfo and not pkginfo['epoch'].isdigit(): + die_commit('invalid epoch: {:s}'.format(pkginfo['epoch']), + str(commit.id)) + + if not re.match(r'[a-z0-9][a-z0-9\.+_-]*$', pkginfo['pkgname']): + die_commit('invalid package name: {:s}'.format( + pkginfo['pkgname']), str(commit.id)) + + for field in ('pkgname', 'pkgdesc', 'url'): + if field in pkginfo and len(pkginfo[field]) > 255: + die_commit('{:s} field too long: {:s}'.format(field, + pkginfo[field]), str(commit.id)) + + for field in ('install', 'changelog'): + if field in pkginfo and not pkginfo[field] in commit.tree: + die_commit('missing {:s} file: {:s}'.format(field, + pkginfo[field]), str(commit.id)) + + for field in extract_arch_fields(pkginfo, 'source'): + fname = field['value'] + if "://" in fname or "lp:" in fname: + continue + if fname not in commit.tree: + die_commit('missing source file: {:s}'.format(fname), + str(commit.id)) + + # Display a warning if .SRCINFO is unchanged. + if sha1_old not in ("0000000000000000000000000000000000000000", sha1_new): + srcinfo_id_old = repo[sha1_old].tree['.SRCINFO'].id + srcinfo_id_new = repo[sha1_new].tree['.SRCINFO'].id + if srcinfo_id_old == srcinfo_id_new: + warn(".SRCINFO unchanged. " + "The package database will not be updated!") + + # Read .SRCINFO from the HEAD commit. + metadata_raw = repo[repo[sha1_new].tree['.SRCINFO'].id].data.decode() + (metadata, errors) = srcinfo.parse.parse_srcinfo(metadata_raw) + + # Ensure that the package base name matches the repository name. + metadata_pkgbase = metadata['pkgbase'] + if metadata_pkgbase != pkgbase: + die('invalid pkgbase: {:s}, expected {:s}'.format(metadata_pkgbase, + pkgbase)) + + # Ensure that packages are neither blacklisted nor overwritten. + pkgbase = metadata['pkgbase'] + cur = conn.execute("SELECT ID FROM PackageBases WHERE Name = ?", [pkgbase]) + row = cur.fetchone() + pkgbase_id = row[0] if row else 0 + + cur = conn.execute("SELECT Name FROM PackageBlacklist") + blacklist = [row[0] for row in cur.fetchall()] + + cur = conn.execute("SELECT Name, Repo FROM OfficialProviders") + providers = dict(cur.fetchall()) + + for pkgname in srcinfo.utils.get_package_names(metadata): + pkginfo = srcinfo.utils.get_merged_package(pkgname, metadata) + pkgname = pkginfo['pkgname'] + + if pkgname in blacklist: + warn_or_die('package is blacklisted: {:s}'.format(pkgname)) + if pkgname in providers: + warn_or_die('package already provided by [{:s}]: {:s}'.format( + providers[pkgname], pkgname)) + + cur = conn.execute("SELECT COUNT(*) FROM Packages WHERE Name = ? " + + "AND PackageBaseID <> ?", [pkgname, pkgbase_id]) + if cur.fetchone()[0] > 0: + die('cannot overwrite package: {:s}'.format(pkgname)) + + # Create a new package base if it does not exist yet. + if pkgbase_id == 0: + pkgbase_id = create_pkgbase(conn, pkgbase, user) + + # Store package base details in the database. + save_metadata(metadata, conn, user) + + # Create (or update) a branch with the name of the package base for better + # accessibility. + branchref = 'refs/heads/' + pkgbase + repo.create_reference(branchref, sha1_new, True) + + # Work around a Git bug: The HEAD ref is not updated when using + # gitnamespaces. This can be removed once the bug fix is included in Git + # mainline. See + # http://git.661346.n2.nabble.com/PATCH-receive-pack-Create-a-HEAD-ref-for-ref-namespace-td7632149.html + # for details. + headref = 'refs/namespaces/' + pkgbase + '/HEAD' + repo.create_reference(headref, sha1_new, True) + + # Send package update notifications. + update_notify(conn, user, pkgbase_id) + + # Close the database. + cur.close() + conn.close() + + +if __name__ == '__main__': + main() diff --git a/conf/config.proto b/conf/config.proto index 21441a9..96fad80 100644 --- a/conf/config.proto +++ b/conf/config.proto @@ -46,14 +46,14 @@ RSA = SHA256:Ju+yWiMb/2O+gKQ9RJCDqvRg7l+Q95KFAeqM5sr6l2s [auth] valid-keytypes = ssh-rsa ssh-dss ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521 ssh-ed25519 username-regex = [a-zA-Z0-9]+[.\-_]?[a-zA-Z0-9]+$ -git-serve-cmd = /srv/http/aurweb/git-interface/git-serve.py +git-serve-cmd = /usr/local/bin/aurweb-git-serve ssh-options = restrict [serve] repo-path = /srv/http/aurweb/aur.git/ repo-regex = [a-z0-9][a-z0-9.+_-]*$ git-shell-cmd = /usr/bin/git-shell -git-update-cmd = /srv/http/aurweb/git-interface/git-update.py +git-update-cmd = /usr/local/bin/aurweb-git-update ssh-cmdline = ssh aur@aur.archlinux.org [update] diff --git a/git-interface/Makefile b/git-interface/Makefile deleted file mode 100644 index 8865790..0000000 --- a/git-interface/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -GIT_INTERFACE_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) - -include config.mk - -git-auth.sh: - sed 's#%GIT_INTERFACE_DIR%#$(GIT_INTERFACE_DIR)#' git-auth.sh - chmod +x git-auth.sh - -install: git-auth.sh - install -Dm0755 git-auth.sh "$(DESTDIR)$(PREFIX)/bin/aur-git-auth" - -uninstall: - rm -f "$(DESTDIR)$(PREFIX)/bin/aur-git-auth" - -clean: - rm -f git-auth.sh - -.PHONY: install uninstall clean diff --git a/git-interface/__init__.py b/git-interface/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/git-interface/config.mk b/git-interface/config.mk deleted file mode 100644 index 4d794a1..0000000 --- a/git-interface/config.mk +++ /dev/null @@ -1 +0,0 @@ -PREFIX = /usr/local diff --git a/git-interface/git-auth.py b/git-interface/git-auth.py deleted file mode 100755 index 022b0ff..0000000 --- a/git-interface/git-auth.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/python3 - -import shlex -import re -import sys - -import aurweb.config -import aurweb.db - - -def format_command(env_vars, command, ssh_opts, ssh_key): - environment = '' - for key, var in env_vars.items(): - environment += '{}={} '.format(key, shlex.quote(var)) - - command = shlex.quote(command) - command = '{}{}'.format(environment, command) - - # The command is being substituted into an authorized_keys line below, - # so we need to escape the double quotes. - command = command.replace('"', '\\"') - msg = 'command="{}",{} {}'.format(command, ssh_opts, ssh_key) - return msg - - -def main(): - valid_keytypes = aurweb.config.get('auth', 'valid-keytypes').split() - username_regex = aurweb.config.get('auth', 'username-regex') - git_serve_cmd = aurweb.config.get('auth', 'git-serve-cmd') - ssh_opts = aurweb.config.get('auth', 'ssh-options') - - keytype = sys.argv[1] - keytext = sys.argv[2] - if keytype not in valid_keytypes: - exit(1) - - conn = aurweb.db.Connection() - - cur = conn.execute("SELECT Users.Username, Users.AccountTypeID FROM Users " - "INNER JOIN SSHPubKeys ON SSHPubKeys.UserID = Users.ID " - "WHERE SSHPubKeys.PubKey = ? AND Users.Suspended = 0", - (keytype + " " + keytext,)) - - row = cur.fetchone() - if not row or cur.fetchone(): - exit(1) - - user, account_type = row - if not re.match(username_regex, user): - exit(1) - - env_vars = { - 'AUR_USER': user, - 'AUR_PRIVILEGED': '1' if account_type > 1 else '0', - } - key = keytype + ' ' + keytext - - print(format_command(env_vars, git_serve_cmd, ssh_opts, key)) - - -if __name__ == '__main__': - main() diff --git a/git-interface/git-auth.sh.in b/git-interface/git-auth.sh.in deleted file mode 100644 index 223816a..0000000 --- a/git-interface/git-auth.sh.in +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -%GIT_INTERFACE_DIR%/git-auth.py "$1" "$2" diff --git a/git-interface/git-serve.py b/git-interface/git-serve.py deleted file mode 100755 index ebfef94..0000000 --- a/git-interface/git-serve.py +++ /dev/null @@ -1,409 +0,0 @@ -#!/usr/bin/python3 - -import os -import re -import shlex -import subprocess -import sys -import time - -import aurweb.config -import aurweb.db - -notify_cmd = aurweb.config.get('notifications', 'notify-cmd') - -repo_path = aurweb.config.get('serve', 'repo-path') -repo_regex = aurweb.config.get('serve', 'repo-regex') -git_shell_cmd = aurweb.config.get('serve', 'git-shell-cmd') -git_update_cmd = aurweb.config.get('serve', 'git-update-cmd') -ssh_cmdline = aurweb.config.get('serve', 'ssh-cmdline') - -enable_maintenance = aurweb.config.getboolean('options', 'enable-maintenance') -maintenance_exc = aurweb.config.get('options', 'maintenance-exceptions').split() - - -def pkgbase_from_name(pkgbase): - conn = aurweb.db.Connection() - cur = conn.execute("SELECT ID FROM PackageBases WHERE Name = ?", [pkgbase]) - - row = cur.fetchone() - return row[0] if row else None - - -def pkgbase_exists(pkgbase): - return pkgbase_from_name(pkgbase) is not None - - -def list_repos(user): - conn = aurweb.db.Connection() - - cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", [user]) - userid = cur.fetchone()[0] - if userid == 0: - die('{:s}: unknown user: {:s}'.format(action, user)) - - cur = conn.execute("SELECT Name, PackagerUID FROM PackageBases " + - "WHERE MaintainerUID = ?", [userid]) - for row in cur: - print((' ' if row[1] else '*') + row[0]) - conn.close() - - -def create_pkgbase(pkgbase, user): - if not re.match(repo_regex, pkgbase): - die('{:s}: invalid repository name: {:s}'.format(action, pkgbase)) - if pkgbase_exists(pkgbase): - die('{:s}: package base already exists: {:s}'.format(action, pkgbase)) - - conn = aurweb.db.Connection() - - cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", [user]) - userid = cur.fetchone()[0] - if userid == 0: - die('{:s}: unknown user: {:s}'.format(action, user)) - - now = int(time.time()) - cur = conn.execute("INSERT INTO PackageBases (Name, SubmittedTS, " + - "ModifiedTS, SubmitterUID, MaintainerUID) VALUES " + - "(?, ?, ?, ?, ?)", [pkgbase, now, now, userid, userid]) - pkgbase_id = cur.lastrowid - - cur = conn.execute("INSERT INTO PackageNotifications " + - "(PackageBaseID, UserID) VALUES (?, ?)", - [pkgbase_id, userid]) - - conn.commit() - conn.close() - - -def pkgbase_adopt(pkgbase, user, privileged): - pkgbase_id = pkgbase_from_name(pkgbase) - if not pkgbase_id: - die('{:s}: package base not found: {:s}'.format(action, pkgbase)) - - conn = aurweb.db.Connection() - - cur = conn.execute("SELECT ID FROM PackageBases WHERE ID = ? AND " + - "MaintainerUID IS NULL", [pkgbase_id]) - if not privileged and not cur.fetchone(): - die('{:s}: permission denied: {:s}'.format(action, user)) - - cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", [user]) - userid = cur.fetchone()[0] - if userid == 0: - die('{:s}: unknown user: {:s}'.format(action, user)) - - cur = conn.execute("UPDATE PackageBases SET MaintainerUID = ? " + - "WHERE ID = ?", [userid, pkgbase_id]) - - cur = conn.execute("SELECT COUNT(*) FROM PackageNotifications WHERE " + - "PackageBaseID = ? AND UserID = ?", - [pkgbase_id, userid]) - if cur.fetchone()[0] == 0: - cur = conn.execute("INSERT INTO PackageNotifications " + - "(PackageBaseID, UserID) VALUES (?, ?)", - [pkgbase_id, userid]) - conn.commit() - - subprocess.Popen((notify_cmd, 'adopt', str(pkgbase_id), str(userid))) - - conn.close() - - -def pkgbase_get_comaintainers(pkgbase): - conn = aurweb.db.Connection() - - cur = conn.execute("SELECT UserName FROM PackageComaintainers " + - "INNER JOIN Users " + - "ON Users.ID = PackageComaintainers.UsersID " + - "INNER JOIN PackageBases " + - "ON PackageBases.ID = PackageComaintainers.PackageBaseID " + - "WHERE PackageBases.Name = ? " + - "ORDER BY Priority ASC", [pkgbase]) - - return [row[0] for row in cur.fetchall()] - - -def pkgbase_set_comaintainers(pkgbase, userlist, user, privileged): - pkgbase_id = pkgbase_from_name(pkgbase) - if not pkgbase_id: - die('{:s}: package base not found: {:s}'.format(action, pkgbase)) - - if not privileged and not pkgbase_has_full_access(pkgbase, user): - die('{:s}: permission denied: {:s}'.format(action, user)) - - conn = aurweb.db.Connection() - - userlist_old = set(pkgbase_get_comaintainers(pkgbase)) - - uids_old = set() - for olduser in userlist_old: - cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", - [olduser]) - userid = cur.fetchone()[0] - if userid == 0: - die('{:s}: unknown user: {:s}'.format(action, user)) - uids_old.add(userid) - - uids_new = set() - for newuser in userlist: - cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", - [newuser]) - userid = cur.fetchone()[0] - if userid == 0: - die('{:s}: unknown user: {:s}'.format(action, user)) - uids_new.add(userid) - - uids_add = uids_new - uids_old - uids_rem = uids_old - uids_new - - i = 1 - for userid in uids_new: - if userid in uids_add: - cur = conn.execute("INSERT INTO PackageComaintainers " + - "(PackageBaseID, UsersID, Priority) " + - "VALUES (?, ?, ?)", [pkgbase_id, userid, i]) - subprocess.Popen((notify_cmd, 'comaintainer-add', str(pkgbase_id), - str(userid))) - else: - cur = conn.execute("UPDATE PackageComaintainers " + - "SET Priority = ? " + - "WHERE PackageBaseID = ? AND UsersID = ?", - [i, pkgbase_id, userid]) - i += 1 - - for userid in uids_rem: - cur = conn.execute("DELETE FROM PackageComaintainers " + - "WHERE PackageBaseID = ? AND UsersID = ?", - [pkgbase_id, userid]) - subprocess.Popen((notify_cmd, 'comaintainer-remove', - str(pkgbase_id), str(userid))) - - conn.commit() - conn.close() - - -def pkgbase_disown(pkgbase, user, privileged): - pkgbase_id = pkgbase_from_name(pkgbase) - if not pkgbase_id: - die('{:s}: package base not found: {:s}'.format(action, pkgbase)) - - initialized_by_owner = pkgbase_has_full_access(pkgbase, user) - if not privileged and not initialized_by_owner: - die('{:s}: permission denied: {:s}'.format(action, user)) - - # TODO: Support disowning package bases via package request. - # TODO: Scan through pending orphan requests and close them. - - comaintainers = [] - new_maintainer_userid = None - - conn = aurweb.db.Connection() - - # Make the first co-maintainer the new maintainer, unless the action was - # enforced by a Trusted User. - if initialized_by_owner: - comaintainers = pkgbase_get_comaintainers(pkgbase) - if len(comaintainers) > 0: - new_maintainer = comaintainers[0] - cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", - [new_maintainer]) - new_maintainer_userid = cur.fetchone()[0] - comaintainers.remove(new_maintainer) - - pkgbase_set_comaintainers(pkgbase, comaintainers, user, privileged) - cur = conn.execute("UPDATE PackageBases SET MaintainerUID = ? " + - "WHERE ID = ?", [new_maintainer_userid, pkgbase_id]) - - conn.commit() - - cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", [user]) - userid = cur.fetchone()[0] - if userid == 0: - die('{:s}: unknown user: {:s}'.format(action, user)) - - subprocess.Popen((notify_cmd, 'disown', str(pkgbase_id), str(userid))) - - conn.close() - - -def pkgbase_set_keywords(pkgbase, keywords): - pkgbase_id = pkgbase_from_name(pkgbase) - if not pkgbase_id: - die('{:s}: package base not found: {:s}'.format(action, pkgbase)) - - conn = aurweb.db.Connection() - - conn.execute("DELETE FROM PackageKeywords WHERE PackageBaseID = ?", - [pkgbase_id]) - for keyword in keywords: - conn.execute("INSERT INTO PackageKeywords (PackageBaseID, Keyword) " + - "VALUES (?, ?)", [pkgbase_id, keyword]) - - conn.commit() - conn.close() - - -def pkgbase_has_write_access(pkgbase, user): - conn = aurweb.db.Connection() - - cur = conn.execute("SELECT COUNT(*) FROM PackageBases " + - "LEFT JOIN PackageComaintainers " + - "ON PackageComaintainers.PackageBaseID = PackageBases.ID " + - "INNER JOIN Users " + - "ON Users.ID = PackageBases.MaintainerUID " + - "OR PackageBases.MaintainerUID IS NULL " + - "OR Users.ID = PackageComaintainers.UsersID " + - "WHERE Name = ? AND Username = ?", [pkgbase, user]) - return cur.fetchone()[0] > 0 - - -def pkgbase_has_full_access(pkgbase, user): - conn = aurweb.db.Connection() - - cur = conn.execute("SELECT COUNT(*) FROM PackageBases " + - "INNER JOIN Users " + - "ON Users.ID = PackageBases.MaintainerUID " + - "WHERE Name = ? AND Username = ?", [pkgbase, user]) - return cur.fetchone()[0] > 0 - - -def die(msg): - sys.stderr.write("{:s}\n".format(msg)) - exit(1) - - -def die_with_help(msg): - die(msg + "\nTry `{:s} help` for a list of commands.".format(ssh_cmdline)) - - -def warn(msg): - sys.stderr.write("warning: {:s}\n".format(msg)) - - -def usage(cmds): - sys.stderr.write("Commands:\n") - colwidth = max([len(cmd) for cmd in cmds.keys()]) + 4 - for key in sorted(cmds): - sys.stderr.write(" " + key.ljust(colwidth) + cmds[key] + "\n") - exit(0) - - -def main(): - user = os.environ.get('AUR_USER') - privileged = (os.environ.get('AUR_PRIVILEGED', '0') == '1') - ssh_cmd = os.environ.get('SSH_ORIGINAL_COMMAND') - ssh_client = os.environ.get('SSH_CLIENT') - - if not ssh_cmd: - die_with_help("Interactive shell is disabled.") - cmdargv = shlex.split(ssh_cmd) - action = cmdargv[0] - remote_addr = ssh_client.split(' ')[0] if ssh_client else None - - if enable_maintenance: - if remote_addr not in maintenance_exc: - die("The AUR is down due to maintenance. We will be back soon.") - - if action == 'git' and cmdargv[1] in ('upload-pack', 'receive-pack'): - action = action + '-' + cmdargv[1] - del cmdargv[1] - - if action == 'git-upload-pack' or action == 'git-receive-pack': - if len(cmdargv) < 2: - die_with_help("{:s}: missing path".format(action)) - - path = cmdargv[1].rstrip('/') - if not path.startswith('/'): - path = '/' + path - if not path.endswith('.git'): - path = path + '.git' - pkgbase = path[1:-4] - if not re.match(repo_regex, pkgbase): - die('{:s}: invalid repository name: {:s}'.format(action, pkgbase)) - - if action == 'git-receive-pack' and pkgbase_exists(pkgbase): - if not privileged and not pkgbase_has_write_access(pkgbase, user): - die('{:s}: permission denied: {:s}'.format(action, user)) - - os.environ["AUR_USER"] = user - os.environ["AUR_PKGBASE"] = pkgbase - os.environ["GIT_NAMESPACE"] = pkgbase - cmd = action + " '" + repo_path + "'" - os.execl(git_shell_cmd, git_shell_cmd, '-c', cmd) - elif action == 'set-keywords': - if len(cmdargv) < 2: - die_with_help("{:s}: missing repository name".format(action)) - pkgbase_set_keywords(cmdargv[1], cmdargv[2:]) - elif action == 'list-repos': - if len(cmdargv) > 1: - die_with_help("{:s}: too many arguments".format(action)) - list_repos(user) - elif action == 'setup-repo': - if len(cmdargv) < 2: - die_with_help("{:s}: missing repository name".format(action)) - if len(cmdargv) > 2: - die_with_help("{:s}: too many arguments".format(action)) - warn('{:s} is deprecated. ' - 'Use `git push` to create new repositories.'.format(action)) - create_pkgbase(cmdargv[1], user) - elif action == 'restore': - if len(cmdargv) < 2: - die_with_help("{:s}: missing repository name".format(action)) - if len(cmdargv) > 2: - die_with_help("{:s}: too many arguments".format(action)) - - pkgbase = cmdargv[1] - if not re.match(repo_regex, pkgbase): - die('{:s}: invalid repository name: {:s}'.format(action, pkgbase)) - - if pkgbase_exists(pkgbase): - die('{:s}: package base exists: {:s}'.format(action, pkgbase)) - create_pkgbase(pkgbase, user) - - os.environ["AUR_USER"] = user - os.environ["AUR_PKGBASE"] = pkgbase - os.execl(git_update_cmd, git_update_cmd, 'restore') - elif action == 'adopt': - if len(cmdargv) < 2: - die_with_help("{:s}: missing repository name".format(action)) - if len(cmdargv) > 2: - die_with_help("{:s}: too many arguments".format(action)) - - pkgbase = cmdargv[1] - pkgbase_adopt(pkgbase, user, privileged) - elif action == 'disown': - if len(cmdargv) < 2: - die_with_help("{:s}: missing repository name".format(action)) - if len(cmdargv) > 2: - die_with_help("{:s}: too many arguments".format(action)) - - pkgbase = cmdargv[1] - pkgbase_disown(pkgbase, user, privileged) - elif action == 'set-comaintainers': - if len(cmdargv) < 2: - die_with_help("{:s}: missing repository name".format(action)) - - pkgbase = cmdargv[1] - userlist = cmdargv[2:] - pkgbase_set_comaintainers(pkgbase, userlist, user, privileged) - elif action == 'help': - cmds = { - "adopt ": "Adopt a package base.", - "disown ": "Disown a package base.", - "help": "Show this help message and exit.", - "list-repos": "List all your repositories.", - "restore ": "Restore a deleted package base.", - "set-comaintainers [...]": "Set package base co-maintainers.", - "set-keywords [...]": "Change package base keywords.", - "setup-repo ": "Create a repository (deprecated).", - "git-receive-pack": "Internal command used with Git.", - "git-upload-pack": "Internal command used with Git.", - } - usage(cmds) - else: - die_with_help("invalid command: {:s}".format(action)) - - -if __name__ == '__main__': - main() diff --git a/git-interface/git-update.py b/git-interface/git-update.py deleted file mode 100755 index 7337341..0000000 --- a/git-interface/git-update.py +++ /dev/null @@ -1,419 +0,0 @@ -#!/usr/bin/python3 - -import os -import pygit2 -import re -import subprocess -import sys -import time - -import srcinfo.parse -import srcinfo.utils - -import aurweb.config -import aurweb.db - -notify_cmd = aurweb.config.get('notifications', 'notify-cmd') - -repo_path = aurweb.config.get('serve', 'repo-path') -repo_regex = aurweb.config.get('serve', 'repo-regex') - -max_blob_size = aurweb.config.getint('update', 'max-blob-size') - - -def size_humanize(num): - for unit in ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB']: - if abs(num) < 2048.0: - if isinstance(num, int): - return "{}{}".format(num, unit) - else: - return "{:.2f}{}".format(num, unit) - num /= 1024.0 - return "{:.2f}{}".format(num, 'YiB') - - -def extract_arch_fields(pkginfo, field): - values = [] - - if field in pkginfo: - for val in pkginfo[field]: - values.append({"value": val, "arch": None}) - - for arch in ['i686', 'x86_64']: - if field + '_' + arch in pkginfo: - for val in pkginfo[field + '_' + arch]: - values.append({"value": val, "arch": arch}) - - return values - - -def parse_dep(depstring): - dep, _, desc = depstring.partition(': ') - depname = re.sub(r'(<|=|>).*', '', dep) - depcond = dep[len(depname):] - - if (desc): - return (depname + ': ' + desc, depcond) - else: - return (depname, depcond) - - -def create_pkgbase(conn, pkgbase, user): - cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", [user]) - userid = cur.fetchone()[0] - - now = int(time.time()) - cur = conn.execute("INSERT INTO PackageBases (Name, SubmittedTS, " + - "ModifiedTS, SubmitterUID, MaintainerUID) VALUES " + - "(?, ?, ?, ?, ?)", [pkgbase, now, now, userid, userid]) - pkgbase_id = cur.lastrowid - - cur = conn.execute("INSERT INTO PackageNotifications " + - "(PackageBaseID, UserID) VALUES (?, ?)", - [pkgbase_id, userid]) - - conn.commit() - - return pkgbase_id - - -def save_metadata(metadata, conn, user): - # Obtain package base ID and previous maintainer. - pkgbase = metadata['pkgbase'] - cur = conn.execute("SELECT ID, MaintainerUID FROM PackageBases " - "WHERE Name = ?", [pkgbase]) - (pkgbase_id, maintainer_uid) = cur.fetchone() - was_orphan = not maintainer_uid - - # Obtain the user ID of the new maintainer. - cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", [user]) - user_id = int(cur.fetchone()[0]) - - # Update package base details and delete current packages. - now = int(time.time()) - conn.execute("UPDATE PackageBases SET ModifiedTS = ?, " + - "PackagerUID = ?, OutOfDateTS = NULL WHERE ID = ?", - [now, user_id, pkgbase_id]) - conn.execute("UPDATE PackageBases SET MaintainerUID = ? " + - "WHERE ID = ? AND MaintainerUID IS NULL", - [user_id, pkgbase_id]) - for table in ('Sources', 'Depends', 'Relations', 'Licenses', 'Groups'): - conn.execute("DELETE FROM Package" + table + " WHERE EXISTS (" + - "SELECT * FROM Packages " + - "WHERE Packages.PackageBaseID = ? AND " + - "Package" + table + ".PackageID = Packages.ID)", - [pkgbase_id]) - conn.execute("DELETE FROM Packages WHERE PackageBaseID = ?", [pkgbase_id]) - - for pkgname in srcinfo.utils.get_package_names(metadata): - pkginfo = srcinfo.utils.get_merged_package(pkgname, metadata) - - if 'epoch' in pkginfo and int(pkginfo['epoch']) > 0: - ver = '{:d}:{:s}-{:s}'.format(int(pkginfo['epoch']), - pkginfo['pkgver'], - pkginfo['pkgrel']) - else: - ver = '{:s}-{:s}'.format(pkginfo['pkgver'], pkginfo['pkgrel']) - - for field in ('pkgdesc', 'url'): - if field not in pkginfo: - pkginfo[field] = None - - # Create a new package. - cur = conn.execute("INSERT INTO Packages (PackageBaseID, Name, " + - "Version, Description, URL) " + - "VALUES (?, ?, ?, ?, ?)", - [pkgbase_id, pkginfo['pkgname'], ver, - pkginfo['pkgdesc'], pkginfo['url']]) - conn.commit() - pkgid = cur.lastrowid - - # Add package sources. - for source_info in extract_arch_fields(pkginfo, 'source'): - conn.execute("INSERT INTO PackageSources (PackageID, Source, " + - "SourceArch) VALUES (?, ?, ?)", - [pkgid, source_info['value'], source_info['arch']]) - - # Add package dependencies. - for deptype in ('depends', 'makedepends', - 'checkdepends', 'optdepends'): - cur = conn.execute("SELECT ID FROM DependencyTypes WHERE Name = ?", - [deptype]) - deptypeid = cur.fetchone()[0] - for dep_info in extract_arch_fields(pkginfo, deptype): - depname, depcond = parse_dep(dep_info['value']) - deparch = dep_info['arch'] - conn.execute("INSERT INTO PackageDepends (PackageID, " + - "DepTypeID, DepName, DepCondition, DepArch) " + - "VALUES (?, ?, ?, ?, ?)", - [pkgid, deptypeid, depname, depcond, deparch]) - - # Add package relations (conflicts, provides, replaces). - for reltype in ('conflicts', 'provides', 'replaces'): - cur = conn.execute("SELECT ID FROM RelationTypes WHERE Name = ?", - [reltype]) - reltypeid = cur.fetchone()[0] - for rel_info in extract_arch_fields(pkginfo, reltype): - relname, relcond = parse_dep(rel_info['value']) - relarch = rel_info['arch'] - conn.execute("INSERT INTO PackageRelations (PackageID, " + - "RelTypeID, RelName, RelCondition, RelArch) " + - "VALUES (?, ?, ?, ?, ?)", - [pkgid, reltypeid, relname, relcond, relarch]) - - # Add package licenses. - if 'license' in pkginfo: - for license in pkginfo['license']: - cur = conn.execute("SELECT ID FROM Licenses WHERE Name = ?", - [license]) - row = cur.fetchone() - if row: - licenseid = row[0] - else: - cur = conn.execute("INSERT INTO Licenses (Name) " + - "VALUES (?)", [license]) - conn.commit() - licenseid = cur.lastrowid - conn.execute("INSERT INTO PackageLicenses (PackageID, " + - "LicenseID) VALUES (?, ?)", - [pkgid, licenseid]) - - # Add package groups. - if 'groups' in pkginfo: - for group in pkginfo['groups']: - cur = conn.execute("SELECT ID FROM Groups WHERE Name = ?", - [group]) - row = cur.fetchone() - if row: - groupid = row[0] - else: - cur = conn.execute("INSERT INTO Groups (Name) VALUES (?)", - [group]) - conn.commit() - groupid = cur.lastrowid - conn.execute("INSERT INTO PackageGroups (PackageID, " - "GroupID) VALUES (?, ?)", [pkgid, groupid]) - - # Add user to notification list on adoption. - if was_orphan: - cur = conn.execute("SELECT COUNT(*) FROM PackageNotifications WHERE " + - "PackageBaseID = ? AND UserID = ?", - [pkgbase_id, user_id]) - if cur.fetchone()[0] == 0: - conn.execute("INSERT INTO PackageNotifications " + - "(PackageBaseID, UserID) VALUES (?, ?)", - [pkgbase_id, user_id]) - - conn.commit() - - -def update_notify(conn, user, pkgbase_id): - # Obtain the user ID of the new maintainer. - cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", [user]) - user_id = int(cur.fetchone()[0]) - - # Execute the notification script. - subprocess.Popen((notify_cmd, 'update', str(user_id), str(pkgbase_id))) - - -def die(msg): - sys.stderr.write("error: {:s}\n".format(msg)) - exit(1) - - -def warn(msg): - sys.stderr.write("warning: {:s}\n".format(msg)) - - -def die_commit(msg, commit): - sys.stderr.write("error: The following error " + - "occurred when parsing commit\n") - sys.stderr.write("error: {:s}:\n".format(commit)) - sys.stderr.write("error: {:s}\n".format(msg)) - exit(1) - - -def main(): - repo = pygit2.Repository(repo_path) - - user = os.environ.get("AUR_USER") - pkgbase = os.environ.get("AUR_PKGBASE") - privileged = (os.environ.get("AUR_PRIVILEGED", '0') == '1') - warn_or_die = warn if privileged else die - - if len(sys.argv) == 2 and sys.argv[1] == "restore": - if 'refs/heads/' + pkgbase not in repo.listall_references(): - die('{:s}: repository not found: {:s}'.format(sys.argv[1], - pkgbase)) - refname = "refs/heads/master" - branchref = 'refs/heads/' + pkgbase - sha1_old = sha1_new = repo.lookup_reference(branchref).target - elif len(sys.argv) == 4: - refname, sha1_old, sha1_new = sys.argv[1:4] - else: - die("invalid arguments") - - if refname != "refs/heads/master": - die("pushing to a branch other than master is restricted") - - conn = aurweb.db.Connection() - - # Detect and deny non-fast-forwards. - if sha1_old != "0" * 40 and not privileged: - walker = repo.walk(sha1_old, pygit2.GIT_SORT_TOPOLOGICAL) - walker.hide(sha1_new) - if next(walker, None) is not None: - die("denying non-fast-forward (you should pull first)") - - # Prepare the walker that validates new commits. - walker = repo.walk(sha1_new, pygit2.GIT_SORT_TOPOLOGICAL) - if sha1_old != "0" * 40: - walker.hide(sha1_old) - - # Validate all new commits. - for commit in walker: - for fname in ('.SRCINFO', 'PKGBUILD'): - if fname not in commit.tree: - die_commit("missing {:s}".format(fname), str(commit.id)) - - for treeobj in commit.tree: - blob = repo[treeobj.id] - - if isinstance(blob, pygit2.Tree): - die_commit("the repository must not contain subdirectories", - str(commit.id)) - - if not isinstance(blob, pygit2.Blob): - die_commit("not a blob object: {:s}".format(treeobj), - str(commit.id)) - - if blob.size > max_blob_size: - die_commit("maximum blob size ({:s}) exceeded".format( - size_humanize(max_blob_size)), str(commit.id)) - - metadata_raw = repo[commit.tree['.SRCINFO'].id].data.decode() - (metadata, errors) = srcinfo.parse.parse_srcinfo(metadata_raw) - if errors: - sys.stderr.write("error: The following errors occurred " - "when parsing .SRCINFO in commit\n") - sys.stderr.write("error: {:s}:\n".format(str(commit.id))) - for error in errors: - for err in error['error']: - sys.stderr.write("error: line {:d}: {:s}\n".format( - error['line'], err)) - exit(1) - - metadata_pkgbase = metadata['pkgbase'] - if not re.match(repo_regex, metadata_pkgbase): - die_commit('invalid pkgbase: {:s}'.format(metadata_pkgbase), - str(commit.id)) - - for pkgname in set(metadata['packages'].keys()): - pkginfo = srcinfo.utils.get_merged_package(pkgname, metadata) - - for field in ('pkgver', 'pkgrel', 'pkgname'): - if field not in pkginfo: - die_commit('missing mandatory field: {:s}'.format(field), - str(commit.id)) - - if 'epoch' in pkginfo and not pkginfo['epoch'].isdigit(): - die_commit('invalid epoch: {:s}'.format(pkginfo['epoch']), - str(commit.id)) - - if not re.match(r'[a-z0-9][a-z0-9\.+_-]*$', pkginfo['pkgname']): - die_commit('invalid package name: {:s}'.format( - pkginfo['pkgname']), str(commit.id)) - - for field in ('pkgname', 'pkgdesc', 'url'): - if field in pkginfo and len(pkginfo[field]) > 255: - die_commit('{:s} field too long: {:s}'.format(field, - pkginfo[field]), str(commit.id)) - - for field in ('install', 'changelog'): - if field in pkginfo and not pkginfo[field] in commit.tree: - die_commit('missing {:s} file: {:s}'.format(field, - pkginfo[field]), str(commit.id)) - - for field in extract_arch_fields(pkginfo, 'source'): - fname = field['value'] - if "://" in fname or "lp:" in fname: - continue - if fname not in commit.tree: - die_commit('missing source file: {:s}'.format(fname), - str(commit.id)) - - # Display a warning if .SRCINFO is unchanged. - if sha1_old not in ("0000000000000000000000000000000000000000", sha1_new): - srcinfo_id_old = repo[sha1_old].tree['.SRCINFO'].id - srcinfo_id_new = repo[sha1_new].tree['.SRCINFO'].id - if srcinfo_id_old == srcinfo_id_new: - warn(".SRCINFO unchanged. " - "The package database will not be updated!") - - # Read .SRCINFO from the HEAD commit. - metadata_raw = repo[repo[sha1_new].tree['.SRCINFO'].id].data.decode() - (metadata, errors) = srcinfo.parse.parse_srcinfo(metadata_raw) - - # Ensure that the package base name matches the repository name. - metadata_pkgbase = metadata['pkgbase'] - if metadata_pkgbase != pkgbase: - die('invalid pkgbase: {:s}, expected {:s}'.format(metadata_pkgbase, - pkgbase)) - - # Ensure that packages are neither blacklisted nor overwritten. - pkgbase = metadata['pkgbase'] - cur = conn.execute("SELECT ID FROM PackageBases WHERE Name = ?", [pkgbase]) - row = cur.fetchone() - pkgbase_id = row[0] if row else 0 - - cur = conn.execute("SELECT Name FROM PackageBlacklist") - blacklist = [row[0] for row in cur.fetchall()] - - cur = conn.execute("SELECT Name, Repo FROM OfficialProviders") - providers = dict(cur.fetchall()) - - for pkgname in srcinfo.utils.get_package_names(metadata): - pkginfo = srcinfo.utils.get_merged_package(pkgname, metadata) - pkgname = pkginfo['pkgname'] - - if pkgname in blacklist: - warn_or_die('package is blacklisted: {:s}'.format(pkgname)) - if pkgname in providers: - warn_or_die('package already provided by [{:s}]: {:s}'.format( - providers[pkgname], pkgname)) - - cur = conn.execute("SELECT COUNT(*) FROM Packages WHERE Name = ? " + - "AND PackageBaseID <> ?", [pkgname, pkgbase_id]) - if cur.fetchone()[0] > 0: - die('cannot overwrite package: {:s}'.format(pkgname)) - - # Create a new package base if it does not exist yet. - if pkgbase_id == 0: - pkgbase_id = create_pkgbase(conn, pkgbase, user) - - # Store package base details in the database. - save_metadata(metadata, conn, user) - - # Create (or update) a branch with the name of the package base for better - # accessibility. - branchref = 'refs/heads/' + pkgbase - repo.create_reference(branchref, sha1_new, True) - - # Work around a Git bug: The HEAD ref is not updated when using - # gitnamespaces. This can be removed once the bug fix is included in Git - # mainline. See - # http://git.661346.n2.nabble.com/PATCH-receive-pack-Create-a-HEAD-ref-for-ref-namespace-td7632149.html - # for details. - headref = 'refs/namespaces/' + pkgbase + '/HEAD' - repo.create_reference(headref, sha1_new, True) - - # Send package update notifications. - update_notify(conn, user, pkgbase_id) - - # Close the database. - cur.close() - conn.close() - - -if __name__ == '__main__': - main() diff --git a/setup.py b/setup.py index 48eb176..b64e71c 100644 --- a/setup.py +++ b/setup.py @@ -17,4 +17,11 @@ setup( name="aurweb", version=version, packages=find_packages(), + entry_points={ + 'console_scripts': [ + 'aurweb-git-auth = aurweb.git.auth:main', + 'aurweb-git-serve = aurweb.git.serve:main', + 'aurweb-git-update = aurweb.git.update:main', + ], + }, ) diff --git a/test/setup.sh b/test/setup.sh index dc9cff2..d02d298 100644 --- a/test/setup.sh +++ b/test/setup.sh @@ -8,9 +8,9 @@ PYTHONPATH="$TOPLEVEL" export PYTHONPATH # Configure paths to the Git interface scripts. -GIT_AUTH="$TOPLEVEL/git-interface/git-auth.py" -GIT_SERVE="$TOPLEVEL/git-interface/git-serve.py" -GIT_UPDATE="$TOPLEVEL/git-interface/git-update.py" +GIT_AUTH="$TOPLEVEL/aurweb/git/auth.py" +GIT_SERVE="$TOPLEVEL/aurweb/git/serve.py" +GIT_UPDATE="$TOPLEVEL/aurweb/git/update.py" MKPKGLISTS="$TOPLEVEL/scripts/mkpkglists.py" TUVOTEREMINDER="$TOPLEVEL/scripts/tuvotereminder.py" PKGMAINT="$TOPLEVEL/scripts/pkgmaint.py" @@ -38,7 +38,7 @@ reply-to = noreply@aur.archlinux.org [auth] valid-keytypes = ssh-rsa ssh-dss ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521 ssh-ed25519 username-regex = [a-zA-Z0-9]+[.\-_]?[a-zA-Z0-9]+$ -git-serve-cmd = /srv/http/aurweb/git-interface/git-serve.py +git-serve-cmd = $GIT_SERVE ssh-options = restrict [serve] -- cgit v1.2.3-54-g00ecf