1- import * as fs from 'fs' ;
21import fetch , { RequestInit , Response } from 'node-fetch' ;
3- import { Git } from './Git ' ;
2+ import queryString from 'querystring ' ;
43
54export interface User {
65 id : number ;
@@ -41,6 +40,7 @@ export interface MergeRequest {
4140 state : MergeState ;
4241 force_remove_source_branch : boolean ;
4342 labels : string [ ] ;
43+ squash : boolean ;
4444}
4545
4646interface MergeRequestUpdateData {
@@ -73,6 +73,8 @@ export interface MergeRequestInfo extends MergeRequest {
7373 head_sha : string ,
7474 } ;
7575 pipeline : MergeRequestPipeline | null ;
76+ diverged_commits_count : number ;
77+ rebase_in_progress : boolean ;
7678}
7779
7880export interface DiscussionNote {
@@ -84,16 +86,6 @@ export interface MergeRequestDiscussion {
8486 notes : DiscussionNote [ ] ;
8587}
8688
87- interface Commit {
88- id : string ;
89- }
90-
91- interface Project {
92- id : number ;
93- ssh_url_to_repo : string ;
94- path_with_namespace : string ;
95- }
96-
9789interface Pipeline {
9890 user : {
9991 id : number ,
@@ -110,28 +102,25 @@ export class GitlabApi {
110102
111103 private readonly gitlabUrl : string ;
112104 private readonly authToken : string ;
113- private readonly repositoryDir : string ;
114105
115- constructor ( gitlabUrl : string , authToken : string , repositoryDir : string ) {
106+ constructor ( gitlabUrl : string , authToken : string ) {
116107 this . gitlabUrl = gitlabUrl ;
117108 this . authToken = authToken ;
118- this . repositoryDir = repositoryDir ;
119109 }
120110
121111 public async getMe ( ) : Promise < User > {
122112 return this . sendRequestWithSingleResponse ( `/api/v4/user` , RequestMethod . Get ) ;
123113 }
124114
125- public async getLastCommitOnTarget ( projectId : number , branch : string ) : Promise < Commit > {
126- return this . sendRequestWithSingleResponse ( `/api/v4/projects/${ projectId } /repository/commits/${ branch } ` , RequestMethod . Get ) ;
127- }
128-
129115 public async getAssignedOpenedMergeRequests ( ) : Promise < MergeRequest [ ] > {
130116 return this . sendRequestWithMultiResponse ( `/api/v4/merge_requests?scope=assigned_to_me&state=opened` , RequestMethod . Get ) ;
131117 }
132118
133119 public async getMergeRequestInfo ( projectId : number , mergeRequestIid : number ) : Promise < MergeRequestInfo > {
134- return this . sendRequestWithSingleResponse ( `/api/v4/projects/${ projectId } /merge_requests/${ mergeRequestIid } ` , RequestMethod . Get ) ;
120+ return this . sendRequestWithSingleResponse ( `/api/v4/projects/${ projectId } /merge_requests/${ mergeRequestIid } ` , RequestMethod . Get , {
121+ include_diverged_commits_count : true ,
122+ include_rebase_in_progress : true ,
123+ } ) ;
135124 }
136125
137126 public async getMergeRequestDiscussions ( projectId : number , mergeRequestIid : number ) : Promise < MergeRequestDiscussion [ ] > {
@@ -160,70 +149,14 @@ export class GitlabApi {
160149 } ) ;
161150 }
162151
163- public async rebaseMergeRequest ( mergeRequest : MergeRequest , user : User ) : Promise < void > {
164- const sourceProject = await this . getProject ( mergeRequest . source_project_id ) ;
165- const targetProject = await this . getProject ( mergeRequest . target_project_id ) ;
166-
167- if ( ! fs . existsSync ( this . repositoryDir ) ) {
168- fs . mkdirSync ( this . repositoryDir , {
169- recursive : true ,
170- } ) ;
171- }
172-
173- const git = await Git . create ( `${ this . repositoryDir } /${ mergeRequest . target_project_id } ` ) ;
174-
175- const remoteRepositories = [
176- targetProject . path_with_namespace ,
177- ] ;
178-
179- if ( targetProject . path_with_namespace !== sourceProject . path_with_namespace ) {
180- remoteRepositories . push ( sourceProject . path_with_namespace ) ;
181- }
182-
183- remoteRepositories . forEach ( async ( remoteRepository : string ) => {
184- try {
185- await git . run ( `remote add ${ remoteRepository } ${ this . gitlabUrl } :${ this . authToken } @gitlab.com/${ remoteRepository } .git` ) ;
186- } catch ( e ) {
187- if ( e . message . indexOf ( `fatal: remote ${ remoteRepository } already exists.` ) === - 1 ) {
188- throw e ;
189- }
190- }
191- } ) ;
192-
193- await git . run ( `config user.name "${ user . name } "` ) ;
194- await git . run ( `config user.email "${ user . email } "` ) ;
195-
196- await git . run ( `fetch ${ targetProject . path_with_namespace } ${ mergeRequest . target_branch } ` ) ;
197- await git . run ( `fetch ${ sourceProject . path_with_namespace } ${ mergeRequest . source_branch } ` ) ;
198-
199- await git . run ( `checkout ${ targetProject . path_with_namespace } /${ mergeRequest . target_branch } ` ) ;
200-
201- try {
202- await git . run ( `branch -D ${ mergeRequest . source_branch } ` ) ;
203- } catch ( e ) {
204- if ( e . message . indexOf ( `error: branch '${ mergeRequest . source_branch } ' not found.` ) === - 1 ) {
205- throw e ;
206- }
207- }
208-
209- await git . run ( `checkout -b ${ mergeRequest . source_branch } ${ sourceProject . path_with_namespace } /${ mergeRequest . source_branch } ` ) ;
210- await git . run ( `rebase ${ targetProject . path_with_namespace } /${ mergeRequest . target_branch } ${ mergeRequest . source_branch } ` ) ;
211-
212- await git . run ( `push --force-with-lease ${ sourceProject . path_with_namespace } ${ mergeRequest . source_branch } :${ mergeRequest . source_branch } ` ) ;
213- await git . run ( `checkout ${ targetProject . path_with_namespace } /${ mergeRequest . target_branch } ` ) ;
214- await git . run ( `branch -D ${ mergeRequest . source_branch } ` ) ;
215- }
216-
217- private async getProject ( projectId : number ) : Promise < Project > {
218- return this . sendRequestWithSingleResponse ( `/api/v4/projects/${ projectId } ` , RequestMethod . Get ) ;
152+ public async rebaseMergeRequest ( projectId : number , mergeRequestIid : number ) : Promise < void > {
153+ const response = await this . sendRawRequest ( `/api/v4/projects/${ projectId } /merge_requests/${ mergeRequestIid } /rebase` , RequestMethod . Put ) ;
154+ this . validateResponseStatus ( response ) ;
219155 }
220156
221157 private async sendRequestWithSingleResponse ( url : string , method : RequestMethod , body ?: object ) : Promise < any > {
222158 const response = await this . sendRawRequest ( url , method , body ) ;
223-
224- if ( response . status === 401 ) {
225- throw new Error ( 'Unauthorized' ) ;
226- }
159+ this . validateResponseStatus ( response ) ;
227160
228161 const data = await response . json ( ) ;
229162 if ( typeof data !== 'object' && data . id === undefined ) {
@@ -236,10 +169,7 @@ export class GitlabApi {
236169
237170 private async sendRequestWithMultiResponse ( url : string , method : RequestMethod , body ?: object ) : Promise < any > {
238171 const response = await this . sendRawRequest ( url , method , body ) ;
239-
240- if ( response . status === 401 ) {
241- throw new Error ( 'Unauthorized' ) ;
242- }
172+ this . validateResponseStatus ( response ) ;
243173
244174 const data = await response . json ( ) ;
245175 if ( ! Array . isArray ( data ) ) {
@@ -250,6 +180,20 @@ export class GitlabApi {
250180 return data ;
251181 }
252182
183+ private validateResponseStatus ( response : Response ) : void {
184+ if ( response . status === 401 ) {
185+ throw new Error ( 'Unauthorized' ) ;
186+ }
187+
188+ if ( response . status === 403 ) {
189+ throw new Error ( 'Forbidden' ) ;
190+ }
191+
192+ if ( response . status < 200 || response . status >= 300 ) {
193+ throw new Error ( 'Unexpected status code' ) ;
194+ }
195+ }
196+
253197 public sendRawRequest ( url : string , method : RequestMethod , body ?: object ) : Promise < Response > {
254198 const options : RequestInit = {
255199 method,
@@ -260,7 +204,11 @@ export class GitlabApi {
260204 } ;
261205
262206 if ( body !== undefined ) {
263- options . body = JSON . stringify ( body ) ;
207+ if ( method === RequestMethod . Get ) {
208+ url = url + '?' + queryString . stringify ( body ) ;
209+ } else {
210+ options . body = JSON . stringify ( body ) ;
211+ }
264212 }
265213
266214 return fetch ( `${ this . gitlabUrl } ${ url } ` , options ) ;
0 commit comments