25
25
26
26
import com .cloudbees .jenkins .plugins .bitbucket .api .BitbucketApi ;
27
27
import com .cloudbees .jenkins .plugins .bitbucket .api .BitbucketBranch ;
28
+ import com .cloudbees .jenkins .plugins .bitbucket .api .BitbucketCommit ;
28
29
import com .cloudbees .jenkins .plugins .bitbucket .api .BitbucketPullRequest ;
30
+ import com .cloudbees .jenkins .plugins .bitbucket .filesystem .BitbucketSCMFile ;
29
31
import edu .umd .cs .findbugs .annotations .CheckForNull ;
30
32
import edu .umd .cs .findbugs .annotations .NonNull ;
33
+ import edu .umd .cs .findbugs .annotations .Nullable ;
34
+ import edu .umd .cs .findbugs .annotations .SuppressFBWarnings ;
31
35
import hudson .Util ;
32
36
import hudson .model .TaskListener ;
33
- import java .io .Closeable ;
34
37
import java .io .IOException ;
35
38
import java .util .Collections ;
36
39
import java .util .EnumSet ;
37
40
import java .util .HashMap ;
38
41
import java .util .HashSet ;
39
42
import java .util .Map ;
40
43
import java .util .Set ;
44
+ import jenkins .scm .api .SCMFile .Type ;
41
45
import jenkins .scm .api .SCMHead ;
46
+ import jenkins .scm .api .SCMHeadObserver ;
42
47
import jenkins .scm .api .SCMHeadOrigin ;
48
+ import jenkins .scm .api .SCMProbe ;
49
+ import jenkins .scm .api .SCMProbeStat ;
50
+ import jenkins .scm .api .SCMRevision ;
51
+ import jenkins .scm .api .SCMSourceCriteria .Probe ;
43
52
import jenkins .scm .api .mixin .ChangeRequestCheckoutStrategy ;
44
53
import jenkins .scm .api .trait .SCMSourceRequest ;
45
54
49
58
* @since 2.2.0
50
59
*/
51
60
public class BitbucketSCMSourceRequest extends SCMSourceRequest {
61
+
62
+ private class BitbucketProbeFactory <I > implements SCMSourceRequest .ProbeLambda <SCMHead , I > {
63
+ private transient final BitbucketApi client ;
64
+
65
+ public BitbucketProbeFactory (BitbucketApi client ) {
66
+ this .client = client ;
67
+ }
68
+
69
+ @ SuppressFBWarnings ("SE_BAD_FIELD" )
70
+ @ SuppressWarnings ("serial" )
71
+ @ NonNull
72
+ @ Override
73
+ public Probe create (@ NonNull final SCMHead head , @ CheckForNull final I revisionInfo ) throws IOException , InterruptedException {
74
+ final String hash = (revisionInfo instanceof BitbucketCommit bbRevision ) //
75
+ ? bbRevision .getHash () //
76
+ : (String ) revisionInfo ;
77
+
78
+ return new SCMProbe () {
79
+
80
+ @ Override
81
+ public void close () throws IOException {
82
+ // client will be closed by BitbucketSCMSourceRequest
83
+ }
84
+
85
+ @ Override
86
+ public String name () {
87
+ return head .getName ();
88
+ }
89
+
90
+ @ Override
91
+ public long lastModified () {
92
+ try {
93
+ BitbucketCommit commit = null ;
94
+ if (hash != null ) {
95
+ commit = (revisionInfo instanceof BitbucketCommit bbRevision ) //
96
+ ? bbRevision //
97
+ : client .resolveCommit (hash );
98
+ }
99
+
100
+ if (commit == null ) {
101
+ listener ().getLogger ().format ("Can not resolve commit by hash [%s] on repository %s/%s%n" , //
102
+ hash , client .getOwner (), client .getRepositoryName ());
103
+ return 0 ;
104
+ }
105
+ return commit .getDateMillis ();
106
+ } catch (InterruptedException | IOException e ) {
107
+ listener ().getLogger ().format ("Can not resolve commit by hash [%s] on repository %s/%s%n" , //
108
+ hash , client .getOwner (), client .getRepositoryName ());
109
+ return 0 ;
110
+ }
111
+ }
112
+
113
+ @ Override
114
+ public SCMProbeStat stat (@ NonNull String path ) throws IOException {
115
+ if (hash == null ) {
116
+ listener ().getLogger () //
117
+ .format ("Can not resolve path for hash [%s] on repository %s/%s%n" , //
118
+ hash , client .getOwner (), client .getRepositoryName ());
119
+ return SCMProbeStat .fromType (Type .NONEXISTENT );
120
+ }
121
+
122
+ try {
123
+ Type pathType = new BitbucketSCMFile (client , name (), hash ).child (path ).getType ();
124
+ return SCMProbeStat .fromType (pathType );
125
+ } catch (InterruptedException e ) {
126
+ throw new IOException ("Interrupted" , e );
127
+ }
128
+ }
129
+ };
130
+ }
131
+ }
132
+
133
+ public static class BitbucketRevisionFactory <I > implements SCMSourceRequest .LazyRevisionLambda <SCMHead , SCMRevision , I > {
134
+ private final BitbucketApi client ;
135
+
136
+ public BitbucketRevisionFactory (BitbucketApi client ) {
137
+ this .client = client ;
138
+ }
139
+
140
+ @ NonNull
141
+ @ Override
142
+ public SCMRevision create (@ NonNull SCMHead head , @ Nullable I input ) throws IOException , InterruptedException {
143
+ return create (head , input , null );
144
+ }
145
+
146
+ @ NonNull
147
+ public SCMRevision create (@ NonNull SCMHead head ,
148
+ @ Nullable I sourceInput ,
149
+ @ Nullable I targetInput ) throws IOException , InterruptedException {
150
+ BitbucketCommit sourceCommit = asCommit (sourceInput );
151
+ BitbucketCommit targetCommit = asCommit (targetInput );
152
+
153
+ SCMRevision revision ;
154
+ if (head instanceof PullRequestSCMHead prHead ) {
155
+ SCMHead targetHead = prHead .getTarget ();
156
+
157
+ return new PullRequestSCMRevision ( //
158
+ prHead , //
159
+ new BitbucketGitSCMRevision (targetHead , targetCommit ), //
160
+ new BitbucketGitSCMRevision (prHead , sourceCommit ));
161
+ } else {
162
+ revision = new BitbucketGitSCMRevision (head , sourceCommit );
163
+ }
164
+ return revision ;
165
+ }
166
+
167
+ @ Nullable
168
+ private BitbucketCommit asCommit (I input ) throws IOException , InterruptedException {
169
+ if (input instanceof String value ) {
170
+ return client .resolveCommit (value );
171
+ } else if (input instanceof BitbucketCommit commit ) {
172
+ return commit ;
173
+ }
174
+ return null ;
175
+ }
176
+ }
177
+
178
+ private class CriteriaWitness implements SCMSourceRequest .Witness {
179
+ @ Override
180
+ public void record (@ NonNull SCMHead scmHead , SCMRevision revision , boolean isMatch ) { // NOSONAR
181
+ if (revision == null ) {
182
+ listener ().getLogger ().println (" Skipped" );
183
+ } else {
184
+ if (isMatch ) {
185
+ listener ().getLogger ().println (" Met criteria" );
186
+ } else {
187
+ listener ().getLogger ().println (" Does not meet criteria" );
188
+ }
189
+
190
+ }
191
+ }
192
+ }
193
+
52
194
/**
53
195
* {@code true} if branch details need to be fetched.
54
196
*/
@@ -355,9 +497,14 @@ public final void setPullRequests(@CheckForNull Iterable<BitbucketPullRequest> p
355
497
* or if the pull request details have not been provided by {@link #setPullRequests(Iterable)} yet.
356
498
*
357
499
* @return the pull request details (may be empty)
500
+ * @throws IOException If the request to retrieve the full details encounters an issue.
501
+ * @throws InterruptedException If the request to retrieve the full details is interrupted.
358
502
*/
359
503
@ NonNull
360
- public final Iterable <BitbucketPullRequest > getPullRequests () {
504
+ public final Iterable <BitbucketPullRequest > getPullRequests () throws IOException , InterruptedException {
505
+ if (pullRequests == null ) {
506
+ pullRequests = (Iterable <BitbucketPullRequest >) getBitbucketApiClient ().getPullRequests ();
507
+ }
361
508
return Util .fixNull (pullRequests );
362
509
}
363
510
@@ -399,9 +546,14 @@ public final void setBranches(@CheckForNull Iterable<BitbucketBranch> branches)
399
546
* or if the branch details have not been provided by {@link #setBranches(Iterable)} yet.
400
547
*
401
548
* @return the branch details (may be empty)
549
+ * @throws IOException if there was a network communications error.
550
+ * @throws InterruptedException if interrupted while waiting on remote communications.
402
551
*/
403
552
@ NonNull
404
- public final Iterable <BitbucketBranch > getBranches () {
553
+ public final Iterable <BitbucketBranch > getBranches () throws IOException , InterruptedException {
554
+ if (branches == null ) {
555
+ branches = (Iterable <BitbucketBranch >) getBitbucketApiClient ().getBranches ();
556
+ }
405
557
return Util .fixNull (branches );
406
558
}
407
559
@@ -419,9 +571,14 @@ public final void setTags(@CheckForNull Iterable<BitbucketBranch> tags) {
419
571
* or if the tag details have not been provided by {@link #setTags(Iterable)} yet.
420
572
*
421
573
* @return the tag details (may be empty)
574
+ * @throws IOException if there was a network communications error.
575
+ * @throws InterruptedException if interrupted while waiting on remote communications.
422
576
*/
423
577
@ NonNull
424
- public final Iterable <BitbucketBranch > getTags () {
578
+ public final Iterable <BitbucketBranch > getTags () throws IOException , InterruptedException {
579
+ if (tags == null ) {
580
+ tags = (Iterable <BitbucketBranch >) getBitbucketApiClient ().getTags ();
581
+ }
425
582
return Util .fixNull (tags );
426
583
}
427
584
@@ -430,12 +587,54 @@ public final Iterable<BitbucketBranch> getTags() {
430
587
*/
431
588
@ Override
432
589
public void close () throws IOException {
433
- if (pullRequests instanceof Closeable closable ) {
434
- closable .close ();
435
- }
436
- if (branches instanceof Closeable closable ) {
437
- closable .close ();
590
+ if (api != null ) {
591
+ api .close ();
438
592
}
439
593
super .close ();
440
594
}
595
+
596
+ /**
597
+ * Processes a head in the context of the current request where an intermediary operation is required before
598
+ * the {@link SCMRevision} can be instantiated.
599
+ *
600
+ * @param head the {@link SCMHead} to process.
601
+ * @param intermediateFactory factory method that provides the seed information for both the {@link ProbeLambda}
602
+ * and the {@link LazyRevisionLambda}.
603
+ * @param <H> the type of {@link SCMHead}.
604
+ * @param <I> the type of the intermediary operation result.
605
+ * @param <R> the type of {@link SCMRevision}.
606
+ * @return {@code true} if the {@link SCMHeadObserver} for this request has completed observing, {@code false} to
607
+ * continue processing.
608
+ * @throws IOException if there was an I/O error.
609
+ * @throws InterruptedException if the processing was interrupted.
610
+ */
611
+ public final <H extends SCMHead , I , R extends SCMRevision > boolean process (@ NonNull H head ,
612
+ @ CheckForNull IntermediateLambda <I > intermediateFactory )
613
+ throws IOException , InterruptedException {
614
+ return super .process (head , //
615
+ intermediateFactory , //
616
+ defaultProbeLamda (), //
617
+ defaultRevisionLamda (), //
618
+ new CriteriaWitness ());
619
+ }
620
+
621
+ @ NonNull
622
+ <I > ProbeLambda <SCMHead , I > defaultProbeLamda () {
623
+ return this .new BitbucketProbeFactory <>(getBitbucketApiClient ());
624
+ }
625
+
626
+ @ NonNull
627
+ <I > ProbeLambda <SCMHead , I > buildProbeLamda (@ NonNull BitbucketApi client ) {
628
+ return this .new BitbucketProbeFactory <>(client );
629
+ }
630
+
631
+ @ NonNull
632
+ <I > LazyRevisionLambda <SCMHead , SCMRevision , I > defaultRevisionLamda () {
633
+ return new BitbucketRevisionFactory <>(getBitbucketApiClient ());
634
+ }
635
+
636
+ @ NonNull
637
+ Witness defaultWitness () {
638
+ return this .new CriteriaWitness ();
639
+ }
441
640
}
0 commit comments