Gradle ๋ฐ Github ์ž‘์—…์„ ์‚ฌ์šฉํ•˜์—ฌ Java ํ”„๋กœ์ ํŠธ๋ฅผ Sonatype Maven ์ค‘์•™ ์ €์žฅ์†Œ์— ๊ฒŒ์‹œ

์ด ๊ธฐ์‚ฌ์—์„œ๋Š” Gradle ๋นŒ๋”๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Github Actions๋ฅผ ํ†ตํ•ด Sonatype Maven Central Repository์— Java ์•„ํ‹ฐํŒฉํŠธ๋ฅผ ์ฒ˜์Œ๋ถ€ํ„ฐ ๊ฒŒ์‹œํ•˜๋Š” ํ”„๋กœ์„ธ์Šค๋ฅผ ์ž์„ธํžˆ ์‚ดํŽด๋ณด๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

ํ•œ ๊ณณ์— ์ผ๋ฐ˜์ ์ธ ํŠœํ† ๋ฆฌ์–ผ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— ์ด ๊ธ€์„ ์“ฐ๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ์ •๋ณด๋Š” ์™„์ „ํžˆ ์‹ ์„ ํ•˜์ง€ ์•Š์€ ๋‹ค์–‘ํ•œ ์ถœ์ฒ˜์—์„œ ํ•˜๋‚˜์”ฉ ์ˆ˜์ง‘ํ•ด์•ผํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ˆ„๊ฐ€ ์‹ ๊ฒฝ ์“ฐ๋‚˜์š”, ๊ณ ์–‘์ด ๋ฐ‘์—์„œ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค.

Sonatype์—์„œ ์ €์žฅ์†Œ ์ƒ์„ฑ

์ฒซ ๋ฒˆ์งธ ๋‹จ๊ณ„๋Š” Sonatype Maven Central์— ์ €์žฅ์†Œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์„ ์œ„ํ•ด ์šฐ๋ฆฌ๋Š” ๊ฐ„๋‹ค ์—ฌ๊ธฐ์—, ์ƒˆ ์ž‘์—…์„ ๋“ฑ๋กํ•˜๊ณ  ์ƒ์„ฑํ•˜์—ฌ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ์ƒ์„ฑ์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์šฐ๋ฆฌ์—์„œ ์šด์ „ ๊ทธ๋ฃน ID ํ”„๋กœ์ ํŠธ, ํ”„๋กœ์ ํŠธ URL ํ”„๋กœ์ ํŠธ ๋งํฌ ๋ฐ SCM URL ํ”„๋กœ์ ํŠธ๊ฐ€ ์žˆ๋Š” ๋ฒ„์ „ ์ œ์–ด ์‹œ์Šคํ…œ์— ๋Œ€ํ•œ ๋งํฌ. ๊ทธ๋ฃน ID com.example, com.example.domain, com.example.testsupport ํ˜•์‹์ด์–ด์•ผ ํ•˜๋ฉฐ github์— ๋Œ€ํ•œ ๋งํฌ ํ˜•์‹์ผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. github.com/yourusername -> io.github.yourusername. ์–ด์จŒ๋“  ์ด ๋„๋ฉ”์ธ ๋˜๋Š” ํ”„๋กœํ•„์˜ ์†Œ์œ ๊ถŒ์„ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. github ํ”„๋กœํ•„์„ ์ง€์ •ํ•œ ๊ฒฝ์šฐ ์›ํ•˜๋Š” ์ด๋ฆ„์œผ๋กœ ๊ณต๊ฐœ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•˜๋ผ๋Š” ๋ฉ”์‹œ์ง€๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

ํ™•์ธ ํ›„ ์ผ์ • ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด GroupId๊ฐ€ ์ƒ์„ฑ๋˜๊ณ  ๋‹ค์Œ ๋‹จ๊ณ„์ธ Gradle ๊ตฌ์„ฑ์œผ๋กœ ๋„˜์–ด๊ฐˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Gradle ๊ตฌ์„ฑ

์ž‘์„ฑ ๋‹น์‹œ์—๋Š” ์•„ํ‹ฐํŒฉํŠธ๋ฅผ ๊ฒŒ์‹œํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋  ์ˆ˜ ์žˆ๋Š” Gradle ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ฐพ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ ๊ทธ๋Ÿฌ๋‚˜ ๋‚ด๊ฐ€ ์ฐพ์€ ์œ ์ผํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์ž‘์„ฑ์ž๊ฐ€ ์ถ”๊ฐ€ ์ง€์›์„ ๊ฑฐ๋ถ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋‚˜๋Š” ์ด๊ฒƒ์„ํ•˜๋Š” ๊ฒƒ์ด ๊ทธ๋ฆฌ ์–ด๋ ต์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋ชจ๋“  ๊ฒƒ์„ ์Šค์Šค๋กœํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ฐ€์žฅ ๋จผ์ € ํŒŒ์•…ํ•ด์•ผ ํ•  ๊ฒƒ์€ ํผ๋ธ”๋ฆฌ์‹ฑ์„ ์œ„ํ•œ Sonatype์˜ ์š”๊ตฌ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ์†Œ์Šค ์ฝ”๋“œ ๋ฐ JavaDoc์˜ ๊ฐ€์šฉ์„ฑ, ์ฆ‰. ์ฐธ์„ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค -sources.jar ะธ-javadoc.jar ํŒŒ์ผ. ๋ฌธ์„œ์— ๋ช…์‹œ๋˜์–ด ์žˆ๋“ฏ์ด ์†Œ์Šค์ฝ”๋“œ๋‚˜ ๋ฌธ์„œ๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ ๋”๋ฏธ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. -sources.jar ๋˜๋Š” -javadoc.jar ๋‚ด๋ถ€์— ๊ฐ„๋‹จํ•œ README๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์–ด ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ชจ๋“  ํŒŒ์ผ์€ ๋‹ค์Œ์œผ๋กœ ์„œ๋ช…ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. GPG/PGP๊ณผ .asc ์„œ๋ช…์ด ํฌํ•จ๋œ ํŒŒ์ผ์€ ๊ฐ ํŒŒ์ผ์— ํฌํ•จ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฐ€์šฉ์„ฑ pom ํŒŒ์ผ
  • ์˜ฌ๋ฐ”๋ฅธ ๊ฐ’ groupId, artifactId ะธ version. ๋ฒ„์ „์€ ์ž„์˜์˜ ๋ฌธ์ž์—ด์ผ ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋‹ค์Œ์œผ๋กœ ๋๋‚  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. -SNAPSHOT
  • ์ฐธ์„ ํ•„์š” name, description ะธ url
  • ๋ผ์ด์„ผ์Šค, ๊ฐœ๋ฐœ์ž ๋ฐ ๋ฒ„์ „ ์ œ์–ด ์‹œ์Šคํ…œ์— ๋Œ€ํ•œ ์ •๋ณด์˜ ์กด์žฌ

๊ฒŒ์‹œํ•  ๋•Œ ๋”ฐ๋ผ์•ผ ํ•˜๋Š” ๊ธฐ๋ณธ ๊ทœ์น™์ž…๋‹ˆ๋‹ค. ์ „์ฒด ์ •๋ณด ์ œ๊ณต ์—ฌ๊ธฐ์—.

์šฐ๋ฆฌ๋Š” ์ด๋Ÿฌํ•œ ์š”๊ตฌ ์‚ฌํ•ญ์„ build.gradle ํŒŒ์ผ. ๋จผ์ € ๊ฐœ๋ฐœ์ž, ๋ผ์ด์„ ์Šค, ๋ฒ„์ „ ์ œ์–ด ์‹œ์Šคํ…œ์— ๋Œ€ํ•œ ๋ชจ๋“  ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ํ”„๋กœ์ ํŠธ์˜ URL, ์ด๋ฆ„ ๋ฐ ์„ค๋ช…๋„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•œ ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์„ ์ž‘์„ฑํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

def customizePom(pom) {
    pom.withXml {
        def root = asNode()

        root.dependencies.removeAll { dep ->
            dep.scope == "test"
        }

        root.children().last() + {
            resolveStrategy = DELEGATE_FIRST

            description 'Some description of artifact'
            name 'Artifct name'
            url 'https://github.com/login/projectname'
            organization {
                name 'com.github.login'
                url 'https://github.com/login'
            }
            issueManagement {
                system 'GitHub'
                url 'https://github.com/login/projectname/issues'
            }
            licenses {
                license {
                    name 'The Apache License, Version 2.0'
                    url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                }
            }
            scm {
                url 'https://github.com/login/projectname'
                connection 'scm:https://github.com/login/projectname.git'
                developerConnection 'scm:git://github.com/login/projectname.git'
            }
            developers {
                developer {
                    id 'dev'
                    name 'DevName'
                    email '[email protected]'
                }
            }
        }
    }
}

๋‹ค์Œ์œผ๋กœ ์–ด์…ˆ๋ธ”๋ฆฌ๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ๋™์•ˆ -sources.jar ะธ-javadoc.jar ํŒŒ์ผ. ์ด ์„น์…˜์˜ ๊ฒฝ์šฐ java ๋‹ค์Œ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

java {
    withJavadocJar()
    withSourcesJar()
}

GPG/PGP ์„œ๋ช…์„ ์„ค์ •ํ•˜๋Š” ๋งˆ์ง€๋ง‰ ์š”๊ตฌ ์‚ฌํ•ญ์œผ๋กœ ์ด๋™ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ ค๋ฉด ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค. signing:

plugins {
    id 'signing'
}

์„น์…˜์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

signing {
    sign publishing.publications
}

๋งˆ์ง€๋ง‰์œผ๋กœ ์„น์…˜์„ ์ถ”๊ฐ€ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. publishing:

publishing {
    publications {
        mavenJava(MavenPublication) {
            customizePom(pom)
            groupId group
            artifactId archivesBaseName
            version version

            from components.java
        }
    }
    repositories {
        maven {
            url "https://oss.sonatype.org/service/local/staging/deploy/maven2"
            credentials {
                username sonatypeUsername
                password sonatypePassword
            }
        }
    }
}

์—ฌ๊ธฐ์— sonatype์‚ฌ์šฉ์ž ์ด๋ฆ„ ะธ sonatype์•”ํ˜ธ ์— ๋“ฑ๋กํ•˜๋Š” ๋™์•ˆ ์ƒ์„ฑ๋œ ๋กœ๊ทธ์ธ ๋ฐ ์•”ํ˜ธ๋ฅผ ํฌํ•จํ•˜๋Š” ๋ณ€์ˆ˜ sonatype.org.

๊ทธ๋ฆฌํ•˜์—ฌ ๊ฒฐ์Šน์ „ build.gradle ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

์ „์ฒด build.gradle ์ฝ”๋“œ

plugins {
    id 'java'
    id 'maven-publish'
    id 'signing'
}

java {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
    withJavadocJar()
    withSourcesJar()
}

group 'io.github.githublogin'
archivesBaseName = 'projectname'
version = System.getenv('RELEASE_VERSION') ?: "0.0.1"

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.5.2'
}

test {
    useJUnitPlatform()
}

jar {
    from sourceSets.main.output
    from sourceSets.main.allJava
}

signing {
    sign publishing.publications
}

publishing {
    publications {
        mavenJava(MavenPublication) {
            customizePom(pom)
            groupId group
            artifactId archivesBaseName
            version version

            from components.java
        }
    }
    repositories {
        maven {
            url "https://oss.sonatype.org/service/local/staging/deploy/maven2"
            credentials {
                username sonatypeUsername
                password sonatypePassword
            }
        }
    }
}

def customizePom(pom) {
    pom.withXml {
        def root = asNode()

        root.dependencies.removeAll { dep ->
            dep.scope == "test"
        }

        root.children().last() + {
            resolveStrategy = DELEGATE_FIRST

            description 'Some description of artifact'
            name 'Artifct name'
            url 'https://github.com/login/projectname'
            organization {
                name 'com.github.login'
                url 'https://github.com/githublogin'
            }
            issueManagement {
                system 'GitHub'
                url 'https://github.com/githublogin/projectname/issues'
            }
            licenses {
                license {
                    name 'The Apache License, Version 2.0'
                    url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                }
            }
            scm {
                url 'https://github.com/githublogin/projectname'
                connection 'scm:https://github.com/githublogin/projectname.git'
                developerConnection 'scm:git://github.com/githublogin/projectname.git'
            }
            developers {
                developer {
                    id 'dev'
                    name 'DevName'
                    email '[email protected]'
                }
            }
        }
    }
}

ํ™˜๊ฒฝ ๋ณ€์ˆ˜์—์„œ ๋ฒ„์ „์„ ์–ป๋Š”๋‹ค๋Š” ์ ์— ์œ ์˜ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. System.getenv('RELEASE_VERSION'). ์–ด์…ˆ๋ธ”๋ฆฌ ์ค‘์— ๋…ธ์ถœํ•˜๊ณ  ํƒœ๊ทธ ์ด๋ฆ„์—์„œ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

PGP ํ‚ค ์ƒ์„ฑ

Sonatype์˜ ์š”๊ตฌ ์‚ฌํ•ญ ์ค‘ ํ•˜๋‚˜๋Š” ๋ชจ๋“  ํŒŒ์ผ์ด GPG/PGP ํ‚ค๋กœ ์„œ๋ช…๋˜์–ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์„ ์œ„ํ•ด ์šฐ๋ฆฌ๋Š” ๊ฐ„๋‹ค ์—ฌ๊ธฐ์— ์šด์˜ ์ฒด์ œ์šฉ GnuPG ์œ ํ‹ธ๋ฆฌํ‹ฐ๋ฅผ ๋‹ค์šด๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.

  • ํ‚ค ์Œ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. gpg --gen-key, ์‚ฌ์šฉ์ž ์ด๋ฆ„, ์ด๋ฉ”์ผ์„ ์ž…๋ ฅํ•˜๊ณ  ๋น„๋ฐ€๋ฒˆํ˜ธ๋„ ์„ค์ •ํ•˜์‹ญ์‹œ์˜ค.
  • ์•Œ์•„ ๋‚ด๊ธฐ id ๋‹ค์Œ ๋ช…๋ น์ด ํฌํ•จ๋œ ํ‚ค: gpg --list-secret-keys --keyid-format short. ID๋Š” ์Šฌ๋ž˜์‹œ ๋’ค์— ์ง€์ •๋ฉ๋‹ˆ๋‹ค(์˜ˆ: rsa2048/9B695056).
  • ์„œ๋ฒ„์— ๊ณต๊ฐœ ํ‚ค ๊ฒŒ์‹œ https://keys.openpgp.org ๋ช…๋ น์œผ๋กœ: gpg --keyserver [https://keys.openpgp.org](https://keys.openpgp.org/) --send-keys 9B695056
  • ๋น„๋ฐ€ ํ‚ค๋ฅผ ์ž„์˜์˜ ์œ„์น˜๋กœ ๋‚ด๋ณด๋ƒ…๋‹ˆ๋‹ค. ์•ž์œผ๋กœ ํ•„์š”ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. gpg --export-secret-key 9B695056 > D:\gpg\9B695056.gpg

Github ์ž‘์—… ์„ค์ •

๋งˆ์ง€๋ง‰ ๋‹จ๊ณ„๋กœ ์ด๋™ํ•˜์—ฌ Github Actions๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋นŒ๋“œ ๋ฐ ์ž๋™ ๊ฒŒ์‹œ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
Github Actions๋Š” ์ „์ฒด CI/CD ์ฃผ๊ธฐ๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ์›Œํฌํ”Œ๋กœ๋ฅผ ์ž๋™ํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค. ๋นŒ๋“œ, ํ…Œ์ŠคํŠธ ๋ฐ ๋ฐฐํฌ๋Š” ์ฝ”๋“œ ํ‘ธ์‹œ, ๋ฆด๋ฆฌ์Šค ์ƒ์„ฑ ๋˜๋Š” ๋ฌธ์ œ์™€ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ์ด๋ฒคํŠธ์— ์˜ํ•ด ํŠธ๋ฆฌ๊ฑฐ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์€ ๊ณต์šฉ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— ๋Œ€ํ•ด ์™„์ „ ๋ฌด๋ฃŒ์ž…๋‹ˆ๋‹ค.

์ด ์„น์…˜์—์„œ๋Š” ๋นŒ๋“œ ๋ฐ ํ‘ธ์‹œ ์ฝ”๋“œ๋ฅผ ์„ค์ •ํ•˜๊ณ  ์ถœ์‹œ ์‹œ Sonatype ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— ๋ฐฐํฌํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ ๋น„๋ฐ€์„ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ๋น„๋ฐ€์„ ์„ค์ •

์ž๋™ ์กฐ๋ฆฝ ๋ฐ ๋ฐฐํฌ๋ฅผ ์œ„ํ•ด์„œ๋Š” ํ‚ค ID, ํ‚ค ์ƒ์„ฑ ์‹œ ์ž…๋ ฅํ•œ ๋น„๋ฐ€๋ฒˆํ˜ธ, PGP ํ‚ค ์ž์ฒด, Sonatype ๋กœ๊ทธ์ธ/๋น„๋ฐ€๋ฒˆํ˜ธ์™€ ๊ฐ™์€ ์—ฌ๋Ÿฌ ๋น„๋ฐ€ ๊ฐ’์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ์„ค์ •์˜ ํŠน์ˆ˜ ์„น์…˜์—์„œ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Gradle ๋ฐ Github ์ž‘์—…์„ ์‚ฌ์šฉํ•˜์—ฌ Java ํ”„๋กœ์ ํŠธ๋ฅผ Sonatype Maven ์ค‘์•™ ์ €์žฅ์†Œ์— ๊ฒŒ์‹œ

๋‹ค์Œ ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

  • SONATYPE_USERNAME / SONATYPE_PASSWORD - Sonatype์— ๋“ฑ๋กํ•  ๋•Œ ์ž…๋ ฅํ•œ ๋กœ๊ทธ์ธ/๋น„๋ฐ€๋ฒˆํ˜ธ
  • SIGNING_KEYID/SIGNING_PASSWORD โ€” ์ƒ์„ฑ ์ค‘์— ์„ค์ •๋œ PGP ํ‚ค ID ๋ฐ ์•”ํ˜ธ์ž…๋‹ˆ๋‹ค.

GPG_KEY_CONTENTS ๋ณ€์ˆ˜์— ๋Œ€ํ•ด ๋” ์ž์„ธํžˆ ์„ค๋ช…ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ์‚ฌ์‹ค ์ถœํŒ์„ ์œ„ํ•ด์„œ๋Š” ๊ฐœ์ธ PGP ํ‚ค๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋น„๋ฐ€๊ธ€์— ์˜ฌ๋ฆฌ๊ธฐ ์œ„ํ•ด, ์ง€์‹œ ์ถ”๊ฐ€๋กœ ์—ฌ๋Ÿฌ ์กฐ์น˜๋ฅผ ์ทจํ–ˆ์Šต๋‹ˆ๋‹ค.

  • gpg๋กœ ํ‚ค๋ฅผ ์•”ํ˜ธํ™”ํ•ด ๋ด…์‹œ๋‹ค. gpg --symmetric --cipher-algo AES256 9B695056.gpg๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์—ฌ. SECRET_PASSPHRASE ๋ณ€์ˆ˜์— ๋ฐฐ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฐ›์€ ์•”ํ˜ธํ™” ํ‚ค๋ฅผ base64๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ…์ŠคํŠธ ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. base64 9B695056.gpg.gpg > 9B695056.txt. ์ฝ˜ํ…์ธ ๋Š” GPG_KEY_CONTENTS ๋ณ€์ˆ˜์— ๋ฐฐ์น˜๋ฉ๋‹ˆ๋‹ค.

์ฝ”๋“œ ํ‘ธ์‹œ ๋ฐ PR ์ƒ์„ฑ ์‹œ ๋นŒ๋“œ ์„ค์ •

๋จผ์ € ํ”„๋กœ์ ํŠธ์˜ ๋ฃจํŠธ์— ํด๋”๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. .github/workflows.

์˜ˆ๋ฅผ ๋“ค์–ด ํŒŒ์ผ์„ ํ‘œ์‹œํ•˜์‹ญ์‹œ์˜ค. gradle-ci-build.yml ๋‹ค์Œ ๋‚ด์šฉ์œผ๋กœ:

name: build

on:
  push:
    branches:
      - master
      - dev
      - testing
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: Set up JDK 8
        uses: actions/setup-java@v1
        with:
          java-version: 8

      - name: Build with Gradle
        uses: eskatos/gradle-command-action@v1
        with:
          gradle-version: current
          arguments: build -PsonatypeUsername=${{secrets.SONATYPE_USERNAME}} -PsonatypePassword=${{secrets.SONATYPE_PASSWORD}}

์ด ์›Œํฌํ”Œ๋กœ๋Š” ๋ธŒ๋žœ์น˜๋กœ ํ‘ธ์‹œํ•  ๋•Œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. master, dev ะธ testing, ํ’€ ์š”์ฒญ์„ ์ƒ์„ฑํ•  ๋•Œ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค.

์ž‘์—… ์„น์…˜์€ ์ง€์ •๋œ ์ด๋ฒคํŠธ์—์„œ ์‹คํ–‰ํ•  ๋‹จ๊ณ„๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ์ตœ์‹  ๋ฒ„์ „์˜ ubuntu์—์„œ ๋นŒ๋“œํ•˜๊ณ  Java 8์„ ์‚ฌ์šฉํ•˜๋ฉฐ Gradle์šฉ ํ”Œ๋Ÿฌ๊ทธ์ธ๋„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. eskatos/gradle-command-action@v1์ตœ์‹  ๋ฒ„์ „์˜ ๋นŒ๋”๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์Œ์— ์ง€์ •๋œ ๋ช…๋ น์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. arguments. ๋ณ€์ˆ˜ secrets.SONATYPE_USERNAME ะธ secrets.SONATYPE_PASSWORD ์ด๊ฒƒ์ด ์šฐ๋ฆฌ๊ฐ€ ์ด์ „์— ๋ฌผ์—ˆ๋˜ ๋น„๋ฐ€์ž…๋‹ˆ๋‹ค.

๋นŒ๋“œ ๊ฒฐ๊ณผ๋Š” ์ž‘์—… ํƒญ์— ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค.

Gradle ๋ฐ Github ์ž‘์—…์„ ์‚ฌ์šฉํ•˜์—ฌ Java ํ”„๋กœ์ ํŠธ๋ฅผ Sonatype Maven ์ค‘์•™ ์ €์žฅ์†Œ์— ๊ฒŒ์‹œ

์ƒˆ ๋ฆด๋ฆฌ์Šค๊ฐ€ ๋ฆด๋ฆฌ์Šค๋˜๋ฉด ์ž๋™ ๋ฐฐํฌ

์ž๋™ ๋ฐฐํฌ๋ฅผ ์œ„ํ•œ ๋ณ„๋„์˜ ์›Œํฌํ”Œ๋กœ ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. gradle-ci-publish.yml:

name: publish

on:
  push:
    tags:
      - 'v*'

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: Set up JDK 8
        uses: actions/setup-java@v1
        with:
          java-version: 8

      - name: Prepare to publish
        run: |
          echo '${{secrets.GPG_KEY_CONTENTS}}' | base64 -d > publish_key.gpg
          gpg --quiet --batch --yes --decrypt --passphrase="${{secrets.SECRET_PASSPHRASE}}" 
          --output secret.gpg publish_key.gpg
          echo "::set-env name=RELEASE_VERSION::${GITHUB_REF:11}"

      - name: Publish with Gradle
        uses: eskatos/gradle-command-action@v1
        with:
          gradle-version: current
          arguments: test publish -Psigning.secretKeyRingFile=secret.gpg -Psigning.keyId=${{secrets.SIGNING_KEYID}} -Psigning.password=${{secrets.SIGNING_PASSWORD}} -PsonatypeUsername=${{secrets.SONATYPE_USERNAME}} -PsonatypePassword=${{secrets.SONATYPE_PASSWORD}}

ํŒŒ์ผ์€ ํŠธ๋ฆฌ๊ฑฐ๋˜๋Š” ์ด๋ฒคํŠธ๋ฅผ ์ œ์™ธํ•˜๊ณ ๋Š” ์ด์ „ ํŒŒ์ผ๊ณผ ๊ฑฐ์˜ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ์ด๋ฆ„์ด v๋กœ ์‹œ์ž‘ํ•˜๋Š” ํƒœ๊ทธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ด๋ฒคํŠธ์ž…๋‹ˆ๋‹ค.

๋ฐฐํฌํ•˜๊ธฐ ์ „์— ์•”ํ˜ธ์—์„œ PGP ํ‚ค๋ฅผ ์ถ”์ถœํ•˜์—ฌ ํ”„๋กœ์ ํŠธ์˜ ๋ฃจํŠธ์— ๋ฐฐ์น˜ํ•˜๊ณ  ์•”ํ˜ธ๋ฅผ ํ•ด๋…ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ์œผ๋กœ ํŠน์ˆ˜ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. RELEASE_VERSION ์šฐ๋ฆฌ๊ฐ€ ์ฐธ์กฐํ•˜๋Š” gradle.build ํŒŒ์ผ. ์ด ๋ชจ๋“  ๊ฒƒ์€ ์„น์…˜์—์„œ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค. Prepare to publish. GPG_KEY_CONTENTS ๋ณ€์ˆ˜์—์„œ ํ‚ค๋ฅผ ๊ฐ€์ ธ์™€ gpg ํŒŒ์ผ๋กœ ๋ณ€ํ™˜ํ•œ ๋‹ค์Œ ํŒŒ์ผ์— ๋„ฃ์–ด ํ•ด๋…ํ•ฉ๋‹ˆ๋‹ค. secret.gpg.

๋‹ค์Œ์œผ๋กœ ํŠน์ˆ˜ ๋ณ€์ˆ˜๋กœ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค. GITHUB_REF, ์—ฌ๊ธฐ์—์„œ ํƒœ๊ทธ๋ฅผ ๋งŒ๋“ค ๋•Œ ์„ค์ •ํ•œ ๋ฒ„์ „์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ณ€์ˆ˜๋Š” ์ด ๊ฒฝ์šฐ์— ๊ด€๋ จ์ด ์žˆ์Šต๋‹ˆ๋‹ค. refs/tags/v0.0.2 ํŠน์ • ๋ฒ„์ „์„ ์–ป๊ธฐ ์œ„ํ•ด ์ฒ˜์Œ 11์ž๋ฅผ ์ž˜๋ผ๋ƒ…๋‹ˆ๋‹ค. ๋‹ค์Œ์œผ๋กœ ๊ฒŒ์‹œ๋ฅผ ์œ„ํ•ด ํ‘œ์ค€ Gradle ๋ช…๋ น์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. test publish

Sonatype ์ €์žฅ์†Œ์—์„œ ๋ฐฐํฌ ๊ฒฐ๊ณผ ํ™•์ธ

๋ฆด๋ฆฌ์Šค๊ฐ€ ์ƒ์„ฑ๋˜๋ฉด ์ด์ „ ์„น์…˜์—์„œ ์„ค๋ช…ํ•œ ์›Œํฌํ”Œ๋กœ๊ฐ€ ์‹œ์ž‘๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ ค๋ฉด ๋ฆด๋ฆฌ์Šค๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

Gradle ๋ฐ Github ์ž‘์—…์„ ์‚ฌ์šฉํ•˜์—ฌ Java ํ”„๋กœ์ ํŠธ๋ฅผ Sonatype Maven ์ค‘์•™ ์ €์žฅ์†Œ์— ๊ฒŒ์‹œ

ํƒœ๊ทธ ์ด๋ฆ„์€ v๋กœ ์‹œ์ž‘ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฆด๋ฆฌ์Šค ๊ฒŒ์‹œ๋ฅผ ํด๋ฆญํ•œ ํ›„ ์›Œํฌํ”Œ๋กœ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜๋ฉด ๋‹ค์Œ์œผ๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์†Œ๋‚˜ํƒ€ ํ˜•์‹์˜ ๋„ฅ์„œ์Šค ํ™•์ธํ•˜์‹ญ์‹œ์˜ค:

Gradle ๋ฐ Github ์ž‘์—…์„ ์‚ฌ์šฉํ•˜์—ฌ Java ํ”„๋กœ์ ํŠธ๋ฅผ Sonatype Maven ์ค‘์•™ ์ €์žฅ์†Œ์— ๊ฒŒ์‹œ

์•„ํ‹ฐํŒฉํŠธ๊ฐ€ Staging ์ €์žฅ์†Œ์— ๋‚˜ํƒ€๋‚ฌ์Šต๋‹ˆ๋‹ค. ์—ด๋ฆผ ์ƒํƒœ๋กœ ์ฆ‰์‹œ ๋‚˜ํƒ€๋‚˜๋ฉฐ ํ•ด๋‹น ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ์ˆ˜๋™์œผ๋กœ ๋‹ซํž˜ ์ƒํƒœ๋กœ ์ „ํ™˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ์š”๊ตฌ ์‚ฌํ•ญ์ด ์ถฉ์กฑ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•œ ํ›„ ์•„ํ‹ฐํŒฉํŠธ๋Š” ๋‹ซ๊ธฐ ์ƒํƒœ๊ฐ€ ๋˜๋ฉฐ ๋” ์ด์ƒ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด ํ˜•์‹์—์„œ๋Š” MavenCentral์—์„œ ๋๋‚ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ฒƒ์ด ์ž˜๋˜๋ฉด ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค ํ•ด์ œ, ์•„ํ‹ฐํŒฉํŠธ๋Š” Sonatype ์ €์žฅ์†Œ์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.

์•„ํ‹ฐํŒฉํŠธ๊ฐ€ MavenCentral์— ๋“ค์–ด๊ฐ€๋ ค๋ฉด ๋งจ ์ฒ˜์Œ์— ์ƒ์„ฑํ•œ ์ž‘์—…์—์„œ ์•„ํ‹ฐํŒฉํŠธ๋ฅผ ์š”์ฒญํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ž‘์—…์€ ํ•œ ๋ฒˆ๋งŒ ์ˆ˜ํ–‰ํ•˜๋ฉด ๋˜๋ฏ€๋กœ ์ฒ˜์Œ์œผ๋กœ ๊ฒŒ์‹œํ•ฉ๋‹ˆ๋‹ค. ์ดํ›„์—๋Š” ์ด๊ฒƒ์ด ํ•„์š”ํ•˜์ง€ ์•Š์œผ๋ฉฐ ๋ชจ๋“  ๊ฒƒ์ด ์ž๋™์œผ๋กœ ๋™๊ธฐํ™”๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋“ค์€ ์ €๋ฅผ ์œ„ํ•ด ๋™๊ธฐํ™”๋ฅผ ๋น ๋ฅด๊ฒŒ ์„ค์ •ํ–ˆ์ง€๋งŒ MavenCentral์—์„œ ์•„ํ‹ฐํŒฉํŠธ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๊ธฐ๊นŒ์ง€ ์•ฝ 5์ผ์ด ๊ฑธ๋ ธ์Šต๋‹ˆ๋‹ค.

๊ทธ๊ฒŒ ๋‹ค์•ผ, ์šฐ๋ฆฌ๋Š” MavenCentral์— ์•„ํ‹ฐํŒฉํŠธ๋ฅผ ๊ฒŒ์‹œํ–ˆ์Šต๋‹ˆ๋‹ค.

์œ ์šฉํ•œ ๋งํฌ

  • ๋น„์Šทํ•œ ๊ธฐ์‚ฌ, maven์„ ํ†ตํ•ด์„œ๋งŒ ๊ฒŒ์‹œ
  • ์ค€๋น„ ์ €์žฅ์†Œ ์†Œ๋‚˜ํƒ€์ž…
  • ๋ฝ์Šค ์ž‘์—…์„ ์ƒ์„ฑํ•  Sonatype
  • ์˜ˆ ๋ชจ๋“  ๊ฒƒ์ด ์„ค์ •๋œ ์ €์žฅ์†Œ

์ถœ์ฒ˜ : habr.com