BigW Consortium Gitlab

builds.rb 8.15 KB
Newer Older
1 2 3 4 5
module API
  # Projects builds API
  class Builds < Grape::API
    before { authenticate! }

Robert Schilling committed
6 7 8
    params do
      requires :id, type: String, desc: 'The ID of a project'
    end
9
    resource :projects do
Robert Schilling committed
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
      helpers do
        params :optional_scope do
          optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show',
                           values:  ['pending', 'running', 'failed', 'success', 'canceled'],
                           coerce_with: ->(scope) {
                             if scope.is_a?(String)
                               [scope]
                             elsif scope.is_a?(Hashie::Mash)
                               scope.values
                             else
                               ['unknown']
                             end
                           }
        end
      end

      desc 'Get a project builds' do
        success Entities::Build
      end
      params do
        use :optional_scope
      end
32
      get ':id/builds' do
33 34
        builds = user_project.builds.order('id DESC')
        builds = filter_builds(builds, params[:scope])
35 36

        present paginate(builds), with: Entities::Build,
37
                                  user_can_download_artifacts: can?(current_user, :read_build, user_project)
38
      end
39

Robert Schilling committed
40 41 42 43 44 45 46
      desc 'Get builds for a specific commit of a project' do
        success Entities::Build
      end
      params do
        requires :sha,   type: String, desc: 'The SHA id of a commit'
        use :optional_scope
      end
47
      get ':id/repository/commits/:sha/builds' do
48 49
        authorize_read_builds!

50 51
        return not_found! unless user_project.commit(params[:sha])

52 53
        pipelines = user_project.pipelines.where(sha: params[:sha])
        builds = user_project.builds.where(pipeline: pipelines).order('id DESC')
54
        builds = filter_builds(builds, params[:scope])
55

56
        present paginate(builds), with: Entities::Build,
57
                                  user_can_download_artifacts: can?(current_user, :read_build, user_project)
58 59
      end

Robert Schilling committed
60 61 62 63 64 65
      desc 'Get a specific build of a project' do
        success Entities::Build
      end
      params do
        requires :build_id, type: Integer, desc: 'The ID of a build'
      end
66
      get ':id/builds/:build_id' do
67 68
        authorize_read_builds!

69
        build = get_build!(params[:build_id])
70

71
        present build, with: Entities::Build,
72
                       user_can_download_artifacts: can?(current_user, :read_build, user_project)
73 74
      end

Robert Schilling committed
75 76 77 78 79 80
      desc 'Download the artifacts file from build' do
        detail 'This feature was introduced in GitLab 8.5'
      end
      params do
        requires :build_id, type: Integer, desc: 'The ID of a build'
      end
81 82 83
      get ':id/builds/:build_id/artifacts' do
        authorize_read_builds!

84
        build = get_build!(params[:build_id])
85

86
        present_artifacts!(build.artifacts_file)
87
      end
88

Robert Schilling committed
89 90 91 92 93 94 95
      desc 'Download the artifacts file from build' do
        detail 'This feature was introduced in GitLab 8.10'
      end
      params do
        requires :ref_name, type: String, desc: 'The ref from repository'
        requires :job,      type: String, desc: 'The name for the build'
      end
96 97
      get ':id/builds/artifacts/:ref_name/download',
        requirements: { ref_name: /.+/ } do
98 99
        authorize_read_builds!

100 101
        builds = user_project.latest_successful_builds_for(params[:ref_name])
        latest_build = builds.find_by!(name: params[:job])
102

103
        present_artifacts!(latest_build.artifacts_file)
104 105
      end

106 107 108
      # TODO: We should use `present_file!` and leave this implementation for backward compatibility (when build trace
      #       is saved in the DB instead of file). But before that, we need to consider how to replace the value of
      #       `runners_token` with some mask (like `xxxxxx`) when sending trace file directly by workhorse.
Robert Schilling committed
109 110 111 112
      desc 'Get a trace of a specific build of a project'
      params do
        requires :build_id, type: Integer, desc: 'The ID of a build'
      end
113
      get ':id/builds/:build_id/trace' do
114 115
        authorize_read_builds!

116
        build = get_build!(params[:build_id])
117 118 119 120

        header 'Content-Disposition', "infile; filename=\"#{build.id}.log\""
        content_type 'text/plain'
        env['api.format'] = :binary
121

122 123
        trace = build.trace
        body trace
124
      end
125

Robert Schilling committed
126 127 128 129 130 131
      desc 'Cancel a specific build of a project' do
        success Entities::Build
      end
      params do
        requires :build_id, type: Integer, desc: 'The ID of a build'
      end
132
      post ':id/builds/:build_id/cancel' do
133
        authorize_update_builds!
134

135
        build = get_build!(params[:build_id])
136 137 138

        build.cancel

139
        present build, with: Entities::Build,
140
                       user_can_download_artifacts: can?(current_user, :read_build, user_project)
141 142
      end

Robert Schilling committed
143 144 145 146 147 148
      desc 'Retry a specific build of a project' do
        success Entities::Build
      end
      params do
        requires :build_id, type: Integer, desc: 'The ID of a build'
      end
149
      post ':id/builds/:build_id/retry' do
150
        authorize_update_builds!
151

152
        build = get_build!(params[:build_id])
153
        return forbidden!('Build is not retryable') unless build.retryable?
154

155
        build = Ci::Build.retry(build, current_user)
156

157
        present build, with: Entities::Build,
158
                       user_can_download_artifacts: can?(current_user, :read_build, user_project)
159
      end
160

Robert Schilling committed
161 162 163 164 165 166
      desc 'Erase build (remove artifacts and build trace)' do
        success Entities::Build
      end
      params do
        requires :build_id, type: Integer, desc: 'The ID of a build'
      end
167
      post ':id/builds/:build_id/erase' do
168
        authorize_update_builds!
169

170
        build = get_build!(params[:build_id])
171
        return forbidden!('Build is not erasable!') unless build.erasable?
172

173
        build.erase(erased_by: current_user)
174 175 176
        present build, with: Entities::Build,
                       user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project)
      end
177

Robert Schilling committed
178 179 180 181 182 183
      desc 'Keep the artifacts to prevent them from being deleted' do
        success Entities::Build
      end
      params do
        requires :build_id, type: Integer, desc: 'The ID of a build'
      end
184 185 186
      post ':id/builds/:build_id/artifacts/keep' do
        authorize_update_builds!

187 188
        build = get_build!(params[:build_id])
        return not_found!(build) unless build.artifacts?
189 190 191 192 193

        build.keep_artifacts!

        status 200
        present build, with: Entities::Build,
Kamil Trzcinski committed
194
                       user_can_download_artifacts: can?(current_user, :read_build, user_project)
195
      end
196 197 198 199 200 201 202 203 204 205 206 207 208

      desc 'Trigger a manual build' do
        success Entities::Build
        detail 'This feature was added in GitLab 8.11'
      end
      params do
        requires :build_id, type: Integer, desc: 'The ID of a Build'
      end
      post ":id/builds/:build_id/play" do
        authorize_read_builds!

        build = get_build!(params[:build_id])

Z.J. van de Weg committed
209 210 211 212 213 214 215
        bad_request!("Unplayable Build") unless build.playable?

        build.play(current_user)

        status 200
        present build, with: Entities::Build,
                       user_can_download_artifacts: can?(current_user, :read_build, user_project)
216
      end
217 218 219 220
    end

    helpers do
      def get_build(id)
221
        user_project.builds.find_by(id: id.to_i)
222
      end
223

224 225 226 227
      def get_build!(id)
        get_build(id) || not_found!
      end

228
      def present_artifacts!(artifacts_file)
229 230 231 232 233 234 235 236 237
        if !artifacts_file.file_storage?
          redirect_to(build.artifacts_file.url)
        elsif artifacts_file.exists?
          present_file!(artifacts_file.path, artifacts_file.filename)
        else
          not_found!
        end
      end

238
      def filter_builds(builds, scope)
239 240
        return builds if scope.nil? || scope.empty?

241
        available_statuses = ::CommitStatus::AVAILABLE_STATUSES
242

243 244
        unknown = scope - available_statuses
        render_api_error!('Scope contains invalid value(s)', 400) unless unknown.empty?
245

246
        builds.where(status: available_statuses && scope)
247
      end
248

249 250 251 252 253 254
      def authorize_read_builds!
        authorize! :read_build, user_project
      end

      def authorize_update_builds!
        authorize! :update_build, user_project
255
      end
256 257 258
    end
  end
end