diff options
49 files changed, 1780 insertions, 218 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 195783454f9..34348247e91 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -213,11 +213,24 @@ rake downtime_check: *exec rake ee_compat_check: <<: *exec only: - - branches + - branches@gitlab-org/gitlab-ce + - branches@gitlab/gitlabhq except: - master - tags + - /^[\d-]+-stable(-ee)?$/ 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: stage: test diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 371cc3787fb..533af80aee0 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -18,7 +18,9 @@ class Projects::PipelinesController < Projects::ApplicationController end 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? render 'new' return diff --git a/app/models/repository.rb b/app/models/repository.rb index 7d06ce1e85b..fe991904601 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -84,15 +84,17 @@ class Repository def commit(ref = 'HEAD') return nil unless exists? + commit = if ref.is_a?(Gitlab::Git::Commit) ref else Gitlab::Git::Commit.find(raw_repository, ref) end + commit = ::Commit.new(commit, @project) if commit commit - rescue Rugged::OdbError + rescue Rugged::OdbError, Rugged::TreeError nil end @@ -232,6 +234,8 @@ class Repository def ref_exists?(ref) rugged.references.exist?(ref) + rescue Rugged::ReferenceError + false end def update_ref!(name, newrev, oldrev) @@ -270,11 +274,7 @@ class Repository end def kept_around?(sha) - begin - ref_exists?(keep_around_ref_name(sha)) - rescue Rugged::ReferenceError - false - end + ref_exists?(keep_around_ref_name(sha)) end def tag_names diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 9f80a974d64..34855c54176 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -6,7 +6,7 @@ - note_count = notes.user.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 %li.commit.js-toggle-container{ id: "commit-#{commit.short_id}" } diff --git a/app/views/projects/imports/show.html.haml b/app/views/projects/imports/show.html.haml index 4d8ee562e6a..c52b3860636 100644 --- a/app/views/projects/imports/show.html.haml +++ b/app/views/projects/imports/show.html.haml @@ -1,4 +1,4 @@ -- page_title "Import in progress" +- page_title @project.forked? ? "Forking in progress" : "Import in progress" .save-project-loader .center %h2 diff --git a/changelogs/unreleased/feature-api_owned_resource.yml b/changelogs/unreleased/feature-api_owned_resource.yml new file mode 100644 index 00000000000..9c270e4ecf4 --- /dev/null +++ b/changelogs/unreleased/feature-api_owned_resource.yml @@ -0,0 +1,4 @@ +--- +title: Add api endpoint `/groups/owned` +merge_request: 7103 +author: Borja Aparicio diff --git a/changelogs/unreleased/fix-cache-for-commit-status.yml b/changelogs/unreleased/fix-cache-for-commit-status.yml new file mode 100644 index 00000000000..eb4e96e75ae --- /dev/null +++ b/changelogs/unreleased/fix-cache-for-commit-status.yml @@ -0,0 +1,4 @@ +--- +title: Fix cache for commit status in commits list to respect branches +merge_request: 7372 +author: diff --git a/changelogs/unreleased/fix-error-when-invalid-branch-for-new-pipeline-used.yml b/changelogs/unreleased/fix-error-when-invalid-branch-for-new-pipeline-used.yml new file mode 100644 index 00000000000..ad6aa214f0f --- /dev/null +++ b/changelogs/unreleased/fix-error-when-invalid-branch-for-new-pipeline-used.yml @@ -0,0 +1,4 @@ +--- +title: Fix error when using invalid branch name when creating a new pipeline +merge_request: 7324 +author: diff --git a/changelogs/unreleased/forking-in-progress-title.yml b/changelogs/unreleased/forking-in-progress-title.yml new file mode 100644 index 00000000000..4b9684844b3 --- /dev/null +++ b/changelogs/unreleased/forking-in-progress-title.yml @@ -0,0 +1,4 @@ +--- +title: Use 'Forking in progress' title when appropriate +merge_request: 7394 +author: Philip Karpiak diff --git a/doc/api/groups.md b/doc/api/groups.md index b56d74d25e0..45a3118f27a 100644 --- a/doc/api/groups.md +++ b/doc/api/groups.md @@ -26,6 +26,15 @@ GET /groups 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 Get a list of projects in this group. diff --git a/doc/university/README.md b/doc/university/README.md index 49714e4fb59..4569bc72797 100644 --- a/doc/university/README.md +++ b/doc/university/README.md @@ -212,5 +212,8 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project 1. [Support Path](support/README.md) 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. [Client Assessment of GitLab versus GitHub](https://docs.google.com/a/gitlab.com/spreadsheets/d/18cRF9Y5I6I7Z_ab6qhBEW55YpEMyU4PitZYjomVHM-M/edit?usp=sharing) diff --git a/doc/university/training/gitlab_flow.md b/doc/university/training/gitlab_flow.md new file mode 100755 index 00000000000..a7db1f2e069 --- /dev/null +++ b/doc/university/training/gitlab_flow.md @@ -0,0 +1,53 @@ +# 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) diff --git a/doc/university/training/gitlab_flow/feature_branches.png b/doc/university/training/gitlab_flow/feature_branches.png Binary files differnew file mode 100644 index 00000000000..88addb623ee --- /dev/null +++ b/doc/university/training/gitlab_flow/feature_branches.png diff --git a/doc/university/training/gitlab_flow/production_branch.png b/doc/university/training/gitlab_flow/production_branch.png Binary files differnew file mode 100644 index 00000000000..33fb26dd621 --- /dev/null +++ b/doc/university/training/gitlab_flow/production_branch.png diff --git a/doc/university/training/gitlab_flow/release_branches.png b/doc/university/training/gitlab_flow/release_branches.png Binary files differnew file mode 100644 index 00000000000..da7ae53413a --- /dev/null +++ b/doc/university/training/gitlab_flow/release_branches.png diff --git a/doc/university/training/index.md b/doc/university/training/index.md new file mode 100755 index 00000000000..03179ff5a77 --- /dev/null +++ b/doc/university/training/index.md @@ -0,0 +1,6 @@ +# 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. diff --git a/doc/university/training/logo.png b/doc/university/training/logo.png Binary files differnew file mode 100644 index 00000000000..cc831790405 --- /dev/null +++ b/doc/university/training/logo.png diff --git a/doc/university/training/topics/additional_resources.md b/doc/university/training/topics/additional_resources.md new file mode 100755 index 00000000000..1ee615432aa --- /dev/null +++ b/doc/university/training/topics/additional_resources.md @@ -0,0 +1,8 @@ +## 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) diff --git a/doc/university/training/topics/agile_git.md b/doc/university/training/topics/agile_git.md new file mode 100755 index 00000000000..e6e4fea9b51 --- /dev/null +++ b/doc/university/training/topics/agile_git.md @@ -0,0 +1,33 @@ +# 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. diff --git a/doc/university/training/topics/bisect.md b/doc/university/training/topics/bisect.md new file mode 100755 index 00000000000..a60c4365e0c --- /dev/null +++ b/doc/university/training/topics/bisect.md @@ -0,0 +1,81 @@ +# 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 +``` diff --git a/doc/university/training/topics/cherry_picking.md b/doc/university/training/topics/cherry_picking.md new file mode 100755 index 00000000000..af7a70a2818 --- /dev/null +++ b/doc/university/training/topics/cherry_picking.md @@ -0,0 +1,39 @@ +# 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> +``` diff --git a/doc/university/training/topics/env_setup.md b/doc/university/training/topics/env_setup.md new file mode 100755 index 00000000000..8149379b36f --- /dev/null +++ b/doc/university/training/topics/env_setup.md @@ -0,0 +1,60 @@ +# 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 +``` diff --git a/doc/university/training/topics/explore_gitlab.md b/doc/university/training/topics/explore_gitlab.md new file mode 100755 index 00000000000..b65457728c0 --- /dev/null +++ b/doc/university/training/topics/explore_gitlab.md @@ -0,0 +1,10 @@ +# Explore GitLab projects + +---------- + +- Dashboard +- User Preferences +- Issues +- Milestones and Labels +- Manage project members +- Project settings diff --git a/doc/university/training/topics/feature_branching.md b/doc/university/training/topics/feature_branching.md new file mode 100755 index 00000000000..4b34406ea75 --- /dev/null +++ b/doc/university/training/topics/feature_branching.md @@ -0,0 +1,32 @@ +# 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 +``` diff --git a/doc/university/training/topics/getting_started.md b/doc/university/training/topics/getting_started.md new file mode 100755 index 00000000000..ec7bb2631aa --- /dev/null +++ b/doc/university/training/topics/getting_started.md @@ -0,0 +1,95 @@ +# 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 diff --git a/doc/university/training/topics/git_add.md b/doc/university/training/topics/git_add.md new file mode 100755 index 00000000000..9ffb4b9c859 --- /dev/null +++ b/doc/university/training/topics/git_add.md @@ -0,0 +1,33 @@ +# 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/ +``` diff --git a/doc/university/training/topics/git_intro.md b/doc/university/training/topics/git_intro.md new file mode 100755 index 00000000000..ca1ff29d93b --- /dev/null +++ b/doc/university/training/topics/git_intro.md @@ -0,0 +1,24 @@ +# 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 diff --git a/doc/university/training/topics/git_log.md b/doc/university/training/topics/git_log.md new file mode 100755 index 00000000000..32ebceff491 --- /dev/null +++ b/doc/university/training/topics/git_log.md @@ -0,0 +1,57 @@ +# 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" +``` diff --git a/doc/university/training/topics/gitlab_flow.md b/doc/university/training/topics/gitlab_flow.md new file mode 100755 index 00000000000..8e5d3baf959 --- /dev/null +++ b/doc/university/training/topics/gitlab_flow.md @@ -0,0 +1,53 @@ +# 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) diff --git a/doc/university/training/topics/merge_conflicts.md b/doc/university/training/topics/merge_conflicts.md new file mode 100755 index 00000000000..77807b3e7ef --- /dev/null +++ b/doc/university/training/topics/merge_conflicts.md @@ -0,0 +1,70 @@ +# 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/ diff --git a/doc/university/training/topics/merge_requests.md b/doc/university/training/topics/merge_requests.md new file mode 100755 index 00000000000..5b446f02f63 --- /dev/null +++ b/doc/university/training/topics/merge_requests.md @@ -0,0 +1,43 @@ +# 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) diff --git a/doc/university/training/topics/rollback_commits.md b/doc/university/training/topics/rollback_commits.md new file mode 100755 index 00000000000..cf647284604 --- /dev/null +++ b/doc/university/training/topics/rollback_commits.md @@ -0,0 +1,81 @@ +# 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) +``` diff --git a/doc/university/training/topics/stash.md b/doc/university/training/topics/stash.md new file mode 100755 index 00000000000..c1bdda32645 --- /dev/null +++ b/doc/university/training/topics/stash.md @@ -0,0 +1,86 @@ +# 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 +``` diff --git a/doc/university/training/topics/subtree.md b/doc/university/training/topics/subtree.md new file mode 100755 index 00000000000..5d869af64c1 --- /dev/null +++ b/doc/university/training/topics/subtree.md @@ -0,0 +1,55 @@ +## 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 +``` diff --git a/doc/university/training/topics/tags.md b/doc/university/training/topics/tags.md new file mode 100755 index 00000000000..e9607b5a875 --- /dev/null +++ b/doc/university/training/topics/tags.md @@ -0,0 +1,38 @@ +# 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 +``` diff --git a/doc/university/training/topics/unstage.md b/doc/university/training/topics/unstage.md new file mode 100755 index 00000000000..17dbb64b9e6 --- /dev/null +++ b/doc/university/training/topics/unstage.md @@ -0,0 +1,31 @@ +# 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 +``` diff --git a/doc/university/training/user_training.md b/doc/university/training/user_training.md new file mode 100755 index 00000000000..35afe73708f --- /dev/null +++ b/doc/university/training/user_training.md @@ -0,0 +1,392 @@ +# 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) diff --git a/lib/api/groups.rb b/lib/api/groups.rb index a13e353b7f5..40644fc2adf 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -26,6 +26,16 @@ module API present @groups, with: Entities::Group 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. # # Parameters: diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb index 9b73f6826cf..8984cf8cdcd 100644 --- a/lib/api/milestones.rb +++ b/lib/api/milestones.rb @@ -11,19 +11,25 @@ module API else milestones 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 + params do + requires :id, type: String, desc: 'The ID of a project' + end resource :projects do - # Get a list of project milestones - # - # Parameters: - # id (required) - The ID of a project - # state (optional) - Return "active" or "closed" milestones - # Example Request: - # GET /projects/:id/milestones - # GET /projects/:id/milestones?iid=42 - # GET /projects/:id/milestones?state=active - # GET /projects/:id/milestones?state=closed + desc 'Get a list of project milestones' do + success Entities::Milestone + end + params do + optional :state, type: String, values: %w[active closed all], default: 'all', + desc: 'Return "active", "closed", or "all" milestones' + optional :iid, type: Integer, desc: 'The IID of the milestone' + end get ":id/milestones" do authorize! :read_milestone, user_project @@ -34,34 +40,31 @@ module API present paginate(milestones), with: Entities::Milestone end - # Get a single project milestone - # - # Parameters: - # id (required) - The ID of a project - # milestone_id (required) - The ID of a project milestone - # Example Request: - # GET /projects/:id/milestones/:milestone_id + desc 'Get a single project milestone' do + success Entities::Milestone + end + params do + requires :milestone_id, type: Integer, desc: 'The ID of a project milestone' + end get ":id/milestones/:milestone_id" do authorize! :read_milestone, user_project - @milestone = user_project.milestones.find(params[:milestone_id]) - present @milestone, with: Entities::Milestone + milestone = user_project.milestones.find(params[:milestone_id]) + present milestone, with: Entities::Milestone end - # Create a new project milestone - # - # Parameters: - # id (required) - The ID of the project - # title (required) - The title of the milestone - # description (optional) - The description of the milestone - # due_date (optional) - The due date of the milestone - # Example Request: - # POST /projects/:id/milestones + desc 'Create a new project milestone' do + success Entities::Milestone + end + params do + requires :title, type: String, desc: 'The title of the milestone' + use :optional_params + end post ":id/milestones" do authorize! :admin_milestone, user_project - required_attributes! [:title] - attrs = attributes_for_keys [:title, :description, :due_date] - milestone = ::Milestones::CreateService.new(user_project, current_user, attrs).execute + milestone_params = declared(params, include_parent_namespaces: false) + + milestone = ::Milestones::CreateService.new(user_project, current_user, milestone_params).execute if milestone.valid? present milestone, with: Entities::Milestone @@ -70,22 +73,23 @@ module API end end - # Update an existing project milestone - # - # Parameters: - # id (required) - The ID of a project - # milestone_id (required) - The ID of a project milestone - # title (optional) - The title of a milestone - # description (optional) - The description of a milestone - # due_date (optional) - The due date of a milestone - # state_event (optional) - The state event of the milestone (close|activate) - # Example Request: - # PUT /projects/:id/milestones/:milestone_id + desc 'Update an existing project milestone' do + success Entities::Milestone + end + params do + requires :milestone_id, type: Integer, desc: 'The ID of a project milestone' + optional :title, type: String, desc: 'The title of the milestone' + optional :state_event, type: String, values: %w[close activate], + desc: 'The state event of the milestone ' + use :optional_params + at_least_one_of :title, :description, :due_date, :state_event + end put ":id/milestones/:milestone_id" do authorize! :admin_milestone, user_project - attrs = attributes_for_keys [:title, :description, :due_date, :state_event] - milestone = user_project.milestones.find(params[:milestone_id]) - milestone = ::Milestones::UpdateService.new(user_project, current_user, attrs).execute(milestone) + milestone_params = declared(params, include_parent_namespaces: false, include_missing: false) + + 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? present milestone, with: Entities::Milestone @@ -94,21 +98,20 @@ module API end end - # Get all issues for a single project milestone - # - # Parameters: - # id (required) - The ID of a project - # milestone_id (required) - The ID of a project milestone - # Example Request: - # GET /projects/:id/milestones/:milestone_id/issues + desc 'Get all issues for a single project milestone' do + success Entities::Issue + end + params do + requires :milestone_id, type: Integer, desc: 'The ID of a project milestone' + end get ":id/milestones/:milestone_id/issues" do authorize! :read_milestone, user_project - @milestone = user_project.milestones.find(params[:milestone_id]) + milestone = user_project.milestones.find(params[:milestone_id]) finder_params = { project_id: user_project.id, - milestone_title: @milestone.title + milestone_title: milestone.title } issues = IssuesFinder.new(current_user, finder_params).execute diff --git a/lib/api/runners.rb b/lib/api/runners.rb index ecc8f2fc5a2..84c19c432b0 100644 --- a/lib/api/runners.rb +++ b/lib/api/runners.rb @@ -1,34 +1,39 @@ module API - # Runners API class Runners < Grape::API before { authenticate! } resource :runners do - # Get runners available for user - # - # Example Request: - # GET /runners + desc 'Get runners available for user' do + success Entities::Runner + end + params do + optional :scope, type: String, values: %w[active paused online], + desc: 'The scope of specific runners to show' + end get do runners = filter_runners(current_user.ci_authorized_runners, params[:scope], without: ['specific', 'shared']) present paginate(runners), with: Entities::Runner end - # Get all runners - shared and specific - # - # Example Request: - # GET /runners/all + desc 'Get all runners - shared and specific' do + success Entities::Runner + end + 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 authenticated_as_admin! runners = filter_runners(Ci::Runner.all, params[:scope]) present paginate(runners), with: Entities::Runner end - # Get runner's details - # - # Parameters: - # id (required) - The ID of ther runner - # Example Request: - # GET /runners/:id + desc "Get runner's details" do + success Entities::RunnerDetails + end + params do + requires :id, type: Integer, desc: 'The ID of the runner' + end get ':id' do runner = get_runner(params[:id]) authenticate_show_runner!(runner) @@ -36,33 +41,37 @@ module API present runner, with: Entities::RunnerDetails, current_user: current_user end - # Update runner's details - # - # Parameters: - # id (required) - The ID of ther runner - # description (optional) - Runner's description - # active (optional) - Runner's status - # tag_list (optional) - Array of tags for runner - # Example Request: - # PUT /runners/:id + desc "Update runner's details" do + success Entities::RunnerDetails + end + params do + requires :id, type: Integer, desc: 'The ID of the runner' + optional :description, type: String, desc: 'The description of the runner' + optional :active, type: Boolean, desc: 'The state of a runner' + optional :tag_list, type: Array[String], desc: 'The list of tags for a runner' + 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 - runner = get_runner(params[:id]) + runner = get_runner(params.delete(:id)) authenticate_update_runner!(runner) - attrs = attributes_for_keys [:description, :active, :tag_list, :run_untagged, :locked] - if runner.update(attrs) + runner_params = declared(params, include_missing: false) + + if runner.update(runner_params) present runner, with: Entities::RunnerDetails, current_user: current_user else render_validation_error!(runner) end end - # Remove runner - # - # Parameters: - # id (required) - The ID of ther runner - # Example Request: - # DELETE /runners/:id + desc 'Remove a runner' do + success Entities::Runner + end + params do + requires :id, type: Integer, desc: 'The ID of the runner' + end delete ':id' do runner = get_runner(params[:id]) authenticate_delete_runner!(runner) @@ -72,28 +81,31 @@ module API end end + params do + requires :id, type: String, desc: 'The ID of a project' + end resource :projects do before { authorize_admin_project } - # Get runners available for project - # - # Example Request: - # GET /projects/:id/runners + desc 'Get runners available for project' do + success Entities::Runner + end + 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 runners = filter_runners(Ci::Runner.owned_or_shared(user_project.id), params[:scope]) present paginate(runners), with: Entities::Runner end - # Enable runner for project - # - # Parameters: - # id (required) - The ID of the project - # runner_id (required) - The ID of the runner - # Example Request: - # POST /projects/:id/runners/:runner_id + desc 'Enable a runner for a project' do + success Entities::Runner + end + params do + requires :runner_id, type: Integer, desc: 'The ID of the runner' + end post ':id/runners' do - required_attributes! [:runner_id] - runner = get_runner(params[:runner_id]) authenticate_enable_runner!(runner) @@ -106,13 +118,12 @@ module API end end - # Disable project's runner - # - # Parameters: - # id (required) - The ID of the project - # runner_id (required) - The ID of the runner - # Example Request: - # DELETE /projects/:id/runners/:runner_id + desc "Disable project's runner" do + success Entities::Runner + end + params do + requires :runner_id, type: Integer, desc: 'The ID of the runner' + end delete ':id/runners/:runner_id' do runner_project = user_project.runner_projects.find_by(runner_id: params[:runner_id]) not_found!('Runner') unless runner_project diff --git a/lib/api/session.rb b/lib/api/session.rb index 55ec66a6d67..d09400b81f5 100644 --- a/lib/api/session.rb +++ b/lib/api/session.rb @@ -1,15 +1,14 @@ module API - # Users API class Session < Grape::API - # Login to get token - # - # Parameters: - # login (*required) - user login - # email (*required) - user email - # password (required) - user password - # - # Example Request: - # POST /session + desc 'Login to get token' do + success Entities::UserLogin + end + params do + optional :login, type: String, desc: 'The username' + optional :email, type: String, desc: 'The email of the user' + requires :password, type: String, desc: 'The password of the user' + at_least_one_of :login, :email + end post "/session" do user = Gitlab::Auth.find_with_user_password(params[:email] || params[:login], params[:password]) diff --git a/lib/gitlab/ee_compat_check.rb b/lib/gitlab/ee_compat_check.rb index b1a6d5fe0f6..f4d1505ea91 100644 --- a/lib/gitlab/ee_compat_check.rb +++ b/lib/gitlab/ee_compat_check.rb @@ -2,39 +2,38 @@ module Gitlab # Checks if a set of migrations requires downtime or not. class EeCompatCheck + CE_REPO = 'https://gitlab.com/gitlab-org/gitlab-ce.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 - @check_dir = check_dir - @ce_repo = ce_repo || 'https://gitlab.com/gitlab-org/gitlab-ce.git' + @ce_repo = ce_repo end def check ensure_ee_repo - delete_patches + ensure_patches_dir generate_patch(ce_branch, ce_patch_full_path) - Dir.chdir(check_dir) do - step("In the #{check_dir} directory") - - step("Pulling latest master", %w[git pull --ff-only origin master]) + Dir.chdir(repo_dir) do + step("In the #{repo_dir} directory") status = catch(:halt_check) do ce_branch_compat_check! - - delete_ee_branch_locally - + delete_ee_branch_locally! ee_branch_presence_check! - ee_branch_compat_check! end - delete_ee_branch_locally - delete_patches + delete_ee_branch_locally! if status.nil? true @@ -47,20 +46,43 @@ module Gitlab private def ensure_ee_repo - if Dir.exist?(check_dir) - step("#{check_dir} already exists") + if Dir.exist?(repo_dir) + step("#{repo_dir} already exists") else - cmd = %W[git clone --branch master --single-branch --depth 1 #{EE_REPO} #{check_dir}] - step("Cloning #{EE_REPO} into #{check_dir}", cmd) + cmd = %W[git clone --branch master --single-branch --depth 200 #{EE_REPO} #{repo_dir}] + step("Cloning #{EE_REPO} into #{repo_dir}", cmd) end end - def ce_branch_compat_check! - cmd = %W[git apply --check #{ce_patch_full_path}] - status = step("Checking if #{ce_patch_name} applies cleanly to EE/master", cmd) + def ensure_patches_dir + FileUtils.mkdir_p(patches_dir) + 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? - puts ce_applies_cleanly_msg(ce_branch) + step("Generating the patch against master in #{patch_path}") + 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) end end @@ -80,10 +102,8 @@ module Gitlab step("Checking out origin/#{ee_branch}", %W[git checkout -b #{ee_branch} FETCH_HEAD]) 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 ee_branch_doesnt_apply_cleanly_msg @@ -91,50 +111,49 @@ module Gitlab end puts - puts ee_applies_cleanly_msg + puts applies_cleanly_msg(ee_branch) end - def generate_patch(branch, filepath) - FileUtils.rm(filepath, force: true) + def check_patch(patch_path) + step("Checking out master", %w[git checkout master]) + step("Reseting to latest master", %w[git reset --hard origin/master]) - depth = 0 - loop do - 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 + step("Checking if #{patch_path} applies cleanly to EE/master") + output, status = Gitlab::Popen.popen(%W[git apply --check #{patch_path}]) - 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") - output, status = Gitlab::Popen.popen(%w[git format-patch FETCH_HEAD --stdout]) - throw(:halt_check, :ko) unless status.zero? + if failed_files.empty? + status = 0 + else + puts "\nConflicting files:" + failed_files.each do |file| + puts " - #{file}" + end + end + end - File.write(filepath, output) - throw(:halt_check, :ko) unless File.exist?(filepath) + status end - def delete_ee_branch_locally + def delete_ee_branch_locally! command(%w[git checkout master]) step("Deleting the local #{ee_branch} branch", %W[git branch -D #{ee_branch}]) 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 @ce_patch_name ||= "#{ce_branch}.patch" end 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 def ee_branch @@ -146,15 +165,18 @@ module Gitlab end 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 def step(desc, cmd = nil) puts "\n=> #{desc}\n" if cmd + start = Time.now puts "\n$ #{cmd.join(' ')}" - command(cmd) + status = command(cmd) + puts "\nFinished in #{Time.now - start} seconds" + status end end @@ -165,12 +187,12 @@ module Gitlab status end - def ce_applies_cleanly_msg(ce_branch) + def applies_cleanly_msg(branch) <<-MSG.strip_heredoc ================================================================= 🎉 Congratulations!! 🎉 - The #{ce_branch} branch applies cleanly to EE/master! + The #{branch} branch applies cleanly to EE/master! Much ❤️!! =================================================================\n @@ -211,7 +233,7 @@ module Gitlab # In the EE repo $ git fetch origin - $ git checkout -b #{ee_branch} FETCH_HEAD + $ git checkout -b #{ee_branch} origin/master $ git fetch #{ce_repo} #{ce_branch} $ git cherry-pick SHA # Repeat for all the commits you want to pick @@ -245,17 +267,5 @@ module Gitlab =================================================================\n MSG 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 diff --git a/lib/tasks/gitlab/dev.rake b/lib/tasks/gitlab/dev.rake index 5ee99dfc810..3117075b08b 100644 --- a/lib/tasks/gitlab/dev.rake +++ b/lib/tasks/gitlab/dev.rake @@ -1,18 +1,22 @@ namespace :gitlab do namespace :dev do desc 'Checks if the branch would apply cleanly to EE' - task ee_compat_check: :environment do - return if defined?(Gitlab::License) - return unless ENV['CI'] + task :ee_compat_check, [:branch] => :environment do |_, args| + opts = + 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 = - 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 + if Gitlab::EeCompatCheck.new(opts || {}).check exit 0 else exit 1 diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 12989d4db53..fe26b4ac18c 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -113,6 +113,26 @@ describe Repository, models: true do 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 subject { repository.last_commit_for_path(sample_commit.id, '.gitignore').id } @@ -197,6 +217,35 @@ describe Repository, models: true do 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 it "commits a change that creates a new directory" do expect do diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 7b47bf5afc1..b29a13b1d8b 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -68,6 +68,24 @@ describe API::API, api: true do 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 context "when authenticated as user" do it "returns one of user1's groups" do diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb index dd192bea432..62327f64e50 100644 --- a/spec/requests/api/milestones_spec.rb +++ b/spec/requests/api/milestones_spec.rb @@ -123,6 +123,15 @@ describe API::API, api: true do expect(json_response['title']).to eq('updated title') 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 put api("/projects/#{project.id}/milestones/1234", user), title: 'updated title' diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 3c8f0ac531a..d6e9fd2c4b2 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -175,6 +175,30 @@ describe API::API, api: true do 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 let(:public_project) { create(:project, :public) } diff --git a/spec/requests/api/runners_spec.rb b/spec/requests/api/runners_spec.rb index f46f016135e..99414270be6 100644 --- a/spec/requests/api/runners_spec.rb +++ b/spec/requests/api/runners_spec.rb @@ -226,7 +226,7 @@ describe API::Runners, api: true do context 'authorized user' do context 'when runner is shared' 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) end @@ -234,7 +234,7 @@ describe API::Runners, api: true do context 'when runner is not shared' 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) end diff --git a/spec/requests/api/session_spec.rb b/spec/requests/api/session_spec.rb index acad1365ace..e3f22b4c578 100644 --- a/spec/requests/api/session_spec.rb +++ b/spec/requests/api/session_spec.rb @@ -67,22 +67,24 @@ describe API::API, api: true do end context "when empty password" do - it "returns authentication error" do + it "returns authentication error with email" do post api("/session"), email: user.email - expect(response).to have_http_status(401) - expect(json_response['email']).to be_nil - expect(json_response['private_token']).to be_nil + expect(response).to have_http_status(400) + end + + it "returns authentication error with username" do + post api("/session"), email: user.username + + expect(response).to have_http_status(400) end end context "when empty name" do it "returns authentication error" do post api("/session"), password: user.password - expect(response).to have_http_status(401) - expect(json_response['email']).to be_nil - expect(json_response['private_token']).to be_nil + expect(response).to have_http_status(400) end end end |