diff options
Diffstat (limited to 'doc/development')
87 files changed, 1941 insertions, 49 deletions
diff --git a/doc/development/README.md b/doc/development/README.md index 58c00f618fa..6f2ca7b8590 100644 --- a/doc/development/README.md +++ b/doc/development/README.md @@ -8,15 +8,21 @@ ## Styleguides +- [API styleguide](api_styleguide.md) Use this styleguide if you are + contributing to the API. - [Documentation styleguide](doc_styleguide.md) Use this styleguide if you are contributing to documentation. - [SQL Migration Style Guide](migration_style_guide.md) for creating safe SQL migrations - [Testing standards and style guidelines](testing.md) -- [UI guide](ui_guide.md) for building GitLab with existing CSS styles and elements -- [SQL guidelines](sql.md) for SQL guidelines +- [UX guide](ux_guide/index.md) for building GitLab with existing CSS styles and elements +- [Frontend guidelines](frontend.md) +- [SQL guidelines](sql.md) for working with SQL queries +- [Sidekiq guidelines](sidekiq_style_guide.md) for working with Sidekiq workers ## Process +- [Generate a changelog entry with `bin/changelog`](changelog.md) +- [Limit conflicts with EE when developing on CE](limit_ee_conflicts.md) - [Code review guidelines](code_review.md) for reviewing code and having code reviewed. - [Merge request performance guidelines](merge_request_performance_guidelines.md) for ensuring merge requests do not negatively impact GitLab performance @@ -32,11 +38,13 @@ - [Rake tasks](rake_tasks.md) for development - [Shell commands](shell_commands.md) in the GitLab codebase - [Sidekiq debugging](sidekiq_debugging.md) +- [Object state models](object_state_models.md) ## Databases - [What requires downtime?](what_requires_downtime.md) - [Adding database indexes](adding_database_indexes.md) +- [Post Deployment Migrations](post_deployment_migrations.md) ## Compliance diff --git a/doc/development/api_styleguide.md b/doc/development/api_styleguide.md new file mode 100644 index 00000000000..ce444ebdde4 --- /dev/null +++ b/doc/development/api_styleguide.md @@ -0,0 +1,96 @@ +# API styleguide + +This styleguide recommends best practices for API development. + +## Instance variables + +Please do not use instance variables, there is no need for them (we don't need +to access them as we do in Rails views), local variables are fine. + +## Entities + +Always use an [Entity] to present the endpoint's payload. + +## Methods and parameters description + +Every method must be described using the [Grape DSL](https://github.com/ruby-grape/grape#describing-methods) +(see https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/api/environments.rb +for a good example): + +- `desc` for the method summary. You should pass it a block for additional + details such as: + - The GitLab version when the endpoint was added + - If the endpoint is deprecated, and if so, when will it be removed + +- `params` for the method params. This acts as description, + [validation, and coercion of the parameters] + +A good example is as follows: + +```ruby +desc 'Get all broadcast messages' do + detail 'This feature was introduced in GitLab 8.12.' + success Entities::BroadcastMessage +end +params do + optional :page, type: Integer, desc: 'Current page number' + optional :per_page, type: Integer, desc: 'Number of messages per page' +end +get do + messages = BroadcastMessage.all + + present paginate(messages), with: Entities::BroadcastMessage +end +``` + +## Declared params + +> Grape allows you to access only the parameters that have been declared by your +`params` block. It filters out the params that have been passed, but are not +allowed. + +– https://github.com/ruby-grape/grape#declared + +### Exclude params from parent namespaces! + +> By default `declared(params) `includes parameters that were defined in all +parent namespaces. + +– https://github.com/ruby-grape/grape#include-parent-namespaces + +In most cases you will want to exclude params from the parent namespaces: + +```ruby +declared(params, include_parent_namespaces: false) +``` + +### When to use `declared(params)`? + +You should always use `declared(params)` when you pass the params hash as +arguments to a method call. + +For instance: + +```ruby +# bad +User.create(params) # imagine the user submitted `admin=1`... :) + +# good +User.create(declared(params, include_parent_namespaces: false).to_h) +``` + +>**Note:** +`declared(params)` return a `Hashie::Mash` object, on which you will have to +call `.to_h`. + +But we can use `params[key]` directly when we access single elements. + +For instance: + +```ruby +# good +Model.create(foo: params[:foo]) +``` + +[Entity]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/api/entities.rb +[validation, and coercion of the parameters]: https://github.com/ruby-grape/grape#parameter-validation-and-coercion diff --git a/doc/development/changelog.md b/doc/development/changelog.md new file mode 100644 index 00000000000..6a97fae9cac --- /dev/null +++ b/doc/development/changelog.md @@ -0,0 +1,185 @@ +# Generate a changelog entry + +This guide contains instructions for generating a changelog entry data file, as +well as information and history about our changelog process. + +## Overview + +Each bullet point, or **entry**, in our [`CHANGELOG.md`][changelog.md] file is +generated from a single data file in the [`changelogs/unreleased/`][unreleased] +(or corresponding EE) folder. The file is expected to be a [YAML] file in the +following format: + +```yaml +--- +title: "Going through change[log]s" +merge_request: 1972 +author: Ozzy Osbourne +``` + +The `merge_request` value is a reference to a merge request that adds this +entry, and the `author` key is used to give attribution to community +contributors. Both are optional. + +Community contributors and core team members are encouraged to add their name to +the `author` field. GitLab team members should not. + +If you're working on the GitLab EE repository, the entry will be added to +`changelogs/unreleased-ee/` instead. + +[changelog.md]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CHANGELOG.md +[unreleased]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/changelogs/ +[YAML]: https://en.wikipedia.org/wiki/YAML + +## Instructions + +A `bin/changelog` script is available to generate the changelog entry file +automatically. + +Its simplest usage is to provide the value for `title`: + +```text +$ bin/changelog 'Hey DZ, I added a feature to GitLab!' +create changelogs/unreleased/my-feature.yml +--- +title: Hey DZ, I added a feature to GitLab! +merge_request: +author: +``` + +The entry filename is based on the name of the current Git branch. If you run +the command above on a branch called `feature/hey-dz`, it will generate a +`changelogs/unreleased/feature-hey-dz.yml` file. + +### Arguments + +| Argument | Shorthand | Purpose | +| ----------------- | --------- | --------------------------------------------- | +| `--amend` | | Amend the previous commit | +| `--force` | `-f` | Overwrite an existing entry | +| `--merge-request` | `-m` | Merge Request ID | +| `--dry-run` | `-n` | Don't actually write anything, just print | +| `--git-username` | `-u` | Use Git user.name configuration as the author | +| `--help` | `-h` | Print help message | + +#### `--amend` + +You can pass the **`--amend`** argument to automatically stage the generated +file and amend it to the previous commit. + +If you use **`--amend`** and don't provide a title, it will automatically use +the "subject" of the previous commit, which is the first line of the commit +message: + +```text +$ git show --oneline +ab88683 Added an awesome new feature to GitLab + +$ bin/changelog --amend +create changelogs/unreleased/feature-hey-dz.yml +--- +title: Added an awesome new feature to GitLab +merge_request: +author: +``` + +#### `--force` or `-f` + +Use **`--force`** or **`-f`** to overwrite an existing changelog entry if it +already exists. + +```text +$ bin/changelog 'Hey DZ, I added a feature to GitLab!' +error changelogs/unreleased/feature-hey-dz.yml already exists! Use `--force` to overwrite. + +$ bin/changelog 'Hey DZ, I added a feature to GitLab!' --force +create changelogs/unreleased/feature-hey-dz.yml +--- +title: Hey DZ, I added a feature to GitLab! +merge_request: 1983 +author: +``` + +#### `--merge-request` or `-m` + +Use the **`--merge-request`** or **`-m`** argument to provide the +`merge_request` value: + +```text +$ bin/changelog 'Hey DZ, I added a feature to GitLab!' -m 1983 +create changelogs/unreleased/feature-hey-dz.yml +--- +title: Hey DZ, I added a feature to GitLab! +merge_request: 1983 +author: +``` + +#### `--dry-run` or `-n` + +Use the **`--dry-run`** or **`-n`** argument to prevent actually writing or +committing anything: + +```text +$ bin/changelog --amend --dry-run +create changelogs/unreleased/feature-hey-dz.yml +--- +title: Added an awesome new feature to GitLab +merge_request: +author: + +$ ls changelogs/unreleased/ +``` + +#### `--git-username` or `-u` + +Use the **`--git-username`** or **`-u`** argument to automatically fill in the +`author` value with your configured Git `user.name` value: + +```text +$ git config user.name +Jane Doe + +$ bin/changelog --u 'Hey DZ, I added a feature to GitLab!' +create changelogs/unreleased/feature-hey-dz.yml +--- +title: Hey DZ, I added a feature to GitLab! +merge_request: +author: Jane Doe +``` + +## History and Reasoning + +Our `CHANGELOG` file was previously updated manually by each contributor that +felt their change warranted an entry. When two merge requests added their own +entries at the same spot in the list, it created a merge conflict in one as soon +as the other was merged. When we had dozens of merge requests fighting for the +same changelog entry location, this quickly became a major source of merge +conflicts and delays in development. + +This led us to a [boring solution] of "add your entry in a random location in +the list." This actually worked pretty well as we got further along in each +monthly release cycle, but at the start of a new cycle, when a new version +section was added and there were fewer places to "randomly" add an entry, the +conflicts became a problem again until we had a sufficient number of entries. + +On top of all this, it created an entirely different headache for [release managers] +when they cherry-picked a commit into a stable branch for a patch release. If +the commit included an entry in the `CHANGELOG`, it would include the entire +changelog for the latest version in `master`, so the release manager would have +to manually remove the later entries. They often would have had to do this +multiple times per patch release. This was compounded when we had to release +multiple patches at once due to a security issue. + +We needed to automate all of this manual work. So we [started brainstorming]. +After much discussion we settled on the current solution of one file per entry, +and then compiling the entries into the overall `CHANGELOG.md` file during the +[release process]. + +[boring solution]: https://about.gitlab.com/handbook/#boring-solutions +[release managers]: https://gitlab.com/gitlab-org/release-tools/blob/master/doc/release-manager.md +[started brainstorming]: https://gitlab.com/gitlab-org/gitlab-ce/issues/17826 +[release process]: https://gitlab.com/gitlab-org/release-tools + +--- + +[Return to Development documentation](README.md) diff --git a/doc/development/code_review.md b/doc/development/code_review.md index 40ae55ab905..c5c23b5c0b8 100644 --- a/doc/development/code_review.md +++ b/doc/development/code_review.md @@ -34,6 +34,10 @@ request is up to one of our merge request "endbosses", denoted on the ## Having your code reviewed +Please keep in mind that code review is a process that can take multiple +iterations, and reviewers may spot things later that they may not have seen the +first time. + - The first reviewer of your code is _you_. Before you perform that first push of your shiny new branch, read through the entire diff. Does it make sense? Did you include something unrelated to the overall purpose of the changes? Did @@ -55,6 +59,7 @@ request is up to one of our merge request "endbosses", denoted on the Understand why the change is necessary (fixes a bug, improves the user experience, refactors the existing code). Then: +- Try to be thorough in your reviews to reduce the number of iterations. - Communicate which ideas you feel strongly about and those you don't. - Identify ways to simplify the code while still solving the problem. - Offer alternative implementations, but assume the author already considered @@ -64,8 +69,10 @@ experience, refactors the existing code). Then: someone else would be confused by it as well. - After a round of line notes, it can be helpful to post a summary note such as "LGTM :thumbsup:", or "Just a couple things to address." -- Avoid accepting a merge request before the build succeeds ("Merge when build - succeeds" is fine). +- Avoid accepting a merge request before the build succeeds. Of course, "Merge + When Build Succeeds" (MWBS) is fine. +- If you set the MR to "Merge When Build Succeeds", you should take over + subsequent revisions for anything that would be spotted after that. ## Credits diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md index 39b801f761d..b137e6ae82e 100644 --- a/doc/development/doc_styleguide.md +++ b/doc/development/doc_styleguide.md @@ -93,12 +93,14 @@ merge request. links shift too, which eventually leads to dead links. If you think it is compelling to add numbers in headings, make sure to at least discuss it with someone in the Merge Request +- Avoid adding things that show ephemeral statuses. For example, if a feature is + considered beta or experimental, put this info in a note, not in the heading. - When introducing a new document, be careful for the headings to be grammatically and syntactically correct. It is advised to mention one or all - of the following GitLab members for a review: `@axil`, `@rspeicher`, - `@dblessing`, `@ashleys`. This is to ensure that no document - with wrong heading is going live without an audit, thus preventing dead links - and redirection issues when corrected + of the following GitLab members for a review: `@axil`, `@rspeicher`, `@marcia`, + `@SeanPackham`. This is to ensure that no document with wrong heading is going + live without an audit, thus preventing dead links and redirection issues when + corrected - Leave exactly one newline after a heading ## Links @@ -314,17 +316,34 @@ In this case: - different highlighting languages are used for each config in the code block - the [references](#references) guide is used for reconfigure/restart +## Fake tokens + +There may be times where a token is needed to demonstrate an API call using +cURL or a secret variable used in CI. It is strongly advised not to use real +tokens in documentation even if the probability of a token being exploited is +low. + +You can use the following fake tokens as examples. + +| **Token type** | **Token value** | +| --------------------- | --------------------------------- | +| Private user token | `9koXpg98eAheJpvBs5tK` | +| Personal access token | `n671WNGecHugsdEDPsyo` | +| Application ID | `2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6` | +| Application secret | `04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df` | +| Secret CI variable | `Li8j-mLUVA3eZYjPfd_H` | +| Specific Runner token | `yrnZW46BrtBFqM7xDzE7dddd` | +| Shared Runner token | `6Vk7ZsosqQyfreAxXTZr` | +| Trigger token | `be20d8dcc028677c931e04f3871a9b` | +| Webhook secret token | `6XhDroRcYPM5by_h-HLY` | +| Health check token | `Tu7BgjR9qeZTEyRzGG2P` | +| Request profile token | `7VgpS4Ax5utVD2esNstz` | + ## API Here is a list of must-have items. Use them in the exact order that appears on this document. Further explanation is given below. -- Every method must be described using [Grape's DSL](https://github.com/ruby-grape/grape/tree/v0.13.0#describing-methods) - (see https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/api/environments.rb - for a good example): - - `desc` for the method summary (you can pass it a block for additional details) - - `params` for the method params (this acts as description **and** validation - of the params) - Every method must have the REST API request. For example: ``` @@ -446,6 +465,7 @@ curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "domain [cURL]: http://curl.haxx.se/ "cURL website" [single spaces]: http://www.slate.com/articles/technology/technology/2011/01/space_invaders.html [gfm]: http://docs.gitlab.com/ce/user/markdown.html#newlines "GitLab flavored markdown documentation" +[ce-1242]: https://gitlab.com/gitlab-org/gitlab-ce/issues/1242 [doc-restart]: ../administration/restart_gitlab.md "GitLab restart documentation" [ce-3349]: https://gitlab.com/gitlab-org/gitlab-ce/issues/3349 "Documentation restructure" [graffle]: https://gitlab.com/gitlab-org/gitlab-design/blob/d8d39f4a87b90fb9ae89ca12dc565347b4900d5e/production/resources/gitlab-map.graffle diff --git a/doc/development/frontend.md b/doc/development/frontend.md new file mode 100644 index 00000000000..9e782ab977f --- /dev/null +++ b/doc/development/frontend.md @@ -0,0 +1,312 @@ +# Frontend Development Guidelines + +This document describes various guidelines to ensure consistency and quality +across GitLab's frontend team. + +## Overview + +GitLab is built on top of [Ruby on Rails][rails] using [Haml][haml] with +[Hamlit][hamlit]. Be wary of [the limitations that come with using +Hamlit][hamlit-limits]. We also use [SCSS][scss] and plain JavaScript with +[ES6 by way of Babel][es6]. + +The asset pipeline is [Sprockets][sprockets], which handles the concatenation, +minification, and compression of our assets. + +[jQuery][jquery] is used throughout the application's JavaScript, with +[Vue.js][vue] for particularly advanced, dynamic elements. + +### Vue + +For more complex frontend features, we recommend using Vue.js. It shares +some ideas with React.js as well as Angular. + +To get started with Vue, read through [their documentation][vue-docs]. + +## Performance + +### Resources + +- [WebPage Test][web-page-test] for testing site loading time and size. +- [Google PageSpeed Insights][pagespeed-insights] grades web pages and provides feedback to improve the page. +- [Profiling with Chrome DevTools][google-devtools-profiling] +- [Browser Diet][browser-diet] is a community-built guide that catalogues practical tips for improving web page performance. + +### Page-specific JavaScript + +Certain pages may require the use of a third party library, such as [d3][d3] for +the User Activity Calendar and [Chart.js][chartjs] for the Graphs pages. These +libraries increase the page size significantly, and impact load times due to +bandwidth bottlenecks and the browser needing to parse more JavaScript. + +In cases where libraries are only used on a few specific pages, we use +"page-specific JavaScript" to prevent the main `application.js` file from +becoming unnecessarily large. + +Steps to split page-specific JavaScript from the main `application.js`: + +1. Create a directory for the specific page(s), e.g. `graphs/`. +1. In that directory, create a `namespace_bundle.js` file, e.g. `graphs_bundle.js`. +1. In `graphs_bundle.js` add the line `//= require_tree .`, this adds all other files in the directory to the bundle. +1. Add any necessary libraries to `app/assets/javascripts/lib/`, all files directly descendant from this directory will be precompiled as separate assets, in this case `chart.js` would be added. +1. Add the new "bundle" file to the list of precompiled assets in +`config/application.rb`. + - For example: `config.assets.precompile << "graphs/graphs_bundle.js"`. +1. Move code reliant on these libraries into the `graphs` directory. +1. In the relevant views, add the scripts to the page with the following: + +```haml +- content_for :page_specific_javascripts do + = page_specific_javascript_tag('lib/chart.js') + = page_specific_javascript_tag('graphs/graphs_bundle.js') +``` + +The above loads `chart.js` and `graphs_bundle.js` for this page only. `chart.js` +is separated from the bundle file so it can be cached separately from the bundle +and reused for other pages that also rely on the library. For an example, see +[this Haml file][page-specific-js-example]. + +### Minimizing page size + +A smaller page size means the page loads faster (especially important on mobile +and poor connections), the page is parsed more quickly by the browser, and less +data is used for users with capped data plans. + +General tips: + +- Don't add new fonts. +- Prefer font formats with better compression, e.g. WOFF2 is better than WOFF, which is better than TTF. +- Compress and minify assets wherever possible (For CSS/JS, Sprockets does this for us). +- If some functionality can reasonably be achieved without adding extra libraries, avoid them. +- Use page-specific JavaScript as described above to dynamically load libraries that are only needed on certain pages. + +## Accessibility + +### Resources + +[Chrome Accessibility Developer Tools][chrome-accessibility-developer-tools] +are useful for testing for potential accessibility problems in GitLab. + +Accessibility best-practices and more in-depth information is available on +[the Audit Rules page][audit-rules] for the Chrome Accessibility Developer Tools. + +## Security + +### Resources + +[Mozilla’s HTTP Observatory CLI][observatory-cli] and the +[Qualys SSL Labs Server Test][qualys-ssl] are good resources for finding +potential problems and ensuring compliance with security best practices. + +<!-- Uncomment these sections when CSP/SRI are implemented. +### Content Security Policy (CSP) + +Content Security Policy is a web standard that intends to mitigate certain +forms of Cross-Site Scripting (XSS) as well as data injection. + +Content Security Policy rules should be taken into consideration when +implementing new features, especially those that may rely on connection with +external services. + +GitLab's CSP is used for the following: + +- Blocking plugins like Flash and Silverlight from running at all on our pages. +- Blocking the use of scripts and stylesheets downloaded from external sources. +- Upgrading `http` requests to `https` when possible. +- Preventing `iframe` elements from loading in most contexts. + +Some exceptions include: + +- Scripts from Google Analytics and Piwik if either is enabled. +- Connecting with GitHub, Bitbucket, GitLab.com, etc. to allow project importing. +- Connecting with Google, Twitter, GitHub, etc. to allow OAuth authentication. + +We use [the Secure Headers gem][secure_headers] to enable Content +Security Policy headers in the GitLab Rails app. + +Some resources on implementing Content Security Policy: + +- [MDN Article on CSP][mdn-csp] +- [GitHub’s CSP Journey on the GitHub Engineering Blog][github-eng-csp] +- The Dropbox Engineering Blog's series on CSP: [1][dropbox-csp-1], [2][dropbox-csp-2], [3][dropbox-csp-3], [4][dropbox-csp-4] + +### Subresource Integrity (SRI) + +Subresource Integrity prevents malicious assets from being provided by a CDN by +guaranteeing that the asset downloaded is identical to the asset the server +is expecting. + +The Rails app generates a unique hash of the asset, which is used as the +asset's `integrity` attribute. The browser generates the hash of the asset +on-load and will reject the asset if the hashes do not match. + +All CSS and JavaScript assets should use Subresource Integrity. For implementation details, +see the documentation for [the Sprockets implementation of SRI][sprockets-sri]. + +Some resources on implementing Subresource Integrity: + +- [MDN Article on SRI][mdn-sri] +- [Subresource Integrity on the GitHub Engineering Blog][github-eng-sri] + +--> + +### Including external resources + +External fonts, CSS, and JavaScript should never be used with the exception of +Google Analytics and Piwik - and only when the instance has enabled it. Assets +should always be hosted and served locally from the GitLab instance. Embedded +resources via `iframes` should never be used except in certain circumstances +such as with ReCaptcha, which cannot be used without an `iframe`. + +### Avoiding inline scripts and styles + +In order to protect users from [XSS vulnerabilities][xss], we will disable inline scripts in the future using Content Security Policy. + +While inline scripts can be useful, they're also a security concern. If +user-supplied content is unintentionally left un-sanitized, malicious users can +inject scripts into the web app. + +Inline styles should be avoided in almost all cases, they should only be used +when no alternatives can be found. This allows reusability of styles as well as +readability. + +## Style guides and linting + +See the relevant style guides for our guidelines and for information on linting: + +- [SCSS][scss-style-guide] + +## Testing + +Feature tests need to be written for all new features. Regression tests +also need to be written for all bug fixes to prevent them from occurring +again in the future. + +See [the Testing Standards and Style Guidelines](testing.md) for more +information. + +### Running frontend tests + +`rake teaspoon` runs the frontend-only (JavaScript) tests. +It consists of two subtasks: + +- `rake teaspoon:fixtures` (re-)generates fixtures +- `rake teaspoon:tests` actually executes the tests + +As long as the fixtures don't change, `rake teaspoon:tests` is sufficient +(and saves you some time). + +If you need to debug your tests and/or application code while they're +running, navigate to [localhost:3000/teaspoon](http://localhost:3000/teaspoon) +in your browser, open DevTools, and run tests for individual files by clicking +on them. This is also much faster than setting up and running tests from the +command line. + +Please note: Not all of the frontend fixtures are generated. Some are still static +files. These will not be touched by `rake teaspoon:fixtures`. + +## Design Patterns + +### Singletons + +When exactly one object is needed for a given task, prefer to define it as a +`class` rather than as an object literal. Prefer also to explicitly restrict +instantiation, unless flexibility is important (e.g. for testing). + +``` +// bad + +gl.MyThing = { + prop1: 'hello', + method1: () => {} +}; + +// good + +class MyThing { + constructor() { + this.prop1 = 'hello'; + } + method1() {} +} + +gl.MyThing = new MyThing(); + +// best + +let singleton; + +class MyThing { + constructor() { + if (!singleton) { + singleton = this; + singleton.init(); + } + return singleton; + } + + init() { + this.prop1 = 'hello'; + } + + method1() {} +} + +gl.MyThing = MyThing; + +``` + +## Supported browsers + +For our currently-supported browsers, see our [requirements][requirements]. + +[rails]: http://rubyonrails.org/ +[haml]: http://haml.info/ +[hamlit]: https://github.com/k0kubun/hamlit +[hamlit-limits]: https://github.com/k0kubun/hamlit/blob/master/REFERENCE.md#limitations +[scss]: http://sass-lang.com/ +[es6]: https://babeljs.io/ +[sprockets]: https://github.com/rails/sprockets +[jquery]: https://jquery.com/ +[vue]: http://vuejs.org/ +[vue-docs]: http://vuejs.org/guide/index.html +[web-page-test]: http://www.webpagetest.org/ +[pagespeed-insights]: https://developers.google.com/speed/pagespeed/insights/ +[google-devtools-profiling]: https://developers.google.com/web/tools/chrome-devtools/profile/?hl=en +[browser-diet]: https://browserdiet.com/ +[d3]: https://d3js.org/ +[chartjs]: http://www.chartjs.org/ +[page-specific-js-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/13bb9ed77f405c5f6ee4fdbc964ecf635c9a223f/app/views/projects/graphs/_head.html.haml#L6-8 +[chrome-accessibility-developer-tools]: https://github.com/GoogleChrome/accessibility-developer-tools +[audit-rules]: https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules +[observatory-cli]: https://github.com/mozilla/http-observatory-cli +[qualys-ssl]: https://www.ssllabs.com/ssltest/analyze.html +[secure_headers]: https://github.com/twitter/secureheaders +[mdn-csp]: https://developer.mozilla.org/en-US/docs/Web/Security/CSP +[github-eng-csp]: http://githubengineering.com/githubs-csp-journey/ +[dropbox-csp-1]: https://blogs.dropbox.com/tech/2015/09/on-csp-reporting-and-filtering/ +[dropbox-csp-2]: https://blogs.dropbox.com/tech/2015/09/unsafe-inline-and-nonce-deployment/ +[dropbox-csp-3]: https://blogs.dropbox.com/tech/2015/09/csp-the-unexpected-eval/ +[dropbox-csp-4]: https://blogs.dropbox.com/tech/2015/09/csp-third-party-integrations-and-privilege-separation/ +[mdn-sri]: https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity +[github-eng-sri]: http://githubengineering.com/subresource-integrity/ +[sprockets-sri]: https://github.com/rails/sprockets-rails#sri-support +[xss]: https://en.wikipedia.org/wiki/Cross-site_scripting +[scss-style-guide]: scss_styleguide.md +[requirements]: ../install/requirements.md#supported-web-browsers + +## Gotchas + +### Phantom.JS (used by Teaspoon & Rspec) chokes, returning vague JavaScript errors + +If you see very generic JavaScript errors (e.g. `jQuery is undefined`) being thrown in tests, but +can't reproduce them manually, you may have included `ES6`-style JavaScript in files that don't +have the `.js.es6` file extension. Either use ES5-friendly JavaScript or rename the file you're +working in (`git mv <file.js> <file.js.es6>`). + +Similar errors will be thrown if you're using +any of the [array methods introduced in ES6](http://www.2ality.com/2014/05/es6-array-methods.html) +whether or not you've updated the file extension. + + + diff --git a/doc/development/gitlab_architecture_diagram.png b/doc/development/gitlab_architecture_diagram.png Binary files differindex 80e975718e0..cda5ce254ce 100644 --- a/doc/development/gitlab_architecture_diagram.png +++ b/doc/development/gitlab_architecture_diagram.png diff --git a/doc/development/gotchas.md b/doc/development/gotchas.md index 159d5ce286d..7bfc9cb361f 100644 --- a/doc/development/gotchas.md +++ b/doc/development/gotchas.md @@ -32,6 +32,95 @@ spec/models/user_spec.rb|6 error| Failure/Error: u = described_class.new NoMeth Except for the top-level `describe` block, always provide a String argument to `describe`. +## Don't assert against the absolute value of a sequence-generated attribute + +Consider the following factory: + +```ruby +FactoryGirl.define do + factory :label do + sequence(:title) { |n| "label#{n}" } + end +end +``` + +Consider the following API spec: + +```ruby +require 'rails_helper' + +describe API::Labels do + it 'creates a first label' do + create(:label) + + get api("/projects/#{project.id}/labels", user) + + expect(response).to have_http_status(200) + expect(json_response.first['name']).to eq('label1') + end + + it 'creates a second label' do + create(:label) + + get api("/projects/#{project.id}/labels", user) + + expect(response).to have_http_status(200) + expect(json_response.first['name']).to eq('label1') + end +end +``` + +When run, this spec doesn't do what we might expect: + +```sh +1) API::API reproduce sequence issue creates a second label + Failure/Error: expect(json_response.first['name']).to eq('label1') + + expected: "label1" + got: "label2" + + (compared using ==) +``` + +That's because FactoryGirl sequences are not reseted for each example. + +Please remember that sequence-generated values exist only to avoid having to +explicitly set attributes that have a uniqueness constraint when using a factory. + +### Solution + +If you assert against a sequence-generated attribute's value, you should set it +explicitly. Also, the value you set shouldn't match the sequence pattern. + +For instance, using our `:label` factory, writing `create(:label, title: 'foo')` +is ok, but `create(:label, title: 'label1')` is not. + +Following is the fixed API spec: + +```ruby +require 'rails_helper' + +describe API::Labels do + it 'creates a first label' do + create(:label, title: 'foo') + + get api("/projects/#{project.id}/labels", user) + + expect(response).to have_http_status(200) + expect(json_response.first['name']).to eq('foo') + end + + it 'creates a second label' do + create(:label, title: 'bar') + + get api("/projects/#{project.id}/labels", user) + + expect(response).to have_http_status(200) + expect(json_response.first['name']).to eq('bar') + end +end +``` + ## Don't `rescue Exception` See ["Why is it bad style to `rescue Exception => e` in Ruby?"][Exception]. @@ -41,9 +130,9 @@ Rubocop](https://gitlab.com/gitlab-org/gitlab-ce/blob/8-4-stable/.rubocop.yml#L9 [Exception]: http://stackoverflow.com/q/10048173/223897 -## Don't use inline CoffeeScript/JavaScript in views +## Don't use inline JavaScript in views -Using the inline `:coffee` or `:coffeescript` Haml filters comes with a +Using the inline `:javascript` Haml filters comes with a performance overhead. Using inline JavaScript is not a good way to structure your code and should be avoided. _**Note:** We've [removed these two filters](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/initializers/hamlit.rb) @@ -51,9 +140,7 @@ in an initializer._ ### Further reading -- Pull Request: [Replace CoffeeScript block into JavaScript in Views](https://git.io/vztMu) - Stack Overflow: [Why you should not write inline JavaScript](http://programmers.stackexchange.com/questions/86589/why-should-i-avoid-inline-scripting) -- Stack Overflow: [Performance implications of using :coffescript filter inside HAML templates?](http://stackoverflow.com/a/17571242/223897) ## ID-based CSS selectors need to be a bit more specific diff --git a/doc/development/img/state-model-issue.png b/doc/development/img/state-model-issue.png Binary files differnew file mode 100644 index 00000000000..ee33b6886c6 --- /dev/null +++ b/doc/development/img/state-model-issue.png diff --git a/doc/development/img/state-model-legend.png b/doc/development/img/state-model-legend.png Binary files differnew file mode 100644 index 00000000000..1c121f2588c --- /dev/null +++ b/doc/development/img/state-model-legend.png diff --git a/doc/development/img/state-model-merge-request.png b/doc/development/img/state-model-merge-request.png Binary files differnew file mode 100644 index 00000000000..e00da10cac2 --- /dev/null +++ b/doc/development/img/state-model-merge-request.png diff --git a/doc/development/instrumentation.md b/doc/development/instrumentation.md index 105e2f1242a..b8669964c84 100644 --- a/doc/development/instrumentation.md +++ b/doc/development/instrumentation.md @@ -24,7 +24,7 @@ namespace you can use the `configure` class method. This method simply yields the supplied block while passing `Gitlab::Metrics::Instrumentation` as its argument. An example: -``` +```ruby Gitlab::Metrics::Instrumentation.configure do |conf| conf.instrument_method(Foo, :bar) conf.instrument_method(Foo, :baz) @@ -41,7 +41,7 @@ Method instrumentation should be added in the initializer Instrumenting a single method: -``` +```ruby Gitlab::Metrics::Instrumentation.configure do |conf| conf.instrument_method(User, :find_by) end @@ -49,7 +49,7 @@ end Instrumenting an entire class hierarchy: -``` +```ruby Gitlab::Metrics::Instrumentation.configure do |conf| conf.instrument_class_hierarchy(ActiveRecord::Base) end @@ -57,7 +57,7 @@ end Instrumenting all public class methods: -``` +```ruby Gitlab::Metrics::Instrumentation.configure do |conf| conf.instrument_methods(User) end @@ -68,7 +68,7 @@ end The easiest way to check if a method has been instrumented is to check its source location. For example: -``` +```ruby method = Rugged::TagCollection.instance_method(:[]) method.source_location diff --git a/doc/development/licensing.md b/doc/development/licensing.md index 8c8c7486fff..5d177eb26ee 100644 --- a/doc/development/licensing.md +++ b/doc/development/licensing.md @@ -54,6 +54,7 @@ Libraries with the following licenses are acceptable for use: - [BSD 2-Clause License][BSD-2-Clause]: A permissive (non-copyleft) license as defined by the Open Source Initiative. - [BSD 3-Clause License][BSD-3-Clause] (also known as New BSD or Modified BSD): A permissive (non-copyleft) license as defined by the Open Source Initiative - [ISC License][ISC] (also known as the OpenBSD License): A permissive (non-copyleft) license as defined by the Open Source Initiative. +- [Creative Commons Zero (CC0)][CC0]: A public domain dedication, recommended as a way to disclaim copyright on your work to the maximum extent possible. ## Unacceptable Licenses @@ -61,6 +62,7 @@ Libraries with the following licenses are unacceptable for use: - [GNU GPL][GPL] (version 1, [version 2][GPLv2], [version 3][GPLv3], or any future versions): GPL-licensed libraries cannot be linked to from non-GPL projects. - [GNU AGPLv3][AGPLv3]: AGPL-licensed libraries cannot be linked to from non-GPL projects. +- [Open Software License (OSL)][OSL]: is a copyleft license. In addition, the FSF [recommend against its use][OSL-GNU]. ## Notes @@ -85,9 +87,12 @@ Gems which are included only in the "development" or "test" groups by Bundler ar [BSD-2-Clause]: https://opensource.org/licenses/BSD-2-Clause [BSD-3-Clause]: https://opensource.org/licenses/BSD-3-Clause [ISC]: https://opensource.org/licenses/ISC +[CC0]: https://creativecommons.org/publicdomain/zero/1.0/ [GPL]: http://choosealicense.com/licenses/gpl-3.0/ [GPLv2]: http://www.gnu.org/licenses/gpl-2.0.txt [GPLv3]: http://www.gnu.org/licenses/gpl-3.0.txt [AGPLv3]: http://choosealicense.com/licenses/agpl-3.0/ [GNU-GPL-FAQ]: http://www.gnu.org/licenses/gpl-faq.html#IfLibraryIsGPL [OSI-GPL]: https://opensource.org/faq#linking-proprietary-code +[OSL]: https://opensource.org/licenses/OSL-3.0 +[OSL-GNU]: https://www.gnu.org/licenses/license-list.en.html#OSL diff --git a/doc/development/limit_ee_conflicts.md b/doc/development/limit_ee_conflicts.md new file mode 100644 index 00000000000..b7e6387838e --- /dev/null +++ b/doc/development/limit_ee_conflicts.md @@ -0,0 +1,272 @@ +# Limit conflicts with EE when developing on CE + +This guide contains best-practices for avoiding conflicts between CE and EE. + +## Context + +Usually, GitLab Community Edition is merged into the Enterprise Edition once a +week. During these merges, it's very common to get conflicts when some changes +in CE do not apply cleanly to EE. + +There are a few things that can help you as a developer to: + +- know when your merge request to CE will conflict when merged to EE +- avoid such conflicts in the first place +- ease future conflict resolutions if conflict is inevitable + +## Check the `rake ee_compat_check` in your merge requests + +For each commit (except on `master`), the `rake ee_compat_check` CI job tries to +detect if the current branch's changes will conflict during the CE->EE merge. + +The job reports what files are conflicting and how to setup a merge request +against EE. Here is roughly how it works: + +1. Generates the diff between your branch and current CE `master` +1. Tries to apply it to current EE `master` +1. If it applies cleanly, the job succeeds, otherwise... +1. Detects a branch with the `-ee` suffix in EE +1. If it exists, generate the diff between this branch and current EE `master` +1. Tries to apply it to current EE `master` +1. If it applies cleanly, the job succeeds + +In the case where the job fails, it means you should create a `<ce_branch>-ee` +branch, push it to EE and open a merge request against EE `master`. At this +point if you retry the failing job in your CE merge request, it should now pass. + +Notes: + +- This task is not a silver-bullet, its current goal is to bring awareness to + developers that their work needs to be ported to EE. +- Community contributors shouldn't submit merge requests against EE, but + reviewers should take actions by either creating such EE merge request or + asking a GitLab developer to do it once the merge request is merged. +- If you branch is more than 500 commits behind `master`, the job will fail and + you should rebase your branch upon latest `master`. + +## Possible type of conflicts + +### Controllers + +#### List or arrays are augmented in EE + +In controllers, the most common type of conflict is with `before_action` that +has a list of actions in CE but EE adds some actions to that list. + +The same problem often occurs for `params.require` / `params.permit` calls. + +##### Mitigations + +Separate CE and EE actions/keywords. For instance for `params.require` in +`ProjectsController`: + +```ruby +def project_params + params.require(:project).permit(project_params_ce) + # On EE, this is always: + # params.require(:project).permit(project_params_ce << project_params_ee) +end + +# Always returns an array of symbols, created however best fits the use case. +# It _should_ be sorted alphabetically. +def project_params_ce + %i[ + description + name + path + ] +end + +# (On EE) +def project_params_ee + %i[ + approvals_before_merge + approver_group_ids + approver_ids + ... + ] +end +``` + +#### Additional condition(s) in EE + +For instance for LDAP: + +```diff + def destroy + @key = current_user.keys.find(params[:id]) + - @key.destroy + + @key.destroy unless @key.is_a? LDAPKey + + respond_to do |format| +``` + +Or for Geo: + +```diff +def after_sign_out_path_for(resource) +- current_application_settings.after_sign_out_path.presence || new_user_session_path ++ if Gitlab::Geo.secondary? ++ Gitlab::Geo.primary_node.oauth_logout_url(@geo_logout_state) ++ else ++ current_application_settings.after_sign_out_path.presence || new_user_session_path ++ end +end +``` + +Or even for audit log: + +```diff +def approve_access_request +- Members::ApproveAccessRequestService.new(membershipable, current_user, params).execute ++ member = Members::ApproveAccessRequestService.new(membershipable, current_user, params).execute ++ ++ log_audit_event(member, action: :create) + + redirect_to polymorphic_url([membershipable, :members]) +end +``` + +### Views + +#### Additional view code in EE + +A block of code added in CE conflicts because there is already another block +at the same place in EE + +##### Mitigations + +Blocks of code that are EE-specific should be moved to partials as much as +possible to avoid conflicts with big chunks of HAML code that that are not fun +to resolve when you add the indentation to the equation. + +For instance this kind of thing: + +```haml +- if can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project) + - has_due_date = issuable.has_attribute?(:due_date) + %hr + .row + %div{ class: (has_due_date ? "col-lg-6" : "col-sm-12") } + .form-group.issue-assignee + = f.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}" + .col-sm-10{ class: ("col-lg-8" if has_due_date) } + .issuable-form-select-holder + - if issuable.assignee_id + = f.hidden_field :assignee_id + = dropdown_tag(user_dropdown_label(issuable.assignee_id, "Assignee"), options: { toggle_class: "js-dropdown-keep-input js-user-search js-issuable-form-dropdown js-assignee-search", title: "Select assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit", + placeholder: "Search assignee", data: { first_user: current_user.try(:username), null_user: true, current_user: true, project_id: project.try(:id), selected: issuable.assignee_id, field_name: "#{issuable.class.model_name.param_key}[assignee_id]", default_label: "Assignee"} }) + .form-group.issue-milestone + = f.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}" + .col-sm-10{ class: ("col-lg-8" if has_due_date) } + .issuable-form-select-holder + = render "shared/issuable/milestone_dropdown", selected: issuable.milestone, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false, extra_class: "js-issuable-form-dropdown js-dropdown-keep-input", dropdown_title: "Select milestone" + .form-group + - has_labels = @labels && @labels.any? + = f.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}" + = f.hidden_field :label_ids, multiple: true, value: '' + .col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" } + .issuable-form-select-holder + = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false, show_menu_above: 'true' }, dropdown_title: "Select label" + + - if issuable.respond_to?(:weight) + .form-group + = f.label :label_ids, class: "control-label #{"col-lg-4" if has_due_date}" do + Weight + .col-sm-10{ class: ("col-lg-8" if has_due_date) } + = f.select :weight, issues_weight_options(issuable.weight, edit: true), { include_blank: true }, + { class: 'select2 js-select2', data: { placeholder: "Select weight" }} + + - if has_due_date + .col-lg-6 + .form-group + = f.label :due_date, "Due date", class: "control-label" + .col-sm-10 + .issuable-form-select-holder + = f.text_field :due_date, id: "issuable-due-date", class: "datepicker form-control", placeholder: "Select due date" +``` + +could be simplified by using partials: + +```haml += render 'metadata_form', issuable: issuable +``` + +and then the `_metadata_form.html.haml` could be as follows: + +```haml +- return unless can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project) + +- has_due_date = issuable.has_attribute?(:due_date) +%hr +.row + %div{ class: (has_due_date ? "col-lg-6" : "col-sm-12") } + .form-group.issue-assignee + = f.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}" + .col-sm-10{ class: ("col-lg-8" if has_due_date) } + .issuable-form-select-holder + - if issuable.assignee_id + = f.hidden_field :assignee_id + = dropdown_tag(user_dropdown_label(issuable.assignee_id, "Assignee"), options: { toggle_class: "js-dropdown-keep-input js-user-search js-issuable-form-dropdown js-assignee-search", title: "Select assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit", + placeholder: "Search assignee", data: { first_user: current_user.try(:username), null_user: true, current_user: true, project_id: project.try(:id), selected: issuable.assignee_id, field_name: "#{issuable.class.model_name.param_key}[assignee_id]", default_label: "Assignee"} }) + .form-group.issue-milestone + = f.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}" + .col-sm-10{ class: ("col-lg-8" if has_due_date) } + .issuable-form-select-holder + = render "shared/issuable/milestone_dropdown", selected: issuable.milestone, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false, extra_class: "js-issuable-form-dropdown js-dropdown-keep-input", dropdown_title: "Select milestone" + .form-group + - has_labels = @labels && @labels.any? + = f.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}" + = f.hidden_field :label_ids, multiple: true, value: '' + .col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" } + .issuable-form-select-holder + = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false, show_menu_above: 'true' }, dropdown_title: "Select label" + + = render 'weight_form', issuable: issuable, has_due_date: has_due_date + + - if has_due_date + .col-lg-6 + .form-group + = f.label :due_date, "Due date", class: "control-label" + .col-sm-10 + .issuable-form-select-holder + = f.text_field :due_date, id: "issuable-due-date", class: "datepicker form-control", placeholder: "Select due date" +``` + +and then the `_weight_form.html.haml` could be as follows: + +```haml +- return unless issuable.respond_to?(:weight) + +- has_due_date = issuable.has_attribute?(:due_date) + +.form-group + = f.label :label_ids, class: "control-label #{"col-lg-4" if has_due_date}" do + Weight + .col-sm-10{ class: ("col-lg-8" if has_due_date) } + = f.select :weight, issues_weight_options(issuable.weight, edit: true), { include_blank: true }, + { class: 'select2 js-select2', data: { placeholder: "Select weight" }} +``` + +Note: + +- The safeguards at the top allow to get rid of an unneccessary indentation level +- Here we only moved the 'Weight' code to a partial since this is the only + EE-specific code in that view, so it's the most likely to conflict, but you + are encouraged to use partials even for code that's in CE to logically split + big views into several smaller files. + +#### Indentation issue + +Sometimes a code block is indented more or less in EE because there's an +additional condition. + +##### Mitigations + +Blocks of code that are EE-specific should be moved to partials as much as +possible to avoid conflicts with big chunks of HAML code that that are not fun +to resolve when you add the indentation in the equation. + +--- + +[Return to Development documentation](README.md) diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md index b8fab3aaff7..fd8335d251e 100644 --- a/doc/development/migration_style_guide.md +++ b/doc/development/migration_style_guide.md @@ -9,10 +9,10 @@ a big burden for most organizations. For this reason it is important that your migrations are written carefully, can be applied online and adhere to the style guide below. Migrations should not require GitLab installations to be taken offline unless -_absolutely_ necessary. If a migration requires downtime this should be -clearly mentioned during the review process as well as being documented in the -monthly release post. For more information see the "Downtime Tagging" section -below. +_absolutely_ necessary - see the ["What Requires Downtime?"](what_requires_downtime.md) +page. If a migration requires downtime, this should be clearly mentioned during +the review process, as well as being documented in the monthly release post. For +more information, see the "Downtime Tagging" section below. When writing your migrations, also consider that databases might have stale data or inconsistencies and guard for that. Try to make as little assumptions as possible @@ -60,7 +60,7 @@ migration was tested. If you need to remove index, please add a condition like in following example: -``` +```ruby remove_index :namespaces, column: :name if index_exists?(:namespaces, :name) ``` @@ -75,7 +75,7 @@ need for downtime. To use this method you must disable transactions by calling the method `disable_ddl_transaction!` in the body of your migration class like so: -``` +```ruby class MyMigration < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers disable_ddl_transaction! @@ -96,7 +96,7 @@ the `up` and `down` methods in your migration class. For example, to add the column `foo` to the `projects` table with a default value of `10` you'd write the following: -``` +```ruby class MyMigration < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers disable_ddl_transaction! @@ -111,6 +111,28 @@ class MyMigration < ActiveRecord::Migration end ``` + +## Integer column type + +By default, an integer column can hold up to a 4-byte (32-bit) number. That is +a max value of 2,147,483,647. Be aware of this when creating a column that will +hold file sizes in byte units. If you are tracking file size in bytes this +restricts the maximum file size to just over 2GB. + +To allow an integer column to hold up to an 8-byte (64-bit) number, explicitly +set the limit to 8-bytes. This will allow the column to hold a value up to +9,223,372,036,854,775,807. + +Rails migration example: + +```ruby +add_column_with_default(:projects, :foo, :integer, default: 10, limit: 8) + +# or + +add_column(:projects, :foo, :integer, default: 10, limit: 8) +``` + ## Testing Make sure that your migration works with MySQL and PostgreSQL with data. An empty database does not guarantee that your migration is correct. @@ -123,7 +145,7 @@ Please prefer Arel and plain SQL over usual ActiveRecord syntax. In case of usin Example with Arel: -``` +```ruby users = Arel::Table.new(:users) users.group(users[:user_id]).having(users[:id].count.gt(5)) @@ -132,7 +154,7 @@ users.group(users[:user_id]).having(users[:id].count.gt(5)) Example with plain SQL and `quote_string` helper: -``` +```ruby select_all("SELECT name, COUNT(id) as cnt FROM tags GROUP BY name HAVING COUNT(id) > 1").each do |tag| tag_name = quote_string(tag["name"]) duplicate_ids = select_all("SELECT id FROM tags WHERE name = '#{tag_name}'").map{|tag| tag["id"]} diff --git a/doc/development/object_state_models.md b/doc/development/object_state_models.md new file mode 100644 index 00000000000..623bbf143ef --- /dev/null +++ b/doc/development/object_state_models.md @@ -0,0 +1,25 @@ +# Object state models + +## Diagrams + +[GitLab object state models](https://drive.google.com/drive/u/3/folders/0B5tDlHAM4iZINmpvYlJXcDVqMGc) + +--- + +## Legend + + + +--- + +## Issue + +[`app/models/issue.rb`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/models/issue.rb) + + +--- + +## Merge request + +[`app/models/merge_request.rb`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/models/merge_request.rb) +
\ No newline at end of file diff --git a/doc/development/performance.md b/doc/development/performance.md index 7ff603e2c4a..8337c2d9cb3 100644 --- a/doc/development/performance.md +++ b/doc/development/performance.md @@ -34,10 +34,11 @@ graphs/dashboards. ## Tooling -GitLab provides two built-in tools to aid the process of improving performance: +GitLab provides built-in tools to aid the process of improving performance: * [Sherlock](profiling.md#sherlock) -* [GitLab Performance Monitoring](../monitoring/performance/monitoring.md) +* [GitLab Performance Monitoring](../administration/monitoring/performance/monitoring.md) +* [Request Profiling](../administration/monitoring/performance/request_profiling.md) GitLab employees can use GitLab.com's performance monitoring systems located at <http://performance.gitlab.net>, this requires you to log in using your @@ -253,5 +254,5 @@ impact on runtime performance, and as such, using a constant instead of referencing an object directly may even slow code down. [#15607]: https://gitlab.com/gitlab-org/gitlab-ce/issues/15607 -[yorickpeterse]: https://gitlab.com/u/yorickpeterse +[yorickpeterse]: https://gitlab.com/yorickpeterse [anti-pattern]: https://en.wikipedia.org/wiki/Anti-pattern diff --git a/doc/development/post_deployment_migrations.md b/doc/development/post_deployment_migrations.md new file mode 100644 index 00000000000..cfc91539bee --- /dev/null +++ b/doc/development/post_deployment_migrations.md @@ -0,0 +1,75 @@ +# Post Deployment Migrations + +Post deployment migrations are regular Rails migrations that can optionally be +executed after a deployment. By default these migrations are executed alongside +the other migrations. To skip these migrations you will have to set the +environment variable `SKIP_POST_DEPLOYMENT_MIGRATIONS` to a non-empty value +when running `rake db:migrate`. + +For example, this would run all migrations including any post deployment +migrations: + +```bash +bundle exec rake db:migrate +``` + +This however will skip post deployment migrations: + +```bash +SKIP_POST_DEPLOYMENT_MIGRATIONS=true bundle exec rake db:migrate +``` + +## Deployment Integration + +Say you're using Chef for deploying new versions of GitLab and you'd like to run +post deployment migrations after deploying a new version. Let's assume you +normally use the command `chef-client` to do so. To make use of this feature +you'd have to run this command as follows: + +```bash +SKIP_POST_DEPLOYMENT_MIGRATIONS=true sudo chef-client +``` + +Once all servers have been updated you can run `chef-client` again on a single +server _without_ the environment variable. + +The process is similar for other deployment techniques: first you would deploy +with the environment variable set, then you'll essentially re-deploy a single +server but with the variable _unset_. + +## Creating Migrations + +To create a post deployment migration you can use the following Rails generator: + +```bash +bundle exec rails g post_deployment_migration migration_name_here +``` + +This will generate the migration file in `db/post_migrate`. These migrations +behave exactly like regular Rails migrations. + +## Use Cases + +Post deployment migrations can be used to perform migrations that mutate state +that an existing version of GitLab depends on. For example, say you want to +remove a column from a table. This requires downtime as a GitLab instance +depends on this column being present while it's running. Normally you'd follow +these steps in such a case: + +1. Stop the GitLab instance +2. Run the migration removing the column +3. Start the GitLab instance again + +Using post deployment migrations we can instead follow these steps: + +1. Deploy a new version of GitLab while ignoring post deployment migrations +2. Re-run `rake db:migrate` but without the environment variable set + +Here we don't need any downtime as the migration takes place _after_ a new +version (which doesn't depend on the column anymore) has been deployed. + +Some other examples where these migrations are useful: + +* Cleaning up data generated due to a bug in GitLab +* Removing tables +* Migrating jobs from one Sidekiq queue to another diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md index a7175f3f87e..827db7e99b8 100644 --- a/doc/development/rake_tasks.md +++ b/doc/development/rake_tasks.md @@ -42,14 +42,6 @@ To run several tests inside one directory: If you want to use [Spring](https://github.com/rails/spring) set `ENABLE_SPRING=1` in your environment. -## Generate searchable docs for source code - -You can find results under the `doc/code` directory. - -``` -bundle exec rake gitlab:generate_docs -``` - ## Generate API documentation for project services (e.g. Slack) ``` diff --git a/doc/development/shell_commands.md b/doc/development/shell_commands.md index 65cdd74bdb6..73893f9dd46 100644 --- a/doc/development/shell_commands.md +++ b/doc/development/shell_commands.md @@ -129,7 +129,7 @@ Various methods for opening and reading files in Ruby can be used to read the standard output of a process instead of a file. The following two commands do roughly the same: -``` +```ruby `touch /tmp/pawned-by-backticks` File.read('|touch /tmp/pawned-by-file-read') ``` @@ -142,7 +142,7 @@ attacker cannot control the start of the filename string you are opening. For instance, the following is sufficient to protect against accidentally starting a shell command with `|`: -``` +```ruby # we assume repo_path is not controlled by the attacker (user) path = File.join(repo_path, user_input) # path cannot start with '|' now. @@ -160,7 +160,7 @@ Path traversal is a security where the program (GitLab) tries to restrict user access to a certain directory on disk, but the user manages to open a file outside that directory by taking advantage of the `../` path notation. -``` +```ruby # Suppose the user gave us a path and they are trying to trick us user_input = '../other-repo.git/other-file' @@ -177,7 +177,7 @@ File.open(full_path) do # Oops! A good way to protect against this is to compare the full path with its 'absolute path' according to Ruby's `File.absolute_path`. -``` +```ruby full_path = File.join(repo_path, user_input) if full_path != File.absolute_path(full_path) raise "Invalid path: #{full_path.inspect}" diff --git a/doc/development/sidekiq_style_guide.md b/doc/development/sidekiq_style_guide.md new file mode 100644 index 00000000000..e3a20f29a09 --- /dev/null +++ b/doc/development/sidekiq_style_guide.md @@ -0,0 +1,38 @@ +# Sidekiq Style Guide + +This document outlines various guidelines that should be followed when adding or +modifying Sidekiq workers. + +## Default Queue + +Use of the "default" queue is not allowed. Every worker should use a queue that +matches the worker's purpose the closest. For example, workers that are to be +executed periodically should use the "cronjob" queue. + +A list of all available queues can be found in `config/sidekiq_queues.yml`. + +## Dedicated Queues + +Most workers should use their own queue. To ease this process a worker can +include the `DedicatedSidekiqQueue` concern as follows: + +```ruby +class ProcessSomethingWorker + include Sidekiq::Worker + include DedicatedSidekiqQueue +end +``` + +This will set the queue name based on the class' name, minus the `Worker` +suffix. In the above example this would lead to the queue being +`process_something`. + +In some cases multiple workers do use the same queue. For example, the various +workers for updating CI pipelines all use the `pipeline` queue. Adding workers +to existing queues should be done with care, as adding more workers can lead to +slow jobs blocking work (even for different jobs) on the shared queue. + +## Tests + +Each Sidekiq worker must be tested using RSpec, just like any other class. These +tests should be placed in `spec/workers`. diff --git a/doc/development/testing.md b/doc/development/testing.md index 513457d203a..dbea6b9c9aa 100644 --- a/doc/development/testing.md +++ b/doc/development/testing.md @@ -36,8 +36,8 @@ the command line via `bundle exec teaspoon`, or via a web browser at `http://localhost:3000/teaspoon` when the Rails server is running. - JavaScript tests live in `spec/javascripts/`, matching the folder structure of - `app/assets/javascripts/`: `app/assets/javascripts/behaviors/autosize.js.coffee` has a corresponding - `spec/javascripts/behaviors/autosize_spec.js.coffee` file. + `app/assets/javascripts/`: `app/assets/javascripts/behaviors/autosize.js.es6` has a corresponding + `spec/javascripts/behaviors/autosize_spec.js.es6` file. - Haml fixtures required for JavaScript tests live in `spec/javascripts/fixtures`. They should contain the bare minimum amount of markup necessary for the test. @@ -63,12 +63,16 @@ the command line via `bundle exec teaspoon`, or via a web browser at - Use `.method` to describe class methods and `#method` to describe instance methods. - Use `context` to test branching logic. +- Use multi-line `do...end` blocks for `before` and `after`, even when it would + fit on a single line. - Don't `describe` symbols (see [Gotchas](gotchas.md#dont-describe-symbols)). +- Don't assert against the absolute value of a sequence-generated attribute (see [Gotchas](gotchas.md#dont-assert-against-the-absolute-value-of-a-sequence-generated-attribute)). - Don't supply the `:each` argument to hooks since it's the default. - Prefer `not_to` to `to_not` (_this is enforced by Rubocop_). - Try to match the ordering of tests to the ordering within the class. - Try to follow the [Four-Phase Test][four-phase-test] pattern, using newlines to separate phases. +- Try to use `Gitlab.config.gitlab.host` rather than hard coding `'localhost'` [four-phase-test]: https://robots.thoughtbot.com/four-phase-test @@ -132,6 +136,42 @@ Adding new Spinach scenarios is acceptable _only if_ the new scenario requires no more than one new `step` definition. If more than that is required, the test should be re-implemented using RSpec instead. +## Testing Rake Tasks + +To make testing Rake tasks a little easier, there is a helper that can be included +in lieu of the standard Spec helper. Instead of `require 'spec_helper'`, use +`require 'rake_helper'`. The helper includes `spec_helper` for you, and configures +a few other things to make testing Rake tasks easier. + +At a minimum, requiring the Rake helper will redirect `stdout`, include the +runtime task helpers, and include the `RakeHelpers` Spec support module. + +The `RakeHelpers` module exposes a `run_rake_task(<task>)` method to make +executing tasks simple. See `spec/support/rake_helpers.rb` for all available +methods. + +Example: + +```ruby +require 'rake_helper' + +describe 'gitlab:shell rake tasks' do + before do + Rake.application.rake_require 'tasks/gitlab/shell' + + stub_warn_user_is_not_gitlab + end + + describe 'install task' do + it 'invokes create_hooks task' do + expect(Rake::Task['gitlab:shell:create_hooks']).to receive(:invoke) + + run_rake_task('gitlab:shell:install') + end + end +end +``` + --- [Return to Development documentation](README.md) diff --git a/doc/development/ux_guide/basics.md b/doc/development/ux_guide/basics.md new file mode 100644 index 00000000000..62ac56a6bce --- /dev/null +++ b/doc/development/ux_guide/basics.md @@ -0,0 +1,94 @@ +# Basics + +## Contents +* [Responsive](#responsive) +* [Typography](#typography) +* [Icons](#icons) +* [Color](#color) +* [Motion](#motion) +* [Voice and tone](#voice-and-tone) + +--- + +## Responsive +GitLab is a responsive experience that works well across all screen sizes, from mobile devices to large monitors. In order to provide a great user experience, the core functionality (browsing files, creating issues, writing comments, etc.) must be available at all resolutions. However, due to size limitations, some secondary functionality may be hidden on smaller screens. Please keep this functionality limited to rare actions that aren't expected to be needed on small devices. + +--- + +## Typography +### Primary typeface +GitLab's main typeface used throughout the UI is **Source Sans Pro**. We support both the bold and regular weight. + + + + +### Monospace typeface +This is the typeface used for code blocks. GitLab uses the OS default font. +- **Menlo** (Mac) +- **Consolas** (Windows) +- **Liberation Mono** (Linux) + + + +--- + +## Icons +GitLab uses Font Awesome icons throughout our interface. + + +The trash icon is used for destructive actions that deletes information. + + +The pencil icon is used for editing content such as comments. + + +The bell icon is for notifications, such as Todos. + + +The eye icon is for subscribing to updates. For example, you can subscribe to a label and get updated on issues with that label. + + +The standard RSS icon is used for linking to RSS/atom feeds. + + +An 'x' is used for closing UI elements such as dropdowns. + + +A plus is used when creating new objects, such as issues, projects, etc. + +> TODO: update this section, add more general guidance to icon usage and personality, etc. + +--- + +## Color + + +Blue is used to highlight primary active elements (such as current tab), as well as other organization and managing commands. + + +Green is for actions that create new objects. + + +Orange is used for warnings + + +Red is reserved for delete and other destructive commands + + +Grey, and white (depending on context) is used for netral, secondary elements + +> TODO: Establish a perspective for color in terms of our personality and rationalize with Marketing usage. + +--- + +## Motion + +Motion is a tool to help convey important relationships, changes or transitions between elements. It should be used sparingly and intentionally, highlighting the right elements at the right moment. + +> TODO: Determine a more concrete perspective on motion, create consistent easing/timing curves to follow. + +--- + +## Voice and tone + +The copy for GitLab is clear and direct. We strike a clear balance between professional and friendly. We can empathesize with users (such as celebrating completing all Todos), and remain respectful of the importance of the work. We are that trusted, friendly coworker that is helpful and understanding. diff --git a/doc/development/ux_guide/components.md b/doc/development/ux_guide/components.md new file mode 100644 index 00000000000..764c3355714 --- /dev/null +++ b/doc/development/ux_guide/components.md @@ -0,0 +1,254 @@ +# Components + +## Contents +* [Tooltips](#tooltips) +* [Anchor links](#anchor-links) +* [Buttons](#buttons) +* [Dropdowns](#dropdowns) +* [Counts](#counts) +* [Lists](#lists) +* [Tables](#tables) +* [Blocks](#blocks) +* [Panels](#panels) +* [Alerts](#alerts) +* [Forms](#forms) +* [File holders](#file-holders) +* [Data formats](#data-formats) + +--- + +## Tooltips + +### Usage +A tooltip should only be added if additional information is required. + + + +### Placement +By default, tooltips should be placed below the element that they refer to. However, if there is not enough space in the viewpoint, the tooltip should be moved to the side as needed. + + + +--- + +## Anchor links + +Anchor links are used for navigational actions and lone, secondary commands (such as 'Reset filters' on the Issues List) when deemed appropriate by the UX team. + +### States + +#### Rest + +Primary links are blue in their rest state. Secondary links (such as the time stamp on comments) are a neutral gray color in rest. Details on the main GitLab navigation links can be found on the [features](features.md#navigation) page. + +#### Hover + +An underline should always be added on hover. A gray link becomes blue on hover. + +#### Focus + +The focus state should match the hover state. + + + +--- + +## Buttons + +Buttons communicate the command that will occur when the user clicks on them. + +### Types + +#### Primary +Primary buttons communicate the main call to action. There should only be one call to action in any given experience. Visually, primary buttons are conveyed with a full background fill + + + +#### Secondary +Secondary buttons are for alternative commands. They should be conveyed by a button with an stroke, and no background fill. + + + +### Icon and text treatment +Text should be in sentence case, where only the first word is capitalized. "Create issue" is correct, not "Create Issue". Buttons should only contain an icon or a text, not both. + +>>> +TODO: Rationalize this. Ensure that we still believe this. +>>> + +### Colors +Follow the color guidance on the [basics](basics.md#color) page. The default color treatment is the white/grey button. + +--- + +## Dropdowns + +Dropdowns are used to allow users to choose one (or many) options from a list of options. If this list of options is more 20, there should generally be a way to search through and filter the options (see the complex filter dropdowns below.) + +>>> +TODO: Will update this section when the new filters UI is implemented. +>>> + + + + + +--- + +## Counts + +A count element is used in navigation contexts where it is helpful to indicate the count, or number of items, in a list. Always use the [`number_with_delimiter`][number_with_delimiter] helper to display counts in the UI. + + + +[number_with_delimiter]: http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#method-i-number_with_delimiter + +--- + +## Lists + +Lists are used where ever there is a single column of information to display. Ths [issues list](https://gitlab.com/gitlab-org/gitlab-ce/issues) is an example of a important list in the GitLab UI. + +### Types + +Simple list using .content-list + + + +List with avatar, title and description using .content-list + + + +List with hover effect .well-list + + + +List inside panel + + + +--- + +## Tables + +When the information is too complex for a list, with multiple columns of information, a table can be used. For example, the [pipelines page](https://gitlab.com/gitlab-org/gitlab-ce/pipelines) uses a table. + + + +--- + +## Blocks + +Blocks are a way to group related information. + +### Types + +#### Content blocks + +Content blocks (`.content-block`) are the basic grouping of content. They are commonly used in [lists](#lists), and are separated by a botton border. + + + +#### Row content blocks + +A background color can be added to this blocks. For example, items in the [issue list](https://gitlab.com/gitlab-org/gitlab-ce/issues) have a green background if they were created recently. Below is an example of a gray content block with side padding using `.row-content-block`. + + + +#### Cover blocks +Cover blocks are generally used to create a heading element for a page, such as a new project, or a user profile page. Below is a cover block (`.cover-block`) for the profile page with an avatar, name and description. + + + +--- + +## Panels + +>>> +TODO: Catalog how we are currently using panels and rationalize how they relate to alerts +>>> + + + +--- + +## Alerts + +>>> +TODO: Catalog how we are currently using alerts +>>> + + + +--- + +## Forms + +There are two options shown below regarding the positioning of labels in forms. Both are options to consider based on context and available size. However, it is important to have a consistent treatment of labels in the same form. + +### Types + +#### Labels stack vertically + +Form (`form`) with label rendered above input. + + + +#### Labels side-by-side + +Horizontal form (`form.horizontal-form`) with label rendered inline with input. + + + +--- + +## File holders +A file holder (`.file-holder`) is used to show the contents of a file inline on a page of GitLab. + + + +--- + +## Data formats + +### Dates + +#### Exact + +Format for exacts dates should be ‘Mon DD, YYYY’, such as the examples below. + + + +#### Relative + +This format relates how long since an action has occurred. The exact date can be shown as a tooltip on hover. + + + +### References + +Referencing GitLab items depends on a symbol for each type of item. Typing that symbol will invoke a dropdown that allows you to search for and autocomplete the item you were looking for. References are shown as [links](#links) in context, and hovering on them shows the full title or name of the item. + + + +#### `%` Milestones + + + +#### `#` Issues + + + +#### `!` Merge Requests + + + +#### `~` Labels + + + +#### `@` People + + + +> TODO: Open issue: Some commit references use monospace fonts, but others don't. Need to standardize this. diff --git a/doc/development/ux_guide/copy.md b/doc/development/ux_guide/copy.md new file mode 100644 index 00000000000..b557fb47120 --- /dev/null +++ b/doc/development/ux_guide/copy.md @@ -0,0 +1,101 @@ +# Copy + +The copy and messaging is a core part of the experience of GitLab and the conversation with our users. Follow the below conventions throughout GitLab. + +>**Note:** +We are currently inconsistent with this guidance. Images below are created to illustrate the point. As this guidance is refined, we will ensure that our experiences align. + +## Contents +* [Brevity](#brevity) +* [Sentence case](#sentence-case) +* [Terminology](#terminology) + +--- + +## Brevity +Users will skim content, rather than read text carefully. +When familiar with a web app, users rely on muscle memory, and may read even less when moving quickly. +A good experience should quickly orient a user, regardless of their experience, to the purpose of the current screen. This should happen without the user having to consciously read long strings of text. +In general, text is burdensome and adds cognitive load. This is especially pronounced in a powerful productivity tool such as GitLab. +We should _not_ rely on words as a crutch to explain the purpose of a screen. +The current navigation and composition of the elements on the screen should get the user 95% there, with the remaining 5% being specific elements such as text. +This means that, as a rule, copy should be very short. A long message or label is a red flag hinting at design that needs improvement. + +>**Example:** +Use `Add` instead of `Add issue` as a button label. +Preferrably use context and placement of controls to make it obvious what clicking on them will do. + +--- + +## Sentence case +Use sentence case for all titles, headings, labels, menu items, and buttons. + +--- + +## Terminology +Only use the terms in the tables below. + +### Issues + +#### Adjectives (states) + +| Term | +| ---- | +| Open | +| Closed | +| Deleted | + +>**Example:** +Use `5 open issues` and don't use `5 pending issues`. + +#### Verbs (actions) + +| Term | Use | Don't | +| ---- | --- | --- | +| Add | Add an issue | Don't use `create` or `new` | +| View | View an open or closed issue || +| Edit | Edit an open or closed issue | Don't use `update` | +| Close | Close an open issue || +| Re-open | Re-open a closed issue | There should never be a need to use `open` as a verb | +| Delete | Delete an open or closed issue || + +#### Add issue + +When viewing a list of issues, there is a button that is labeled `Add`. Given the context in the example, it is clearly referring to issues. If the context were not clear enough, the label could be `Add issue`. Clicking the button will bring you to the `Add issue` form. Other add flows should be similar. + + + +The form should be titled `Add issue`. The submit button should be labeled `Submit`. Don't use `Add`, `Create`, `New`, or `Save changes`. The cancel button should be labeled `Cancel`. Don't use `Back`. + + + +#### Edit issue + +When in context of an issue, the affordance to edit it is labeled `Edit`. If the context is not clear enough, `Edit issue` could be considered. Other edit flows should be similar. + + + +The form should be titled `Edit issue`. The submit button should be labeled `Save`. Don't use `Edit`, `Update`, `Submit`, or `Save changes`. The cancel button should be labeled `Cancel`. Don't use `Back`. + + + + +### Merge requests + +#### Adjectives (states) + +| Term | +| ---- | +| Open | +| Merged | + +#### Verbs (actions) + +| Term | Use | Don't | +| ---- | --- | --- | +| Add | Add a merge request | Do not use `create` or `new` | +| View | View an open or merged merge request || +| Edit | Edit an open or merged merge request| Do not use `update` | +| Approve | Approve an open merge request || +| Remove approval, unapproved | Remove approval of an open merge request | Do not use `unapprove` as that is not an English word| +| Merge | Merge an open merge request || diff --git a/doc/development/ux_guide/features.md b/doc/development/ux_guide/features.md new file mode 100644 index 00000000000..9472995c68c --- /dev/null +++ b/doc/development/ux_guide/features.md @@ -0,0 +1,57 @@ +# Features + +## Contents +* [Navigation](#navigation) +* [Filtering](#filtering) +* [Search results](#search-results) +* [Conversations](#conversations) +* [Empty states](#empty-states) + +--- + +## Navigation + +### Global navigation + +The global navigation is accessible via the menu button on the top left of the screen, and can be pinned to keep it open. It contains a consistent list of pages that allow you to view content that is across GitLab. For example, you can view your todos, issues and merge requests across projects and groups. + + + + +### Contextual navigation + +The navigation in the header is contextual to each page. These options change depending on if you are looking at a project, group, or settings page. There should be no more than 10 items on a level in the contextual navigation, allowing it to comfortably fit on a typical laptop screen. There can be up to too levels of navigation. Each sub nav group should be a self-contained group of functionality. For example, everything related to the issue tracker should be under the 'Issue' tab, while everything relating to the wiki will be grouped under the 'Wiki' tab. The names used for each section should be short and easy to remember, ideally 1-2 words in length. + + + +### Information architecture + +The [GitLab Product Map](https://gitlab.com/gitlab-org/gitlab-design/raw/master/production/resources/gitlab-map.png) shows a visual representation of the information architecture for GitLab. + +--- + +## Filtering + +Today, lists are filtered by a series of dropdowns. Some of these dropdowns allow multiselect (labels), while others allow you to filter to one option (milestones). However, we are currently implementing a [new model](https://gitlab.com/gitlab-org/gitlab-ce/issues/21747) for this, and will update the guide when it is ready. + + + +--- + +## Search results + +### Global search + +[Global search](https://gitlab.com/search?group_id=&project_id=13083&repository_ref=&scope=issues&search=mobile) allows you to search across items in a project, or even across multiple projects. You can switch tabs to filter on type of object, or filter by group. + +### List search + +There are several core lists in the GitLab experience, such as the Issue list and the Merge Request list. You are also able to [filter and search these lists](https://gitlab.com/gitlab-org/gitlab-ce/issues?utf8=%E2%9C%93&search=mobile). This UI will be updated with the [new filtering model](https://gitlab.com/gitlab-org/gitlab-ce/issues/21747). + +--- + +## Empty states + +Empty states need to be considered in the design of features. They are vital to helping onboard new users, making the experience feel more approachable and understandable. Empty states should feel inviting and provide just enough information to get people started. There should be a single call to action and a clear explanation of what to use the feature for. + + diff --git a/doc/development/ux_guide/img/button-primary.png b/doc/development/ux_guide/img/button-primary.png Binary files differnew file mode 100644 index 00000000000..eda5ed84aec --- /dev/null +++ b/doc/development/ux_guide/img/button-primary.png diff --git a/doc/development/ux_guide/img/button-secondary.png b/doc/development/ux_guide/img/button-secondary.png Binary files differnew file mode 100644 index 00000000000..26d4e8cf43d --- /dev/null +++ b/doc/development/ux_guide/img/button-secondary.png diff --git a/doc/development/ux_guide/img/color-blue.png b/doc/development/ux_guide/img/color-blue.png Binary files differnew file mode 100644 index 00000000000..2ca360173eb --- /dev/null +++ b/doc/development/ux_guide/img/color-blue.png diff --git a/doc/development/ux_guide/img/color-green.png b/doc/development/ux_guide/img/color-green.png Binary files differnew file mode 100644 index 00000000000..489db8f4343 --- /dev/null +++ b/doc/development/ux_guide/img/color-green.png diff --git a/doc/development/ux_guide/img/color-grey.png b/doc/development/ux_guide/img/color-grey.png Binary files differnew file mode 100644 index 00000000000..58c474d5ce9 --- /dev/null +++ b/doc/development/ux_guide/img/color-grey.png diff --git a/doc/development/ux_guide/img/color-orange.png b/doc/development/ux_guide/img/color-orange.png Binary files differnew file mode 100644 index 00000000000..4c4b772d438 --- /dev/null +++ b/doc/development/ux_guide/img/color-orange.png diff --git a/doc/development/ux_guide/img/color-red.png b/doc/development/ux_guide/img/color-red.png Binary files differnew file mode 100644 index 00000000000..3440ad48f05 --- /dev/null +++ b/doc/development/ux_guide/img/color-red.png diff --git a/doc/development/ux_guide/img/components-alerts.png b/doc/development/ux_guide/img/components-alerts.png Binary files differnew file mode 100644 index 00000000000..66a43ac69e1 --- /dev/null +++ b/doc/development/ux_guide/img/components-alerts.png diff --git a/doc/development/ux_guide/img/components-anchorlinks.png b/doc/development/ux_guide/img/components-anchorlinks.png Binary files differnew file mode 100644 index 00000000000..7dd6a8a3876 --- /dev/null +++ b/doc/development/ux_guide/img/components-anchorlinks.png diff --git a/doc/development/ux_guide/img/components-contentblock.png b/doc/development/ux_guide/img/components-contentblock.png Binary files differnew file mode 100644 index 00000000000..58d87729701 --- /dev/null +++ b/doc/development/ux_guide/img/components-contentblock.png diff --git a/doc/development/ux_guide/img/components-counts.png b/doc/development/ux_guide/img/components-counts.png Binary files differnew file mode 100644 index 00000000000..19280e988a0 --- /dev/null +++ b/doc/development/ux_guide/img/components-counts.png diff --git a/doc/development/ux_guide/img/components-coverblock.png b/doc/development/ux_guide/img/components-coverblock.png Binary files differnew file mode 100644 index 00000000000..fb135f9648a --- /dev/null +++ b/doc/development/ux_guide/img/components-coverblock.png diff --git a/doc/development/ux_guide/img/components-dateexact.png b/doc/development/ux_guide/img/components-dateexact.png Binary files differnew file mode 100644 index 00000000000..686ca727293 --- /dev/null +++ b/doc/development/ux_guide/img/components-dateexact.png diff --git a/doc/development/ux_guide/img/components-daterelative.png b/doc/development/ux_guide/img/components-daterelative.png Binary files differnew file mode 100644 index 00000000000..4954dfb51b3 --- /dev/null +++ b/doc/development/ux_guide/img/components-daterelative.png diff --git a/doc/development/ux_guide/img/components-dropdown.png b/doc/development/ux_guide/img/components-dropdown.png Binary files differnew file mode 100644 index 00000000000..7f9a701c089 --- /dev/null +++ b/doc/development/ux_guide/img/components-dropdown.png diff --git a/doc/development/ux_guide/img/components-fileholder.png b/doc/development/ux_guide/img/components-fileholder.png Binary files differnew file mode 100644 index 00000000000..ec2911a1232 --- /dev/null +++ b/doc/development/ux_guide/img/components-fileholder.png diff --git a/doc/development/ux_guide/img/components-horizontalform.png b/doc/development/ux_guide/img/components-horizontalform.png Binary files differnew file mode 100644 index 00000000000..c57dceda43a --- /dev/null +++ b/doc/development/ux_guide/img/components-horizontalform.png diff --git a/doc/development/ux_guide/img/components-listinsidepanel.png b/doc/development/ux_guide/img/components-listinsidepanel.png Binary files differnew file mode 100644 index 00000000000..3a72d39bb5d --- /dev/null +++ b/doc/development/ux_guide/img/components-listinsidepanel.png diff --git a/doc/development/ux_guide/img/components-listwithavatar.png b/doc/development/ux_guide/img/components-listwithavatar.png Binary files differnew file mode 100644 index 00000000000..f6db575433c --- /dev/null +++ b/doc/development/ux_guide/img/components-listwithavatar.png diff --git a/doc/development/ux_guide/img/components-listwithhover.png b/doc/development/ux_guide/img/components-listwithhover.png Binary files differnew file mode 100644 index 00000000000..8521a8ad53e --- /dev/null +++ b/doc/development/ux_guide/img/components-listwithhover.png diff --git a/doc/development/ux_guide/img/components-panels.png b/doc/development/ux_guide/img/components-panels.png Binary files differnew file mode 100644 index 00000000000..c1391ca07e5 --- /dev/null +++ b/doc/development/ux_guide/img/components-panels.png diff --git a/doc/development/ux_guide/img/components-referencehover.png b/doc/development/ux_guide/img/components-referencehover.png Binary files differnew file mode 100644 index 00000000000..f80564dbb16 --- /dev/null +++ b/doc/development/ux_guide/img/components-referencehover.png diff --git a/doc/development/ux_guide/img/components-referenceissues.png b/doc/development/ux_guide/img/components-referenceissues.png Binary files differnew file mode 100644 index 00000000000..51fb2cf3e43 --- /dev/null +++ b/doc/development/ux_guide/img/components-referenceissues.png diff --git a/doc/development/ux_guide/img/components-referencelabels.png b/doc/development/ux_guide/img/components-referencelabels.png Binary files differnew file mode 100644 index 00000000000..aba450cc3ba --- /dev/null +++ b/doc/development/ux_guide/img/components-referencelabels.png diff --git a/doc/development/ux_guide/img/components-referencemilestone.png b/doc/development/ux_guide/img/components-referencemilestone.png Binary files differnew file mode 100644 index 00000000000..adf2555ccf8 --- /dev/null +++ b/doc/development/ux_guide/img/components-referencemilestone.png diff --git a/doc/development/ux_guide/img/components-referencemrs.png b/doc/development/ux_guide/img/components-referencemrs.png Binary files differnew file mode 100644 index 00000000000..6c3375f1ea1 --- /dev/null +++ b/doc/development/ux_guide/img/components-referencemrs.png diff --git a/doc/development/ux_guide/img/components-referencepeople.png b/doc/development/ux_guide/img/components-referencepeople.png Binary files differnew file mode 100644 index 00000000000..b8dd431e2e6 --- /dev/null +++ b/doc/development/ux_guide/img/components-referencepeople.png diff --git a/doc/development/ux_guide/img/components-rowcontentblock.png b/doc/development/ux_guide/img/components-rowcontentblock.png Binary files differnew file mode 100644 index 00000000000..c66a50f9564 --- /dev/null +++ b/doc/development/ux_guide/img/components-rowcontentblock.png diff --git a/doc/development/ux_guide/img/components-simplelist.png b/doc/development/ux_guide/img/components-simplelist.png Binary files differnew file mode 100644 index 00000000000..858e5064c25 --- /dev/null +++ b/doc/development/ux_guide/img/components-simplelist.png diff --git a/doc/development/ux_guide/img/components-table.png b/doc/development/ux_guide/img/components-table.png Binary files differnew file mode 100644 index 00000000000..cedc55758a9 --- /dev/null +++ b/doc/development/ux_guide/img/components-table.png diff --git a/doc/development/ux_guide/img/components-verticalform.png b/doc/development/ux_guide/img/components-verticalform.png Binary files differnew file mode 100644 index 00000000000..489ae6f862f --- /dev/null +++ b/doc/development/ux_guide/img/components-verticalform.png diff --git a/doc/development/ux_guide/img/copy-form-addissuebutton.png b/doc/development/ux_guide/img/copy-form-addissuebutton.png Binary files differnew file mode 100644 index 00000000000..8457f0ab2ab --- /dev/null +++ b/doc/development/ux_guide/img/copy-form-addissuebutton.png diff --git a/doc/development/ux_guide/img/copy-form-addissueform.png b/doc/development/ux_guide/img/copy-form-addissueform.png Binary files differnew file mode 100644 index 00000000000..89c6b4acdfb --- /dev/null +++ b/doc/development/ux_guide/img/copy-form-addissueform.png diff --git a/doc/development/ux_guide/img/copy-form-editissuebutton.png b/doc/development/ux_guide/img/copy-form-editissuebutton.png Binary files differnew file mode 100644 index 00000000000..04bcc2bf831 --- /dev/null +++ b/doc/development/ux_guide/img/copy-form-editissuebutton.png diff --git a/doc/development/ux_guide/img/copy-form-editissueform.png b/doc/development/ux_guide/img/copy-form-editissueform.png Binary files differnew file mode 100644 index 00000000000..126ef34ea7e --- /dev/null +++ b/doc/development/ux_guide/img/copy-form-editissueform.png diff --git a/doc/development/ux_guide/img/features-contextualnav.png b/doc/development/ux_guide/img/features-contextualnav.png Binary files differnew file mode 100644 index 00000000000..f8466f28627 --- /dev/null +++ b/doc/development/ux_guide/img/features-contextualnav.png diff --git a/doc/development/ux_guide/img/features-emptystates.png b/doc/development/ux_guide/img/features-emptystates.png Binary files differnew file mode 100644 index 00000000000..51835a7080b --- /dev/null +++ b/doc/development/ux_guide/img/features-emptystates.png diff --git a/doc/development/ux_guide/img/features-filters.png b/doc/development/ux_guide/img/features-filters.png Binary files differnew file mode 100644 index 00000000000..41db76db938 --- /dev/null +++ b/doc/development/ux_guide/img/features-filters.png diff --git a/doc/development/ux_guide/img/features-globalnav.png b/doc/development/ux_guide/img/features-globalnav.png Binary files differnew file mode 100644 index 00000000000..73294d1b524 --- /dev/null +++ b/doc/development/ux_guide/img/features-globalnav.png diff --git a/doc/development/ux_guide/img/icon-add.png b/doc/development/ux_guide/img/icon-add.png Binary files differnew file mode 100644 index 00000000000..0d4c1a7692a --- /dev/null +++ b/doc/development/ux_guide/img/icon-add.png diff --git a/doc/development/ux_guide/img/icon-close.png b/doc/development/ux_guide/img/icon-close.png Binary files differnew file mode 100644 index 00000000000..88d2b3b0c6d --- /dev/null +++ b/doc/development/ux_guide/img/icon-close.png diff --git a/doc/development/ux_guide/img/icon-edit.png b/doc/development/ux_guide/img/icon-edit.png Binary files differnew file mode 100644 index 00000000000..f73be7a10fb --- /dev/null +++ b/doc/development/ux_guide/img/icon-edit.png diff --git a/doc/development/ux_guide/img/icon-notification.png b/doc/development/ux_guide/img/icon-notification.png Binary files differnew file mode 100644 index 00000000000..4758632edd7 --- /dev/null +++ b/doc/development/ux_guide/img/icon-notification.png diff --git a/doc/development/ux_guide/img/icon-rss.png b/doc/development/ux_guide/img/icon-rss.png Binary files differnew file mode 100644 index 00000000000..c7ac9fb1349 --- /dev/null +++ b/doc/development/ux_guide/img/icon-rss.png diff --git a/doc/development/ux_guide/img/icon-subscribe.png b/doc/development/ux_guide/img/icon-subscribe.png Binary files differnew file mode 100644 index 00000000000..5cb277bfd5d --- /dev/null +++ b/doc/development/ux_guide/img/icon-subscribe.png diff --git a/doc/development/ux_guide/img/icon-trash.png b/doc/development/ux_guide/img/icon-trash.png Binary files differnew file mode 100644 index 00000000000..357289a6fff --- /dev/null +++ b/doc/development/ux_guide/img/icon-trash.png diff --git a/doc/development/ux_guide/img/monospacefont-sample.png b/doc/development/ux_guide/img/monospacefont-sample.png Binary files differnew file mode 100644 index 00000000000..1cd290b713c --- /dev/null +++ b/doc/development/ux_guide/img/monospacefont-sample.png diff --git a/doc/development/ux_guide/img/sourcesanspro-sample.png b/doc/development/ux_guide/img/sourcesanspro-sample.png Binary files differnew file mode 100644 index 00000000000..f7ecf0c7c66 --- /dev/null +++ b/doc/development/ux_guide/img/sourcesanspro-sample.png diff --git a/doc/development/ux_guide/img/surfaces-contentitemtitle.png b/doc/development/ux_guide/img/surfaces-contentitemtitle.png Binary files differnew file mode 100644 index 00000000000..3af0b56c8fb --- /dev/null +++ b/doc/development/ux_guide/img/surfaces-contentitemtitle.png diff --git a/doc/development/ux_guide/img/surfaces-header.png b/doc/development/ux_guide/img/surfaces-header.png Binary files differnew file mode 100644 index 00000000000..ba616388003 --- /dev/null +++ b/doc/development/ux_guide/img/surfaces-header.png diff --git a/doc/development/ux_guide/img/surfaces-systeminformationblock.png b/doc/development/ux_guide/img/surfaces-systeminformationblock.png Binary files differnew file mode 100644 index 00000000000..9f42f1d4dd0 --- /dev/null +++ b/doc/development/ux_guide/img/surfaces-systeminformationblock.png diff --git a/doc/development/ux_guide/img/surfaces-ux.png b/doc/development/ux_guide/img/surfaces-ux.png Binary files differnew file mode 100644 index 00000000000..53208727c64 --- /dev/null +++ b/doc/development/ux_guide/img/surfaces-ux.png diff --git a/doc/development/ux_guide/img/tooltip-placement.png b/doc/development/ux_guide/img/tooltip-placement.png Binary files differnew file mode 100644 index 00000000000..061f82e4df0 --- /dev/null +++ b/doc/development/ux_guide/img/tooltip-placement.png diff --git a/doc/development/ux_guide/img/tooltip-usage.png b/doc/development/ux_guide/img/tooltip-usage.png Binary files differnew file mode 100644 index 00000000000..40c4f051cd0 --- /dev/null +++ b/doc/development/ux_guide/img/tooltip-usage.png diff --git a/doc/development/ux_guide/index.md b/doc/development/ux_guide/index.md new file mode 100644 index 00000000000..8aed11ebac3 --- /dev/null +++ b/doc/development/ux_guide/index.md @@ -0,0 +1,58 @@ +# GitLab UX Guide + +The goal of this guide is to provide standards, principles and in-depth information to design beautiful and effective GitLab features. This will be a living document, and we welcome contributions, feedback and suggestions. + +## Design + +--- + +### [Principles](principles.md) +These guiding principles set a solid foundation for our design system, and should remain relatively stable over multiple releases. They should be referenced as new design patterns are created. + +--- + +### [Basics](basics.md) +The basic ingredients of our experience establish our personality and feel. This section includes details about typography, color, and motion. + +--- + +### [Components](components.md) +Components are the controls that make up the GitLab experience, including guidance around buttons, links, dropdowns, etc. + +--- + +### [Surfaces](surfaces.md) +The GitLab experience is broken apart into several surfaces. Each of these surfaces is designated for a specific scope or type of content. Examples include the header, global menu, side pane, etc. + +--- + +### [Copy](copy.md) +Conventions on text and messaging within labels, buttons, and other components. + +--- + +### [Features](features.md) +The previous building blocks are combined into complete features in the GitLab UX. Examples include our navigation, filters, search results, and empty states. + +--- + +## Research + +--- + +### [Users](users.md) +How we think about the variety of users of GitLab, from small to large teams, comparing opensource usage to enterprise, etc. + +--- + +## Other + +--- + +### [Tips for designers](tips.md) +Tips for exporting assets, and other guidance. + +--- + +### [Resources](resources.md) +Resources for GitLab UX diff --git a/doc/development/ux_guide/principles.md b/doc/development/ux_guide/principles.md new file mode 100644 index 00000000000..1a297cba2cc --- /dev/null +++ b/doc/development/ux_guide/principles.md @@ -0,0 +1,17 @@ +# Principles + +These are the guiding principles that we should strive for to establish a solid foundation for the GitLab experience. + +## Professional and productive +GitLab is a tool to support what people do, day in, day out. We need to respect the importance of their work, and avoid gimicky details. + +## Minimal and efficient +While work can get complicated, GitLab is about bringing a sharp focus, helping our customers know what matters now. + +## Immediately recognizable +When you look at any screen, you should know immediately that it is GitLab. Our personality is strong and consistent across product and marketing experiences. + +## Human and quirky +We need to build empathy with our users, understanding their state of mind, and connect with them at a human level. Quirkiness is part of our DNA, and we should embrace it in the right moments and contexts. + +> TODO: Ensure these principles align well with the goals of the Marketing team diff --git a/doc/development/ux_guide/resources.md b/doc/development/ux_guide/resources.md new file mode 100644 index 00000000000..2f760c94414 --- /dev/null +++ b/doc/development/ux_guide/resources.md @@ -0,0 +1,13 @@ +# Resources + +## GitLab UI development kit + +We created a page inside GitLab where you can check commonly used html and css elements. + +When you run GitLab instance locally - just visit http://localhost:3000/help/ui page to see UI examples +you can use during GitLab development. + +## Design repository + +All design files are stored in the [gitlab-design](https://gitlab.com/gitlab-org/gitlab-design) +repository and maintained by GitLab UX designers.
\ No newline at end of file diff --git a/doc/development/ux_guide/surfaces.md b/doc/development/ux_guide/surfaces.md new file mode 100644 index 00000000000..881d6aa4cd6 --- /dev/null +++ b/doc/development/ux_guide/surfaces.md @@ -0,0 +1,47 @@ +# Surfaces + +## Contents +* [Header](#header) +* [Global menu](#global-menu) +* [Side pane](#side-pane) +* [Content area](#content-area) + +--- + + + +## Global menu + +This menu is to navigate to pages that contain content global to GitLab. + +--- + +## Header + +The header contains 3 main elements: Project switching and searching, user account avatar and settings, and a contextual menu that changes based on the current page. + + + +--- + +## Side pane + +The side pane holds supporting information and meta data for the information in the content area. + +--- + +## Content area + +The main content of the page. The content area can include other surfaces. + +### Item title bar + +The item title bar contains the top level information to identify the item, such as the name, id and status. + + + +### Item system information + +The system information block contains relevant system controlled information. + + diff --git a/doc/development/ux_guide/tips.md b/doc/development/ux_guide/tips.md new file mode 100644 index 00000000000..8348de4f8a2 --- /dev/null +++ b/doc/development/ux_guide/tips.md @@ -0,0 +1,44 @@ +# Tips + +## Contents +* [SVGs](#svgs) + +--- + +## SVGs + +When exporting SVGs, be sure to follow the following guidelines: + +1. Convert all strokes to outlines. +2. Use pathfinder tools to combine overlapping paths and create compound paths. +3. SVGs that are limited to one color should be exported without a fill color so the color can be set using CSS. +4. Ensure that exported SVGs have been run through an [SVG cleaner](https://github.com/RazrFalcon/SVGCleaner) to remove unused elements and attributes. + +You can open your svg in a text editor to ensure that it is clean. +Incorrect files will look like this: + +```xml +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="16px" height="17px" viewBox="0 0 16 17" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- Generator: Sketch 3.7.2 (28276) - http://www.bohemiancoding.com/sketch --> + <title>Group</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> + <g id="Group" fill="#7E7C7C"> + <path d="M15.1111,1 L0.8891,1 C0.3981,1 0.0001,1.446 0.0001,1.996 L0.0001,15.945 C0.0001,16.495 0.3981,16.941 0.8891,16.941 L15.1111,16.941 C15.6021,16.941 16.0001,16.495 16.0001,15.945 L16.0001,1.996 C16.0001,1.446 15.6021,1 15.1111,1 L15.1111,1 L15.1111,1 Z M14.0001,6.0002 L14.0001,14.949 L2.0001,14.949 L2.0001,6.0002 L14.0001,6.0002 Z M14.0001,4.0002 L14.0001,2.993 L2.0001,2.993 L2.0001,4.0002 L14.0001,4.0002 Z" id="Combined-Shape"></path> + <polygon id="Fill-11" points="3 2.0002 5 2.0002 5 0.0002 3 0.0002"></polygon> + <polygon id="Fill-16" points="11 2.0002 13 2.0002 13 0.0002 11 0.0002"></polygon> + <path d="M5.37709616,11.5511984 L6.92309616,12.7821984 C7.35112915,13.123019 7.97359761,13.0565604 8.32002627,12.6330535 L10.7740263,9.63305349 C11.1237073,9.20557058 11.0606364,8.57555475 10.6331535,8.22587373 C10.2056706,7.87619272 9.57565475,7.93926361 9.22597373,8.36674651 L6.77197373,11.3667465 L8.16890384,11.2176016 L6.62290384,9.98660159 C6.19085236,9.6425813 5.56172188,9.71394467 5.21770159,10.1459962 C4.8736813,10.5780476 4.94504467,11.2071781 5.37709616,11.5511984 L5.37709616,11.5511984 Z" id="Stroke-21"></path> + </g> + </g> +</svg> +``` + +Correct file will look like this: + +```xml +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 17" enable-background="new 0 0 16 17"><path d="m15.1 1h-2.1v-1h-2v1h-6v-1h-2v1h-2.1c-.5 0-.9.5-.9 1v14c0 .6.4 1 .9 1h14.2c.5 0 .9-.4.9-1v-14c0-.5-.4-1-.9-1m-1.1 14h-12v-9h12v9m0-11h-12v-1h12v1"/><path d="m5.4 11.6l1.5 1.2c.4.3 1.1.3 1.4-.1l2.5-3c.3-.4.3-1.1-.1-1.4-.5-.4-1.1-.3-1.5.1l-1.8 2.2-.8-.6c-.4-.3-1.1-.3-1.4.2-.3.4-.3 1 .2 1.4"/></svg> +``` + +> TODO: Checkout [https://github.com/svg/svgo](https://github.com/svg/svgo) diff --git a/doc/development/ux_guide/users.md b/doc/development/ux_guide/users.md new file mode 100644 index 00000000000..717a902c424 --- /dev/null +++ b/doc/development/ux_guide/users.md @@ -0,0 +1,16 @@ +# Users + +> TODO: Create personas. Understand the similarities and differences across the below spectrums. + +## Users by organization + +- Enterprise +- Medium company +- Small company +- Open source communities + +## Users by role + +- Admin +- Manager +- Developer diff --git a/doc/development/what_requires_downtime.md b/doc/development/what_requires_downtime.md index 2574c2c0472..bbcd26477f3 100644 --- a/doc/development/what_requires_downtime.md +++ b/doc/development/what_requires_downtime.md @@ -66,6 +66,12 @@ producing errors whenever it tries to use the `dummy` column. As a result of the above downtime _is_ required when removing a column, even when using PostgreSQL. +## Renaming Columns + +Renaming columns requires downtime as running GitLab instances will continue +using the old column name until a new version is deployed. This can result +in the instance producing errors, which in turn can impact the user experience. + ## Changing Column Constraints Generally changing column constraints requires checking all rows in the table to |