BigW Consortium Gitlab

Commit a3fdd76d by Olaf Tomalka

Merge branch 'master' into feature/fork-to-group-api

parents 636db151 1e042ac2
......@@ -248,6 +248,21 @@ bundler:audit:
script:
- "bundle exec bundle-audit check --update --ignore OSVDB-115941"
migration paths:
stage: test
<<: *use-db
only:
- master@gitlab-org/gitlab-ce
script:
- git checkout HEAD .
- git fetch --tags
- git checkout v8.5.9
- 'echo test: unix:/var/opt/gitlab/redis/redis.socket > config/resque.yml'
- bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}" --retry=3
- rake db:drop db:create db:schema:load db:seed_fu
- git checkout $CI_BUILD_REF
- rake db:migrate
coverage:
stage: post-test
services: []
......@@ -263,7 +278,6 @@ coverage:
- coverage/index.html
- coverage/assets/
# Notify slack in the end
notify:slack:
......
......@@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.12.0 (unreleased)
- Add ability to fork to a specific namespace using API. (ritave)
- Cleanup misalignments in Issue list view !6206
- Prepend blank line to `Closes` message on merge request linked to issue (lukehowell)
- Filter tags by name !6121
- Make push events have equal vertical spacing.
......@@ -10,6 +11,7 @@ v 8.12.0 (unreleased)
- Add font color contrast to external label in admin area (ClemMakesApps)
- Change logo animation to CSS (ClemMakesApps)
- Instructions for enabling Git packfile bitmaps !6104
- Fix pagination on user snippets page
- Change merge_error column from string to text type
- Reduce contributions calendar data payload (ClemMakesApps)
- Add `web_url` field to issue, merge request, and snippet API objects (Ben Boeckel)
......@@ -25,6 +27,7 @@ v 8.12.0 (unreleased)
- Add white background for no readme container (ClemMakesApps)
- API: Expose issue confidentiality flag. (Robert Schilling)
- Fix markdown anchor icon interaction (ClemMakesApps)
- Test migration paths from 8.5 until current release !4874
- Optimistic locking for Issues and Merge Requests (title and description overriding prevention)
- Add `wiki_page_events` to project hook APIs (Ben Boeckel)
- Remove Gitorious import
......@@ -83,14 +86,19 @@ v 8.12.0 (unreleased)
- Adds response mime type to transaction metric action when it's not HTML
- Fix hover leading space bug in pipeline graph !5980
- User can edit closed MR with deleted fork (Katarzyna Kobierska Ula Budziszewska) !5496
- Fix repository page ui issues
- Fixed invisible scroll controls on build page on iPhone
- Fix error on raw build trace download for old builds stored in database !4822
- Refactor the triggers page and documentation !6217
- Show values of CI trigger variables only when clicked (Katarzyna Kobierska Ula Budziszewska)
- Use default clone protocol on "check out, review, and merge locally" help page URL
v 8.11.5 (unreleased)
- Optimize branch lookups and force a repository reload for Repository#find_branch
- Fix member expiration date picker after update
- Fix suggested colors options for new labels in the admin area. !6138
- Fix GitLab import button
- Remove gitorious from import_sources
v 8.11.4
- Fix resolving conflicts on forks. !6082
......
......@@ -53,7 +53,7 @@ gem 'browser', '~> 2.2'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
gem 'gitlab_git', '~> 10.5'
gem 'gitlab_git', '~> 10.6.3'
# LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes
......
......@@ -279,7 +279,7 @@ GEM
diff-lcs (~> 1.1)
mime-types (>= 1.16, < 3)
posix-spawn (~> 0.3)
gitlab_git (10.5.0)
gitlab_git (10.6.3)
activesupport (~> 4.0)
charlock_holmes (~> 0.7.3)
github-linguist (~> 4.7.0)
......@@ -858,7 +858,7 @@ DEPENDENCIES
github-linguist (~> 4.7.0)
github-markup (~> 1.4)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab_git (~> 10.5)
gitlab_git (~> 10.6.3)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.2)
......
Vue.http.interceptors.push((request, next) => {
Vue.http.interceptors.push((request, next) => {
Vue.activeResources = Vue.activeResources ? Vue.activeResources + 1 : 1;
Vue.nextTick(() => {
setTimeout(() => {
Vue.activeResources--;
}, 500);
next(function (response) {
Vue.activeResources--;
});
next();
});
......@@ -54,12 +54,14 @@
}
Build.prototype.getInitialBuildTrace = function() {
var removeRefreshStatuses = ['success', 'failed', 'canceled', 'skipped']
return $.ajax({
url: this.build_url,
dataType: 'json',
success: function(build_data) {
$('.js-build-output').html(build_data.trace_html);
if (build_data.status === 'success' || build_data.status === 'failed') {
if (removeRefreshStatuses.indexOf(build_data.status) >= 0) {
return $('.js-build-refresh').remove();
}
}
......
$(function(){
$('.reveal-variables').off('click').on('click',function(){
$('.js-build').toggle().niceScroll();
$(this).hide();
});
});
(global => {
global.gl = global.gl || {};
gl.SnippetsList = function() {
var $holder = $('.snippets-list-holder');
$holder.find('.pagination').on('ajax:success', (e, data) => {
$holder.replaceWith(data.html);
});
}
})(window);
......@@ -138,7 +138,7 @@
}
li a {
padding: 16px 10px 11px;
padding: 16px 15px 11px;
}
/* Small devices (phones, tablets, 768px and lower) */
......
......@@ -101,6 +101,7 @@ $gl-icon-color: $gl-placeholder-color;
$gl-grayish-blue: #7f8fa4;
$gl-gray: $gl-text-color;
$gl-gray-dark: #313236;
$gl-gray-light: $gl-placeholder-color;
$gl-header-color: $gl-title-color;
/*
......
......@@ -115,6 +115,16 @@
width: 100%;
}
.js-build-variable {
color: $code-color;
}
.js-build-value {
padding: 2px 4px;
color: $black;
background-color: $white-light;
}
.build-sidebar-header {
padding: 0 $gl-padding $gl-padding;
......
......@@ -18,8 +18,7 @@
}
.commit-row-title {
line-height: 1;
margin-bottom: 7px;
line-height: 1.35;
.notes_count {
float: right;
......@@ -43,6 +42,7 @@
border: 1px solid $border-gray-dark;
border-radius: $border-radius-default;
margin-left: 5px;
line-height: 1;
&:hover {
background-color: darken($gray-light, 10%);
......@@ -136,7 +136,7 @@
.commit-row-info {
color: $gl-gray;
line-height: 1;
line-height: 1.35;
a {
color: $gl-gray;
......
......@@ -9,7 +9,7 @@
.issue-check {
float: left;
padding-right: 8px;
padding-right: 16px;
margin-bottom: 10px;
min-width: 15px;
......
......@@ -15,16 +15,6 @@
vertical-align: top;
}
.last-commit {
max-width: 506px;
.last-commit-content {
@include str-truncated;
width: calc(100% - 140px);
margin-left: 3px;
}
}
.tree-table {
margin-bottom: 0;
......@@ -36,6 +26,15 @@
line-height: 21px;
}
.last-commit {
@include str-truncated(60%);
}
.commit-history-link-spacer {
margin: 0 10px;
color: $table-border-color;
}
&:hover {
td {
background-color: $row-hover;
......@@ -91,11 +90,17 @@
}
}
.tree_commit {
color: $gl-gray;
.tree-time-ago {
min-width: 135px;
color: $gl-gray-light;
}
.tree-commit {
max-width: 320px;
color: $gl-gray-light;
.tree-commit-link {
color: $gl-gray;
color: $gl-gray-light;
&:hover {
text-decoration: underline;
......
......@@ -90,12 +90,13 @@
- if @build.trigger_request.variables
%p
%span.build-light-text Variables:
%button.btn.group.btn-group-justified.reveal-variables Reveal Variables
- @build.trigger_request.variables.each do |key, value|
%code
#{key}=#{value}
.hide.js-build
.js-build-variable= key
.js-build-value= value
.block
.title
......
......@@ -12,7 +12,7 @@
%pre.dark#merge-info-1
- if @merge_request.for_fork?
:preserve
git fetch #{h @merge_request.source_project.http_url_to_repo} #{h @merge_request.source_branch}
git fetch #{h default_url_to_repo(@merge_request.source_project)} #{h @merge_request.source_branch}
git checkout -b #{h @merge_request.source_project_path}-#{h @merge_request.source_branch} FETCH_HEAD
- else
:preserve
......
......@@ -5,8 +5,8 @@
:plain
var row = $("table.table_#{@hex_path} tr.file_#{hexdigest(file_name)}");
row.find("td.tree_time_ago").html('#{escape_javascript time_ago_with_tooltip(commit.committed_date)}');
row.find("td.tree_commit").html('#{escape_javascript render("projects/tree/tree_commit_column", commit: commit)}');
row.find("td.tree-time-ago").html('#{escape_javascript time_ago_with_tooltip(commit.committed_date)}');
row.find("td.tree-commit").html('#{escape_javascript render("projects/tree/tree_commit_column", commit: commit)}');
- if @more_log_url
:plain
......
......@@ -4,6 +4,6 @@
- file_name = blob_item.name
= link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@id || @commit.id, blob_item.name)), title: file_name do
%span.str-truncated= file_name
%td.tree_time_ago.cgray
= render 'projects/tree/spinner'
%td.hidden-xs.tree_commit
%td.hidden-xs.tree-commit
%td.tree-time-ago.cgray.text-right
= render 'projects/tree/spinner'
\ No newline at end of file
......@@ -4,20 +4,20 @@
%thead
%tr
%th Name
%th Last Update
%th.hidden-xs.last-commit
Last Commit
.last-commit-content.hidden-sm
%th.hidden-xs
.pull-left Last Commit
.last-commit.hidden-sm.pull-left
&nbsp;
%i.fa.fa-angle-right
&nbsp;
%small.light
= link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace"
&ndash;
= time_ago_with_tooltip(@commit.committed_date)
&ndash;
= @commit.full_title
= link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), class: 'pull-right'
%small.commit-history-link-spacer &#124;
= link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), class: 'commit-history-link'
%th.text-right Last Update
- if @path.present?
%tr.tree-item
%td.tree-item-file-name
......
......@@ -4,6 +4,6 @@
- path = flatten_tree(tree_item)
= link_to namespace_project_tree_path(@project.namespace, @project, tree_join(@id || @commit.id, path)), title: path do
%span.str-truncated= path
%td.tree_time_ago.cgray
= render 'projects/tree/spinner'
%td.hidden-xs.tree_commit
%td.hidden-xs.tree-commit
%td.tree-time-ago.text-right
= render 'projects/tree/spinner'
\ No newline at end of file
......@@ -4,65 +4,89 @@
.col-lg-3
%h4.prepend-top-0
= page_title
%p
Triggers can force a specific branch or tag to rebuild with an API call.
%p.prepend-top-20
Triggers can force a specific branch or tag to get rebuilt with an API call.
%p.append-bottom-0
= succeed '.' do
Learn more in the
= link_to 'triggers documentation', help_page_path('ci/triggers/README'), target: '_blank'
.col-lg-9
%h5.prepend-top-0
Your triggers
- if @triggers.any?
.table-responsive
%table.table
%thead
%th Token
%th Last used
%th
= render partial: 'trigger', collection: @triggers, as: :trigger
- else
%p.settings-message.text-center.append-bottom-default
No triggers have been created yet. Add one using the button below.
.panel.panel-default
.panel-heading
%h4.panel-title
Manage your project's triggers
.panel-body
- if @triggers.any?
.table-responsive
%table.table
%thead
%th
%strong Token
%th
%strong Last used
%th
= render partial: 'trigger', collection: @triggers, as: :trigger
- else
%p.settings-message.text-center.append-bottom-default
No triggers have been created yet. Add one using the button below.
= form_for @trigger, url: url_for(controller: 'projects/triggers', action: 'create') do |f|
= f.submit "Add Trigger", class: 'btn btn-success'
= form_for @trigger, url: url_for(controller: 'projects/triggers', action: 'create') do |f|
= f.submit "Add trigger", class: 'btn btn-success'
%h5.prepend-top-default
Use CURL
.panel-footer
%p.light
Copy the token above, set your branch or tag name, and that reference will be rebuilt.
%p
In the following examples, you can see the exact API call you need to
make in order to rebuild a specific
%code ref
(branch or tag) with a trigger token.
%p
All you need to do is replace the
%code TOKEN
and
%code REF_NAME
with the trigger token and the branch or tag name respectively.
%pre
:plain
curl -X POST \
-F token=TOKEN \
-F ref=REF_NAME \
#{builds_trigger_url(@project.id)}
%h5.prepend-top-default
Use .gitlab-ci.yml
%h5.prepend-top-default
Use cURL
%p.light
In the
%code .gitlab-ci.yml
of the dependent project, include the following snippet.
The project will rebuild at the end of the build.
%p.light
Copy one of the tokens above, set your branch or tag name, and that
reference will be rebuilt.
%pre
:plain
trigger:
type: deploy
script:
- "curl -X POST -F token=TOKEN -F ref=REF_NAME #{builds_trigger_url(@project.id)}"
%h5.prepend-top-default
Pass build variables
%pre
:plain
curl -X POST \
-F token=TOKEN \
-F ref=REF_NAME \
#{builds_trigger_url(@project.id)}
%h5.prepend-top-default
Use .gitlab-ci.yml
%p.light
Add
%code variables[VARIABLE]=VALUE
to an API request. Variable values can be used to distinguish between triggered builds and normal builds.
%p.light
In the
%code .gitlab-ci.yml
of another project, include the following snippet.
The project will be rebuilt at the end of the build.
%pre.append-bottom-0
:plain
curl -X POST \
-F token=TOKEN \
-F "ref=REF_NAME" \
-F "variables[RUN_NIGHTLY_BUILD]=true" \
#{builds_trigger_url(@project.id)}
%pre
:plain
trigger_build:
stage: deploy
script:
- "curl -X POST -F token=TOKEN -F ref=REF_NAME #{builds_trigger_url(@project.id)}"
%h5.prepend-top-default
Pass build variables
%p.light
Add
%code variables[VARIABLE]=VALUE
to an API request. Variable values can be used to distinguish between triggered builds and normal builds.
%pre.append-bottom-0
:plain
curl -X POST \
-F token=TOKEN \
-F "ref=REF_NAME" \
-F "variables[RUN_NIGHTLY_BUILD]=true" \
#{builds_trigger_url(@project.id)}
%ul.content-list
= render partial: 'shared/snippets/snippet', collection: @snippets
- if @snippets.empty?
%li
.nothing-here-block Nothing here.
.snippets-list-holder
%ul.content-list
= render partial: 'shared/snippets/snippet', collection: @snippets
- if @snippets.empty?
%li
.nothing-here-block Nothing here.
= paginate @snippets, theme: 'gitlab'
= paginate @snippets, theme: 'gitlab', remote: true
:javascript
gl.SnippetsList();
class DropGitoriousFieldFromApplicationSettings < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# After the deploy the caches will be cold anyway
DOWNTIME = false
def up
require 'yaml'
import_sources = connection.execute('SELECT import_sources FROM application_settings;')
return unless import_sources.first # support empty databases
yaml = if Gitlab::Database.postgresql?
import_sources.values[0][0]
else
import_sources.first[0]
end
yaml = YAML.safe_load(yaml)
yaml.delete 'gitorious'
# No need for a WHERE clause as there is only one
connection.execute("UPDATE application_settings SET import_sources = #{update_yaml(yaml)}")
end
def down
# noop, gitorious still yields a 404 anyway
end
private
def connection
ActiveRecord::Base.connection
end
def update_yaml(yaml)
connection.quote(YAML.dump(yaml))
end
end
doc/ci/triggers/img/builds_page.png

32.5 KB | W: | H:

doc/ci/triggers/img/builds_page.png

74.4 KB | W: | H:

doc/ci/triggers/img/builds_page.png
doc/ci/triggers/img/builds_page.png
doc/ci/triggers/img/builds_page.png
doc/ci/triggers/img/builds_page.png
  • 2-up
  • Swipe
  • Onion skin
doc/ci/triggers/img/triggers_page.png

12.6 KB | W: | H:

doc/ci/triggers/img/triggers_page.png

11.7 KB | W: | H:

doc/ci/triggers/img/triggers_page.png
doc/ci/triggers/img/triggers_page.png
doc/ci/triggers/img/triggers_page.png
doc/ci/triggers/img/triggers_page.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -6,50 +6,6 @@ GitLab Runner to manage your project's builds.
If you want a quick introduction to GitLab CI, follow our
[quick start guide](../quick_start/README.md).
---
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [.gitlab-ci.yml](#gitlab-ci-yml)
- [image and services](#image-and-services)
- [before_script](#before_script)
- [after_script](#after_script)
- [stages](#stages)
- [types](#types)
- [variables](#variables)
- [cache](#cache)
- [cache:key](#cache-key)
- [Jobs](#jobs)
- [script](#script)
- [stage](#stage)
- [only and except](#only-and-except)
- [job variables](#job-variables)
- [tags](#tags)
- [allow_failure](#allow_failure)
- [when](#when)
- [Manual actions](#manual-actions)
- [environment](#environment)
- [artifacts](#artifacts)
- [artifacts:name](#artifacts-name)
- [artifacts:when](#artifacts-when)
- [artifacts:expire_in](#artifacts-expire_in)
- [dependencies](#dependencies)
- [before_script and after_script](#before_script-and-after_script)
- [Git Strategy](#git-strategy)
- [Shallow cloning](#shallow-cloning)
- [Hidden keys](#hidden-keys)
- [Special YAML features](#special-yaml-features)
- [Anchors](#anchors)
- [Validate the .gitlab-ci.yml](#validate-the-gitlab-ci-yml)
- [Skipping builds](#skipping-builds)
- [Examples](#examples)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
---
## .gitlab-ci.yml
From version 7.12, GitLab CI uses a [YAML](https://en.wikipedia.org/wiki/YAML)
......@@ -183,7 +139,7 @@ Alias for [stages](#stages).
Introduced in GitLab Runner v0.5.0.
GitLab CI allows you to add variables to `.gitlab-ci.yml` that are set in the
build environment. The variables are stored in the git repository and are meant
build environment. The variables are stored in the Git repository and are meant
to store non-sensitive project configuration, for example:
```yaml
......@@ -198,6 +154,8 @@ thus allowing to fine tune them.
Variables can be also defined on [job level](#job-variables).
[Learn more about variables.](../variables/README.md)
### cache
>**Note:**
......@@ -1086,6 +1044,13 @@ test:mysql:
You can see that the hidden keys are conveniently used as templates.
## Triggers
Triggers can be used to force a rebuild of a specific branch, tag or commit,
with an API call.
[Read more in the triggers documentation.](../triggers/README.md)
## Validate the .gitlab-ci.yml
Each instance of GitLab CI has an embedded debug tool called Lint.
......
......@@ -28,11 +28,6 @@ module Gitlab
end
end
def total_count
@total_count ||= issues_count + merge_requests_count + blobs_count +
notes_count + wiki_blobs_count + commits_count
end
def blobs_count
@blobs_count ||= blobs.count
end
......
......@@ -27,11 +27,6 @@ module Gitlab
end
end
def total_count
@total_count ||= projects_count + issues_count + merge_requests_count +
milestones_count
end
def projects_count
@projects_count ||= projects.count
end
......@@ -48,10 +43,6 @@ module Gitlab
@milestones_count ||= milestones.count
end
def empty?
total_count.zero?
end
private
def projects
......
......@@ -20,10 +20,6 @@ module Gitlab
end
end
def total_count
@total_count ||= snippet_titles_count + snippet_blobs_count
end
def snippet_titles_count
@snippet_titles_count ||= snippet_titles.count
end
......
......@@ -164,6 +164,26 @@ describe "Builds" do
expect(page).to have_link 'Raw'
end
end
describe 'Variables' do
before do
@trigger_request = create :ci_trigger_request_with_variables
@build = create :ci_build, pipeline: @commit, trigger_request: @trigger_request
visit namespace_project_build_path(@project.namespace, @project, @build)
end
it 'shows variable key and value after click', js: true do
expect(page).to have_css('.reveal-variables')
expect(page).not_to have_css('.js-build-variable')
expect(page).not_to have_css('.js-build-value')
click_button 'Reveal Variables'
expect(page).not_to have_css('.reveal-variables')
expect(page).to have_selector('.js-build-variable', text: 'TRIGGER_KEY_1')
expect(page).to have_selector('.js-build-value', text: 'TRIGGER_VALUE_1')
end
end
end
describe "POST /:project/builds/:id/cancel" do
......
......@@ -12,7 +12,7 @@ describe 'Triggers' do
context 'create a trigger' do
before do
click_on 'Add Trigger'
click_on 'Add trigger'
expect(@project.triggers.count).to eq(1)
end
......
require 'spec_helper'
describe 'Snippets tab on a user profile', feature: true, js: true do
include WaitForAjax
let(:user) { create(:user) }
context 'when the user has snippets' do
before do
create_list(:snippet, 25, :public, author: user)
visit user_path(user)
page.within('.user-profile-nav') { click_link 'Snippets' }
wait_for_ajax
end
it 'is limited to 20 items per page' do
expect(page.all('.snippets-list-holder .snippet-row').count).to eq(20)
end
context 'clicking on the link to the second page' do
before { click_link('2') }
it 'shows the remaining snippets' do
expect(page.all('.snippets-list-holder .snippet-row').count).to eq(5)
end
end
end
end
......@@ -12,12 +12,6 @@ describe Gitlab::SearchResults do
let!(:milestone) { create(:milestone, project: project, title: 'foo') }
let(:results) { described_class.new(user, Project.all, 'foo') }
describe '#total_count' do
it 'returns the total amount of search hits' do
expect(results.total_count).to eq(4)
end
end
describe '#projects_count' do
it 'returns the total amount of projects' do
expect(results.projects_count).to eq(1)
......@@ -42,18 +36,6 @@ describe Gitlab::SearchResults do
end
end
describe '#empty?' do
it 'returns true when there are no search results' do
allow(results).to receive(:total_count).and_return(0)
expect(results.empty?).to eq(true)
end
it 'returns false when there are search results' do
expect(results.empty?).to eq(false)
end
end
describe 'confidential issues' do
let(:project_1) { create(:empty_project) }
let(:project_2) { create(:empty_project) }
......
......@@ -5,12 +5,6 @@ describe Gitlab::SnippetSearchResults do
let(:results) { described_class.new(Snippet.all, 'foo') }
describe '#total_count' do
it 'returns the total amount of search hits' do
expect(results.total_count).to eq(2)
end
end
describe '#snippet_titles_count' do
it 'returns the amount of matched snippet titles' do
expect(results.snippet_titles_count).to eq(1)
......
......@@ -59,14 +59,10 @@ describe 'projects/builds/show' do
end
it 'shows trigger variables in separate lines' do
expect(rendered).to have_css('code', text: variable_regexp('TRIGGER_KEY_1', 'TRIGGER_VALUE_1'))
expect(rendered).to have_css('code', text: variable_regexp('TRIGGER_KEY_2', 'TRIGGER_VALUE_2'))
expect(rendered).to have_css('.js-build-variable', visible: false, text: 'TRIGGER_KEY_1')
expect(rendered).to have_css('.js-build-variable', visible: false, text: 'TRIGGER_KEY_2')
expect(rendered).to have_css('.js-build-value', visible: false, text: 'TRIGGER_VALUE_1')
expect(rendered).to have_css('.js-build-value', visible: false, text: 'TRIGGER_VALUE_2')
end
end
private
def variable_regexp(key, value)
/\A#{Regexp.escape("#{key}=#{value}")}\Z/
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