Skip to content
Snippets Groups Projects
Verified Commit a582ee2f authored by Luke "Jared" Bennett's avatar Luke "Jared" Bennett
Browse files

Merge branch 'master' into droplab-templating-xss-fix

parents b34534b6 cd041082
No related branches found
No related tags found
No related merge requests found
Showing
with 704 additions and 102 deletions
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-phantomjs-2.1-node-7.1"
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-phantomjs-2.1-node-7.1-postgresql-9.6"
 
cache:
key: "ruby-233"
Loading
Loading
@@ -10,8 +10,6 @@ variables:
RAILS_ENV: "test"
NODE_ENV: "test"
SIMPLECOV: "true"
SETUP_DB: "true"
USE_BUNDLE_INSTALL: "true"
GIT_DEPTH: "20"
PHANTOMJS_VERSION: "2.1.1"
GET_SOURCES_ATTEMPTS: "3"
Loading
Loading
@@ -19,12 +17,9 @@ variables:
KNAPSACK_SPINACH_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/spinach_report-master.json
 
before_script:
- source ./scripts/prepare_build.sh
- cp config/gitlab.yml.example config/gitlab.yml
- bundle --version
- '[ "$USE_BUNDLE_INSTALL" != "true" ] || retry bundle install --without postgres production --jobs $(nproc) --clean $FLAGS'
- retry gem install knapsack fog-aws mime-types
- '[ "$SETUP_DB" != "true" ] || bundle exec rake db:drop db:create db:schema:load db:migrate add_limits_mysql'
- . scripts/utils.sh
- ./scripts/prepare_build.sh
 
stages:
- prepare
Loading
Loading
@@ -52,20 +47,34 @@ stages:
paths:
- knapsack/
 
.use-db: &use-db
.use-pg: &use-pg
services:
- postgres:latest
- redis:alpine
.use-mysql: &use-mysql
services:
- mysql:latest
- redis:alpine
 
.only-master-and-ee-or-mysql: &only-master-and-ee-or-mysql
only:
- /\-(?i)mysql$/
- master@gitlab-org/gitlab-ce
- master@gitlab/gitlabhq
- tags@gitlab-org/gitlab-ce
- tags@gitlab/gitlabhq
- //@gitlab-org/gitlab-ee
- //@gitlab/gitlab-ee
.rspec-knapsack: &rspec-knapsack
stage: test
<<: *dedicated-runner
<<: *use-db
script:
- JOB_NAME=( $CI_JOB_NAME )
- export CI_NODE_INDEX=${JOB_NAME[1]}
- export CI_NODE_TOTAL=${JOB_NAME[2]}
- export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- export CI_NODE_INDEX=${JOB_NAME[-2]}
- export CI_NODE_TOTAL=${JOB_NAME[-1]}
- export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_${JOB_NAME[1]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- export KNAPSACK_GENERATE_REPORT=true
- export CACHE_CLASSES=true
- cp ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} ${KNAPSACK_REPORT_PATH}
Loading
Loading
@@ -78,15 +87,23 @@ stages:
- knapsack/
- tmp/capybara/
 
.rspec-knapsack-pg: &rspec-knapsack-pg
<<: *rspec-knapsack
<<: *use-pg
.rspec-knapsack-mysql: &rspec-knapsack-mysql
<<: *rspec-knapsack
<<: *use-mysql
<<: *only-master-and-ee-or-mysql
.spinach-knapsack: &spinach-knapsack
stage: test
<<: *dedicated-runner
<<: *use-db
script:
- JOB_NAME=( $CI_JOB_NAME )
- export CI_NODE_INDEX=${JOB_NAME[1]}
- export CI_NODE_TOTAL=${JOB_NAME[2]}
- export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- export CI_NODE_INDEX=${JOB_NAME[-2]}
- export CI_NODE_TOTAL=${JOB_NAME[-1]}
- export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_${JOB_NAME[1]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- export KNAPSACK_GENERATE_REPORT=true
- export CACHE_CLASSES=true
- cp ${KNAPSACK_SPINACH_SUITE_REPORT_PATH} ${KNAPSACK_REPORT_PATH}
Loading
Loading
@@ -99,6 +116,15 @@ stages:
- knapsack/
- tmp/capybara/
 
.spinach-knapsack-pg: &spinach-knapsack-pg
<<: *spinach-knapsack
<<: *use-pg
.spinach-knapsack-mysql: &spinach-knapsack-mysql
<<: *spinach-knapsack
<<: *use-mysql
<<: *only-master-and-ee-or-mysql
# Prepare and merge knapsack tests
knapsack:
<<: *knapsack-state
Loading
Loading
@@ -116,8 +142,8 @@ update-knapsack:
<<: *dedicated-runner
stage: post-test
script:
- scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec_node_*.json
- scripts/merge-reports ${KNAPSACK_SPINACH_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/spinach_node_*.json
- scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec_pg_node_*.json
- scripts/merge-reports ${KNAPSACK_SPINACH_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/spinach_pg_node_*.json
- '[[ -z ${KNAPSACK_S3_BUCKET} ]] || scripts/sync-reports put $KNAPSACK_S3_BUCKET $KNAPSACK_RSPEC_SUITE_REPORT_PATH $KNAPSACK_SPINACH_SUITE_REPORT_PATH'
- rm -f knapsack/${CI_PROJECT_NAME}/*_node_*.json
only:
Loading
Loading
@@ -127,7 +153,7 @@ update-knapsack:
- master@gitlab/gitlab-ee
 
setup-test-env:
<<: *use-db
<<: *use-pg
<<: *dedicated-runner
stage: prepare
script:
Loading
Loading
@@ -142,37 +168,69 @@ setup-test-env:
- public/assets
- tmp/tests
 
rspec 0 20: *rspec-knapsack
rspec 1 20: *rspec-knapsack
rspec 2 20: *rspec-knapsack
rspec 3 20: *rspec-knapsack
rspec 4 20: *rspec-knapsack
rspec 5 20: *rspec-knapsack
rspec 6 20: *rspec-knapsack
rspec 7 20: *rspec-knapsack
rspec 8 20: *rspec-knapsack
rspec 9 20: *rspec-knapsack
rspec 10 20: *rspec-knapsack
rspec 11 20: *rspec-knapsack
rspec 12 20: *rspec-knapsack
rspec 13 20: *rspec-knapsack
rspec 14 20: *rspec-knapsack
rspec 15 20: *rspec-knapsack
rspec 16 20: *rspec-knapsack
rspec 17 20: *rspec-knapsack
rspec 18 20: *rspec-knapsack
rspec 19 20: *rspec-knapsack
spinach 0 10: *spinach-knapsack
spinach 1 10: *spinach-knapsack
spinach 2 10: *spinach-knapsack
spinach 3 10: *spinach-knapsack
spinach 4 10: *spinach-knapsack
spinach 5 10: *spinach-knapsack
spinach 6 10: *spinach-knapsack
spinach 7 10: *spinach-knapsack
spinach 8 10: *spinach-knapsack
spinach 9 10: *spinach-knapsack
rspec pg 0 20: *rspec-knapsack-pg
rspec pg 1 20: *rspec-knapsack-pg
rspec pg 2 20: *rspec-knapsack-pg
rspec pg 3 20: *rspec-knapsack-pg
rspec pg 4 20: *rspec-knapsack-pg
rspec pg 5 20: *rspec-knapsack-pg
rspec pg 6 20: *rspec-knapsack-pg
rspec pg 7 20: *rspec-knapsack-pg
rspec pg 8 20: *rspec-knapsack-pg
rspec pg 9 20: *rspec-knapsack-pg
rspec pg 10 20: *rspec-knapsack-pg
rspec pg 11 20: *rspec-knapsack-pg
rspec pg 12 20: *rspec-knapsack-pg
rspec pg 13 20: *rspec-knapsack-pg
rspec pg 14 20: *rspec-knapsack-pg
rspec pg 15 20: *rspec-knapsack-pg
rspec pg 16 20: *rspec-knapsack-pg
rspec pg 17 20: *rspec-knapsack-pg
rspec pg 18 20: *rspec-knapsack-pg
rspec pg 19 20: *rspec-knapsack-pg
rspec mysql 0 20: *rspec-knapsack-mysql
rspec mysql 1 20: *rspec-knapsack-mysql
rspec mysql 2 20: *rspec-knapsack-mysql
rspec mysql 3 20: *rspec-knapsack-mysql
rspec mysql 4 20: *rspec-knapsack-mysql
rspec mysql 5 20: *rspec-knapsack-mysql
rspec mysql 6 20: *rspec-knapsack-mysql
rspec mysql 7 20: *rspec-knapsack-mysql
rspec mysql 8 20: *rspec-knapsack-mysql
rspec mysql 9 20: *rspec-knapsack-mysql
rspec mysql 10 20: *rspec-knapsack-mysql
rspec mysql 11 20: *rspec-knapsack-mysql
rspec mysql 12 20: *rspec-knapsack-mysql
rspec mysql 13 20: *rspec-knapsack-mysql
rspec mysql 14 20: *rspec-knapsack-mysql
rspec mysql 15 20: *rspec-knapsack-mysql
rspec mysql 16 20: *rspec-knapsack-mysql
rspec mysql 17 20: *rspec-knapsack-mysql
rspec mysql 18 20: *rspec-knapsack-mysql
rspec mysql 19 20: *rspec-knapsack-mysql
spinach pg 0 10: *spinach-knapsack-pg
spinach pg 1 10: *spinach-knapsack-pg
spinach pg 2 10: *spinach-knapsack-pg
spinach pg 3 10: *spinach-knapsack-pg
spinach pg 4 10: *spinach-knapsack-pg
spinach pg 5 10: *spinach-knapsack-pg
spinach pg 6 10: *spinach-knapsack-pg
spinach pg 7 10: *spinach-knapsack-pg
spinach pg 8 10: *spinach-knapsack-pg
spinach pg 9 10: *spinach-knapsack-pg
spinach mysql 0 10: *spinach-knapsack-mysql
spinach mysql 1 10: *spinach-knapsack-mysql
spinach mysql 2 10: *spinach-knapsack-mysql
spinach mysql 3 10: *spinach-knapsack-mysql
spinach mysql 4 10: *spinach-knapsack-mysql
spinach mysql 5 10: *spinach-knapsack-mysql
spinach mysql 6 10: *spinach-knapsack-mysql
spinach mysql 7 10: *spinach-knapsack-mysql
spinach mysql 8 10: *spinach-knapsack-mysql
spinach mysql 9 10: *spinach-knapsack-mysql
 
# Other generic tests
.ruby-static-analysis: &ruby-static-analysis
Loading
Loading
@@ -228,24 +286,37 @@ rake ee_compat_check:
paths:
- ee_compat_check/patches/*.patch
 
rake db:migrate:reset:
.db-migrate-reset: &db-migrate-reset
stage: test
<<: *use-db
<<: *dedicated-runner
script:
- bundle exec rake db:migrate:reset
 
rake db:rollback:
rake pg db:migrate:reset:
<<: *db-migrate-reset
<<: *use-pg
rake mysql db:migrate:reset:
<<: *db-migrate-reset
<<: *use-mysql
.db-rollback: &db-rollback
stage: test
<<: *use-db
<<: *dedicated-runner
script:
- bundle exec rake db:rollback STEP=120
- bundle exec rake db:migrate
 
rake db:seed_fu:
rake pg db:rollback:
<<: *db-rollback
<<: *use-pg
rake mysql db:rollback:
<<: *db-rollback
<<: *use-mysql
.db-seed_fu: &db-seed_fu
stage: test
<<: *use-db
<<: *dedicated-runner
variables:
SIZE: "1"
Loading
Loading
@@ -261,6 +332,14 @@ rake db:seed_fu:
paths:
- log/development.log
 
rake pg db:seed_fu:
<<: *db-seed_fu
<<: *use-pg
rake mysql db:seed_fu:
<<: *db-seed_fu
<<: *use-mysql
rake gitlab:assets:compile:
stage: test
<<: *dedicated-runner
Loading
Loading
@@ -285,7 +364,7 @@ rake karma:
paths:
- vendor/ruby
stage: test
<<: *use-db
<<: *use-pg
<<: *dedicated-runner
variables:
BABEL_ENV: "coverage"
Loading
Loading
@@ -342,9 +421,8 @@ bundler:audit:
script:
- "bundle exec bundle-audit check --update --ignore CVE-2016-4658"
 
migration paths:
.migration-paths: &migration-paths
stage: test
<<: *use-db
<<: *dedicated-runner
variables:
SETUP_DB: "false"
Loading
Loading
@@ -356,13 +434,21 @@ migration paths:
script:
- git fetch origin v8.14.10
- git checkout -f FETCH_HEAD
- bundle install --without postgres production --jobs $(nproc) $FLAGS --retry=3
- bundle install $BUNDLE_INSTALL_FLAGS
- bundle exec rake db:drop db:create db:schema:load db:seed_fu
- git checkout $CI_COMMIT_SHA
- bundle install --without postgres production --jobs $(nproc) $FLAGS --retry=3
- source scripts/prepare_build.sh
- bundle install $BUNDLE_INSTALL_FLAGS
- . scripts/prepare_build.sh
- bundle exec rake db:migrate
 
migration pg paths:
<<: *migration-paths
<<: *use-pg
migration mysql paths:
<<: *migration-paths
<<: *use-mysql
coverage:
stage: post-test
services: []
Loading
Loading
@@ -409,7 +495,7 @@ trigger_docs:
before_script:
- apk update && apk add curl
variables:
GIT_STRATEGY: none
GIT_STRATEGY: "none"
cache: {}
artifacts: {}
script:
Loading
Loading
Loading
Loading
@@ -16,47 +16,44 @@ const defaults = {
class BlobForkSuggestion {
constructor(options) {
this.elementMap = Object.assign({}, defaults, options);
this.onClickWrapper = this.onClick.bind(this);
document.addEventListener('click', this.onClickWrapper);
this.onOpenButtonClick = this.onOpenButtonClick.bind(this);
this.onCancelButtonClick = this.onCancelButtonClick.bind(this);
}
 
showSuggestionSection(forkPath, action = 'edit') {
[].forEach.call(this.elementMap.suggestionSections, (suggestionSection) => {
suggestionSection.classList.remove('hidden');
});
init() {
this.bindEvents();
 
[].forEach.call(this.elementMap.forkButtons, (forkButton) => {
forkButton.setAttribute('href', forkPath);
});
return this;
}
 
[].forEach.call(this.elementMap.actionTextPieces, (actionTextPiece) => {
// eslint-disable-next-line no-param-reassign
actionTextPiece.textContent = action;
});
bindEvents() {
$(this.elementMap.openButtons).on('click', this.onOpenButtonClick);
$(this.elementMap.cancelButtons).on('click', this.onCancelButtonClick);
}
 
hideSuggestionSection() {
[].forEach.call(this.elementMap.suggestionSections, (suggestionSection) => {
suggestionSection.classList.add('hidden');
});
showSuggestionSection(forkPath, action = 'edit') {
$(this.elementMap.suggestionSections).removeClass('hidden');
$(this.elementMap.forkButtons).attr('href', forkPath);
$(this.elementMap.actionTextPieces).text(action);
}
 
onClick(e) {
const el = e.target;
hideSuggestionSection() {
$(this.elementMap.suggestionSections).addClass('hidden');
}
 
if ([].includes.call(this.elementMap.openButtons, el)) {
const { forkPath, action } = el.dataset;
this.showSuggestionSection(forkPath, action);
}
onOpenButtonClick(e) {
const forkPath = $(e.currentTarget).attr('data-fork-path');
const action = $(e.currentTarget).attr('data-action');
this.showSuggestionSection(forkPath, action);
}
 
if ([].includes.call(this.elementMap.cancelButtons, el)) {
this.hideSuggestionSection();
}
onCancelButtonClick() {
this.hideSuggestionSection();
}
 
destroy() {
document.removeEventListener('click', this.onClickWrapper);
$(this.elementMap.openButtons).off('click', this.onOpenButtonClick);
$(this.elementMap.cancelButtons).off('click', this.onCancelButtonClick);
}
}
 
Loading
Loading
/* eslint-disable no-new */
import Vue from 'vue';
import VueResource from 'vue-resource';
import NotebookLab from 'vendor/notebooklab';
import notebookLab from '../../notebook/index.vue';
 
Vue.use(VueResource);
Vue.use(NotebookLab);
 
export default () => {
const el = document.getElementById('js-notebook-viewer');
Loading
Loading
@@ -19,6 +18,9 @@ export default () => {
json: {},
};
},
components: {
notebookLab,
},
template: `
<div class="container-fluid md prepend-top-default append-bottom-default">
<div
Loading
Loading
Loading
Loading
@@ -97,7 +97,8 @@ const ShortcutsBlob = require('./shortcuts_blob');
cancelButtons: document.querySelectorAll('.js-cancel-fork-suggestion-button'),
suggestionSections: document.querySelectorAll('.js-file-fork-suggestion-section'),
actionTextPieces: document.querySelectorAll('.js-file-fork-suggestion-section-action'),
});
})
.init();
}
 
switch (page) {
Loading
Loading
<template>
<div class="cell">
<code-cell
type="input"
:raw-code="rawInputCode"
:count="cell.execution_count"
:code-css-class="codeCssClass" />
<output-cell
v-if="hasOutput"
:count="cell.execution_count"
:output="output"
:code-css-class="codeCssClass" />
</div>
</template>
<script>
import CodeCell from './code/index.vue';
import OutputCell from './output/index.vue';
export default {
components: {
'code-cell': CodeCell,
'output-cell': OutputCell,
},
props: {
cell: {
type: Object,
required: true,
},
codeCssClass: {
type: String,
required: false,
default: '',
},
},
computed: {
rawInputCode() {
if (this.cell.source) {
return this.cell.source.join('');
}
return '';
},
hasOutput() {
return this.cell.outputs.length;
},
output() {
return this.cell.outputs[0];
},
},
};
</script>
<style scoped>
.cell {
flex-direction: column;
}
</style>
<template>
<div :class="type">
<prompt
:type="promptType"
:count="count" />
<pre
class="language-python"
:class="codeCssClass"
ref="code"
v-text="code">
</pre>
</div>
</template>
<script>
import Prism from '../../lib/highlight';
import Prompt from '../prompt.vue';
export default {
components: {
prompt: Prompt,
},
props: {
count: {
type: Number,
required: false,
default: 0,
},
codeCssClass: {
type: String,
required: false,
default: '',
},
type: {
type: String,
required: true,
},
rawCode: {
type: String,
required: true,
},
},
computed: {
code() {
return this.rawCode;
},
promptType() {
const type = this.type.split('put')[0];
return type.charAt(0).toUpperCase() + type.slice(1);
},
},
mounted() {
Prism.highlightElement(this.$refs.code);
},
};
</script>
export { default as MarkdownCell } from './markdown.vue';
export { default as CodeCell } from './code.vue';
<template>
<div class="cell text-cell">
<prompt />
<div class="markdown" v-html="markdown"></div>
</div>
</template>
<script>
/* global katex */
import marked from 'marked';
import Prompt from './prompt.vue';
const renderer = new marked.Renderer();
/*
Regex to match KaTex blocks.
Supports the following:
\begin{equation}<math>\end{equation}
$$<math>$$
inline $<math>$
The matched text then goes through the KaTex renderer & then outputs the HTML
*/
const katexRegexString = `(
^\\\\begin{[a-zA-Z]+}\\s
|
^\\$\\$
|
\\s\\$(?!\\$)
)
(.+?)
(
\\s\\\\end{[a-zA-Z]+}$
|
\\$\\$$
|
\\$
)
`.replace(/\s/g, '').trim();
renderer.paragraph = (t) => {
let text = t;
let inline = false;
if (typeof katex !== 'undefined') {
const katexString = text.replace(/\\/g, '\\');
const matches = new RegExp(katexRegexString, 'gi').exec(katexString);
if (matches && matches.length > 0) {
if (matches[1].trim() === '$' && matches[3].trim() === '$') {
inline = true;
text = `${katexString.replace(matches[0], '')} ${katex.renderToString(matches[2])}`;
} else {
text = katex.renderToString(matches[2]);
}
}
}
return `<p class="${inline ? 'inline-katex' : ''}">${text}</p>`;
};
marked.setOptions({
sanitize: true,
renderer,
});
export default {
components: {
prompt: Prompt,
},
props: {
cell: {
type: Object,
required: true,
},
},
computed: {
markdown() {
return marked(this.cell.source.join(''));
},
},
};
</script>
<style>
.markdown .katex {
display: block;
text-align: center;
}
.markdown .inline-katex .katex {
display: inline;
text-align: initial;
}
</style>
<template>
<div class="output">
<prompt />
<div v-html="rawCode"></div>
</div>
</template>
<script>
import Prompt from '../prompt.vue';
export default {
props: {
rawCode: {
type: String,
required: true,
},
},
components: {
prompt: Prompt,
},
};
</script>
<template>
<div class="output">
<prompt />
<img
:src="'data:' + outputType + ';base64,' + rawCode" />
</div>
</template>
<script>
import Prompt from '../prompt.vue';
export default {
props: {
outputType: {
type: String,
required: true,
},
rawCode: {
type: String,
required: true,
},
},
components: {
prompt: Prompt,
},
};
</script>
<template>
<component :is="componentName"
type="output"
:outputType="outputType"
:count="count"
:raw-code="rawCode"
:code-css-class="codeCssClass" />
</template>
<script>
import CodeCell from '../code/index.vue';
import Html from './html.vue';
import Image from './image.vue';
export default {
props: {
codeCssClass: {
type: String,
required: false,
default: '',
},
count: {
type: Number,
required: false,
default: 0,
},
output: {
type: Object,
requred: true,
},
},
components: {
'code-cell': CodeCell,
'html-output': Html,
'image-output': Image,
},
data() {
return {
outputType: '',
};
},
computed: {
componentName() {
if (this.output.text) {
return 'code-cell';
} else if (this.output.data['image/png']) {
this.outputType = 'image/png';
return 'image-output';
} else if (this.output.data['text/html']) {
this.outputType = 'text/html';
return 'html-output';
} else if (this.output.data['image/svg+xml']) {
this.outputType = 'image/svg+xml';
return 'html-output';
}
this.outputType = 'text/plain';
return 'code-cell';
},
rawCode() {
if (this.output.text) {
return this.output.text.join('');
}
return this.dataForType(this.outputType);
},
},
methods: {
dataForType(type) {
let data = this.output.data[type];
if (typeof data === 'object') {
data = data.join('');
}
return data;
},
},
};
</script>
<template>
<div class="prompt">
<span v-if="type && count">
{{ type }} [{{ count }}]:
</span>
</div>
</template>
<script>
export default {
props: {
type: {
type: String,
required: false,
},
count: {
type: Number,
required: false,
},
},
};
</script>
<style scoped>
.prompt {
padding: 0 10px;
min-width: 7em;
font-family: monospace;
}
</style>
<template>
<div v-if="hasNotebook">
<component
v-for="(cell, index) in cells"
:is="cellType(cell.cell_type)"
:cell="cell"
:key="index"
:code-css-class="codeCssClass" />
</div>
</template>
<script>
import {
MarkdownCell,
CodeCell,
} from './cells';
export default {
components: {
'code-cell': CodeCell,
'markdown-cell': MarkdownCell,
},
props: {
notebook: {
type: Object,
required: true,
},
codeCssClass: {
type: String,
required: false,
default: '',
},
},
methods: {
cellType(type) {
return `${type}-cell`;
},
},
computed: {
cells() {
if (this.notebook.worksheets) {
const data = {
cells: [],
};
return this.notebook.worksheets.reduce((cellData, sheet) => {
const cellDataCopy = cellData;
cellDataCopy.cells = cellDataCopy.cells.concat(sheet.cells);
return cellDataCopy;
}, data).cells;
}
return this.notebook.cells;
},
hasNotebook() {
return Object.keys(this.notebook).length;
},
},
};
</script>
<style>
.cell,
.input,
.output {
display: flex;
width: 100%;
margin-bottom: 10px;
}
.cell pre {
margin: 0;
width: 100%;
}
</style>
import Prism from 'prismjs';
import 'prismjs/components/prism-python';
import 'prismjs/plugins/custom-class/prism-custom-class';
Prism.plugins.customClass.map({
comment: 'c',
error: 'err',
operator: 'o',
constant: 'kc',
namespace: 'kn',
keyword: 'k',
string: 's',
number: 'm',
'attr-name': 'na',
builtin: 'nb',
entity: 'ni',
function: 'nf',
tag: 'nt',
variable: 'nv',
});
export default Prism;
Loading
Loading
@@ -387,6 +387,7 @@
@media (max-width: $screen-xs-max) {
display: flex;
width: 100%;
margin-bottom: 10px;
 
.comment-btn {
flex-grow: 1;
Loading
Loading
Loading
Loading
@@ -386,6 +386,10 @@ ul.notes {
.note-headline-meta {
display: inline-block;
white-space: nowrap;
.system-note-message {
white-space: normal;
}
}
 
/**
Loading
Loading
Loading
Loading
@@ -23,6 +23,7 @@ class Projects::MilestonesController < Projects::ApplicationController
 
respond_to do |format|
format.html do
@project_namespace = @project.namespace.becomes(Namespace)
@milestones = @milestones.includes(:project)
@milestones = @milestones.page(params[:page])
end
Loading
Loading
##
# DEPRECATED
#
# These helpers are deprecated in favor of detailed CI/CD statuses.
#
# See 'detailed_status?` method and `Gitlab::Ci::Status` module.
#
module CiStatusHelper
def ci_status_path(pipeline)
project = pipeline.project
namespace_project_pipeline_path(project.namespace, project, pipeline)
end
 
# Is used by Commit and Merge Request Widget
def ci_label_for_status(status)
if detailed_status?(status)
return status.label
Loading
Loading
@@ -22,6 +28,23 @@ module CiStatusHelper
end
end
 
def ci_text_for_status(status)
if detailed_status?(status)
return status.text
end
case status
when 'success'
'passed'
when 'success_with_warnings'
'passed'
when 'manual'
'blocked'
else
status
end
end
def ci_status_for_statuseable(subject)
status = subject.try(:status) || 'not found'
status.humanize
Loading
Loading
Loading
Loading
@@ -7,6 +7,11 @@ module IconsHelper
# font-awesome-rails gem, but should we ever use a different icon pack in the
# future we won't have to change hundreds of method calls.
def icon(names, options = {})
if (options.keys & %w[aria-hidden aria-label]).empty?
# Add `aria-hidden` if there are no aria's set
options['aria-hidden'] = true
end
options.include?(:base) ? fa_stacked_icon(names, options) : fa_icon(names, options)
end
 
Loading
Loading
Loading
Loading
@@ -75,29 +75,32 @@ module Ci
pipeline.update_duration
end
 
before_transition any => [:manual] do |pipeline|
pipeline.update_duration
end
before_transition canceled: any - [:canceled] do |pipeline|
pipeline.auto_canceled_by = nil
end
 
after_transition [:created, :pending] => :running do |pipeline|
pipeline.run_after_commit { PipelineMetricsWorker.perform_async(id) }
pipeline.run_after_commit { PipelineMetricsWorker.perform_async(pipeline.id) }
end
 
after_transition any => [:success] do |pipeline|
pipeline.run_after_commit { PipelineMetricsWorker.perform_async(id) }
pipeline.run_after_commit { PipelineMetricsWorker.perform_async(pipeline.id) }
end
 
after_transition [:created, :pending, :running] => :success do |pipeline|
pipeline.run_after_commit { PipelineSuccessWorker.perform_async(id) }
pipeline.run_after_commit { PipelineSuccessWorker.perform_async(pipeline.id) }
end
 
after_transition do |pipeline, transition|
next if transition.loopback?
 
pipeline.run_after_commit do
PipelineHooksWorker.perform_async(id)
Ci::ExpirePipelineCacheService.new(project, nil)
.execute(pipeline)
PipelineHooksWorker.perform_async(pipeline.id)
ExpirePipelineCacheWorker.perform_async(pipeline.id)
end
end
 
Loading
Loading
@@ -385,6 +388,11 @@ module Ci
.select { |merge_request| merge_request.head_pipeline.try(:id) == self.id }
end
 
# All the merge requests for which the current pipeline runs/ran against
def all_merge_requests
@all_merge_requests ||= project.merge_requests.where(source_branch: ref)
end
def detailed_status(current_user)
Gitlab::Ci::Status::Pipeline::Factory
.new(self, current_user)
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment