Merge branch 'master' into issue-discussions-refactor
# Conflicts: # package.json
|
@ -6,6 +6,7 @@
|
|||
},
|
||||
"extends": "airbnb-base",
|
||||
"globals": {
|
||||
"__webpack_public_path__": true,
|
||||
"_": false,
|
||||
"gl": false,
|
||||
"gon": false,
|
||||
|
|
|
@ -259,7 +259,7 @@ setup-test-env:
|
|||
<<: *default-cache
|
||||
script:
|
||||
- node --version
|
||||
- yarn install --pure-lockfile --cache-folder .yarn-cache
|
||||
- yarn install --frozen-lockfile --cache-folder .yarn-cache
|
||||
- bundle exec rake gettext:po_to_json
|
||||
- bundle exec rake gitlab:assets:compile
|
||||
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
|
||||
|
@ -508,7 +508,7 @@ gitlab:assets:compile:
|
|||
WEBPACK_REPORT: "true"
|
||||
NO_COMPRESSION: "true"
|
||||
script:
|
||||
- yarn install --pure-lockfile --production --cache-folder .yarn-cache
|
||||
- yarn install --frozen-lockfile --production --cache-folder .yarn-cache
|
||||
- bundle exec rake gettext:po_to_json
|
||||
- bundle exec rake gitlab:assets:compile
|
||||
artifacts:
|
||||
|
@ -522,7 +522,7 @@ karma:
|
|||
<<: *dedicated-runner
|
||||
<<: *except-docs
|
||||
<<: *pull-cache
|
||||
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.13-chrome-59.0-node-7.1-postgresql-9.6"
|
||||
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.13-chrome-60.0-node-7.1-postgresql-9.6"
|
||||
stage: test
|
||||
variables:
|
||||
BABEL_ENV: "coverage"
|
||||
|
|
166
CHANGELOG.md
|
@ -2,6 +2,172 @@
|
|||
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||
entry.
|
||||
|
||||
## 9.5.0 (2017-08-22)
|
||||
|
||||
- [FIXED] Fix timeouts when creating projects in groups with many members. !13508
|
||||
- [FIXED] Improve API pagination headers when no record found. !13629 (Jordan Patterson)
|
||||
- [FIXED] Fix deleting GitLab Pages files when a project is removed. !13631
|
||||
- [FIXED] Fix commit list not loading the correct page when scrolling.
|
||||
- [OTHER] Cache the number of forks of a project. !13535
|
||||
- GPG signed commits integration. !9546 (Alexis Reigel)
|
||||
- Alert the user if a Wiki page changed while they were editing it in order to prevent overwriting changes. !9707 (Hiroyuki Sato)
|
||||
- Add custom linter for inline JavaScript to haml_lint. !9742 (winniehell)
|
||||
- Add /shrug and /tableflip commands. !10068 (Alex Ives)
|
||||
- Allow wiki pages to be renamed in the UI. !10069 (wendy0402)
|
||||
- Insert user name directly without encoding. !10085 (Nathan Neulinger <nneul@neulinger.org>)
|
||||
- Avoid plucking Todo ids in TodoService. !10845
|
||||
- Handle errors while a project is being deleted asynchronously. !11088
|
||||
- Decrease ABC threshold to 56.96. !11227 (Maxim Rydkin)
|
||||
- Remove Mattermost team when deleting a group. !11362
|
||||
- Block access to failing repository storage. !11449
|
||||
- Add coordinator url to admin area runner page. !11603
|
||||
- Allow testing any events for project hooks and system hooks. !11728 (Alexander Randa (@randaalex))
|
||||
- Disallow running the pipeline if ref is protected and user cannot merge the branch or create the tag. !11910
|
||||
- Remove project_key from the Jira configuration. !12050
|
||||
- Add CSRF token verification to API. !12154 (Vitaliy @blackst0ne Klachkov)
|
||||
- Fixes needed when GitLab sign-in is not enabled. !12491 (Robin Bobbitt)
|
||||
- Lazy load images for better Frontend performance. !12503
|
||||
- Replaces dashboard/event_filters.feature spinach with rspec. !12651 (Alexander Randa (@randaalex))
|
||||
- Toggle import description with import_sources_enabled. !12691 (Brianna Kicia)
|
||||
- Bump scss-lint to 0.54.0. !12733 (Takuya Noguchi)
|
||||
- Enable SpaceAfterComma in scss-lint. !12734 (Takuya Noguchi)
|
||||
- Remove CSS for nprogress removed. !12737 (Takuya Noguchi)
|
||||
- Enable UnnecessaryParentReference in scss-lint. !12738 (Takuya Noguchi)
|
||||
- Extract "@request.env[devise.mapping] = Devise.mappings[:user]" to a test helper. !12742 (Jacopo Beschi @jacopo-beschi)
|
||||
- Enable ImportPath in scss-lint. !12749 (Takuya Noguchi)
|
||||
- Enable PropertySpelling in scss-lint. !12752 (Takuya Noguchi)
|
||||
- Add API for protected branches to allow for wildcard matching and no access restrictions. !12756 (Eric Yu)
|
||||
- refactor initializations in dropzone_input.js. !12768 (Brandon Everett)
|
||||
- Improve CSS for global nav dropdown UI. !12772 (Takuya Noguchi)
|
||||
- Remove public/ci/favicon.ico. !12803 (Takuya Noguchi)
|
||||
- Enable DeclarationOrder in scss-lint. !12805 (Takuya Noguchi)
|
||||
- Increase width of dropdown menus automatically. !12809 (Thomas Wucher)
|
||||
- Enable BangFormat in scss-lint [ci skip]. !12815 (Takuya Noguchi)
|
||||
- Added /duplicate quick action to close a duplicate issue. !12845 (Ryan Scott)
|
||||
- Make all application-settings accessible through the API. !12851
|
||||
- Remove Inactive Personal Access Tokens list from Access Tokens page. !12866
|
||||
- Replaces dashboard/dashboard.feature spinach with rspec. !12876 (Alexander Randa (@randaalex))
|
||||
- Reduce memory usage of the GitHub importer. !12886
|
||||
- Bump fog-core to 1.44.3 and fog providers' plugins to latest. !12897 (Takuya Noguchi)
|
||||
- Use only CSS to truncate commit message in blame. !12900 (Takuya Noguchi)
|
||||
- Protect manual actions against protected tag too. !12908
|
||||
- Allow to configure automatic retry of a failed CI/CD job. !12909
|
||||
- Remove help message about prioritized labels for non-members. !12912 (Takuya Noguchi)
|
||||
- Add link to doc/api/ci/lint.md. !12914 (Takuya Noguchi)
|
||||
- Add RequestCache which makes caching with RequestStore easier. !12920
|
||||
- Free up some top level words, reject top level groups named like files in the public folder. !12932
|
||||
- Extend API for Group Secret Variable. !12936
|
||||
- Hide description about protected branches to non-member. !12945 (Takuya Noguchi)
|
||||
- Support custom directory in gitlab:backup:create task. !12984 (Markus Koller)
|
||||
- Raise guessed encoding confidence threshold to 50. !12990
|
||||
- Add author_id & assignee_id param to /issues API. !13004
|
||||
- Fix today day highlight in calendar. !13048
|
||||
- Prevent LDAP login callback from being called with a GET request. !13059
|
||||
- Add top-level merge_requests API endpoint. !13060
|
||||
- Handle maximum pages artifacts size correctly. !13072
|
||||
- Enable gitaly_post_upload_pack by default. !13078
|
||||
- Add Prometheus metrics exporter to Sidekiq. !13082
|
||||
- Fix improperly skipped backups of wikis. !13096
|
||||
- Projects can be created from templates. !13108
|
||||
- Fix the /projects/:id/repository/branches endpoint to handle dots in the branch name when the project full path contains a `/`. !13115
|
||||
- Fix project logos that are not centered vertically on list pages. !13124 (Florian Lemaitre)
|
||||
- Derive project path from import URL. !13131
|
||||
- Fix deletion of deploy keys linked to other projects. !13162
|
||||
- repository archive download url now ends with selected file extension. !13178 (haseebeqx)
|
||||
- Show auto-generated avatars for Groups without avatars. !13188
|
||||
- Allow any logged in users to read_users_list even if it's restricted. !13201
|
||||
- Unlock stuck merge request and set the proper state. !13207
|
||||
- Fix timezone inconsistencies in user contribution graph. !13208
|
||||
- Fix Issue board when using Ruby 2.4. !13220
|
||||
- Don't rename namespace called system when upgrading from 9.1.x to 9.5. !13228
|
||||
- Fix encoding error for WebHook logging. !13230 (Alexander Randa (@randaalex))
|
||||
- Uniquify reserved word usernames on OAuth user creation. !13244 (Robin Bobbitt)
|
||||
- Expose target_iid in Events API. !13247 (sue445)
|
||||
- Add star for action scope, in order to delete image from registry. !13248 (jean)
|
||||
- Make Delete Merged Branches handle wildcard protected branches correctly. !13251
|
||||
- Fix an order of operations for CI connection error message in merge request widget. !13252
|
||||
- Don't send rejection mails for all auto-generated mails. !13254
|
||||
- Expose noteable_iid in Note. !13265 (sue445)
|
||||
- Fix pipeline_schedules pages when active schedule has an abnormal state. !13286
|
||||
- Move some code from services to workers in order to improve performance. !13326
|
||||
- Fix destroy of case-insensitive conflicting redirects. !13357
|
||||
- Fix the /projects/:id/repository/tags endpoint to handle dots in the tag name when the project full path contains a `/`. !13368
|
||||
- Fix the /projects/:id/repository/commits endpoint to handle dots in the ref name when the project full path contains a `/`. !13370
|
||||
- Project pending delete no longer return 500 error in admins projects view. !13389
|
||||
- Use full path of user's avatar in webhooks. !13401 (Vitaliy @blackst0ne Klachkov)
|
||||
- Make GPGME temporary directory handling thread safe. !13481 (Alexis Reigel)
|
||||
- Add support for kube_namespace in Metrics queries. !16169
|
||||
- Fix bar chart does not display label at 0 hour. !35136 (Jason Dai)
|
||||
- Use project_ref_path to create the link to a branch to fix links that 404.
|
||||
- Declare related resources into V4 API entities.
|
||||
- Add Slack and JIRA services counts to Usage Data.
|
||||
- Prevent web hook and project service background jobs from going to the dead jobs queue.
|
||||
- Display specific error message when JIRA test fails.
|
||||
- clean up merge request widget UI.
|
||||
- Associate Issues tab only with internal issues tracker.
|
||||
- Remove events column from notification settings table.
|
||||
- Clarifies and rearranges the input variables on the kubernetes integration page and adjusts the docs slightly to meet the same order.
|
||||
- Respect blockquote line breaks in markdown.
|
||||
- Update confidential issue UI - add confidential visibility and settings to sidebar.
|
||||
- Add icons to contextual sidebars.
|
||||
- Make contextual sidebar collapsible.
|
||||
- Update Pipeline's badge count in Merge Request and Commits view to match real-time content.
|
||||
- Added link to the MR widget that directs to the monitoring dashboard.
|
||||
- Use jQuery to control scroll behavior in job log for cross browser consistency.
|
||||
- move edit comment button outside of dropdown.
|
||||
- Updates vue resource and code according to breaking changes.
|
||||
- Add GitHub imported projects count to usage data.
|
||||
- Rename about to overview for group and project page.
|
||||
- Prevent disabled pagination button to be clicked.
|
||||
- Remove coffee-rails gem. (Takuya Noguchi)
|
||||
- Remove net-ssh gem. (Takuya Noguchi)
|
||||
- Bump rubocop to 0.49.1 and rubocop-rspec to 1.15.1. (Takuya Noguchi)
|
||||
- improve file upload/replace experience.
|
||||
- allow closing Cycle Analytics intro box in firefox.
|
||||
- Fix label creation from new list for subgroup projects.
|
||||
- fix transient js error in rspec tests.
|
||||
- fix jump to next discussion button.
|
||||
- Fix translations for Star/Unstar in JS file.
|
||||
- Improve mobile sidebar.
|
||||
- Rename Pipelines tab to CI / CD in new navigation.
|
||||
- Fix display of new diff comments after changing b between diff views.
|
||||
- Store & use ConvDev percentages returned by the Version app.
|
||||
- Fixes new issue button for failed job returning 404.
|
||||
- Align OR separator to center in new project page.
|
||||
- Add filtered search to group issue dashboard.
|
||||
- Cache Appearance instances in Redis.
|
||||
- Fixed breadcrumbs title aggressively collapsing.
|
||||
- Better caching and indexing of broadcast messages.
|
||||
- Moved diff changed files into a dropdown.
|
||||
- Improve performance of large (initial) push into default branch.
|
||||
- Improve performance of checking for projects on the projects dashboard.
|
||||
- Eager load project creators for project dashboards.
|
||||
- Modify if condition to be more readable.
|
||||
- Fix links to group milestones from issue and merge request sidebar.
|
||||
- Remove hidden symlinks from project import files.
|
||||
- Fixed sign-in restrictions buttons not toggling active state.
|
||||
- Fix replying to commit comments on merge requests created from forks.
|
||||
- Support Markdown references, autocomplete, and quick actions for group milestones.
|
||||
- Cache recent projects for group-level new resource creation.
|
||||
- Fix API responses when dealing with txt files.
|
||||
- Fix project milestones import when projects belongs to a group.
|
||||
- Fix Mattermost integration.
|
||||
- Memoize the number of personal projects a user has to reduce COUNT queries.
|
||||
- Merge issuable "reopened" state into "opened".
|
||||
- Migrate events into a new format to reduce the storage necessary and improve performance.
|
||||
- MR branch link now links to tree instead of commits.
|
||||
- Use Prev/Next pagination for exploring projects.
|
||||
- Pass before_script and script as-is preserving arrays.
|
||||
- Change project FK migration to skip existing FKs.
|
||||
- Remove redundant query when retrieving the most recent push of a user.
|
||||
- Re-organise "issues" indexes for faster ordering.
|
||||
- Disallow Git URLs that include a username or hostname beginning with a non-alphanumeric character.
|
||||
- Fix search box losing focus when typing.
|
||||
- Add structured logging for Rails processes.
|
||||
- Skip oAuth authorization for trusted applications.
|
||||
- Use a specialized class for querying events to improve performance.
|
||||
- Update build badges to be pipeline badges and display passing instead of success.
|
||||
|
||||
## 9.4.5 (2017-08-14)
|
||||
|
||||
- Fix deletion of deploy keys linked to other projects. !13162
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.30.0
|
||||
0.33.0
|
||||
|
|
8
Gemfile
|
@ -144,8 +144,6 @@ end
|
|||
|
||||
# State machine
|
||||
gem 'state_machines-activerecord', '~> 0.4.0'
|
||||
# Run events after state machine commits
|
||||
gem 'after_commit_queue', '~> 1.3.0'
|
||||
|
||||
# Issue tags
|
||||
gem 'acts-as-taggable-on', '~> 4.0'
|
||||
|
@ -154,7 +152,7 @@ gem 'acts-as-taggable-on', '~> 4.0'
|
|||
gem 'sidekiq', '~> 5.0'
|
||||
gem 'sidekiq-cron', '~> 0.6.0'
|
||||
gem 'redis-namespace', '~> 1.5.2'
|
||||
gem 'sidekiq-limit_fetch', '~> 3.4'
|
||||
gem 'sidekiq-limit_fetch', '~> 3.4', require: false
|
||||
|
||||
# Cron Parser
|
||||
gem 'rufus-scheduler', '~> 3.4'
|
||||
|
@ -289,7 +287,7 @@ group :metrics do
|
|||
gem 'influxdb', '~> 0.2', require: false
|
||||
|
||||
# Prometheus
|
||||
gem 'prometheus-client-mmap', '~>0.7.0.beta11'
|
||||
gem 'prometheus-client-mmap', '~>0.7.0.beta12'
|
||||
gem 'raindrops', '~> 0.18'
|
||||
end
|
||||
|
||||
|
@ -403,7 +401,7 @@ group :ed25519 do
|
|||
end
|
||||
|
||||
# Gitaly GRPC client
|
||||
gem 'gitaly', '~> 0.27.0'
|
||||
gem 'gitaly', '~> 0.29.0'
|
||||
|
||||
gem 'toml-rb', '~> 0.3.15', require: false
|
||||
|
||||
|
|
15
Gemfile.lock
|
@ -46,8 +46,6 @@ GEM
|
|||
ice_nine (~> 0.11.0)
|
||||
memoizable (~> 0.4.0)
|
||||
addressable (2.3.8)
|
||||
after_commit_queue (1.3.0)
|
||||
activerecord (>= 3.0)
|
||||
akismet (2.0.0)
|
||||
allocations (1.0.5)
|
||||
arel (6.0.4)
|
||||
|
@ -277,7 +275,7 @@ GEM
|
|||
po_to_json (>= 1.0.0)
|
||||
rails (>= 3.2.0)
|
||||
gherkin-ruby (0.3.2)
|
||||
gitaly (0.27.0)
|
||||
gitaly (0.29.0)
|
||||
google-protobuf (~> 3.1)
|
||||
grpc (~> 1.0)
|
||||
github-linguist (4.7.6)
|
||||
|
@ -356,7 +354,7 @@ GEM
|
|||
activesupport
|
||||
grape (>= 0.16.0)
|
||||
rake
|
||||
grpc (1.4.0)
|
||||
grpc (1.4.5)
|
||||
google-protobuf (~> 3.1)
|
||||
googleauth (~> 0.5.1)
|
||||
haml (4.0.7)
|
||||
|
@ -621,7 +619,7 @@ GEM
|
|||
parser
|
||||
unparser
|
||||
procto (0.0.3)
|
||||
prometheus-client-mmap (0.7.0.beta11)
|
||||
prometheus-client-mmap (0.7.0.beta12)
|
||||
mmap2 (~> 2.2, >= 2.2.7)
|
||||
pry (0.10.4)
|
||||
coderay (~> 1.1.0)
|
||||
|
@ -724,7 +722,7 @@ GEM
|
|||
retriable (1.4.1)
|
||||
rinku (2.0.0)
|
||||
rotp (2.1.2)
|
||||
rouge (2.1.0)
|
||||
rouge (2.2.0)
|
||||
rqrcode (0.7.0)
|
||||
chunky_png
|
||||
rqrcode-rails3 (0.1.7)
|
||||
|
@ -960,7 +958,6 @@ DEPENDENCIES
|
|||
activerecord_sane_schema_dumper (= 0.2)
|
||||
acts-as-taggable-on (~> 4.0)
|
||||
addressable (~> 2.3.8)
|
||||
after_commit_queue (~> 1.3.0)
|
||||
akismet (~> 2.0)
|
||||
allocations (~> 1.0)
|
||||
asana (~> 0.6.0)
|
||||
|
@ -1022,7 +1019,7 @@ DEPENDENCIES
|
|||
gettext (~> 3.2.2)
|
||||
gettext_i18n_rails (~> 1.8.0)
|
||||
gettext_i18n_rails_js (~> 1.2.0)
|
||||
gitaly (~> 0.27.0)
|
||||
gitaly (~> 0.29.0)
|
||||
github-linguist (~> 4.7.0)
|
||||
gitlab-flowdock-git-hook (~> 1.0.1)
|
||||
gitlab-markup (~> 1.5.1)
|
||||
|
@ -1097,7 +1094,7 @@ DEPENDENCIES
|
|||
pg (~> 0.18.2)
|
||||
poltergeist (~> 1.9.0)
|
||||
premailer-rails (~> 1.9.7)
|
||||
prometheus-client-mmap (~> 0.7.0.beta11)
|
||||
prometheus-client-mmap (~> 0.7.0.beta12)
|
||||
pry-byebug (~> 3.4.1)
|
||||
pry-rails (~> 0.3.4)
|
||||
rack-attack (~> 4.4.1)
|
||||
|
|
|
@ -97,7 +97,7 @@ gl.issueBoards.IssueCardInner = Vue.extend({
|
|||
return `Avatar for ${assignee.name}`;
|
||||
},
|
||||
showLabel(label) {
|
||||
if (!this.list || !label) return true;
|
||||
if (!label.id) return false;
|
||||
return true;
|
||||
},
|
||||
filterByLabel(label, e) {
|
||||
|
|
|
@ -413,7 +413,7 @@ import initChangesDropdown from './init_changes_dropdown';
|
|||
case 'projects:tree:show':
|
||||
shortcut_handler = new ShortcutsNavigation();
|
||||
|
||||
if (UserFeatureHelper.isNewRepo()) break;
|
||||
if (UserFeatureHelper.isNewRepoEnabled()) break;
|
||||
|
||||
new TreeView();
|
||||
new BlobViewer();
|
||||
|
@ -433,7 +433,7 @@ import initChangesDropdown from './init_changes_dropdown';
|
|||
shortcut_handler = true;
|
||||
break;
|
||||
case 'projects:blob:show':
|
||||
if (UserFeatureHelper.isNewRepo()) break;
|
||||
if (UserFeatureHelper.isNewRepoEnabled()) break;
|
||||
new BlobViewer();
|
||||
initBlob();
|
||||
break;
|
||||
|
|
|
@ -111,8 +111,7 @@ window.GroupsSelect = (function() {
|
|||
};
|
||||
|
||||
GroupsSelect.prototype.forceOverflow = function (e) {
|
||||
const itemHeight = this.dropdown.querySelector('.select2-result:first-child').clientHeight;
|
||||
this.dropdown.style.height = `${Math.floor(this.dropdown.scrollHeight - (itemHeight * 0.9))}px`;
|
||||
this.dropdown.style.height = `${Math.floor(this.dropdown.scrollHeight)}px`;
|
||||
};
|
||||
|
||||
GroupsSelect.PER_PAGE = 20;
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
import Cookies from 'js-cookie';
|
||||
|
||||
function isNewRepo() {
|
||||
return Cookies.get('new_repo') === 'true';
|
||||
}
|
||||
|
||||
const UserFeatureHelper = {
|
||||
isNewRepo,
|
||||
export default {
|
||||
isNewRepoEnabled() {
|
||||
return Cookies.get('new_repo') === 'true';
|
||||
},
|
||||
};
|
||||
|
||||
export default UserFeatureHelper;
|
||||
|
|
|
@ -386,15 +386,15 @@
|
|||
w.gl.utils.backOff = (fn, timeout = 60000) => {
|
||||
const maxInterval = 32000;
|
||||
let nextInterval = 2000;
|
||||
|
||||
const startTime = Date.now();
|
||||
let timeElapsed = 0;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const stop = arg => ((arg instanceof Error) ? reject(arg) : resolve(arg));
|
||||
|
||||
const next = () => {
|
||||
if (Date.now() - startTime < timeout) {
|
||||
setTimeout(fn.bind(null, next, stop), nextInterval);
|
||||
if (timeElapsed < timeout) {
|
||||
setTimeout(() => fn(next, stop), nextInterval);
|
||||
timeElapsed += nextInterval;
|
||||
nextInterval = Math.min(nextInterval + nextInterval, maxInterval);
|
||||
} else {
|
||||
reject(new Error('BACKOFF_TIMEOUT'));
|
||||
|
|
|
@ -4,10 +4,10 @@ export default class ProjectSelectComboButton {
|
|||
constructor(select) {
|
||||
this.projectSelectInput = $(select);
|
||||
this.newItemBtn = $('.new-project-item-link');
|
||||
this.newItemBtnBaseText = this.newItemBtn.data('label');
|
||||
this.itemType = this.deriveItemTypeFromLabel();
|
||||
this.resourceType = this.newItemBtn.data('type');
|
||||
this.resourceLabel = this.newItemBtn.data('label');
|
||||
this.formattedText = this.deriveTextVariants();
|
||||
this.groupId = this.projectSelectInput.data('groupId');
|
||||
|
||||
this.bindEvents();
|
||||
this.initLocalStorage();
|
||||
}
|
||||
|
@ -23,9 +23,7 @@ export default class ProjectSelectComboButton {
|
|||
const localStorageIsSafe = AccessorUtilities.isLocalStorageAccessSafe();
|
||||
|
||||
if (localStorageIsSafe) {
|
||||
const itemTypeKebabed = this.newItemBtnBaseText.toLowerCase().split(' ').join('-');
|
||||
|
||||
this.localStorageKey = ['group', this.groupId, itemTypeKebabed, 'recent-project'].join('-');
|
||||
this.localStorageKey = ['group', this.groupId, this.formattedText.localStorageItemType, 'recent-project'].join('-');
|
||||
this.setBtnTextFromLocalStorage();
|
||||
}
|
||||
}
|
||||
|
@ -57,19 +55,14 @@ export default class ProjectSelectComboButton {
|
|||
setNewItemBtnAttributes(project) {
|
||||
if (project) {
|
||||
this.newItemBtn.attr('href', project.url);
|
||||
this.newItemBtn.text(`${this.newItemBtnBaseText} in ${project.name}`);
|
||||
this.newItemBtn.text(`${this.formattedText.defaultTextPrefix} in ${project.name}`);
|
||||
this.newItemBtn.enable();
|
||||
} else {
|
||||
this.newItemBtn.text(`Select project to create ${this.itemType}`);
|
||||
this.newItemBtn.text(`Select project to create ${this.formattedText.presetTextSuffix}`);
|
||||
this.newItemBtn.disable();
|
||||
}
|
||||
}
|
||||
|
||||
deriveItemTypeFromLabel() {
|
||||
// label is either 'New issue' or 'New merge request'
|
||||
return this.newItemBtnBaseText.split(' ').slice(1).join(' ');
|
||||
}
|
||||
|
||||
getProjectFromLocalStorage() {
|
||||
const projectString = localStorage.getItem(this.localStorageKey);
|
||||
|
||||
|
@ -81,5 +74,19 @@ export default class ProjectSelectComboButton {
|
|||
|
||||
localStorage.setItem(this.localStorageKey, projectString);
|
||||
}
|
||||
|
||||
deriveTextVariants() {
|
||||
const defaultTextPrefix = this.resourceLabel;
|
||||
|
||||
// the trailing slice call depluralizes each of these strings (e.g. new-issues -> new-issue)
|
||||
const localStorageItemType = `new-${this.resourceType.split('_').join('-').slice(0, -1)}`;
|
||||
const presetTextSuffix = this.resourceType.split('_').join(' ').slice(0, -1);
|
||||
|
||||
return {
|
||||
localStorageItemType, // new-issue / new-merge-request
|
||||
defaultTextPrefix, // New issue / New merge request
|
||||
presetTextSuffix, // issue / merge request
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -74,7 +74,8 @@ export default {
|
|||
<tbody>
|
||||
<repo-file-options
|
||||
:is-mini="isMini"
|
||||
:project-name="projectName"/>
|
||||
:project-name="projectName"
|
||||
/>
|
||||
<repo-previous-directory
|
||||
v-if="isRoot"
|
||||
:prev-url="prevURL"
|
||||
|
@ -84,7 +85,8 @@ export default {
|
|||
:key="n"
|
||||
:loading="loading"
|
||||
:has-files="!!files.length"
|
||||
:is-mini="isMini"/>
|
||||
:is-mini="isMini"
|
||||
/>
|
||||
<repo-file
|
||||
v-for="file in files"
|
||||
:key="file.id"
|
||||
|
@ -93,7 +95,8 @@ export default {
|
|||
@linkclicked="fileClicked(file)"
|
||||
:is-tree="isTree"
|
||||
:has-files="!!files.length"
|
||||
:active-file="activeFile"/>
|
||||
:active-file="activeFile"
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
/* eslint-disable no-underscore-dangle, camelcase */
|
||||
/* global __webpack_public_path__ */
|
||||
|
||||
import monacoContext from 'monaco-editor/dev/vs/loader';
|
||||
|
||||
monacoContext.require.config({
|
||||
paths: {
|
||||
vs: `${__webpack_public_path__}monaco-editor/vs`,
|
||||
vs: `${__webpack_public_path__}monaco-editor/vs`, // eslint-disable-line camelcase
|
||||
},
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
window.__monaco_context__ = monacoContext;
|
||||
export default monacoContext.require;
|
||||
|
|
|
@ -5,5 +5,5 @@
|
|||
*/
|
||||
|
||||
if (gon && gon.webpack_public_path) {
|
||||
__webpack_public_path__ = gon.webpack_public_path; // eslint-disable-line
|
||||
__webpack_public_path__ = gon.webpack_public_path; // eslint-disable-line camelcase
|
||||
}
|
||||
|
|
|
@ -732,26 +732,41 @@
|
|||
@mixin new-style-dropdown($selector: '') {
|
||||
#{$selector}.dropdown-menu,
|
||||
#{$selector}.dropdown-menu-nav {
|
||||
.divider {
|
||||
margin: 6px 0;
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 0 1px;
|
||||
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&.divider {
|
||||
margin: 6px 0;
|
||||
|
||||
&:hover {
|
||||
background-color: $dropdown-divider-color;
|
||||
}
|
||||
}
|
||||
|
||||
&.dropdown-header {
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
a {
|
||||
a,
|
||||
button {
|
||||
border-radius: 0;
|
||||
padding: 8px 16px;
|
||||
|
||||
// make sure the text color is not overriden
|
||||
&.text-danger {
|
||||
@extend .text-danger;
|
||||
}
|
||||
|
||||
&.is-focused,
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
background-color: $gray-darker;
|
||||
background-color: $dropdown-item-hover-bg;
|
||||
color: $gl-text-color;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
|
|
|
@ -50,6 +50,8 @@
|
|||
}
|
||||
|
||||
.filtered-search-wrapper {
|
||||
@include new-style-dropdown;
|
||||
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
|
||||
|
@ -411,8 +413,6 @@
|
|||
}
|
||||
|
||||
%filter-dropdown-item-btn-hover {
|
||||
background-color: $dropdown-hover-color;
|
||||
color: $white-light;
|
||||
text-decoration: none;
|
||||
outline: 0;
|
||||
|
||||
|
@ -422,8 +422,6 @@
|
|||
}
|
||||
|
||||
.droplab-dropdown .dropdown-menu .filter-dropdown-item {
|
||||
padding: 0;
|
||||
|
||||
.btn {
|
||||
border: none;
|
||||
width: 100%;
|
||||
|
|
|
@ -132,6 +132,8 @@ ul.content-list {
|
|||
}
|
||||
|
||||
.controls {
|
||||
@include new-style-dropdown;
|
||||
|
||||
float: right;
|
||||
|
||||
> .control-text {
|
||||
|
|
|
@ -264,3 +264,41 @@
|
|||
.ajax-users-dropdown {
|
||||
min-width: 250px !important;
|
||||
}
|
||||
|
||||
// TODO: change global style
|
||||
.ajax-project-dropdown {
|
||||
&.select2-drop {
|
||||
color: $gl-text-color;
|
||||
}
|
||||
|
||||
.select2-results {
|
||||
.select2-no-results,
|
||||
.select2-searching,
|
||||
.select2-ajax-error,
|
||||
.select2-selection-limit {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.select2-result {
|
||||
padding: 0 1px;
|
||||
|
||||
.select2-match {
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.select2-result-label {
|
||||
padding: #{$gl-padding / 2} $gl-padding;
|
||||
}
|
||||
|
||||
&.select2-highlighted {
|
||||
background-color: transparent !important;
|
||||
color: $gl-text-color;
|
||||
|
||||
.select2-result-label {
|
||||
background-color: $dropdown-item-hover-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,12 +13,16 @@
|
|||
img {
|
||||
/*max-width: 100%;*/
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
img.lazy {
|
||||
min-width: 200px;
|
||||
min-height: 100px;
|
||||
background-color: $gray-lightest;
|
||||
}
|
||||
|
||||
img.js-lazy-loaded {
|
||||
img.js-lazy-loaded,
|
||||
img.emoji {
|
||||
min-width: inherit;
|
||||
min-height: inherit;
|
||||
background-color: inherit;
|
||||
|
|
|
@ -294,7 +294,7 @@ $dropdown-input-focus-shadow: rgba($dropdown-input-focus-border, .4);
|
|||
$dropdown-loading-bg: rgba(#fff, .6);
|
||||
$dropdown-chevron-size: 10px;
|
||||
$dropdown-toggle-active-border-color: darken($border-color, 14%);
|
||||
|
||||
$dropdown-item-hover-bg: $gray-darker;
|
||||
|
||||
/*
|
||||
* Filtered Search
|
||||
|
|
|
@ -104,6 +104,10 @@ $new-sidebar-collapsed-width: 50px;
|
|||
&.sidebar-icons-only {
|
||||
width: $new-sidebar-collapsed-width;
|
||||
|
||||
.nav-sidebar-inner-scroll {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.badge,
|
||||
.project-title {
|
||||
display: none;
|
||||
|
|
|
@ -1,2 +1,11 @@
|
|||
class Admin::LogsController < Admin::ApplicationController
|
||||
def show
|
||||
@loggers = [
|
||||
Gitlab::AppLogger,
|
||||
Gitlab::GitLogger,
|
||||
Gitlab::EnvironmentLogger,
|
||||
Gitlab::SidekiqLogger,
|
||||
Gitlab::RepositoryCheckLogger
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26,6 +26,13 @@ class GroupsController < Groups::ApplicationController
|
|||
|
||||
def new
|
||||
@group = Group.new
|
||||
|
||||
if params[:parent_id].present?
|
||||
parent = Group.find_by(id: params[:parent_id])
|
||||
if can?(current_user, :create_subgroup, parent)
|
||||
@group.parent = parent
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
|
|
|
@ -4,7 +4,6 @@ class Projects::ServicesController < Projects::ApplicationController
|
|||
# Authorize
|
||||
before_action :authorize_admin_project!
|
||||
before_action :service, only: [:edit, :update, :test]
|
||||
before_action :update_service, only: [:update, :test]
|
||||
|
||||
respond_to :html
|
||||
|
||||
|
@ -14,6 +13,8 @@ class Projects::ServicesController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def update
|
||||
@service.attributes = service_params[:service]
|
||||
|
||||
if @service.save(context: :manual_change)
|
||||
redirect_to(project_settings_integrations_path(@project), notice: success_message)
|
||||
else
|
||||
|
@ -24,7 +25,7 @@ class Projects::ServicesController < Projects::ApplicationController
|
|||
def test
|
||||
message = {}
|
||||
|
||||
if @service.can_test?
|
||||
if @service.can_test? && @service.update_attributes(service_params[:service])
|
||||
data = @service.test_data(project, current_user)
|
||||
outcome = @service.test(data)
|
||||
|
||||
|
@ -50,10 +51,6 @@ class Projects::ServicesController < Projects::ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def update_service
|
||||
@service.assign_attributes(service_params[:service])
|
||||
end
|
||||
|
||||
def service
|
||||
@service ||= @project.find_or_initialize_service(params[:id])
|
||||
end
|
||||
|
|
|
@ -116,6 +116,7 @@ module ApplicationSettingsHelper
|
|||
:email_author_in_body,
|
||||
:enabled_git_access_protocol,
|
||||
:gravatar_enabled,
|
||||
:hashed_storage_enabled,
|
||||
:help_page_hide_commercial_content,
|
||||
:help_page_support_url,
|
||||
:help_page_text,
|
||||
|
|
|
@ -35,18 +35,18 @@ module EventsHelper
|
|||
[event.action_name, target].join(" ")
|
||||
end
|
||||
|
||||
def event_filter_link(key, tooltip)
|
||||
def event_filter_link(key, text, tooltip)
|
||||
key = key.to_s
|
||||
active = 'active' if @event_filter.active?(key)
|
||||
link_opts = {
|
||||
class: "event-filter-link",
|
||||
class: "event-filter-link has-tooltip",
|
||||
id: "#{key}_event_filter",
|
||||
title: "Filter by #{tooltip.downcase}"
|
||||
title: tooltip
|
||||
}
|
||||
|
||||
content_tag :li, class: active do
|
||||
link_to request.path, link_opts do
|
||||
content_tag(:span, ' ' + tooltip)
|
||||
content_tag(:span, ' ' + text)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -176,7 +176,7 @@ module EventsHelper
|
|||
sanitize(
|
||||
text,
|
||||
tags: %w(a img gl-emoji b pre code p span),
|
||||
attributes: Rails::Html::WhiteListSanitizer.allowed_attributes + ['style', 'data-name', 'data-unicode-version']
|
||||
attributes: Rails::Html::WhiteListSanitizer.allowed_attributes + ['style', 'data-src', 'data-name', 'data-unicode-version']
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ module BlobViewer
|
|||
end
|
||||
|
||||
def manager_url
|
||||
'https://getcomposer.com/'
|
||||
'https://getcomposer.org/'
|
||||
end
|
||||
|
||||
def package_name
|
||||
|
|
|
@ -19,11 +19,21 @@ class BroadcastMessage < ActiveRecord::Base
|
|||
after_commit :flush_redis_cache
|
||||
|
||||
def self.current
|
||||
Rails.cache.fetch(CACHE_KEY) do
|
||||
where('ends_at > :now AND starts_at <= :now', now: Time.zone.now)
|
||||
.reorder(id: :asc)
|
||||
.to_a
|
||||
end
|
||||
messages = Rails.cache.fetch(CACHE_KEY) { current_and_future_messages.to_a }
|
||||
|
||||
return messages if messages.empty?
|
||||
|
||||
now_or_future = messages.select(&:now_or_future?)
|
||||
|
||||
# If there are cached entries but none are to be displayed we'll purge the
|
||||
# cache so we don't keep running this code all the time.
|
||||
Rails.cache.delete(CACHE_KEY) if now_or_future.empty?
|
||||
|
||||
now_or_future.select(&:now?)
|
||||
end
|
||||
|
||||
def self.current_and_future_messages
|
||||
where('ends_at > :now', now: Time.zone.now).reorder(id: :asc)
|
||||
end
|
||||
|
||||
def active?
|
||||
|
@ -38,6 +48,18 @@ class BroadcastMessage < ActiveRecord::Base
|
|||
ends_at < Time.zone.now
|
||||
end
|
||||
|
||||
def now?
|
||||
(starts_at..ends_at).cover?(Time.zone.now)
|
||||
end
|
||||
|
||||
def future?
|
||||
starts_at > Time.zone.now
|
||||
end
|
||||
|
||||
def now_or_future?
|
||||
now? || future?
|
||||
end
|
||||
|
||||
def flush_redis_cache
|
||||
Rails.cache.delete(CACHE_KEY)
|
||||
end
|
||||
|
|
|
@ -9,7 +9,7 @@ module Ci
|
|||
belongs_to :owner, class_name: 'User'
|
||||
has_one :last_pipeline, -> { order(id: :desc) }, class_name: 'Ci::Pipeline'
|
||||
has_many :pipelines
|
||||
has_many :variables, class_name: 'Ci::PipelineScheduleVariable'
|
||||
has_many :variables, class_name: 'Ci::PipelineScheduleVariable', validate: false
|
||||
|
||||
validates :cron, unless: :importing?, cron: true, presence: { unless: :importing? }
|
||||
validates :cron_timezone, cron_timezone: true, presence: { unless: :importing? }
|
||||
|
|
|
@ -4,5 +4,7 @@ module Ci
|
|||
include HasVariable
|
||||
|
||||
belongs_to :pipeline_schedule
|
||||
|
||||
validates :key, uniqueness: { scope: :pipeline_schedule_id }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,11 +1,66 @@
|
|||
module Ci
|
||||
class Stage < ActiveRecord::Base
|
||||
extend Ci::Model
|
||||
include Importable
|
||||
include HasStatus
|
||||
include Gitlab::OptimisticLocking
|
||||
|
||||
enum status: HasStatus::STATUSES_ENUM
|
||||
|
||||
belongs_to :project
|
||||
belongs_to :pipeline
|
||||
|
||||
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id
|
||||
has_many :builds, foreign_key: :commit_id
|
||||
has_many :statuses, class_name: 'CommitStatus', foreign_key: :stage_id
|
||||
has_many :builds, foreign_key: :stage_id
|
||||
|
||||
validates :project, presence: true, unless: :importing?
|
||||
validates :pipeline, presence: true, unless: :importing?
|
||||
validates :name, presence: true, unless: :importing?
|
||||
|
||||
state_machine :status, initial: :created do
|
||||
event :enqueue do
|
||||
transition created: :pending
|
||||
transition [:success, :failed, :canceled, :skipped] => :running
|
||||
end
|
||||
|
||||
event :run do
|
||||
transition any - [:running] => :running
|
||||
end
|
||||
|
||||
event :skip do
|
||||
transition any - [:skipped] => :skipped
|
||||
end
|
||||
|
||||
event :drop do
|
||||
transition any - [:failed] => :failed
|
||||
end
|
||||
|
||||
event :succeed do
|
||||
transition any - [:success] => :success
|
||||
end
|
||||
|
||||
event :cancel do
|
||||
transition any - [:canceled] => :canceled
|
||||
end
|
||||
|
||||
event :block do
|
||||
transition any - [:manual] => :manual
|
||||
end
|
||||
end
|
||||
|
||||
def update_status
|
||||
retry_optimistic_lock(self) do
|
||||
case statuses.latest.status
|
||||
when 'pending' then enqueue
|
||||
when 'running' then run
|
||||
when 'success' then succeed
|
||||
when 'failed' then drop
|
||||
when 'canceled' then cancel
|
||||
when 'manual' then block
|
||||
when 'skipped' then skip
|
||||
else skip
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -39,14 +39,14 @@ class CommitStatus < ActiveRecord::Base
|
|||
scope :after_stage, -> (index) { where('stage_idx > ?', index) }
|
||||
|
||||
state_machine :status do
|
||||
event :enqueue do
|
||||
transition [:created, :skipped, :manual] => :pending
|
||||
end
|
||||
|
||||
event :process do
|
||||
transition [:skipped, :manual] => :created
|
||||
end
|
||||
|
||||
event :enqueue do
|
||||
transition [:created, :skipped, :manual] => :pending
|
||||
end
|
||||
|
||||
event :run do
|
||||
transition pending: :running
|
||||
end
|
||||
|
@ -91,6 +91,7 @@ class CommitStatus < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
StageUpdateWorker.perform_async(commit_status.stage_id)
|
||||
ExpireJobCacheWorker.perform_async(commit_status.id)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,6 +8,8 @@ module HasStatus
|
|||
ACTIVE_STATUSES = %w[pending running].freeze
|
||||
COMPLETED_STATUSES = %w[success failed canceled skipped].freeze
|
||||
ORDERED_STATUSES = %w[failed pending running manual canceled success skipped created].freeze
|
||||
STATUSES_ENUM = { created: 0, pending: 1, running: 2, success: 3,
|
||||
failed: 4, canceled: 5, skipped: 6, manual: 7 }.freeze
|
||||
|
||||
class_methods do
|
||||
def status_sql
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
module Storage
|
||||
module LegacyProject
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def disk_path
|
||||
full_path
|
||||
end
|
||||
|
||||
def ensure_storage_path_exist
|
||||
gitlab_shell.add_namespace(repository_storage_path, namespace.full_path)
|
||||
end
|
||||
|
||||
def rename_repo
|
||||
path_was = previous_changes['path'].first
|
||||
old_path_with_namespace = File.join(namespace.full_path, path_was)
|
||||
new_path_with_namespace = File.join(namespace.full_path, path)
|
||||
|
||||
Rails.logger.error "Attempting to rename #{old_path_with_namespace} -> #{new_path_with_namespace}"
|
||||
|
||||
if has_container_registry_tags?
|
||||
Rails.logger.error "Project #{old_path_with_namespace} cannot be renamed because container registry tags are present!"
|
||||
|
||||
# we currently doesn't support renaming repository if it contains images in container registry
|
||||
raise StandardError.new('Project cannot be renamed, because images are present in its container registry')
|
||||
end
|
||||
|
||||
expire_caches_before_rename(old_path_with_namespace)
|
||||
|
||||
if gitlab_shell.mv_repository(repository_storage_path, old_path_with_namespace, new_path_with_namespace)
|
||||
# If repository moved successfully we need to send update instructions to users.
|
||||
# However we cannot allow rollback since we moved repository
|
||||
# So we basically we mute exceptions in next actions
|
||||
begin
|
||||
gitlab_shell.mv_repository(repository_storage_path, "#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki")
|
||||
send_move_instructions(old_path_with_namespace)
|
||||
expires_full_path_cache
|
||||
|
||||
@old_path_with_namespace = old_path_with_namespace
|
||||
|
||||
SystemHooksService.new.execute_hooks_for(self, :rename)
|
||||
|
||||
@repository = nil
|
||||
rescue => e
|
||||
Rails.logger.error "Exception renaming #{old_path_with_namespace} -> #{new_path_with_namespace}: #{e}"
|
||||
# Returning false does not rollback after_* transaction but gives
|
||||
# us information about failing some of tasks
|
||||
false
|
||||
end
|
||||
else
|
||||
Rails.logger.error "Repository could not be renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}"
|
||||
|
||||
# if we cannot move namespace directory we should rollback
|
||||
# db changes in order to prevent out of sync between db and fs
|
||||
raise StandardError.new('repository cannot be renamed')
|
||||
end
|
||||
|
||||
Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}"
|
||||
|
||||
Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.full_path)
|
||||
Gitlab::PagesTransfer.new.rename_project(path_was, path, namespace.full_path)
|
||||
end
|
||||
|
||||
def create_repository(force: false)
|
||||
# Forked import is handled asynchronously
|
||||
return if forked? && !force
|
||||
|
||||
if gitlab_shell.add_repository(repository_storage_path, path_with_namespace)
|
||||
repository.after_create
|
||||
true
|
||||
else
|
||||
errors.add(:base, 'Failed to create repository via gitlab-shell')
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -83,6 +83,10 @@ class Event < ActiveRecord::Base
|
|||
self.inheritance_column = 'action'
|
||||
|
||||
class << self
|
||||
def model_name
|
||||
ActiveModel::Name.new(self, nil, 'event')
|
||||
end
|
||||
|
||||
def find_sti_class(action)
|
||||
if action.to_i == PUSHED
|
||||
PushEvent
|
||||
|
@ -438,6 +442,12 @@ class Event < ActiveRecord::Base
|
|||
EventForMigration.create!(new_attributes)
|
||||
end
|
||||
|
||||
def to_partial_path
|
||||
# We are intentionally using `Event` rather than `self.class` so that
|
||||
# subclasses also use the `Event` implementation.
|
||||
Event._to_partial_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def recent_update?
|
||||
|
|
|
@ -9,11 +9,8 @@ class Issue < ActiveRecord::Base
|
|||
include Spammable
|
||||
include FasterCacheKeys
|
||||
include RelativePositioning
|
||||
include IgnorableColumn
|
||||
include CreatedAtFilterable
|
||||
|
||||
ignore_column :position
|
||||
|
||||
DueDateStruct = Struct.new(:title, :name).freeze
|
||||
NoDueDate = DueDateStruct.new('No Due Date', '0').freeze
|
||||
AnyDueDate = DueDateStruct.new('Any Due Date', '').freeze
|
||||
|
|
|
@ -7,7 +7,6 @@ class MergeRequest < ActiveRecord::Base
|
|||
include IgnorableColumn
|
||||
include CreatedAtFilterable
|
||||
|
||||
ignore_column :position
|
||||
ignore_column :locked_at
|
||||
|
||||
belongs_to :target_project, class_name: "Project"
|
||||
|
|
|
@ -17,7 +17,6 @@ class Project < ActiveRecord::Base
|
|||
include ProjectFeaturesCompatibility
|
||||
include SelectForProjectAuthorization
|
||||
include Routable
|
||||
include Storage::LegacyProject
|
||||
|
||||
extend Gitlab::ConfigHelper
|
||||
|
||||
|
@ -25,6 +24,7 @@ class Project < ActiveRecord::Base
|
|||
|
||||
NUMBER_OF_PERMITTED_BOARDS = 1
|
||||
UNKNOWN_IMPORT_URL = 'http://unknown.git'.freeze
|
||||
LATEST_STORAGE_VERSION = 1
|
||||
|
||||
cache_markdown_field :description, pipeline: :description
|
||||
|
||||
|
@ -32,6 +32,8 @@ class Project < ActiveRecord::Base
|
|||
:merge_requests_enabled?, :issues_enabled?, to: :project_feature,
|
||||
allow_nil: true
|
||||
|
||||
delegate :base_dir, :disk_path, :ensure_storage_path_exists, to: :storage
|
||||
|
||||
default_value_for :archived, false
|
||||
default_value_for :visibility_level, gitlab_config_features.visibility_level
|
||||
default_value_for :container_registry_enabled, gitlab_config_features.container_registry
|
||||
|
@ -44,32 +46,24 @@ class Project < ActiveRecord::Base
|
|||
default_value_for :snippets_enabled, gitlab_config_features.snippets
|
||||
default_value_for :only_allow_merge_if_all_discussions_are_resolved, false
|
||||
|
||||
after_create :ensure_storage_path_exist
|
||||
after_create :create_project_feature, unless: :project_feature
|
||||
add_authentication_token_field :runners_token
|
||||
before_save :ensure_runners_token
|
||||
|
||||
after_save :update_project_statistics, if: :namespace_id_changed?
|
||||
|
||||
# set last_activity_at to the same as created_at
|
||||
after_create :create_project_feature, unless: :project_feature
|
||||
after_create :set_last_activity_at
|
||||
def set_last_activity_at
|
||||
update_column(:last_activity_at, self.created_at)
|
||||
end
|
||||
|
||||
after_create :set_last_repository_updated_at
|
||||
def set_last_repository_updated_at
|
||||
update_column(:last_repository_updated_at, self.created_at)
|
||||
end
|
||||
after_update :update_forks_visibility_level
|
||||
|
||||
before_destroy :remove_private_deploy_keys
|
||||
after_destroy -> { run_after_commit { remove_pages } }
|
||||
|
||||
# update visibility_level of forks
|
||||
after_update :update_forks_visibility_level
|
||||
|
||||
after_validation :check_pending_delete
|
||||
|
||||
# Legacy Storage specific hooks
|
||||
|
||||
after_save :ensure_storage_path_exist, if: :namespace_id_changed?
|
||||
# Storage specific hooks
|
||||
after_initialize :use_hashed_storage
|
||||
after_create :ensure_storage_path_exists
|
||||
after_save :ensure_storage_path_exists, if: :namespace_id_changed?
|
||||
|
||||
acts_as_taggable
|
||||
|
||||
|
@ -238,9 +232,6 @@ class Project < ActiveRecord::Base
|
|||
presence: true,
|
||||
inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } }
|
||||
|
||||
add_authentication_token_field :runners_token
|
||||
before_save :ensure_runners_token
|
||||
|
||||
mount_uploader :avatar, AvatarUploader
|
||||
has_many :uploads, as: :model, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
|
||||
|
||||
|
@ -369,7 +360,10 @@ class Project < ActiveRecord::Base
|
|||
state :failed
|
||||
|
||||
after_transition [:none, :finished, :failed] => :scheduled do |project, _|
|
||||
project.run_after_commit { add_import_job }
|
||||
project.run_after_commit do
|
||||
job_id = add_import_job
|
||||
update(import_jid: job_id) if job_id
|
||||
end
|
||||
end
|
||||
|
||||
after_transition started: :finished do |project, _|
|
||||
|
@ -484,6 +478,10 @@ class Project < ActiveRecord::Base
|
|||
@repository ||= Repository.new(full_path, self, disk_path: disk_path)
|
||||
end
|
||||
|
||||
def reload_repository!
|
||||
@repository = nil
|
||||
end
|
||||
|
||||
def container_registry_url
|
||||
if Gitlab.config.registry.enabled
|
||||
"#{Gitlab.config.registry.host_port}/#{full_path.downcase}"
|
||||
|
@ -524,17 +522,26 @@ class Project < ActiveRecord::Base
|
|||
def add_import_job
|
||||
job_id =
|
||||
if forked?
|
||||
RepositoryForkWorker.perform_async(id, forked_from_project.repository_storage_path,
|
||||
forked_from_project.full_path,
|
||||
self.namespace.full_path)
|
||||
RepositoryForkWorker.perform_async(id,
|
||||
forked_from_project.repository_storage_path,
|
||||
forked_from_project.full_path,
|
||||
self.namespace.full_path)
|
||||
else
|
||||
RepositoryImportWorker.perform_async(self.id)
|
||||
end
|
||||
|
||||
log_import_activity(job_id)
|
||||
|
||||
job_id
|
||||
end
|
||||
|
||||
def log_import_activity(job_id, type: :import)
|
||||
job_type = type.to_s.capitalize
|
||||
|
||||
if job_id
|
||||
Rails.logger.info "Import job started for #{full_path} with job ID #{job_id}"
|
||||
Rails.logger.info("#{job_type} job scheduled for #{full_path} with job ID #{job_id}.")
|
||||
else
|
||||
Rails.logger.error "Import job failed to start for #{full_path}"
|
||||
Rails.logger.error("#{job_type} job failed to create for #{full_path}.")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -543,6 +550,7 @@ class Project < ActiveRecord::Base
|
|||
ProjectCacheWorker.perform_async(self.id)
|
||||
end
|
||||
|
||||
update(import_error: nil)
|
||||
remove_import_data
|
||||
end
|
||||
|
||||
|
@ -991,6 +999,19 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def create_repository(force: false)
|
||||
# Forked import is handled asynchronously
|
||||
return if forked? && !force
|
||||
|
||||
if gitlab_shell.add_repository(repository_storage_path, disk_path)
|
||||
repository.after_create
|
||||
true
|
||||
else
|
||||
errors.add(:base, 'Failed to create repository via gitlab-shell')
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def hook_attrs(backward: true)
|
||||
attrs = {
|
||||
name: name,
|
||||
|
@ -1073,6 +1094,7 @@ class Project < ActiveRecord::Base
|
|||
!!repository.exists?
|
||||
end
|
||||
|
||||
# update visibility_level of forks
|
||||
def update_forks_visibility_level
|
||||
return unless visibility_level < visibility_level_was
|
||||
|
||||
|
@ -1200,7 +1222,8 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def pages_path
|
||||
File.join(Settings.pages.path, disk_path)
|
||||
# TODO: when we migrate Pages to work with new storage types, change here to use disk_path
|
||||
File.join(Settings.pages.path, full_path)
|
||||
end
|
||||
|
||||
def public_pages_path
|
||||
|
@ -1239,6 +1262,50 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def rename_repo
|
||||
new_full_path = build_full_path
|
||||
|
||||
Rails.logger.error "Attempting to rename #{full_path_was} -> #{new_full_path}"
|
||||
|
||||
if has_container_registry_tags?
|
||||
Rails.logger.error "Project #{full_path_was} cannot be renamed because container registry tags are present!"
|
||||
|
||||
# we currently doesn't support renaming repository if it contains images in container registry
|
||||
raise StandardError.new('Project cannot be renamed, because images are present in its container registry')
|
||||
end
|
||||
|
||||
expire_caches_before_rename(full_path_was)
|
||||
|
||||
if storage.rename_repo
|
||||
Gitlab::AppLogger.info "Project was renamed: #{full_path_was} -> #{new_full_path}"
|
||||
rename_repo_notify!
|
||||
after_rename_repo
|
||||
else
|
||||
Rails.logger.error "Repository could not be renamed: #{full_path_was} -> #{new_full_path}"
|
||||
|
||||
# if we cannot move namespace directory we should rollback
|
||||
# db changes in order to prevent out of sync between db and fs
|
||||
raise StandardError.new('repository cannot be renamed')
|
||||
end
|
||||
end
|
||||
|
||||
def rename_repo_notify!
|
||||
send_move_instructions(full_path_was)
|
||||
expires_full_path_cache
|
||||
|
||||
self.old_path_with_namespace = full_path_was
|
||||
SystemHooksService.new.execute_hooks_for(self, :rename)
|
||||
|
||||
reload_repository!
|
||||
end
|
||||
|
||||
def after_rename_repo
|
||||
path_before_change = previous_changes['path'].first
|
||||
|
||||
Gitlab::UploadsTransfer.new.rename_project(path_before_change, self.path, namespace.full_path)
|
||||
Gitlab::PagesTransfer.new.rename_project(path_before_change, self.path, namespace.full_path)
|
||||
end
|
||||
|
||||
def running_or_pending_build_count(force: false)
|
||||
Rails.cache.fetch(['projects', id, 'running_or_pending_build_count'], force: force) do
|
||||
builds.running_or_pending.count(:all)
|
||||
|
@ -1397,6 +1464,10 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def full_path_was
|
||||
File.join(namespace.full_path, previous_changes['path'].first)
|
||||
end
|
||||
|
||||
alias_method :name_with_namespace, :full_name
|
||||
alias_method :human_name, :full_name
|
||||
# @deprecated cannot remove yet because it has an index with its name in elasticsearch
|
||||
|
@ -1406,8 +1477,36 @@ class Project < ActiveRecord::Base
|
|||
Projects::ForksCountService.new(self).count
|
||||
end
|
||||
|
||||
def legacy_storage?
|
||||
self.storage_version.nil?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def storage
|
||||
@storage ||=
|
||||
if self.storage_version && self.storage_version >= 1
|
||||
Storage::HashedProject.new(self)
|
||||
else
|
||||
Storage::LegacyProject.new(self)
|
||||
end
|
||||
end
|
||||
|
||||
def use_hashed_storage
|
||||
if self.new_record? && current_application_settings.hashed_storage_enabled
|
||||
self.storage_version = LATEST_STORAGE_VERSION
|
||||
end
|
||||
end
|
||||
|
||||
# set last_activity_at to the same as created_at
|
||||
def set_last_activity_at
|
||||
update_column(:last_activity_at, self.created_at)
|
||||
end
|
||||
|
||||
def set_last_repository_updated_at
|
||||
update_column(:last_repository_updated_at, self.created_at)
|
||||
end
|
||||
|
||||
def cross_namespace_reference?(from)
|
||||
case from
|
||||
when Project
|
||||
|
|
|
@ -24,6 +24,8 @@ class KubernetesService < DeploymentService
|
|||
validates :token
|
||||
end
|
||||
|
||||
before_validation :enforce_namespace_to_lower_case
|
||||
|
||||
validates :namespace,
|
||||
allow_blank: true,
|
||||
length: 1..63,
|
||||
|
@ -207,4 +209,8 @@ class KubernetesService < DeploymentService
|
|||
max_session_time: current_application_settings.terminal_max_session_time
|
||||
}
|
||||
end
|
||||
|
||||
def enforce_namespace_to_lower_case
|
||||
self.namespace = self.namespace&.downcase
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
module Storage
|
||||
class HashedProject
|
||||
attr_accessor :project
|
||||
delegate :gitlab_shell, :repository_storage_path, to: :project
|
||||
|
||||
ROOT_PATH_PREFIX = '@hashed'.freeze
|
||||
|
||||
def initialize(project)
|
||||
@project = project
|
||||
end
|
||||
|
||||
# Base directory
|
||||
#
|
||||
# @return [String] directory where repository is stored
|
||||
def base_dir
|
||||
"#{ROOT_PATH_PREFIX}/#{disk_hash[0..1]}/#{disk_hash[2..3]}" if disk_hash
|
||||
end
|
||||
|
||||
# Disk path is used to build repository and project's wiki path on disk
|
||||
#
|
||||
# @return [String] combination of base_dir and the repository own name without `.git` or `.wiki.git` extensions
|
||||
def disk_path
|
||||
"#{base_dir}/#{disk_hash}" if disk_hash
|
||||
end
|
||||
|
||||
def ensure_storage_path_exists
|
||||
gitlab_shell.add_namespace(repository_storage_path, base_dir)
|
||||
end
|
||||
|
||||
def rename_repo
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Generates the hash for the project path and name on disk
|
||||
# If you need to refer to the repository on disk, use the `#disk_path`
|
||||
def disk_hash
|
||||
@disk_hash ||= Digest::SHA2.hexdigest(project.id.to_s) if project.id
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,51 @@
|
|||
module Storage
|
||||
class LegacyProject
|
||||
attr_accessor :project
|
||||
delegate :namespace, :gitlab_shell, :repository_storage_path, to: :project
|
||||
|
||||
def initialize(project)
|
||||
@project = project
|
||||
end
|
||||
|
||||
# Base directory
|
||||
#
|
||||
# @return [String] directory where repository is stored
|
||||
def base_dir
|
||||
namespace.full_path
|
||||
end
|
||||
|
||||
# Disk path is used to build repository and project's wiki path on disk
|
||||
#
|
||||
# @return [String] combination of base_dir and the repository own name without `.git` or `.wiki.git` extensions
|
||||
def disk_path
|
||||
project.full_path
|
||||
end
|
||||
|
||||
def ensure_storage_path_exists
|
||||
return unless namespace
|
||||
|
||||
gitlab_shell.add_namespace(repository_storage_path, base_dir)
|
||||
end
|
||||
|
||||
def rename_repo
|
||||
new_full_path = project.build_full_path
|
||||
|
||||
if gitlab_shell.mv_repository(repository_storage_path, project.full_path_was, new_full_path)
|
||||
# If repository moved successfully we need to send update instructions to users.
|
||||
# However we cannot allow rollback since we moved repository
|
||||
# So we basically we mute exceptions in next actions
|
||||
begin
|
||||
gitlab_shell.mv_repository(repository_storage_path, "#{project.full_path_was}.wiki", "#{new_full_path}.wiki")
|
||||
return true
|
||||
rescue => e
|
||||
Rails.logger.error "Exception renaming #{project.full_path_was} -> #{new_full_path}: #{e}"
|
||||
# Returning false does not rollback after_* transaction but gives
|
||||
# us information about failing some of tasks
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
|
@ -837,7 +837,12 @@ class User < ActiveRecord::Base
|
|||
create_namespace!(path: username, name: username) unless namespace
|
||||
|
||||
if username_changed?
|
||||
namespace.update_attributes(path: username, name: username)
|
||||
unless namespace.update_attributes(path: username, name: username)
|
||||
namespace.errors.each do |attribute, message|
|
||||
self.errors.add(:"namespace_#{attribute}", message)
|
||||
end
|
||||
raise ActiveRecord::RecordInvalid.new(namespace)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ class GroupPolicy < BasePolicy
|
|||
condition(:master) { access_level >= GroupMember::MASTER }
|
||||
condition(:reporter) { access_level >= GroupMember::REPORTER }
|
||||
|
||||
condition(:nested_groups_supported, scope: :global) { Group.supports_nested_groups? }
|
||||
|
||||
condition(:has_projects) do
|
||||
GroupProjectsFinder.new(group: @subject, current_user: @user).execute.any?
|
||||
end
|
||||
|
@ -42,7 +44,7 @@ class GroupPolicy < BasePolicy
|
|||
enable :change_visibility_level
|
||||
end
|
||||
|
||||
rule { owner & can_create_group }.enable :create_subgroup
|
||||
rule { owner & can_create_group & nested_groups_supported }.enable :create_subgroup
|
||||
|
||||
rule { public_group | logged_in_viewable }.enable :view_globally
|
||||
|
||||
|
|
|
@ -176,9 +176,14 @@ module Ci
|
|||
end
|
||||
|
||||
def error(message, save: false)
|
||||
pipeline.errors.add(:base, message)
|
||||
pipeline.drop if save
|
||||
pipeline
|
||||
pipeline.tap do
|
||||
pipeline.errors.add(:base, message)
|
||||
|
||||
if save
|
||||
pipeline.drop
|
||||
update_merge_requests_head_pipeline
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def pipeline_created_counter
|
||||
|
|
|
@ -13,9 +13,9 @@ module Groups
|
|||
return @group
|
||||
end
|
||||
|
||||
if @group.parent && !can?(current_user, :admin_group, @group.parent)
|
||||
if @group.parent && !can?(current_user, :create_subgroup, @group.parent)
|
||||
@group.parent = nil
|
||||
@group.errors.add(:parent_id, 'manage access required to create subgroup')
|
||||
@group.errors.add(:parent_id, 'You don’t have permission to create a subgroup in this group.')
|
||||
|
||||
return @group
|
||||
end
|
||||
|
|
|
@ -13,7 +13,7 @@ module Groups
|
|||
# Execute the destruction of the models immediately to ensure atomic cleanup.
|
||||
# Skip repository removal because we remove directory with namespace
|
||||
# that contain all these repositories
|
||||
::Projects::DestroyService.new(project, current_user, skip_repo: true).execute
|
||||
::Projects::DestroyService.new(project, current_user, skip_repo: project.legacy_storage?).execute
|
||||
end
|
||||
|
||||
group.children.each do |group|
|
||||
|
|
|
@ -3,6 +3,8 @@ module MergeRequests
|
|||
def execute
|
||||
return error('Invalid issue iid') unless issue_iid.present? && issue.present?
|
||||
|
||||
params[:label_ids] = issue.label_ids if issue.label_ids.any?
|
||||
|
||||
result = CreateBranchService.new(project, current_user).execute(branch_name, ref)
|
||||
return result if result[:status] == :error
|
||||
|
||||
|
@ -43,7 +45,8 @@ module MergeRequests
|
|||
{
|
||||
source_project_id: project.id,
|
||||
source_branch: branch_name,
|
||||
target_project_id: project.id
|
||||
target_project_id: project.id,
|
||||
milestone_id: issue.milestone_id
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -9,7 +9,8 @@ module Projects
|
|||
class HousekeepingService < BaseService
|
||||
include Gitlab::CurrentSettings
|
||||
|
||||
LEASE_TIMEOUT = 3600
|
||||
# Timeout set to 24h
|
||||
LEASE_TIMEOUT = 86400
|
||||
|
||||
class LeaseTaken < StandardError
|
||||
def to_s
|
||||
|
|
|
@ -35,16 +35,18 @@ module Users
|
|||
Groups::DestroyService.new(group, current_user).execute
|
||||
end
|
||||
|
||||
namespace = user.namespace
|
||||
namespace.prepare_for_destroy
|
||||
|
||||
user.personal_projects.each do |project|
|
||||
# Skip repository removal because we remove directory with namespace
|
||||
# that contain all this repositories
|
||||
::Projects::DestroyService.new(project, current_user, skip_repo: true).execute
|
||||
::Projects::DestroyService.new(project, current_user, skip_repo: project.legacy_storage?).execute
|
||||
end
|
||||
|
||||
MigrateToGhostUserService.new(user).execute unless options[:hard_delete]
|
||||
|
||||
# Destroy the namespace after destroying the user since certain methods may depend on the namespace existing
|
||||
namespace = user.namespace
|
||||
user_data = user.destroy
|
||||
namespace.really_destroy!
|
||||
|
||||
|
|
|
@ -362,7 +362,9 @@
|
|||
%fieldset
|
||||
%legend Background Jobs
|
||||
%p
|
||||
These settings require a restart to take effect.
|
||||
These settings require a
|
||||
= link_to 'restart', help_page_path('administration/restart_gitlab')
|
||||
to take effect.
|
||||
.form-group
|
||||
.col-sm-offset-2.col-sm-10
|
||||
.checkbox
|
||||
|
@ -490,6 +492,16 @@
|
|||
|
||||
%fieldset
|
||||
%legend Repository Storage
|
||||
.form-group
|
||||
.col-sm-offset-2.col-sm-10
|
||||
.checkbox
|
||||
= f.label :hashed_storage_enabled do
|
||||
= f.check_box :hashed_storage_enabled
|
||||
Create new projects using hashed storage paths
|
||||
.help-block
|
||||
Enable immutable, hash-based paths and repository names to store repositories on disk. This prevents
|
||||
repositories from having to be moved or renamed when the Project URL changes and may improve disk I/O performance.
|
||||
%em (EXPERIMENTAL)
|
||||
.form-group
|
||||
= f.label :repository_storages, 'Storage paths for new projects', class: 'control-label col-sm-2'
|
||||
.col-sm-10
|
||||
|
@ -499,6 +511,7 @@
|
|||
= succeed "." do
|
||||
= link_to "repository storages documentation", help_page_path("administration/repository_storages")
|
||||
|
||||
|
||||
%fieldset
|
||||
%legend Repository Checks
|
||||
.form-group
|
||||
|
|
|
@ -1,25 +1,21 @@
|
|||
- @no_container = true
|
||||
- page_title "Logs"
|
||||
- loggers = [Gitlab::GitLogger, Gitlab::AppLogger,
|
||||
Gitlab::EnvironmentLogger, Gitlab::SidekiqLogger,
|
||||
Gitlab::RepositoryCheckLogger]
|
||||
= render 'admin/monitoring/head'
|
||||
|
||||
%div{ class: container_class }
|
||||
%ul.nav-links.log-tabs
|
||||
- loggers.each do |klass|
|
||||
%li{ class: active_when(klass == Gitlab::GitLogger) }>
|
||||
= link_to klass::file_name, "##{klass::file_name_noext}",
|
||||
'data-toggle' => 'tab'
|
||||
- @loggers.each do |klass|
|
||||
%li{ class: active_when(klass == @loggers.first) }>
|
||||
= link_to klass.file_name, "##{klass.file_name_noext}", data: { toggle: 'tab' }
|
||||
.row-content-block
|
||||
To prevent performance issues admin logs output the last 2000 lines
|
||||
.tab-content
|
||||
- loggers.each do |klass|
|
||||
.tab-pane{ class: active_when(klass == Gitlab::GitLogger), id: klass::file_name_noext }
|
||||
- @loggers.each do |klass|
|
||||
.tab-pane{ class: active_when(klass == @loggers.first), id: klass.file_name_noext }
|
||||
.file-holder#README
|
||||
.js-file-title.file-title
|
||||
%i.fa.fa-file
|
||||
= klass::file_name
|
||||
= klass.file_name
|
||||
.pull-right
|
||||
= link_to '#', class: 'log-bottom' do
|
||||
%i.fa.fa-arrow-down
|
||||
|
|
|
@ -40,9 +40,10 @@
|
|||
%li
|
||||
= link_to 'Remove user', admin_user_path(user),
|
||||
data: { confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?" },
|
||||
class: 'text-danger',
|
||||
method: :delete
|
||||
%li
|
||||
= link_to 'Remove user and contributions', admin_user_path(user, hard_delete: true),
|
||||
data: { confirm: "USER #{user.name} WILL BE REMOVED! All issues, merge requests and comments authored by this user, and groups owned solely by them, will also be removed! Are you sure?" },
|
||||
class: 'btn btn-remove btn-block',
|
||||
class: 'text-danger',
|
||||
method: :delete
|
||||
|
|
|
@ -8,14 +8,14 @@
|
|||
- content_for :breadcrumbs_extra do
|
||||
= link_to params.merge(rss_url_options), class: 'btn has-tooltip append-right-10', title: 'Subscribe' do
|
||||
= icon('rss')
|
||||
= render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", with_feature_enabled: 'issues'
|
||||
= render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", with_feature_enabled: 'issues', type: :issues
|
||||
|
||||
.top-area
|
||||
= render 'shared/issuable/nav', type: :issues
|
||||
.nav-controls{ class: ("visible-xs" if show_new_nav?) }
|
||||
= link_to params.merge(rss_url_options), class: 'btn has-tooltip', title: 'Subscribe' do
|
||||
= icon('rss')
|
||||
= render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", with_feature_enabled: 'issues'
|
||||
= render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", with_feature_enabled: 'issues', type: :issues
|
||||
|
||||
= render 'shared/issuable/filter', type: :issues
|
||||
= render 'shared/issues'
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
|
||||
- if show_new_nav?
|
||||
- content_for :breadcrumbs_extra do
|
||||
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", with_feature_enabled: 'merge_requests'
|
||||
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", with_feature_enabled: 'merge_requests', type: :merge_requests
|
||||
|
||||
.top-area
|
||||
= render 'shared/issuable/nav', type: :merge_requests
|
||||
.nav-controls{ class: ("visible-xs" if show_new_nav?) }
|
||||
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", with_feature_enabled: 'merge_requests'
|
||||
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", with_feature_enabled: 'merge_requests', type: :merge_requests
|
||||
|
||||
= render 'shared/issuable/filter', type: :merge_requests
|
||||
= render 'shared/merge_requests'
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
|
||||
- if show_new_nav?
|
||||
- content_for :breadcrumbs_extra do
|
||||
= render 'shared/new_project_item_select', path: 'milestones/new', label: 'New milestone', include_groups: true
|
||||
= render 'shared/new_project_item_select', path: 'milestones/new', label: 'New milestone', include_groups: true, type: :milestones
|
||||
|
||||
.top-area
|
||||
= render 'shared/milestones_filter', counts: @milestone_states
|
||||
|
||||
.nav-controls{ class: ("visible-xs" if show_new_nav?) }
|
||||
= render 'shared/new_project_item_select', path: 'milestones/new', label: 'New milestone', include_groups: true
|
||||
= render 'shared/new_project_item_select', path: 'milestones/new', label: 'New milestone', include_groups: true, type: :milestones
|
||||
|
||||
.milestones
|
||||
%ul.content-list
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
- content_for :breadcrumbs_extra do
|
||||
= link_to params.merge(rss_url_options), class: 'btn btn-default append-right-10' do
|
||||
= icon('rss')
|
||||
= render 'shared/new_project_item_select', path: 'issues/new', label: "New issue"
|
||||
= render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", type: :issues
|
||||
|
||||
- if group_issues_exists
|
||||
.top-area
|
||||
|
@ -22,7 +22,7 @@
|
|||
= icon('rss')
|
||||
%span.icon-label
|
||||
Subscribe
|
||||
= render 'shared/new_project_item_select', path: 'issues/new', label: "New issue"
|
||||
= render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", type: :issues
|
||||
|
||||
= render 'shared/issuable/search_bar', type: :issues
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
- if show_new_nav? && current_user
|
||||
- content_for :breadcrumbs_extra do
|
||||
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request"
|
||||
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", type: :merge_requests
|
||||
|
||||
- if @group_merge_requests.empty?
|
||||
= render 'shared/empty_states/merge_requests', project_select_button: true
|
||||
|
@ -11,7 +11,7 @@
|
|||
= render 'shared/issuable/nav', type: :merge_requests
|
||||
- if current_user
|
||||
.nav-controls{ class: ("visible-xs" if show_new_nav?) }
|
||||
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request"
|
||||
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", type: :merge_requests
|
||||
|
||||
= render 'shared/issuable/filter', type: :merge_requests
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
= hidden_field_tag :namespace_id, value: current_user.namespace_id
|
||||
.form-group.col-xs-12.col-sm-6.project-path
|
||||
= label_tag :path, 'Project name', class: 'label-light'
|
||||
= text_field_tag :path, nil, placeholder: "my-awesome-project", class: "js-path-name form-control", tabindex: 2, autofocus: true, required: true
|
||||
= text_field_tag :path, @path, placeholder: "my-awesome-project", class: "js-path-name form-control", tabindex: 2, autofocus: true, required: true
|
||||
|
||||
.row
|
||||
.form-group.col-md-12
|
||||
|
@ -33,7 +33,6 @@
|
|||
.row
|
||||
.form-group.col-sm-12
|
||||
= hidden_field_tag :namespace_id, @namespace.id
|
||||
= hidden_field_tag :path, @path
|
||||
= label_tag :file, 'GitLab project export', class: 'label-light'
|
||||
.form-group
|
||||
= file_field_tag :file, class: ''
|
||||
|
|
|
@ -92,25 +92,24 @@
|
|||
Update username
|
||||
%hr
|
||||
|
||||
- if signup_enabled?
|
||||
.row.prepend-top-default
|
||||
.col-lg-4.profile-settings-sidebar
|
||||
%h4.prepend-top-0.danger-title
|
||||
Remove account
|
||||
.col-lg-8
|
||||
- if @user.can_be_removed? && can?(current_user, :destroy_user, @user)
|
||||
.row.prepend-top-default
|
||||
.col-lg-4.profile-settings-sidebar
|
||||
%h4.prepend-top-0.danger-title
|
||||
Remove account
|
||||
.col-lg-8
|
||||
- if @user.can_be_removed? && can?(current_user, :destroy_user, @user)
|
||||
%p
|
||||
Deleting an account has the following effects:
|
||||
= render 'users/deletion_guidance', user: current_user
|
||||
= link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove"
|
||||
- else
|
||||
- if @user.solo_owned_groups.present?
|
||||
%p
|
||||
Deleting an account has the following effects:
|
||||
= render 'users/deletion_guidance', user: current_user
|
||||
= link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove"
|
||||
Your account is currently an owner in these groups:
|
||||
%strong= @user.solo_owned_groups.map(&:name).join(', ')
|
||||
%p
|
||||
You must transfer ownership or delete these groups before you can delete your account.
|
||||
- else
|
||||
- if @user.solo_owned_groups.present?
|
||||
%p
|
||||
Your account is currently an owner in these groups:
|
||||
%strong= @user.solo_owned_groups.map(&:name).join(', ')
|
||||
%p
|
||||
You must transfer ownership or delete these groups before you can delete your account.
|
||||
- else
|
||||
%p
|
||||
You don't have access to delete this user.
|
||||
%p
|
||||
You don't have access to delete this user.
|
||||
.append-bottom-default
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
Add a GPG key
|
||||
%p.profile-settings-content
|
||||
Before you can add a GPG key you need to
|
||||
= link_to 'generate it.', help_page_path('workflow/gpg_signed_commits/index.md')
|
||||
= link_to 'generate it.', help_page_path('user/project/gpg_signed_commits/index.md')
|
||||
= render 'form'
|
||||
%hr
|
||||
%h5
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
%div{ class: container_class }
|
||||
.nav-block.activity-filter-block.activities
|
||||
.controls
|
||||
= link_to project_path(@project, rss_url_options), title: "Subscribe", class: 'btn rss-btn has-tooltip' do
|
||||
= link_to project_path(@project, rss_url_options), title: s_("ProjectActivityRSS|Subscribe"), class: 'btn rss-btn has-tooltip' do
|
||||
= icon('rss')
|
||||
|
||||
= render 'shared/event_filter'
|
||||
|
|
|
@ -3,16 +3,16 @@
|
|||
.row-content-block.top-block.hidden-xs.white
|
||||
.event-last-push
|
||||
.event-last-push-text
|
||||
%span You pushed to
|
||||
%span= s_("LastPushEvent|You pushed to")
|
||||
%strong
|
||||
= link_to event.ref_name, project_commits_path(event.project, event.ref_name), class: 'ref-name'
|
||||
|
||||
- if event.project != @project
|
||||
%span at
|
||||
%span= s_("LastPushEvent|at")
|
||||
%strong= link_to_project event.project
|
||||
|
||||
#{time_ago_with_tooltip(event.created_at)}
|
||||
|
||||
.pull-right
|
||||
= link_to new_mr_path_from_push_event(event), title: "New merge request", class: "btn btn-info btn-sm" do
|
||||
= link_to new_mr_path_from_push_event(event), title: _("New merge request"), class: "btn btn-info btn-sm" do
|
||||
#{ _('Create merge request') }
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
- @no_container = true
|
||||
|
||||
- if show_new_nav?
|
||||
- add_to_breadcrumbs("Project", project_path(@project))
|
||||
- add_to_breadcrumbs(_("Project"), project_path(@project))
|
||||
|
||||
- page_title "Activity"
|
||||
- page_title _("Activity")
|
||||
= render "projects/head"
|
||||
|
||||
= render 'projects/last_push'
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
%span.monospace= signature.gpg_key_primary_keyid
|
||||
|
||||
|
||||
= link_to('Learn more about signing commits', help_page_path('workflow/gpg_signed_commits/index.md'), class: 'gpg-popover-help-link')
|
||||
= link_to('Learn more about signing commits', help_page_path('user/project/gpg_signed_commits/index.md'), class: 'gpg-popover-help-link')
|
||||
|
||||
%button{ class: css_classes, data: { toggle: 'popover', html: 'true', placement: 'auto top', title: title, content: content } }
|
||||
= label
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
- notes = commit.notes
|
||||
- note_count = notes.user.count
|
||||
|
||||
- cache_key = [project.full_path, commit.id, current_application_settings, note_count, @path.presence, current_controller?(:commits)]
|
||||
- cache_key = [project.full_path, commit.id, current_application_settings, note_count, @path.presence, current_controller?(:commits), I18n.locale]
|
||||
- cache_key.push(commit.status(ref)) if commit.status(ref)
|
||||
|
||||
= cache(cache_key, expires_in: 1.day) do
|
||||
|
|
|
@ -202,8 +202,6 @@
|
|||
.sub-section.rename-respository
|
||||
%h4.warning-title
|
||||
Rename repository
|
||||
%p
|
||||
Export this project with all its related data in order to move your project to a new GitLab instance. Once the export is finished, you can import the file from the "New Project" page.
|
||||
= render 'projects/errors'
|
||||
= form_for([@project.namespace.becomes(Namespace), @project]) do |f|
|
||||
.form-group.project_name_holder
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
%ul.nav-links.event-filter.scrolling-tabs
|
||||
= event_filter_link EventFilter.all, 'All'
|
||||
= event_filter_link EventFilter.all, _('All'), s_('EventFilterBy|Filter by all')
|
||||
- if event_filter_visible(:repository)
|
||||
= event_filter_link EventFilter.push, 'Push events'
|
||||
= event_filter_link EventFilter.push, _('Push events'), s_('EventFilterBy|Filter by push events')
|
||||
- if event_filter_visible(:merge_requests)
|
||||
= event_filter_link EventFilter.merged, 'Merge events'
|
||||
= event_filter_link EventFilter.merged, _('Merge events'), s_('EventFilterBy|Filter by merge events')
|
||||
- if event_filter_visible(:issues)
|
||||
= event_filter_link EventFilter.issue, 'Issue events'
|
||||
= event_filter_link EventFilter.issue, _('Issue events'), s_('EventFilterBy|Filter by issue events')
|
||||
- if comments_visible?
|
||||
= event_filter_link EventFilter.comments, 'Comments'
|
||||
= event_filter_link EventFilter.team, 'Team'
|
||||
= event_filter_link EventFilter.comments, _('Comments'), s_('EventFilterBy|Filter by comments')
|
||||
= event_filter_link EventFilter.team, _('Team'), s_('EventFilterBy|Filter by team')
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- content_for :page_specific_javascripts do
|
||||
= page_specific_javascript_bundle_tag('group')
|
||||
- parent = GroupFinder.new(current_user).execute(id: params[:parent_id] || @group.parent_id)
|
||||
- parent = @group.parent
|
||||
- group_path = root_url
|
||||
- group_path << parent.full_path + '/' if parent
|
||||
|
||||
|
@ -13,13 +13,12 @@
|
|||
%span>= root_url
|
||||
- if parent
|
||||
%strong= parent.full_path + '/'
|
||||
= f.hidden_field :parent_id
|
||||
= f.text_field :path, placeholder: 'open-source', class: 'form-control',
|
||||
autofocus: local_assigns[:autofocus] || false, required: true,
|
||||
pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS,
|
||||
title: 'Please choose a group path with no special characters.',
|
||||
"data-bind-in" => "#{'create_chat_team' if Gitlab.config.mattermost.enabled}"
|
||||
- if parent
|
||||
= f.hidden_field :parent_id, value: parent.id
|
||||
|
||||
- if @group.persisted?
|
||||
.alert.alert-warning.prepend-top-10
|
||||
|
|
|
@ -13,4 +13,4 @@
|
|||
%li
|
||||
The import will time out after 15 minutes. For repositories that take longer, use a clone/push combination.
|
||||
%li
|
||||
To migrate an SVN repository, check out #{link_to "this document", help_page_path('workflow/importing/migrating_from_svn')}.
|
||||
To migrate an SVN repository, check out #{link_to "this document", help_page_path('user/project/import/svn')}.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- if any_projects?(@projects)
|
||||
.project-item-select-holder.btn-group.pull-right
|
||||
%a.btn.btn-new.new-project-item-link{ href: '', data: { label: local_assigns[:label] } }
|
||||
%a.btn.btn-new.new-project-item-link{ href: '', data: { label: local_assigns[:label], type: local_assigns[:type] } }
|
||||
= icon('spinner spin')
|
||||
= project_select_tag :project_path, class: "project-item-select", data: { include_groups: local_assigns[:include_groups], order_by: 'last_activity_at', relative_path: local_assigns[:path] }, with_feature_enabled: local_assigns[:with_feature_enabled]
|
||||
%button.btn.btn-new.new-project-item-select-button
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
Issues can be bugs, tasks or ideas to be discussed.
|
||||
Also, issues are searchable and filterable.
|
||||
- if project_select_button
|
||||
= render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue'
|
||||
= render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue', type: :issues
|
||||
- else
|
||||
= link_to 'New issue', button_path, class: 'btn btn-new', title: 'New issue', id: 'new_issue_link'
|
||||
- else
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
%p
|
||||
Interested parties can even contribute by pushing commits if they want to.
|
||||
- if project_select_button
|
||||
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: 'New merge request'
|
||||
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: 'New merge request', type: :merge_requests
|
||||
- else
|
||||
= link_to 'New merge request', button_path, class: 'btn btn-new', title: 'New merge request', id: 'new_merge_request_link'
|
||||
- else
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 14"><g fill-rule="evenodd"><path fill-rule="nonzero" d="m0 7c0-3.866 3.142-7 7-7 3.866 0 7 3.142 7 7 0 3.866-3.142 7-7 7-3.866 0-7-3.142-7-7m1 0c0 3.309 2.69 6 6 6 3.309 0 6-2.69 6-6 0-3.309-2.69-6-6-6-3.309 0-6 2.69-6 6"/><path d="m7 6h-2.702c-.154 0-.298.132-.298.295v1.41c0 .164.133.295.298.295h2.702v1.694c0 .18.095.209.213.09l2.539-2.568c.115-.116.118-.312 0-.432l-2.539-2.568c-.115-.116-.213-.079-.213.09v1.694"/></g></svg>
|
||||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m9 6h-7a2 2 0 1 0 0 4h7v2.586a1 1 0 0 0 1.707.707l4.586-4.586a1 1 0 0 0 0-1.414l-4.586-4.586a1 1 0 0 0 -1.707.707z" fill-rule="evenodd"/></svg>
|
||||
|
||||
|
|
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 237 B |
|
@ -1 +1,2 @@
|
|||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1472 930v318q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h832q63 0 117 25 15 7 18 23 3 17-9 29l-49 49q-10 10-23 10-3 0-9-2-23-6-45-6h-832q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-254q0-13 9-22l64-64q10-10 23-10 6 0 12 3 20 8 20 29zm231-489l-814 814q-24 24-57 24t-57-24l-430-430q-24-24-24-57t24-57l110-110q24-24 57-24t57 24l263 263 647-647q24-24 57-24t57 24l110 110q24 24 24 57t-24 57z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M7.536 8.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 0 1 6.12 7.243l1.415 1.414zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></svg>
|
||||
|
||||
|
|
Before Width: | Height: | Size: 574 B After Width: | Height: | Size: 373 B |
|
@ -1 +1 @@
|
|||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1024 544v448q0 14-9 23t-23 9h-320q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h224v-352q0-14 9-23t23-9h64q14 0 23 9t9 23zm416 352q0-148-73-273t-198-198-273-73-273 73-198 198-73 273 73 273 198 198 273 73 273-73 198-198 73-273zm224 0q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/></svg>
|
||||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M9 7h1c.552 0 1 .448 1 1s-.448 1-1 1H8c-.276 0-.526-.112-.707-.293S7 8.277 7 8V5c0-.552.448-1 1-1s1 .448 1 1zm-1 9c-4.418 0-8-3.582-8-8s3.582-8 8-8 8 3.582 8 8-3.582 8-8 8zm0-2c3.314 0 6-2.686 6-6s-2.686-6-6-6-6 2.686-6 6 2.686 6 6 6z"/></svg>
|
||||
|
|
Before Width: | Height: | Size: 469 B After Width: | Height: | Size: 336 B |
|
@ -1,3 +1,2 @@
|
|||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="14" viewBox="0 0 14 14">
|
||||
<path d="M13 12.75v-8.5q0-0.102-0.074-0.176t-0.176-0.074h-8.5q-0.102 0-0.176 0.074t-0.074 0.176v8.5q0 0.102 0.074 0.176t0.176 0.074h8.5q0.102 0 0.176-0.074t0.074-0.176zM14 4.25v8.5q0 0.516-0.367 0.883t-0.883 0.367h-8.5q-0.516 0-0.883-0.367t-0.367-0.883v-8.5q0-0.516 0.367-0.883t0.883-0.367h8.5q0.516 0 0.883 0.367t0.367 0.883zM11 1.25v1.25h-1v-1.25q0-0.102-0.074-0.176t-0.176-0.074h-8.5q-0.102 0-0.176 0.074t-0.074 0.176v8.5q0 0.102 0.074 0.176t0.176 0.074h1.25v1h-1.25q-0.516 0-0.883-0.367t-0.367-0.883v-8.5q0-0.516 0.367-0.883t0.883-0.367h8.5q0.516 0 0.883 0.367t0.367 0.883z"></path>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M10.874 2H12a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3h-2c-.918 0-1.74-.413-2.29-1.063a3.987 3.987 0 0 0 1.988-.984A1 1 0 0 0 10 14h2a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1h-1V3c0-.345-.044-.68-.126-1zM4 0h3a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H4z"/></svg>
|
||||
|
||||
|
|
Before Width: | Height: | Size: 735 B After Width: | Height: | Size: 421 B |
|
@ -1 +1,2 @@
|
|||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M672 1472q0-40-28-68t-68-28-68 28-28 68 28 68 68 28 68-28 28-68zm0-1152q0-40-28-68t-68-28-68 28-28 68 28 68 68 28 68-28 28-68zm640 128q0-40-28-68t-68-28-68 28-28 68 28 68 68 28 68-28 28-68zm96 0q0 52-26 96.5t-70 69.5q-2 287-226 414-68 38-203 81-128 40-169.5 71t-41.5 100v26q44 25 70 69.5t26 96.5q0 80-56 136t-136 56-136-56-56-136q0-52 26-96.5t70-69.5v-820q-44-25-70-69.5t-26-96.5q0-80 56-136t136-56 136 56 56 136q0 52-26 96.5t-70 69.5v497q54-26 154-57 55-17 87.5-29.5t70.5-31 59-39.5 40.5-51 28-69.5 8.5-91.5q-44-25-70-69.5t-26-96.5q0-80 56-136t136-56 136 56 56 136z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M6 11.978v.29a2 2 0 1 1-2 0V3.732a2 2 0 1 1 2 0v3.849c.592-.491 1.31-.854 2.15-1.081 1.308-.353 1.875-.882 1.893-1.743a2 2 0 1 1 2.002-.051C12.053 6.54 10.857 7.84 8.67 8.43 7.056 8.867 6.195 9.98 6 11.978zM5 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm6 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM5 15a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></svg>
|
||||
|
||||
|
|
Before Width: | Height: | Size: 676 B After Width: | Height: | Size: 409 B |
|
@ -1 +1,2 @@
|
|||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M896 384q-204 0-381.5 69.5t-282 187.5-104.5 255q0 112 71.5 213.5t201.5 175.5l87 50-27 96q-24 91-70 172 152-63 275-171l43-38 57 6q69 8 130 8 204 0 381.5-69.5t282-187.5 104.5-255-104.5-255-282-187.5-381.5-69.5zm896 512q0 174-120 321.5t-326 233-450 85.5q-70 0-145-8-198 175-460 242-49 14-114 22h-5q-15 0-27-10.5t-16-27.5v-1q-3-4-.5-12t2-10 4.5-9.5l6-9 7-8.5 8-9q7-8 31-34.5t34.5-38 31-39.5 32.5-51 27-59 26-76q-157-89-247.5-220t-90.5-281q0-174 120-321.5t326-233 450-85.5 450 85.5 326 233 120 321.5z"/></svg>
|
||||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1.707 15.707c-.63.63-1.707.184-1.707-.707v-12a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1 -3 3h-7.586zm.293-3.121 2.293-2.293a1 1 0 0 1 .707-.293h8a1 1 0 0 0 1-1v-6a1 1 0 0 0 -1-1h-10a1 1 0 0 0 -1 1z"/></svg>
|
||||
|
||||
|
|
Before Width: | Height: | Size: 605 B After Width: | Height: | Size: 303 B |
|
@ -1 +1,2 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 18" enable-background="new 0 0 36 18"><path d="m34 7h-7.2c-.9-4-4.5-7-8.8-7s-7.9 3-8.8 7h-7.2c-1.1 0-2 .9-2 2 0 1.1.9 2 2 2h7.2c.9 4 4.5 7 8.8 7s7.9-3 8.8-7h7.2c1.1 0 2-.9 2-2 0-1.1-.9-2-2-2m-16 7c-2.8 0-5-2.2-5-5s2.2-5 5-5 5 2.2 5 5-2.2 5-5 5"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M8 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm3.876-1.008a4.002 4.002 0 0 1-7.752 0A1.01 1.01 0 0 1 4 9H1a1 1 0 1 1 0-2h3c.042 0 .083.003.124.008a4.002 4.002 0 0 1 7.752 0A1.01 1.01 0 0 1 12 7h3a1 1 0 0 1 0 2h-3a1.01 1.01 0 0 1-.124-.008z"/></svg>
|
||||
|
||||
|
|
Before Width: | Height: | Size: 309 B After Width: | Height: | Size: 330 B |
|
@ -1 +1,2 @@
|
|||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M888 1184l116-116-152-152-116 116v56h96v96h56zm440-720q-16-16-33 1l-350 350q-17 17-1 33t33-1l350-350q17-17 1-33zm80 594v190q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h832q63 0 117 25 15 7 18 23 3 17-9 29l-49 49q-14 14-32 8-23-6-45-6h-832q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-126q0-13 9-22l64-64q15-15 35-7t20 29zm-96-738l288 288-672 672h-288v-288zm444 132l-92 92-288-288 92-92q28-28 68-28t68 28l152 152q28 28 28 68t-28 68z"/></svg>
|
||||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m13.436 1.413 1.415 1.414a1 1 0 0 1 0 1.414l-10.316 10.315a1 1 0 0 1 -.703.293l-2.407.008-.008-2.421a1 1 0 0 1 .293-.71l10.312-10.314a1 1 0 0 1 1.414 0zm-9.608 12.436 10.315-10.315-1.413-1.414-10.313 10.312.005 1.422zm7.486-10.313 1.414 1.414-7.778 7.778-1.407.007-.007-1.421zm1.414-1.415 1.414 1.415-.707.707-1.414-1.415z"/></svg>
|
||||
|
||||
|
|
Before Width: | Height: | Size: 617 B After Width: | Height: | Size: 426 B |
|
@ -1 +1,2 @@
|
|||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1664 960q-152-236-381-353 61 104 61 225 0 185-131.5 316.5t-316.5 131.5-316.5-131.5-131.5-316.5q0-121 61-225-229 117-381 353 133 205 333.5 326.5t434.5 121.5 434.5-121.5 333.5-326.5zm-720-384q0-20-14-34t-34-14q-125 0-214.5 89.5t-89.5 214.5q0 20 14 34t34 14 34-14 14-34q0-86 61-147t147-61q20 0 34-14t14-34zm848 384q0 34-20 69-140 230-376.5 368.5t-499.5 138.5-499.5-139-376.5-368q-20-35-20-69t20-69q140-229 376.5-368t499.5-139 499.5 139 376.5 368q20 35 20 69z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M8 14C4.816 14 2.253 12.284.393 8.981a2 2 0 0 1 0-1.962C2.253 3.716 4.816 2 8 2s5.747 1.716 7.607 5.019a2 2 0 0 1 0 1.962C13.747 12.284 11.184 14 8 14zm0-2c2.41 0 4.338-1.29 5.864-4C12.338 5.29 10.411 4 8 4 5.59 4 3.662 5.29 2.136 8 3.662 10.71 5.589 12 8 12zm0-1a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm1-3a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></svg>
|
||||
|
||||
|
|
Before Width: | Height: | Size: 566 B After Width: | Height: | Size: 428 B |
|
@ -1 +1,2 @@
|
|||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M555 1335l78-141q-87-63-136-159t-49-203q0-121 61-225-229 117-381 353 167 258 427 375zm389-759q0-20-14-34t-34-14q-125 0-214.5 89.5t-89.5 214.5q0 20 14 34t34 14 34-14 14-34q0-86 61-147t147-61q20 0 34-14t14-34zm363-191q0 7-1 9-105 188-315 566t-316 567l-49 89q-10 16-28 16-12 0-134-70-16-10-16-28 0-12 44-87-143-65-263.5-173t-208.5-245q-20-31-20-69t20-69q153-235 380-371t496-136q89 0 180 17l54-97q10-16 28-16 5 0 18 6t31 15.5 33 18.5 31.5 18.5 19.5 11.5q16 10 16 27zm37 447q0 139-79 253.5t-209 164.5l280-502q8 45 8 84zm448 128q0 35-20 69-39 64-109 145-150 172-347.5 267t-419.5 95l74-132q212-18 392.5-137t301.5-307q-115-179-282-294l63-112q95 64 182.5 153t144.5 184q20 34 20 69z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M13.618 2.62L1.62 14.619a1 1 0 0 1-.985-1.668l1.525-1.526C1.516 10.742.926 9.927.393 8.981a2 2 0 0 1 0-1.962C2.253 3.716 4.816 2 8 2c1.074 0 2.076.195 3.006.58l.944-.944a1 1 0 0 1 1.668.985zM8.068 11a3 3 0 0 0 2.931-2.932l-2.931 2.931zm-3.02-2.462a3 3 0 0 1 3.49-3.49l.884-.884A6.044 6.044 0 0 0 8 4C5.59 4 3.662 5.29 2.136 8c.445.79.924 1.46 1.439 2.011l1.473-1.473zm.421 5.06l1.658-1.658c.283.04.575.06.873.06 2.41 0 4.338-1.29 5.864-4a11.023 11.023 0 0 0-1.133-1.664l1.418-1.418a12.799 12.799 0 0 1 1.458 2.1 2 2 0 0 1 0 1.963C13.747 12.284 11.184 14 8 14a7.883 7.883 0 0 1-2.53-.402z"/></svg>
|
||||
|
||||
|
|
Before Width: | Height: | Size: 782 B After Width: | Height: | Size: 690 B |
|
@ -1 +1,2 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="m2 3c.552 0 1-.448 1-1 0-.552-.448-1-1-1-.552 0-1 .448-1 1 0 .552.448 1 1 1m.761.85c.154 2.556 1.987 4.692 4.45 5.255.328-.655 1.01-1.105 1.789-1.105 1.105 0 2 .895 2 2 0 1.105-.895 2-2 2-.89 0-1.645-.582-1.904-1.386-1.916-.376-3.548-1.5-4.596-3.044v4.493c.863.222 1.5 1.01 1.5 1.937 0 1.105-.895 2-2 2-1.105 0-2-.895-2-2 0-.74.402-1.387 1-1.732v-8.535c-.598-.346-1-.992-1-1.732 0-1.105.895-2 2-2 1.105 0 2 .895 2 2 0 .835-.512 1.551-1.239 1.85m6.239 7.15c.552 0 1-.448 1-1 0-.552-.448-1-1-1-.552 0-1 .448-1 1 0 .552.448 1 1 1m-7 4c.552 0 1-.448 1-1 0-.552-.448-1-1-1-.552 0-1 .448-1 1 0 .552.448 1 1 1" transform="translate(3)"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M11 12.268V5a1 1 0 0 0-1-1v1a.5.5 0 0 1-.8.4l-2.667-2a.5.5 0 0 1 0-.8L9.2.6a.5.5 0 0 1 .8.4v1a3 3 0 0 1 3 3v7.268a2 2 0 1 1-2 0zm-6 0a2 2 0 1 1-2 0V4.732a2 2 0 1 1 2 0v7.536zM4 4a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm8 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></svg>
|
||||
|
||||
|
|
Before Width: | Height: | Size: 706 B After Width: | Height: | Size: 377 B |
|
@ -1 +1,2 @@
|
|||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M491 1536l91-91-235-235-91 91v107h128v128h107zm523-928q0-22-22-22-10 0-17 7l-542 542q-7 7-7 17 0 22 22 22 10 0 17-7l542-542q7-7 7-17zm-54-192l416 416-832 832h-416v-416zm683 96q0 53-37 90l-166 166-416-416 166-165q36-38 90-38 53 0 91 38l235 234q37 39 37 91z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M13.02 1.293l1.414 1.414a1 1 0 0 1 0 1.414L4.119 14.436a1 1 0 0 1-.704.293l-2.407.008L1 12.316a1 1 0 0 1 .293-.71L11.605 1.292a1 1 0 0 1 1.414 0zm-1.416 1.415l-.707.707L12.31 4.83l.707-.707-1.414-1.415zM3.411 13.73l1.123-1.122H3.12v-1.415L2 12.312l.005 1.422 1.406-.005z"/></svg>
|
||||
|
||||
|
|
Before Width: | Height: | Size: 365 B After Width: | Height: | Size: 373 B |
|
@ -1 +1,2 @@
|
|||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M666 481q-60 92-137 273-22-45-37-72.5t-40.5-63.5-51-56.5-63-35-81.5-14.5h-224q-14 0-23-9t-9-23v-192q0-14 9-23t23-9h224q250 0 410 225zm1126 799q0 14-9 23l-320 320q-9 9-23 9-13 0-22.5-9.5t-9.5-22.5v-192q-32 0-85 .5t-81 1-73-1-71-5-64-10.5-63-18.5-58-28.5-59-40-55-53.5-56-69.5q59-93 136-273 22 45 37 72.5t40.5 63.5 51 56.5 63 35 81.5 14.5h256v-192q0-14 9-23t23-9q12 0 24 10l319 319q9 9 9 23zm0-896q0 14-9 23l-320 320q-9 9-23 9-13 0-22.5-9.5t-9.5-22.5v-192h-256q-48 0-87 15t-69 45-51 61.5-45 77.5q-32 62-78 171-29 66-49.5 111t-54 105-64 100-74 83-90 68.5-106.5 42-128 16.5h-224q-14 0-23-9t-9-23v-192q0-14 9-23t23-9h224q48 0 87-15t69-45 51-61.5 45-77.5q32-62 78-171 29-66 49.5-111t54-105 64-100 74-83 90-68.5 106.5-42 128-16.5h256v-192q0-14 9-23t23-9q12 0 24 10l319 319q9 9 9 23z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586zM5 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></svg>
|
||||
|
||||
|
|
Before Width: | Height: | Size: 885 B After Width: | Height: | Size: 402 B |
|
@ -1 +1 @@
|
|||
<svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><path d="M0 7c0-3.866 3.142-7 7-7 3.866 0 7 3.142 7 7 0 3.866-3.142 7-7 7-3.866 0-7-3.142-7-7z"/><path d="M1 7c0 3.309 2.69 6 6 6 3.309 0 6-2.69 6-6 0-3.309-2.69-6-6-6-3.309 0-6 2.69-6 6z" fill="#FFF"/><rect x="3.36" y="6.16" width="7.28" height="1.68" rx=".84"/></svg>
|
||||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.83c.39-.39 1.024-.39 1.414 0 .39.392.39 1.025 0 1.416l-3.535 3.535c-.196.195-.452.293-.707.293-.256 0-.512-.097-.708-.292l-2.12-2.12c-.39-.392-.39-1.025 0-1.415s1.023-.39 1.413 0zM8 16c-4.418 0-8-3.582-8-8s3.582-8 8-8 8 3.582 8 8-3.582 8-8 8zm0-2c3.314 0 6-2.686 6-6s-2.686-6-6-6-6 2.686-6 6 2.686 6 6 6z"/></svg>
|
||||
|
|
Before Width: | Height: | Size: 353 B After Width: | Height: | Size: 427 B |
|
@ -1 +1,2 @@
|
|||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M384 448q0-53-37.5-90.5t-90.5-37.5-90.5 37.5-37.5 90.5 37.5 90.5 90.5 37.5 90.5-37.5 37.5-90.5zm1067 576q0 53-37 90l-491 492q-39 37-91 37-53 0-90-37l-715-716q-38-37-64.5-101t-26.5-117v-416q0-52 38-90t90-38h416q53 0 117 26.5t102 64.5l715 714q37 39 37 91zm384 0q0 53-37 90l-491 492q-39 37-91 37-36 0-59-14t-53-45l470-470q37-37 37-90 0-52-37-91l-715-714q-38-38-102-64.5t-117-26.5h224q53 0 117 26.5t102 64.5l715 714q37 39 37 91z"/></svg>
|
||||
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m7.222.222c1.657 0 3 1.343 3 3v8.327c0 .631-.298 1.226-.805 1.603l-3 2.237c-.709.529-1.682.529-2.391 0l-3-2.237c-.506-.377-.805-.972-.805-1.603v-8.327c0-1.657 1.343-3 3-3h4m-5 3v8.08c0 .158.075.306.201.401l2.5 1.864c.177.132.42.132.598 0l2.5-1.864c.127-.094.201-.243.201-.401v-8.08c0-.552-.448-1-1-1h-4c-.552 0-1 .448-1 1m2.778 7.778c-.552 0-1-.448-1-1s .448-1 1-1 1 .448 1 1-.448 1-1 1" transform="matrix(-.70711 .70711 -.70711 -.70711 17.05 9.767)"/></svg>
|
||||
|
||||
|
|
Before Width: | Height: | Size: 534 B After Width: | Height: | Size: 529 B |
|
@ -1 +1 @@
|
|||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1600 1405q0 120-73 189.5t-194 69.5h-874q-121 0-194-69.5t-73-189.5q0-53 3.5-103.5t14-109 26.5-108.5 43-97.5 62-81 85.5-53.5 111.5-20q9 0 42 21.5t74.5 48 108 48 133.5 21.5 133.5-21.5 108-48 74.5-48 42-21.5q61 0 111.5 20t85.5 53.5 62 81 43 97.5 26.5 108.5 14 109 3.5 103.5zm-320-893q0 159-112.5 271.5t-271.5 112.5-271.5-112.5-112.5-271.5 112.5-271.5 271.5-112.5 271.5 112.5 112.5 271.5z"/></svg>
|
||||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 7C6.343 7 5 5.657 5 4s1.343-3 3-3 3 1.343 3 3-1.343 3-3 3zm0 8c-6.888 0-6.976-.78-6.976-2.52S2.144 8 8 8s6.976 2.692 6.976 4.48S14.888 15 8 15z" fill-rule="evenodd"/></svg>
|
||||
|
|
Before Width: | Height: | Size: 494 B After Width: | Height: | Size: 268 B |
|
@ -57,7 +57,7 @@
|
|||
%li.filter-dropdown-item{ data: { value: 'none' } }
|
||||
%button.btn.btn-link
|
||||
No Assignee
|
||||
%li.divider
|
||||
%li.divider.droplab-item-ignore
|
||||
- if current_user
|
||||
= render 'shared/issuable/user_dropdown_item',
|
||||
user: current_user
|
||||
|
@ -76,7 +76,7 @@
|
|||
%li.filter-dropdown-item{ 'data-value' => 'started' }
|
||||
%button.btn.btn-link
|
||||
Started
|
||||
%li.divider
|
||||
%li.divider.droplab-item-ignore
|
||||
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
|
||||
%li.filter-dropdown-item
|
||||
%button.btn.btn-link.js-data-value
|
||||
|
@ -86,7 +86,7 @@
|
|||
%li.filter-dropdown-item{ data: { value: 'none' } }
|
||||
%button.btn.btn-link
|
||||
No Label
|
||||
%li.divider
|
||||
%li.divider.droplab-item-ignore
|
||||
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
|
||||
%li.filter-dropdown-item
|
||||
%button.btn.btn-link
|
||||
|
|
|
@ -4,18 +4,25 @@ class AuthorizedProjectsWorker
|
|||
|
||||
# Schedules multiple jobs and waits for them to be completed.
|
||||
def self.bulk_perform_and_wait(args_list)
|
||||
job_ids = bulk_perform_async(args_list)
|
||||
waiter = Gitlab::JobWaiter.new(args_list.size)
|
||||
|
||||
Gitlab::JobWaiter.new(job_ids).wait
|
||||
# Point all the bulk jobs at the same JobWaiter. Converts, [[1], [2], [3]]
|
||||
# into [[1, "key"], [2, "key"], [3, "key"]]
|
||||
waiting_args_list = args_list.map { |args| args << waiter.key }
|
||||
bulk_perform_async(waiting_args_list)
|
||||
|
||||
waiter.wait
|
||||
end
|
||||
|
||||
def self.bulk_perform_async(args_list)
|
||||
Sidekiq::Client.push_bulk('class' => self, 'queue' => sidekiq_options['queue'], 'args' => args_list)
|
||||
end
|
||||
|
||||
def perform(user_id)
|
||||
def perform(user_id, notify_key = nil)
|
||||
user = User.find_by(id: user_id)
|
||||
|
||||
user&.refresh_authorized_projects
|
||||
ensure
|
||||
Gitlab::JobWaiter.notify(notify_key, jid) if notify_key
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,7 +18,8 @@ class NamespacelessProjectDestroyWorker
|
|||
rescue ActiveRecord::RecordNotFound
|
||||
return
|
||||
end
|
||||
return unless project.namespace_id.nil? # Reject doing anything for projects that *do* have a namespace
|
||||
|
||||
return if project.namespace # Reject doing anything for projects that *do* have a namespace
|
||||
|
||||
project.team.truncate
|
||||
|
||||
|
|
|
@ -5,14 +5,17 @@ class RepositoryForkWorker
|
|||
include Gitlab::ShellAdapter
|
||||
include DedicatedSidekiqQueue
|
||||
|
||||
sidekiq_options status_expiration: StuckImportJobsWorker::IMPORT_JOBS_EXPIRATION
|
||||
|
||||
def perform(project_id, forked_from_repository_storage_path, source_path, target_path)
|
||||
project = Project.find(project_id)
|
||||
|
||||
return unless start_fork(project)
|
||||
|
||||
Gitlab::Metrics.add_event(:fork_repository,
|
||||
source_path: source_path,
|
||||
target_path: target_path)
|
||||
|
||||
project = Project.find(project_id)
|
||||
project.import_start
|
||||
|
||||
result = gitlab_shell.fork_repository(forked_from_repository_storage_path, source_path,
|
||||
project.repository_storage_path, target_path)
|
||||
raise ForkError, "Unable to fork project #{project_id} for repository #{source_path} -> #{target_path}" unless result
|
||||
|
@ -33,6 +36,13 @@ class RepositoryForkWorker
|
|||
|
||||
private
|
||||
|
||||
def start_fork(project)
|
||||
return true if project.import_start
|
||||
|
||||
Rails.logger.info("Project #{project.full_path} was in inconsistent state (#{project.import_status}) while forking.")
|
||||
false
|
||||
end
|
||||
|
||||
def fail_fork(project, message)
|
||||
Rails.logger.error(message)
|
||||
project.mark_import_as_failed(message)
|
||||
|
|
|
@ -4,23 +4,18 @@ class RepositoryImportWorker
|
|||
include Sidekiq::Worker
|
||||
include DedicatedSidekiqQueue
|
||||
|
||||
sidekiq_options status_expiration: StuckImportJobsWorker::IMPORT_EXPIRATION
|
||||
|
||||
attr_accessor :project, :current_user
|
||||
sidekiq_options status_expiration: StuckImportJobsWorker::IMPORT_JOBS_EXPIRATION
|
||||
|
||||
def perform(project_id)
|
||||
@project = Project.find(project_id)
|
||||
@current_user = @project.creator
|
||||
project = Project.find(project_id)
|
||||
|
||||
project.import_start
|
||||
return unless start_import(project)
|
||||
|
||||
Gitlab::Metrics.add_event(:import_repository,
|
||||
import_url: @project.import_url,
|
||||
path: @project.full_path)
|
||||
import_url: project.import_url,
|
||||
path: project.full_path)
|
||||
|
||||
project.update_columns(import_jid: self.jid, import_error: nil)
|
||||
|
||||
result = Projects::ImportService.new(project, current_user).execute
|
||||
result = Projects::ImportService.new(project, project.creator).execute
|
||||
raise ImportError, result[:message] if result[:status] == :error
|
||||
|
||||
project.repository.after_import
|
||||
|
@ -37,6 +32,13 @@ class RepositoryImportWorker
|
|||
|
||||
private
|
||||
|
||||
def start_import(project)
|
||||
return true if project.import_start
|
||||
|
||||
Rails.logger.info("Project #{project.full_path} was in inconsistent state (#{project.import_status}) while importing.")
|
||||
false
|
||||
end
|
||||
|
||||
def fail_import(project, message)
|
||||
project.mark_import_as_failed(message)
|
||||
end
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
class StageUpdateWorker
|
||||
include Sidekiq::Worker
|
||||
include PipelineQueue
|
||||
|
||||
def perform(stage_id)
|
||||
Ci::Stage.find_by(id: stage_id).try do |stage|
|
||||
stage.update_status
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,36 +2,60 @@ class StuckImportJobsWorker
|
|||
include Sidekiq::Worker
|
||||
include CronjobQueue
|
||||
|
||||
IMPORT_EXPIRATION = 15.hours.to_i
|
||||
IMPORT_JOBS_EXPIRATION = 15.hours.to_i
|
||||
|
||||
def perform
|
||||
stuck_projects.find_in_batches(batch_size: 500) do |group|
|
||||
jids = group.map(&:import_jid)
|
||||
projects_without_jid_count = mark_projects_without_jid_as_failed!
|
||||
projects_with_jid_count = mark_projects_with_jid_as_failed!
|
||||
|
||||
# Find the jobs that aren't currently running or that exceeded the threshold.
|
||||
completed_jids = Gitlab::SidekiqStatus.completed_jids(jids)
|
||||
|
||||
if completed_jids.any?
|
||||
completed_ids = group.select { |project| completed_jids.include?(project.import_jid) }.map(&:id)
|
||||
|
||||
fail_batch!(completed_jids, completed_ids)
|
||||
end
|
||||
end
|
||||
Gitlab::Metrics.add_event(:stuck_import_jobs,
|
||||
projects_without_jid_count: projects_without_jid_count,
|
||||
projects_with_jid_count: projects_with_jid_count)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def stuck_projects
|
||||
Project.select('id, import_jid').with_import_status(:started).where.not(import_jid: nil)
|
||||
def mark_projects_without_jid_as_failed!
|
||||
started_projects_without_jid.each do |project|
|
||||
project.mark_import_as_failed(error_message)
|
||||
end.count
|
||||
end
|
||||
|
||||
def fail_batch!(completed_jids, completed_ids)
|
||||
Project.where(id: completed_ids).update_all(import_status: 'failed', import_error: error_message)
|
||||
def mark_projects_with_jid_as_failed!
|
||||
completed_jids_count = 0
|
||||
|
||||
Rails.logger.info("Marked stuck import jobs as failed. JIDs: #{completed_jids.join(', ')}")
|
||||
started_projects_with_jid.find_in_batches(batch_size: 500) do |group|
|
||||
jids = group.map(&:import_jid)
|
||||
|
||||
# Find the jobs that aren't currently running or that exceeded the threshold.
|
||||
completed_jids = Gitlab::SidekiqStatus.completed_jids(jids).to_set
|
||||
|
||||
if completed_jids.any?
|
||||
completed_jids_count += completed_jids.count
|
||||
group.each do |project|
|
||||
project.mark_import_as_failed(error_message) if completed_jids.include?(project.import_jid)
|
||||
end
|
||||
|
||||
Rails.logger.info("Marked stuck import jobs as failed. JIDs: #{completed_jids.to_a.join(', ')}")
|
||||
end
|
||||
end
|
||||
|
||||
completed_jids_count
|
||||
end
|
||||
|
||||
def started_projects
|
||||
Project.with_import_status(:started)
|
||||
end
|
||||
|
||||
def started_projects_with_jid
|
||||
started_projects.where.not(import_jid: nil)
|
||||
end
|
||||
|
||||
def started_projects_without_jid
|
||||
started_projects.where(import_jid: nil)
|
||||
end
|
||||
|
||||
def error_message
|
||||
"Import timed out. Import took longer than #{IMPORT_EXPIRATION} seconds"
|
||||
"Import timed out. Import took longer than #{IMPORT_JOBS_EXPIRATION} seconds"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
title: "Insert user name directly without encoding"
|
||||
merge_request: 10085
|
||||
author: Nathan Neulinger <nneul@neulinger.org>
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
title: Expose target_iid in Events API
|
||||
merge_request: 13247
|
||||
author: sue445
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
title: Expose noteable_iid in Note
|
||||
merge_request: 13265
|
||||
author: sue445
|