@@ -6,34 +6,59 @@ import com.cdancy.jenkins.rest.JenkinsClient
66import io.codemc.api.*
77import kotlinx.coroutines.Dispatchers
88import kotlinx.coroutines.runBlocking
9- import kotlinx.coroutines.withContext
109import org.jetbrains.annotations.VisibleForTesting
1110import java.net.http.HttpRequest
1211
12+ /* *
13+ * The [JenkinsConfig] instance.
14+ */
1315var jenkinsConfig: JenkinsConfig = JenkinsConfig (" " , " " , " " )
1416 set(value) {
1517 field = value
1618
1719 val client0 = JenkinsClient .builder()
1820 .endPoint(value.url)
1921
20- if (value.username.isNotEmpty() && value.password .isNotEmpty())
21- client0.credentials(" ${value.username} :${value.password } " )
22+ if (value.username.isNotEmpty() && value.token .isNotEmpty())
23+ client0.credentials(" ${value.username} :${value.token } " )
2224
2325 client = client0.build()
2426 }
2527
2628private lateinit var client: JenkinsClient
2729
30+ /* *
31+ * The Jenkins configuration.
32+ * @param url The URL to the Jenkins Instance.
33+ * @param username The Jenkins username.
34+ * @param token The Jenkins API token.
35+ */
2836data class JenkinsConfig (
2937 val url : String ,
3038 val username : String ,
31- val password : String
39+ val token : String
3240)
3341
42+ /* *
43+ * Pings the Jenkins server.
44+ * @return `true` if the server is reachable, `false` otherwise.
45+ */
3446fun ping (): Boolean =
3547 client.api().systemApi().systemInfo().jenkinsVersion() != null
3648
49+ // Credentials API Documentation:
50+ // https://github.com/jenkinsci/credentials-plugin/blob/master/docs/user.adoc
51+
52+ /* *
53+ * The Jenkins Credentials ID for the Nexus Credentials.
54+ */
55+ const val NEXUS_CREDENTIALS_ID = " nexus-repository"
56+
57+ /* *
58+ * The Jenkins Credentials Description for the Nexus Credentials.
59+ */
60+ const val NEXUS_CREDENTIALS_DESCRIPTION = " Your Nexus Login Details"
61+
3762internal suspend fun createCredentials (username : String , password : String ): Boolean {
3863 // Create Credentials Domain
3964 val domainConfig = RESOURCE_CACHE [CREDENTIALS_DOMAIN ] ? : return false
@@ -48,6 +73,8 @@ internal suspend fun createCredentials(username: String, password: String): Bool
4873
4974 // Create Credentials Store
5075 val storeConfig = (RESOURCE_CACHE [CREDENTIALS ] ? : return false )
76+ .replace(" {ID}" , NEXUS_CREDENTIALS_ID )
77+ .replace(" {DESCRIPTION}" , NEXUS_CREDENTIALS_DESCRIPTION )
5178 .replace(" {USERNAME}" , username.lowercase())
5279 .replace(" {PASSWORD}" , password)
5380
@@ -61,6 +88,35 @@ internal suspend fun createCredentials(username: String, password: String): Bool
6188 return store.statusCode() == 200
6289}
6390
91+ /* *
92+ * Changes the Jenkins password for a user.
93+ * @param username The username of the user.
94+ * @param newPassword The new password.
95+ * @return `true` if the password was changed, `false` otherwise.
96+ */
97+ suspend fun changeJenkinsPassword (username : String , newPassword : String ): Boolean {
98+ val config = (RESOURCE_CACHE [CREDENTIALS ] ? : return false )
99+ .replace(" {ID}" , NEXUS_CREDENTIALS_ID )
100+ .replace(" {DESCRIPTION}" , NEXUS_CREDENTIALS_DESCRIPTION )
101+ .replace(" {USERNAME}" , username.lowercase())
102+ .replace(" {PASSWORD}" , newPassword)
103+
104+ val res = req(" ${jenkinsConfig.url} /job/$username /credentials/store/folder/domain/Services/credential/$NEXUS_CREDENTIALS_ID /config.xml" ) {
105+ POST (HttpRequest .BodyPublishers .ofString(config))
106+
107+ header(" Authorization" , " Basic ${client.authValue()} " )
108+ header(" Content-Type" , " application/xml" )
109+ }
110+
111+ return res.statusCode() == 200
112+ }
113+
114+ /* *
115+ * Creates a Jenkins user.
116+ * @param username The username of the user.
117+ * @param password The password of the user.
118+ * @return `true` if the user was created, `false` otherwise.
119+ */
64120fun createJenkinsUser (username : String , password : String ): Boolean = runBlocking(Dispatchers .IO ) {
65121 val config0 = RESOURCE_CACHE [USER_CONFIG ] ? : return @runBlocking false
66122
@@ -76,14 +132,31 @@ fun createJenkinsUser(username: String, password: String): Boolean = runBlocking
76132 return @runBlocking createCredentials(username, password)
77133}
78134
135+ /* *
136+ * Gets a Jenkins user's configuration.
137+ * @param username The username of the user.
138+ * @return The user's configuration in XML format.
139+ */
79140fun getJenkinsUser (username : String ): String {
80141 val user = client.api().jobsApi().config(" /" , username)
81142 return user ? : " "
82143}
83144
145+ /* *
146+ * Gets all Jenkins users.
147+ * @return A list of all Jenkins users mapped by their username.
148+ */
84149fun getAllJenkinsUsers (): List <String >
85150 = client.api().jobsApi().jobList(" /" ).jobs().map { it.name() }
86151
152+ /* *
153+ * Creates a Jenkins job.
154+ * @param username The username of the user to create the job at.
155+ * @param jobName The name of the job.
156+ * @param repoLink The Git URL to the repository.
157+ * @param isFreestyle `true` if the job is a freestyle job, `false` otherwise. A freestyle job is defined as a job that isn't built with Maven.
158+ * @param config A function to modify the XML configuration of the job.
159+ */
87160@JvmOverloads
88161fun createJenkinsJob (
89162 username : String ,
@@ -110,11 +183,23 @@ internal fun getJenkinsJob(username: String, jobName: String): String {
110183 return job ? : " "
111184}
112185
186+ /* *
187+ * Gets the information of a Jenkins job.
188+ * @param username The username of the user.
189+ * @param jobName The name of the job.
190+ * @return The job information, or `null` if the job doesn't exist.
191+ */
113192fun getJobInfo (username : String , jobName : String ): JenkinsJob ? {
114193 val job = client.api().jobsApi().jobInfo(" /" , " $username /job/$jobName " )
115194 return if (job == null ) null else JenkinsJob (job)
116195}
117196
197+ /* *
198+ * Triggers a build for a Jenkins job.
199+ * @param username The username of the user.
200+ * @param jobName The name of the job.
201+ * @return `true` if the build was triggered, `false` otherwise.
202+ */
118203fun triggerBuild (username : String , jobName : String ): Boolean {
119204 val status = client.api().jobsApi().build(" /" , " $username /job/$jobName " )
120205 if (status.errors().isNotEmpty()) {
@@ -131,6 +216,11 @@ internal fun isBuilding(username: String, jobName: String): Boolean {
131216 return (job.color() ? : " " ).contains(" anime" ) || job.inQueue() || (job.lastBuild()?.building() ? : false )
132217}
133218
219+ /* *
220+ * Deletes a Jenkins user.
221+ * @param username The username of the user.
222+ * @return `true` if the user was deleted, `false` otherwise.
223+ */
134224fun deleteUser (username : String ): Boolean {
135225 val status = client.api().jobsApi().delete(" /" , username)
136226
@@ -140,6 +230,11 @@ fun deleteUser(username: String): Boolean {
140230 return status.value()
141231}
142232
233+ /* *
234+ * Deletes a Jenkins job.
235+ * @param username The username of the user.
236+ * @param jobName The name of the job.
237+ */
143238fun deleteJob (username : String , jobName : String ): Boolean {
144239 val status = client.api().jobsApi().delete(" /" , " $username /job/$jobName " )
145240
@@ -154,6 +249,9 @@ private val nonFreestyles = listOf(
154249 " dependency-reduced-pom.xml"
155250)
156251
157- suspend fun isFreestyle (url : String ): Boolean = withContext(Dispatchers .IO ) {
158- ! filesExists(url, nonFreestyles)
159- }
252+ /* *
253+ * Checks if a Git repository is a freestyle project.
254+ * @param url The URL to the Git repository.
255+ * @return `true` if the project is a freestyle project, `false` otherwise.
256+ */
257+ suspend fun isFreestyle (url : String ): Boolean = ! filesExists(url, nonFreestyles)
0 commit comments