| | 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("%s-%s" % (nightly_label, version), 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 | version = "trunk" |
|---|
| | 106 | subpath = "trunk/" |
|---|
| | 107 | else: |
|---|
| | 108 | rel = get_object_or_404(DocumentRelease, version=version) |
|---|
| | 109 | subpath = rel.repository_path |
|---|
| | 110 | svnroot = urlparse.urljoin(settings.DJANGO_SVN_ROOT, subpath) |
|---|
| | 111 | |
|---|
| | 112 | try: |
|---|
| | 113 | client.export(svnroot, os.path.join(tmpdir, "django")) |
|---|
| | 114 | except pysvn.ClientError: |
|---|
| | 115 | raise Http404("Bad SVN path: %s" % svnroot) |
|---|
| | 116 | |
|---|
| | 117 | return client, version, svnroot, tmpdir |
|---|
| | 118 | |