| 1 | """ |
| 2 | This is a simple module to get and create nightly snapshots of Django releases |
| 3 | as defined in the documentation model. |
| 4 | |
| 5 | The archive files are by default saved in the media root in a automatically |
| 6 | created directory "nightlies" with filename: "django-nightly-YYYY-MM-DD.EXT" |
| 7 | where EXT can be "zip", "tar.gz" or "tar.bz2". |
| 8 | |
| 9 | There are currently two possible ways of creating the nightlies: |
| 10 | |
| 11 | 1. The first visitor to the url "/nightlies/latest/" will create a gzipped |
| 12 | tarfile of the trunk (the default) and save it in the nightlies directory. |
| 13 | "/nightlies/latest.zip" will create a zipfile, "/nightlies/latest.tar.bz2" a |
| 14 | bzip2'ed tarball accordingly. A version number can be passed too: |
| 15 | "/nightlies/0.95/latest.zip" and "/nightlies/0.96/latest.tar.gz" |
| 16 | |
| 17 | 2. Using the make_nightlies() utility from build_nightlies.py as a cronjob |
| 18 | creates a archive in every format (see ARCHIVE_FORMATS) from every repository |
| 19 | from the documentation model. |
| 20 | """ |
| 21 | |
| 22 | import os |
| 23 | import sys |
| 24 | import stat |
| 25 | import pysvn |
| 26 | import shutil |
| 27 | import tarfile |
| 28 | import zipfile |
| 29 | import urlparse |
| 30 | from datetime import datetime |
| 31 | from tempfile import NamedTemporaryFile, mkdtemp |
| 32 | from django_website.apps.docs.models import DocumentRelease |
| 33 | |
| 34 | from django.conf import settings |
| 35 | from django.http import Http404, HttpResponseRedirect |
| 36 | |
| 37 | def make_tarball (base_name, tmpdir, save_path, compress="gz"): |
| 38 | archive_name = os.path.join(save_path, base_name, ".tar.%s" % compress) |
| 39 | tar = tarfile.open(archive_name, 'w:%s' % compress) |
| 40 | tar.posix = False |
| 41 | tar.add(tmpdir,"") |
| 42 | tar.close() |
| 43 | return archive_name |
| 44 | |
| 45 | def make_zipfile (base_name, tmpdir, save_path, compress=None): |
| 46 | archive_name = os.path.join(save_path, base_name, ".zip") |
| 47 | z = zipfile.ZipFile(archive_name, "w", compression = zipfile.ZIP_DEFLATED) |
| 48 | for root, dirs, files in os.walk(tmpdir): |
| 49 | for f in files: |
| 50 | filepath = os.path.join(root, f) |
| 51 | nice_filepath = "/".join(filepath.split("/")[len(tmpdir.split("/")):]) |
| 52 | if os.path.isfile(filepath): |
| 53 | z.write(filepath, nice_filepath) |
| 54 | z.close() |
| 55 | return archive_name |
| 56 | |
| 57 | ARCHIVE_FORMATS = { |
| 58 | 'tar.gz': (make_tarball, [('compress', 'gz')], "gzip'ed tar-file"), |
| 59 | 'tar.bz2': (make_tarball, [('compress', 'bz2')], "bzip2'ed tar-file"), |
| 60 | 'zip': (make_zipfile, [],"ZIP file") |
| 61 | } |
| 62 | |
| 63 | def make_archive (base_name, ext, tmpdir, kwargs = {}): |
| 64 | try: |
| 65 | format_info = ARCHIVE_FORMATS[ext] |
| 66 | except KeyError: |
| 67 | raise ValueError, "unknown archive format '%s'" % ext |
| 68 | |
| 69 | func = format_info[0] |
| 70 | for (arg,val) in format_info[1]: |
| 71 | kwargs[arg] = val |
| 72 | |
| 73 | save_path = os.path.join(settings.MEDIA_ROOT, "nightlies") |
| 74 | if not os.path.exists(save_path): |
| 75 | os.makedirs(save_path) |
| 76 | |
| 77 | filename = apply(func, (base_name, tmpdir, save_path), kwargs) |
| 78 | return filename |
| 79 | |
| 80 | def get_nightly(request, ext="tar.gz", version=None): |
| 81 | nightly_label, filename, nightly_path, nightly_url = _nightly_vars(ext) |
| 82 | |
| 83 | if not os.path.exists(nightly_path): |
| 84 | client, version, svnroot, tmpdir = _export_svn(version) |
| 85 | try: |
| 86 | make_archive(nightly_label, ext, tmpdir) |
| 87 | except: |
| 88 | raise Http404("Error while saving: %s" % filename) |
| 89 | shutil.rmtree(tmpdir) |
| 90 | return HttpResponseRedirect(nightly_url) |
| 91 | |
| 92 | def _nightly_vars(ext): |
| 93 | label = datetime.now().strftime("django-nightly-%Y-%m-%d") |
| 94 | filename = "%s.%s" % (label, ext) |
| 95 | path = os.path.join(settings.MEDIA_ROOT, "nightlies", filename) |
| 96 | url = urlparse.urljoin(settings.MEDIA_URL, "nightlies", filename) |
| 97 | |
| 98 | return label, filename, path, url |
| 99 | |
| 100 | def _export_svn(version): |
| 101 | tmpdir = mkdtemp() |
| 102 | client = pysvn.Client() |
| 103 | |
| 104 | if version is None: |
| 105 | subpath = "trunk/" |
| 106 | else: |
| 107 | rel = get_object_or_404(DocumentRelease, version=version) |
| 108 | subpath = rel.repository_path |
| 109 | svnroot = urlparse.urljoin(settings.DJANGO_SVN_ROOT, subpath) |
| 110 | |
| 111 | try: |
| 112 | client.export(svnroot, os.path.join(tmpdir, "django")) |
| 113 | except pysvn.ClientError: |
| 114 | raise Http404("Bad SVN path: %s" % svnroot) |
| 115 | |
| 116 | return client, version, svnroot, tmpdir |
| 117 | |