GitLab wurde aktualisiert. Dank regelmäßiger Updates bleibt das THM GitLab sicher und Sie profitieren von den neuesten Funktionen. Vielen Dank für Ihre Geduld.

Unverified Commit 354b69dd authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets
Browse files

Merge remote-tracking branch 'origin/release-notes'

Signed-off-by: default avatarDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
parents 2ee3f838 b7619dad
......@@ -32,6 +32,7 @@ v 8.2.0 (unreleased)
v 8.1.4
- Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu)
- Prevent redirect loop when home_page_url is set to the root URL
- Ability to add release notes (markdown text and attachments) to git tags
v 8.1.3
- Force update refs/merge-requests/X/head upon a push to the source branch of a merge request (Stan Hu)
......
......@@ -39,6 +39,12 @@ class Dispatcher
shortcut_handler = new ShortcutsNavigation()
new DropzoneInput($('.merge-request-form'))
new IssuableForm($('.merge-request-form'))
when 'projects:tags:new'
new ZenMode()
new DropzoneInput($('.tag-form'))
when 'projects:releases:edit'
new ZenMode()
new DropzoneInput($('.release-form'))
when 'projects:merge_requests:show'
new Diff()
shortcut_handler = new ShortcutsIssuable()
......
......@@ -9,9 +9,9 @@
.bs-callout {
margin: 20px 0;
padding: 20px;
border-left: 3px solid #eee;
color: #666;
background: #f9f9f9;
border-left: 3px solid $border-color;
color: $text-color;
background: $background-color;
}
.bs-callout h4 {
margin-top: 0;
......
......@@ -16,6 +16,7 @@
.append-bottom-10 { margin-bottom:10px }
.append-bottom-15 { margin-bottom:15px }
.append-bottom-20 { margin-bottom:20px }
.append-bottom-default { margin-bottom: $gl-padding; }
.inline { display: inline-block }
.center { text-align: center }
......
......@@ -117,7 +117,7 @@ ul.content-list {
}
.controls {
padding-top: 4px;
padding-top: 1px;
float: right;
.btn {
......
......@@ -72,9 +72,10 @@
list-style: none;
> li {
@include clearfix;
padding: 10px 0;
border-bottom: 1px solid #EEE;
overflow: hidden;
display: block;
margin: 0px;
......
......@@ -115,3 +115,10 @@ li.commit {
}
}
}
.branch-commit {
color: $gl-gray;
.commit-id, .commit-row-message {
color: $gl-gray;
}
}
class Projects::ReleasesController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project
before_action :authorize_download_code!
before_action :authorize_push_code!
before_action :tag
before_action :release
def edit
end
def update
release.update_attributes(release_params)
redirect_to namespace_project_tag_path(@project.namespace, @project, @tag.name)
end
private
def tag
@tag ||= @repository.find_tag(params[:tag_id])
end
def release
@release ||= @project.releases.find_or_initialize_by(tag: @tag.name)
end
def release_params
params.require(:release).permit(:description)
end
end
......@@ -8,15 +8,23 @@ class Projects::TagsController < Projects::ApplicationController
def index
sorted = VersionSorter.rsort(@repository.tag_names)
@tags = Kaminari.paginate_array(sorted).page(params[:page]).per(PER_PAGE)
@releases = project.releases.where(tag: @tags)
end
def show
@tag = @repository.find_tag(params[:id])
@release = @project.releases.find_or_initialize_by(tag: @tag.name)
@commit = @repository.commit(@tag.target)
end
def create
result = CreateTagService.new(@project, current_user).
execute(params[:tag_name], params[:ref], params[:message])
execute(params[:tag_name], params[:ref], params[:message], params[:release_description])
if result[:status] == :success
@tag = result[:tag]
redirect_to namespace_project_tags_path(@project.namespace, @project)
redirect_to namespace_project_tag_path(@project.namespace, @project, @tag.name)
else
@error = result[:message]
render action: 'new'
......@@ -26,12 +34,6 @@ def create
def destroy
DeleteTagService.new(project, current_user).execute(params[:id])
respond_to do |format|
format.html do
redirect_to namespace_project_tags_path(@project.namespace,
@project)
end
format.js
end
redirect_to namespace_project_tags_path(@project.namespace, @project)
end
end
......@@ -122,6 +122,7 @@ def set_last_activity_at
has_many :starrers, through: :users_star_projects, source: :user
has_many :ci_commits, dependent: :destroy, class_name: 'Ci::Commit', foreign_key: :gl_project_id
has_many :ci_builds, through: :ci_commits, source: :builds, dependent: :destroy, class_name: 'Ci::Build'
has_many :releases, dependent: :destroy
has_one :import_data, dependent: :destroy, class_name: "ProjectImportData"
has_one :gitlab_ci_project, dependent: :destroy, class_name: "Ci::Project", foreign_key: :gitlab_id
......@@ -248,7 +249,7 @@ def find_with_namespace(id)
joins(:namespace).
iwhere('namespaces.path' => namespace_path)
projects.where('projects.path' => project_path).take ||
projects.where('projects.path' => project_path).take ||
projects.iwhere('projects.path' => project_path).take
end
......
class Release < ActiveRecord::Base
belongs_to :project
validates :description, :project, :tag, presence: true
end
require_relative 'base_service'
class CreateTagService < BaseService
def execute(tag_name, ref, message)
def execute(tag_name, ref, message, release_description = nil)
valid_tag = Gitlab::GitRefValidator.validate(tag_name)
if valid_tag == false
return error('Tag name invalid')
......@@ -19,8 +19,12 @@ def execute(tag_name, ref, message)
new_tag = repository.find_tag(tag_name)
if new_tag
push_data = create_push_data(project, current_user, new_tag)
if release_description
release = project.releases.find_or_initialize_by(tag: tag_name)
release.update_attributes(description: release_description)
end
push_data = create_push_data(project, current_user, new_tag)
EventCreateService.new.push(project, current_user, push_data)
project.execute_hooks(push_data.dup, :tag_push_hooks)
project.execute_services(push_data.dup, :tag_push_hooks)
......
......@@ -11,8 +11,10 @@ def execute(tag_name)
end
if repository.rm_tag(tag_name)
release = project.releases.find_by(tag: tag_name)
release.destroy if release
push_data = build_push_data(tag)
EventCreateService.new.push(project, current_user, push_data)
project.execute_hooks(push_data.dup, :tag_push_hooks)
project.execute_services(push_data.dup, :tag_push_hooks)
......
......@@ -32,7 +32,7 @@
Files
- if project_nav_tab? :commits
= nav_link(controller: %w(commit commits compare repositories tags branches)) do
= nav_link(controller: %w(commit commits compare repositories tags branches releases)) do
= link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits', data: {placement: 'right'} do
= icon('history fw')
%span
......
.branch-commit.light
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
.branch-commit
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-id"
&middot;
%span.str-truncated
= link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message"
......
......@@ -12,7 +12,7 @@
Branches
%span.badge.js-totalbranch-count= @repository.branches.size
= nav_link(controller: :tags) do
= nav_link(controller: [:tags, :releases]) do
= link_to namespace_project_tags_path(@project.namespace, @project) do
Tags
%span.badge.js-totaltags-count= @repository.tags.length
- page_title "Edit", @tag.name, "Tags"
= render "projects/commits/header_title"
= render "projects/commits/head"
.gray-content-block
.oneline
.title
Release notes for tag
%strong #{@tag.name}
.prepend-top-default
= form_for(@release, method: :put, url: namespace_project_tag_release_path(@project.namespace, @project, @tag.name), html: { class: 'form-horizontal gfm-form release-form' }) do |f|
= render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
= render 'projects/zen', f: f, attr: :description, classes: 'description js-quick-submit'
= render 'projects/notes/hints'
.error-alert
.prepend-top-default
= f.submit 'Save changes', class: 'btn btn-save'
= link_to "Cancel", namespace_project_tag_path(@project.namespace, @project, @tag.name), class: "btn btn-default btn-cancel"
%span.btn-group.btn-grouped
= link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'zip'), class: 'btn btn-default', rel: 'nofollow' do
%i.fa.fa-download
%span source code
%a.btn.btn-default.dropdown-toggle{ 'data-toggle' => 'dropdown' }
%span.caret
%span.sr-only
Select Archive Format
%ul.col-xs-10.dropdown-menu{ role: 'menu' }
%li
= link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'zip'), rel: 'nofollow' do
%i.fa.fa-download
%span Download zip
%li
= link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do
%i.fa.fa-download
%span Download tar.gz
- commit = @repository.commit(tag.target)
- release = @releases.find { |release| release.tag == tag.name }
%li
%div
= link_to namespace_project_commits_path(@project.namespace, @project, tag.name), class: "" do
= link_to namespace_project_tag_path(@project.namespace, @project, tag.name) do
%strong
%i.fa.fa-tag
= icon('tag')
= tag.name
- if tag.message.present?
&nbsp;
= strip_gpg_signature(tag.message)
.controls
= link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn' do
= icon("pencil")
- if can? current_user, :download_code, @project
= render 'projects/repositories/download_archive', ref: tag.name, btn_class: 'btn-grouped btn-group-xs'
- if can?(current_user, :admin_project, @project)
= link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-xs btn-remove remove-row grouped', method: :delete, data: { confirm: 'Removed tag cannot be restored. Are you sure?'}, remote: true do
%i.fa.fa-trash-o
= render 'projects/tags/download', ref: tag.name, project: @project
- if commit
= render 'projects/branches/commit', commit: commit, project: @project
- else
%p
Cant find HEAD commit for this tag
- if release && release.description.present?
.description.prepend-top-default
.wiki
= preserve do
= markdown release.description
$('.js-totaltags-count').html("#{@repository.tags.size}")
- if @repository.tags.size == 0
$('.tags').load(document.URL + ' .nothing-here-block').hide().fadeIn(1000)
......@@ -5,10 +5,12 @@
.alert.alert-danger
%button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
= @error
%h3.page-title
%i.fa.fa-code-fork
New tag
= form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal" do
New git tag
%hr
= form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal tag-form" do
.form-group
= label_tag :tag_name, 'Name for new tag', class: 'control-label'
.col-sm-10
......@@ -17,12 +19,29 @@
= label_tag :ref, 'Create from', class: 'control-label'
.col-sm-10
= text_field_tag :ref, params[:ref], placeholder: 'master', required: true, tabindex: 2, class: 'form-control'
.light Branch name or commit SHA
.help-block Branch name or commit SHA
.form-group
= label_tag :message, 'Message', class: 'control-label'
.col-sm-10
= text_field_tag :message, nil, placeholder: 'Enter message.', required: false, tabindex: 3, class: 'form-control'
.light (Optional) Entering a message will create an annotated tag.
.help-block (Optional) Entering a message will create an annotated tag.
%hr
.form-group
= label_tag :release_description, 'Release notes', class: 'control-label'
.col-sm-10
= render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
.zennable
%input#zen-toggle-comment.zen-toggle-comment(tabindex="-1" type="checkbox")
.zen-backdrop
= text_area_tag :release_description, nil, class: 'js-gfm-input markdown-area description js-quick-submit form-control', placeholder: ''
%a.zen-enter-link(tabindex="-1" href="#")
= icon('expand')
Edit in fullscreen
%a.zen-leave-link(href="#")
= icon('compress')
= render 'projects/notes/hints'
.help-block (Optional) You can add release notes to your tag. It will be stored in the GitLab database and shown on the tags page
.form-actions
= button_tag 'Create tag', class: 'btn btn-create', tabindex: 3
= link_to 'Cancel', namespace_project_tags_path(@project.namespace, @project), class: 'btn btn-cancel'
......
- page_title @tag.name, "Tags"
= render "projects/commits/header_title"
= render "projects/commits/head"
.gray-content-block
.pull-right
- if can?(current_user, :push_code, @project)
= link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn', title: 'Edit release notes' do
= icon("pencil")
= link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped', title: 'Browse source code' do
= icon('files-o')
= link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped', title: 'Browse commits' do
= icon('history')
- if can? current_user, :download_code, @project
= render 'projects/tags/download', ref: @tag.name, project: @project
- if can?(current_user, :admin_project, @project)
.pull-right
= link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped', method: :delete, data: { confirm: 'Removed tag cannot be restored. Are you sure?'} do
%i.fa.fa-trash-o
.title
%strong= @tag.name
- if @tag.message.present?
%span.light
&nbsp;
= strip_gpg_signature(@tag.message)
- if @commit
= render 'projects/branches/commit', commit: @commit, project: @project
- else
Cant find HEAD commit for this tag
.append-bottom-default.prepend-top-default
- if @release.description.present?
.description
.wiki
= preserve do
= markdown @release.description
- else
This tag has no release notes.
......@@ -583,7 +583,10 @@
end
resources :branches, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex }
resources :tags, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex }
resources :tags, only: [:index, :show, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } do
resource :release, only: [:edit, :update]
end
resources :protected_branches, only: [:index, :create, :update, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex }
resource :variables, only: [:show, :update]
resources :triggers, only: [:index, :create, :destroy]
......
class CreateReleases < ActiveRecord::Migration
def change
create_table :releases do |t|
t.string :tag
t.text :description
t.integer :project_id
t.timestamps
end
add_index :releases, :project_id
add_index :releases, [:project_id, :tag]
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20151103133339) do
ActiveRecord::Schema.define(version: 20151105094515) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -639,6 +639,17 @@
add_index "protected_branches", ["project_id"], name: "index_protected_branches_on_project_id", using: :btree
create_table "releases", force: true do |t|
t.string "tag"
t.text "description"
t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "releases", ["project_id", "tag"], name: "index_releases_on_project_id_and_tag", using: :btree
add_index "releases", ["project_id"], name: "index_releases_on_project_id", using: :btree
create_table "sent_notifications", force: true do |t|
t.integer "project_id"
t.integer "noteable_id"
......
......@@ -12,6 +12,12 @@ Feature: Project Commits Tags
And I submit new tag form
Then I should see new tag created
Scenario: I create a tag with release notes
Given I click new tag link
And I submit new tag form with release notes
Then I should see new tag created
And I should see tag release notes
Scenario: I create a tag with invalid name
And I click new tag link
And I submit new tag form with invalid name
......@@ -27,15 +33,13 @@ Feature: Project Commits Tags
And I submit new tag form with tag that already exists
Then I should see new an error that tag already exists
@javascript
Scenario: I delete a tag
Given I visit tag 'v1.1.0' page
Given I delete tag 'v1.1.0'
Then I should not see tag 'v1.1.0'
@javascript
Scenario: I delete all tags and see info message
Given I delete all tags
Then I should see tags info message
# @wip
# Scenario: I can download project by tag
Scenario: I add release notes to the tag
Given I visit tag 'v1.1.0' page
When I click edit tag link
And I fill release notes and submit form
Then I should see tag release notes
......@@ -18,6 +18,18 @@ class Spinach::Features::ProjectCommitsTags < Spinach::FeatureSteps
click_button 'Create tag'
end
step 'I submit new tag form with release notes' do
fill_in 'tag_name', with: 'v7.0'
fill_in 'ref', with: 'master'
fill_in 'release_description', with: 'Awesome release notes'
click_button 'Create tag'
end
step 'I fill release notes and submit form' do
fill_in 'release_description', with: 'Awesome release notes'
click_button 'Save changes'
end
step 'I submit new tag form with invalid name' do
fill_in 'tag_name', with: 'v 1.0'
fill_in 'ref', with: 'master'
......@@ -52,31 +64,27 @@ class Spinach::Features::ProjectCommitsTags < Spinach::FeatureSteps
expect(page).to have_content 'Tag already exists'
end
step "I visit tag 'v1.1.0' page" do
click_link 'v1.1.0'
end
step "I delete tag 'v1.1.0'" do
page.within '.tags' do
page.within('.content') do
first('.btn-remove').click
sleep 0.05
end
end
step "I should not see tag 'v1.1.0'" do
page.within '.tags' do
expect(page.all(visible: true)).not_to have_content 'v1.1.0'
expect(page).not_to have_link 'v1.1.0'
end
end
step 'I delete all tags' do
page.within '.tags' do
page.all('.btn-remove').each do |remove|
remove.click
sleep 0.05
end
end
step 'I click edit tag link' do
click_link 'Edit release notes'
end
step 'I should see tags info message' do
page.within '.tags' do
expect(page).to have_content 'Repository has no tags yet.'
end
step 'I should see tag release notes' do
expect(page).to have_content 'Awesome release notes'
end
end
# Read about factories at https://github.com/thoughtbot/factory_girl
FactoryGirl.define do
factory :release do
tag "v1.1.0"
description "Awesome release"
project
end
end
require 'rails_helper'
RSpec.describe Release, type: :model do
let(:release) { create(:release) }
it { expect(release).to be_valid }
describe 'associations' do
it { is_expected.to belong_to(:project) }
end
describe 'validation' do
it { is_expected.to validate_presence_of(:project) }
it { is_expected.to validate_presence_of(:description) }
end
end
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment