Skip to content

Commit

Permalink
spring boot starter for Arline CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
fprochazka committed Nov 22, 2024
1 parent af5b000 commit b969da2
Show file tree
Hide file tree
Showing 30 changed files with 1,327 additions and 1 deletion.
36 changes: 36 additions & 0 deletions .github/workflows/dependency-submission.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Dependency Submission

on:
push:
branches: [ 'master' ]

permissions:
contents: write

env:
GRADLE_OPTS: "-Dorg.gradle.console=plain -Dorg.gradle.daemon=false -Dorg.gradle.stacktrace=always"

jobs:
dependency-submission:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21

- name: Generate and submit dependency graph
id: dependency-submission
uses: gradle/actions/dependency-submission@v4
with:
dependency-graph-exclude-projects: ':(testing-*)'
dependency-graph-exclude-configurations: '.*[Tt]est(Compile|Runtime)Classpath'

- name: Also upload the raw dependency graph as an artifact
uses: actions/upload-artifact@v3
with:
name: gradle-dependency-graph-file
path: ${{ steps.dependency-submission.outputs.dependency-graph-file }}
retention-days: 1
42 changes: 42 additions & 0 deletions .github/workflows/gradle-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Run Checks via Gradle

on:
push:
branches:
- master
pull_request:

env:
GRADLE_OPTS: "-Dorg.gradle.console=plain -Dorg.gradle.daemon=false -Dorg.gradle.stacktrace=always"

jobs:
gradle:
strategy:
matrix:
java: [21, 22, 23]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: ${{ matrix.java }}
cache: gradle

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
with:
add-job-summary-as-pr-comment: on-failure

- name: Execute Gradle build
run: ./gradlew --continue build

- name: Upload Test Report
uses: actions/upload-artifact@v3
if: always() # always run even if the previous step fails
with:
name: junit-test-results
path: '**/build/test-results/test/TEST-*.xml'
retention-days: 1
76 changes: 76 additions & 0 deletions .github/workflows/jreleaser-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: Release using JReleaser

on:
workflow_dispatch:
inputs:
version:
description: 'Release version'
required: true
nextVersion:
description: 'Next version after release (-SNAPSHOT will be added automatically)'
required: true

env:
GRADLE_OPTS: "-Dorg.gradle.console=plain -Dorg.gradle.daemon=false -Dorg.gradle.stacktrace=always"

jobs:
release:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v3
with:
token: ${{ secrets.JRELEASER_GITHUB_TOKEN }}
fetch-depth: 0

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
cache: gradle

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

- name: Gradle build and publish to local
run: ./gradlew publish -Pversion=${{ github.event.inputs.version }}

- name: JReleaser full-release
uses: jreleaser/release-action@v2
with:
setup-java: false
env:
JRELEASER_STRICT: "true"
JRELEASER_OUTPUT_DIRECTORY: "build/"
JRELEASER_PROJECT_VERSION: ${{ github.event.inputs.version }}
JRELEASER_GITHUB_TOKEN: ${{ secrets.JRELEASER_GITHUB_TOKEN }}
JRELEASER_MAVENCENTRAL_USERNAME: ${{ secrets.JRELEASER_MAVENCENTRAL_USERNAME }}
JRELEASER_MAVENCENTRAL_TOKEN: ${{ secrets.JRELEASER_MAVENCENTRAL_TOKEN }}
JRELEASER_GPG_KEYNAME: ${{ secrets.JRELEASER_GPG_KEYNAME }}
JRELEASER_GPG_PASSPHRASE: ${{ secrets.JRELEASER_GPG_PASSPHRASE }}
JRELEASER_GPG_PUBLIC_KEY: ${{ secrets.JRELEASER_GPG_PUBLIC_KEY }}
JRELEASER_GPG_SECRET_KEY: ${{ secrets.JRELEASER_GPG_SECRET_KEY }}

- name: JReleaser upload output
if: always()
uses: actions/upload-artifact@v4
with:
name: jreleaser-release
path: |
build/jreleaser/trace.log
build/jreleaser/output.properties
- name: Update version in properties
run: sed -i 's/^version=.*/version=${{ github.event.inputs.nextVersion }}-SNAPSHOT/g' gradle.properties

- name: Commit & Push version update
uses: actions-js/push@master
with:
github_token: ${{ secrets.JRELEASER_GITHUB_TOKEN }}
message: "chore: bump version to ${{ github.event.inputs.nextVersion }}-SNAPSHOT"
author_email: '[email protected]'
author_name: 'Framefork BOT'
branch: master
tags: false
27 changes: 27 additions & 0 deletions .github/workflows/publish-test-results.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Publish Test Results

on:
workflow_run:
workflows: [ 'Run Checks via Gradle' ]
types: [ completed ]

permissions:
checks: write

jobs:
checks:
runs-on: ubuntu-latest
steps:
- name: Download Test Report
uses: dawidd6/action-download-artifact@v2
with:
name: junit-test-results
workflow: ${{ github.event.workflow.id }}
run_id: ${{ github.event.workflow_run.id }}

- name: Publish Test Report
uses: mikepenz/action-junit-report@v5
with:
commit: ${{github.event.workflow_run.head_sha}}
report_paths: '**/build/test-results/test/TEST-*.xml'
detailed_summary: true
1 change: 1 addition & 0 deletions .sdkmanrc
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
java=21.0.5-tem
jreleaser=1.15.0
77 changes: 77 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Airline CLI Spring Boot Starter

The [Airline CLI](https://rvesse.github.io/airline/guide/) is a Java library providing an annotation-based framework for parsing command line interfaces.
This project aims to integrate the Airline into Spring and serve as an application entrypoint.

## Installation

Install the latest `org.framefork:airline-cli-spring-boot-starter:*` and you're good to go.

## Usage

### 1. create an entrypoint

```java
import static org.framefork.cli.airline.CliMainClass.runOneCommand;

@SpringBootApplication
public class TestingSpringApplicationMock
{
public static void main(final String[] args)
{
runOneCommand(TestingSpringApplicationMock.class, args);
}
}
```

If you need to, you can use the third argument to customize the `SpringApplicationBuilder`

```java
public static void main(final String[] args)
{
runOneCommand(TestingSpringApplicationMock.class, args, builder -> {
builder.profiles("cli");
});
}
```

### 2. define commands

The command must implement `org.framefork.cli.airline.ExecutableCommand`, so that there is a single main method that will be called for the command.
And be annotated with `org.framefork.cli.airline.CliCommand`, which makes it a Spring bean with prototype scope.

```java
@CliCommand
public class MyCommand implements ExecutableCommand
{

@Option(name = {"--dry-run"}, description = "An option that requires no values")
private boolean dryRun = false;

@Override
public void execute()
{
if (dryRun) {
// dry run
} else {
// real run
}
}

}
```

### 3. profit

Now you should be able to, using your new main class, execute one-off commands.

## How it works

When you execute a command

1. the application is started in non-web mode, the `runOneCommand()` takes care of that
2. the console arguments are parsed and validated
3. Airline uses the bridge in this project to ask spring for the command instance, which is created from the prototype bean
4. Airline binds all the options and arguments to the command, making them available to the command
5. the `execute()` method is called
6. if at any point the handling ends with an error, its printed
49 changes: 49 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
plugins {
id("base")
id("idea")
id("org.barfuin.gradle.taskinfo") version ("2.2.0") // ./gradlew tiTree publish
id("be.vbgn.ci-detect") version ("0.5.0")
}

repositories {
mavenCentral()
mavenLocal()
}

group = "org.framefork"
version = (properties["version"] as String).trim()

allprojects {
group = rootProject.group
version = rootProject.version
}

tasks.withType<Wrapper> {
distributionType = Wrapper.DistributionType.ALL
}

tasks.register<Delete>("cleanAllPublications") {
outputs.upToDateWhen { false }
setDelete(rootProject.layout.buildDirectory.dir("staging-deploy"))
}

allprojects {
apply(plugin = "project-report")

this.task("allDependencies", DependencyReportTask::class) {
evaluationDependsOnChildren()
this.setRenderer(org.gradle.api.tasks.diagnostics.internal.dependencies.AsciiDependencyReportRenderer().apply {
outputFile = file(project.layout.buildDirectory.file("reports/dependencies.txt"))
})
}
}

gradle.projectsEvaluated {
// Make sure tests of individual modules are executed sequentially
val testTasks = subprojects
.flatMap { it.tasks.withType<Test>() }
.sortedBy { it.project.path } // Sort to ensure a consistent order
for (i in 1 until testTasks.size) {
testTasks[i].mustRunAfter(testTasks[i - 1])
}
}
15 changes: 15 additions & 0 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
plugins {
`kotlin-dsl`
}

repositories {
gradlePluginPortal()
mavenCentral()
}

dependencies {
implementation("com.adarshr:gradle-test-logger-plugin:4.0.0")
implementation("io.github.joselion:strict-null-check:3.5.0")
implementation("net.ltgt.gradle:gradle-errorprone-plugin:4.1.0")
implementation("net.ltgt.gradle:gradle-nullaway-plugin:2.1.0")
}
63 changes: 63 additions & 0 deletions buildSrc/src/main/kotlin/framefork.java-public.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
plugins {
id("framefork.java")
`maven-publish`
}

java {
withJavadocJar()
withSourcesJar()
}

publishing {
publications {
create<MavenPublication>("mavenStaging") {
from(components["java"])

pom {
url = "https://github.com/framefork/airline-cli-spring-boot-starter"
inceptionYear = "2024"
licenses {
license {
name = "Apache-2.0"
url = "https://spdx.org/licenses/Apache-2.0.html"
}
}
organization {
name = "Framefork"
url = "https://github.com/framefork"
}
developers {
developer {
id = "fprochazka"
name = "Filip Procházka"
url = "https://filip-prochazka.com/"
}
}
scm {
connection = "scm:git:https://github.com/framefork/airline-cli-spring-boot-starter.git"
developerConnection = "scm:git:ssh://github.com/framefork/airline-cli-spring-boot-starter.git"
url = "https://github.com/framefork/airline-cli-spring-boot-starter"
}
issueManagement {
system = "GitHub"
url = "https://github.com/framefork/airline-cli-spring-boot-starter/issues"
}
}

afterEvaluate {
pom.name = "${project.group}:${project.name}"
pom.description = project.description
}
}
}

repositories {
maven {
url = uri(rootProject.layout.buildDirectory.dir("staging-deploy"))
}
}
}

tasks.named("publish") {
dependsOn(rootProject.tasks.named("cleanAllPublications"))
}
Loading

0 comments on commit b969da2

Please sign in to comment.