Skip to content

Commit d67a3ed

Browse files
Merge pull request #7 from Esri/jonas/add-jreleaser
ci: publish to maven
2 parents a8e246b + ceb1abc commit d67a3ed

File tree

5 files changed

+310
-3
lines changed

5 files changed

+310
-3
lines changed

.github/workflows/main.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ on:
1212

1313
jobs:
1414
build:
15+
if: github.event_name != 'release'
1516
runs-on: ubuntu-24.04
1617
steps:
1718
- uses: actions/checkout@v4
@@ -22,6 +23,30 @@ jobs:
2223
- name: Test
2324
run: ./gradlew test
2425

26+
deploy:
27+
if: github.event_name == 'release' && github.event.action == 'created'
28+
runs-on: ubuntu-24.04
29+
steps:
30+
- uses: actions/checkout@v4
31+
with:
32+
submodules: recursive
33+
- name: Validate version
34+
id: version
35+
run: |
36+
set -o pipefail
37+
tag_name="${{ github.ref_name }}"
38+
python ./tools/version_validator.py "$tag_name"
39+
echo "tag_name=$tag_name" >> $GITHUB_OUTPUT
40+
- name: Build
41+
run: ./gradlew -PVERSION=${{ steps.version.outputs.tag_name }} build
42+
- name: Test
43+
run: ./gradlew -PVERSION=${{ steps.version.outputs.tag_name }} test
44+
- name: Publish (Maven)
45+
run: |
46+
echo -n "${{ secrets.MAVEN_SIGNING_KEY }}" | base64 --decode | gpg --import
47+
./gradlew -PVERSION=${{ steps.version.outputs.tag_name }} -PcentralUsername=${{ secrets.CENTRAL_USERNAME }} -PcentralPassword=${{ secrets.CENTRAL_PASSWORD }} publish
48+
./gradlew -PVERSION=${{ steps.version.outputs.tag_name }} -PcentralUsername=${{ secrets.CENTRAL_USERNAME }} -PcentralPassword=${{ secrets.CENTRAL_PASSWORD }} jreleaserDeploy
49+
2550
xmllint:
2651
runs-on: ubuntu-24.04
2752
steps:

build.gradle.kts

Lines changed: 123 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
1+
import java.util.Properties
2+
import java.io.FileInputStream
3+
import java.io.IOException
4+
15
plugins {
26
alias(libs.plugins.android.library)
37
alias(libs.plugins.jetbrains.kotlin.android)
8+
alias(libs.plugins.jreleaser)
9+
`maven-publish`
10+
}
11+
12+
// Load file "keystore.properties" where we keep our keys
13+
val keystorePropertiesFile = rootProject.file("keystore.properties")
14+
val keystoreProperties = Properties()
15+
16+
try {
17+
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
18+
} catch (ignored: IOException) {
19+
if (project.hasProperty("centralUsername")) keystoreProperties["centralUsername"] = property("centralUsername")
20+
if (project.hasProperty("centralPassword")) keystoreProperties["centralPassword"] = property("centralPassword")
421
}
522

623
android {
@@ -9,8 +26,12 @@ android {
926

1027
defaultConfig {
1128
minSdk = 24
12-
group = "com.esri"
13-
version = "0.0.1"
29+
group = "com.esri.logger"
30+
31+
// The version must be of the form "X.Y.Z[-b][-SNAPSHOT]"
32+
version =
33+
if (project.hasProperty("VERSION")) project.property("VERSION").toString()
34+
else "0.0.1-SNAPSHOT"
1435

1536
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
1637
}
@@ -26,6 +47,10 @@ android {
2647
}
2748
kotlinOptions { jvmTarget = "17" }
2849
testOptions { unitTests { isIncludeAndroidResources = true } }
50+
51+
publishing {
52+
singleVariant("release") {}
53+
}
2954
}
3055

3156
tasks.withType<Test> {
@@ -52,3 +77,99 @@ dependencies {
5277
androidTestImplementation(libs.androidx.junit)
5378
androidTestImplementation(libs.androidx.espresso.core)
5479
}
80+
81+
if (keystoreProperties.containsKey("centralUsername") && keystoreProperties.containsKey("centralPassword")) {
82+
afterEvaluate {
83+
publishing {
84+
publications {
85+
create<MavenPublication>("release") {
86+
from(components["release"])
87+
88+
pom {
89+
name = "Khronicle"
90+
packaging = "aar"
91+
description = "An SLF4J backend for Android."
92+
url = "https://github.com/esri/khronicle"
93+
94+
scm {
95+
connection = "scm:git:https://github.com/esri/khronicle"
96+
developerConnection = "scm:git:https://github.com/esri/khronicle"
97+
url = "https://github.com/esri/khronicle"
98+
}
99+
100+
licenses {
101+
license {
102+
name = "Apache License 2.0"
103+
url = "https://spdx.org/licenses/Apache-2.0.html"
104+
}
105+
}
106+
107+
developers {
108+
developer {
109+
id = "award"
110+
name = "Adam Ward"
111+
112+
}
113+
developer {
114+
id = "jonasvautherin"
115+
name = "Jonas Vautherin"
116+
117+
}
118+
}
119+
}
120+
}
121+
}
122+
repositories {
123+
maven {
124+
url = uri(layout.buildDirectory.dir("target/staging-deploy"))
125+
}
126+
}
127+
}
128+
}
129+
130+
jreleaser {
131+
signing {
132+
setActive("ALWAYS")
133+
armored.set(true)
134+
setMode("COMMAND")
135+
136+
command {
137+
keyName.set("C8B1511EF991537875A649517D5F7A7C9542C299")
138+
}
139+
}
140+
deploy {
141+
release {
142+
github {
143+
skipRelease = true
144+
skipTag = true
145+
}
146+
}
147+
maven {
148+
mavenCentral {
149+
create("sonatype") {
150+
verifyPom = false
151+
setActive("RELEASE")
152+
username = keystoreProperties["centralUsername"] as String
153+
password = keystoreProperties["centralPassword"] as String
154+
url = "https://central.sonatype.com/api/v1/publisher"
155+
stagingRepository("build/target/staging-deploy")
156+
}
157+
}
158+
nexus2 {
159+
create("snapshot-deploy") {
160+
verifyPom = false
161+
setActive("SNAPSHOT")
162+
snapshotUrl.set("https://central.sonatype.com/repository/maven-snapshots")
163+
url = "https://central.sonatype.com/repository/maven-snapshots"
164+
applyMavenCentralRules = true
165+
snapshotSupported = true
166+
username = keystoreProperties["centralUsername"] as String
167+
password = keystoreProperties["centralPassword"] as String
168+
stagingRepository("build/target/staging-deploy")
169+
}
170+
}
171+
}
172+
}
173+
}
174+
}
175+

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ coreKtx = "1.16.0"
55
espressoCore = "3.6.1"
66
junit = "4.13.2"
77
junitVersion = "1.2.1"
8+
jreleaser-plugin = "1.19.0"
89
kotlin = "2.1.20"
910
kotlinReflect = "2.1.0"
1011
material = "1.12.0"
@@ -28,4 +29,5 @@ slf4j-api = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4jApi"}
2829
[plugins]
2930
android-library = { id = "com.android.library", version.ref = "agp" }
3031
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
32+
jreleaser = { id = "org.jreleaser", version.ref = "jreleaser-plugin" }
3133

settings.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ dependencyResolutionManagement {
1414
}
1515
}
1616

17-
rootProject.name = "logger-android"
17+
rootProject.name = "khronicle"

tools/version_validator.py

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Version Number Validator
4+
5+
This script validates version numbers and extracts major.minor.patch format.
6+
7+
Version format rules:
8+
- Can start with 'v' (optional): v2.3.1 or 2.3.1
9+
- Must have major.minor.patch: 1.2.3 (minimum required)
10+
- May have build number: 1.2.3-1 or 1.2.3-2
11+
- May end with -SNAPSHOT: 3.4.5-1-SNAPSHOT or 3.4.5-SNAPSHOT
12+
13+
Usage:
14+
python version_validator.py <version_string>
15+
python version_validator.py --extract <version_string>
16+
"""
17+
18+
import re
19+
import sys
20+
import argparse
21+
22+
def extract_major_minor_patch(version_string):
23+
"""
24+
Extract major.minor.patch from a valid version string in vX.Y.Z format.
25+
26+
Args:
27+
version_string (str): The version string to extract from
28+
29+
Returns:
30+
str: The extracted version in vX.Y.Z format, or None if invalid
31+
"""
32+
# Define the regex pattern for valid version strings
33+
pattern = r'^v?(\d+)\.(\d+)\.(\d+)(?:-(\d+))?(?:-SNAPSHOT)?$'
34+
match = re.match(pattern, version_string)
35+
36+
if match:
37+
major, minor, patch = match.groups()[:3]
38+
return f"v{major}.{minor}.{patch}"
39+
40+
return None
41+
42+
def validate_version(version_string):
43+
"""
44+
Validate a version string according to the specified rules.
45+
46+
Args:
47+
version_string (str): The version string to validate
48+
49+
Returns:
50+
bool: True if valid, False otherwise
51+
"""
52+
return extract_major_minor_patch(version_string) is not None
53+
54+
def main():
55+
parser = argparse.ArgumentParser(
56+
description="Validate version number and extract major.minor.patch format",
57+
formatter_class=argparse.RawDescriptionHelpFormatter,
58+
epilog="""
59+
Examples:
60+
python version_validator.py v2.3.1 # Validate version
61+
python version_validator.py 1.2.3-1 # Validate version with build
62+
python version_validator.py --extract v2.3.1-SNAPSHOT # Extract v2.3.1
63+
python version_validator.py -e 3.4.5-2-SNAPSHOT # Extract v3.4.5
64+
65+
Valid version formats:
66+
v1.2.3, 1.2.3, v1.2.3-1, 1.2.3-1, v1.2.3-SNAPSHOT, 1.2.3-1-SNAPSHOT
67+
"""
68+
)
69+
70+
parser.add_argument(
71+
'version',
72+
help='Version string to validate/extract'
73+
)
74+
parser.add_argument(
75+
'-e', '--extract',
76+
action='store_true',
77+
help='Extract major.minor.patch in vX.Y.Z format'
78+
)
79+
80+
args = parser.parse_args()
81+
82+
if args.extract:
83+
# Extract mode
84+
result = extract_major_minor_patch(args.version)
85+
if result:
86+
print(result)
87+
sys.exit(0)
88+
else:
89+
print(f"Error: '{args.version}' is not a valid version string", file=sys.stderr)
90+
sys.exit(1)
91+
else:
92+
# Validation mode
93+
if validate_version(args.version):
94+
print(f"'{args.version}' is a valid version string")
95+
sys.exit(0)
96+
else:
97+
print(f"Error: '{args.version}' is not a valid version string", file=sys.stderr)
98+
sys.exit(1)
99+
100+
101+
if __name__ == "__main__":
102+
# If no arguments provided, run some test cases
103+
if len(sys.argv) == 1:
104+
print("Running test cases...")
105+
106+
test_cases = [
107+
# Valid cases
108+
("v2.3.1", True),
109+
("2.3.1", True),
110+
("1.2.3-1", True),
111+
("v1.2.3-1", True),
112+
("3.4.5-SNAPSHOT", True),
113+
("v3.4.5-SNAPSHOT", True),
114+
("1.2.3-1-SNAPSHOT", True),
115+
("v1.2.3-1-SNAPSHOT", True),
116+
("10.20.30", True),
117+
("v0.0.1", True),
118+
119+
# Invalid cases
120+
("1.2", False),
121+
("v1.2", False),
122+
("1.2.3.4", False),
123+
("v1.2.3.4", False),
124+
("1.2.3-", False),
125+
("1.2.3-SNAPSHOT-1", False),
126+
("1.2.3-a", False),
127+
("v1.2.3-a", False),
128+
("av1.2.3", False),
129+
("a1.2.3", False),
130+
(" v1.2.3", False),
131+
("V1.2.3-a", False),
132+
("1.2.x", False),
133+
("", False),
134+
("v", False),
135+
("1.2.3-1-SNAPSHOT-extra", False),
136+
]
137+
138+
print("\nValidation Tests:")
139+
for version, expected in test_cases:
140+
result = validate_version(version)
141+
status = "✓" if result == expected else "✗"
142+
print(f"{status} {version:<25} -> {result} (expected {expected})")
143+
144+
print("\nExtraction Tests:")
145+
extraction_tests = [
146+
("v2.3.1", "v2.3.1"),
147+
("2.3.1", "v2.3.1"),
148+
("1.2.3-1", "v1.2.3"),
149+
("v1.2.3-1-SNAPSHOT", "v1.2.3"),
150+
("3.4.5-SNAPSHOT", "v3.4.5"),
151+
("invalid", None),
152+
]
153+
154+
for version, expected in extraction_tests:
155+
result = extract_major_minor_patch(version)
156+
status = "✓" if result == expected else "✗"
157+
print(f"{status} {version:<25} -> {result} (expected {expected})")
158+
else:
159+
main()

0 commit comments

Comments
 (0)