@@ -12,6 +12,7 @@ import java.util.concurrent.ScheduledExecutorService
12
12
import scala .build .EitherCps .{either , value }
13
13
import scala .build .Logger
14
14
import scala .build .errors .BuildException
15
+ import scala .build .internals .ConsoleUtils .ScalaCliConsole .warnPrefix
15
16
import scala .cli .commands .util .ScalaCliSttpBackend
16
17
17
18
final case class RepoParams (
@@ -25,6 +26,8 @@ final case class RepoParams(
25
26
shouldSign : Boolean ,
26
27
shouldAuthenticate : Boolean
27
28
) {
29
+ import RepoParams .*
30
+
28
31
def withAuth (auth : Authentication ): RepoParams =
29
32
copy(
30
33
repo = repo.withAuthentication(auth),
@@ -41,14 +44,25 @@ final case class RepoParams(
41
44
)
42
45
def withAuth (authOpt : Option [Authentication ]): RepoParams = authOpt.fold(this )(withAuth)
43
46
44
- lazy val isLegacySonatype : Boolean =
47
+ lazy val isSonatype : Boolean =
45
48
Option (new URI (repo.snapshotRepo.root))
46
49
.filter(_.getScheme == " https" )
47
50
.map(_.getHost)
48
- .exists(host => host == " oss.sonatype.org " || host.endsWith( " .oss.sonatype.org " ) )
51
+ .exists(sonatypeHosts.contains )
49
52
}
50
53
51
54
object RepoParams {
55
+ private val sonatypeOssrhStagingApiBase = " https://ossrh-staging-api.central.sonatype.com"
56
+ private val sonatypeSnapshotsBase = " https://central.sonatype.com/repository/maven-snapshots/"
57
+ private val sonatypeLegacyBase = " https://oss.sonatype.org"
58
+ private val sonatypeS01LegacyBase = " https://s01.oss.sonatype.org"
59
+ private def sonatypeHosts : Seq [String ] =
60
+ Seq (
61
+ sonatypeLegacyBase,
62
+ sonatypeSnapshotsBase,
63
+ sonatypeS01LegacyBase,
64
+ sonatypeOssrhStagingApiBase
65
+ ).map(new URI (_).getHost)
52
66
53
67
def apply (
54
68
repo : String ,
@@ -67,24 +81,42 @@ object RepoParams {
67
81
case " ivy2-local" =>
68
82
RepoParams .ivy2Local(ivy2HomeOpt)
69
83
case " sonatype" | " central" | " maven-central" | " mvn-central" =>
84
+ logger.message(s " Using Portal OSSRH Staging API: $sonatypeOssrhStagingApiBase" )
85
+ RepoParams .centralRepo(
86
+ base = sonatypeOssrhStagingApiBase,
87
+ useLegacySnapshots = false ,
88
+ connectionTimeoutRetries = connectionTimeoutRetries,
89
+ connectionTimeoutSeconds = connectionTimeoutSeconds,
90
+ stagingRepoRetries = stagingRepoRetries,
91
+ stagingRepoWaitTimeMilis = stagingRepoWaitTimeMilis,
92
+ es = es,
93
+ logger = logger
94
+ )
95
+ case " sonatype-legacy" | " central-legacy" | " maven-central-legacy" | " mvn-central-legacy" =>
96
+ logger.message(s " $warnPrefix $sonatypeLegacyBase is EOL since 2025-06-30. " )
97
+ logger.message(s " $warnPrefix $sonatypeLegacyBase publishing is expected to fail. " )
70
98
RepoParams .centralRepo(
71
- " https://oss.sonatype.org" ,
72
- connectionTimeoutRetries,
73
- connectionTimeoutSeconds,
74
- stagingRepoRetries,
75
- stagingRepoWaitTimeMilis,
76
- es,
77
- logger
99
+ base = sonatypeLegacyBase,
100
+ useLegacySnapshots = true ,
101
+ connectionTimeoutRetries = connectionTimeoutRetries,
102
+ connectionTimeoutSeconds = connectionTimeoutSeconds,
103
+ stagingRepoRetries = stagingRepoRetries,
104
+ stagingRepoWaitTimeMilis = stagingRepoWaitTimeMilis,
105
+ es = es,
106
+ logger = logger
78
107
)
79
108
case " sonatype-s01" | " central-s01" | " maven-central-s01" | " mvn-central-s01" =>
109
+ logger.message(s " $warnPrefix $sonatypeS01LegacyBase is EOL since 2025-06-30. " )
110
+ logger.message(s " $warnPrefix it's expected publishing will fail. " )
80
111
RepoParams .centralRepo(
81
- " https://s01.oss.sonatype.org" ,
82
- connectionTimeoutRetries,
83
- connectionTimeoutSeconds,
84
- stagingRepoRetries,
85
- stagingRepoWaitTimeMilis,
86
- es,
87
- logger
112
+ base = sonatypeS01LegacyBase,
113
+ useLegacySnapshots = true ,
114
+ connectionTimeoutRetries = connectionTimeoutRetries,
115
+ connectionTimeoutSeconds = connectionTimeoutSeconds,
116
+ stagingRepoRetries = stagingRepoRetries,
117
+ stagingRepoWaitTimeMilis = stagingRepoWaitTimeMilis,
118
+ es = es,
119
+ logger = logger
88
120
)
89
121
case " github" =>
90
122
value(RepoParams .gitHubRepo(vcsUrlOpt, workspace, logger))
@@ -103,77 +135,86 @@ object RepoParams {
103
135
}
104
136
105
137
RepoParams (
106
- PublishRepository .Simple (repo0),
107
- None ,
108
- Hooks .dummy,
109
- isIvy2LocalLike,
110
- true ,
111
- true ,
112
- true ,
113
- false ,
114
- false
138
+ repo = PublishRepository .Simple (repo0),
139
+ targetRepoOpt = None ,
140
+ hooks = Hooks .dummy,
141
+ isIvy2LocalLike = isIvy2LocalLike ,
142
+ defaultParallelUpload = true ,
143
+ supportsSig = true ,
144
+ acceptsChecksums = true ,
145
+ shouldSign = false ,
146
+ shouldAuthenticate = false
115
147
)
116
148
}
117
149
}
118
150
119
151
def centralRepo (
120
152
base : String ,
153
+ useLegacySnapshots : Boolean ,
121
154
connectionTimeoutRetries : Option [Int ],
122
155
connectionTimeoutSeconds : Option [Int ],
123
156
stagingRepoRetries : Option [Int ],
124
157
stagingRepoWaitTimeMilis : Option [Int ],
125
158
es : ScheduledExecutorService ,
126
159
logger : Logger
127
- ) = {
128
- val repo0 = PublishRepository .Sonatype (MavenRepository (base))
160
+ ): RepoParams = {
161
+ val repo0 = PublishRepository .Sonatype (
162
+ base = MavenRepository (base),
163
+ useLegacySnapshots = useLegacySnapshots
164
+ )
129
165
val backend = ScalaCliSttpBackend .httpURLConnection(logger, connectionTimeoutSeconds)
130
166
val api = SonatypeApi (
131
- backend,
132
- base + " /service/local" ,
133
- None ,
134
- logger.verbosity,
167
+ backend = backend ,
168
+ base = base + " /service/local" ,
169
+ authentication = None ,
170
+ verbosity = logger.verbosity,
135
171
retryOnTimeout = connectionTimeoutRetries.getOrElse(3 ),
136
- stagingRepoRetryParams = EmaRetryParams (
137
- stagingRepoRetries.getOrElse(3 ),
138
- stagingRepoWaitTimeMilis.getOrElse(10 * 1000 ),
139
- 2.0f
140
- )
172
+ stagingRepoRetryParams =
173
+ EmaRetryParams (
174
+ attempts = stagingRepoRetries.getOrElse(3 ),
175
+ initialWaitDurationMs = stagingRepoWaitTimeMilis.getOrElse(10 * 1000 ),
176
+ factor = 2.0f
177
+ )
141
178
)
142
179
val hooks0 = Hooks .sonatype(
143
- repo0,
144
- api,
145
- logger.compilerOutputStream, // meh
146
- logger.verbosity,
180
+ repo = repo0,
181
+ api = api ,
182
+ out = logger.compilerOutputStream, // meh
183
+ verbosity = logger.verbosity,
147
184
batch = coursier.paths.Util .useAnsiOutput(), // FIXME Get via logger
148
- es
185
+ es = es
149
186
)
150
187
RepoParams (
151
- repo0,
152
- Some (" https://repo1.maven.org/maven2" ),
153
- hooks0,
154
- false ,
155
- true ,
156
- true ,
157
- true ,
158
- true ,
159
- true
188
+ repo = repo0,
189
+ targetRepoOpt = Some (" https://repo1.maven.org/maven2" ),
190
+ hooks = hooks0,
191
+ isIvy2LocalLike = false ,
192
+ defaultParallelUpload = true ,
193
+ supportsSig = true ,
194
+ acceptsChecksums = true ,
195
+ shouldSign = true ,
196
+ shouldAuthenticate = true
160
197
)
161
198
}
162
199
163
- def gitHubRepoFor (org : String , name : String ) =
200
+ def gitHubRepoFor (org : String , name : String ): RepoParams =
164
201
RepoParams (
165
- PublishRepository .Simple (MavenRepository (s " https://maven.pkg.github.com/ $org/ $name" )),
166
- None ,
167
- Hooks .dummy,
168
- false ,
169
- false ,
170
- false ,
171
- false ,
172
- false ,
173
- true
202
+ repo = PublishRepository .Simple (MavenRepository (s " https://maven.pkg.github.com/ $org/ $name" )),
203
+ targetRepoOpt = None ,
204
+ hooks = Hooks .dummy,
205
+ isIvy2LocalLike = false ,
206
+ defaultParallelUpload = false ,
207
+ supportsSig = false ,
208
+ acceptsChecksums = false ,
209
+ shouldSign = false ,
210
+ shouldAuthenticate = true
174
211
)
175
212
176
- def gitHubRepo (vcsUrlOpt : Option [String ], workspace : os.Path , logger : Logger ) = either {
213
+ def gitHubRepo (
214
+ vcsUrlOpt : Option [String ],
215
+ workspace : os.Path ,
216
+ logger : Logger
217
+ ): Either [BuildException , RepoParams ] = either {
177
218
val orgNameFromVcsOpt = vcsUrlOpt.flatMap(GitRepo .maybeGhOrgName)
178
219
179
220
val (org, name) = orgNameFromVcsOpt match {
@@ -184,23 +225,23 @@ object RepoParams {
184
225
gitHubRepoFor(org, name)
185
226
}
186
227
187
- def ivy2Local (ivy2HomeOpt : Option [os.Path ]) = {
228
+ def ivy2Local (ivy2HomeOpt : Option [os.Path ]): RepoParams = {
188
229
val home = ivy2HomeOpt
189
230
.orElse(sys.props.get(" ivy.home" ).map(prop => os.Path (prop)))
190
231
.orElse(sys.props.get(" user.home" ).map(prop => os.Path (prop) / " .ivy2" ))
191
232
.getOrElse(os.home / " .ivy2" )
192
233
val base = home / " local"
193
234
// not really a Maven repo…
194
235
RepoParams (
195
- PublishRepository .Simple (MavenRepository (base.toNIO.toUri.toASCIIString)),
196
- None ,
197
- Hooks .dummy,
198
- true ,
199
- true ,
200
- true ,
201
- true ,
202
- false ,
203
- false
236
+ repo = PublishRepository .Simple (MavenRepository (base.toNIO.toUri.toASCIIString)),
237
+ targetRepoOpt = None ,
238
+ hooks = Hooks .dummy,
239
+ isIvy2LocalLike = true ,
240
+ defaultParallelUpload = true ,
241
+ supportsSig = true ,
242
+ acceptsChecksums = true ,
243
+ shouldSign = false ,
244
+ shouldAuthenticate = false
204
245
)
205
246
}
206
247
0 commit comments