Normal upload mode

To avoid coding repetitive functionality, we usually package some common components and upload them to the Maven repository, while the uploaded functionality is usually used

Gradle implements maven or maven-publish as follows:

Maven plugin (deprecated)

plugins {
    id 'maven'
}

// Configure the repository and version information
ext {
    libGroup = "com.yumdao.daydayup"
    libArtifactId = "demo01"
    libVersion = "1.0.0"

    mavenUrl = libVersion.endsWith("SNAPSHOT")?"http://localhost:8081/repository/maven-snapshots/" : "http://localhost:8081/repository/maven-app/"
    mavenAccount = "admin"
    mavenPassword = "admin123"
}

// Define the upload method
uploadArchives {
    repositories {
        mavenDeployer {
            repository(url: mavenUrl) {
                authentication(userName: mavenAccount, password: mavenPassword)
            }

            pom.groupId = libGroup
            pom.artifactId = libArtifactId
            pom.version = libVersion
        }
    }
}
Copy the code

UploadArchives can then be uploaded to the Maven repository via uploadArchives in the Module’s Tasks sub-upload group. However, the Maven plugin has been removed from Gradle 7.x. This method is not recommended.

Maven – publish plug-in

plugins {
    id 'maven-publish'
}


ext {
    libGroup = "com.yumdao.daydayup"
    libArtifactId = "demo01"
    libVersion = "1.0.1"

    mavenUrl = libVersion.endsWith("SNAPSHOT")?"http://localhost:8081/repository/maven-snapshots/" : "http://localhost:8081/repository/maven-app/"
    mavenAccount = "admin"
    mavenPassword = "admin123"
}

Release and debug in buildTypes are available after afterEvaluate
afterEvaluate {
    publishing {
        publications {
            // Configure the task generated to upload the Release node
            release(MavenPublication) {
                from components.release

                groupId = libGroup
                artifactId = libArtifactId
                version = libVersion
            }

            // If necessary, you can configure to upload the debug version or just the Release version
            debug(MavenPublication) {
                from components.debug
                groupId = libGroup
                artifactId = libArtifactId
                version = libVersion
            }
        }

        repositories {
            maven {
                url = mavenUrl
                credentials {
                    username = mavenAccount
                    password = mavenPassword
                }
            }
        }
    }
}
Copy the code

After the configuration is complete, two new Tasks can be generated under publishing group under Tasks of the module. PublishReleasePublicationToMavenRepository and publishDebugPublicationToMavenRepository respectively, the corresponding is packaged after release or debug uploaded to the maven repository.

The inadequacy of conventional methods

The above configuration makes it easy to upload components, but there are some problems:

Problem 1: If a large number of components need to be maintained and each one needs to be configured in this way, it will be tedious.

Fault 2: Version verification is not performed before uploading to Maven. If you forget the upgrade version, the original version will be overwritten and cache problems will occur during use.

Problem 3: Version update logs cannot be effectively managed, usually by manually modifying the README after a release.

The solution

Solution 1: you can use custom gradle script or plugin, uploading the core encapsulated, need only exposed outside the necessary parameters, maximum reduce configuration complicated degree, in order to avoid problems with the custom gradle script copy everywhere, can put the script hosting to the server, using a remote depend on the way, such as:

// Use a file server to host scripts
apply from: "http://localhost:8080/gradle/upload.gradle"
Copy the code

The remote dependency approach is suitable for simple scripts, but if you want to enhance the functionality, you can write a lot of code in a single file, which is not conducive to the maintenance of later iterations of the script. Moreover, the API of Gradle scripts is not friendly to developers during development.

Therefore, I chose to use the plug-in to encapsulate the upload function.

Solution 2: Before packaging and uploading the Maven repository, you need to do some pre-operations to check whether the current uploaded configuration items are normal and whether the same version number exists online.

Solution 3: In the original packaging on the cross, we usually need to manually modify all libraries upgrade the README of the current version update content description, so quite cumbersome to do it, and also can’t intuitive view each version history information, if you update the information on the configuration, uploaded to the server after upload Maven repositories, unified management, And then display it in the form of a page, which will undoubtedly be more intuitive.

Script implementation

1. Create a custom parameter class

// This class is open
open class Publisher {

    companion object {
        const val NAME = "publisher"
    }

    // Configure the warehouse information
    var repoRelease: String = ""
    var repoSnapshot: String = ""

    // Set the account name and password
    var repoAccount: String = ""
    var repoPassword: String = ""

    // Configure the library version information
    var libGroup: String = ""
    var libArtifact: String = ""
    var libVersion: String = ""

    // Update description of the current version
    var libUpdateDesc: ArrayList<String> = arrayListOf()

    // Version update description
    var pomDesc: String = ""
    var pomName: String = ""
    var pomUrl: String = ""

    / / release
    var publisher: String = ""
}
Copy the code

2. Create a custom Task

// This class is open
open class PublisherTask : DefaultTask() {

    // Whether the execution task is complete
    private var executeFinishFlag: AtomicBoolean = AtomicBoolean(false)

    // Check whether the status is passed
    private var checkStatus = false

    private lateinit var publisher: Publisher

    init {
        group = "upload"

        project.run {
            publisher = extensions.getByName(Publisher.NAME) as Publisher

            // Dynamically introduce an upload plug-in for this module
            apply(hashMapOf<String, String>(Pair("plugin"."maven-publish")))
            val publishing = project.extensions.getByType(PublishingExtension::class.java)

            afterEvaluate {
                components.forEach {
                    if (it.name == "release") {
                        publishing.publications { publications ->
                            // Register to upload task
                            publications.create("release",
                                MavenPublication::class.java) { publication ->
                                publication.groupId = publisher.libGroup
                                publication.artifactId = publisher.libArtifact
                                publication.version = publisher.libVersion

                                publication.from(it)
                            }
                        }

                        publishing.repositories { artifactRepositories ->
                            artifactRepositories.maven { mavenArtifactRepository ->
                                mavenArtifactRepository.url =
                                    if (publisher.libVersion.endsWith("SNAPSHOT")) {
                                        URI(publisher.repoSnapshot)
                                    } else {
                                        URI(publisher.repoRelease)
                                    }
                                mavenArtifactRepository.credentials { credentials ->
                                    credentials.username = publisher.repoAccount
                                    credentials.password = publisher.repoPassword
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    @TaskAction
    fun doTask(a) {

        executeTask()

        // Enable the thread daemon to prevent the task from finishing before the child thread completes the task
        while(! executeFinishFlag.get()) {
            Thread.sleep(500)}}private fun executeTask(a) {
        //1. Verify the publisher configuration
        //2. Upload Publisher to the server and verify version repeatability
        checkStatus = requestCheckVersion()

        // If the first two steps pass, set checkStatus to true

        if (checkStatus) {
            val out = ByteArrayOutputStream()
            // Call maven task from the command line
            project.exec { exec ->
                exec.standardOutput = out
                exec.isIgnoreExitValue = true
                exec.commandLine(
                    "${project.rootDir}/gradlew"."publishReleasePublicationToMavenRepository")}val result = out.toString()
            if (result.contains("UP-TO-DATE")) {
                // The Maven repository was successfully uploaded to the server
                val isSuccess = requestUploadVersion()
                if (isSuccess) {
                    // A success message is displayed
                } else {
                    // An error message is displayed
                }
                executeFinish()
            } else {
                throw Exception("Failed to upload Maven repository, please check configuration!")}}}private fun requestCheckVersion(a): Boolean {
        //TODO reports to the server for version check
        return true
    }

    private fun requestUploadVersion(a): Boolean {
        //TODO reports to the server for version update
        return true
    }

    /** * The task is complete */
    private fun executeFinish(a) {
        executeFinishFlag.set(true)}}Copy the code

3. Create a custom Plugin and register parameters and tasks

class DDPlugin : Plugin<Project> {

    override fun apply(project: Project) {
        // Register custom parameters
        project.extensions.create(Publisher.NAME, Publisher::class.java)

        val currProjectName = project.displayName

        // The configured parameters can only be obtained from the build.gradle file after the afterProject lifecycle
        project.gradle.afterProject { currProject ->

            // The task is registered only for the current module to avoid redundant registration
            if (currProjectName == currProject.displayName) {
                // Register to upload task, no dependsOn here
                project.tasks.create("publishToMaven", PublisherTask::class.java)
            }
        }
    }
}
Copy the code

4. Configure the plug-in in Module

plugins {
    id 'com.android.library'
    id 'kotlin-android'
    id 'com.yumdao.daydayup' // Declare the plug-in
}
// Configure parameters
publisher {
    // Configure the repository address for the release version
    repoRelease = "http://localhost:8081/repository/maven-app/"
    // Set the storage address of the snapshot version
    repoSnapshot = "http://localhost:8081/repository/maven-snapshots/"
    / / account
    repoAccount = "admin"
    / / from close
    repoPassword = "admin123"

    // Three components are reported
    libGroup = "com.yumdao.test"
    libArtifact = "daydayup"
    libVersion = "1.0.0 - the SNAPSHOT"

    // Update the log
    libUpdateDesc = [
            "1. Release a new version",]// Configure the project information
    pomName = "DayDayUp"
    pomUrl = "http://daydayup.com"
    pomDesc = "This is just a test project!"

    / / release
    publisher = "wangyumdao"
}
Copy the code

After the Sync project, the Upload group will appear under the Tasks of the module, which contains a Task named publishToMaven, which can complete the upload function by running the Task.

After the package is completed, the configuration information needs to be uploaded to the server and recorded by the server. Although the support of the server is required to complete the reporting function of the component, the problem is not big, this year who will not build a server (manual dog head).

Finally, present the Demo address