From caad9fe9810d480b6e41f57bd6f245715614b000 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 23 Jun 2017 17:42:35 +0200 Subject: [PATCH] Introduce release assets with support for debian repository --- .../projects/packages/debs_controller.rb | 59 +++++++++++++++++ .../projects/release_asset_controller.rb | 48 ++++++++++++++ app/models/project.rb | 1 + app/models/release.rb | 2 + app/models/release_asset.rb | 63 +++++++++++++++++++ app/uploaders/release_asset_uploader.rb | 33 ++++++++++ .../projects/packages/debs/packages.text.haml | 9 +++ .../projects/packages/debs/release.text.haml | 6 ++ app/views/projects/tags/show.html.haml | 14 +++++ config/routes/project.rb | 10 +++ config/routes/repository.rb | 4 +- ...20170623130729_add_release_assets_table.rb | 30 +++++++++ lib/api/entities.rb | 4 ++ lib/api/tags.rb | 30 +++++++++ 14 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 app/controllers/projects/packages/debs_controller.rb create mode 100644 app/controllers/projects/release_asset_controller.rb create mode 100644 app/models/release_asset.rb create mode 100644 app/uploaders/release_asset_uploader.rb create mode 100644 app/views/projects/packages/debs/packages.text.haml create mode 100644 app/views/projects/packages/debs/release.text.haml create mode 100644 db/migrate/20170623130729_add_release_assets_table.rb diff --git a/app/controllers/projects/packages/debs_controller.rb b/app/controllers/projects/packages/debs_controller.rb new file mode 100644 index 00000000000000..238729d744b941 --- /dev/null +++ b/app/controllers/projects/packages/debs_controller.rb @@ -0,0 +1,59 @@ +module Projects + module Packages + class DebsController < Projects::ApplicationController + before_action :assets + before_action :packages_content + + def packages + render plain: packages_content + end + + def release + render plain: release_content + end + + def release_gpg + render plain: `echo "#{release_content}" | gpg --sign --armor` + end + + def in_release + render plain: `echo "#{release_content}" | gpg --sign --armor --clearsign` + end + + def file + release = project.releases.find_by(tag: params[:release]) + asset = release.assets.find_by(file: params[:file]) + file = asset.file + + if file.file_storage? + send_file file.path, disposition: 'attachment' + else + redirect_to file.url + end + end + + private + + def assets + @assets ||= project.release_assets.where(file_type: 1).where.not(file_details: nil) + end + + def packages_content + @packages_content ||= render_to_string("projects/packages/debs/packages", formats: :text) + end + + def release_content + @files = { + Packages: packages_content, + } + @shas = { + MD5Sum: Digest::MD5, + SHA1: Digest::SHA1, + SHA256: Digest::SHA256, + SHA512: Digest::SHA512, + } + @release_content ||= render_to_string("projects/packages/debs/release", formats: :text) + end + end + end +end diff --git a/app/controllers/projects/release_asset_controller.rb b/app/controllers/projects/release_asset_controller.rb new file mode 100644 index 00000000000000..c165a3c0de8250 --- /dev/null +++ b/app/controllers/projects/release_asset_controller.rb @@ -0,0 +1,48 @@ +class Projects::ReleaseAssetController < Projects::ApplicationController + before_action :authorize_download_code! + before_action :release + before_action :asset + before_action :file + + def show + if file.file_storage? + send_file file.path, disposition: 'attachment' + else + redirect_to file.url + end + end + + def update + if asset.update_attributes(asset_params) + redirect_to namespace_project_release_tag_path(@project.namespace, @project, release.tag, asset.file) + else + redirect_to namespace_project_tag_path(@project.namespace, @project, release.tag) + end + end + + def destroy + if asset.destroy + redirect_to namespace_project_release_tag_path(@project.namespace, @project, release.tag, asset.file) + else + redirect_to namespace_project_tag_path(@project.namespace, @project, release.tag) + end + end + + private + + def file + @file ||= asset.file + end + + def asset + @asset ||= release.assets.find_by(file: params[:id]) + end + + def release + @release ||= @project.releases.find_by(tag: params[:tag_id]) + end + + def asset_params + params.require(:assets).permit(:file) + end +end diff --git a/app/models/project.rb b/app/models/project.rb index b7d1cd5725f587..6e8e74c33077d5 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -159,6 +159,7 @@ def set_last_repository_updated_at has_many :approvers, as: :target, dependent: :destroy has_many :approver_groups, as: :target, dependent: :destroy has_many :releases, dependent: :destroy + has_many :release_assets, dependent: :destroy has_many :lfs_objects_projects, dependent: :destroy has_many :lfs_objects, through: :lfs_objects_projects has_many :project_group_links, dependent: :destroy diff --git a/app/models/release.rb b/app/models/release.rb index c936899799e790..6dea6b5dc578d0 100644 --- a/app/models/release.rb +++ b/app/models/release.rb @@ -6,4 +6,6 @@ class Release < ActiveRecord::Base belongs_to :project validates :description, :project, :tag, presence: true + + has_many :assets, class_name: ::ReleaseAsset end diff --git a/app/models/release_asset.rb b/app/models/release_asset.rb new file mode 100644 index 00000000000000..acff8122b962f4 --- /dev/null +++ b/app/models/release_asset.rb @@ -0,0 +1,63 @@ +class ReleaseAsset < ActiveRecord::Base + belongs_to :release + belongs_to :project + + before_save :update_size, if: :file_changed? + before_save :update_file_type, if: :file_changed? + before_save :update_file_content, if: :file_changed? + + #validates :file_architecture, presence: true, if: :debian? + #validates :file_version, presence: true, if: :debian? + + enum file_type: { + unknown: nil, + debian: 1 + } + + mount_uploader :file, ReleaseAssetUploader + + def debian_control + return unless file_details + + details = file_details.strip_heredoc + details = details.match(/Package:.*/m)[0].to_s + details.strip + end + + private + + def update_size + self.size = + if file.exists? + file.size + else + nil + end + end + + def update_file_type + self.file_type = + if file.filename.ends_with?('.deb') + :debian + else + unknown + end + end + + def update_file_content + self.file_details = nil + self.file_version = nil + self.file_architecture = nil + self.md5sum = Digest::MD5.file(file.path).hexdigest + self.sha1sum = Digest::SHA1.file(file.path).hexdigest + self.sha256sum = Digest::SHA256.file(file.path).hexdigest + self.sha512sum = Digest::SHA512.file(file.path).hexdigest + + case + when self.debian? + self.file_details = `dpkg -I "#{file.path}"` + self.file_architecture = self.file_details.match(/Architecture:\s*(.*)\s*/)[1].to_s + self.file_version = self.file_details.match(/Version:\s*(.*)\s*/)[1].to_s + end + end +end diff --git a/app/uploaders/release_asset_uploader.rb b/app/uploaders/release_asset_uploader.rb new file mode 100644 index 00000000000000..b71be17b4353a0 --- /dev/null +++ b/app/uploaders/release_asset_uploader.rb @@ -0,0 +1,33 @@ +class ReleaseAssetUploader < ObjectStoreUploader + storage_options Gitlab.config.artifacts + + def self.local_artifacts_store + Gitlab.config.artifacts.path + end + + def self.artifacts_upload_path + File.join(self.local_artifacts_store, 'tmp-assets/uploads/') + end + + def store_dir + if file_storage? + default_local_path + else + default_path + end + end + + def cache_dir + File.join(self.class.local_artifacts_store, 'tmp-assets/cache') + end + + private + + def default_local_path + File.join(self.class.local_artifacts_store, default_path) + end + + def default_path + File.join('projects', subject.project_id.to_s, 'releases', subject.release_id.to_s, 'assets', subject.id.to_s) + end +end diff --git a/app/views/projects/packages/debs/packages.text.haml b/app/views/projects/packages/debs/packages.text.haml new file mode 100644 index 00000000000000..200721dffdaa27 --- /dev/null +++ b/app/views/projects/packages/debs/packages.text.haml @@ -0,0 +1,9 @@ +- @assets.each do |asset| + = raw asset.debian_control + Filename: file/#{asset.release.tag}/#{asset.file.filename} + Size: #{asset.size.to_s} + MD5sum: #{asset.md5sum} + SHA1: #{asset.sha1sum} + SHA256: #{asset.sha256sum} + SHA512: #{asset.sha512sum} + = "" diff --git a/app/views/projects/packages/debs/release.text.haml b/app/views/projects/packages/debs/release.text.haml new file mode 100644 index 00000000000000..b17a3c24b0856e --- /dev/null +++ b/app/views/projects/packages/debs/release.text.haml @@ -0,0 +1,6 @@ +Date: #{Time.now.utc.to_s(:rfc822)} +- @shas.each do |name, hash| + = name.to_s + ":" + = precede ' ' do + - @files.each do |name, content| + = hash.hexdigest(content) + " " + content.size.to_s + " " + name.to_s diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index 2b81ce4b9fa923..d13fd6dde5f268 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -43,3 +43,17 @@ = markdown_field(@release, :description) - else This tag has no release notes. + + .table-holder + %table.table.ci-table.builds-page + %thead + %tr + %th File + %th Size + %tbody + - @release.assets.each do |asset| + %tr + %td + = link_to asset.file.filename, namespace_project_tag_release_asset_path(@project.namespace, @project, @release.tag, asset.file.filename) + %td + = asset.size diff --git a/config/routes/project.rb b/config/routes/project.rb index f3a98f45fe5893..d37f43f0e7fc71 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -274,6 +274,16 @@ end end + namespace :packages do + resource :deb, only: [] do + get :packages, path: 'Packages' + get :release, path: 'Release' + get :release_gpg, path: 'Release.gpg' + get :in_release, path: 'InRelease' + get :file, path: 'file/*release/*file', format: false + end + end + resources :milestones, constraints: { id: /\d+/ } do member do put :sort_issues diff --git a/config/routes/repository.rb b/config/routes/repository.rb index 4596199ae8b384..75b2c05c14dd60 100644 --- a/config/routes/repository.rb +++ b/config/routes/repository.rb @@ -49,7 +49,9 @@ resources :branches, only: [:index, :new, :create, :destroy] delete :merged_branches, controller: 'branches', action: :destroy_all_merged resources :tags, only: [:index, :show, :new, :create, :destroy] do - resource :release, only: [:edit, :update] + resource :release, only: [:edit, :update] do + resources :assets, only: [:show, :update, :destroy], controller: 'release_asset' + end end resources :protected_branches, only: [:index, :show, :create, :update, :destroy, :patch], constraints: { id: Gitlab::PathRegex.git_reference_regex } do diff --git a/db/migrate/20170623130729_add_release_assets_table.rb b/db/migrate/20170623130729_add_release_assets_table.rb new file mode 100644 index 00000000000000..bac7e4fc1b1054 --- /dev/null +++ b/db/migrate/20170623130729_add_release_assets_table.rb @@ -0,0 +1,30 @@ +class AddReleaseAssetsTable < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + create_table :release_assets do |t| + t.integer :project_id + t.integer :release_id + t.string :file + t.integer :file_store, default: 1, null: false + t.integer :size, limit: 8 + t.integer :file_type + t.text :file_details + t.string :file_version + t.string :file_architecture + t.string :md5sum + t.string :sha1sum + t.string :sha256sum + t.string :sha512sum + t.timestamps + end + + add_index :release_assets, :project_id + add_index :release_assets, :release_id + add_index :release_assets, [:project_id, :release_id] + add_index :release_assets, [:release_id, :file], unique: true + add_index :release_assets, [:release_id, :file, :file_architecture, :file_version], unique: true, name: 'index_release_assets_on_release_id_and_file_arch_and_version' + end +end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 14eb4b27dbb9fe..7ce4369b083900 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -666,6 +666,10 @@ class Release < Grape::Entity expose :tag, as: :tag_name expose :description end + + class ReleaseAsset < Grape::Entity + expose :id, :project_id, :release_id, :file, :size, :file_type, :file_details + end class RepoTag < Grape::Entity expose :name, :message diff --git a/lib/api/tags.rb b/lib/api/tags.rb index c7b1efe0bfa988..1610fd3018ce7b 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -91,6 +91,36 @@ class Tags < Grape::API end end + desc 'Upload release asset' do + success Entities::Release + end + params do + requires :tag_name, type: String, desc: 'The name of the tag' + optional :file, type: File, desc: %q(Asset's file) + optional 'file.path', type: String, desc: %q(path to locally stored body (generated by Workhorse)) + optional 'file.name', type: String, desc: %q(real filename as send in Content-Disposition (generated by Workhorse)) + optional 'file.type', type: String, desc: %q(real content type as send in Content-Type (generated by Workhorse)) + end + post ':id/repository/tags/:tag_name/release/asset', requirements: { tag_name: /.+/ } do + authorize_push_project + release = user_project.releases.find_by(tag: params[:tag_name]) + not_found!("release") unless release + + file = uploaded_file(:file, ArtifactUploader.artifacts_upload_path) + bad_request!('Missing file!') unless file + + asset = release.assets.build( + project: user_project, + file: file, + ) + + if asset.save + present asset, with: Entities::ReleaseAsset + else + render_validation_error!(job) + end + end + desc "Update a tag's release note" do success Entities::Release end -- GitLab