On Wed, Jul 30, 2008 at 08:56:19PM +0200, Marc 'HE' Brockschmidt wrote: > Roberto C. Sánchez <roberto@connexer.com> writes: > > As of about one week ago, version 1.3 is now available from upstream > > [2]. This version works with all the changes to SourceForge's site. > > My first instinct was to simply request removal of the package. > > However, that is undesirable as it has already shipped with a stable > > release (version 1.1-1 is in Etch). Removal from Lenny would leave Etch > > users without any sort of upgrade path. Leaving the current version in > > Lenny would, however, result in a package that is completely broken for > > Lenny's entire life cycle. > > Pushing an untested package into the frozen lenny is not an option, so > removal seems to be the most reasonable course. > OK. Based on #492933, I asked for a removal from testing. After that, I asked about the possibility for inclusion in Lenny on #debian-release and the impression that I got from vorlon and dato was that if the changes were limited, that it would be possible to get an exception. So, I and another user have tested the updated package. Have a look at the log for #492933 for the user's comments about the package working for him. I have also verified that it works for me. I have attached the debdiff between 1.1-4 (currently in Sid and it was in Lenny until yesterday) and 1.3-1 (please note that upstream never released version 1.2, he went from 1.1 a little over two years ago to 1.3 just last week). Please consider this package for inclusion in Lenny (the package is currently in INCOMING). Regards, -Roberto -- Roberto C. Sánchez http://people.connexer.com/~roberto http://www.connexer.com
diff -Nru releaseforge-1.1/CHANGELOG.txt releaseforge-1.3/CHANGELOG.txt
--- releaseforge-1.1/CHANGELOG.txt 2006-06-15 05:51:53.000000000 -0400
+++ releaseforge-1.3/CHANGELOG.txt 2008-06-12 00:12:41.000000000 -0400
@@ -1,6 +1,17 @@
ReleaseForge ChangeLog
----------------------
+version 1.3:
+ *) added support for sftp
+ *) removed support for ftp
+
+version 1.2:
+ *) uploading release notes and change log now attempts to use tempfiles to transmit
+ the data as multipart encodings rather than inline text (if unable to create the temp files
+ ReleaseForge will fallback to the old mechanism). This should allow encoded entities (such as
+ the ampersand) to be transmitted properly.
+ *) ReleaseForge will now automatically retry failed https connections
+
version 1.1:
*) added re-guess file attributes button
*) fix: disabled chunking when selecting files to include in a release which caused the
diff -Nru /tmp/user/2000/X5m6gbvF7f/releaseforge-1.1/MANIFEST.in /tmp/user/2000/EZty4Ck08i/releaseforge-1.3/MANIFEST.in
--- releaseforge-1.1/MANIFEST.in 2006-04-24 12:46:24.000000000 -0400
+++ releaseforge-1.3/MANIFEST.in 2008-07-07 12:17:28.000000000 -0400
@@ -1,7 +1,5 @@
recursive-include help *.html
recursive-include help *.png
-######recursive-include translations *
-######recursive-include scripts *
include setup.cfg
include images/*.png
include images/*.ico
@@ -9,6 +7,7 @@
include LICENSE.txt
include README.txt
include CHANGELOG.txt
+include RELEASE_NOTES.txt
include ReleaseForge/*.ui
include ReleaseForge/*.py
include releaseforge.pro
diff -Nru /tmp/user/2000/X5m6gbvF7f/releaseforge-1.1/PKG-INFO /tmp/user/2000/EZty4Ck08i/releaseforge-1.3/PKG-INFO
--- releaseforge-1.1/PKG-INFO 2006-06-15 06:02:06.000000000 -0400
+++ releaseforge-1.3/PKG-INFO 2008-07-07 12:17:53.000000000 -0400
@@ -1,6 +1,6 @@
Metadata-Version: 1.0
Name: releaseforge
-Version: 1.1
+Version: 1.3
Summary: ReleaseForge is a utility designed for the administrators and release engineers of SourceForge projects.
Home-page: http://releaseforge.sourceforge.net
Author: Phil Schwartz
diff -Nru /tmp/user/2000/X5m6gbvF7f/releaseforge-1.1/RELEASE_NOTES.txt /tmp/user/2000/EZty4Ck08i/releaseforge-1.3/RELEASE_NOTES.txt
--- releaseforge-1.1/RELEASE_NOTES.txt 1969-12-31 19:00:00.000000000 -0500
+++ releaseforge-1.3/RELEASE_NOTES.txt 2008-07-07 12:16:27.000000000 -0400
@@ -0,0 +1,19 @@
+Version 1.3:
+============
+
+Sourceforge has recently deprecated the FTP upload mechanism. This policy change cripples ReleaseForge prior to 1.3.
+
+ReleaseForge 1.3 now uploads files to Sourceforge via SFTP (one of the new supported methods).
+
+Prior to running ReleaseForge 1.3 you will need to obtain Paramiko v1.74 or greater.
+ReleaseFroge uses paramiko module for uploading files to SourceForge via SFTP. [Special thanks to Robey Pointer for adding the
+patch required for ReleaseForge].
+
+Paramiko can be downloaded from:
+
+http://www.lag.net/paramiko/
+
+Make sure you obtain 1.74 or greater. Versions prior to 1.74 will not work.
+
+Once paramiko is installed, ReleaseForge should be able to upload files to Sourceforge via SFTP using the same login credentials
+for accessing the Sourceforge web interface.
diff -Nru /tmp/user/2000/X5m6gbvF7f/releaseforge-1.1/ReleaseForge/MultipartPostHandler.py /tmp/user/2000/EZty4Ck08i/releaseforge-1.3/ReleaseForge/MultipartPostHandler.py
--- releaseforge-1.1/ReleaseForge/MultipartPostHandler.py 1969-12-31 19:00:00.000000000 -0500
+++ releaseforge-1.3/ReleaseForge/MultipartPostHandler.py 2006-07-01 17:30:14.000000000 -0400
@@ -0,0 +1,128 @@
+#!/usr/bin/python
+
+####
+# 02/2006 Will Holcomb <wholcomb@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+"""
+Usage:
+ Enables the use of multipart/form-data for posting forms
+
+Inspirations:
+ Upload files in python:
+ http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306
+ urllib2_file:
+ Fabien Seisen: <fabien@seisen.org>
+
+Example:
+ import MultipartPostHandler, urllib2, cookielib
+
+ cookies = cookielib.CookieJar()
+ opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookies),
+ MultipartPostHandler.MultipartPostHandler)
+ params = { "username" : "bob", "password" : "riviera",
+ "file" : open("filename", "rb") }
+ opener.open("http://wwww.bobsite.com/upload/", params)
+
+Further Example:
+ The main function of this file is a sample which downloads a page and
+ then uploads it to the W3C validator.
+"""
+
+import urllib
+import urllib2
+import mimetools, mimetypes
+import os, stat
+
+class Callable:
+ def __init__(self, anycallable):
+ self.__call__ = anycallable
+
+class MultipartPostHandler(urllib2.BaseHandler):
+ handler_order = urllib2.HTTPHandler.handler_order - 10 # needs to run first
+
+ def http_request(self, request):
+ data = request.get_data()
+ if data is not None and type(data) != str:
+ v_files = []
+ v_vars = []
+ try:
+ for(key, value) in data.items():
+ if type(value) == file:
+ v_files.append((key, value))
+ else:
+ v_vars.append((key, value))
+ except TypeError:
+ systype, value, traceback = sys.exc_info()
+ raise TypeError, "not a valid non-string sequence or mapping object", traceback
+
+ if len(v_files) == 0:
+ data = urllib.urlencode(v_vars)
+ else:
+ boundary, data = self.multipart_encode(v_vars, v_files)
+ contenttype = 'multipart/form-data; boundary=%s' % boundary
+ if request.has_header('Content-type'):
+ pass # print "Replacing %s with %s" % (request.get_header('content-type'), contenttype)
+ request.add_unredirected_header('Content-type', contenttype)
+
+ request.add_data(data)
+
+ return request
+
+ def multipart_encode(vars, files, boundary = None, buffer = None):
+ if boundary is None:
+ boundary = mimetools.choose_boundary()
+ if buffer is None:
+ buffer = ''
+ for(key, value) in vars:
+ buffer += '--%s\r\n' % boundary
+ buffer += 'Content-Disposition: form-data; name="%s"' % key
+ buffer += '\r\n\r\n' + value + '\r\n'
+ for(key, fd) in files:
+ file_size = os.fstat(fd.fileno())[stat.ST_SIZE]
+ filename = fd.name.split('/')[-1]
+ contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
+ buffer += '--%s\r\n' % boundary
+ buffer += 'Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename)
+ buffer += 'Content-Type: %s\r\n' % contenttype
+ # buffer += 'Content-Length: %s\r\n' % file_size
+ fd.seek(0)
+ buffer += '\r\n' + fd.read() + '\r\n'
+ buffer += '--%s--\r\n\r\n' % boundary
+ return boundary, buffer
+ multipart_encode = Callable(multipart_encode)
+
+ https_request = http_request
+
+def main():
+ import tempfile, sys
+
+ validatorURL = "http://validator.w3.org/check"
+ opener = urllib2.build_opener(MultipartPostHandler)
+
+ def validateFile(url):
+ temp = tempfile.mkstemp(suffix=".html")
+ os.write(temp[0], opener.open(url).read())
+ params = { "ss" : "0", # show source
+ "doctype" : "Inline",
+ "uploaded_file" : open(temp[1], "rb") }
+ print opener.open(validatorURL, params).read()
+ os.remove(temp[1])
+
+ if len(sys.argv[1:]) > 0:
+ for arg in sys.argv[1:]:
+ validateFile(arg)
+ else:
+ validateFile("http://www.google.com")
+
+if __name__=="__main__":
+ main()
diff -Nru /tmp/user/2000/X5m6gbvF7f/releaseforge-1.1/ReleaseForge/constants.py /tmp/user/2000/EZty4Ck08i/releaseforge-1.3/ReleaseForge/constants.py
--- releaseforge-1.1/ReleaseForge/constants.py 2006-04-21 16:31:00.000000000 -0400
+++ releaseforge-1.3/ReleaseForge/constants.py 2008-06-12 21:33:57.000000000 -0400
@@ -12,8 +12,10 @@
LOGGER_CONFIG_FILENAME = "logger.ini"
-SF_FTP_SERVER = "upload.sourceforge.net"
-SF_FTP_DIRECTORY = "incoming"
+SF_SFTP_SERVER = "frs.sourceforge.net"
+SF_SFTP_PORT = 22
+SF_SFTP_DIRECTORY = "uploads"
+
SF_WARNING = """
The follwing files could not be added to your release
diff -Nru /tmp/user/2000/X5m6gbvF7f/releaseforge-1.1/ReleaseForge/ftp.py /tmp/user/2000/EZty4Ck08i/releaseforge-1.3/ReleaseForge/ftp.py
--- releaseforge-1.1/ReleaseForge/ftp.py 2005-12-16 17:47:12.000000000 -0500
+++ releaseforge-1.3/ReleaseForge/ftp.py 1969-12-31 19:00:00.000000000 -0500
@@ -1,44 +0,0 @@
-from ftplib import FTP
-
-class FTP_(FTP):
- def storbinary(self, cmd, fp, blocksize=8192, callback=None):
- '''Store a file in binary mode and optionally
- use a callback func to provide a tuple containing
- number of bytes transmitted to the ftp server and total size
- '''
- self.voidcmd('TYPE I')
- conn = self.transfercmd(cmd)
- if callback:
- totalsent = 0
- fp.seek(0L, 2) # go to file end
- totalsize = fp.tell()
- fp.seek(0L, 0) # go to file start
-
- while 1:
- buf = fp.read(blocksize)
- if not buf: break
- conn.sendall(buf)
- if callback:
- callback( (totalsent, totalsize) )
- totalsent += blocksize
- conn.close()
- return self.voidresp()
-
-
-if __name__ == '__main__':
- def func(transmitted): print "transmitted:", transmitted
-
- import sys
- from constants import SF_FTP_SERVER, SF_FTP_DIRECTORY
-
- filename = sys.argv[1]
- f = open(filename, "rb")
-
- ftp = FTP_()
- ftp.connect(SF_FTP_SERVER)
- ftp.getwelcome()
-
- ftp.login("ftp", "releaseforge")
- ftp.cwd(SF_FTP_DIRECTORY)
- ftp.storbinary('STOR ' + filename, f, callback=func)
- ftp.close()
diff -Nru /tmp/user/2000/X5m6gbvF7f/releaseforge-1.1/ReleaseForge/messageQueue.py /tmp/user/2000/EZty4Ck08i/releaseforge-1.3/ReleaseForge/messageQueue.py
--- releaseforge-1.1/ReleaseForge/messageQueue.py 2005-12-16 17:47:12.000000000 -0500
+++ releaseforge-1.3/ReleaseForge/messageQueue.py 2008-06-12 21:30:14.000000000 -0400
@@ -22,4 +22,4 @@
mainMessageQueue = MessageQueue()
progressMessageQueue = MessageQueue()
freshmeatMessageQueue = MessageQueue()
-ftpProgressQueue = MessageQueue()
+sftpProgressQueue = MessageQueue()
diff -Nru /tmp/user/2000/X5m6gbvF7f/releaseforge-1.1/ReleaseForge/releaseWizard.py /tmp/user/2000/EZty4Ck08i/releaseforge-1.3/ReleaseForge/releaseWizard.py
--- releaseforge-1.1/ReleaseForge/releaseWizard.py 2006-06-15 05:14:06.000000000 -0400
+++ releaseforge-1.3/ReleaseForge/releaseWizard.py 2008-06-12 21:31:35.000000000 -0400
@@ -6,7 +6,7 @@
import os
from ReleaseDataVO import ReleaseDataVO
from FileVO import FileVO
-from messageQueue import progressMessageQueue, ftpProgressQueue
+from messageQueue import progressMessageQueue, sftpProgressQueue
from workerThread import WorkerThread
from help import Help
import time
@@ -79,9 +79,9 @@
self.connect(self.timer, SIGNAL("timeout()"),
self.getProgressMsg)
- self.ftp_timer = QTimer(self)
- self.connect(self.ftp_timer, SIGNAL("timeout()"),
- self.update_ftp_progress)
+ self.sftp_timer = QTimer(self)
+ self.connect(self.sftp_timer, SIGNAL("timeout()"),
+ self.update_sftp_progress)
self.connect(self, SIGNAL("selected(const QString &)"), self.pageChanged)
self.releaseNameEdited(self.newReleaseNameLineEdit.text())
@@ -200,12 +200,12 @@
def show_file_progress(self, show):
debug("show_file_progress: %s", show)
if show:
- self.ftp_timer.start(50, False)
+ self.sftp_timer.start(50, False)
self.fileTextLabel.show()
self.fileProgressBar.setProgress(-1)
self.fileProgressBar.show()
else:
- self.ftp_timer.stop()
+ self.sftp_timer.stop()
self.fileTextLabel.hide()
self.fileProgressBar.hide()
@@ -664,7 +664,7 @@
release_notes = data_tuple[0]
change_log = data_tuple[1]
files = data_tuple[2]
-
+
self.releaseNotesTextEdit.setText(from_utf8(release_notes))
self.changeLogTextEdit.setText(from_utf8(change_log))
self.setNextEnabled(self.page(WIZARD_PAGE_RELEASE_NAME), False)
@@ -763,13 +763,19 @@
# populate the progress label with it.
msg = progressMessageQueue.get()
if msg:
- self.progressTextLabel.setText(msg)
- self.incrementProgressBar()
+ self.progressTextLabel.setText(msg)
+ if not msg.startswith("Retry: "):
+ self.incrementProgressBar()
+# else:
+# self.decrementProgressBar(1)
def incrementProgressBar(self, increment=1):
self.progressBar.setProgress(self.progressBar.progress() + increment)
+ def decrementProgressBar(self, decrement=1):
+ self.progressBar.setProgress(self.progressBar.progress() - decrement)
+
def isCancelled(self):
return self.cancelled
@@ -1054,7 +1060,7 @@
def error(self, msg):
self.timer.stop()
- self.ftp_timer.stop()
+ self.sftp_timer.stop()
page = self.page(WIZARD_PAGE_PROGRESS)
self.progressTextLabel.setText(msg)
self.setFinishEnabled(page, False)
@@ -1065,7 +1071,7 @@
def complete(self, data):
self.timer.stop()
- self.ftp_timer.stop()
+ self.sftp_timer.stop()
page = self.page(WIZARD_PAGE_PROGRESS)
if self.create:
self.progressTextLabel.setText("Successfully created new release")
@@ -1107,10 +1113,10 @@
self.notifyTextLabel.setEnabled(False)
- def update_ftp_progress(self):
+ def update_sftp_progress(self):
# read the message queue. If an item exists,
# populate the progress label with it.
- progress_tuple = ftpProgressQueue.get()
+ progress_tuple = sftpProgressQueue.get()
#debug("update_ftp_progress: %s", progress_tuple)
if progress_tuple:
if progress_tuple == (True, True):
diff -Nru /tmp/user/2000/X5m6gbvF7f/releaseforge-1.1/ReleaseForge/sfcomm.py /tmp/user/2000/EZty4Ck08i/releaseforge-1.3/ReleaseForge/sfcomm.py
--- releaseforge-1.1/ReleaseForge/sfcomm.py 2006-06-15 05:49:44.000000000 -0400
+++ releaseforge-1.3/ReleaseForge/sfcomm.py 2008-07-07 09:51:03.000000000 -0400
@@ -1,7 +1,7 @@
from workerThread import WorkerThread
from sfurls import *
from sfregex import *
-from constants import SF_COOKIE_FILENAME, SF_FTP_SERVER, SF_FTP_DIRECTORY
+from constants import SF_COOKIE_FILENAME, SF_SFTP_SERVER, SF_SFTP_DIRECTORY, SF_SFTP_PORT
from ProjectVO import ProjectVO
from PackageVO import PackageVO
@@ -12,14 +12,19 @@
except:
# python2.3 compatibility
from python24.urllib2 import build_opener, HTTPCookieProcessor, ProxyHandler
+import MultipartPostHandler
import logging
-from messageQueue import mainMessageQueue, progressMessageQueue, ftpProgressQueue
+from messageQueue import mainMessageQueue, progressMessageQueue, sftpProgressQueue
import os, sys
-from ftp import FTP_
+import paramiko
import time
from constants import SF_RESPONSE_CHUNK_SIZE
+from types import StringType
+import tempfile
debug = logging.getLogger("sfcomm").debug
+info = logging.getLogger("sfcomm").info
+warn = logging.getLogger("sfcomm").warn
error = logging.getLogger("sfcomm").error
exception = logging.getLogger("sfcomm").exception
@@ -43,6 +48,18 @@
TRUNCATE_POST = 1024
+class RetryException(Exception): pass
+
+def unescape_entities(s):
+ # replace & < > " in s with their text counterparts
+ if not s: return s
+ s = s.replace("&", "&")
+ s = s.replace("<", "<")
+ s = s.replace(">", ">")
+ s = s.replace(""", '"')
+ return s
+
+
class SFComm:
def __init__(self, data_path, proxy=None):
self.cookie_file = os.path.join(data_path, SF_COOKIE_FILENAME)
@@ -64,15 +81,17 @@
queue=progressMessageQueue,
addl_headers=None,
ok_status=None,
+ post_as_multipart=None,
+ retry=True,
dbg=None):
#
# parameters:
# url: url to retrieve
- # post_encoded: the encoded query string sent to SF
+ # post_encoded: the (preferably) encoded query string sent to SF
# queue: the python Queue that status information will be sent to
# addl_headers: any additional headers that need to be passed to SF
# ok_status: a string as described below.
- #
+ # post_as_multipart: post form data with enctype: multipart/form-data
# returns a tuple containing the data returned from SF and the status.
#
# If ok_status is provided, the status returned is True if the
@@ -81,26 +100,59 @@
#
# If ok_status is None, status is always True and data will not be truncated
#
+ # If retry is True, then this is the first attempt and if it fails this transaction
+ # will be retried.
+ #
# return tuple form: (data, status)
#
data = ""
ok = True
try:
debug("Fetching: %s", url)
- if post_encoded:
+ if post_encoded and type(post_encoded) is StringType:
if len(post_encoded) > TRUNCATE_POST: truncated = "..."
else: truncated = ""
+
debug("posted: %s%s", post_encoded[:TRUNCATE_POST], truncated)
- queue.put("Fetching: %s" % url)
+ if not retry:
+ # has already been tried, this is the retry attempt
+ queue.put("Retry: %s" % url)
+ else:
+ # has not be attempted yet... will retry if this fails
+ queue.put("Fetching: %s" % url)
cj = cookielib.MozillaCookieJar()
cj.load(self.cookie_file, True)
- opener = build_opener(HTTPCookieProcessor(cj))
+ if post_as_multipart:
+ # enctype: multipart/form-data
+ opener = build_opener(HTTPCookieProcessor(cj),
+ MultipartPostHandler.MultipartPostHandler)
+ else:
+ opener = build_opener(HTTPCookieProcessor(cj))
+
if addl_headers:
opener.addheaders = addl_headers
- r = opener.open(url, post_encoded)
+ try:
+ r = opener.open(url, post_encoded)
+ except Exception, e:
+ exception(e)
+ error("failed posting: %s", post_encoded)
+ if retry:
+ error("retrying last action")
+ return self.fetch_url(url,
+ post_encoded,
+ queue,
+ addl_headers,
+ ok_status,
+ post_as_multipart,
+ False,
+ dbg)
+ else:
+ error("giving up")
+ raise RetryException("Retry failed... giving up")
+
# debug("r methods: %s", dir(r))
# cj.save(self.cookie_file, True)
if not ok_status:
@@ -129,8 +181,10 @@
r.close()
if dbg: debug("Done")
#debug("length: %ld - url: %s", len(data), url)
+ except RetryException, e:
+ raise
except Exception, e:
- print e
+ exception(e)
if dbg: debug("DATA: %s", data)
return data, ok
@@ -140,6 +194,8 @@
def login(self, username, password, return_to=None):
# returns a set of projects for the given username/password
# or None
+ self.username = username # used by sftp
+ self.password = password # used by sftp
debug("Attempting to login to SourceForge")
form = {'form_loginname': username,
'form_pw': password,
@@ -168,13 +224,14 @@
cj.save(self.cookie_file, True, True) # ignore discard! ignore expires
+ projects = set()
+
data = r.read()
idx = data.find("Invalid Password or User Name")
if idx != -1:
debug("Could not login")
return projects
- projects = set()
#debug(data)
project_tuples = PROJECTS_FROM_MY_PROJECTS.findall(data)
for pt in project_tuples:
@@ -186,7 +243,6 @@
return projects
-
def get_packages(self, group_id):
url = "%s=%s" % (EDIT_PACKAGE_BASE_URL, group_id)
packages = set()
@@ -237,32 +293,7 @@
mainMessageQueue.put("You do not have the proper permissions")
return False
else: return True
-
- def add_release(self, group_id, package_id, name):
- url = "%s?package_id=%s&group_id=%s" % (NEW_RELEASE_URL,
- package_id,
- group_id)
- form = {'package_id': package_id,
- 'group_id': group_id,
- 'release_name': name,
- 'newrelease': 'yes',
- 'submit': 'Create This Release'}
-
- data = self.fetch_url(url, urlencode(form))[0]
- #print url
- m = GET_RELEASE_ID.search(data)
- if m:
- return m.group("releaseid")
- else:
- if data.find("Error creating Project object") != -1:
- progressMessageQueue.put("Error creating Project object")
- elif data.find("Login to SourceForge.net") != -1:
- progressMessageQueue.put("Login required?")
- #print data
- else:
- progressMessageQueue.put("Error creating new release")
- return None
def get_releases(self, group_id, package_id):
url = "%s?group_id=%s&package_id=%s" % (EDIT_RELEASE_URL,
@@ -293,8 +324,8 @@
m = GET_NOTES_AND_LOG.search(data)
if m:
- release_notes = m.group("releasenotes")
- change_log = m.group("changelog")
+ release_notes = unescape_entities(m.group("releasenotes"))
+ change_log = unescape_entities(m.group("changelog"))
filedata = GET_FILE_NAMES_ETC.findall(data)
files = []
@@ -313,6 +344,7 @@
debug("file: %s - %s - %s - %s", fileid, filename, processor, filetype)
debug("edit release - files: %s", files)
+
return (release_notes, change_log, files)
@@ -338,8 +370,6 @@
return result
-
-
def add_release(self, group_id, package_id, name):
url = "%s?package_id=%s&group_id=%s" % (NEW_RELEASE_URL,
package_id,
@@ -363,6 +393,7 @@
#print data
else:
progressMessageQueue.put("Error creating new release")
+ error("error creating new release (could not find releaseid): " + data)
return None
@@ -383,19 +414,92 @@
'status_id': status_id,
'uploaded_notes': '',
'uploaded_changes': '',
- 'release_notes': release_notes,
- 'release_changes': change_log,
+ 'release_notes': '',
+ 'release_changes': '',
'preformatted': '1',
'submit': 'Submit/Refresh'
}
- ok = self.fetch_url(url,
- urlencode(form),
- ok_status="Data Saved")[1]
+
+ # attempt to create temp files to store the release notes and change log
+ # if unable to do so or if it cannot completed the task fallback to old behavior
+ # and simply post the text of each as-is.
+ try:
+ release_notes_fp, release_notes_path = tempfile.mkstemp(prefix="rf-", text=True)
+ os.write(release_notes_fp, "%s\n" % release_notes)
+ os.close(release_notes_fp)
+ form['uploaded_notes'] = open(release_notes_path, "r")
+ except:
+ warn("could not create tempfile for release_notes, reverting to old implementation")
+ release_notes_path = ""
+
+ try:
+ change_log_fp, change_log_path = tempfile.mkstemp(prefix="rf-", text=True)
+ os.write(change_log_fp, "%s\n" % change_log)
+ os.close(change_log_fp)
+ form['uploaded_changes'] = open(change_log_path, "r")
+ except:
+ warn("could not create tempfile for change_log, reverting to old implementation")
+ change_log_path = ""
+
+ if not change_log_path or not release_notes_path:
+ return self.edit_release_step1_fallback(group_id, package_id, release_id,
+ name, release_notes, change_log, status)
+
+ try:
+ ok = self.fetch_url(url,
+ form, # encoded by MultipartPostHandler
+ post_as_multipart=True,
+ ok_status="Data Saved")[1]
+ except:
+ ok = self.edit_release_step1_fallback(group_id, package_id, release_id,
+ name, release_notes, change_log, status)
+
+ # if the temp files were created, remove them now
+ if release_notes_path:
+ try: os.remove(release_notes_path)
+ except: pass
+
+ if change_log_path:
+ try: os.remove(change_log_path)
+ except: pass
+
return ok
- #if data.find("Data Saved") != -1: return True
- #else: return False
+ def edit_release_step1_fallback(self, group_id, package_id, release_id,
+ name, release_notes, change_log, status='active'):
+ url = EDIT_RELEASE_URL
+ if status.lower() == 'active': status_id = '1'
+ else: status_id = '3'
+
+ warn("using (post) fallback method... html entities will be munged")
+
+ form = {'package_id': package_id,
+ 'new_package_id': package_id,
+ 'group_id': group_id,
+ 'release_id': release_id,
+ 'release_name': name,
+ 'release_date': time.strftime("%Y-%m-%d"),
+ 'step1': '1',
+ 'status_id': status_id,
+ 'uploaded_notes': '',
+ 'uploaded_changes': '',
+ 'release_notes': release_notes,
+ 'release_changes': change_log,
+ 'preformatted': '1',
+ 'submit': 'Submit/Refresh'
+ }
+
+ try:
+ ok = self.fetch_url(url,
+ urlencode(form),
+ ok_status="Data Saved",
+ retry=False)[1]
+ except:
+ ok = False
+
+ return ok
+
def edit_release_step2(self, group_id, package_id, release_id, fileVOs):
url = EDIT_RELEASE_URL
@@ -421,7 +525,7 @@
error("Error selecting %s for inclusion", filename)
#debug("dumping data: %s", data) # remove me...
ok = False
-
+
matches = GET_FILE_ID.findall(data)
lookup = {}
for match in matches:
@@ -432,7 +536,7 @@
def edit_release_step3(self, group_id, package_id, release_id, fileVOs, lookup_dict):
url = EDIT_RELEASE_URL
-
+
form = {'package_id': package_id,
'group_id': group_id,
'release_id': release_id,
@@ -446,8 +550,10 @@
form['processor_id'] = fileVO.getProcessorId()
form['type_id'] = fileVO.getFileTypeId()
try:
- #debug("filename: %s", fileVO.getFilename() )
- #debug("dict: %s", str(lookup_dict))
+ # REMOVE THESE!!!!! for debugging purpose re: twb
+ #error("filename: %s", fileVO.getFilename() )
+ #error("dict: %s", str(lookup_dict))
+ ######
form['file_id'] = lookup_dict[fileVO.getFilename()]
ok &= self.fetch_url(url, urlencode(form), ok_status="File Updated")[1]
@@ -510,29 +616,23 @@
status)
- def upload_files(self, parent, fileVOs):
- ftp = FTP_()
+ def upload_files(self, parent, fileVOs):
try:
- ftp.connect(SF_FTP_SERVER)
- ftp.getwelcome()
-
- ftp.login("ftp", "releaseforge")
- ftp.cwd(SF_FTP_DIRECTORY)
+ t = paramiko.Transport((SF_SFTP_SERVER, SF_SFTP_PORT))
+ t.start_client()
+ t.auth_password(self.username, self.password)
+ sftp = paramiko.SFTPClient.from_transport(t)
+ sftp.chdir(SF_SFTP_DIRECTORY)
+
num_uploaded = 0
-
for fileVO in fileVOs:
fullpath = fileVO.getFullPath()
filename = fileVO.getFilename()
debug("uploading %s (%s)", filename, fullpath)
progressMessageQueue.put("Uploading: %s" % filename)
- f = open(fullpath, "rb")
-
- ftp.storbinary('STOR ' + filename,
- f,
- callback=self.update_ftp_progress)
- f.close()
+ sftp.put(fullpath, filename, callback=self.update_sftp_progress)
num_uploaded += 1
if parent.isCancelled(): break
@@ -542,21 +642,22 @@
msg = "Successfully uploaded %d files" % num_uploaded
except Exception, e:
msg = str(e)
- progressMessageQueue.put("FTP error: %s" % msg)
+ error(msg)
+ progressMessageQueue.put("SFTP error: %s" % msg)
debug(msg)
try:
- ftp.close()
+ sftp.close()
except: pass
- ftpProgressQueue.clear()
- ftpProgressQueue.put( (True, True) )
+ sftpProgressQueue.clear()
+ sftpProgressQueue.put( (True, True) )
return msg
- def update_ftp_progress(self, progress):
+ def update_sftp_progress(self, progress, total):
#debug("progress: %s", progress)
- ftpProgressQueue.clear()
- ftpProgressQueue.put(progress)
+ sftpProgressQueue.clear()
+ sftpProgressQueue.put( (progress, total) )
def submit_news(self, group_id, subject, message):
diff -Nru /tmp/user/2000/X5m6gbvF7f/releaseforge-1.1/ReleaseForge/version.py /tmp/user/2000/EZty4Ck08i/releaseforge-1.3/ReleaseForge/version.py
--- releaseforge-1.1/ReleaseForge/version.py 2006-06-15 06:01:54.000000000 -0400
+++ releaseforge-1.3/ReleaseForge/version.py 2008-07-07 12:17:32.000000000 -0400
@@ -1 +1 @@
-VERSION="1.1"
+VERSION="1.3"
diff -Nru /tmp/user/2000/X5m6gbvF7f/releaseforge-1.1/debian/changelog /tmp/user/2000/EZty4Ck08i/releaseforge-1.3/debian/changelog
--- releaseforge-1.1/debian/changelog 2008-08-02 13:50:52.000000000 -0400
+++ releaseforge-1.3/debian/changelog 2008-08-02 13:50:53.000000000 -0400
@@ -1,3 +1,14 @@
+releaseforge (1.3-1) unstable; urgency=low
+
+ * New upstream release. (Closes: #492933)
+ - Remove patches now included upstream:
+ + debian/patches/02_empty_set.dpatch
+ + debian/patches/03_new_login_form.dpatch
+ - Add dependency on python-paramiko (>= 1.7.4)
+ * Updated Standards-Version to 3.8.0 (no changes)
+
+ -- Roberto C. Sanchez <roberto@connexer.com> Fri, 01 Aug 2008 23:53:10 -0400
+
releaseforge (1.1-4) unstable; urgency=low
* Update man page.
diff -Nru /tmp/user/2000/X5m6gbvF7f/releaseforge-1.1/debian/control /tmp/user/2000/EZty4Ck08i/releaseforge-1.3/debian/control
--- releaseforge-1.1/debian/control 2008-08-02 13:50:52.000000000 -0400
+++ releaseforge-1.3/debian/control 2008-08-02 13:50:53.000000000 -0400
@@ -5,11 +5,11 @@
Homepage: http://releaseforge.sourceforge.net/
Build-Depends: debhelper (>= 5.0.37.2), dpatch
Build-Depends-Indep: python, python-dev, python-qt3 (>= 3.13), pyqt-tools (>= 3.14.1), python-support (>= 0.5.6)
-Standards-Version: 3.7.3
+Standards-Version: 3.8.0
Package: releaseforge
Architecture: all
-Depends: ${python:Depends}, python-qt3 (>= 3.13)
+Depends: ${python:Depends}, python-qt3 (>= 3.13), python-paramiko (>= 1.7.4)
Description: alternative to SourceForge's File Release System (FRS)
An open source utility designed for the administrators and
release engineers of SourceForge projects. ReleaseForge allows
diff -Nru /tmp/user/2000/X5m6gbvF7f/releaseforge-1.1/debian/copyright /tmp/user/2000/EZty4Ck08i/releaseforge-1.3/debian/copyright
--- releaseforge-1.1/debian/copyright 2008-08-02 13:50:52.000000000 -0400
+++ releaseforge-1.3/debian/copyright 2008-08-02 13:50:53.000000000 -0400
@@ -3,7 +3,9 @@
It was downloaded from http://releaseforge.sourceforge.net/
-Copyright Holder: Copyright (c) 2005 Phil Schwartz <phil_schwartz@users.sourceforge.net>
+Copyright Holder:
+
+Copyright (c) 2005 Phil Schwartz <phil_schwartz@users.sourceforge.net>
License:
diff -Nru /tmp/user/2000/X5m6gbvF7f/releaseforge-1.1/debian/patches/00list /tmp/user/2000/EZty4Ck08i/releaseforge-1.3/debian/patches/00list
--- releaseforge-1.1/debian/patches/00list 2008-08-02 13:50:52.000000000 -0400
+++ releaseforge-1.3/debian/patches/00list 2008-08-02 13:50:53.000000000 -0400
@@ -1,3 +1 @@
01_interpreter_patch.dpatch
-02_empty_set.dpatch
-03_new_login_form.dpatch
diff -Nru /tmp/user/2000/X5m6gbvF7f/releaseforge-1.1/debian/patches/02_empty_set.dpatch /tmp/user/2000/EZty4Ck08i/releaseforge-1.3/debian/patches/02_empty_set.dpatch
--- releaseforge-1.1/debian/patches/02_empty_set.dpatch 2008-08-02 13:50:52.000000000 -0400
+++ releaseforge-1.3/debian/patches/02_empty_set.dpatch 1969-12-31 19:00:00.000000000 -0500
@@ -1,25 +0,0 @@
-#! /bin/sh /usr/share/dpatch/dpatch-run
-## 02_empty_set.dpatch by <roberto@connexer.com>
-##
-## All lines beginning with `## DP:' are a description of the patch.
-## DP: Fixes return of non-existent variable
-
-@DPATCH@
-
-diff -uNr releaseforge-1.1.orig/ReleaseForge/sfcomm.py releaseforge-1.1/ReleaseForge/sfcomm.py
---- releaseforge-1.1.orig/ReleaseForge/sfcomm.py 2006-06-15 05:49:44.000000000 -0400
-+++ releaseforge-1.1/ReleaseForge/sfcomm.py 2006-08-03 18:28:07.099087126 -0400
-@@ -169,12 +169,12 @@
- cj.save(self.cookie_file, True, True) # ignore discard! ignore expires
-
- data = r.read()
-+ projects = set()
- idx = data.find("Invalid Password or User Name")
- if idx != -1:
- debug("Could not login")
- return projects
-
-- projects = set()
- #debug(data)
- project_tuples = PROJECTS_FROM_MY_PROJECTS.findall(data)
- for pt in project_tuples:
diff -Nru /tmp/user/2000/X5m6gbvF7f/releaseforge-1.1/debian/patches/03_new_login_form.dpatch /tmp/user/2000/EZty4Ck08i/releaseforge-1.3/debian/patches/03_new_login_form.dpatch
--- releaseforge-1.1/debian/patches/03_new_login_form.dpatch 2008-08-02 13:50:52.000000000 -0400
+++ releaseforge-1.3/debian/patches/03_new_login_form.dpatch 1969-12-31 19:00:00.000000000 -0500
@@ -1,24 +0,0 @@
-#! /bin/sh /usr/share/dpatch/dpatch-run
-## 03_new_login_form.dpatch by <roberto@connexer.com>
-##
-## All lines beginning with `## DP:' are a description of the patch.
-## DP: New login form
-
-@DPATCH@
-
-Index: trunk/ReleaseForge/sfcomm.py
-===================================================================
---- trunk/ReleaseForge/sfcomm.py (revision 761)
-+++ trunk/ReleaseForge/sfcomm.py (working copy)
-@@ -143,9 +143,8 @@
- debug("Attempting to login to SourceForge")
- form = {'form_loginname': username,
- 'form_pw': password,
-- 'stay_in_ssl': '1',
-- 'persistent_login': '1',
-- 'login': 'Login With SSL'}
-+ 'ssl_status': '1',
-+ 'login': 'Login+in'}
- url = LOGIN_URL
-
- if return_to: url += "?return_to=%s" % quote_plus(return_to)
diff -Nru /tmp/user/2000/X5m6gbvF7f/releaseforge-1.1/setup.cfg /tmp/user/2000/EZty4Ck08i/releaseforge-1.3/setup.cfg
--- releaseforge-1.1/setup.cfg 2006-06-15 06:01:18.000000000 -0400
+++ releaseforge-1.3/setup.cfg 2008-06-22 21:08:50.000000000 -0400
@@ -5,7 +5,10 @@
distribution-name = Red Hat Linux
requires = python >= 2.3
requires = PyQt >= 3.2
+requires = paramiko > 1.7.3
build-requires = python-devel
-doc_files = README.txt LICENSE.txt CHANGELOG.txt
+#doc_files = README.txt LICENSE.txt CHANGELOG.txt
#icon = images/releaseforge-icon.png
install-script = install.sh
+[install]
+optimize = 1
diff -Nru /tmp/user/2000/X5m6gbvF7f/releaseforge-1.1/setup.py /tmp/user/2000/EZty4Ck08i/releaseforge-1.3/setup.py
--- releaseforge-1.1/setup.py 2006-03-31 23:21:15.000000000 -0500
+++ releaseforge-1.3/setup.py 2008-07-07 12:17:08.000000000 -0400
@@ -1,6 +1,7 @@
#!/usr/bin/env python
from ReleaseForge.version import VERSION
from distutils.core import setup
+#from setuptools import setup
import os
import os.path
import sys
@@ -43,6 +44,7 @@
(libpath, glob('releaseforge.pro')),
(libpath, glob('logger.ini')),
(libpath, glob('README.txt')),
+ (libpath, glob('RELEASE_NOTES.txt')),
(libpath, glob('CHANGELOG.txt')),
(libpath, glob('LICENSE.txt')),
# (TRANSLATIONS_DIR, glob(os.path.join("translations", "*"))),
Attachment:
signature.asc
Description: Digital signature