@@ -16,18 +16,14 @@ import {
1616} from "@gitpod/gitpod-protocol" ;
1717import { log } from "@gitpod/gitpod-protocol/lib/util/logging" ;
1818import { PrebuiltWorkspaceState , WithCommitHistory } from "@gitpod/gitpod-protocol/lib/protocol" ;
19- import { WorkspaceDB } from "@gitpod/gitpod-db/lib" ;
19+ import { PrebuildWithWorkspace , WorkspaceDB } from "@gitpod/gitpod-db/lib" ;
2020import { Config } from "../config" ;
2121import { HostContextProvider } from "../auth/host-context-provider" ;
2222import { ImageSourceProvider } from "../workspace/image-source-provider" ;
2323
2424const MAX_HISTORY_DEPTH = 100 ;
2525
26- enum Match {
27- None = 0 ,
28- Loose = 1 ,
29- Exact = 2 ,
30- }
26+ type IncrementalWorkspaceMatch = "none" | "incremental" | "exact" ;
3127
3228@injectable ( )
3329export class IncrementalWorkspaceService {
@@ -93,30 +89,44 @@ export class IncrementalWorkspaceService {
9389 const imageSource = await imageSourcePromise ;
9490
9591 // traverse prebuilds by commit history instead of their creationTime, so that we don't match prebuilds created for older revisions but triggered later
96- for ( const commit of history . commitHistory ) {
97- const prebuildsForCommit = recentPrebuilds . filter ( ( { prebuild } ) => prebuild . commit === commit ) ;
98- for ( const entry of prebuildsForCommit ) {
99- const { prebuild, workspace } = entry ;
100- const match = this . isMatchForIncrementalBuild (
101- history ,
102- config ,
103- imageSource ,
92+ const candidates : { candidate : PrebuildWithWorkspace ; index : number } [ ] = [ ] ;
93+ for ( const recentPrebuild of recentPrebuilds ) {
94+ const { prebuild, workspace } = recentPrebuild ;
95+ const { match, index } = this . isMatchForIncrementalBuild (
96+ history ,
97+ config ,
98+ imageSource ,
99+ prebuild ,
100+ workspace ,
101+ includeUnfinishedPrebuilds ,
102+ ) ;
103+ if ( match === "exact" ) {
104+ console . log ( "Found base for incremental build" , {
104105 prebuild,
105106 workspace,
106- includeUnfinishedPrebuilds ,
107- ) ;
108- if ( match > Match . None ) {
109- console . log ( "Found base for incremental build" , {
110- prebuild,
111- workspace,
112- exactMatch : match === Match . Exact ,
113- } ) ;
114- return prebuild ;
115- }
107+ exactMatch : true ,
108+ } ) ;
109+ return prebuild ;
110+ }
111+ if ( match === "incremental" ) {
112+ candidates . push ( { candidate : recentPrebuild , index : index ! } ) ;
116113 }
117114 }
118115
119- return undefined ;
116+ if ( candidates . length === 0 ) {
117+ return undefined ;
118+ }
119+
120+ // Sort by index ASC
121+ candidates . sort ( ( a , b ) => a . index - b . index ) ;
122+ const { prebuild, workspace } = candidates [ 0 ] . candidate ;
123+
124+ console . log ( "Found base for incremental build" , {
125+ prebuild,
126+ workspace,
127+ exactMatch : false ,
128+ } ) ;
129+ return prebuild ;
120130 }
121131
122132 private isMatchForIncrementalBuild (
@@ -126,13 +136,13 @@ export class IncrementalWorkspaceService {
126136 candidatePrebuild : PrebuiltWorkspace ,
127137 candidateWorkspace : Workspace ,
128138 includeUnfinishedPrebuilds ?: boolean ,
129- ) : Match {
139+ ) : { match : Omit < IncrementalWorkspaceMatch , "none" > ; index : number } | { match : "none" ; index ?: undefined } {
130140 // make typescript happy, we know that history.commitHistory is defined
131141 if ( ! history . commitHistory ) {
132- return Match . None ;
142+ return { match : "none" } ;
133143 }
134144 if ( ! CommitContext . is ( candidateWorkspace . context ) ) {
135- return Match . None ;
145+ return { match : "none" } ;
136146 }
137147
138148 const acceptableStates : PrebuiltWorkspaceState [ ] = [ "available" ] ;
@@ -141,35 +151,40 @@ export class IncrementalWorkspaceService {
141151 acceptableStates . push ( "queued" ) ;
142152 }
143153 if ( ! acceptableStates . includes ( candidatePrebuild . state ) ) {
144- return Match . None ;
154+ return { match : "none" } ;
145155 }
146156
147157 // we are only considering full prebuilds (we are not building on top of incremental prebuilds)
148158 if ( candidateWorkspace . basedOnPrebuildId ) {
149- return Match . None ;
159+ return { match : "none" } ;
150160 }
151161
152162 // check if the amount of additional repositories matches the candidate
153163 if (
154164 candidateWorkspace . context . additionalRepositoryCheckoutInfo ?. length !==
155165 history . additionalRepositoryCommitHistories ?. length
156166 ) {
157- return Match . None ;
167+ return { match : "none" } ;
158168 }
159169
160170 const candidateCtx = candidateWorkspace . context ;
161171
162172 // check for overlapping commit history
173+ // TODO(gpl) Isn't "candidateCtx.revision" identical to "candidatePrebuild.commit"? If yes, we could do .indexOf once...
174+ if ( candidateCtx . revision !== candidatePrebuild . commit ) {
175+ log . warn ( "Prebuild matching: commits mismatch!" , { candidateCtx, candidatePrebuild } ) ;
176+ }
163177 if ( ! history . commitHistory . some ( ( sha ) => sha === candidateCtx . revision ) ) {
164- return Match . None ;
178+ return { match : "none" } ;
165179 }
180+
166181 // check for overlapping git history for each additional repo
167182 for ( const subRepo of candidateWorkspace . context . additionalRepositoryCheckoutInfo ?? [ ] ) {
168183 const matchingRepo = history . additionalRepositoryCommitHistories ?. find (
169184 ( repo ) => repo . cloneUrl === subRepo . repository . cloneUrl ,
170185 ) ;
171186 if ( ! matchingRepo || ! matchingRepo . commitHistory . some ( ( sha ) => sha === subRepo . revision ) ) {
172- return Match . None ;
187+ return { match : "none" } ;
173188 }
174189 }
175190
@@ -179,7 +194,7 @@ export class IncrementalWorkspaceService {
179194 imageSource,
180195 parentImageSource : candidateWorkspace . imageSource ,
181196 } ) ;
182- return Match . None ;
197+ return { match : "none" } ;
183198 }
184199
185200 // ensure the tasks haven't changed
@@ -190,14 +205,15 @@ export class IncrementalWorkspaceService {
190205 prebuildTasks,
191206 parentPrebuildTasks,
192207 } ) ;
193- return Match . None ;
208+ return { match : "none" } ;
194209 }
195210
196- if ( candidatePrebuild . commit === history . commitHistory [ 0 ] ) {
197- return Match . Exact ;
211+ const index = history . commitHistory . indexOf ( candidatePrebuild . commit ) ;
212+ if ( index === 0 ) {
213+ return { match : "exact" , index } ;
198214 }
199215
200- return Match . Loose ;
216+ return { match : "incremental" , index } ;
201217 }
202218
203219 /**
0 commit comments