Skip to content

Commit 475c611

Browse files
committed
Improve ergonomics + Github Actions
1 parent 2ff225d commit 475c611

File tree

8 files changed

+400
-147
lines changed

8 files changed

+400
-147
lines changed

.github/workflows/ci.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ main, master ]
6+
pull_request:
7+
branches: [ main, master ]
8+
9+
permissions:
10+
contents: read
11+
actions: read
12+
checks: write
13+
14+
concurrency:
15+
group: ci-${{ github.workflow }}-${{ github.ref }}
16+
cancel-in-progress: true
17+
18+
jobs:
19+
build:
20+
runs-on: ubuntu-latest
21+
22+
steps:
23+
- name: Checkout
24+
uses: actions/checkout@v4
25+
- uses: actions/setup-java@v4
26+
with:
27+
distribution: temurin
28+
java-version: '21'
29+
cache: 'gradle'
30+
- uses: actions/setup-node@v4
31+
with:
32+
node-version: '22'
33+
- uses: browser-actions/setup-chrome@v2
34+
with:
35+
chrome-version: 'stable'
36+
install-chromedriver: true
37+
- name: Setup Gradle
38+
uses: gradle/actions/setup-gradle@v3
39+
with:
40+
cache-read-only: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/master' }}
41+
- name: Install Node modules
42+
run: npm install
43+
- name: Run tests
44+
run: |
45+
npm run hmr &
46+
./gradlew --no-daemon jacocoTestReport
47+
env:
48+
HEADLESS: true
49+
- name: Upload coverage to Codecov
50+
uses: codecov/codecov-action@v5
51+
env:
52+
CODECOV_TOKEN: ${{ secrets.CODECOV_ORG_TOKEN }}
53+
- name: Publish Test Results
54+
uses: dorny/test-reporter@v1
55+
if: ${{ !cancelled() }}
56+
with:
57+
name: CI test result
58+
path: build/test-results/test/*.xml
59+
reporter: java-junit
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
name: create-release-and-docker
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
workflow_dispatch:
8+
inputs:
9+
tag:
10+
description: 'Optional tag. Default to the tag of the selected branch.'
11+
required: false
12+
type: string
13+
14+
15+
permissions:
16+
contents: write
17+
18+
jobs:
19+
create-release-and-docker:
20+
runs-on: ubuntu-latest
21+
22+
steps:
23+
- name: Checkout
24+
uses: actions/checkout@v4
25+
with:
26+
ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref_name }}
27+
- uses: actions/setup-java@v4
28+
with:
29+
distribution: temurin
30+
java-version: '21'
31+
cache: 'gradle'
32+
- uses: actions/setup-node@v4
33+
with:
34+
node-version: '22'
35+
- name: Setup Gradle
36+
uses: gradle/actions/setup-gradle@v3
37+
with:
38+
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
39+
- name: Get version
40+
id: version
41+
run: echo "VERSION=$(./gradlew -q printVersion)" >> "$GITHUB_OUTPUT"
42+
- name: Validate the tag name
43+
run: |
44+
if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ ! -z "${{ github.event.inputs.tag }}" ]; then
45+
TAG=${{ github.event.inputs.tag }}
46+
else
47+
TAG=${GITHUB_REF#refs/tags/}
48+
fi
49+
if [[ ! "$TAG" =~ ^v ]]; then
50+
echo "Error: Tag must start with 'v'"
51+
exit 1
52+
fi
53+
TAG_VERSION=${TAG#v}
54+
if [ "$TAG_VERSION" != "${{ steps.version.outputs.VERSION }}" ]; then
55+
echo "Error: Git tag version ($TAG_VERSION) doesn't match project version (v${{ steps.version.outputs.VERSION }})"
56+
exit 1
57+
fi
58+
- name: Install Node modules
59+
run: npm install
60+
- name: Build a publishable JAR
61+
run: ./gradlew clean publish
62+
- name: Upload Release Asset
63+
uses: softprops/action-gh-release@v2
64+
with:
65+
draft: true
66+
prerelease: true
67+
files: ./build/staging-deploy/io/github/tanin47/embeddable-java-web-framework/**/*
68+
overwrite_files: true
69+
fail_on_unmatched_files: true
70+
generate_release_notes: true
71+
tag_name: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref_name }}
72+
- name: Log in to Docker Hub
73+
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
74+
with:
75+
username: ${{ secrets.DOCKER_USERNAME }}
76+
password: ${{ secrets.DOCKER_PASSWORD }}
77+
- name: Set up Docker Buildx
78+
uses: docker/setup-buildx-action@v3
79+
- name: Build and push Docker image
80+
id: push
81+
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671
82+
with:
83+
platforms: linux/amd64,linux/arm64
84+
context: .
85+
file: ./Dockerfile
86+
push: true
87+
tags: tanin47/embeddable-java-web-framework:${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref_name }}

.github/workflows/publish-jar.yml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: publish-jar
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
tag:
7+
description: 'Optional tag. Default to the tag of the selected branch.'
8+
required: false
9+
type: string
10+
11+
permissions:
12+
contents: read
13+
14+
jobs:
15+
publish-jar:
16+
runs-on: ubuntu-latest
17+
18+
steps:
19+
- name: Checkout
20+
uses: actions/checkout@v4
21+
with:
22+
ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref_name }}
23+
- uses: actions/setup-java@v4
24+
with:
25+
distribution: temurin
26+
java-version: '21'
27+
cache: 'gradle'
28+
- uses: actions/setup-node@v4
29+
with:
30+
node-version: '22'
31+
- name: Setup Gradle
32+
uses: gradle/actions/setup-gradle@v3
33+
with:
34+
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
35+
- name: Get version
36+
id: version
37+
run: echo "VERSION=$(./gradlew -q printVersion)" >> "$GITHUB_OUTPUT"
38+
- name: Validate tag format and version
39+
run: |
40+
if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ ! -z "${{ github.event.inputs.tag }}" ]; then
41+
TAG=${{ github.event.inputs.tag }}
42+
else
43+
TAG=${GITHUB_REF#refs/tags/}
44+
fi
45+
if [[ ! $TAG =~ ^v ]]; then
46+
echo "Error: Tag must start with 'v'"
47+
exit 1
48+
fi
49+
if [[ ! $TAG == v${{ steps.version.outputs.VERSION }} ]]; then
50+
echo "Error: Tag version ($TAG) does not match project version (v${{ steps.version.outputs.VERSION }})"
51+
exit 1
52+
fi
53+
- name: Install Node modules
54+
run: npm install
55+
- name: Build a publishable JAR
56+
run: ./gradlew clean publish
57+
- name: Publish to Sonatype
58+
run: ./gradlew jreleaserDeploy
59+
env:
60+
JRELEASER_MAVENCENTRAL_USERNAME: ${{ secrets.JRELEASER_MAVENCENTRAL_USERNAME }}
61+
JRELEASER_MAVENCENTRAL_PASSWORD: ${{ secrets.JRELEASER_MAVENCENTRAL_PASSWORD }}
62+
CI: true
63+
JRELEASER_GPG_PUBLIC_KEY: ${{ secrets.JRELEASER_GPG_PUBLIC_KEY }}
64+
JRELEASER_GPG_SECRET_KEY: ${{ secrets.JRELEASER_GPG_SECRET_KEY }}

README.md

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ It is suitable for <ins>a sidecar-style website embeddable on a larger JVM syste
99
The main selling point of EJWF is that it comes with productive and useful conventions and libraries such as:
1010

1111
1. Support Typescripts + Svelte + Tailwind + DaisyUI with Hot-Reload Module (HMR).
12-
2. Support hot-reloading Java through the plugin sbt-revolver.
13-
3. Support packaging a fat JAR with [shading](https://stackoverflow.com/questions/13620281/what-is-the-maven-shade-plugin-used-for-and-why-would-you-want-to-relocate-java).
12+
2. Support packaging a fat JAR with [shading](https://stackoverflow.com/questions/13620281/what-is-the-maven-shade-plugin-used-for-and-why-would-you-want-to-relocate-java).
1413
The JAR is 350KB in size, has *zero* external dependencies, and eliminates any potential dependency conflict when embedding into another JVM system.
15-
4. Avoid Java reflection and magic. This is largely a feature of [Minum](https://github.com/byronka/minum). Any potential runtime errors and conflicts are minimized, which is important when embedding into a larger system.
16-
5. Browser tests are setup and ready to go.
14+
3. Avoid Java reflection and magic. This is largely a feature of [Minum](https://github.com/byronka/minum). Any potential runtime errors and conflicts are minimized, which is important when embedding into a larger system.
15+
4. Browser tests are setup and ready to go.
16+
5. Github actions for testing, code coverage reporting, and publishing have been implemented.
1717

1818
In contrast, most of the lightweight web frameworks focus on being a bare metal web server serving HTML and JSON.
1919
They don't provide support for any frontend framework like React or Svelte; you would have to do it yourself. This is exactly what EJWF provides.
2020

21-
Initially, EJWF was built as a foundation for [Backdoor](https://github.com/tanin47/backdoor), an embeddable sidecar-style JVM-based database administration tool, where
21+
Initially, EJWF was built as a foundation for [embeddable-java-web-framework](https://github.com/tanin47/embeddable-java-web-framework), a self-hosted database querying and editing tool, where
2222
you can embed it into your larger application like SpringBoot or PlayFramework.
2323

2424
How to develop
@@ -29,29 +29,49 @@ How to develop
2929
3. On a separate terminal, run `npm run hmr` in order to hot-reload the frontend code changes.
3030

3131

32-
Publish
33-
--------
32+
Publish JAR
33+
------------
34+
35+
This flow has been set up as the Github Actions workflow: `publish-jar`.
3436

3537
EJWF is a template repository with collections of libraries and conventions. It's important that you understand
3638
each build process and are able to customize to your needs.
3739

3840
Here's how you can build your fat JAR:
3941

40-
1. Run `./gradlew clean`. This step is IMPORTANT to clean out the previous versions.
41-
2. Build the tailwindbase.css with: `./node_modules/.bin/postcss ./frontend/stylesheets/tailwindbase.css --config . --output ./src/main/resources/assets/stylesheets/tailwindbase.css`
42-
3. Build the production Svelte code with: `ENABLE_SVELTE_CHECK=true ./node_modules/webpack/bin/webpack.js --config ./webpack.config.js --output-path ./src/main/resources/assets --mode production`
43-
4. Build the fat JAR with: `./gradlew shadowJar`
42+
1. Run `./gradlew clean publish`. This step is IMPORTANT to clean out the previous versions.
4443

4544
The far JAR is built at `./build/libs/embeddablee-java-web-framework-VERSION.jar`
4645

4746
You can run your server with: `java -jar ./build/libs/embeddable-java-web-framework-VERSION.jar`
4847

4948
To publish to a Maven repository, please follow the below steps:
5049

51-
1. Remove `./build/staging-deploy` by running `rm -rf ./build/staging-deploy`
52-
2. Run `./gradlew publish`
53-
3. Set up `~/.jreleaser/config.toml` with `JRELEASER_MAVENCENTRAL_USERNAME` and `JRELEASER_MAVENCENTRAL_PASSWORD`
54-
4. Run `./gradlew jreleaserDeploy`
50+
1. Set up `~/.jreleaser/config.toml` with `JRELEASER_MAVENCENTRAL_USERNAME` and `JRELEASER_MAVENCENTRAL_PASSWORD`
51+
2. Run `./gradlew jreleaserDeploy`
52+
53+
54+
Publish Docker
55+
---------------
56+
57+
This flow has been set up as a part of the Github Actions workflow: `create-release-and-docker`.
58+
59+
1. Run `docker buildx build --platform linux/amd64,linux/arm64 -t embeddable-java-web-framework:v0.4.0 .`
60+
2. Test locally with:
61+
`docker run -p 9090:9090 --entrypoint "" embeddable-java-web-framework:v0.4.0 java -jar embeddable-java-web-framework-0.4.0.jar -port 9090`
62+
3. Run: `docker tag embeddable-java-web-framework:v0.4.0 tanin47/embeddable-java-web-framework:v0.4.0`
63+
4. Run: `docker push tanin47/embeddable-java-web-framework:v0.4.0`
64+
5. Go to Render.com, sync the blueprint, and test that it works
65+
66+
Release a new version
67+
----------------------
68+
69+
1. Create an empty release with a new tag. The tag must follow the format: `vX.Y.Z`.
70+
2. Go to Actions and wait for the `create-release-and-docker` (which is triggered automatically) workflow to finish.
71+
3. Test the docker with
72+
`docker run -p 9090:9090 --entrypoint "" tanin47/embeddable-java-web-framework:v0.4.0 java -jar embeddable-java-web-framework-0.4.0.jar -port 9090`.
73+
4. Go to Actions and trigger the workflow `publish-jar` on the tag `vX.Y.Z` in order to publish the JAR to Central
74+
Sonatype.
5575

5676
Embed your website into a larger system
5777
----------------------------------------

build.gradle.kts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1+
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
12
import org.jreleaser.model.Active
23
import org.jreleaser.model.Signing.Mode
34

45
plugins {
56
`java-library`
67
application
78
`maven-publish`
9+
jacoco
810
id("org.jreleaser") version "1.21.0"
911
id("com.gradleup.shadow") version "9.2.2"
1012
}
1113

1214
group = "tanin.ejwf"
13-
version = "0.4.0"
15+
version = "1.0.0-rc1"
1416

1517
description = "Embeddable Java Web Framework (EJWF)"
1618

@@ -29,6 +31,16 @@ java {
2931
}
3032
}
3133

34+
tasks.jacocoTestReport {
35+
dependsOn(tasks.test) // tests are required to run before generating the report
36+
37+
reports {
38+
xml.required = true
39+
csv.required = false
40+
html.outputLocation = layout.buildDirectory.dir("jacocoHtml")
41+
}
42+
}
43+
3244
repositories {
3345
mavenCentral()
3446
}
@@ -47,8 +59,14 @@ tasks.named<Test>("test") {
4759
maxHeapSize = "1G"
4860

4961
testLogging {
50-
events("passed")
62+
events("started", "passed", "skipped", "failed")
63+
showStandardStreams = true
64+
showStackTraces = true
65+
showExceptions = true
66+
showCauses = true
67+
exceptionFormat = TestExceptionFormat.FULL
5168
}
69+
5270
}
5371

5472
application {

0 commit comments

Comments
 (0)