From 7cc25205410efc6b20b11d94ab2cbc1a322ff816 Mon Sep 17 00:00:00 2001
From: Jason Hollingsworth <jhworth.developer@gmail.com>
Date: Fri, 20 Dec 2013 13:12:44 -0600
Subject: [PATCH] Add support for various archive formats.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Used mime-types gem instead of hardcoding content types.
Allow multiple extensions in archive route (.tar.gz, .tar.bz2).
Change content disposition from infile(?) to attachment for api.
Fixed api would return “archive” instead of {project}-{hash}.{ext}
---
 .../projects/repositories_controller.rb       |  2 +-
 config/routes.rb                              |  2 +-
 lib/api/repositories.rb                       | 12 ++++++----
 lib/gitlab/regex.rb                           |  5 ++++
 spec/requests/api/repositories_spec.rb        | 23 +++++++++++++++++--
 spec/routing/project_routing_spec.rb          |  8 +++++++
 6 files changed, 44 insertions(+), 8 deletions(-)

diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb
index 20e2a9311ee..f686db70bd4 100644
--- a/app/controllers/projects/repositories_controller.rb
+++ b/app/controllers/projects/repositories_controller.rb
@@ -16,7 +16,7 @@ class Projects::RepositoriesController < Projects::ApplicationController
 
     storage_path = Rails.root.join("tmp", "repositories")
 
-    file_path = @repository.archive_repo(params[:ref], storage_path)
+    file_path = @repository.archive_repo(params[:ref], storage_path, params[:format].downcase)
 
     if file_path
       # Send file to user
diff --git a/config/routes.rb b/config/routes.rb
index 8322d6a9d4e..734421ede1d 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -217,7 +217,7 @@ Gitlab::Application.routes.draw do
       resource :repository, only: [:show] do
         member do
           get "stats"
-          get "archive"
+          get "archive", constraints: { format: Gitlab::Regex.archive_formats_regex }
         end
       end
 
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index 6a9dc9a39f1..0a32135ff10 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -1,3 +1,5 @@
+require 'mime/types'
+
 module API
   # Projects API
   class Repositories < Grape::API
@@ -206,18 +208,20 @@ module API
       #   sha (optional) - the commit sha to download defaults to the tip of the default branch
       # Example Request:
       #   GET /projects/:id/repository/archive
-      get ":id/repository/archive" do
+      get ":id/repository/archive", requirements: { format: Gitlab::Regex.archive_formats_regex } do
         authorize! :download_code, user_project
         repo = user_project.repository
         ref = params[:sha]
+        format = params[:format]
         storage_path = Rails.root.join("tmp", "repositories")
 
-        file_path = repo.archive_repo(ref, storage_path)
+        file_path = repo.archive_repo(ref, storage_path, format)
         if file_path && File.exists?(file_path)
           data = File.open(file_path, 'rb').read
 
-          header "Content-Disposition:", " infile; filename=\"#{File.basename(file_path)}\""
-          content_type 'application/x-gzip'
+          header["Content-Disposition"] = "attachment; filename=\"#{File.basename(file_path)}\""
+
+          content_type MIME::Types.type_for(file_path).first.content_type
 
           env['api.format'] = :binary
 
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index 93e013ab1b3..943dc9dc7ea 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -17,6 +17,11 @@ module Gitlab
     def path_regex
       default_regex
     end
+    
+    def archive_formats_regex
+      #|zip|tar|    tar.gz    |         tar.bz2         |
+      /(zip|tar|tar\.gz|tgz|gz|tar\.bz2|tbz|tbz2|tb2|bz2)/
+    end
 
     def git_reference_regex
       # Valid git ref regex, see:
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index e078efcc50a..f73ac4372b2 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -1,4 +1,5 @@
 require 'spec_helper'
+require 'mime/types'
 
 describe API::API do
   include ApiHelpers
@@ -232,11 +233,29 @@ describe API::API do
     end
   end
 
-  describe "GET /projects/:id/repository/archive/:sha" do
+  describe "GET /projects/:id/repository/archive(.:format)?:sha" do
     it "should get the archive" do
       get api("/projects/#{project.id}/repository/archive", user)
+      repo_name = project.repository.name.gsub("\.git", "")
       response.status.should == 200
-      response.content_type.should == 'application/x-gzip'
+      response.headers['Content-Disposition'].should =~ /filename\=\"#{repo_name}\-[^\.]+\.tar.gz\"/
+      response.content_type.should == MIME::Types.type_for('file.tar.gz').first.content_type
+    end
+
+    it "should get the archive.zip" do
+      get api("/projects/#{project.id}/repository/archive.zip", user)
+      repo_name = project.repository.name.gsub("\.git", "")
+      response.status.should == 200
+      response.headers['Content-Disposition'].should =~ /filename\=\"#{repo_name}\-[^\.]+\.zip\"/
+      response.content_type.should == MIME::Types.type_for('file.zip').first.content_type
+    end
+
+    it "should get the archive.tar.bz2" do
+      get api("/projects/#{project.id}/repository/archive.tar.bz2", user)
+      repo_name = project.repository.name.gsub("\.git", "")
+      response.status.should == 200
+      response.headers['Content-Disposition'].should =~ /filename\=\"#{repo_name}\-[^\.]+\.tar.bz2\"/
+      response.content_type.should == MIME::Types.type_for('file.tar.bz2').first.content_type
     end
 
     it "should return 404 for invalid sha" do
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 5597f08d186..97f7392e50a 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -130,6 +130,14 @@ describe Projects::RepositoriesController, "routing" do
     get("/gitlab/gitlabhq/repository/archive").should route_to('projects/repositories#archive', project_id: 'gitlab/gitlabhq')
   end
 
+  it "to #archive format:zip" do
+    get("/gitlab/gitlabhq/repository/archive.zip").should route_to('projects/repositories#archive', project_id: 'gitlab/gitlabhq', format: 'zip')
+  end
+
+  it "to #archive format:tar.bz2" do
+    get("/gitlab/gitlabhq/repository/archive.tar.bz2").should route_to('projects/repositories#archive', project_id: 'gitlab/gitlabhq', format: 'tar.bz2')
+  end
+
   it "to #show" do
     get("/gitlab/gitlabhq/repository").should route_to('projects/repositories#show', project_id: 'gitlab/gitlabhq')
   end
-- 
GitLab