Commit 634e9272 authored by Regis's avatar Regis

Merge branch 'master' into auto-pipelines-vue

parents 4d45ef5d a3fd8521
...@@ -213,11 +213,24 @@ rake downtime_check: *exec ...@@ -213,11 +213,24 @@ rake downtime_check: *exec
rake ee_compat_check: rake ee_compat_check:
<<: *exec <<: *exec
only: only:
- branches - branches@gitlab-org/gitlab-ce
- branches@gitlab/gitlabhq
except: except:
- master - master
- tags - tags
- /^[\d-]+-stable(-ee)?$/
allow_failure: yes allow_failure: yes
cache:
key: "ruby231-ee_compat_check_repo"
paths:
- ee_compat_check/repo/
- vendor/ruby
artifacts:
name: "${CI_BUILD_NAME}_${CI_BUILD_REF_NAME}_${CI_BUILD_REF}"
when: on_failure
expire_in: 10d
paths:
- ee_compat_check/patches/*.patch
rake db:migrate:reset: rake db:migrate:reset:
stage: test stage: test
......
...@@ -4,6 +4,37 @@ entry. ...@@ -4,6 +4,37 @@ entry.
## 8.14.0 (2016-11-22) ## 8.14.0 (2016-11-22)
- Use separate email-token for incoming email and revert back the inactive feature. !5914
- Replace jQuery.timeago with timeago.js. !6274 (ClemMakesApps)
- Add CI notifications. Who triggered a pipeline would receive an email after the pipeline is succeeded or failed. Users could also update notification settings accordingly. !6342
- Finer-grained Git gargage collection. !6588
- Introduce better credential and error checking to `rake gitlab:ldap:check`. !6601
- Process commits using a dedicated Sidekiq worker. !6802
- Fix showing pipeline status for a given commit from correct branch. !7034
- Add query param to filter users by external & blocked type. !7109 (Yatish Mehta)
- Issues atom feed url reflect filters on dashboard. !7114 (Lucas Deschamps)
- Add setting to only allow merge requests to be merged when all discussions are resolved. !7125 (Rodolfo Arruda)
- Remove an extra leading space from diff paste data. !7133 (Hiroyuki Sato)
- Fix 404 on network page when entering non-existent git revision. !7172 (Hiroyuki Sato)
- Rewrite git blame spinach feature tests to rspec feature tests. !7197 (Lisanne Fellinger)
- Only skip group when it's actually a group in the "Share with group" select. !7262
- Introduce round-robin project creation to spread load over multiple shards. !7266
- Ensure merge request's "remove branch" accessors return booleans. !7267
- Expose label IDs in API. !7275 (Rares Sfirlogea)
- Fix invalid filename validation on eslint. !7281
- API: Ability to retrieve version information. !7286 (Robert Schilling)
- Set default Sidekiq retries to 3. !7294
- Return 400 when creating a system hook fails. !7350 (Robert Schilling)
- Use the Gitlab Workhorse HTTP header in the admin dashboard. (Chris Wright)
- Add an index for project_id in project_import_data to improve performance.
- Fix broken link to observatory cli on Frontend Dev Guide. (Sam Rose)
- Faster search inside Project.
- Clicking "force remove source branch" label now toggles the checkbox again.
- Allow to test JIRA service settings without having a repository.
- Fix: Guest sees some repository details and gets 404.
- Bump omniauth-gitlab to 1.0.2 to fix incompatibility with omniauth-oauth2.
- Fix: Todos Filter Shows All Users.
- Fix broken commits search.
- Show correct environment log in admin/logs (@duk3luk3 !7191) - Show correct environment log in admin/logs (@duk3luk3 !7191)
- Fix Milestone dropdown not stay selected for `Upcoming` and `No Milestone` option !7117 - Fix Milestone dropdown not stay selected for `Upcoming` and `No Milestone` option !7117
- Diff collapse won't shift when collapsing. - Diff collapse won't shift when collapsing.
...@@ -77,9 +108,6 @@ entry. ...@@ -77,9 +108,6 @@ entry.
## 8.13.5 (2016-11-08) ## 8.13.5 (2016-11-08)
- Restore unauthenticated access to public container registries - Restore unauthenticated access to public container registries
## 8.13.4 (2016-11-07)
- Fix showing pipeline status for a given commit from correct branch. !7034 - Fix showing pipeline status for a given commit from correct branch. !7034
- Only skip group when it's actually a group in the "Share with group" select. !7262 - Only skip group when it's actually a group in the "Share with group" select. !7262
- Introduce round-robin project creation to spread load over multiple shards. !7266 - Introduce round-robin project creation to spread load over multiple shards. !7266
...@@ -96,13 +124,15 @@ entry. ...@@ -96,13 +124,15 @@ entry.
- Fix builds tab visibility. !7178 - Fix builds tab visibility. !7178
- Fix project features default values. !7181 - Fix project features default values. !7181
## 8.13.4
- Pulled due to packaging error.
## 8.13.3 (2016-11-02) ## 8.13.3 (2016-11-02)
- Removes any symlinks before importing a project export file. CVE-2016-9086 - Removes any symlinks before importing a project export file. CVE-2016-9086
- Fixed Import/Export foreign key issue to do with project members. - Fixed Import/Export foreign key issue to do with project members.
- Fix relative links in Markdown wiki when displayed in "Project" tab !7218 - Fix relative links in Markdown wiki when displayed in "Project" tab !7218
- Reduce the overhead to calculate number of open/closed issues and merge requests within the group or project
- Fix project features default values
- Changed build dropdown list length to be 6,5 builds long in the pipeline graph - Changed build dropdown list length to be 6,5 builds long in the pipeline graph
## 8.13.2 (2016-10-31) ## 8.13.2 (2016-10-31)
......
...@@ -62,6 +62,8 @@ ...@@ -62,6 +62,8 @@
.ci-status-link { .ci-status-link {
display: inline-block; display: inline-block;
position: relative;
top: 1px;
} }
.btn-clipboard, .btn-clipboard,
......
...@@ -267,20 +267,6 @@ ...@@ -267,20 +267,6 @@
} }
} }
.issuable-header-btn {
background: $gray-normal;
border: 1px solid $border-gray-normal;
&:hover {
background: $gray-dark;
border: 1px solid $border-gray-dark;
}
&.btn-primary {
@extend .btn-primary;
}
}
a { a {
&:hover { &:hover {
color: $md-link-color; color: $md-link-color;
......
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
&.right { &.right {
float: right; float: right;
padding-right: 0;
a { a {
color: $gl-gray; color: $gl-gray;
......
...@@ -35,7 +35,9 @@ class Projects::PipelinesController < Projects::ApplicationController ...@@ -35,7 +35,9 @@ class Projects::PipelinesController < Projects::ApplicationController
end end
def create def create
@pipeline = Ci::CreatePipelineService.new(project, current_user, create_params).execute(ignore_skip_ci: true, save_on_errors: false) @pipeline = Ci::CreatePipelineService
.new(project, current_user, create_params)
.execute(ignore_skip_ci: true, save_on_errors: false)
unless @pipeline.persisted? unless @pipeline.persisted?
render 'new' render 'new'
return return
......
...@@ -84,15 +84,17 @@ class Repository ...@@ -84,15 +84,17 @@ class Repository
def commit(ref = 'HEAD') def commit(ref = 'HEAD')
return nil unless exists? return nil unless exists?
commit = commit =
if ref.is_a?(Gitlab::Git::Commit) if ref.is_a?(Gitlab::Git::Commit)
ref ref
else else
Gitlab::Git::Commit.find(raw_repository, ref) Gitlab::Git::Commit.find(raw_repository, ref)
end end
commit = ::Commit.new(commit, @project) if commit commit = ::Commit.new(commit, @project) if commit
commit commit
rescue Rugged::OdbError rescue Rugged::OdbError, Rugged::TreeError
nil nil
end end
...@@ -232,6 +234,8 @@ class Repository ...@@ -232,6 +234,8 @@ class Repository
def ref_exists?(ref) def ref_exists?(ref)
rugged.references.exist?(ref) rugged.references.exist?(ref)
rescue Rugged::ReferenceError
false
end end
def update_ref!(name, newrev, oldrev) def update_ref!(name, newrev, oldrev)
...@@ -270,11 +274,7 @@ class Repository ...@@ -270,11 +274,7 @@ class Repository
end end
def kept_around?(sha) def kept_around?(sha)
begin ref_exists?(keep_around_ref_name(sha))
ref_exists?(keep_around_ref_name(sha))
rescue Rugged::ReferenceError
false
end
end end
def tag_names def tag_names
......
...@@ -8,3 +8,6 @@ ...@@ -8,3 +8,6 @@
- if signin_enabled? - if signin_enabled?
%li %li
= link_to 'Standard', '#ldap-standard', 'data-toggle' => 'tab' = link_to 'Standard', '#ldap-standard', 'data-toggle' => 'tab'
- if signin_enabled? && signup_enabled?
%li
= link_to 'Register', '#register-pane', 'data-toggle' => 'tab'
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
- if can_admin_group - if can_admin_group
= nav_link(path: 'groups#projects') do = nav_link(path: 'groups#projects') do
= link_to 'Projects', projects_group_path(@group), title: 'Projects' = link_to 'Projects', projects_group_path(@group), title: 'Projects'
- if can_edit || can_leave - if (can_edit || can_leave) && can_admin_group
%li.divider %li.divider
- if can_edit - if can_edit
%li %li
......
.pipeline-graph-container .pipeline-graph-container
.row-content-block.build-content.middle-block.pipeline-actions .row-content-block.build-content.middle-block.pipeline-actions
.pull-right .pull-right
.btn.btn-grouped.btn-white.toggle-pipeline-btn %button.btn.btn-grouped.btn-white.toggle-pipeline-btn
%span.toggle-btn-text Hide %span.toggle-btn-text Hide
%span pipeline graph %span pipeline graph
%span.caret %span.caret
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
- note_count = notes.user.count - note_count = notes.user.count
- cache_key = [project.path_with_namespace, commit.id, current_application_settings, note_count] - cache_key = [project.path_with_namespace, commit.id, current_application_settings, note_count]
- cache_key.push(commit.status) if commit.status - cache_key.push(commit.status(ref)) if commit.status(ref)
= cache(cache_key, expires_in: 1.day) do = cache(cache_key, expires_in: 1.day) do
%li.commit.js-toggle-container{ id: "commit-#{commit.short_id}" } %li.commit.js-toggle-container{ id: "commit-#{commit.short_id}" }
......
- page_title "Import in progress" - page_title @project.forked? ? "Forking in progress" : "Import in progress"
.save-project-loader .save-project-loader
.center .center
%h2 %h2
......
<svg width="20" height="20" class="ci-status-icon-skipped" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>Group Copy 31</title><g fill="#5C5C5C" fill-rule="evenodd"><path d="M10 17.857c4.286 0 7.857-3.571 7.857-7.857S14.286 2.143 10 2.143 2.143 5.714 2.143 10 5.714 17.857 10 17.857M10 0c5.571 0 10 4.429 10 10s-4.429 10-10 10S0 15.571 0 10 4.429 0 10 0"/><path d="M10.986 11l-1.293 1.293a1 1 0 0 0 1.414 1.414l2.644-2.644a1.505 1.505 0 0 0 0-2.126l-2.644-2.644a1 1 0 0 0-1.414 1.414L10.986 9H6.4a1 1 0 0 0 0 2h4.586z"/></g></svg> <svg width="14" height="14" class="ci-status-icon-skipped" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>Group Copy 31</title><g fill="#5C5C5C" fill-rule="evenodd"><path d="M10 17.857c4.286 0 7.857-3.571 7.857-7.857S14.286 2.143 10 2.143 2.143 5.714 2.143 10 5.714 17.857 10 17.857M10 0c5.571 0 10 4.429 10 10s-4.429 10-10 10S0 15.571 0 10 4.429 0 10 0"/><path d="M10.986 11l-1.293 1.293a1 1 0 0 0 1.414 1.414l2.644-2.644a1.505 1.505 0 0 0 0-2.126l-2.644-2.644a1 1 0 0 0-1.414 1.414L10.986 9H6.4a1 1 0 0 0 0 2h4.586z"/></g></svg>
---
title: Add setting to only allow merge requests to be merged when all discussions are resolved
merge_request: 7125
author: Rodolfo Arruda
---
title: Use the Gitlab Workhorse HTTP header in the admin dashboard
merge_request:
author: Chris Wright
---
title: 'Fix: Todos Filter Shows All Users'
merge_request:
author:
---
title: Issues atom feed url reflect filters on dashboard
merge_request: 7114
author: Lucas Deschamps
---
title: Rewrite git blame spinach feature tests to rspec feature tests
merge_request: 7197
author: Lisanne Fellinger
---
title: Only skip group when it's actually a group in the "Share with group" select
merge_request: 7262
author:
---
title: Fix no "Register" tab if ldap auth is enabled (#24038)
merge_request: 7274
author: Luc Didry
---
title: "[Fix] Extra divider issue in dropdown"
merge_request: 7398
author:
---
title: 'Fix: Guest sees some repository details and gets 404'
merge_request:
author:
---
title: Introduce round-robin project creation to spread load over multiple shards
merge_request: 7266
author:
---
title: Ensure merge request's "remove branch" accessors return booleans
merge_request: 7267
author:
---
title: Fix broken commits search
merge_request:
author:
---
title: Removed gray button styling from todo buttons in sidebars
merge_request: 7387
author:
---
title: Remove additional padding on right-aligned items in MR widget.
merge_request: 7411
author: Didem Acet
---
title: Expose label IDs in API
merge_request: 7275
author: Rares Sfirlogea
---
title: Add an index for project_id in project_import_data to improve performance
merge_request:
author:
---
title: API: Ability to retrieve version information
merge_request: 7286
author: Robert Schilling
---
title: Return 400 when creating a system hook fails
merge_request: 7350
author: Robert Schilling
---
title: Fix broken link to observatory cli on Frontend Dev Guide
merge_request:
author: Sam Rose
---
title: Faster search inside Project
merge_request:
author:
---
title: Add api endpoint `/groups/owned`
merge_request: 7103
author: Borja Aparicio
---
title: Fix 404 on network page when entering non-existent git revision
merge_request: 7172
author: Hiroyuki Sato
---
title: Fix cache for commit status in commits list to respect branches
merge_request: 7372
author:
---
title: Fix error when using invalid branch name when creating a new pipeline
merge_request: 7324
author:
---
title: Fix invalid filename validation on eslint
merge_request: 7281
author:
---
title: Clicking "force remove source branch" label now toggles the checkbox again
merge_request:
author:
---
title: Use 'Forking in progress' title when appropriate
merge_request: 7394
author: Philip Karpiak
---
title: Finer-grained Git gargage collection
merge_request: 6588
author:
---
title: Allow to test JIRA service settings without having a repository
merge_request:
author:
---
title: Introduce better credential and error checking to `rake gitlab:ldap:check`
merge_request: 6601
author:
---
title: Add CI notifications. Who triggered a pipeline would receive an email after
the pipeline is succeeded or failed. Users could also update notification settings
accordingly
merge_request: 6342
author:
---
title: Process commits using a dedicated Sidekiq worker
merge_request: 6802
author:
---
title: Remove an extra leading space from diff paste data
merge_request: 7133
author: Hiroyuki Sato
---
title: Bump omniauth-gitlab to 1.0.2 to fix incompatibility with omniauth-oauth2
merge_request:
author:
---
title: Fix showing pipeline status for a given commit from correct branch
merge_request: 7034
author:
---
title: Set default Sidekiq retries to 3
merge_request: 7294
author:
---
title: Replace jQuery.timeago with timeago.js
merge_request: 6274
author: ClemMakesApps
---
title: Use separate email-token for incoming email and revert back the inactive feature
merge_request: 5914
author:
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
- [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN. - [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN.
- [University](university/README.md) Learn Git and GitLab through videos and courses. - [University](university/README.md) Learn Git and GitLab through videos and courses.
- [Git Attributes](user/project/git_attributes.md) Managing Git attributes using a `.gitattributes` file. - [Git Attributes](user/project/git_attributes.md) Managing Git attributes using a `.gitattributes` file.
- [Git cheatsheet](https://gitlab.com/gitlab-com/marketing/raw/master/design/print/git-cheatsheet/print-pdf/git-cheatsheet.pdf) Download a PDF describing the most used Git operations.
## Administrator documentation ## Administrator documentation
......
...@@ -26,6 +26,15 @@ GET /groups ...@@ -26,6 +26,15 @@ GET /groups
You can search for groups by name or path, see below. You can search for groups by name or path, see below.
=======
## List owned groups
Get a list of groups which are owned by the authenticated user.
```
GET /groups/owned
```
## List a group's projects ## List a group's projects
Get a list of projects in this group. Get a list of projects in this group.
......
...@@ -33,6 +33,18 @@ GET /users ...@@ -33,6 +33,18 @@ GET /users
] ]
``` ```
In addition, you can filter users based on states eg. `blocked`, `active`
This works only to filter users who are `blocked` or `active`.
It does not support `active=false` or `blocked=false`.
```
GET /users?active=true
```
```
GET /users?blocked=true
```
### For admins ### For admins
``` ```
...@@ -120,6 +132,8 @@ For example: ...@@ -120,6 +132,8 @@ For example:
GET /users?username=jack_smith GET /users?username=jack_smith
``` ```
You can search for users who are external with: `/users?external=true`
## Single user ## Single user
Get a single user. Get a single user.
......
...@@ -24,7 +24,7 @@ namespace you can use the `configure` class method. This method simply yields ...@@ -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 the supplied block while passing `Gitlab::Metrics::Instrumentation` as its
argument. An example: argument. An example:
``` ```ruby
Gitlab::Metrics::Instrumentation.configure do |conf| Gitlab::Metrics::Instrumentation.configure do |conf|
conf.instrument_method(Foo, :bar) conf.instrument_method(Foo, :bar)
conf.instrument_method(Foo, :baz) conf.instrument_method(Foo, :baz)
...@@ -41,7 +41,7 @@ Method instrumentation should be added in the initializer ...@@ -41,7 +41,7 @@ Method instrumentation should be added in the initializer
Instrumenting a single method: Instrumenting a single method:
``` ```ruby
Gitlab::Metrics::Instrumentation.configure do |conf| Gitlab::Metrics::Instrumentation.configure do |conf|
conf.instrument_method(User, :find_by) conf.instrument_method(User, :find_by)
end end
...@@ -49,7 +49,7 @@ end ...@@ -49,7 +49,7 @@ end
Instrumenting an entire class hierarchy: Instrumenting an entire class hierarchy:
``` ```ruby
Gitlab::Metrics::Instrumentation.configure do |conf| Gitlab::Metrics::Instrumentation.configure do |conf|
conf.instrument_class_hierarchy(ActiveRecord::Base) conf.instrument_class_hierarchy(ActiveRecord::Base)
end end
...@@ -57,7 +57,7 @@ end ...@@ -57,7 +57,7 @@ end
Instrumenting all public class methods: Instrumenting all public class methods:
``` ```ruby
Gitlab::Metrics::Instrumentation.configure do |conf| Gitlab::Metrics::Instrumentation.configure do |conf|
conf.instrument_methods(User) conf.instrument_methods(User)
end end
...@@ -68,7 +68,7 @@ end ...@@ -68,7 +68,7 @@ end
The easiest way to check if a method has been instrumented is to check its The easiest way to check if a method has been instrumented is to check its
source location. For example: source location. For example:
``` ```ruby
method = Rugged::TagCollection.instance_method(:[]) method = Rugged::TagCollection.instance_method(:[])
method.source_location method.source_location
......
...@@ -60,7 +60,7 @@ migration was tested. ...@@ -60,7 +60,7 @@ migration was tested.
If you need to remove index, please add a condition like in following example: 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) 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 ...@@ -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 the method `disable_ddl_transaction!` in the body of your migration class like
so: so:
``` ```ruby
class MyMigration < ActiveRecord::Migration class MyMigration < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
disable_ddl_transaction! disable_ddl_transaction!
...@@ -96,7 +96,7 @@ the `up` and `down` methods in your migration class. ...@@ -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 For example, to add the column `foo` to the `projects` table with a default
value of `10` you'd write the following: value of `10` you'd write the following:
``` ```ruby
class MyMigration < ActiveRecord::Migration class MyMigration < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
disable_ddl_transaction! disable_ddl_transaction!
...@@ -125,7 +125,7 @@ set the limit to 8-bytes. This will allow the column to hold a value up to ...@@ -125,7 +125,7 @@ set the limit to 8-bytes. This will allow the column to hold a value up to
Rails migration example: Rails migration example:
``` ```ruby
add_column_with_default(:projects, :foo, :integer, default: 10, limit: 8) add_column_with_default(:projects, :foo, :integer, default: 10, limit: 8)
# or # or
...@@ -145,7 +145,7 @@ Please prefer Arel and plain SQL over usual ActiveRecord syntax. In case of usin ...@@ -145,7 +145,7 @@ Please prefer Arel and plain SQL over usual ActiveRecord syntax. In case of usin
Example with Arel: Example with Arel:
``` ```ruby
users = Arel::Table.new(:users) users = Arel::Table.new(:users)
users.group(users[:user_id]).having(users[:id].count.gt(5)) users.group(users[:user_id]).having(users[:id].count.gt(5))
...@@ -154,7 +154,7 @@ users.group(users[:user_id]).having(users[:id].count.gt(5)) ...@@ -154,7 +154,7 @@ users.group(users[:user_id]).having(users[:id].count.gt(5))
Example with plain SQL and `quote_string` helper: 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| 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"]) tag_name = quote_string(tag["name"])
duplicate_ids = select_all("SELECT id FROM tags WHERE name = '#{tag_name}'").map{|tag| tag["id"]} duplicate_ids = select_all("SELECT id FROM tags WHERE name = '#{tag_name}'").map{|tag| tag["id"]}
......
...@@ -129,7 +129,7 @@ Various methods for opening and reading files in Ruby can be used to read the ...@@ -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 standard output of a process instead of a file. The following two commands do
roughly the same: roughly the same:
``` ```ruby
`touch /tmp/pawned-by-backticks` `touch /tmp/pawned-by-backticks`
File.read('|touch /tmp/pawned-by-file-read') 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 ...@@ -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 instance, the following is sufficient to protect against accidentally starting
a shell command with `|`: a shell command with `|`:
``` ```ruby
# we assume repo_path is not controlled by the attacker (user) # we assume repo_path is not controlled by the attacker (user)
path = File.join(repo_path, user_input) path = File.join(repo_path, user_input)
# path cannot start with '|' now. # path cannot start with '|' now.
...@@ -160,7 +160,7 @@ Path traversal is a security where the program (GitLab) tries to restrict user ...@@ -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 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. 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 # Suppose the user gave us a path and they are trying to trick us
user_input = '../other-repo.git/other-file' user_input = '../other-repo.git/other-file'
...@@ -177,7 +177,7 @@ File.open(full_path) do # Oops! ...@@ -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 A good way to protect against this is to compare the full path with its
'absolute path' according to Ruby's `File.absolute_path`. 'absolute path' according to Ruby's `File.absolute_path`.
``` ```ruby
full_path = File.join(repo_path, user_input) full_path = File.join(repo_path, user_input)
if full_path != File.absolute_path(full_path) if full_path != File.absolute_path(full_path)
raise "Invalid path: #{full_path.inspect}" raise "Invalid path: #{full_path.inspect}"
......
...@@ -212,5 +212,8 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project ...@@ -212,5 +212,8 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
1. [Support Path](support/README.md) 1. [Support Path](support/README.md)
1. [Sales Path (redirect to sales handbook)](https://about.gitlab.com/handbook/sales-onboarding/) 1. [Sales Path (redirect to sales handbook)](https://about.gitlab.com/handbook/sales-onboarding/)
1. [User Training](training/user_training.md)
1. [GitLab Flow Training](training/gitlab_flow.md)
1. [Training Topics](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/university/training/topics/)
1. [GitLab architecture for noobs](https://dev.gitlab.org/gitlab/gitlabhq/blob/master/doc/development/architecture.md) 1. [GitLab architecture for noobs](https://dev.gitlab.org/gitlab/gitlabhq/blob/master/doc/development/architecture.md)
1. [Client Assessment of GitLab versus GitHub](https://docs.google.com/a/gitlab.com/spreadsheets/d/18cRF9Y5I6I7Z_ab6qhBEW55YpEMyU4PitZYjomVHM-M/edit?usp=sharing) 1. [Client Assessment of GitLab versus GitHub](https://docs.google.com/a/gitlab.com/spreadsheets/d/18cRF9Y5I6I7Z_ab6qhBEW55YpEMyU4PitZYjomVHM-M/edit?usp=sharing)
# GitLab Flow
- A simplified branching strategy
- All features and fixes first go to master
- Allows for 'production' or 'stable' branches
- Bug fixes/hot fix patches are cherry-picked from master
---
# Feature branches
- Create a feature/bugfix branch to do all work
- Use merge requests to merge to master
![inline](gitlab_flow/feature_branches.png)
---
# Production branch
- One, long-running production release branch
as opposed to individual stable branches
- Consider creating a tag for each version that gets deployed
---
# Production branch
![inline](gitlab_flow/production_branch.png)
---
# Release branch
- Useful if you release software to customers
- When preparing a new release, create stable branch
from master
- Consider creating a tag for each version
- Cherry-pick critical bug fixes to stable branch for patch release
- Never commit bug fixes directly to stable branch
---
# Release branch
![inline](gitlab_flow/release_branches.png)
---
# More details
Blog post on 'GitLab Flow' at
[http://doc.gitlab.com/ee/workflow/gitlab_flow.html](http://doc.gitlab.com/ee/workflow/gitlab_flow.html)
# GitLab Training Material
All GitLab training material is stored in markdown format. Slides are
generated using [Deskset](http://www.decksetapp.com/).
All training material is open to public contribution.
## Additional Resources
1. GitLab Documentation [http://docs.gitlab.com](http://docs.gitlab.com/)
2. GUI Clients [http://git-scm.com/downloads/guis](http://git-scm.com/downloads/guis)
3. Pro git book [http://git-scm.com/book](http://git-scm.com/book)
4. Platzi Course [https://courses.platzi.com/courses/git-gitlab/](https://courses.platzi.com/courses/git-gitlab/)
5. Code School tutorial [http://try.github.io/](http://try.github.io/)
6. Contact Us - [subscribers@gitlab.com](subscribers@gitlab.com)
# Agile and Git
----------
## Agile
Lean software development methods focused on collaboration and interaction
with fast and smaller deployment cycles.
----------
## Where Git comes in
Git is an excellent tool for an Agile team considering that it allows
decentralized and simultaneous development.
----------
### Branching And Workflows
Branching in an Agile environment usually happens around user stories with one
or more developers working on it.
If more than one developer then another branch for each developer is also used
with his/her initials, and US id.
After its tested merge into master and remove the branch.
----------
## What about GitLab
Tools like GitLab enhance collaboration by adding dialog around code mainly
through issues and merge requests.
# Bisect
----------
## Bisect
- Find a commit that introduced a bug
- Works through a process of elimination
- Specify a known good and bad revision to begin
----------
## Bisect
1. Start the bisect process
2. Enter the bad revision (usually latest commit)
3. Enter a known good revision (commit/branch)
4. Run code to see if bug still exists
5. Tell bisect the result
6. Repeat the previous 2 items until you find the offending commit
----------
## Setup
```
mkdir bisect-ex
cd bisect-ex
touch index.html
git add -A
git commit -m "starting out"
vi index.html
# Add all good
git add -A
git commit -m "second commit"
vi index.html
# Add all good 2
git add -A
git commit -m "third commit"
vi index.html
```
----------
```
# Add all good 3
git add -A
git commit -m "fourth commit"
vi index.html
# This looks bad
git add -A
git commit -m "fifth commit"
vi index.html
# Really bad
git add -A
git commit -m "sixth commit"
vi index.html
# again just bad
git add -A
git commit -m "seventh commit"
```
----------
## Commands
```
git bisect start
# Test your code
git bisect bad
git bisect next
# Say yes to the warning
# Test
git bisect good
# Test
git bisect bad
# Test
git bisect good
# done
git bisect reset
```
# Cherry Pick
----------
## Cherry Pick
- Given an existing commit on one branch, apply the change to another branch
- Useful for backporting bug fixes to previous release branches
- Make the commit on the master branch and pick in to stable
----------
## Cherry Pick
1. Check out a new 'stable' branch from 'master'
1. Change back to 'master'
1. Edit '`cherry_pick.rb`' and commit the changes.
1. Check commit log to get the commit SHA
1. Check out the 'stable' branch
1. Cherry pick the commit using the SHA obtained earlier
----------
## Commands
```bash
git checkout master
git checkout -b stable
git checkout master
# Edit `cherry_pick.rb`
git add cherry_pick.rb
git commit -m 'Fix bugs in cherry_pick.rb'
git log
# Copy commit SHA
git checkout stable
git cherry-pick <commit SHA>
```
# Configure your environment
----------
## Install
- **Windows**
- Install 'Git for Windows' from https://git-for-windows.github.io
- **Mac**
- Type '`git`' in the Terminal application.
- If it's not installed, it will prompt you to install it.
- **Linux**
```bash
sudo yum install git-all
```
```bash
sudo apt-get install git-all
```
----------
## Configure Git
One-time configuration of the Git client
```bash
git config --global user.name "Your Name"
git config --global user.email you@example.com
```
----------
## Configure SSH Key
```bash
ssh-keygen -t rsa -b 4096 -C "you@computer-name"
```
```bash
# You will be prompted for the following information. Press enter to accept the defaults. Defaults appear in parentheses.
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/you/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/you/.ssh/id_rsa.
Your public key has been saved in /Users/you/.ssh/id_rsa.pub.
The key fingerprint is:
39:fc:ce:94:f4:09:13:95:64:9a:65:c1:de:05:4d:01 you@computer-name
```
Copy your public key and add it to your GitLab profile
```bash
cat ~/.ssh/id_rsa.pub
```
```bash
ssh-rsa AAAAB3NzaC1yc2EAAAADAQEL17Ufacg8cDhlQMS5NhV8z3GHZdhCrZbl4gz you@example.com
```
# Explore GitLab projects
----------
- Dashboard
- User Preferences
- Issues
- Milestones and Labels
- Manage project members
- Project settings
# Feature branching
----------
- Efficient parallel workflow for teams
- Develop each feature in a branch
- Keeps changes isolated
- Consider a 1-to-1 link to issues
- Push branches to the server frequently
- Hint: This is a cheap backup for your work-in-progress code
----------
## Feature branching
1. Create a new feature branch called 'squash_some_bugs'
1. Edit '`bugs.rb`' and remove all the bugs.
1. Commit
1. Push
----------
## Commands
```
git checkout -b squash_some_bugs
# Edit `bugs.rb`
git status
git add bugs.rb
git commit -m 'Fix some buggy code'
git push origin squash_some_bugs
```
# Getting Started
----------
## Instantiating Repositories
* Create a new repository by instantiating it through
```bash
git init
```
* Copy an existing project by cloning the repository through
```bash
git clone <url>
```
----------
## Central Repos
* To instantiate a central repository a `--bare` flag is required.
* Bare repositories don't allow file editing or committing changes.
* Create a bare repo with
```bash
git init --bare project-name.git
```
----------
## Instantiate workflow with clone
1. Create a project in your user namespace
- Choose to import from 'Any Repo by URL' and use
https://gitlab.com/gitlab-org/training-examples.git
2. Create a '`Workspace`' directory in your home directory.
3. Clone the '`training-examples`' project
----------
## Commands
```
mkdir ~/workspace
cd ~/workspace
git clone git@gitlab.example.com:<username>/training-examples.git
cd training-examples
```
----------
## Git concepts
**Untracked files**
New files that Git has not been told to track previously.
**Working area**
Files that have been modified but are not committed.
**Staging area**
Modified files that have been marked to go in the next commit.
----------
## Committing Workflow
1. Edit '`edit_this_file.rb`' in '`training-examples`'
1. See it listed as a changed file (working area)
1. View the differences
1. Stage the file
1. Commit
1. Push the commit to the remote
1. View the git log
----------
## Commands
```
# Edit `edit_this_file.rb`
git status
git diff
git add <file>
git commit -m 'My change'
git push origin master
git log
```
----------
## Note
* git fetch vs pull
* Pull is git fetch + git merge
# Git Add
----------
## Git Add
Adds content to the index or staging area.
* Adds a list of file
```bash
git add <files>
```
* Adds all files including deleted ones
```bash
git add -A
```
----------
## Git add continued
* Add all text files in current dir
```bash
git add *.txt
```
* Add all text file in the project
```bash
git add "*.txt*"
```
* Adds all files in directory
```bash
git add views/layouts/
```
# Git introduction
----------
## Intro
https://git-scm.com/about
- Distributed version control
- Does not rely on connection to a central server
- Many copies of the complete history
- Powerful branching and merging
- Adapts to nearly any workflow
- Fast, reliable and stable file format
----------
## Help!
Use the tools at your disposal when you get stuck.
- Use '`git help <command>`' command
- Use Google
- Read documentation at https://git-scm.com
# Git Log
----------
Git log lists commit history. It allows searching and filtering.
* Initiate log
```
git log
```
* Retrieve set number of records:
```
git log -n 2
```
* Search commits by author. Allows user name or a regular expression.
```
git log --author="user_name"
```
----------
* Search by comment message.
```
git log --grep="<pattern>"
```
* Search by date
```
git log --since=1.month.ago --until=3.weeks.ago
```
----------
## Git Log Workflow
1. Change to workspace directory
2. Clone the multi runner projects
3. Change to project dir
4. Search by author
5. Search by date
6. Combine
----------
## Commands
```
cd ~/workspace
git clone git@gitlab.com:gitlab-org/gitlab-ci-multi-runner.git
cd gitlab-ci-multi-runner
git log --author="Travis"
git log --since=1.month.ago --until=3.weeks.ago
git log --since=1.month.ago --until=1.day.ago --author="Travis"
```
# GitLab Flow
----------
- A simplified branching strategy
- All features and fixes first go to master
- Allows for 'production' or 'stable' branches
- Bug fixes/hot fix patches are cherry-picked from master
----------
### Feature branches
- Create a feature/bugfix branch to do all work
- Use merge requests to merge to master
![inline](http://gitlab.com/gitlab-org/University/raw/5baea0fe222a915d0500e40747d35eb18681cdc3/training/gitlab_flow/feature_branches.png)
----------
## Production branch
- One, long-running production release branch
as opposed to individual stable branches
- Consider creating a tag for each version that gets deployed
----------
## Production branch
![inline](http://gitlab.com/gitlab-org/University/raw/5baea0fe222a915d0500e40747d35eb18681cdc3/training/gitlab_flow/production_branch.png)
----------
## Release branch
- Useful if you release software to customers
- When preparing a new release, create stable branch
from master
- Consider creating a tag for each version
- Cherry-pick critical bug fixes to stable branch for patch release
- Never commit bug fixes directly to stable branch
----------
![inline](http://gitlab.com/gitlab-org/University/raw/5baea0fe222a915d0500e40747d35eb18681cdc3/training/gitlab_flow/release_branches.png)
----------
## More details
Blog post on 'GitLab Flow' at
[http://doc.gitlab.com/ee/workflow/gitlab_flow.html](http://doc.gitlab.com/ee/workflow/gitlab_flow.html)
# Merge conflicts
----------
- Happen often
- Learning to fix conflicts is hard
- Practice makes perfect
- Force push after fixing conflicts. Be careful!
----------
## Merge conflicts
1. Checkout a new branch and edit `conflicts.rb`. Add 'Line4' and 'Line5'.
2. Commit and push
3. Checkout master and edit `conflicts.rb`. Add 'Line6' and 'Line7' below 'Line3'.
4. Commit and push to master
5. Create a merge request and watch it fail
6. Rebase our new branch with master
7. Fix conflicts on the `conflicts.rb` file.
8. Stage the file and continue rebasing
9. Force push the changes
10. Finally continue with the Merge Request
----------
## Commands
```
git checkout -b conflicts_branch
# vi conflicts.rb
# Add 'Line4' and 'Line5'
git commit -am "add line4 and line5"
git push origin conflicts_branch
git checkout master
# vi conflicts.rb
# Add 'Line6' and 'Line7'
git commit -am "add line6 and line7"
git push origin master
```
Create a merge request on the GitLab web UI. You'll see a conflict warning.
```
git checkout conflicts_branch
git fetch
git rebase master
# Fix conflicts by editing the files.
git add conflicts.rb
# No need to commit this file
git rebase --continue
# Remember that we have rewritten our commit history so we
# need to force push so that our remote branch is restructured
git push origin conflicts_branch -f
```
----------
## Note
* When to use 'git merge' and when to use 'git rebase'
* Rebase when updating your branch with master
* Merge when bringing changes from feature to master
* Reference: https://www.atlassian.com/git/tutorials/merging-vs-rebasing/
# Merge requests
----------
- When you want feedback create a merge request
- Target is the default branch (usually master)
- Assign or mention the person you would like to review
- Add 'WIP' to the title if it's a work in progress
- When accepting, always delete the branch
- Anyone can comment, not just the assignee
- Push corrections to the same branch
----------
## Merge requests
**Create your first merge request**
1. Use the blue button in the activity feed
1. View the diff (changes) and leave a comment
1. Push a new commit to the same branch
1. Review the changes again and notice the update
----------
## Feedback and Collaboration
- Merge requests are a time for feedback and collaboration
- Giving feedback is hard
- Be as kind as possible
- Receiving feedback is hard
- Be as receptive as possible
- Feedback is about the best code, not the person. You are not your code
----------
## Feedback and Collaboration
Review the Thoughtbot code-review guide for suggestions to follow when reviewing merge requests:
[https://github.com/thoughtbot/guides/tree/master/code-review](https://github.com/thoughtbot/guides/tree/master/code-review)
See GitLab merge requests for examples:
[https://gitlab.com/gitlab-org/gitlab-ce/merge_requests](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests)
# Rollback Commits
----------
## Undo Commits
* Undo last commit putting everything back into the staging area.
```
git reset --soft HEAD^
```
* Add files and change message with:
```
git commit --amend -m "New Message"
```
----------
* Undo last and remove changes
```
git reset --hard HEAD^
```
* Same as last one but for two commits back
```
git reset --hard HEAD^^
```
** Don't reset after pushing **
----------
## Reset Workflow
1. Edit file again 'edit_this_file.rb'
2. Check status
3. Add and commit with wrong message
4. Check log
5. Amend commit
6. Check log
7. Soft reset
8. Check log
9. Pull for updates
10. Push changes
----------
## Commands
```
# Change file edit_this_file.rb
git status
git commit -am "kjkfjkg"
git log
git commit --amend -m "New comment added"
git log
git reset --soft HEAD^
git log
git pull origin master
git push origin master
```
----------
## Note
* git revert vs git reset
* Reset removes the commit while revert removes the changes but leaves the commit
* Revert is safer considering we can revert a revert
```
# Changed file
git commit -am "bug introduced"
git revert HEAD
# New commit created reverting changes
# Now we want to re apply the reverted commit
git log # take hash from the revert commit
git revert <rev commit hash>
# reverted commit is back (new commit created again)
```
# Git Stash
----------
We use git stash to store our changes when they are not ready to be committed
and we need to change to a different branch.
* Stash
```
git stash save
# or
git stash
# or with a message
git stash save "this is a message to display on the list"
```
* Apply stash to keep working on it
```
git stash apply
# or apply a specific one from out stack
git stash apply stash@{3}
```
----------
* Every time we save a stash it gets stacked so by using list we can see all our
stashes.
```
git stash list
# or for more information (log methods)
git stash list --stat
```
* To clean our stack we need to manually remove them.
```
# drop top stash
git stash drop
# or
git stash drop <name>
# to clear all history we can use
git stash clear
```
----------
* Apply and drop on one command
```
git stash pop
```
* If we meet conflicts we need to either reset or commit our changes.
* Conflicts through `pop` will not drop a stash afterwards.
----------
## Git Stash
1. Modify a file
2. Stage file
3. Stash it
4. View our stash list
5. Confirm no pending changes through status
5. Apply with pop
6. View list to confirm changes
----------
## Commands
```
# Modify edit_this_file.rb file
git add .
git stash save "Saving changes from edit this file"
git stash list
git status
git stash pop
git stash list
git status
```
## Subtree
----------
## Subtree
* Used when there are nested repositories.
* Not recommended when the amount of dependencies is too large
* For these cases we need a dependency control system
* Command are painfully long so aliases are necessary
----------
## Subtree Aliases
* Add: git subtree add --prefix <target-folder> <url> <branch> --squash
* Pull: git subtree add --prefix <target-folder> <url> <branch> --squash
* Push: git subtree add --prefix <target-folder> <url> <branch>
* Ex: git config alias.sbp 'subtree pull --prefix st /
git@gitlab.com:balameb/subtree-nested-example.git master --squash'
----------
```
# Add an alias
# Add
git config alias.sba 'subtree add --prefix st /
git@gitlab.com:balameb/subtree-nested-example.git master --squash'
# Pull
git config alias.sbpl 'subtree pull --prefix st /
git@gitlab.com:balameb/subtree-nested-example.git master --squash'
# Push
git config alias.sbph 'subtree push --prefix st /
git@gitlab.com:balameb/subtree-nested-example.git master'
# Adding this subtree adds a st dir with a readme
git sba
vi st/README.md
# Edit file
git status shows differences
```
----------
```
# Adding, or committing won't change the sub repo at remote
# even if we push
git add -A
git commit -m "Adding to subtree readme"
# Push to subtree repo
git sbph
# now we can check our remote sub repo
```
# Tags
----------
- Useful for marking deployments and releases
- Annotated tags are an unchangeable part of Git history
- Soft/lightweight tags can be set and removed at will
- Many projects combine an anotated release tag with a stable branch
- Consider setting deployment/release tags automatically
----------
# Tags
- Create a lightweight tag
- Create an annotated tag
- Push the tags to the remote repository
**Additional resources**
[http://git-scm.com/book/en/Git-Basics-Tagging](http://git-scm.com/book/en/Git-Basics-Tagging)
----------
# Commands
```
git checkout master
# Lightweight tag
git tag my_lightweight_tag
# Annotated tag
git tag -a v1.0 -m ‘Version 1.0’
git tag
git push origin --tags
```
# Unstage
----------
## Unstage
* To remove files from stage use reset HEAD. Where HEAD is the last commit of the current branch.
```bash
git reset HEAD <file>
```
* This will unstage the file but maintain the modifications. To revert the file back to the state it was in before the changes we can use:
```bash
git checkout -- <file>
```
----------
* To remove a file from disk and repo use 'git rm' and to rm a dir use the '-r' flag.
```
git rm '*.txt'
git rm -r <dirname>
```
* If we want to remove a file from the repository but keep it on disk, say we forgot to add it to our `.gitignore` file then use `--cache`.
```
git rm <filename> --cache
```
# GitLab Git Workshop
---
# Agenda
1. Brief history of Git
1. GitLab walkthrough
1. Configure your environment
1. Workshop
---
# Git introduction
https://git-scm.com/about
- Distributed version control
- Does not rely on connection to a central server
- Many copies of the complete history
- Powerful branching and merging
- Adapts to nearly any workflow
- Fast, reliable and stable file format
---
# Help!
Use the tools at your disposal when you get stuck.
- Use '`git help <command>`' command
- Use Google
- Read documentation at https://git-scm.com
---
# GitLab Walkthrough
![fit](logo.png)
---
# Configure your environment
- Windows: Install 'Git for Windows'
> https://git-for-windows.github.io
- Mac: Type '`git`' in the Terminal application.
> If it's not installed, it will prompt you to install it.
- Debian: '`sudo apt-get install git-all`'
or Red Hat '`sudo yum install git-all`'
---
# Git Workshop
## Overview
1. Configure Git
1. Configure SSH Key
1. Create a project
1. Committing
1. Feature branching
1. Merge requests
1. Feedback and Collaboration
---
# Configure Git
One-time configuration of the Git client
```bash
git config --global user.name "Your Name"
git config --global user.email you@example.com
```
---
# Configure SSH Key
```bash
ssh-keygen -t rsa -b 4096 -C "you@computer-name"
```
```bash
# You will be prompted for the following information. Press enter to accept the defaults. Defaults appear in parentheses.
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/you/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/you/.ssh/id_rsa.
Your public key has been saved in /Users/you/.ssh/id_rsa.pub.
The key fingerprint is:
39:fc:ce:94:f4:09:13:95:64:9a:65:c1:de:05:4d:01 you@computer-name
```
Copy your public key and add it to your GitLab profile
```bash
cat ~/.ssh/id_rsa.pub
```
```bash
ssh-rsa AAAAB3NzaC1yc2EAAAADAQEL17Ufacg8cDhlQMS5NhV8z3GHZdhCrZbl4gz you@example.com
```
---
# Create a project
- Create a project in your user namespace
- Choose to import from 'Any Repo by URL' and use
https://gitlab.com/gitlab-org/training-examples.git
- Create a '`development`' or '`workspace`' directory in your home directory.
- Clone the '`training-examples`' project
---
# Commands
```
mkdir ~/development
cd ~/development
-or-
mkdir ~/workspace
cd ~/workspace
git clone git@gitlab.example.com:<username>/training-examples.git
cd training-examples
```
---
# Git concepts
**Untracked files**
New files that Git has not been told to track previously.
**Working area**
Files that have been modified but are not committed.
**Staging area**
Modified files that have been marked to go in the next commit.
---
# Committing
1. Edit '`edit_this_file.rb`' in '`training-examples`'
1. See it listed as a changed file (working area)
1. View the differences
1. Stage the file
1. Commit
1. Push the commit to the remote
1. View the git log
---
# Commands
```
# Edit `edit_this_file.rb`
git status
git diff
git add <file>
git commit -m 'My change'
git push origin master
git log
```
---
# Feature branching
- Efficient parallel workflow for teams
- Develop each feature in a branch
- Keeps changes isolated
- Consider a 1-to-1 link to issues
- Push branches to the server frequently
- Hint: This is a cheap backup for your work-in-progress code
---
# Feature branching
1. Create a new feature branch called 'squash_some_bugs'
1. Edit '`bugs.rb`' and remove all the bugs.
1. Commit
1. Push
---
# Commands
```
git checkout -b squash_some_bugs
# Edit `bugs.rb`
git status
git add bugs.rb
git commit -m 'Fix some buggy code'
git push origin squash_some_bugs
```
---
# Merge requests
- When you want feedback create a merge request
- Target is the ‘default’ branch (usually master)
- Assign or mention the person you would like to review
- Add 'WIP' to the title if it's a work in progress
- When accepting, always delete the branch
- Anyone can comment, not just the assignee
- Push corrections to the same branch
---
# Merge requests
**Create your first merge request**
1. Use the blue button in the activity feed
1. View the diff (changes) and leave a comment
1. Push a new commit to the same branch
1. Review the changes again and notice the update
---
# Feedback and Collaboration
- Merge requests are a time for feedback and collaboration
- Giving feedback is hard
- Be as kind as possible
- Receiving feedback is hard
- Be as receptive as possible
- Feedback is about the best code, not the person. You are not your code
---
# Feedback and Collaboration
Review the Thoughtbot code-review guide for suggestions to follow when reviewing merge requests:
[https://github.com/thoughtbot/guides/tree/master/code-review](https://github.com/thoughtbot/guides/tree/master/code-review)
See GitLab merge requests for examples:
[https://gitlab.com/gitlab-org/gitlab-ce/merge_requests](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests)
---
# Explore GitLab projects
![fit](logo.png)
- Dashboard
- User Preferences
- ReadMe, Changelog, License shortcuts
- Issues
- Milestones and Labels
- Manage project members
- Project settings
---
# Tags
- Useful for marking deployments and releases
- Annotated tags are an unchangeable part of Git history
- Soft/lightweight tags can be set and removed at will
- Many projects combine an anotated release tag with a stable branch
- Consider setting deployment/release tags automatically
---
# Tags
- Create a lightweight tag
- Create an annotated tag
- Push the tags to the remote repository
**Additional resources**
[http://git-scm.com/book/en/Git-Basics-Tagging](http://git-scm.com/book/en/Git-Basics-Tagging)
---
# Commands
```
git checkout master
# Lightweight tag
git tag my_lightweight_tag
# Annotated tag
git tag -a v1.0 -m ‘Version 1.0’
git tag
git push origin --tags
```
---
# Merge conflicts
- Happen often
- Learning to fix conflicts is hard
- Practice makes perfect
- Force push after fixing conflicts. Be careful!
---
# Merge conflicts
1. Checkout a new branch and edit `conflicts.rb`. Add 'Line4' and 'Line5'.
1. Commit and push
1. Checkout master and edit `conflicts.rb`. Add 'Line6' and 'Line7' below 'Line3'.
1. Commit and push to master
1. Create a merge request
---
# Merge conflicts
After creating a merge request you should notice that conflicts exist. Resolve
the conflicts locally by rebasing.
```
git rebase master
# Fix conflicts by editing the files.
git add conflicts.rb
git commit -m 'Fix conflicts'
git rebase --continue
git push origin <branch> -f
```
---
# Rebase with squash
You may end up with a commit log that looks like this:
```
Fix issue #13
Test
Fix
Fix again
Test
Test again
Does this work?
```
Squash these in to meaningful commits using an interactive rebase.
---
# Rebase with squash
Squash the commits on the same branch we used for the merge conflicts step.
```
git rebase -i master
```
In the editor, leave the first commit as 'pick' and set others to 'fixup'.
---
# Questions?
![fit](logo.png)
Thank you for your hard work!
**Additional Resources**
GitLab Documentation [http://docs.gitlab.com](http://docs.gitlab.com/)
GUI Clients [http://git-scm.com/downloads/guis](http://git-scm.com/downloads/guis)
Pro git book [http://git-scm.com/book](http://git-scm.com/book)
Platzi Course [https://courses.platzi.com/courses/git-gitlab/](https://courses.platzi.com/courses/git-gitlab/)
Code School tutorial [http://try.github.io/](http://try.github.io/)
Contact Us - [subscribers@gitlab.com](subscribers@gitlab.com)
...@@ -26,6 +26,16 @@ module API ...@@ -26,6 +26,16 @@ module API
present @groups, with: Entities::Group present @groups, with: Entities::Group
end end
# Get list of owned groups for authenticated user
#
# Example Request:
# GET /groups/owned
get '/owned' do
@groups = current_user.owned_groups
@groups = paginate @groups
present @groups, with: Entities::Group, user: current_user
end
# Create group. Available only for users who can create groups. # Create group. Available only for users who can create groups.
# #
# Parameters: # Parameters:
......
...@@ -11,19 +11,25 @@ module API ...@@ -11,19 +11,25 @@ module API
else milestones else milestones
end end
end end
params :optional_params do
optional :description, type: String, desc: 'The description of the milestone'
optional :due_date, type: String, desc: 'The due date of the milestone'
end
end end
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects do resource :projects do
# Get a list of project milestones desc 'Get a list of project milestones' do
# success Entities::Milestone
# Parameters: end
# id (required) - The ID of a project params do
# state (optional) - Return "active" or "closed" milestones optional :state, type: String, values: %w[active closed all], default: 'all',
# Example Request: desc: 'Return "active", "closed", or "all" milestones'
# GET /projects/:id/milestones optional :iid, type: Integer, desc: 'The IID of the milestone'
# GET /projects/:id/milestones?iid=42 end
# GET /projects/:id/milestones?state=active
# GET /projects/:id/milestones?state=closed
get ":id/milestones" do get ":id/milestones" do
authorize! :read_milestone, user_project authorize! :read_milestone, user_project
...@@ -34,34 +40,31 @@ module API ...@@ -34,34 +40,31 @@ module API
present paginate(milestones), with: Entities::Milestone present paginate(milestones), with: Entities::Milestone
end end
# Get a single project milestone desc 'Get a single project milestone' do
# success Entities::Milestone
# Parameters: end
# id (required) - The ID of a project params do
# milestone_id (required) - The ID of a project milestone requires :milestone_id, type: Integer, desc: 'The ID of a project milestone'
# Example Request: end
# GET /projects/:id/milestones/:milestone_id
get ":id/milestones/:milestone_id" do get ":id/milestones/:milestone_id" do
authorize! :read_milestone, user_project authorize! :read_milestone, user_project
@milestone = user_project.milestones.find(params[:milestone_id]) milestone = user_project.milestones.find(params[:milestone_id])
present @milestone, with: Entities::Milestone present milestone, with: Entities::Milestone
end end
# Create a new project milestone desc 'Create a new project milestone' do
# success Entities::Milestone
# Parameters: end
# id (required) - The ID of the project params do
# title (required) - The title of the milestone requires :title, type: String, desc: 'The title of the milestone'
# description (optional) - The description of the milestone use :optional_params
# due_date (optional) - The due date of the milestone end
# Example Request:
# POST /projects/:id/milestones
post ":id/milestones" do post ":id/milestones" do
authorize! :admin_milestone, user_project authorize! :admin_milestone, user_project
required_attributes! [:title] milestone_params = declared(params, include_parent_namespaces: false)
attrs = attributes_for_keys [:title, :description, :due_date]
milestone = ::Milestones::CreateService.new(user_project, current_user, attrs).execute milestone = ::Milestones::CreateService.new(user_project, current_user, milestone_params).execute
if milestone.valid? if milestone.valid?
present milestone, with: Entities::Milestone present milestone, with: Entities::Milestone
...@@ -70,22 +73,23 @@ module API ...@@ -70,22 +73,23 @@ module API
end end
end end
# Update an existing project milestone desc 'Update an existing project milestone' do
# success Entities::Milestone
# Parameters: end
# id (required) - The ID of a project params do
# milestone_id (required) - The ID of a project milestone requires :milestone_id, type: Integer, desc: 'The ID of a project milestone'
# title (optional) - The title of a milestone optional :title, type: String, desc: 'The title of the milestone'
# description (optional) - The description of a milestone optional :state_event, type: String, values: %w[close activate],
# due_date (optional) - The due date of a milestone desc: 'The state event of the milestone '
# state_event (optional) - The state event of the milestone (close|activate) use :optional_params
# Example Request: at_least_one_of :title, :description, :due_date, :state_event
# PUT /projects/:id/milestones/:milestone_id end
put ":id/milestones/:milestone_id" do put ":id/milestones/:milestone_id" do
authorize! :admin_milestone, user_project authorize! :admin_milestone, user_project
attrs = attributes_for_keys [:title, :description, :due_date, :state_event] milestone_params = declared(params, include_parent_namespaces: false, include_missing: false)
milestone = user_project.milestones.find(params[:milestone_id])
milestone = ::Milestones::UpdateService.new(user_project, current_user, attrs).execute(milestone) milestone = user_project.milestones.find(milestone_params.delete(:milestone_id))
milestone = ::Milestones::UpdateService.new(user_project, current_user, milestone_params).execute(milestone)
if milestone.valid? if milestone.valid?
present milestone, with: Entities::Milestone present milestone, with: Entities::Milestone
...@@ -94,21 +98,20 @@ module API ...@@ -94,21 +98,20 @@ module API
end end
end end
# Get all issues for a single project milestone desc 'Get all issues for a single project milestone' do
# success Entities::Issue
# Parameters: end
# id (required) - The ID of a project params do
# milestone_id (required) - The ID of a project milestone requires :milestone_id, type: Integer, desc: 'The ID of a project milestone'
# Example Request: end
# GET /projects/:id/milestones/:milestone_id/issues
get ":id/milestones/:milestone_id/issues" do get ":id/milestones/:milestone_id/issues" do
authorize! :read_milestone, user_project authorize! :read_milestone, user_project
@milestone = user_project.milestones.find(params[:milestone_id]) milestone = user_project.milestones.find(params[:milestone_id])
finder_params = { finder_params = {
project_id: user_project.id, project_id: user_project.id,
milestone_title: @milestone.title milestone_title: milestone.title
} }
issues = IssuesFinder.new(current_user, finder_params).execute issues = IssuesFinder.new(current_user, finder_params).execute
......
module API module API
# Runners API
class Runners < Grape::API class Runners < Grape::API
before { authenticate! } before { authenticate! }
resource :runners do resource :runners do
# Get runners available for user desc 'Get runners available for user' do
# success Entities::Runner
# Example Request: end
# GET /runners params do
optional :scope, type: String, values: %w[active paused online],
desc: 'The scope of specific runners to show'
end
get do get do
runners = filter_runners(current_user.ci_authorized_runners, params[:scope], without: ['specific', 'shared']) runners = filter_runners(current_user.ci_authorized_runners, params[:scope], without: ['specific', 'shared'])
present paginate(runners), with: Entities::Runner present paginate(runners), with: Entities::Runner
end end
# Get all runners - shared and specific desc 'Get all runners - shared and specific' do
# success Entities::Runner
# Example Request: end
# GET /runners/all params do
optional :scope, type: String, values: %w[active paused online specific shared],
desc: 'The scope of specific runners to show'
end
get 'all' do get 'all' do
authenticated_as_admin! authenticated_as_admin!
runners = filter_runners(Ci::Runner.all, params[:scope]) runners = filter_runners(Ci::Runner.all, params[:scope])
present paginate(runners), with: Entities::Runner present paginate(runners), with: Entities::Runner
end end
# Get runner's details desc "Get runner's details" do
# success Entities::RunnerDetails
# Parameters: end
# id (required) - The ID of ther runner params do
# Example Request: requires :id, type: Integer, desc: 'The ID of the runner'
# GET /runners/:id end
get ':id' do get ':id' do
runner = get_runner(params[:id]) runner = get_runner(params[:id])
authenticate_show_runner!(runner) authenticate_show_runner!(runner)
...@@ -36,33 +41,37 @@ module API ...@@ -36,33 +41,37 @@ module API
present runner, with: Entities::RunnerDetails, current_user: current_user present runner, with: Entities::RunnerDetails, current_user: current_user
end end
# Update runner's details desc "Update runner's details" do
# success Entities::RunnerDetails
# Parameters: end
# id (required) - The ID of ther runner params do
# description (optional) - Runner's description requires :id, type: Integer, desc: 'The ID of the runner'
# active (optional) - Runner's status optional :description, type: String, desc: 'The description of the runner'
# tag_list (optional) - Array of tags for runner optional :active, type: Boolean, desc: 'The state of a runner'
# Example Request: optional :tag_list, type: Array[String], desc: 'The list of tags for a runner'
# PUT /runners/:id optional :run_untagged, type: Boolean, desc: 'Flag indicating the runner can execute untagged jobs'
optional :locked, type: Boolean, desc: 'Flag indicating the runner is locked'
at_least_one_of :description, :active, :tag_list, :run_untagged, :locked
end
put ':id' do put ':id' do
runner = get_runner(params[:id]) runner = get_runner(params.delete(:id))
authenticate_update_runner!(runner) authenticate_update_runner!(runner)
attrs = attributes_for_keys [:description, :active, :tag_list, :run_untagged, :locked] runner_params = declared(params, include_missing: false)
if runner.update(attrs)
if runner.update(runner_params)
present runner, with: Entities::RunnerDetails, current_user: current_user present runner, with: Entities::RunnerDetails, current_user: current_user
else else
render_validation_error!(runner) render_validation_error!(runner)
end end
end end
# Remove runner desc 'Remove a runner' do
# success Entities::Runner
# Parameters: end
# id (required) - The ID of ther runner params do
# Example Request: requires :id, type: Integer, desc: 'The ID of the runner'
# DELETE /runners/:id end
delete ':id' do delete ':id' do
runner = get_runner(params[:id]) runner = get_runner(params[:id])
authenticate_delete_runner!(runner) authenticate_delete_runner!(runner)
...@@ -72,28 +81,31 @@ module API ...@@ -72,28 +81,31 @@ module API
end end
end end
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects do resource :projects do
before { authorize_admin_project } before { authorize_admin_project }
# Get runners available for project desc 'Get runners available for project' do
# success Entities::Runner
# Example Request: end
# GET /projects/:id/runners params do
optional :scope, type: String, values: %w[active paused online specific shared],
desc: 'The scope of specific runners to show'
end
get ':id/runners' do get ':id/runners' do
runners = filter_runners(Ci::Runner.owned_or_shared(user_project.id), params[:scope]) runners = filter_runners(Ci::Runner.owned_or_shared(user_project.id), params[:scope])
present paginate(runners), with: Entities::Runner present paginate(runners), with: Entities::Runner
end end
# Enable runner for project desc 'Enable a runner for a project' do
# success Entities::Runner
# Parameters: end
# id (required) - The ID of the project params do
# runner_id (required) - The ID of the runner requires :runner_id, type: Integer, desc: 'The ID of the runner'
# Example Request: end
# POST /projects/:id/runners/:runner_id
post ':id/runners' do post ':id/runners' do
required_attributes! [:runner_id]
runner = get_runner(params[:runner_id]) runner = get_runner(params[:runner_id])
authenticate_enable_runner!(runner) authenticate_enable_runner!(runner)
...@@ -106,13 +118,12 @@ module API ...@@ -106,13 +118,12 @@ module API
end end
end end
# Disable project's runner desc "Disable project's runner" do
# success Entities::Runner
# Parameters: end
# id (required) - The ID of the project params do
# runner_id (required) - The ID of the runner requires :runner_id, type: Integer, desc: 'The ID of the runner'
# Example Request: end
# DELETE /projects/:id/runners/:runner_id
delete ':id/runners/:runner_id' do delete ':id/runners/:runner_id' do
runner_project = user_project.runner_projects.find_by(runner_id: params[:runner_id]) runner_project = user_project.runner_projects.find_by(runner_id: params[:runner_id])
not_found!('Runner') unless runner_project not_found!('Runner') unless runner_project
......
module API module API
# Users API
class Session < Grape::API class Session < Grape::API
# Login to get token desc 'Login to get token' do
# success Entities::UserLogin
# Parameters: end
# login (*required) - user login params do
# email (*required) - user email optional :login, type: String, desc: 'The username'
# password (required) - user password optional :email, type: String, desc: 'The email of the user'
# requires :password, type: String, desc: 'The password of the user'
# Example Request: at_least_one_of :login, :email
# POST /session end
post "/session" do post "/session" do
user = Gitlab::Auth.find_with_user_password(params[:email] || params[:login], params[:password]) user = Gitlab::Auth.find_with_user_password(params[:email] || params[:login], params[:password])
......
module API module API
# Triggers API
class Triggers < Grape::API class Triggers < Grape::API
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects do resource :projects do
# Trigger a GitLab project build desc 'Trigger a GitLab project build' do
# success Entities::TriggerRequest
# Parameters: end
# id (required) - The ID of a CI project params do
# ref (required) - The name of project's branch or tag requires :ref, type: String, desc: 'The commit sha or name of a branch or tag'
# token (required) - The uniq token of trigger requires :token, type: String, desc: 'The unique token of trigger'
# variables (optional) - The list of variables to be injected into build optional :variables, type: Hash, desc: 'The list of variables to be injected into build'
# Example Request: end
# POST /projects/:id/trigger/builds
post ":id/trigger/builds" do post ":id/trigger/builds" do
required_attributes! [:ref, :token]
project = Project.find_with_namespace(params[:id]) || Project.find_by(id: params[:id]) project = Project.find_with_namespace(params[:id]) || Project.find_by(id: params[:id])
trigger = Ci::Trigger.find_by_token(params[:token].to_s) trigger = Ci::Trigger.find_by_token(params[:token].to_s)
not_found! unless project && trigger not_found! unless project && trigger
...@@ -22,10 +21,6 @@ module API ...@@ -22,10 +21,6 @@ module API
# validate variables # validate variables
variables = params[:variables] variables = params[:variables]
if variables if variables
unless variables.is_a?(Hash)
render_api_error!('variables needs to be a hash', 400)
end
unless variables.all? { |key, value| key.is_a?(String) && value.is_a?(String) } unless variables.all? { |key, value| key.is_a?(String) && value.is_a?(String) }
render_api_error!('variables needs to be a map of key-valued strings', 400) render_api_error!('variables needs to be a map of key-valued strings', 400)
end end
...@@ -44,31 +39,24 @@ module API ...@@ -44,31 +39,24 @@ module API
end end
end end
# Get triggers list desc 'Get triggers list' do
# success Entities::Trigger
# Parameters: end
# id (required) - The ID of a project
# page (optional) - The page number for pagination
# per_page (optional) - The value of items per page to show
# Example Request:
# GET /projects/:id/triggers
get ':id/triggers' do get ':id/triggers' do
authenticate! authenticate!
authorize! :admin_build, user_project authorize! :admin_build, user_project
triggers = user_project.triggers.includes(:trigger_requests) triggers = user_project.triggers.includes(:trigger_requests)
triggers = paginate(triggers)
present triggers, with: Entities::Trigger present paginate(triggers), with: Entities::Trigger
end end
# Get specific trigger of a project desc 'Get specific trigger of a project' do
# success Entities::Trigger
# Parameters: end
# id (required) - The ID of a project params do
# token (required) - The `token` of a trigger requires :token, type: String, desc: 'The unique token of trigger'
# Example Request: end
# GET /projects/:id/triggers/:token
get ':id/triggers/:token' do get ':id/triggers/:token' do
authenticate! authenticate!
authorize! :admin_build, user_project authorize! :admin_build, user_project
...@@ -79,12 +67,9 @@ module API ...@@ -79,12 +67,9 @@ module API
present trigger, with: Entities::Trigger present trigger, with: Entities::Trigger
end end
# Create trigger desc 'Create a trigger' do
# success Entities::Trigger
# Parameters: end
# id (required) - The ID of a project
# Example Request:
# POST /projects/:id/triggers
post ':id/triggers' do post ':id/triggers' do
authenticate! authenticate!
authorize! :admin_build, user_project authorize! :admin_build, user_project
...@@ -94,13 +79,12 @@ module API ...@@ -94,13 +79,12 @@ module API
present trigger, with: Entities::Trigger present trigger, with: Entities::Trigger
end end
# Delete trigger desc 'Delete a trigger' do
# success Entities::Trigger
# Parameters: end
# id (required) - The ID of a project params do
# token (required) - The `token` of a trigger requires :token, type: String, desc: 'The unique token of trigger'
# Example Request: end
# DELETE /projects/:id/triggers/:token
delete ':id/triggers/:token' do delete ':id/triggers/:token' do
authenticate! authenticate!
authorize! :admin_build, user_project authorize! :admin_build, user_project
......
...@@ -10,6 +10,9 @@ module API ...@@ -10,6 +10,9 @@ module API
# GET /users # GET /users
# GET /users?search=Admin # GET /users?search=Admin
# GET /users?username=root # GET /users?username=root
# GET /users?active=true
# GET /users?external=true
# GET /users?blocked=true
get do get do
unless can?(current_user, :read_users_list, nil) unless can?(current_user, :read_users_list, nil)
render_api_error!("Not authorized.", 403) render_api_error!("Not authorized.", 403)
...@@ -19,8 +22,10 @@ module API ...@@ -19,8 +22,10 @@ module API
@users = User.where(username: params[:username]) @users = User.where(username: params[:username])
else else
@users = User.all @users = User.all
@users = @users.active if params[:active].present? @users = @users.active if to_boolean(params[:active])
@users = @users.search(params[:search]) if params[:search].present? @users = @users.search(params[:search]) if params[:search].present?
@users = @users.blocked if to_boolean(params[:blocked])
@users = @users.external if to_boolean(params[:external]) && current_user.is_admin?
@users = paginate @users @users = paginate @users
end end
......
...@@ -2,39 +2,38 @@ ...@@ -2,39 +2,38 @@
module Gitlab module Gitlab
# Checks if a set of migrations requires downtime or not. # Checks if a set of migrations requires downtime or not.
class EeCompatCheck class EeCompatCheck
CE_REPO = 'https://gitlab.com/gitlab-org/gitlab-ce.git'.freeze
EE_REPO = 'https://gitlab.com/gitlab-org/gitlab-ee.git'.freeze EE_REPO = 'https://gitlab.com/gitlab-org/gitlab-ee.git'.freeze
CHECK_DIR = Rails.root.join('ee_compat_check')
MAX_FETCH_DEPTH = 500
IGNORED_FILES_REGEX = /(VERSION|CHANGELOG\.md:\d+)/.freeze
attr_reader :ce_branch, :check_dir, :ce_repo attr_reader :repo_dir, :patches_dir, :ce_repo, :ce_branch
def initialize(branch:, check_dir:, ce_repo: nil) def initialize(branch:, ce_repo: CE_REPO)
@repo_dir = CHECK_DIR.join('repo')
@patches_dir = CHECK_DIR.join('patches')
@ce_branch = branch @ce_branch = branch
@check_dir = check_dir @ce_repo = ce_repo
@ce_repo = ce_repo || 'https://gitlab.com/gitlab-org/gitlab-ce.git'
end end
def check def check
ensure_ee_repo ensure_ee_repo
delete_patches ensure_patches_dir
generate_patch(ce_branch, ce_patch_full_path) generate_patch(ce_branch, ce_patch_full_path)
Dir.chdir(check_dir) do Dir.chdir(repo_dir) do
step("In the #{check_dir} directory") step("In the #{repo_dir} directory")
step("Pulling latest master", %w[git pull --ff-only origin master])
status = catch(:halt_check) do status = catch(:halt_check) do
ce_branch_compat_check! ce_branch_compat_check!
delete_ee_branch_locally!
delete_ee_branch_locally
ee_branch_presence_check! ee_branch_presence_check!
ee_branch_compat_check! ee_branch_compat_check!
end end
delete_ee_branch_locally delete_ee_branch_locally!
delete_patches
if status.nil? if status.nil?
true true
...@@ -47,20 +46,43 @@ module Gitlab ...@@ -47,20 +46,43 @@ module Gitlab
private private
def ensure_ee_repo def ensure_ee_repo
if Dir.exist?(check_dir) if Dir.exist?(repo_dir)
step("#{check_dir} already exists") step("#{repo_dir} already exists")
else else
cmd = %W[git clone --branch master --single-branch --depth 1 #{EE_REPO} #{check_dir}] cmd = %W[git clone --branch master --single-branch --depth 200 #{EE_REPO} #{repo_dir}]
step("Cloning #{EE_REPO} into #{check_dir}", cmd) step("Cloning #{EE_REPO} into #{repo_dir}", cmd)
end end
end end
def ce_branch_compat_check! def ensure_patches_dir
cmd = %W[git apply --check #{ce_patch_full_path}] FileUtils.mkdir_p(patches_dir)
status = step("Checking if #{ce_patch_name} applies cleanly to EE/master", cmd) end
def generate_patch(branch, patch_path)
FileUtils.rm(patch_path, force: true)
depth = 0
loop do
depth += 50
cmd = %W[git fetch --depth #{depth} origin --prune +refs/heads/master:refs/remotes/origin/master]
Gitlab::Popen.popen(cmd)
_, status = Gitlab::Popen.popen(%w[git merge-base FETCH_HEAD HEAD])
raise "#{branch} is too far behind master, please rebase it!" if depth >= MAX_FETCH_DEPTH
break if status.zero?
end
if status.zero? step("Generating the patch against master in #{patch_path}")
puts ce_applies_cleanly_msg(ce_branch) output, status = Gitlab::Popen.popen(%w[git format-patch FETCH_HEAD --stdout])
throw(:halt_check, :ko) unless status.zero?
File.write(patch_path, output)
throw(:halt_check, :ko) unless File.exist?(patch_path)
end
def ce_branch_compat_check!
if check_patch(ce_patch_full_path).zero?
puts applies_cleanly_msg(ce_branch)
throw(:halt_check) throw(:halt_check)
end end
end end
...@@ -80,10 +102,8 @@ module Gitlab ...@@ -80,10 +102,8 @@ module Gitlab
step("Checking out origin/#{ee_branch}", %W[git checkout -b #{ee_branch} FETCH_HEAD]) step("Checking out origin/#{ee_branch}", %W[git checkout -b #{ee_branch} FETCH_HEAD])
generate_patch(ee_branch, ee_patch_full_path) generate_patch(ee_branch, ee_patch_full_path)
cmd = %W[git apply --check #{ee_patch_full_path}]
status = step("Checking if #{ee_patch_name} applies cleanly to EE/master", cmd)
unless status.zero? unless check_patch(ee_patch_full_path).zero?
puts puts
puts ee_branch_doesnt_apply_cleanly_msg puts ee_branch_doesnt_apply_cleanly_msg
...@@ -91,50 +111,49 @@ module Gitlab ...@@ -91,50 +111,49 @@ module Gitlab
end end
puts puts
puts ee_applies_cleanly_msg puts applies_cleanly_msg(ee_branch)
end end
def generate_patch(branch, filepath) def check_patch(patch_path)
FileUtils.rm(filepath, force: true) step("Checking out master", %w[git checkout master])
step("Reseting to latest master", %w[git reset --hard origin/master])
depth = 0 step("Checking if #{patch_path} applies cleanly to EE/master")
loop do output, status = Gitlab::Popen.popen(%W[git apply --check #{patch_path}])
depth += 10
step("Fetching origin/master", %W[git fetch origin master --depth=#{depth}])
status = step("Finding merge base with master", %W[git merge-base FETCH_HEAD #{branch}])
break if status.zero? || depth > 500
end
raise "#{branch} is too far behind master, please rebase it!" if depth > 500 unless status.zero?
failed_files = output.lines.reduce([]) do |memo, line|
if line.start_with?('error: patch failed:')
file = line.sub(/\Aerror: patch failed: /, '')
memo << file unless file =~ IGNORED_FILES_REGEX
end
memo
end
step("Generating the patch against master") if failed_files.empty?
output, status = Gitlab::Popen.popen(%w[git format-patch FETCH_HEAD --stdout]) status = 0
throw(:halt_check, :ko) unless status.zero? else
puts "\nConflicting files:"
failed_files.each do |file|
puts " - #{file}"
end
end
end
File.write(filepath, output) status
throw(:halt_check, :ko) unless File.exist?(filepath)
end end
def delete_ee_branch_locally def delete_ee_branch_locally!
command(%w[git checkout master]) command(%w[git checkout master])
step("Deleting the local #{ee_branch} branch", %W[git branch -D #{ee_branch}]) step("Deleting the local #{ee_branch} branch", %W[git branch -D #{ee_branch}])
end end
def delete_patches
step("Deleting #{ce_patch_full_path}")
FileUtils.rm(ce_patch_full_path, force: true)
step("Deleting #{ee_patch_full_path}")
FileUtils.rm(ee_patch_full_path, force: true)
end
def ce_patch_name def ce_patch_name
@ce_patch_name ||= "#{ce_branch}.patch" @ce_patch_name ||= "#{ce_branch}.patch"
end end
def ce_patch_full_path def ce_patch_full_path
@ce_patch_full_path ||= File.expand_path(ce_patch_name, check_dir) @ce_patch_full_path ||= patches_dir.join(ce_patch_name)
end end
def ee_branch def ee_branch
...@@ -146,15 +165,18 @@ module Gitlab ...@@ -146,15 +165,18 @@ module Gitlab
end end
def ee_patch_full_path def ee_patch_full_path
@ee_patch_full_path ||= File.expand_path(ee_patch_name, check_dir) @ee_patch_full_path ||= patches_dir.join(ee_patch_name)
end end
def step(desc, cmd = nil) def step(desc, cmd = nil)
puts "\n=> #{desc}\n" puts "\n=> #{desc}\n"
if cmd if cmd
start = Time.now
puts "\n$ #{cmd.join(' ')}" puts "\n$ #{cmd.join(' ')}"
command(cmd) status = command(cmd)
puts "\nFinished in #{Time.now - start} seconds"
status
end end
end end
...@@ -165,12 +187,12 @@ module Gitlab ...@@ -165,12 +187,12 @@ module Gitlab
status status
end end
def ce_applies_cleanly_msg(ce_branch) def applies_cleanly_msg(branch)
<<-MSG.strip_heredoc <<-MSG.strip_heredoc
================================================================= =================================================================
🎉 Congratulations!! 🎉 🎉 Congratulations!! 🎉
The #{ce_branch} branch applies cleanly to EE/master! The #{branch} branch applies cleanly to EE/master!
Much ❤️!! Much ❤️!!
=================================================================\n =================================================================\n
...@@ -211,7 +233,7 @@ module Gitlab ...@@ -211,7 +233,7 @@ module Gitlab
# In the EE repo # In the EE repo
$ git fetch origin $ git fetch origin
$ git checkout -b #{ee_branch} FETCH_HEAD $ git checkout -b #{ee_branch} origin/master
$ git fetch #{ce_repo} #{ce_branch} $ git fetch #{ce_repo} #{ce_branch}
$ git cherry-pick SHA # Repeat for all the commits you want to pick $ git cherry-pick SHA # Repeat for all the commits you want to pick
...@@ -245,17 +267,5 @@ module Gitlab ...@@ -245,17 +267,5 @@ module Gitlab
=================================================================\n =================================================================\n
MSG MSG
end end
def ee_applies_cleanly_msg
<<-MSG.strip_heredoc
=================================================================
🎉 Congratulations!! 🎉
The #{ee_branch} branch applies cleanly to EE/master!
Much ❤️!!
=================================================================\n
MSG
end
end end
end end
namespace :gitlab do namespace :gitlab do
namespace :dev do namespace :dev do
desc 'Checks if the branch would apply cleanly to EE' desc 'Checks if the branch would apply cleanly to EE'
task ee_compat_check: :environment do task :ee_compat_check, [:branch] => :environment do |_, args|
return if defined?(Gitlab::License) opts =
return unless ENV['CI'] if ENV['CI']
{
branch: ENV['CI_BUILD_REF_NAME'],
ce_repo: ENV['CI_BUILD_REPO']
}
else
unless args[:branch]
puts "Must specify a branch as an argument".color(:red)
exit 1
end
args
end
success = if Gitlab::EeCompatCheck.new(opts || {}).check
Gitlab::EeCompatCheck.new(
branch: ENV['CI_BUILD_REF_NAME'],
check_dir: File.expand_path('ee-compat-check', __dir__),
ce_repo: ENV['CI_BUILD_REPO']
).check
if success
exit 0 exit 0
else else
exit 1 exit 1
......
...@@ -18,7 +18,7 @@ describe GroupLabel, models: true do ...@@ -18,7 +18,7 @@ describe GroupLabel, models: true do
end end
describe '#to_reference' do describe '#to_reference' do
let(:label) { create(:group_label) } let(:label) { create(:group_label, title: 'feature') }
context 'using id' do context 'using id' do
it 'returns a String reference to the object' do it 'returns a String reference to the object' do
......
...@@ -113,6 +113,26 @@ describe Repository, models: true do ...@@ -113,6 +113,26 @@ describe Repository, models: true do
end end
end end
describe '#ref_exists?' do
context 'when ref exists' do
it 'returns true' do
expect(repository.ref_exists?('refs/heads/master')).to be true
end
end
context 'when ref does not exist' do
it 'returns false' do
expect(repository.ref_exists?('refs/heads/non-existent')).to be false
end
end
context 'when ref format is incorrect' do
it 'returns false' do
expect(repository.ref_exists?('refs/heads/invalid:master')).to be false
end
end
end
describe '#last_commit_for_path' do describe '#last_commit_for_path' do
subject { repository.last_commit_for_path(sample_commit.id, '.gitignore').id } subject { repository.last_commit_for_path(sample_commit.id, '.gitignore').id }
...@@ -197,6 +217,35 @@ describe Repository, models: true do ...@@ -197,6 +217,35 @@ describe Repository, models: true do
end end
end end
describe '#commit' do
context 'when ref exists' do
it 'returns commit object' do
expect(repository.commit('master'))
.to be_an_instance_of Commit
end
end
context 'when ref does not exist' do
it 'returns nil' do
expect(repository.commit('non-existent-ref')).to be_nil
end
end
context 'when ref is not valid' do
context 'when preceding tree element exists' do
it 'returns nil' do
expect(repository.commit('master:ref')).to be_nil
end
end
context 'when preceding tree element does not exist' do
it 'returns nil' do
expect(repository.commit('non-existent:ref')).to be_nil
end
end
end
end
describe "#commit_dir" do describe "#commit_dir" do
it "commits a change that creates a new directory" do it "commits a change that creates a new directory" do
expect do expect do
......
...@@ -68,6 +68,24 @@ describe API::API, api: true do ...@@ -68,6 +68,24 @@ describe API::API, api: true do
end end
end end
describe 'GET /groups/owned' do
context 'when unauthenticated' do
it 'returns authentication error' do
get api('/groups/owned')
expect(response).to have_http_status(401)
end
end
context 'when authenticated as group owner' do
it 'returns an array of groups the user owns' do
get api('/groups/owned', user2)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.first['name']).to eq(group2.name)
end
end
end
describe "GET /groups/:id" do describe "GET /groups/:id" do
context "when authenticated as user" do context "when authenticated as user" do
it "returns one of user1's groups" do it "returns one of user1's groups" do
......
...@@ -15,7 +15,7 @@ describe API::API, api: true do ...@@ -15,7 +15,7 @@ describe API::API, api: true do
describe 'GET /projects/:id/labels' do describe 'GET /projects/:id/labels' do
it 'returns all available labels to the project' do it 'returns all available labels to the project' do
group = create(:group) group = create(:group)
group_label = create(:group_label, group: group) group_label = create(:group_label, title: 'feature', group: group)
project.update(group: group) project.update(group: group)
expected_keys = [ expected_keys = [
'id', 'name', 'color', 'description', 'id', 'name', 'color', 'description',
......
...@@ -123,6 +123,15 @@ describe API::API, api: true do ...@@ -123,6 +123,15 @@ describe API::API, api: true do
expect(json_response['title']).to eq('updated title') expect(json_response['title']).to eq('updated title')
end end
it 'removes a due date if nil is passed' do
milestone.update!(due_date: "2016-08-05")
put api("/projects/#{project.id}/milestones/#{milestone.id}", user), due_date: nil
expect(response).to have_http_status(200)
expect(json_response['due_date']).to be_nil
end
it 'returns a 404 error if milestone id not found' do it 'returns a 404 error if milestone id not found' do
put api("/projects/#{project.id}/milestones/1234", user), put api("/projects/#{project.id}/milestones/1234", user),
title: 'updated title' title: 'updated title'
......
...@@ -175,6 +175,30 @@ describe API::API, api: true do ...@@ -175,6 +175,30 @@ describe API::API, api: true do
end end
end end
describe 'GET /projects/owned' do
before do
project3
project4
end
context 'when unauthenticated' do
it 'returns authentication error' do
get api('/projects/owned')
expect(response).to have_http_status(401)
end
end
context 'when authenticated as project owner' do
it 'returns an array of projects the user owns' do
get api('/projects/owned', user4)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.first['name']).to eq(project4.name)
expect(json_response.first['owner']['username']).to eq(user4.username)
end
end
end
describe 'GET /projects/visible' do describe 'GET /projects/visible' do
let(:public_project) { create(:project, :public) } let(:public_project) { create(:project, :public) }
......
...@@ -226,7 +226,7 @@ describe API::Runners, api: true do ...@@ -226,7 +226,7 @@ describe API::Runners, api: true do
context 'authorized user' do context 'authorized user' do
context 'when runner is shared' do context 'when runner is shared' do
it 'does not update runner' do it 'does not update runner' do
put api("/runners/#{shared_runner.id}", user) put api("/runners/#{shared_runner.id}", user), description: 'test'
expect(response).to have_http_status(403) expect(response).to have_http_status(403)
end end
...@@ -234,7 +234,7 @@ describe API::Runners, api: true do ...@@ -234,7 +234,7 @@ describe API::Runners, api: true do
context 'when runner is not shared' do context 'when runner is not shared' do
it 'does not update runner without access to it' do it 'does not update runner without access to it' do
put api("/runners/#{specific_runner.id}", user2) put api("/runners/#{specific_runner.id}", user2), description: 'test'
expect(response).to have_http_status(403) expect(response).to have_http_status(403)
end end
......
...@@ -67,22 +67,24 @@ describe API::API, api: true do ...@@ -67,22 +67,24 @@ describe API::API, api: true do
end end
context "when empty password" do context "when empty password" do
it "returns authentication error" do it "returns authentication error with email" do
post api("/session"), email: user.email post api("/session"), email: user.email
expect(response).to have_http_status(401)
expect(json_response['email']).to be_nil expect(response).to have_http_status(400)
expect(json_response['private_token']).to be_nil end
it "returns authentication error with username" do
post api("/session"), email: user.username
expect(response).to have_http_status(400)
end end
end end
context "when empty name" do context "when empty name" do
it "returns authentication error" do it "returns authentication error" do
post api("/session"), password: user.password post api("/session"), password: user.password
expect(response).to have_http_status(401)
expect(json_response['email']).to be_nil expect(response).to have_http_status(400)
expect(json_response['private_token']).to be_nil
end end
end end
end end
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment