Skip to content

Commit f69d5b2

Browse files
authored
Merge pull request #3268 from AndreMiras/feature/java_linting
👷 Add Spotless Java linting to CI pipeline
2 parents d467b7c + ab65dc4 commit f69d5b2

File tree

24 files changed

+1097
-1007
lines changed

24 files changed

+1097
-1007
lines changed

.github/workflows/push.yml

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,26 @@ jobs:
3030
pip install tox>=2.0
3131
tox -e pep8
3232
33+
spotless:
34+
name: Java Spotless check
35+
runs-on: ubuntu-latest
36+
steps:
37+
- name: Checkout python-for-android
38+
uses: actions/checkout@v5
39+
- name: Set up Java 17
40+
uses: actions/setup-java@v4
41+
with:
42+
distribution: 'temurin'
43+
java-version: '17'
44+
- name: Set up Gradle
45+
uses: gradle/actions/setup-gradle@v4
46+
- name: Run Spotless check
47+
working-directory: pythonforandroid/bootstraps
48+
run: ./common/build/gradlew spotlessCheck
49+
3350
test:
3451
name: Pytest [Python ${{ matrix.python-version }} | ${{ matrix.os }}]
35-
needs: flake8
52+
needs: [flake8, spotless]
3653
runs-on: ${{ matrix.os }}
3754
strategy:
3855
fail-fast: false
@@ -61,7 +78,7 @@ jobs:
6178

6279
ubuntu_build:
6380
name: Build test APP [ ${{ matrix.runs_on }} | ${{ matrix.bootstrap.name }} ]
64-
needs: [flake8]
81+
needs: [flake8, spotless]
6582
runs-on: ${{ matrix.runs_on }}
6683
strategy:
6784
matrix:
@@ -120,7 +137,7 @@ jobs:
120137

121138
macos_build:
122139
name: Build test APP [ ${{ matrix.runs_on }} | ${{ matrix.bootstrap.name }} ]
123-
needs: [flake8]
140+
needs: [flake8, spotless]
124141
runs-on: ${{ matrix.runs_on }}
125142
strategy:
126143
matrix:
@@ -194,7 +211,7 @@ jobs:
194211

195212
ubuntu_rebuild_updated_recipes:
196213
name: Test updated recipes for arch ${{ matrix.android_arch }} [ ubuntu-latest ]
197-
needs: [flake8]
214+
needs: [flake8, spotless]
198215
runs-on: ubuntu-latest
199216
# continue on error to see failures across all architectures
200217
continue-on-error: true
@@ -227,7 +244,7 @@ jobs:
227244
228245
macos_rebuild_updated_recipes:
229246
name: Test updated recipes for arch ${{ matrix.android_arch }} [ ${{ matrix.runs_on }} ]
230-
needs: [flake8]
247+
needs: [flake8, spotless]
231248
runs-on: ${{ matrix.runs_on }}
232249
# continue on error to see failures across all architectures
233250
continue-on-error: true

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,9 @@ coverage.xml
3636
# testapp's build folder
3737
testapps/build/
3838

39+
# Gradle build artifacts (Java linting)
40+
pythonforandroid/bootstraps/.gradle/
41+
pythonforandroid/bootstraps/build/
42+
3943
# Dolphin (the KDE file manager autogenerates the file `.directory`)
4044
.directory

CONTRIBUTING.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,60 @@ latest python-for-android release that supported building Python 2 was version
6666
On August 2021, we added support for Android App Bundle (aab). As a
6767
collateral benefit, we now support multi-arch apk.
6868

69+
## Code Quality
70+
71+
### Python Linting
72+
73+
Python code is linted using flake8. Run it locally with:
74+
75+
```bash
76+
tox -e pep8
77+
```
78+
79+
### Java Linting
80+
81+
Java source files in the bootstrap directories are linted using
82+
[Spotless](https://github.com/diffplug/spotless) with Google Java Format
83+
(AOSP style). The CI runs this check automatically.
84+
85+
**Local execution** (requires Java 17+):
86+
87+
```bash
88+
# Check for violations
89+
make java-lint
90+
91+
# Auto-fix violations
92+
make java-lint-fix
93+
```
94+
95+
The Makefile uses the Gradle wrapper (`gradlew`), which automatically downloads
96+
the correct Gradle version on first run. No manual Gradle installation is required.
97+
98+
**Using Docker** (if you don't have Java 17):
99+
100+
```bash
101+
# Check for violations
102+
make docker/java-lint
103+
104+
# Auto-fix violations
105+
make docker/java-lint-fix
106+
```
107+
108+
The Docker approach builds the project's Docker image (which includes Java 17)
109+
and runs the linting inside the container.
110+
111+
**What gets linted:**
112+
113+
- All `.java` files in `pythonforandroid/bootstraps/*/build/src/main/java/`
114+
- Excludes third-party code (`org/kamranzafar/jtar/`)
115+
116+
**Formatting rules applied:**
117+
118+
- Google Java Format with AOSP style (Android-friendly indentation)
119+
- Removal of unused imports
120+
- Trailing whitespace trimming
121+
- Files end with newline
122+
69123
## Creating a new release
70124

71125
(These instructions are for core developers, not casual contributors.)

Makefile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,20 @@ virtualenv: $(VIRTUAL_ENV)
2424
test:
2525
$(TOX) -- tests/ --ignore tests/test_pythonpackage.py
2626

27+
# Java linting using Spotless (requires Java 17+, uses Gradle wrapper)
28+
java-lint:
29+
cd pythonforandroid/bootstraps && ./common/build/gradlew spotlessCheck
30+
31+
java-lint-fix:
32+
cd pythonforandroid/bootstraps && ./common/build/gradlew spotlessApply
33+
34+
# Java linting via Docker (no local Java required)
35+
docker/java-lint: docker/build
36+
docker run --rm -v $(CURDIR):/home/user/app -w /home/user/app/pythonforandroid/bootstraps $(DOCKER_IMAGE) ./common/build/gradlew spotlessCheck
37+
38+
docker/java-lint-fix: docker/build
39+
docker run --rm -v $(CURDIR):/home/user/app -w /home/user/app/pythonforandroid/bootstraps $(DOCKER_IMAGE) ./common/build/gradlew spotlessApply
40+
2741
# Also install and configure rust
2842
rebuild_updated_recipes: virtualenv
2943
. $(ACTIVATE) && \

pythonforandroid/bootstraps/_sdl_common/build/src/main/java/org/kivy/android/launcher/Project.java

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
package org.kivy.android.launcher;
22

3-
import java.io.UnsupportedEncodingException;
3+
import android.graphics.Bitmap;
4+
import android.graphics.BitmapFactory;
5+
import android.util.Log;
46
import java.io.File;
57
import java.io.FileInputStream;
8+
import java.io.UnsupportedEncodingException;
69
import java.util.Properties;
710

8-
import android.util.Log;
9-
import android.graphics.Bitmap;
10-
import android.graphics.BitmapFactory;
11-
12-
13-
/**
14-
* This represents a project we've scanned for.
15-
*/
11+
/** This represents a project we've scanned for. */
1612
public class Project {
1713

1814
public String dir = null;
@@ -30,9 +26,8 @@ static String decode(String s) {
3026
}
3127

3228
/**
33-
* Scans directory for a android.txt file. If it finds one,
34-
* and it looks valid enough, then it creates a new Project,
35-
* and returns that. Otherwise, returns null.
29+
* Scans directory for a android.txt file. If it finds one, and it looks valid enough, then it
30+
* creates a new Project, and returns that. Otherwise, returns null.
3631
*/
3732
public static Project scanDirectory(File dir) {
3833

@@ -61,7 +56,7 @@ public static Project scanDirectory(File dir) {
6156
}
6257

6358
// Make sure we're dealing with a directory.
64-
if (! dir.isDirectory()) {
59+
if (!dir.isDirectory()) {
6560
return null;
6661
}
6762

@@ -94,6 +89,5 @@ public static Project scanDirectory(File dir) {
9489
}
9590

9691
return null;
97-
9892
}
9993
}

pythonforandroid/bootstraps/_sdl_common/build/src/main/java/org/kivy/android/launcher/ProjectAdapter.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,14 @@
44
import android.view.View;
55
import android.view.ViewGroup;
66
import android.widget.ArrayAdapter;
7-
import android.widget.TextView;
87
import android.widget.ImageView;
9-
8+
import android.widget.TextView;
109
import org.renpy.android.ResourceManager;
1110

1211
public class ProjectAdapter extends ArrayAdapter<Project> {
1312

1413
private ResourceManager resourceManager;
15-
14+
1615
public ProjectAdapter(Activity context) {
1716
super(context, 0);
1817
resourceManager = new ResourceManager(context);
@@ -29,7 +28,7 @@ public View getView(int position, View convertView, ViewGroup parent) {
2928
title.setText(p.title);
3029
author.setText(p.author);
3130
icon.setImageBitmap(p.icon);
32-
33-
return v;
31+
32+
return v;
3433
}
3534
}

pythonforandroid/bootstraps/_sdl_common/build/src/main/java/org/kivy/android/launcher/ProjectChooser.java

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
package org.kivy.android.launcher;
22

33
import android.app.Activity;
4-
54
import android.content.Intent;
5+
import android.net.Uri;
6+
import android.os.Environment;
67
import android.view.View;
8+
import android.widget.AdapterView;
79
import android.widget.ListView;
810
import android.widget.TextView;
9-
import android.widget.AdapterView;
10-
import android.os.Environment;
11-
1211
import java.io.File;
1312
import java.util.Arrays;
14-
import android.net.Uri;
15-
1613
import org.renpy.android.ResourceManager;
1714

1815
public class ProjectChooser extends Activity implements AdapterView.OnItemClickListener {
@@ -22,8 +19,7 @@ public class ProjectChooser extends Activity implements AdapterView.OnItemClickL
2219
String urlScheme;
2320

2421
@Override
25-
public void onStart()
26-
{
22+
public void onStart() {
2723
super.onStart();
2824

2925
resourceManager = new ResourceManager(this);
@@ -55,12 +51,12 @@ public void onStart()
5551
}
5652
}
5753

58-
if (projectAdapter.getCount() != 0) {
54+
if (projectAdapter.getCount() != 0) {
5955

6056
View v = resourceManager.inflateView("project_chooser");
6157
ListView l = (ListView) resourceManager.getViewById(v, "projectList");
6258

63-
l.setAdapter(projectAdapter);
59+
l.setAdapter(projectAdapter);
6460
l.setOnItemClickListener(this);
6561

6662
setContentView(v);
@@ -70,7 +66,10 @@ public void onStart()
7066
View v = resourceManager.inflateView("project_empty");
7167
TextView emptyText = (TextView) resourceManager.getViewById(v, "emptyText");
7268

73-
emptyText.setText("No projects are available to launch. Please place a project into " + dir + " and restart this application. Press the back button to exit.");
69+
emptyText.setText(
70+
"No projects are available to launch. Please place a project into "
71+
+ dir
72+
+ " and restart this application. Press the back button to exit.");
7473

7574
setContentView(v);
7675
}
@@ -79,9 +78,7 @@ public void onStart()
7978
public void onItemClick(AdapterView parent, View view, int position, long id) {
8079
Project p = (Project) parent.getItemAtPosition(position);
8180

82-
Intent intent = new Intent(
83-
"org.kivy.LAUNCH",
84-
Uri.fromParts(urlScheme, p.dir, ""));
81+
Intent intent = new Intent("org.kivy.LAUNCH", Uri.fromParts(urlScheme, p.dir, ""));
8582

8683
intent.setClassName(getPackageName(), "org.kivy.android.PythonActivity");
8784
this.startActivity(intent);
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Java Lint Configuration for python-for-android
2+
// This file configures Spotless to lint Java source files across all bootstraps
3+
4+
plugins {
5+
id 'java'
6+
id 'com.diffplug.spotless' version '6.25.0'
7+
}
8+
9+
// Repositories for plugin dependencies (e.g., google-java-format)
10+
repositories {
11+
mavenCentral()
12+
}
13+
14+
// Define the root directory for bootstrap Java sources
15+
def bootstrapsDir = "${rootProject.projectDir}"
16+
17+
// Collect all Java source directories from all bootstraps
18+
def javaSourceDirs = []
19+
file(bootstrapsDir).eachDir { bootstrapDir ->
20+
def srcDir = new File(bootstrapDir, 'build/src/main/java')
21+
if (srcDir.exists()) {
22+
javaSourceDirs.add(srcDir.absolutePath)
23+
}
24+
}
25+
26+
sourceSets {
27+
main {
28+
java {
29+
srcDirs = javaSourceDirs
30+
}
31+
}
32+
}
33+
34+
spotless {
35+
java {
36+
// Target all Java files from the source directories
37+
target fileTree(bootstrapsDir) {
38+
include '**/build/src/main/java/**/*.java'
39+
// Exclude third-party vendored code
40+
exclude '**/org/kamranzafar/jtar/**'
41+
}
42+
43+
// Use Google Java Format with AOSP style (Android-friendly, slightly relaxed)
44+
googleJavaFormat('1.19.2').aosp()
45+
46+
// Remove unused imports
47+
removeUnusedImports()
48+
49+
// Trim trailing whitespace
50+
trimTrailingWhitespace()
51+
52+
// Ensure files end with a newline
53+
endWithNewline()
54+
}
55+
}
56+
57+
// Disable compilation - we only want to lint, not build
58+
tasks.withType(JavaCompile).configureEach {
59+
enabled = false
60+
}

pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/GenericBroadcastReceiver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package org.kivy.android;
22

33
import android.content.BroadcastReceiver;
4-
import android.content.Intent;
54
import android.content.Context;
5+
import android.content.Intent;
66

77
public class GenericBroadcastReceiver extends BroadcastReceiver {
88

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package org.kivy.android;
22

3-
import android.content.Intent;
43
import android.content.Context;
4+
import android.content.Intent;
55

66
public interface GenericBroadcastReceiverCallback {
77
void onReceive(Context context, Intent intent);
8-
};
8+
}
9+
;

0 commit comments

Comments
 (0)