Commit 0f2e1ff3 authored by Fatih Acet's avatar Fatih Acet Committed by Jacob Schatz

Merge branch 'add-svg-loader' into 'master'

Fixes Tech debt: No need to pass svgs as props into Vue with Webpack

Closes #27692 and #27840

See merge request !9522
parent c38465d8
...@@ -39,15 +39,10 @@ const PipelineStore = require('./pipelines_store'); ...@@ -39,15 +39,10 @@ const PipelineStore = require('./pipelines_store');
*/ */
data() { data() {
const pipelinesTableData = document.querySelector('#commit-pipeline-table-view').dataset; const pipelinesTableData = document.querySelector('#commit-pipeline-table-view').dataset;
const svgsData = document.querySelector('.pipeline-svgs').dataset;
const store = new PipelineStore(); const store = new PipelineStore();
// Transform svgs DOMStringMap to a plain Object.
const svgsObject = gl.utils.DOMStringMapToObject(svgsData);
return { return {
endpoint: pipelinesTableData.endpoint, endpoint: pipelinesTableData.endpoint,
svgs: svgsObject,
store, store,
state: store.state, state: store.state,
isLoading: false, isLoading: false,
...@@ -101,10 +96,7 @@ const PipelineStore = require('./pipelines_store'); ...@@ -101,10 +96,7 @@ const PipelineStore = require('./pipelines_store');
<div class="table-holder pipelines" <div class="table-holder pipelines"
v-if="!isLoading && state.pipelines.length > 0"> v-if="!isLoading && state.pipelines.length > 0">
<pipelines-table-component <pipelines-table-component :pipelines="state.pipelines"/>
:pipelines="state.pipelines"
:svgs="svgs">
</pipelines-table-component>
</div> </div>
</div> </div>
`, `,
......
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
/* global Vue */ import Vue from 'vue';
import iconCommit from '../svg/icon_commit.svg';
((global) => { ((global) => {
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
...@@ -9,6 +10,11 @@ ...@@ -9,6 +10,11 @@
items: Array, items: Array,
stage: Object, stage: Object,
}, },
data() {
return { iconCommit };
},
template: ` template: `
<div> <div>
<div class="events-description"> <div class="events-description">
...@@ -31,7 +37,7 @@ ...@@ -31,7 +37,7 @@
</h5> </h5>
<span> <span>
First First
<span class="commit-icon">${global.cycleAnalytics.svgs.iconCommit}</span> <span class="commit-icon">${iconCommit}</span>
<a :href="commit.commitUrl" class="commit-hash-link monospace">{{ commit.shortSha }}</a> <a :href="commit.commitUrl" class="commit-hash-link monospace">{{ commit.shortSha }}</a>
pushed by pushed by
<a :href="commit.author.webUrl" class="commit-author-link"> <a :href="commit.author.webUrl" class="commit-author-link">
......
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
/* global Vue */ import Vue from 'vue';
import iconBranch from '../svg/icon_branch.svg';
((global) => { ((global) => {
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
...@@ -9,6 +10,9 @@ ...@@ -9,6 +10,9 @@
items: Array, items: Array,
stage: Object, stage: Object,
}, },
data() {
return { iconBranch };
},
template: ` template: `
<div> <div>
<div class="events-description"> <div class="events-description">
...@@ -22,7 +26,7 @@ ...@@ -22,7 +26,7 @@
<a :href="build.url" class="pipeline-id">#{{ build.id }}</a> <a :href="build.url" class="pipeline-id">#{{ build.id }}</a>
<i class="fa fa-code-fork"></i> <i class="fa fa-code-fork"></i>
<a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a> <a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a>
<span class="icon-branch">${global.cycleAnalytics.svgs.iconBranch}</span> <span class="icon-branch">${iconBranch}</span>
<a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a> <a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a>
</h5> </h5>
<span> <span>
......
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
/* global Vue */ import Vue from 'vue';
import iconBuildStatus from '../svg/icon_build_status.svg';
import iconBranch from '../svg/icon_branch.svg';
((global) => { ((global) => {
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
...@@ -9,6 +11,9 @@ ...@@ -9,6 +11,9 @@
items: Array, items: Array,
stage: Object, stage: Object,
}, },
data() {
return { iconBuildStatus, iconBranch };
},
template: ` template: `
<div> <div>
<div class="events-description"> <div class="events-description">
...@@ -18,13 +23,13 @@ ...@@ -18,13 +23,13 @@
<li v-for="build in items" class="stage-event-item item-build-component"> <li v-for="build in items" class="stage-event-item item-build-component">
<div class="item-details"> <div class="item-details">
<h5 class="item-title"> <h5 class="item-title">
<span class="icon-build-status">${global.cycleAnalytics.svgs.iconBuildStatus}</span> <span class="icon-build-status">${iconBuildStatus}</span>
<a :href="build.url" class="item-build-name">{{ build.name }}</a> <a :href="build.url" class="item-build-name">{{ build.name }}</a>
&middot; &middot;
<a :href="build.url" class="pipeline-id">#{{ build.id }}</a> <a :href="build.url" class="pipeline-id">#{{ build.id }}</a>
<i class="fa fa-code-fork"></i> <i class="fa fa-code-fork"></i>
<a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a> <a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a>
<span class="icon-branch">${global.cycleAnalytics.svgs.iconBranch}</span> <span class="icon-branch">${iconBranch}</span>
<a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a> <a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a>
</h5> </h5>
<span> <span>
......
...@@ -4,9 +4,6 @@ ...@@ -4,9 +4,6 @@
window.Vue = require('vue'); window.Vue = require('vue');
window.Cookies = require('js-cookie'); window.Cookies = require('js-cookie');
require('./svg/icon_branch');
require('./svg/icon_build_status');
require('./svg/icon_commit');
require('./components/stage_code_component'); require('./components/stage_code_component');
require('./components/stage_issue_component'); require('./components/stage_issue_component');
require('./components/stage_plan_component'); require('./components/stage_plan_component');
......
/* eslint-disable no-param-reassign */
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
global.cycleAnalytics.svgs = global.cycleAnalytics.svgs || {};
global.cycleAnalytics.svgs.iconBranch = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14"><path fill="#8C8C8C" fill-rule="evenodd" d="M9.678 6.722C9.353 5.167 8.053 4 6.5 4S3.647 5.167 3.322 6.722h-2.6c-.397 0-.722.35-.722.778 0 .428.325.778.722.778h2.6C3.647 9.833 4.947 11 6.5 11s2.853-1.167 3.178-2.722h2.6c.397 0 .722-.35.722-.778 0-.428-.325-.778-.722-.778h-2.6zM4.694 7.5c0-1.09.795-1.944 1.806-1.944 1.01 0 1.806.855 1.806 1.944 0 1.09-.795 1.944-1.806 1.944-1.01 0-1.806-.855-1.806-1.944z"/></svg>';
})(window.gl || (window.gl = {}));
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14"><path fill="#8C8C8C" fill-rule="evenodd" d="M9.678 6.722C9.353 5.167 8.053 4 6.5 4S3.647 5.167 3.322 6.722h-2.6c-.397 0-.722.35-.722.778 0 .428.325.778.722.778h2.6C3.647 9.833 4.947 11 6.5 11s2.853-1.167 3.178-2.722h2.6c.397 0 .722-.35.722-.778 0-.428-.325-.778-.722-.778h-2.6zM4.694 7.5c0-1.09.795-1.944 1.806-1.944 1.01 0 1.806.855 1.806 1.944 0 1.09-.795 1.944-1.806 1.944-1.01 0-1.806-.855-1.806-1.944z"/></svg>
/* eslint-disable no-param-reassign */
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
global.cycleAnalytics.svgs = global.cycleAnalytics.svgs || {};
global.cycleAnalytics.svgs.iconBuildStatus = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14"><g fill="#31AF64" fill-rule="evenodd"><path d="M12.5 7c0-3.038-2.462-5.5-5.5-5.5S1.5 3.962 1.5 7s2.462 5.5 5.5 5.5 5.5-2.462 5.5-5.5zM0 7c0-3.866 3.134-7 7-7s7 3.134 7 7-3.134 7-7 7-7-3.134-7-7z"/><path d="M6.28 7.697L5.045 6.464c-.117-.117-.305-.117-.42-.002l-.614.614c-.11.113-.11.303.007.42l1.91 1.91c.19.19.51.197.703.004l.264-.265L9.997 6.04c.108-.107.107-.293-.01-.408l-.612-.614c-.114-.113-.298-.12-.41-.01L6.28 7.7z"/></g></svg>';
})(window.gl || (window.gl = {}));
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14"><g fill="#31AF64" fill-rule="evenodd"><path d="M12.5 7c0-3.038-2.462-5.5-5.5-5.5S1.5 3.962 1.5 7s2.462 5.5 5.5 5.5 5.5-2.462 5.5-5.5zM0 7c0-3.866 3.134-7 7-7s7 3.134 7 7-3.134 7-7 7-7-3.134-7-7z"/><path d="M6.28 7.697L5.045 6.464c-.117-.117-.305-.117-.42-.002l-.614.614c-.11.113-.11.303.007.42l1.91 1.91c.19.19.51.197.703.004l.264-.265L9.997 6.04c.108-.107.107-.293-.01-.408l-.612-.614c-.114-.113-.298-.12-.41-.01L6.28 7.7z"/></g></svg>
/* eslint-disable no-param-reassign */
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
global.cycleAnalytics.svgs = global.cycleAnalytics.svgs || {};
global.cycleAnalytics.svgs.iconCommit = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40"><path fill="#8F8F8F" fill-rule="evenodd" d="M28.777 18c-.91-4.008-4.494-7-8.777-7-4.283 0-7.868 2.992-8.777 7H4.01C2.9 18 2 18.895 2 20c0 1.112.9 2 2.01 2h7.213c.91 4.008 4.494 7 8.777 7 4.283 0 7.868-2.992 8.777-7h7.214C37.1 22 38 21.105 38 20c0-1.112-.9-2-2.01-2h-7.213zM20 25c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5z"/></svg>';
})(window.gl || (window.gl = {}));
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40"><path fill="#8F8F8F" fill-rule="evenodd" d="M28.777 18c-.91-4.008-4.494-7-8.777-7-4.283 0-7.868 2.992-8.777 7H4.01C2.9 18 2 18.895 2 20c0 1.112.9 2 2.01 2h7.213c.91 4.008 4.494 7 8.777 7 4.283 0 7.868-2.992 8.777-7h7.214C37.1 22 38 21.105 38 20c0-1.112-.9-2-2.01-2h-7.213zM20 25c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5z"/></svg>
...@@ -36,9 +36,6 @@ module.exports = Vue.component('environment-component', { ...@@ -36,9 +36,6 @@ module.exports = Vue.component('environment-component', {
projectStoppedEnvironmentsPath: environmentsData.projectStoppedEnvironmentsPath, projectStoppedEnvironmentsPath: environmentsData.projectStoppedEnvironmentsPath,
newEnvironmentPath: environmentsData.newEnvironmentPath, newEnvironmentPath: environmentsData.newEnvironmentPath,
helpPagePath: environmentsData.helpPagePath, helpPagePath: environmentsData.helpPagePath,
commitIconSvg: environmentsData.commitIconSvg,
playIconSvg: environmentsData.playIconSvg,
terminalIconSvg: environmentsData.terminalIconSvg,
// Pagination Properties, // Pagination Properties,
paginationInformation: {}, paginationInformation: {},
...@@ -195,13 +192,9 @@ module.exports = Vue.component('environment-component', { ...@@ -195,13 +192,9 @@ module.exports = Vue.component('environment-component', {
:environments="state.environments" :environments="state.environments"
:can-create-deployment="canCreateDeploymentParsed" :can-create-deployment="canCreateDeploymentParsed"
:can-read-environment="canReadEnvironmentParsed" :can-read-environment="canReadEnvironmentParsed"
:play-icon-svg="playIconSvg"
:terminal-icon-svg="terminalIconSvg"
:commit-icon-svg="commitIconSvg"
:toggleDeployBoard="toggleDeployBoard" :toggleDeployBoard="toggleDeployBoard"
:store="store" :store="store"
:service="service"> :service="service"/>
</environment-table>
</div> </div>
<table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1" <table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1"
......
const Vue = require('vue'); const Vue = require('vue');
const playIconSvg = require('icons/_icon_play.svg');
module.exports = Vue.component('actions-component', { module.exports = Vue.component('actions-component', {
props: { props: {
...@@ -7,11 +8,10 @@ module.exports = Vue.component('actions-component', { ...@@ -7,11 +8,10 @@ module.exports = Vue.component('actions-component', {
required: false, required: false,
default: () => [], default: () => [],
}, },
},
playIconSvg: { data() {
type: String, return { playIconSvg };
required: false,
},
}, },
template: ` template: `
...@@ -28,9 +28,7 @@ module.exports = Vue.component('actions-component', { ...@@ -28,9 +28,7 @@ module.exports = Vue.component('actions-component', {
data-method="post" data-method="post"
rel="nofollow" rel="nofollow"
class="js-manual-action-link"> class="js-manual-action-link">
${playIconSvg}
<span class="js-action-play-icon-container" v-html="playIconSvg"></span>
<span> <span>
{{action.name}} {{action.name}}
</span> </span>
......
...@@ -47,21 +47,6 @@ module.exports = Vue.component('environment-item', { ...@@ -47,21 +47,6 @@ module.exports = Vue.component('environment-item', {
default: false, default: false,
}, },
commitIconSvg: {
type: String,
required: false,
},
playIconSvg: {
type: String,
required: false,
},
terminalIconSvg: {
type: String,
required: false,
},
toggleDeployBoard: { toggleDeployBoard: {
type: Function, type: Function,
required: false, required: false,
...@@ -507,9 +492,7 @@ module.exports = Vue.component('environment-item', { ...@@ -507,9 +492,7 @@ module.exports = Vue.component('environment-item', {
:commit-url="commitUrl" :commit-url="commitUrl"
:short-sha="commitShortSha" :short-sha="commitShortSha"
:title="commitTitle" :title="commitTitle"
:author="commitAuthor" :author="commitAuthor"/>
:commit-icon-svg="commitIconSvg">
</commit-component>
</div> </div>
<p v-if="!model.isFolder && !hasLastDeploymentKey" class="commit-title"> <p v-if="!model.isFolder && !hasLastDeploymentKey" class="commit-title">
No deployments yet No deployments yet
...@@ -526,27 +509,20 @@ module.exports = Vue.component('environment-item', { ...@@ -526,27 +509,20 @@ module.exports = Vue.component('environment-item', {
<td class="environments-actions"> <td class="environments-actions">
<div v-if="!model.isFolder" class="btn-group pull-right" role="group"> <div v-if="!model.isFolder" class="btn-group pull-right" role="group">
<actions-component v-if="hasManualActions && canCreateDeployment" <actions-component v-if="hasManualActions && canCreateDeployment"
:play-icon-svg="playIconSvg" :actions="manualActions"/>
:actions="manualActions">
</actions-component>
<external-url-component v-if="externalURL && canReadEnvironment" <external-url-component v-if="externalURL && canReadEnvironment"
:external-url="externalURL"> :external-url="externalURL"/>
</external-url-component>
<stop-component v-if="hasStopAction && canCreateDeployment" <stop-component v-if="hasStopAction && canCreateDeployment"
:stop-url="model.stop_path"> :stop-url="model.stop_path"/>
</stop-component>
<terminal-button-component v-if="model && model.terminal_path" <terminal-button-component v-if="model && model.terminal_path"
:terminal-icon-svg="terminalIconSvg" :terminal-path="model.terminal_path"/>
:terminal-path="model.terminal_path">
</terminal-button-component>
<rollback-component v-if="canRetry && canCreateDeployment" <rollback-component v-if="canRetry && canCreateDeployment"
:is-last-deployment="isLastDeployment" :is-last-deployment="isLastDeployment"
:retry-url="retryUrl"> :retry-url="retryUrl"/>
</rollback-component>
</div> </div>
</td> </td>
</tr> </tr>
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* Used in environments table. * Used in environments table.
*/ */
const Vue = require('vue'); const Vue = require('vue');
const terminalIconSvg = require('icons/_icon_terminal.svg');
module.exports = Vue.component('terminal-button-component', { module.exports = Vue.component('terminal-button-component', {
props: { props: {
...@@ -10,16 +11,16 @@ module.exports = Vue.component('terminal-button-component', { ...@@ -10,16 +11,16 @@ module.exports = Vue.component('terminal-button-component', {
type: String, type: String,
default: '', default: '',
}, },
terminalIconSvg: { },
type: String,
default: '', data() {
}, return { terminalIconSvg };
}, },
template: ` template: `
<a class="btn terminal-button" <a class="btn terminal-button"
:href="terminalPath"> :href="terminalPath">
<span class="js-terminal-icon-container" v-html="terminalIconSvg"></span> ${terminalIconSvg}
</a> </a>
`, `,
}); });
...@@ -34,21 +34,6 @@ module.exports = Vue.component('environment-table-component', { ...@@ -34,21 +34,6 @@ module.exports = Vue.component('environment-table-component', {
default: false, default: false,
}, },
commitIconSvg: {
type: String,
required: false,
},
playIconSvg: {
type: String,
required: false,
},
terminalIconSvg: {
type: String,
required: false,
},
toggleDeployBoard: { toggleDeployBoard: {
type: Function, type: Function,
required: false, required: false,
...@@ -88,9 +73,6 @@ module.exports = Vue.component('environment-table-component', { ...@@ -88,9 +73,6 @@ module.exports = Vue.component('environment-table-component', {
:model="model" :model="model"
:can-create-deployment="canCreateDeployment" :can-create-deployment="canCreateDeployment"
:can-read-environment="canReadEnvironment" :can-read-environment="canReadEnvironment"
:play-icon-svg="playIconSvg"
:terminal-icon-svg="terminalIconSvg"
:commit-icon-svg="commitIconSvg"
:toggleDeployBoard="toggleDeployBoard"></tr> :toggleDeployBoard="toggleDeployBoard"></tr>
<tr v-if="model.hasDeployBoard && model.isDeployBoardVisible" class="js-deploy-board-row"> <tr v-if="model.hasDeployBoard && model.isDeployBoardVisible" class="js-deploy-board-row">
......
/* global Vue */ /* global Vue */
import stopwatchSvg from 'icons/_icon_stopwatch.svg';
require('../../../lib/utils/pretty_time'); require('../../../lib/utils/pretty_time');
(() => { (() => {
...@@ -11,7 +13,6 @@ require('../../../lib/utils/pretty_time'); ...@@ -11,7 +13,6 @@ require('../../../lib/utils/pretty_time');
'showNoTimeTrackingState', 'showNoTimeTrackingState',
'timeSpentHumanReadable', 'timeSpentHumanReadable',
'timeEstimateHumanReadable', 'timeEstimateHumanReadable',
'stopwatchSvg',
], ],
methods: { methods: {
abbreviateTime(timeStr) { abbreviateTime(timeStr) {
...@@ -20,7 +21,7 @@ require('../../../lib/utils/pretty_time'); ...@@ -20,7 +21,7 @@ require('../../../lib/utils/pretty_time');
}, },
template: ` template: `
<div class='sidebar-collapsed-icon'> <div class='sidebar-collapsed-icon'>
<div v-html='stopwatchSvg'></div> ${stopwatchSvg}
<div class='time-tracking-collapsed-summary'> <div class='time-tracking-collapsed-summary'>
<div class='compare' v-if='showComparisonState'> <div class='compare' v-if='showComparisonState'>
<span>{{ abbreviateTime(timeSpentHumanReadable) }} / {{ abbreviateTime(timeEstimateHumanReadable) }}</span> <span>{{ abbreviateTime(timeSpentHumanReadable) }} / {{ abbreviateTime(timeEstimateHumanReadable) }}</span>
......
...@@ -15,7 +15,6 @@ require('./comparison_pane'); ...@@ -15,7 +15,6 @@ require('./comparison_pane');
'time_spent', 'time_spent',
'human_time_estimate', 'human_time_estimate',
'human_time_spent', 'human_time_spent',
'stopwatchSvg',
'docsUrl', 'docsUrl',
], ],
data() { data() {
...@@ -71,8 +70,7 @@ require('./comparison_pane'); ...@@ -71,8 +70,7 @@ require('./comparison_pane');
:show-spent-only-state='showSpentOnlyState' :show-spent-only-state='showSpentOnlyState'
:show-estimate-only-state='showEstimateOnlyState' :show-estimate-only-state='showEstimateOnlyState'
:time-spent-human-readable='timeSpentHumanReadable' :time-spent-human-readable='timeSpentHumanReadable'
:time-estimate-human-readable='timeEstimateHumanReadable' :time-estimate-human-readable='timeEstimateHumanReadable'>
:stopwatch-svg='stopwatchSvg'>
</time-tracking-collapsed-state> </time-tracking-collapsed-state>
<div class='title hide-collapsed'> <div class='title hide-collapsed'>
Time tracking Time tracking
......
...@@ -246,17 +246,6 @@ ...@@ -246,17 +246,6 @@
previousPage: parseInt(paginationInformation['X-PREV-PAGE'], 10), previousPage: parseInt(paginationInformation['X-PREV-PAGE'], 10),
}); });
/**
* Transforms a DOMStringMap into a plain object.
*
* @param {DOMStringMap} DOMStringMapObject
* @returns {Object}
*/
w.gl.utils.DOMStringMapToObject = DOMStringMapObject => Object.keys(DOMStringMapObject).reduce((acc, element) => {
acc[element] = DOMStringMapObject[element];
return acc;
}, {});
/** /**
* Updates the search parameter of a URL given the parameter and values provided. * Updates the search parameter of a URL given the parameter and values provided.
* *
......
...@@ -11,15 +11,10 @@ $(() => new Vue({ ...@@ -11,15 +11,10 @@ $(() => new Vue({
data() { data() {
const project = document.querySelector('.pipelines'); const project = document.querySelector('.pipelines');
const svgs = document.querySelector('.pipeline-svgs').dataset;
// Transform svgs DOMStringMap to a plain Object.
const svgsObject = gl.utils.DOMStringMapToObject(svgs);
return { return {
scope: project.dataset.url, scope: project.dataset.url,
store: new gl.PipelineStore(), store: new gl.PipelineStore(),
svgs: svgsObject,
}; };
}, },
components: { components: {
...@@ -27,10 +22,8 @@ $(() => new Vue({ ...@@ -27,10 +22,8 @@ $(() => new Vue({
}, },
template: ` template: `
<vue-pipelines <vue-pipelines
:scope='scope' :scope="scope"
:store='store' :store="store">
:svgs='svgs'
>
</vue-pipelines> </vue-pipelines>
`, `,
})); }));
/* global Vue, Flash, gl */ /* global Vue, Flash, gl */
/* eslint-disable no-param-reassign, no-alert */ /* eslint-disable no-param-reassign, no-alert */
const playIconSvg = require('icons/_icon_play.svg');
((gl) => { ((gl) => {
gl.VuePipelineActions = Vue.extend({ gl.VuePipelineActions = Vue.extend({
props: ['pipeline', 'svgs'], props: ['pipeline'],
computed: { computed: {
actions() { actions() {
return this.pipeline.details.manual_actions.length > 0; return this.pipeline.details.manual_actions.length > 0;
...@@ -31,6 +32,11 @@ ...@@ -31,6 +32,11 @@
} }
}, },
}, },
data() {
return { playIconSvg };
},
template: ` template: `
<td class="pipeline-actions"> <td class="pipeline-actions">
<div class="pull-right"> <div class="pull-right">
...@@ -42,7 +48,7 @@ ...@@ -42,7 +48,7 @@
title="Manual job" title="Manual job"
data-placement="top" data-placement="top"
aria-label="Manual job"> aria-label="Manual job">
<span v-html="svgs.iconPlay" aria-hidden="true"></span> <span v-html="playIconSvg" aria-hidden="true"></span>
<i class="fa fa-caret-down" aria-hidden="true"></i> <i class="fa fa-caret-down" aria-hidden="true"></i>
</button> </button>
<ul class="dropdown-menu dropdown-menu-align-right"> <ul class="dropdown-menu dropdown-menu-align-right">
...@@ -50,8 +56,8 @@ ...@@ -50,8 +56,8 @@
<a <a
rel="nofollow" rel="nofollow"
data-method="post" data-method="post"
:href="action.path"> :href="action.path" >
<span v-html="svgs.iconPlay" aria-hidden="true"></span> <span v-html="playIconSvg" aria-hidden="true"></span>
<span>{{action.name}}</span> <span>{{action.name}}</span>
</a> </a>
</li> </li>
......
...@@ -27,7 +27,7 @@ const CommitPipelinesStoreWithTimeAgo = require('../commit/pipelines/pipelines_s ...@@ -27,7 +27,7 @@ const CommitPipelinesStoreWithTimeAgo = require('../commit/pipelines/pipelines_s
pageRequest: false, pageRequest: false,
}; };
}, },
props: ['scope', 'store', 'svgs'], props: ['scope', 'store'],
created() { created() {
const pagenum = gl.utils.getParameterByName('page'); const pagenum = gl.utils.getParameterByName('page');
const scope = gl.utils.getParameterByName('scope'); const scope = gl.utils.getParameterByName('scope');
...@@ -70,10 +70,7 @@ const CommitPipelinesStoreWithTimeAgo = require('../commit/pipelines/pipelines_s ...@@ -70,10 +70,7 @@ const CommitPipelinesStoreWithTimeAgo = require('../commit/pipelines/pipelines_s
</div> </div>
<div class="table-holder" v-if='!pageRequest && pipelines.length'> <div class="table-holder" v-if='!pageRequest && pipelines.length'>
<pipelines-table-component <pipelines-table-component :pipelines='pipelines'/>
:pipelines='pipelines'
:svgs='svgs'>
</pipelines-table-component>
</div> </div>
<gl-pagination <gl-pagination
......
/* global Vue, Flash, gl */ /* global Vue, Flash, gl */
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
import canceledSvg from 'icons/_icon_status_canceled_borderless.svg';
import createdSvg from 'icons/_icon_status_created_borderless.svg';
import failedSvg from 'icons/_icon_status_failed_borderless.svg';
import manualSvg from 'icons/_icon_status_manual_borderless.svg';
import pendingSvg from 'icons/_icon_status_pending_borderless.svg';
import runningSvg from 'icons/_icon_status_running_borderless.svg';
import skippedSvg from 'icons/_icon_status_skipped_borderless.svg';
import successSvg from 'icons/_icon_status_success_borderless.svg';
import warningSvg from 'icons/_icon_status_warning_borderless.svg';
((gl) => { ((gl) => {
gl.VueStage = Vue.extend({ gl.VueStage = Vue.extend({
data() { data() {
const svgsDictionary = {
icon_status_canceled: canceledSvg,
icon_status_created: createdSvg,
icon_status_failed: failedSvg,
icon_status_manual: manualSvg,
icon_status_pending: pendingSvg,
icon_status_running: runningSvg,
icon_status_skipped: skippedSvg,
icon_status_success: successSvg,
icon_status_warning: warningSvg,
};
return { return {
builds: '', builds: '',
spinner: '<span class="fa fa-spinner fa-spin"></span>', spinner: '<span class="fa fa-spinner fa-spin"></span>',
svg: svgsDictionary[this.stage.status.icon],
}; };
}, },
props: { props: {
stage: { stage: {
type: Object, type: Object,
required: true, required: true,
}, },
svgs: {
type: Object,
required: true,
},
match: {
type: Function,
required: true,
},
}, },
updated() { updated() {
...@@ -73,11 +88,6 @@ ...@@ -73,11 +88,6 @@
tooltip() { tooltip() {
return `has-tooltip ci-status-icon ci-status-icon-${this.stage.status.group}`; return `has-tooltip ci-status-icon ci-status-icon-${this.stage.status.group}`;
}, },
svg() {
const { icon } = this.stage.status;
const stageIcon = icon.replace(/icon/i, 'stage_icon');
return this.svgs[this.match(stageIcon)];
},
triggerButtonClass() { triggerButtonClass() {
return `mini-pipeline-graph-dropdown-toggle has-tooltip js-builds-dropdown-button ci-status-icon-${this.stage.status.group}`; return `mini-pipeline-graph-dropdown-toggle has-tooltip js-builds-dropdown-button ci-status-icon-${this.stage.status.group}`;
}, },
...@@ -91,8 +101,7 @@ ...@@ -91,8 +101,7 @@
data-placement="top" data-placement="top"
data-toggle="dropdown" data-toggle="dropdown"
type="button" type="button"
:aria-label="stage.title" :aria-label="stage.title">
>
<span v-html="svg" aria-hidden="true"></span> <span v-html="svg" aria-hidden="true"></span>
<i class="fa fa-caret-down" aria-hidden="true"></i> <i class="fa fa-caret-down" aria-hidden="true"></i>
</button> </button>
...@@ -101,8 +110,7 @@ ...@@ -101,8 +110,7 @@
<div <div
:class="dropdownClass" :class="dropdownClass"
class="js-builds-dropdown-list scrollable-menu" class="js-builds-dropdown-list scrollable-menu"
v-html="buildsOrSpinner" v-html="buildsOrSpinner">
>
</div> </div>
</ul> </ul>
</div> </div>
......
/* global Vue, gl */ /* global Vue, gl */
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
import canceledSvg from 'icons/_icon_status_canceled.svg';
import createdSvg from 'icons/_icon_status_created.svg';
import failedSvg from 'icons/_icon_status_failed.svg';
import manualSvg from 'icons/_icon_status_manual.svg';
import pendingSvg from 'icons/_icon_status_pending.svg';
import runningSvg from 'icons/_icon_status_running.svg';
import skippedSvg from 'icons/_icon_status_skipped.svg';
import successSvg from 'icons/_icon_status_success.svg';
import warningSvg from 'icons/_icon_status_warning.svg';
((gl) => { ((gl) => {
gl.VueStatusScope = Vue.extend({ gl.VueStatusScope = Vue.extend({
props: [ props: [
'pipeline', 'svgs', 'match', 'pipeline',
], ],
data() {
const svgsDictionary = {
icon_status_canceled: canceledSvg,
icon_status_created: createdSvg,
icon_status_failed: failedSvg,
icon_status_manual: manualSvg,
icon_status_pending: pendingSvg,
icon_status_running: runningSvg,
icon_status_skipped: skippedSvg,
icon_status_success: successSvg,
icon_status_warning: warningSvg,
};
return {
svg: svgsDictionary[this.pipeline.details.status.icon],
};
},
computed: { computed: {
cssClasses() { cssClasses() {
const cssObject = { 'ci-status': true }; const cssObject = { 'ci-status': true };
cssObject[`ci-${this.pipeline.details.status.group}`] = true; cssObject[`ci-${this.pipeline.details.status.group}`] = true;
return cssObject; return cssObject;
}, },
svg() {
return this.svgs[this.match(this.pipeline.details.status.icon)];
},
detailsPath() { detailsPath() {
const { status } = this.pipeline.details; const { status } = this.pipeline.details;
return status.has_details ? status.details_path : false; return status.has_details ? status.details_path : false;
}, },
content() {
return `${this.svg} ${this.pipeline.details.status.text}`;
},
}, },
template: ` template: `
<td class="commit-link"> <td class="commit-link">
<a <a
:class='cssClasses' :class="cssClasses"
:href='detailsPath' :href="detailsPath"
v-html='svg + pipeline.details.status.text' v-html="content">
>
</a> </a>
</td> </td>
`, `,
......
...@@ -4,14 +4,17 @@ ...@@ -4,14 +4,17 @@
window.Vue = require('vue'); window.Vue = require('vue');
require('../lib/utils/datetime_utility'); require('../lib/utils/datetime_utility');
const iconTimerSvg = require('../../../views/shared/icons/_icon_timer.svg');
((gl) => { ((gl) => {
gl.VueTimeAgo = Vue.extend({ gl.VueTimeAgo = Vue.extend({
data() { data() {
return { return {
currentTime: new Date(), currentTime: new Date(),
iconTimerSvg,
}; };
}, },
props: ['pipeline', 'svgs'], props: ['pipeline'],
computed: { computed: {
timeAgo() { timeAgo() {
return gl.utils.getTimeago(); return gl.utils.getTimeago();
...@@ -56,7 +59,7 @@ require('../lib/utils/datetime_utility'); ...@@ -56,7 +59,7 @@ require('../lib/utils/datetime_utility');
template: ` template: `
<td class="pipelines-time-ago"> <td class="pipelines-time-ago">
<p class="duration" v-if='duration'> <p class="duration" v-if='duration'>
<span v-html='svgs.iconTimer'></span> <span v-html="iconTimerSvg"></span>
{{duration}} {{duration}}
</p> </p>
<p class="finished-at" v-if='timeStopped'> <p class="finished-at" v-if='timeStopped'>
......
/* global Vue */ /* global Vue */
window.Vue = require('vue'); window.Vue = require('vue');
const commitIconSvg = require('icons/_icon_commit.svg');
(() => { (() => {
window.gl = window.gl || {}; window.gl = window.gl || {};
...@@ -69,11 +70,6 @@ window.Vue = require('vue'); ...@@ -69,11 +70,6 @@ window.Vue = require('vue');
required: false, required: false,
default: () => ({}), default: () => ({}),
}, },
commitIconSvg: {
type: String,
required: false,
},
}, },
computed: { computed: {
...@@ -116,6 +112,10 @@ window.Vue = require('vue'); ...@@ -116,6 +112,10 @@ window.Vue = require('vue');
}, },
}, },
data() {
return { commitIconSvg };
},
template: ` template: `
<div class="branch-commit"> <div class="branch-commit">
......
...@@ -21,14 +21,6 @@ require('./pipelines_table_row'); ...@@ -21,14 +21,6 @@ require('./pipelines_table_row');
default: () => ([]), default: () => ([]),
}, },
/**
* TODO: Remove this when we have webpack.
*/
svgs: {
type: Object,
required: true,
default: () => ({}),
},
}, },
components: { components: {
...@@ -51,8 +43,7 @@ require('./pipelines_table_row'); ...@@ -51,8 +43,7 @@ require('./pipelines_table_row');
<template v-for="model in pipelines" <template v-for="model in pipelines"
v-bind:model="model"> v-bind:model="model">
<tr is="pipelines-table-row-component" <tr is="pipelines-table-row-component"
:pipeline="model" :pipeline="model"></tr>
:svgs="svgs"></tr>
</template> </template>
</tbody> </tbody>
</table> </table>
......
...@@ -25,14 +25,6 @@ require('./commit'); ...@@ -25,14 +25,6 @@ require('./commit');
default: () => ({}), default: () => ({}),
}, },
/**
* TODO: Remove this when we have webpack;
*/
svgs: {
type: Object,
required: true,
default: () => ({}),
},
}, },
components: { components: {
...@@ -174,30 +166,9 @@ require('./commit'); ...@@ -174,30 +166,9 @@ require('./commit');
}, },
}, },
methods: {
/**
* FIXME: This should not be in this component but in the components that
* need this function.
*
* Used to render SVGs in the following components:
* - status-scope
* - dropdown-stage
*
* @param {String} string
* @return {String}
*/
match(string) {
return string.replace(/_([a-z])/g, (m, w) => w.toUpperCase());
},
},
template: ` template: `
<tr class="commit"> <tr class="commit">
<status-scope <status-scope :pipeline="pipeline"/>
:pipeline="pipeline"
:svgs="svgs"
:match="match">
</status-scope>
<pipeline-url :pipeline="pipeline"></pipeline-url> <pipeline-url :pipeline="pipeline"></pipeline-url>
...@@ -208,26 +179,20 @@ require('./commit'); ...@@ -208,26 +179,20 @@ require('./commit');
:commit-url="commitUrl" :commit-url="commitUrl"
:short-sha="commitShortSha" :short-sha="commitShortSha"
:title="commitTitle" :title="commitTitle"
:author="commitAuthor" :author="commitAuthor"/>
:commit-icon-svg="svgs.commitIconSvg">
</commit-component>
</td> </td>
<td class="stage-cell"> <td class="stage-cell">
<div class="stage-container dropdown js-mini-pipeline-graph" <div class="stage-container dropdown js-mini-pipeline-graph"
v-if="pipeline.details.stages.length > 0" v-if="pipeline.details.stages.length > 0"
v-for="stage in pipeline.details.stages"> v-for="stage in pipeline.details.stages">
<dropdown-stage <dropdown-stage :stage="stage"/>
:stage="stage"
:svgs="svgs"
:match="match">
</dropdown-stage>
</div> </div>
</td> </td>
<time-ago :pipeline="pipeline" :svgs="svgs"></time-ago> <time-ago :pipeline="pipeline"/>
<pipeline-actions :pipeline="pipeline" :svgs="svgs"></pipeline-actions> <pipeline-actions :pipeline="pipeline" />
</tr> </tr>
`, `,
}); });
......
...@@ -19,7 +19,6 @@ window.Vue = require('vue'); ...@@ -19,7 +19,6 @@ window.Vue = require('vue');
/** /**
This function will take the information given by the pagination component This function will take the information given by the pagination component
And make a new Turbolinks call
Here is an example `change` method: Here is an example `change` method:
......
...@@ -2,27 +2,6 @@ ...@@ -2,27 +2,6 @@
#commit-pipeline-table-view{ data: { disable_initialization: disable_initialization, #commit-pipeline-table-view{ data: { disable_initialization: disable_initialization,
endpoint: endpoint, endpoint: endpoint,
} } } }
.pipeline-svgs{ data: { "commit_icon_svg" => custom_icon("icon_commit"),
"icon_status_canceled" => custom_icon("icon_status_canceled"),
"icon_status_running" => custom_icon("icon_status_running"),
"icon_status_skipped" => custom_icon("icon_status_skipped"),
"icon_status_created" => custom_icon("icon_status_created"),
"icon_status_pending" => custom_icon("icon_status_pending"),
"icon_status_success" => custom_icon("icon_status_success"),
"icon_status_failed" => custom_icon("icon_status_failed"),
"icon_status_warning" => custom_icon("icon_status_warning"),
"stage_icon_status_canceled" => custom_icon("icon_status_canceled_borderless"),
"stage_icon_status_running" => custom_icon("icon_status_running_borderless"),
"stage_icon_status_skipped" => custom_icon("icon_status_skipped_borderless"),
"stage_icon_status_created" => custom_icon("icon_status_created_borderless"),
"stage_icon_status_pending" => custom_icon("icon_status_pending_borderless"),
"stage_icon_status_success" => custom_icon("icon_status_success_borderless"),
"stage_icon_status_failed" => custom_icon("icon_status_failed_borderless"),
"stage_icon_status_warning" => custom_icon("icon_status_warning_borderless"),
"icon_play" => custom_icon("icon_play"),
"icon_timer" => custom_icon("icon_timer"),
"icon_status_manual" => custom_icon("icon_status_manual"),
} }
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('commit_pipelines') = page_specific_javascript_bundle_tag('commit_pipelines')
...@@ -13,7 +13,4 @@ ...@@ -13,7 +13,4 @@
"project-stopped-environments-path" => project_environments_path(@project, scope: :stopped), "project-stopped-environments-path" => project_environments_path(@project, scope: :stopped),
"new-environment-path" => new_namespace_project_environment_path(@project.namespace, @project), "new-environment-path" => new_namespace_project_environment_path(@project.namespace, @project),
"help-page-path" => help_page_path("ci/environments"), "help-page-path" => help_page_path("ci/environments"),
"css-class" => container_class, "css-class" => container_class } }
"commit-icon-svg" => custom_icon("icon_commit"),
"terminal-icon-svg" => custom_icon("icon_terminal"),
"play-icon-svg" => custom_icon("icon_play") } }
...@@ -52,28 +52,6 @@ ...@@ -52,28 +52,6 @@
= link_to ci_lint_path, class: 'btn btn-default' do = link_to ci_lint_path, class: 'btn btn-default' do
%span CI Lint %span CI Lint
.content-list.pipelines{ data: { url: namespace_project_pipelines_path(@project.namespace, @project, format: :json) } } .content-list.pipelines{ data: { url: namespace_project_pipelines_path(@project.namespace, @project, format: :json) } }
.pipeline-svgs{ "data" => {"commit_icon_svg" => custom_icon("icon_commit"), .vue-pipelines-index
"icon_status_canceled" => custom_icon("icon_status_canceled"),
"icon_status_running" => custom_icon("icon_status_running"),
"icon_status_skipped" => custom_icon("icon_status_skipped"),
"icon_status_created" => custom_icon("icon_status_created"),
"icon_status_pending" => custom_icon("icon_status_pending"),
"icon_status_success" => custom_icon("icon_status_success"),
"icon_status_failed" => custom_icon("icon_status_failed"),
"icon_status_warning" => custom_icon("icon_status_warning"),
"stage_icon_status_canceled" => custom_icon("icon_status_canceled_borderless"),
"stage_icon_status_running" => custom_icon("icon_status_running_borderless"),
"stage_icon_status_skipped" => custom_icon("icon_status_skipped_borderless"),
"stage_icon_status_created" => custom_icon("icon_status_created_borderless"),
"stage_icon_status_pending" => custom_icon("icon_status_pending_borderless"),
"stage_icon_status_success" => custom_icon("icon_status_success_borderless"),
"stage_icon_status_failed" => custom_icon("icon_status_failed_borderless"),
"stage_icon_status_warning" => custom_icon("icon_status_warning_borderless"),
"icon_play" => custom_icon("icon_play"),
"icon_timer" => custom_icon("icon_timer"),
"icon_status_manual" => custom_icon("icon_status_manual"),
} }
.vue-pipelines-index
= page_specific_javascript_bundle_tag('vue_pipelines') = page_specific_javascript_bundle_tag('vue_pipelines')
...@@ -77,7 +77,7 @@ ...@@ -77,7 +77,7 @@
= dropdown_tag('Milestone', options: { title: 'Assign milestone', toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: 'Search milestones', data: { show_no: true, field_name: "#{issuable.to_ability_name}[milestone_id]", project_id: @project.id, issuable_id: issuable.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable), use_id: true }}) = dropdown_tag('Milestone', options: { title: 'Assign milestone', toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: 'Search milestones', data: { show_no: true, field_name: "#{issuable.to_ability_name}[milestone_id]", project_id: @project.id, issuable_id: issuable.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable), use_id: true }})
- if issuable.has_attribute?(:time_estimate) - if issuable.has_attribute?(:time_estimate)
#issuable-time-tracker.block #issuable-time-tracker.block
%issuable-time-tracker{ ':time_estimate' => 'issuable.time_estimate', ':time_spent' => 'issuable.total_time_spent', ':human_time_estimate' => 'issuable.human_time_estimate', ':human_time_spent' => 'issuable.human_total_time_spent', 'stopwatch-svg' => custom_icon('icon_stopwatch'), 'docs-url' => help_page_path('workflow/time_tracking.md') } %issuable-time-tracker{ ':time_estimate' => 'issuable.time_estimate', ':time_spent' => 'issuable.total_time_spent', ':human_time_estimate' => 'issuable.human_time_estimate', ':human_time_spent' => 'issuable.human_total_time_spent', 'docs-url' => help_page_path('workflow/time_tracking.md') }
// Fallback while content is loading // Fallback while content is loading
.title.hide-collapsed .title.hide-collapsed
Time tracking Time tracking
......
...@@ -64,6 +64,10 @@ var config = { ...@@ -64,6 +64,10 @@ var config = {
'stage-2' 'stage-2'
] ]
} }
},
{
test: /\.svg$/,
use: 'raw-loader'
} }
] ]
}, },
...@@ -87,6 +91,7 @@ var config = { ...@@ -87,6 +91,7 @@ var config = {
'~': path.join(ROOT_PATH, 'app/assets/javascripts'), '~': path.join(ROOT_PATH, 'app/assets/javascripts'),
'bootstrap/js': 'bootstrap-sass/assets/javascripts/bootstrap', 'bootstrap/js': 'bootstrap-sass/assets/javascripts/bootstrap',
'emoji-aliases$': path.join(ROOT_PATH, 'fixtures/emojis/aliases.json'), 'emoji-aliases$': path.join(ROOT_PATH, 'fixtures/emojis/aliases.json'),
'icons': path.join(ROOT_PATH, 'app/views/shared/icons'),
'vendor': path.join(ROOT_PATH, 'vendor/assets/javascripts'), 'vendor': path.join(ROOT_PATH, 'vendor/assets/javascripts'),
'vue$': 'vue/dist/vue.common.js', 'vue$': 'vue/dist/vue.common.js',
} }
......
...@@ -23,7 +23,6 @@ describe('Actions Component', () => { ...@@ -23,7 +23,6 @@ describe('Actions Component', () => {
el: document.querySelector('.test-dom-element'), el: document.querySelector('.test-dom-element'),
propsData: { propsData: {
actions: actionsMock, actions: actionsMock,
playIconSvg: '<svg></svg>',
}, },
}); });
...@@ -34,33 +33,4 @@ describe('Actions Component', () => { ...@@ -34,33 +33,4 @@ describe('Actions Component', () => {
component.$el.querySelector('.dropdown-menu li a').getAttribute('href'), component.$el.querySelector('.dropdown-menu li a').getAttribute('href'),
).toEqual(actionsMock[0].play_path); ).toEqual(actionsMock[0].play_path);
}); });
it('should render a dropdown with the provided svg', () => {
const actionsMock = [
{
name: 'bar',
play_path: 'https://gitlab.com/play',
},
{
name: 'foo',
play_path: '#',
},
];
const component = new ActionsComponent({
el: document.querySelector('.test-dom-element'),
propsData: {
actions: actionsMock,
playIconSvg: '<svg></svg>',
},
});
expect(
component.$el.querySelector('.js-dropdown-play-icon-container').children,
).toContain('svg');
expect(
component.$el.querySelector('.js-action-play-icon-container').children,
).toContain('svg');
});
}); });
...@@ -3566,6 +3566,10 @@ raw-body@~2.2.0: ...@@ -3566,6 +3566,10 @@ raw-body@~2.2.0:
iconv-lite "0.4.15" iconv-lite "0.4.15"
unpipe "1.0.0" unpipe "1.0.0"
raw-loader@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa"
rc@~1.1.6: rc@~1.1.6:
version "1.1.6" version "1.1.6"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.1.6.tgz#43651b76b6ae53b5c802f1151fa3fc3b059969c9" resolved "https://registry.yarnpkg.com/rc/-/rc-1.1.6.tgz#43651b76b6ae53b5c802f1151fa3fc3b059969c9"
......
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