Commit 6e3880e2 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 6d31b8f0
@import 'framework/variables';
// Do not use 3-letter hex codes, bgcolor vs css background-color is problematic in emails
// See https://stackoverflow.com/questions/28551981/why-are-3-digit-hex-color-code-values-interpreted-differently-in-internet-explor
//
// stylelint-disable color-hex-length
$mailer-font: 'Helvetica Neue', Helvetica, Arial, sans-serif;
$mailer-text-color: #333333;
$mailer-bg-color: #fafafa;
$mailer-link-color: #3777b0;
$mailer-link-muted-color: #333333;
$mailer-line-cell-bg-color: #6b4fbb;
$mailer-wrapper-cell-bg-color: #ffffff;
$mailer-wrapper-cell-border-color: #ededed;
$mailer-header-footer-text-color: #5c5c5c;
body {
margin: 0 !important;
background-color: $mailer-bg-color;
padding: 0;
text-align: center;
min-width: 640px;
width: 100%;
height: 100%;
font-family: $mailer-font;
}
table#body {
background-color: $mailer-bg-color;
margin: 0;
padding: 0;
text-align: center;
min-width: 640px;
width: 100%;
}
a {
color: $mailer-link-color;
text-decoration: none;
&.muted {
color: $mailer-link-muted-color;
}
}
.highlight {
font-weight: 500;
}
tr td {
font-family: $mailer-font;
}
tr.line td {
font-family: $mailer-font;
background-color: $mailer-line-cell-bg-color;
height: 4px;
font-size: 4px;
line-height: 4px;
}
tr.header td,
tr.footer td,
td.footer-message {
font-family: $mailer-font;
padding: 25px 0;
font-size: 13px;
line-height: 1.6;
color: $mailer-header-footer-text-color;
}
table.wrapper {
width: 640px;
margin: 0 auto;
border-collapse: separate;
border-spacing: 0;
td.wrapper-cell {
font-family: $mailer-font;
background-color: $mailer-wrapper-cell-bg-color;
text-align: left;
padding: 18px 25px;
border: 1px solid $mailer-wrapper-cell-border-color;
border-radius: 3px;
overflow: hidden;
}
}
table.content {
width: 100%;
border-collapse: separate;
border-spacing: 0;
td.text-content {
font-family: $mailer-font;
color: $mailer-text-color;
font-size: 15px;
font-weight: 400;
line-height: 1.4;
padding: 15px 5px;
text-align: center;
}
}
tr.footer td {
img {
display: block;
margin: 0 auto 1em;
}
.mng-notif-link,
.help-link {
color: $mailer-link-color;
text-decoration: none;
}
}
/* CLIENT-SPECIFIC STYLES */
// These are client-specific rules, ignore some linting rules
//
// stylelint-disable property-no-vendor-prefix, property-no-unknown, length-zero-no-unit
// scss-lint:disable PropertySpelling, ZeroUnit
body,
table,
td,
a {
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
table,
td {
mso-table-lspace: 0pt;
mso-table-rspace: 0pt;
}
img {
-ms-interpolation-mode: bicubic;
}
.hidden {
display: none !important;
visibility: hidden !important;
}
/* iOS BLUE LINKS */
a[x-apple-data-detectors] {
color: inherit !important;
text-decoration: none !important;
font-size: inherit !important;
font-family: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
}
/* ANDROID MARGIN HACK */
div[style*='margin: 16px 0'] {
margin: 0 !important;
}
@media only screen and (max-width: 639px) {
body,
#body {
min-width: 320px !important;
}
table.wrapper {
width: 100% !important;
min-width: 320px !important;
}
table.wrapper td.wrapper-cell {
border-left: 0 !important;
border-right: 0 !important;
border-radius: 0 !important;
padding-left: 10px !important;
padding-right: 10px !important;
}
}
...@@ -270,7 +270,7 @@ ...@@ -270,7 +270,7 @@
} }
.count-badge, .count-badge,
.btn { .btn-xs {
height: 24px; height: 24px;
} }
......
...@@ -324,6 +324,15 @@ module ApplicationHelper ...@@ -324,6 +324,15 @@ module ApplicationHelper
} }
end end
def asset_to_string(name)
app = Rails.application
if Rails.configuration.assets.compile
app.assets.find_asset(name).to_s
else
controller.view_context.render(file: File.join('public/assets', app.assets_manifest.assets[name]))
end
end
private private
def appearance def appearance
......
...@@ -15,16 +15,18 @@ module Emails ...@@ -15,16 +15,18 @@ module Emails
user = User.find(recipient_id) user = User.find(recipient_id)
mail(to: user.notification_email_for(notification_group), member_email_with_layout(
subject: subject("Request to join the #{member_source.human_name} #{member_source.model_name.singular}")) to: user.notification_email_for(notification_group),
subject: subject("Request to join the #{member_source.human_name} #{member_source.model_name.singular}"))
end end
def member_access_granted_email(member_source_type, member_id) def member_access_granted_email(member_source_type, member_id)
@member_source_type = member_source_type @member_source_type = member_source_type
@member_id = member_id @member_id = member_id
mail(to: member.user.notification_email_for(notification_group), member_email_with_layout(
subject: subject("Access to the #{member_source.human_name} #{member_source.model_name.singular} was granted")) to: member.user.notification_email_for(notification_group),
subject: subject("Access to the #{member_source.human_name} #{member_source.model_name.singular} was granted"))
end end
def member_access_denied_email(member_source_type, source_id, user_id) def member_access_denied_email(member_source_type, source_id, user_id)
...@@ -33,8 +35,9 @@ module Emails ...@@ -33,8 +35,9 @@ module Emails
user = User.find(user_id) user = User.find(user_id)
mail(to: user.notification_email_for(notification_group), member_email_with_layout(
subject: subject("Access to the #{member_source.human_name} #{member_source.model_name.singular} was denied")) to: user.notification_email_for(notification_group),
subject: subject("Access to the #{member_source.human_name} #{member_source.model_name.singular} was denied"))
end end
def member_invited_email(member_source_type, member_id, token) def member_invited_email(member_source_type, member_id, token)
...@@ -42,8 +45,9 @@ module Emails ...@@ -42,8 +45,9 @@ module Emails
@member_id = member_id @member_id = member_id
@token = token @token = token
mail(to: member.invite_email, member_email_with_layout(
subject: subject("Invitation to join the #{member_source.human_name} #{member_source.model_name.singular}")) to: member.invite_email,
subject: subject("Invitation to join the #{member_source.human_name} #{member_source.model_name.singular}"))
end end
def member_invite_accepted_email(member_source_type, member_id) def member_invite_accepted_email(member_source_type, member_id)
...@@ -51,8 +55,9 @@ module Emails ...@@ -51,8 +55,9 @@ module Emails
@member_id = member_id @member_id = member_id
return unless member.created_by return unless member.created_by
mail(to: member.created_by.notification_email_for(notification_group), member_email_with_layout(
subject: subject('Invitation accepted')) to: member.created_by.notification_email_for(notification_group),
subject: subject('Invitation accepted'))
end end
def member_invite_declined_email(member_source_type, source_id, invite_email, created_by_id) def member_invite_declined_email(member_source_type, source_id, invite_email, created_by_id)
...@@ -64,8 +69,9 @@ module Emails ...@@ -64,8 +69,9 @@ module Emails
user = User.find(created_by_id) user = User.find(created_by_id)
mail(to: user.notification_email_for(notification_group), member_email_with_layout(
subject: subject('Invitation declined')) to: user.notification_email_for(notification_group),
subject: subject('Invitation declined'))
end end
def member def member
...@@ -85,5 +91,12 @@ module Emails ...@@ -85,5 +91,12 @@ module Emails
def member_source_class def member_source_class
@member_source_type.classify.constantize @member_source_type.classify.constantize
end end
def member_email_with_layout(to:, subject:)
mail(to: to, subject: subject) do |format|
format.html { render layout: 'mailer' }
format.text { render layout: 'mailer' }
end
end
end end
end end
...@@ -18,12 +18,11 @@ module Emails ...@@ -18,12 +18,11 @@ module Emails
@merge_request = pipeline.all_merge_requests.first @merge_request = pipeline.all_merge_requests.first
add_headers add_headers
# We use bcc here because we don't want to generate this emails for a # We use bcc here because we don't want to generate these emails for a
# thousand times. This could be potentially expensive in a loop, and # thousand times. This could be potentially expensive in a loop, and
# recipients would contain all project watchers so it could be a lot. # recipients would contain all project watchers so it could be a lot.
mail(bcc: recipients, mail(bcc: recipients,
subject: pipeline_subject(status), subject: pipeline_subject(status)) do |format|
skip_premailer: true) do |format|
format.html { render layout: 'mailer' } format.html { render layout: 'mailer' }
format.text { render layout: 'mailer' } format.text { render layout: 'mailer' }
end end
......
...@@ -21,7 +21,13 @@ module Emails ...@@ -21,7 +21,13 @@ module Emails
private private
def release_email_subject def release_email_subject
release_info = [@release.name, @release.tag].select(&:presence).join(' - ') release_info =
if @release.name == @release.tag
@release.tag
else
[@release.name, @release.tag].select(&:presence).join(' - ')
end
"New release: #{release_info}" "New release: #{release_info}"
end end
end end
......
...@@ -77,7 +77,7 @@ class NotifyPreview < ActionMailer::Preview ...@@ -77,7 +77,7 @@ class NotifyPreview < ActionMailer::Preview
end end
def import_issues_csv_email def import_issues_csv_email
Notify.import_issues_csv_email(user, project, { success: 3, errors: [5, 6, 7], valid_file: true }) Notify.import_issues_csv_email(user.id, project.id, { success: 3, errors: [5, 6, 7], valid_file: true })
end end
def closed_merge_request_email def closed_merge_request_email
...@@ -109,11 +109,11 @@ class NotifyPreview < ActionMailer::Preview ...@@ -109,11 +109,11 @@ class NotifyPreview < ActionMailer::Preview
end end
def member_access_requested_email def member_access_requested_email
Notify.member_access_requested_email('group', user.id, user.id).message Notify.member_access_requested_email(member.source_type, member.id, user.id).message
end end
def member_invite_accepted_email def member_invite_accepted_email
Notify.member_invite_accepted_email('project', user.id).message Notify.member_invite_accepted_email(member.source_type, member.id).message
end end
def member_invite_declined_email def member_invite_declined_email
......
...@@ -69,6 +69,10 @@ class Release < ApplicationRecord ...@@ -69,6 +69,10 @@ class Release < ApplicationRecord
released_at.present? && released_at > Time.zone.now released_at.present? && released_at > Time.zone.now
end end
def name
self.read_attribute(:name) || tag
end
private private
def actual_sha def actual_sha
......
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
%html{ lang: "en" } %html{ lang: "en" }
%head %head
%meta{ content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type" }/ %meta{ content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type" }/
%meta{ content: "width=device-width, initial-scale=1", name: "viewport" }/ %meta{ content: "width=device-width, initial-scale=1", name: "viewport" }/
%meta{ content: "IE=edge", "http-equiv" => "X-UA-Compatible" }/ %meta{ content: "IE=edge", "http-equiv" => "X-UA-Compatible" }/
%title= message.subject %title= message.subject
:css
/* CLIENT-SPECIFIC STYLES */
body, table, td, a { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
img { -ms-interpolation-mode: bicubic; }
.hidden {
display: none !important;
visibility: hidden !important;
}
/* iOS BLUE LINKS */ -# Avoid premailer processing of client-specific styles (@media tag not supported)
a[x-apple-data-detectors] { -# We need to inline the contents here because mail clients (e.g. iOS Mail, Outlook)
color: inherit !important; -# do not support linked stylesheets.
text-decoration: none !important; %style{ type: 'text/css', 'data-premailer': 'ignore' }
font-size: inherit !important; = asset_to_string('mailer_client_specific.css').html_safe
font-family: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
}
/* ANDROID MARGIN HACK */ = stylesheet_link_tag 'mailer.css'
body { margin:0 !important; } %body
div[style*="margin: 16px 0"] { margin:0 !important; } %table#body{ border: "0", cellpadding: "0", cellspacing: "0" }
@media only screen and (max-width: 639px) {
body, #body {
min-width: 320px !important;
}
table.wrapper {
width: 100% !important;
min-width: 320px !important;
}
table.wrapper > tbody > tr > td {
border-left: 0 !important;
border-right: 0 !important;
border-radius: 0 !important;
padding-left: 10px !important;
padding-right: 10px !important;
}
}
%body{ style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
%table#body{ border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;" }
%tbody %tbody
%tr.line %tr.line
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;" } %td
%tr.header %tr.header
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" } %td
= html_header_message = html_header_message
= header_logo = header_logo
%tr %tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" } %td
%table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;" } %table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0" }
%tbody %tbody
%tr %tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" } %td.wrapper-cell
%table.content{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;" } %table.content{ border: "0", cellpadding: "0", cellspacing: "0" }
%tbody %tbody
= yield = yield
= render_if_exists 'layouts/mailer/additional_text' = render_if_exists 'layouts/mailer/additional_text'
%tr.footer %tr.footer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" } %td
%img{ alt: "GitLab", height: "33", src: image_url('mailers/gitlab_footer_logo.gif'), style: "display:block;margin:0 auto 1em;", width: "90" }/ %img{ alt: "GitLab", height: "33", width: "90", src: image_url('mailers/gitlab_footer_logo.gif') }
%div %div
- manage_notifications_link = link_to(_("Manage all notifications"), profile_notifications_url, style: "color:#3777b0;text-decoration:none;") - manage_notifications_link = link_to(_("Manage all notifications"), profile_notifications_url, class: 'mng-notif-link')
- help_link = link_to(_("Help"), help_url, style: "color:#3777b0;text-decoration:none;") - help_link = link_to(_("Help"), help_url, class: 'help-link')
= _("You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}").html_safe % { host: Gitlab.config.gitlab.host, manage_notifications_link: manage_notifications_link, help_link: help_link } = _("You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}").html_safe % { host: Gitlab.config.gitlab.host, manage_notifications_link: manage_notifications_link, help_link: help_link }
= yield :additional_footer = yield :additional_footer
%tr %tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" } %td.footer-message
= html_footer_message = html_footer_message
%p %tr
Your request to join the %td.text-content
#{link_to member_source.human_name, member_source.web_url} #{member_source.model_name.singular} %p
has been denied. Your request to join the
#{link_to member_source.human_name, member_source.web_url, class: :highlight} #{member_source.model_name.singular}
has been #{content_tag :span, 'denied', class: :highlight}.
- link_end = '</a>'.html_safe - link_end = '</a>'.html_safe
- source_type = member_source.model_name.singular - source_type = member_source.model_name.singular
- leave_link = polymorphic_url([member_source], leave: 1) - leave_link = polymorphic_url([member_source], leave: 1)
- source_link = link_to(member_source.human_name, member_source.web_url, target: '_blank', rel: 'noopener noreferrer') - source_link = link_to(member_source.human_name, member_source.web_url, target: '_blank', rel: 'noopener noreferrer', class: :highlight)
- access_level = content_tag(:span, member.human_access, class: :highlight)
%tr
%td.text-content
%p
= _('You have been granted %{access_level} access to the %{source_link} %{source_type}.').html_safe % { access_level: access_level, source_link: source_link, source_type: source_type }
%p
- leave_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: leave_link }
= _('If this was a mistake you can %{leave_link_start}leave the %{source_type}%{link_end}.').html_safe % { source_type: source_type, leave_link_start: leave_link_start, link_end: link_end }
%p
= _('You have been granted %{access_level} access to the %{source_link} %{source_type}.').html_safe % { access_level: member.human_access, source_link: source_link, source_type: source_type }
%p
- leave_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: leave_link }
= _('If this was a mistake you can %{leave_link_start}leave the %{source_type}%{link_end}.').html_safe % { source_type: source_type, leave_link_start: leave_link_start, link_end: link_end }
%p %tr
#{link_to member.user.name, member.user} requested #{member.human_access} %td.text-content
access to the #{link_to member_source.human_name, polymorphic_url([member_source, :members])} #{member_source.model_name.singular}. %p
#{link_to member.user.name, member.user, class: :highlight} requested #{content_tag :span, member.human_access, class: :highlight}
access to the #{link_to member_source.human_name, polymorphic_url([member_source, :members]), class: :highlight} #{member_source.model_name.singular}.
%p %tr
#{member.invite_email}, now known as %td.text-content
#{link_to member.user.name, user_url(member.user)}, %p
has accepted your invitation to join the #{content_tag :span, member.invite_email, class: :highlight}, now known as
#{link_to member_source.human_name, member_source.web_url} #{member_source.model_name.singular}. #{link_to member.user.name, user_url(member.user)},
has accepted your invitation to join the
#{link_to member_source.human_name, member_source.web_url, class: :highlight} #{member_source.model_name.singular}.
%p %tr
#{@invite_email} %td.text-content
has declined your invitation to join the %p
#{link_to member_source.human_name, member_source.web_url} #{member_source.model_name.singular}. #{content_tag :span, @invite_email, class: :highlight}
has #{content_tag :span, 'declined', class: :highlight} your invitation to join the
#{link_to member_source.human_name, member_source.web_url, class: :highlight} #{member_source.model_name.singular}.
%p %tr
You have been invited %td.text-content
- if member.created_by %p
by You have been invited
= link_to member.created_by.name, user_url(member.created_by) - if member.created_by
to join the by
= link_to member_source.human_name, member_source.public? ? member_source.web_url : invite_url(@token) = link_to member.created_by.name, user_url(member.created_by)
#{member_source.model_name.singular} as #{member.human_access}. to join the
= link_to member_source.human_name, member_source.public? ? member_source.web_url : invite_url(@token), class: :highlight
#{member_source.model_name.singular} as #{content_tag :span, member.human_access, class: :highlight}.
%p
= link_to 'Accept invitation', invite_url(@token)
or
= link_to 'decline', decline_invite_url(@token)
%p
= link_to 'Accept invitation', invite_url(@token)
or
= link_to 'decline', decline_invite_url(@token)
...@@ -6,17 +6,18 @@ ...@@ -6,17 +6,18 @@
= render 'projects/blob/viewer_switcher', blob: blob unless blame = render 'projects/blob/viewer_switcher', blob: blob unless blame
.btn-group{ role: "group" }< .btn-group{ role: "group" }<
= copy_blob_source_button(blob) unless blame
= open_raw_blob_button(blob)
= download_blob_button(blob)
= view_on_environment_button(@commit.sha, @path, @environment) if @environment
.btn-group{ role: "group" }<
= render_if_exists 'projects/blob/header_file_locks_link'
= edit_blob_button = edit_blob_button
= ide_edit_button = ide_edit_button
.btn-group{ role: "group" }<
= render_if_exists 'projects/blob/header_file_locks_link'
- if current_user - if current_user
= replace_blob_link = replace_blob_link
= delete_blob_link = delete_blob_link
.btn-group{ role: "group" }<
= copy_blob_source_button(blob) unless blame
= open_raw_blob_button(blob)
= download_blob_button(blob)
= view_on_environment_button(@commit.sha, @path, @environment) if @environment
= render 'projects/fork_suggestion' = render 'projects/fork_suggestion'
= render_if_exists 'projects/blob/header_file_locks', project: @project, path: @path = render_if_exists 'projects/blob/header_file_locks', project: @project, path: @path
---
title: Made `name` optional parameter of Release entity
merge_request: 19705
author:
type: changed
---
title: Revert btn-xs styling in projects scss
merge_request: 19640
author:
type: fixed
---
title: Visual design for edit buttons in blob view
merge_request: 19932
author:
type: other
---
title: Unify html email layout for member html emails
merge_request: 17699
author: Diego Louzán
type: added
...@@ -157,6 +157,8 @@ module Gitlab ...@@ -157,6 +157,8 @@ module Gitlab
config.assets.paths << "#{config.root}/vendor/assets/fonts" config.assets.paths << "#{config.root}/vendor/assets/fonts"
config.assets.precompile << "print.css" config.assets.precompile << "print.css"
config.assets.precompile << "mailer.css"
config.assets.precompile << "mailer_client_specific.css"
config.assets.precompile << "notify.css" config.assets.precompile << "notify.css"
config.assets.precompile << "mailers/*.css" config.assets.precompile << "mailers/*.css"
config.assets.precompile << "page_bundles/ide.css" config.assets.precompile << "page_bundles/ide.css"
......
...@@ -303,7 +303,7 @@ POST /projects/:id/releases ...@@ -303,7 +303,7 @@ POST /projects/:id/releases
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| -------------------| --------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- | | -------------------| --------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../README.md#namespaced-path-encoding). | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](../README.md#namespaced-path-encoding). |
| `name` | string | yes | The release name. | | `name` | string | no | The release name. |
| `tag_name` | string | yes | The tag where the release will be created from. | | `tag_name` | string | yes | The tag where the release will be created from. |
| `description` | string | yes | The description of the release. You can use [markdown](../../user/markdown.md). | | `description` | string | yes | The description of the release. You can use [markdown](../../user/markdown.md). |
| `ref` | string | yes, if `tag_name` doesn't exist | If `tag_name` doesn't exist, the release will be created from `ref`. It can be a commit SHA, another tag name, or a branch name. | | `ref` | string | yes, if `tag_name` doesn't exist | If `tag_name` doesn't exist, the release will be created from `ref`. It can be a commit SHA, another tag name, or a branch name. |
......
...@@ -38,7 +38,7 @@ that's tested and deployed on every push to the `master` branch of the [codebase ...@@ -38,7 +38,7 @@ that's tested and deployed on every push to the `master` branch of the [codebase
This will also provide This will also provide
boilerplate code for starting a browser-based game with the following components: boilerplate code for starting a browser-based game with the following components:
- Written in [Typescript](https://www.typescriptlang.org/) and [PhaserJs](https://phaser.io) - Written in [TypeScript](https://www.typescriptlang.org/) and [PhaserJs](https://phaser.io)
- Building, running, and testing with [Gulp](https://gulpjs.com) - Building, running, and testing with [Gulp](https://gulpjs.com)
- Unit tests with [Chai](https://www.chaijs.com) and [Mocha](https://mochajs.org/) - Unit tests with [Chai](https://www.chaijs.com) and [Mocha](https://mochajs.org/)
- CI/CD with GitLab - CI/CD with GitLab
...@@ -508,7 +508,7 @@ deploy: ...@@ -508,7 +508,7 @@ deploy:
## Conclusion ## Conclusion
Within the [demo repository](https://gitlab.com/blitzgren/gitlab-game-demo) you can also find a handful of boilerplate code to get Within the [demo repository](https://gitlab.com/blitzgren/gitlab-game-demo) you can also find a handful of boilerplate code to get
[Typescript](https://www.typescriptlang.org/), [Mocha](https://mochajs.org/), [Gulp](https://gulpjs.com/) and [Phaser](https://phaser.io) all playing [TypeScript](https://www.typescriptlang.org/), [Mocha](https://mochajs.org/), [Gulp](https://gulpjs.com/) and [Phaser](https://phaser.io) all playing
together nicely with GitLab CI/CD, which is the result of lessons learned while making [Dark Nova](https://www.darknova.io). together nicely with GitLab CI/CD, which is the result of lessons learned while making [Dark Nova](https://www.darknova.io).
Using a combination of free and open source software, we have a full CI/CD pipeline, a game foundation, Using a combination of free and open source software, we have a full CI/CD pipeline, a game foundation,
and unit tests, all running and deployed at every push to master - with shockingly little code. and unit tests, all running and deployed at every push to master - with shockingly little code.
......
...@@ -268,7 +268,7 @@ Each issue scheduled for the current milestone should be labeled ~Deliverable ...@@ -268,7 +268,7 @@ Each issue scheduled for the current milestone should be labeled ~Deliverable
or ~"Stretch". Any open issue for a previous milestone should be labeled or ~"Stretch". Any open issue for a previous milestone should be labeled
~"Next Patch Release", or otherwise rescheduled to a different milestone. ~"Next Patch Release", or otherwise rescheduled to a different milestone.
#### Priority labels ### Priority labels
Priority labels help us define the time a ~bug fix should be completed. Priority determines how quickly the defect turnaround time must be. Priority labels help us define the time a ~bug fix should be completed. Priority determines how quickly the defect turnaround time must be.
If there are multiple defects, the priority decides which defect has to be fixed immediately versus later. If there are multiple defects, the priority decides which defect has to be fixed immediately versus later.
......
...@@ -25,7 +25,7 @@ SAST supports the following official analyzers: ...@@ -25,7 +25,7 @@ SAST supports the following official analyzers:
- [`security-code-scan`](https://gitlab.com/gitlab-org/security-products/analyzers/security-code-scan) (Security Code Scan (.NET)) - [`security-code-scan`](https://gitlab.com/gitlab-org/security-products/analyzers/security-code-scan) (Security Code Scan (.NET))
- [`sobelow`](https://gitlab.com/gitlab-org/security-products/analyzers/sobelow) (Sobelow (Elixir Phoenix)) - [`sobelow`](https://gitlab.com/gitlab-org/security-products/analyzers/sobelow) (Sobelow (Elixir Phoenix))
- [`spotbugs`](https://gitlab.com/gitlab-org/security-products/analyzers/spotbugs) (SpotBugs with the Find Sec Bugs plugin (Ant, Gradle and wrapper, Grails, Maven and wrapper, SBT)) - [`spotbugs`](https://gitlab.com/gitlab-org/security-products/analyzers/spotbugs) (SpotBugs with the Find Sec Bugs plugin (Ant, Gradle and wrapper, Grails, Maven and wrapper, SBT))
- [`tslint`](https://gitlab.com/gitlab-org/security-products/analyzers/tslint) (TSLint (Typescript)) - [`tslint`](https://gitlab.com/gitlab-org/security-products/analyzers/tslint) (TSLint (TypeScript))
The analyzers are published as Docker images that SAST will use to launch The analyzers are published as Docker images that SAST will use to launch
dedicated containers for each analysis. dedicated containers for each analysis.
......
...@@ -78,7 +78,7 @@ The following table shows which languages, package managers and frameworks are s ...@@ -78,7 +78,7 @@ The following table shows which languages, package managers and frameworks are s
| Python ([pip](https://pip.pypa.io/en/stable/)) | [bandit](https://github.com/PyCQA/bandit) | 10.3 | | Python ([pip](https://pip.pypa.io/en/stable/)) | [bandit](https://github.com/PyCQA/bandit) | 10.3 |
| Ruby on Rails | [brakeman](https://brakemanscanner.org) | 10.3 | | Ruby on Rails | [brakeman](https://brakemanscanner.org) | 10.3 |
| Scala ([Ant](https://ant.apache.org/), [Gradle](https://gradle.org/), [Maven](https://maven.apache.org/) and [SBT](https://www.scala-sbt.org/)) | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 11.0 (SBT) & 11.9 (Ant, Gradle, Maven) | | Scala ([Ant](https://ant.apache.org/), [Gradle](https://gradle.org/), [Maven](https://maven.apache.org/) and [SBT](https://www.scala-sbt.org/)) | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 11.0 (SBT) & 11.9 (Ant, Gradle, Maven) |
| Typescript | [TSLint config security](https://github.com/webschik/tslint-config-security/) | 11.9 | | TypeScript | [TSLint config security](https://github.com/webschik/tslint-config-security/) | 11.9 |
NOTE: **Note:** NOTE: **Note:**
The Java analyzers can also be used for variants like the The Java analyzers can also be used for variants like the
......
...@@ -1299,7 +1299,9 @@ module API ...@@ -1299,7 +1299,9 @@ module API
class Release < Grape::Entity class Release < Grape::Entity
include ::API::Helpers::Presentable include ::API::Helpers::Presentable
expose :name expose :name do |release, _|
can_download_code? ? release.name : "Release-#{release.id}"
end
expose :tag, as: :tag_name, if: ->(_, _) { can_download_code? } expose :tag, as: :tag_name, if: ->(_, _) { can_download_code? }
expose :description expose :description
expose :description_html do |entity| expose :description_html do |entity|
......
...@@ -45,7 +45,7 @@ module API ...@@ -45,7 +45,7 @@ module API
end end
params do params do
requires :tag_name, type: String, desc: 'The name of the tag', as: :tag requires :tag_name, type: String, desc: 'The name of the tag', as: :tag
requires :name, type: String, desc: 'The name of the release' optional :name, type: String, desc: 'The name of the release'
requires :description, type: String, desc: 'The release notes' requires :description, type: String, desc: 'The release notes'
optional :ref, type: String, desc: 'The commit sha or branch name' optional :ref, type: String, desc: 'The commit sha or branch name'
optional :assets, type: Hash do optional :assets, type: Hash do
......
{ {
"type": "object", "type": "object",
"required": ["name", "tag_name"], "required": ["tag_name", "description"],
"properties": { "properties": {
"name": { "type": "string" }, "name": { "type": "string" },
"tag_name": { "type": "string" }, "tag_name": { "type": "string" },
"ref": { "type": "string "},
"description": { "type": "string" }, "description": { "type": "string" },
"description_html": { "type": "string" }, "description_html": { "type": "string" },
"created_at": { "type": "date" }, "created_at": { "type": "date" },
......
...@@ -18,6 +18,7 @@ describe Emails::Releases do ...@@ -18,6 +18,7 @@ describe Emails::Releases do
context 'when the release has a name' do context 'when the release has a name' do
it 'shows the correct subject' do it 'shows the correct subject' do
release.name = 'beta-1'
expected_subject = "#{release.project.name} | New release: #{release.name} - #{release.tag}" expected_subject = "#{release.project.name} | New release: #{release.name} - #{release.tag}"
is_expected.to have_subject(expected_subject) is_expected.to have_subject(expected_subject)
end end
......
...@@ -27,7 +27,7 @@ describe Evidence do ...@@ -27,7 +27,7 @@ describe Evidence do
let(:release) { create(:release, project: project, name: nil) } let(:release) { create(:release, project: project, name: nil) }
it 'creates a valid JSON object' do it 'creates a valid JSON object' do
expect(release.name).to be_nil expect(release.name).to eq(release.tag)
expect(summary_json).to match_schema(schema_file) expect(summary_json).to match_schema(schema_file)
end end
end end
......
...@@ -34,7 +34,7 @@ RSpec.describe Release do ...@@ -34,7 +34,7 @@ RSpec.describe Release do
expect(existing_release_without_name).to be_valid expect(existing_release_without_name).to be_valid
expect(existing_release_without_name.description).to eq("change") expect(existing_release_without_name.description).to eq("change")
expect(existing_release_without_name.name).to be_nil expect(existing_release_without_name.name).not_to be_nil
end end
end end
...@@ -129,4 +129,16 @@ RSpec.describe Release do ...@@ -129,4 +129,16 @@ RSpec.describe Release do
end end
end end
end end
describe '#name' do
context 'name is nil' do
before do
release.update(name: nil)
end
it 'returns tag' do
expect(release.name).to eq(release.tag)
end
end
end
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