g1t/apps/docs/src/content/docs/guides/maven.md

271 lines8,919 bytesCodeBlame
1---
2title: Maven
3description: Deploy and depend on a workspace's Java and Kotlin libraries with Maven or Gradle, from a Maven repository of its own on g1t.sh, SNAPSHOTs included.
4---
5
6Every workspace has a Maven repository of its own, in the standard
7layout that Maven, Gradle and every other JVM build tool read. `mvn
8deploy` and Gradle's `publish` upload to it, and builds resolve
9dependencies from it, private ones included.
10
11```text
12https://g1t.sh/-/maven/<workspace>/
13```
14
15Dependencies from Maven Central still come from Maven Central; only the
16artifacts you deploy here come from g1t.
17
18## Credentials
19
20Maven and Gradle send Basic credentials: any username, and an
21[access token](https://g1t.sh/settings/tokens) as the password. A token
22with full access works; one with scopes needs `packages:read` to resolve
23private artifacts and `packages:write` to deploy. Public artifacts resolve
24without one.
25
26For Maven, put the credentials in `~/.m2/settings.xml`, as a `<server>`
27whose `id` is the one you give the repository in `pom.xml`. These pages use
28the workspace's slug:
29
30```xml
31<settings>
32 <servers>
33 <server>
34 <id>acme</id>
35 <username>ada</username>
36 <password>${env.G1T_TOKEN}</password>
37 </server>
38 </servers>
39</settings>
40```
41
42Maven reads `${env.G1T_TOKEN}` from the environment, so the token stays
43out of the file.
44
45For Gradle, keep them in `~/.gradle/gradle.properties`, named for the
46repository (`acmeUsername` and `acmePassword` for a repository named
47`acme`), or read them from the environment as the examples below do.
48
49## Deploy with Maven
50
51Name the repository in `pom.xml`, for releases and SNAPSHOTs alike, and say
52which repository the source is in:
53
54```xml
55<project>
56 <groupId>com.acme</groupId>
57 <artifactId>http-client</artifactId>
58 <version>0.3.1</version>
59
60 <scm>
61 <url>https://g1t.sh/acme/http-client</url>
62 </scm>
63
64 <distributionManagement>
65 <repository>
66 <id>acme</id>
67 <url>https://g1t.sh/-/maven/acme/</url>
68 </repository>
69 <snapshotRepository>
70 <id>acme</id>
71 <url>https://g1t.sh/-/maven/acme/</url>
72 </snapshotRepository>
73 </distributionManagement>
74</project>
75```
76
77```sh
78mvn deploy
79```
80
81Maven uploads the artifact, its POM and their checksums, then the
82artifact's `maven-metadata.xml`. The artifact is named by its coordinates,
83`com.acme:http-client`, and its page on g1t.sh shows the description from
84the POM of its highest release.
85
86## Deploy with Gradle
87
88With the `maven-publish` plugin, in `build.gradle.kts`:
89
90```kotlin
91plugins {
92 `java-library`
93 `maven-publish`
94}
95
96group = "com.acme"
97version = "0.3.1"
98
99publishing {
100 publications {
101 create<MavenPublication>("library") {
102 from(components["java"])
103 pom {
104 scm { url = "https://g1t.sh/acme/http-client" }
105 }
106 }
107 }
108 repositories {
109 maven {
110 name = "acme"
111 url = uri("https://g1t.sh/-/maven/acme/")
112 credentials(PasswordCredentials::class)
113 }
114 }
115}
116```
117
118```sh
119./gradlew publish
120```
121
122`credentials(PasswordCredentials::class)` reads `acmeUsername` and
123`acmePassword` from `gradle.properties` or from the environment as
124`ORG_GRADLE_PROJECT_acmeUsername` and `ORG_GRADLE_PROJECT_acmePassword`.
125Gradle's Gradle Module Metadata (`.module`) is uploaded and served beside
126the POM.
127
128## Which repository an artifact belongs to
129
130The first file deployed makes the artifact. When a repository of the
131workspace is named like its artifactId (`http-client`), it is linked to
132that repository and has its visibility and roles: deploying needs Write
133on it. Otherwise, the POM's `<scm><url>` (or its `<url>`) naming a g1t.sh
134repository of the workspace links a new artifact to that repository, if you
135may write to it. An artifact linked to neither is the workspace's, private,
136and needs the workspace's Write base permission. See
137[who can see and publish a package](/guides/packages/#who-can-see-and-publish-a-package).
138
139## Depend on an artifact
140
141In `pom.xml`, name the repository, with the same `id` as the `<server>`
142holding your credentials:
143
144```xml
145<repositories>
146 <repository>
147 <id>acme</id>
148 <url>https://g1t.sh/-/maven/acme/</url>
149 </repository>
150</repositories>
151
152<dependencies>
153 <dependency>
154 <groupId>com.acme</groupId>
155 <artifactId>http-client</artifactId>
156 <version>0.3.1</version>
157 </dependency>
158</dependencies>
159```
160
161In Gradle:
162
163```kotlin
164repositories {
165 mavenCentral()
166 maven {
167 name = "acme"
168 url = uri("https://g1t.sh/-/maven/acme/")
169 credentials(PasswordCredentials::class)
170 }
171}
172
173dependencies {
174 implementation("com.acme:http-client:0.3.1")
175}
176```
177
178To fetch one artifact without a project:
179
180```sh
181mvn dependency:get -Dartifact=com.acme:http-client:0.3.1 \
182 -DremoteRepositories=acme::default::https://g1t.sh/-/maven/acme/
183```
184
185## SNAPSHOTs
186
187A version ending in `-SNAPSHOT` takes a new build each time it is
188deployed. Maven and Gradle upload each build's files with a timestamp and
189build number in their names (`http-client-0.4.0-20261006.120000-3.jar`),
190and the version's `maven-metadata.xml` names the newest build of each file,
191which is what a build depending on `0.4.0-SNAPSHOT` resolves. Earlier
192builds stay, by their full names.
193
194## Versions and files
195
196- **A release's files are written once.** Deploying a file of a released
197 version again with different content is refused with `409`; the same
198 content again is accepted, so a deploy that stopped part way can be run
199 again. Bump the version to change a release.
200- **Checksums are worked out by g1t.** The `.md5`, `.sha1`, `.sha256` and
201 `.sha512` beside every file are answered from the file itself. Checksums
202 uploaded are checked against it, and a mismatch is refused with `400`.
203- **`maven-metadata.xml` is made by g1t** from the versions there, so it
204 always lists every version, with the highest as `latest` and the highest
205 that is not a SNAPSHOT as `release`. The one a build uploads is accepted
206 and not kept.
207- **The deploy's last step publishes it.** Maven and Gradle upload the
208 artifact's `maven-metadata.xml` after its files. Then each version (or
209 SNAPSHOT build) whose POM arrived in the deploy is published: it is an
210 audit entry and a `package.published` event, with every file in place.
211 A POM whose `groupId`, `artifactId` or `version` is not the one in its
212 path is refused with `400`.
213
214Delete a version, or the artifact, on its page on g1t.sh, with Admin on
215the linked repository (an owner, for the workspace's own artifacts).
216
217## Private and public artifacts
218
219An artifact linked to a repository has the repository's visibility; one of
220the workspace's own is private until an owner makes it public on its page.
221
222| The workspace's artifacts | Without credentials | With credentials |
223| --- | --- | --- |
224| All public | Maven and Gradle download them. | The same; credentials are sent to deploy. |
225| Some private | Every request without credentials is answered `401`, which makes Maven and Gradle send theirs. | Each artifact the credentials' owner may see. |
226
227A private artifact you cannot see looks exactly like one that does not
228exist.
229
230## In workflows
231
232A workflow's `G1T_TOKEN` is the workspace's own token for the run, and can
233resolve and deploy the workspace's artifacts. With the `settings.xml`
234above, give it to Maven as `G1T_TOKEN`:
235
236```yaml
237jobs:
238 deploy:
239 runs-on: ubuntu-latest
240 env:
241 G1T_TOKEN: ${{ secrets.G1T_TOKEN }}
242 steps:
243 - uses: actions/checkout@v4
244 - run: |
245 mkdir -p ~/.m2
246 printf '<settings><servers><server><id>acme</id><username>g1t</username><password>${env.G1T_TOKEN}</password></server></servers></settings>\n' > ~/.m2/settings.xml
247 - run: mvn --batch-mode deploy
248```
249
250For Gradle, set `ORG_GRADLE_PROJECT_acmeUsername: g1t` and
251`ORG_GRADLE_PROJECT_acmePassword: ${{ secrets.G1T_TOKEN }}` and run
252`./gradlew publish`.
253
254## Size
255
256Each file is one request and may be at most 100 MB. Without the
257[g1t plan](/guides/usage-and-billing/#the-g1t-plan), a workspace's private
258packages may hold 500 MB and its public ones 10 GB, as for
259[container images](/guides/containers/#storage-and-pull-limits). A file is
260stored once, by its content.
261
262## Errors
263
264| Error | Means |
265| --- | --- |
266| `401` | No credentials, or a wrong or expired token. Check the `<server>` whose `id` matches the repository's, or Gradle's `acmeUsername` and `acmePassword`. |
267| `403` | Signed in, but your role or your token's scopes do not allow it, or the workspace is out of free package storage. The response says which. |
268| `404` | No such artifact, version or file, or a private one you cannot see. |
269| `409` | That file of a release is already there with other content. Bump the version. |
270| `400` | The upload was refused: a POM that does not match its path, or a checksum that does not match its file. The response says which. |
271| `413` | The file is over 100 MB. |