diff --git a/.circleci/config.yml b/.circleci/config.yml index f8394fc2..9bb01639 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,10 +1,6 @@ version: 2 jobs: build: - environment: - GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError"' - _JAVA_OPTIONS: "-Xms512m -Xmx1024m" - TERM: dumb docker: - image: openjdk:8-jdk steps: @@ -21,8 +17,12 @@ jobs: - run: name: Upload Coverage when: on_success - command: bash <(curl -s https://codecov.io/bash) + command: bash <(curl -s https://codecov.io/bash) -Z -C $CIRCLE_SHA1 - save_cache: paths: - ~/.m2 - key: v1-dependencies-{{ checksum "build.gradle" }} \ No newline at end of file + key: v1-dependencies-{{ checksum "build.gradle" }} + environment: + GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError"' + _JAVA_OPTIONS: "-Xms512m -Xmx1024m" + TERM: dumb diff --git a/.gitignore b/.gitignore index ab35b855..5ab9b34a 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ Temporary Items ## Plugin-specific files: # IntelliJ +bin/ /out/ /lib/out/ diff --git a/CHANGELOG.md b/CHANGELOG.md index ba66c602..6bce7fb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Change Log +## [3.5.0](https://github.com/auth0/java-jwt/tree/3.5.0) (2019-01-03) +[Full Changelog](https://github.com/auth0/java-jwt/compare/3.4.1...3.5.0) + +**Added** +- Verify a DecodedJWT [\#308](https://github.com/auth0/java-jwt/pull/308) ([martinoconnor](https://github.com/martinoconnor)) + +**Changed** +- Add an interface for JWTVerifier. [\#205](https://github.com/auth0/java-jwt/pull/205) ([jebbench](https://github.com/jebbench)) + +**Fixed** +- Remove unnecessary cast between long/double and floor call [\#296](https://github.com/auth0/java-jwt/pull/296) ([jhorstmann](https://github.com/jhorstmann)) + +**Security** +- Bump jackson-databind to patch security issues [\#309](https://github.com/auth0/java-jwt/pull/309) ([lbalmaceda](https://github.com/lbalmaceda)) + +## [3.4.1](https://github.com/auth0/java-jwt/tree/3.4.1) (2018-10-24) +[Full Changelog](https://github.com/auth0/java-jwt/compare/3.4.0...3.4.1) + +**Security** +- Update jackson-databind dependency [\#292](https://github.com/auth0/java-jwt/pull/292) ([lbalmaceda](https://github.com/lbalmaceda)) + ## [3.4.0](https://github.com/auth0/java-jwt/tree/3.4.0) (2018-06-13) [Full Changelog](https://github.com/auth0/java-jwt/compare/3.3.0...3.4.0) diff --git a/README.md b/README.md index b0150031..79d7a74d 100644 --- a/README.md +++ b/README.md @@ -18,14 +18,14 @@ If you're looking for an **Android** version of the JWT Decoder take a look at o com.auth0 java-jwt - 3.4.0 + 3.5.0 ``` ### Gradle ```gradle -compile 'com.auth0:java-jwt:3.4.0' +implementation 'com.auth0:java-jwt:3.5.0' ``` ## Available Algorithms @@ -74,8 +74,7 @@ By using a `KeyProvider` you can change in runtime the key used either to verify - `getPrivateKeyId()`: Its called during token signing and it should return the id of the key that identifies the one returned by `getPrivateKey()`. This value is preferred over the one set in the `JWTCreator.Builder#withKeyId(String)` method. If you don't need to set a `kid` value avoid instantiating an Algorithm using a `KeyProvider`. -The following snippet uses example classes showing how this would work: - +The following example shows how this would work with `JwkStore`, an imaginary [JWK Set](https://auth0.com/docs/jwks) implementation. For simple key rotation using JWKS, try the [jwks-rsa-java](https://github.com/auth0/jwks-rsa-java) library. ```java final JwkStore jwkStore = new JwkStore("{JWKS_FILE_HOST}"); @@ -105,9 +104,6 @@ Algorithm algorithm = Algorithm.RSA256(keyProvider); //Use the Algorithm to create and verify JWTs. ``` -> For simple key rotation using JWKs try the [jwks-rsa-java](https://github.com/auth0/jwks-rsa-java) library. - - ### Create and Sign a Token You'll first need to create a `JWTCreator` instance by calling `JWT.create()`. Use the builder to define the custom Claims your token needs to have. Finally to get the String token call `sign()` and pass the `Algorithm` instance. @@ -187,7 +183,7 @@ If the token has an invalid signature or the Claim requirement is not met, a `JW The JWT token may include DateNumber fields that can be used to validate that: * The token was issued in a past date `"iat" < TODAY` * The token hasn't expired yet `"exp" > TODAY` and -* The token can already be used. `"nbf" > TODAY` +* The token can already be used. `"nbf" < TODAY` When verifying a token the time validation occurs automatically, resulting in a `JWTVerificationException` being throw when the values are invalid. If any of the previous fields are missing they won't be considered in this validation. diff --git a/build.gradle b/build.gradle index f1dc45a3..bf6dfe4f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,5 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. -buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.1' - } -} - allprojects { group = 'com.auth0' diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 13372aef..29953ea1 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 04e285f3..d76b502e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Mon Dec 28 10:00:20 PST 2015 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/gradlew b/gradlew index 9d82f789..cccdd3d5 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -6,20 +6,38 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,26 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -85,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -150,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index aec99730..e95643d6 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +46,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line diff --git a/lib/build.gradle b/lib/build.gradle index 7da318e5..c13635e4 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -1,30 +1,30 @@ -apply plugin: 'jacoco' -apply plugin: 'java' -apply from: '../scripts/release.gradle' -apply from: '../scripts/maven.gradle' -apply from: '../scripts/bintray.gradle' +plugins { + id "com.jfrog.bintray" version "1.8.4" + id "com.auth0.gradle.oss-library.java" version "0.8.0" + id "jacoco" +} logger.lifecycle("Using version ${version} for ${group}.${name}") -auth0 { +oss { name "java jwt" - repo "java-jwt" + repository "java-jwt" + organization "auth0" description "Java implementation of JSON Web Token (JWT)" - url 'http://www.jwt.io' - developer { - id = "auth0" - name = "Auth0" - email = "oss@auth0.com" - } - developer { - id = "lbalmaceda" - name = "Luciano Balmaceda" - email = "luciano.balmaceda@auth0.com" - } - developer { - id = "hzalaz" - name = "Hernan Zalazar" - email = "hernan@auth0.com" + + developers { + auth0 { + displayName = "Auth0" + email = "oss@auth0.com" + } + lbalmaceda { + displayName = "Luciano Balmaceda" + email = "luciano.balmaceda@auth0.com" + } + hzalaz { + displayName = "Hernan Zalazar" + email = "hernan@auth0.com" + } } } @@ -34,13 +34,13 @@ compileJava { } dependencies { - compile 'com.fasterxml.jackson.core:jackson-databind:2.9.6' - compile 'commons-codec:commons-codec:1.11' - testCompile 'org.bouncycastle:bcprov-jdk15on:1.59' - testCompile 'junit:junit:4.12' - testCompile 'net.jodah:concurrentunit:0.4.3' - testCompile 'org.hamcrest:java-hamcrest:2.0.0.0' - testCompile 'org.mockito:mockito-core:2.18.3' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8' + implementation 'commons-codec:commons-codec:1.11' + testImplementation 'org.bouncycastle:bcprov-jdk15on:1.59' + testImplementation 'junit:junit:4.12' + testImplementation 'net.jodah:concurrentunit:0.4.3' + testImplementation 'org.hamcrest:java-hamcrest:2.0.0.0' + testImplementation 'org.mockito:mockito-core:2.18.3' } jacocoTestReport { @@ -55,9 +55,4 @@ test { events "skipped", "failed", "standardError" exceptionFormat "short" } -} - -task clean(type: Delete) { - delete rootProject.buildDir - delete 'CHANGELOG.md.release' -} +} \ No newline at end of file diff --git a/lib/src/main/java/com/auth0/jwt/JWTVerifier.java b/lib/src/main/java/com/auth0/jwt/JWTVerifier.java index dcd9b850..26de4034 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTVerifier.java +++ b/lib/src/main/java/com/auth0/jwt/JWTVerifier.java @@ -14,7 +14,7 @@ * The JWTVerifier class holds the verify method to assert that a given Token has not only a proper JWT format, but also it's signature matches. */ @SuppressWarnings("WeakerAccess") -public final class JWTVerifier { +public final class JWTVerifier implements com.auth0.jwt.interfaces.JWTVerifier { private final Algorithm algorithm; final Map claims; private final Clock clock; @@ -361,8 +361,24 @@ private void requireClaim(String name, Object value) { * @throws TokenExpiredException if the token has expired. * @throws InvalidClaimException if a claim contained a different value than the expected one. */ + @Override public DecodedJWT verify(String token) throws JWTVerificationException { DecodedJWT jwt = JWT.decode(token); + return verify(jwt); + } + + /** + * Perform the verification against the given decoded JWT, using any previous configured options. + * + * @param jwt to verify. + * @return a verified and decoded JWT. + * @throws AlgorithmMismatchException if the algorithm stated in the token's header it's not equal to the one defined in the {@link JWTVerifier}. + * @throws SignatureVerificationException if the signature is invalid. + * @throws TokenExpiredException if the token has expired. + * @throws InvalidClaimException if a claim contained a different value than the expected one. + */ + @Override + public DecodedJWT verify(DecodedJWT jwt) throws JWTVerificationException { verifyAlgorithm(jwt, algorithm); algorithm.verify(jwt); verifyClaims(jwt, claims); @@ -440,7 +456,7 @@ private void assertValidStringClaim(String claimName, String value, String expec private void assertValidDateClaim(Date date, long leeway, boolean shouldBeFuture) { Date today = clock.getToday(); - today.setTime((long) Math.floor((today.getTime() / 1000) * 1000)); // truncate millis + today.setTime(today.getTime() / 1000 * 1000); // truncate millis if (shouldBeFuture) { assertDateIsFuture(date, leeway, today); } else { diff --git a/lib/src/main/java/com/auth0/jwt/interfaces/JWTVerifier.java b/lib/src/main/java/com/auth0/jwt/interfaces/JWTVerifier.java new file mode 100644 index 00000000..140af8e6 --- /dev/null +++ b/lib/src/main/java/com/auth0/jwt/interfaces/JWTVerifier.java @@ -0,0 +1,25 @@ +package com.auth0.jwt.interfaces; + +import com.auth0.jwt.exceptions.JWTVerificationException; + + +public interface JWTVerifier { + + /** + * Performs the verification against the given Token + * + * @param token to verify. + * @return a verified and decoded JWT. + * @throws JWTVerificationException if any of the verification steps fail + */ + DecodedJWT verify(String token) throws JWTVerificationException; + + /** + * Performs the verification against the given decoded JWT + * + * @param jwt to verify. + * @return a verified and decoded JWT. + * @throws JWTVerificationException if any of the verification steps fail + */ + DecodedJWT verify(DecodedJWT jwt) throws JWTVerificationException; +} diff --git a/lib/src/test/java/com/auth0/jwt/JWTTest.java b/lib/src/test/java/com/auth0/jwt/JWTTest.java index 31df4494..579ea271 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTTest.java @@ -58,6 +58,18 @@ public void shouldGetStringToken() throws Exception { // Verify + @Test + public void shouldVerifyDecodedToken() throws Exception { + String token = "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.mvL5LoMyIrWYjk5umEXZTmbyIrkbbcVPUkvdGZbu0qFBxGOf0nXP5PZBvPcOu084lvpwVox5n3VaD4iqzW-PsJyvKFgi5TnwmsbKchAp7JexQEsQOnTSGcfRqeUUiBZqRQdYsho71oAB3T4FnalDdFEpM-fztcZY9XqKyayqZLreTeBjqJm4jfOWH7KfGBHgZExQhe96NLq1UA9eUyQwdOA1Z0SgXe4Ja5PxZ6Fm37KnVDtDlNnY4JAAGFo6y74aGNnp_BKgpaVJCGFu1f1S5xCQ1HSvs8ZSdVWs5NgawW3wRd0kRt_GJ_Y3mIwiF4qUyHWGtsSHu_qjVdCTtbFyow"; + DecodedJWT decodedJWT = JWT.decode(token); + RSAKey key = (RSAKey) PemUtils.readPublicKeyFromFile(PUBLIC_KEY_FILE_RSA, "RSA"); + DecodedJWT jwt = JWT.require(Algorithm.RSA512(key)) + .build() + .verify(decodedJWT); + + assertThat(jwt, is(notNullValue())); + } + @Test public void shouldAcceptNoneAlgorithm() throws Exception { String token = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJhdXRoMCJ9."; diff --git a/scripts/bintray.gradle b/scripts/bintray.gradle deleted file mode 100644 index ba868123..00000000 --- a/scripts/bintray.gradle +++ /dev/null @@ -1,55 +0,0 @@ -def credentials = new Bintray(project); - -if (credentials.valid()) { - apply plugin: 'com.jfrog.bintray' - bintray { - user = credentials.user - key = credentials.key - publications = ['mavenJava'] - dryRun = project.version.endsWith("-SNAPSHOT") - publish = false - pkg { - repo = 'java' - name = 'java-jwt' - desc = 'Java implementation of JSON Web Token (JWT) ' - websiteUrl = 'https://github.com/auth0/java-jwt' - vcsUrl = 'scm:git@github.com:auth0/java-jwt.git' - licenses = ["MIT"] - userOrg = 'auth0' - publish = false - version { - gpg { - sign = true - passphrase = credentials.passphrasse - } - vcsTag = project.version - name = project.version - released = new Date() - } - } - } -} - -class Bintray { - String user - String key - String passphrasse - - Bintray(project) { - this.user = Bintray.value(project, 'BINTRAY_USER', 'bintray.user') - this.key = Bintray.value(project, 'BINTRAY_KEY', 'bintray.key') - this.passphrasse = Bintray.value(project, 'BINTRAY_PASSPHRASE', 'bintray.gpg.password') - } - - def valid() { - return this.user != null && this.key != null && this.passphrasse != null; - } - - private static def value(Project project, String env, String property) { - def value = System.getenv(env) - if (project.hasProperty(property)) { - value = project.getProperty(property); - } - return value - } -} \ No newline at end of file diff --git a/scripts/maven.gradle b/scripts/maven.gradle deleted file mode 100644 index a541b008..00000000 --- a/scripts/maven.gradle +++ /dev/null @@ -1,105 +0,0 @@ -apply plugin: Auth0OSS - -class Auth0OSS implements Plugin { - - void apply(Project target) { - target.extensions.create("auth0", Auth0Extension, target) - target.configure(target) { - apply plugin: 'maven-publish' - - target.task("sourcesJar", type: Jar, dependsOn: classes) { - classifier = 'sources' - from sourceSets.main.allSource - } - target.task("javadocJar", type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.getDestinationDir() - } - - artifacts { - archives sourcesJar, javadocJar - } - - publishing { - publications { - mavenJava(MavenPublication) { - from components.java - artifact sourcesJar - artifact javadocJar - groupId project.group - artifactId project.name - version project.version - } - } - } - - publishing.publications.all { - pom.withXml { - - def lib = project.extensions.auth0 - def root = asNode() - - root.appendNode('packaging', 'jar') - root.appendNode('name', lib.name) - root.appendNode('description', lib.description) - root.appendNode('url', lib.url) - - def developersNode = root.appendNode('developers') - project.extensions.auth0.developers.each { - def node = developersNode.appendNode('developer') - node.appendNode('id', it.id) - node.appendNode('name', it.name) - node.appendNode('email', it.email) - } - - def dependenciesNode = root.appendNode('dependencies') - - configurations.compile.allDependencies.each { - def dependencyNode = dependenciesNode.appendNode('dependency') - dependencyNode.appendNode('groupId', it.group) - dependencyNode.appendNode('artifactId', it.name) - dependencyNode.appendNode('version', it.version) - } - - def licenceNode = root.appendNode('licenses').appendNode('license') - licenceNode.appendNode('name', 'The MIT License (MIT)') - licenceNode.appendNode('url', "https://raw.githubusercontent.com/auth0/${lib.repo}/master/LICENSE") - licenceNode.appendNode('distribution', 'repo') - - def scmNode = root.appendNode('scm') - scmNode.appendNode('connection', "scm:git@github.com:auth0/${lib.repo}.git") - scmNode.appendNode('developerConnection', "scm:git@github.com:auth0/${lib.repo}.git") - scmNode.appendNode('url', "https://github.com/auth0/${lib.repo}") - } - } - } - } -} - -class Auth0Extension { - String name - String repo - String description - String url - List developers = [] - - private Project project - - Auth0Extension(project) { - this.project = project - } - - void developer(Closure developerClosure) { - def developer = project.configure(new Developer(), developerClosure) - developers.add(developer) - } -} - -class Developer { - String id - String name - String email -} - - - diff --git a/scripts/release.gradle b/scripts/release.gradle deleted file mode 100644 index da74e9b5..00000000 --- a/scripts/release.gradle +++ /dev/null @@ -1,170 +0,0 @@ -import java.text.SimpleDateFormat - -apply plugin: ReleasePlugin - -class Semver { - String version - def snapshot - - def getStringVersion() { - return snapshot ? "$version-SNAPSHOT" : version - } - - def nextPatch() { - def parts = version.split("\\.") - def patch = Integer.parseInt(parts[2]) + 1 - return "${parts[0]}.${parts[1]}.${patch}" - } - - def nextMinor() { - def parts = version.split("\\.") - def minor = Integer.parseInt(parts[1]) + 1 - return "${parts[0]}.${minor}.0" - } - -} - -class ChangeLogTask extends DefaultTask { - - def current - def next - - @TaskAction - def update() { - def repository = project.auth0.repo - def file = new File('CHANGELOG.md') - def output = new File('CHANGELOG.md.release') - output.newWriter().withWriter { writer -> - - file.eachLine { line, number -> - if (number == 0 && !line.startsWith('# Change Log')) { - throw new GradleException('Change Log file is not properly formatted') - } - - writer.println(line) - - if (number == 0 || number > 1) { - return - } - - def formatter = new SimpleDateFormat('yyyy-MM-dd') - writer.println() - writer.println("## [${next}](https://github.com/auth0/${repository}/tree/${next}) (${formatter.format(new Date())})") - writer.println("[Full Changelog](https://github.com/auth0/${repository}/compare/${current}...${next})") - def command = ["curl", "https://webtask.it.auth0.com/api/run/wt-hernan-auth0_com-0/oss-changelog.js?webtask_no_cache=1&repo=${repository}&milestone=${next}", "-f", "-s", "-H", "Accept: text/markdown"] - def content = command.execute() - content.consumeProcessOutputStream(writer) - if (content.waitFor() != 0) { - throw new GradleException("Failed to request changelog for version ${next}") - } - } - } - file.delete() - output.renameTo('CHANGELOG.md') - } -} - -class ReleaseTask extends DefaultTask { - - def tagName - - @TaskAction - def perform() { - def path = project.getRootProject().getProjectDir().path - project.exec { - commandLine 'git', 'add', 'README.md' - workingDir path - } - project.exec { - commandLine 'git', 'add', 'CHANGELOG.md' - workingDir path - } - project.exec { - commandLine 'git', 'commit', '-m', "Release ${tagName}" - workingDir path - } - project.exec { - commandLine 'git', 'tag', "${tagName}" - workingDir path - } - } -} - -class ReadmeTask extends DefaultTask { - - def current - def next - - final filename = 'README.md' - - @TaskAction - def update() { - def file = new File(filename) - def gradleUpdated = "compile '${project.group}:${project.name}:${next}'" - def oldSingleQuote = "compile '${project.group}:${project.name}:${current}'" - def oldDoubleQuote = "compile \"${project.group}:${project.name}:${current}\"" - def mavenUpdated = "${next}" - def mavenOld = "${current}" - def contents = file.getText('UTF-8') - contents = contents.replace(oldSingleQuote, gradleUpdated).replace(oldDoubleQuote, gradleUpdated).replace(mavenOld, mavenUpdated) - file.write(contents, 'UTF-8') - } - -} - -class ReleasePlugin implements Plugin { - void apply(Project target) { - def semver = current() - target.version = semver.stringVersion - def version = semver.version - def nextMinor = semver.nextMinor() - def nextPatch = semver.nextPatch() - target.task('changelogMinor', type: ChangeLogTask) { - current = version - next = nextMinor - } - target.task('changelogPatch', type: ChangeLogTask) { - current = version - next = nextPatch - } - target.task('readmeMinor', type: ReadmeTask, dependsOn: 'changelogMinor') { - current = version - next = nextMinor - } - target.task('readmePatch', type: ReadmeTask, dependsOn: 'changelogPatch') { - current = version - next = nextPatch - } - target.task('releaseMinor', type: ReleaseTask, dependsOn: 'readmeMinor') { - tagName = nextMinor - } - target.task('releasePatch', type: ReleaseTask, dependsOn: 'readmePatch') { - tagName = nextPatch - } - } - - static def current() { - def current = describeGit(false) - def snapshot = current == null - if (snapshot) { - current = describeGit(snapshot, '0.0.1') - } - return new Semver(snapshot: snapshot, version: current) - } - - static def describeGit(boolean snapshot, String defaultValue = null) { - def command = ['git', 'describe', '--tags', (snapshot ? '--abbrev=0' : '--exact-match')].execute() - def stdout = new ByteArrayOutputStream() - def errout = new ByteArrayOutputStream() - command.consumeProcessOutput(stdout, errout) - if (command.waitFor() != 0) { - Logging.getLogger(ReleasePlugin.class).debug(errout.toString()) - return defaultValue - } - if (stdout.toByteArray().length > 0) { - return stdout.toString().replace('\n', "") - } - - return defaultValue - } -} \ No newline at end of file