Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: Publish to Maven Central

on:
workflow_dispatch:
inputs:
target:
description: Publication target
required: true
default: dev
type: choice
options:
- dev
- release

concurrency:
group: publish-${{ inputs.target }}
cancel-in-progress: true

jobs:
publish:
name: Publish to Maven Central
runs-on: ubuntu-latest
services:
redis:
image: redis:6.2.7-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5

steps:
- name: Checkout code
uses: actions/checkout@v5

- name: Set up JDK 17
uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'temurin'
cache: gradle

- name: Validate Gradle wrapper
uses: gradle/actions/wrapper-validation@v4

- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Run tests
run: ./gradlew test
env:
TEST_REDIS_HOST: localhost
TEST_REDIS_PORT: 6379

- name: Set snapshot version
if: inputs.target == 'dev'
run: sed -i 's/snapshotBuild=false/snapshotBuild=true/' gradle.properties

- name: Set release version
if: inputs.target == 'release'
run: sed -i 's/snapshotBuild=true/snapshotBuild=false/' gradle.properties

- name: Build with Gradle
run: ./gradlew build

- name: Publish Dev Snapshot to Sonatype
if: inputs.target == 'dev'
run: ./gradlew --max-workers=1 publishToSonatype
env:
ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.GPG_SECRET_KEY_ID }}
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_SECRET_KEY }}
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_SECRET_PASSWORD }}

- name: Publish Release to Maven Central
if: inputs.target == 'release'
run: ./gradlew --max-workers=1 publishToSonatype closeAndReleaseSonatypeRepository
env:
ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.GPG_SECRET_KEY_ID }}
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_SECRET_KEY }}
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_SECRET_PASSWORD }}

- name: Cleanup Gradle Cache
if: always()
run: |
rm -f ~/.gradle/caches/modules-2/modules-2.lock
rm -f ~/.gradle/caches/modules-2/gc.properties
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
.codex
.gradle-user-home

### IntelliJ IDEA ###
.idea
Expand Down
75 changes: 41 additions & 34 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,23 @@ plugins {
application
`maven-publish`
`java-library`
signing
id("io.github.gradle-nexus.publish-plugin") version "2.0.0"
}

val snapshotBuild = providers.gradleProperty("snapshotBuild").getOrElse("true").toBoolean()

allprojects {

group = "com.linecorp.cse.reqshield"
version = "1.0.0"
version = "1.0.0${if (snapshotBuild) "-SNAPSHOT" else ""}"

apply {
plugin("java-test-fixtures")
plugin("maven-publish")
plugin("java-library")
plugin("jacoco")
plugin("signing")
}

repositories {
Expand Down Expand Up @@ -65,7 +70,7 @@ allprojects {

// Enforce the minimum line coverage documented in CLAUDE.md for library modules.
// Example applications (req-shield-*-example) are demos and are not held to the threshold.
if (project != rootProject && !project.name.startsWith("req-shield-")) {
if (project != rootProject && !project.name.endsWith("-example")) {
tasks.withType<JacocoCoverageVerification> {
dependsOn(tasks.test)
violationRules {
Expand Down Expand Up @@ -100,6 +105,9 @@ subprojects {
plugin("org.jlleitschuh.gradle.ktlint")
}
java {
withJavadocJar()
withSourcesJar()

sourceCompatibility =
when (project.name) {
in springBoot3ProjectNames -> JavaVersion.VERSION_17
Expand All @@ -113,37 +121,9 @@ subprojects {
}

afterEvaluate {
fun getProfile() = properties["PROFILE"] ?: System.getenv()["PROFILE"] ?: "local"

fun getVersion() = project.version.toString()

fun getSemanticPostfix() =
when (getProfile()) {
"real" -> ""
else -> "-SNAPSHOT"
}

version = "${getVersion()}${getSemanticPostfix()}"
if (project.name.endsWith("-example")) return@afterEvaluate

publishing {
repositories {
maven {
fun getUrl(): String =
if (getProfile() == "real") {
"https://oss.sonatype.org/service/local/staging/deploy/maven2/"
} else {
"https://oss.sonatype.org/content/repositories/snapshots/"
}

url = uri(getUrl())

credentials {
username = System.getenv()["NEXUS_USER"]
password = System.getenv()["NEXUS_PASS"]
}
}
}

publications {
register("mavenJava", MavenPublication::class) {

Expand All @@ -154,6 +134,14 @@ subprojects {
description.set("LINE Req-Shield")
url.set("https://github.com/line/req-shield.git")

developers {
developer {
name.set("LINE Corporation")
organization.set("LY Corporation")
organizationUrl.set("https://www.lycorp.co.jp/en/")
}
}

licenses {
license {
name.set("The Apache License, Version 2.0")
Expand All @@ -162,13 +150,32 @@ subprojects {
}

scm {
url.set("scm:git@github.com:line/req-shield.git")
connection.set("scm:git@github.com:line/req-shield.git")
developerConnection.set("scm:git@github.com:line/req-shield.git")
url.set("https://github.com/line/req-shield")
connection.set("scm:git:https://github.com/line/req-shield.git")
developerConnection.set("scm:git:ssh://git@github.com/line/req-shield.git")
}
}
}
}
}

val signingKeyId = providers.gradleProperty("signingKeyId").orNull
val signingKey = providers.gradleProperty("signingKey").orNull
val signingPassword = providers.gradleProperty("signingPassword").orNull
if (signingKey != null) {
signing {
useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword)
sign(publishing.publications["mavenJava"])
}
}
}
}

nexusPublishing {
repositories {
sonatype {
nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/"))
snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/"))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,23 @@ class ReqShield<T>(
val onlyUpdateCache = reqShieldConfig.reqShieldWorkMode == ReqShieldWorkMode.ONLY_UPDATE_CACHE
val token = if (onlyUpdateCache) null else reqShieldConfig.keyLock.tryLock(key, lockType)

if (token != null) {
var cacheCreationRequired = false
try {
// Another request may have filled the cache between our initial miss and lock acquisition.
val cachedData = executeGetCacheFunction(reqShieldConfig.getCacheFunction, key)
if (cachedData != null) return cachedData
cacheCreationRequired = true
} finally {
// A hit, read failure, or cancellation must release the token; creation owns it on a miss.
if (!cacheCreationRequired) {
withContext(NonCancellable) {
reqShieldConfig.keyLock.unLock(key, lockType, token)
}
}
}
}

return if (onlyUpdateCache || token != null) {
createReqShieldData(key, callable, timeToLiveMillis, lockType, token)
} else {
Expand Down
Loading
Loading