Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: initial implementation of gradle plugin #73 #1005

Merged
merged 10 commits into from
Oct 30, 2024

Conversation

pmupkin
Copy link
Contributor

@pmupkin pmupkin commented Sep 9, 2024

Motivation

Related issue: #73

Enable SofaArk to support building Biz and Ark packages in the Gradle environment. Subsequent implementations, documentation, testing, and optimizations will be carried out in this PR.

Summary by CodeRabbit

  • New Features

    • Introduced new classes for managing ARK archive creation and configuration, including ArkArchiveSupport, ArkBizCopyAction, ArkPluginAction, ArkJar, LoaderZipEntries, MainClassFinder, ResolveMainClassName, and DefaultTimeZoneOffset.
    • Added a Gradle plugin for the Sofa Ark framework, enabling seamless integration and configuration within Gradle projects.
    • Enhanced functionality for managing JAR tasks and dependencies, including bootable JAR creation and artifact configuration.
    • Implemented a utility for resolving and storing the main class name for Java applications.
    • Added utility classes for string manipulation and JAR type specification.
    • Introduced an interface for defining bootable archives and a new enum for zip compression types.
  • Documentation

    • Added comprehensive documentation for the sofa-ark-gradle-plugin, outlining configuration and usage instructions.
  • Bug Fixes

    • Improved error handling and exception management across various classes to ensure smoother operation during archive creation and configuration.

Copy link
Contributor

coderabbitai bot commented Sep 9, 2024

Walkthrough

This pull request introduces a comprehensive set of new classes and functionalities within the ark-gradle-plugin project, specifically in the com.alipay.sofa.ark.plugin package. Key additions include the ArkArchiveSupport class for managing ARK archives, the ArkBizCopyAction class for handling business artifacts, and the ArkPluginAction class for configuring the Gradle plugin. Additional classes such as SofaArkGradlePlugin, SofaArkGradlePluginExtension, and various utility classes enhance the Gradle build process, providing structured management of classpath entries, archive creation, and error handling mechanisms.

Changes

File Path Summary of Changes
.../ArkArchiveSupport.java Introduced ArkArchiveSupport class for managing ARK archives, including manifest configuration and file operations.
.../ArkBizCopyAction.java Added ArkBizCopyAction class for copying business artifacts into archives, handling errors, and processing files.
.../ArkPluginAction.java Created ArkPluginAction class to manage Gradle project configurations and tasks related to the Sofa Ark framework.
.../SofaArkGradlePlugin.java Introduced SofaArkGradlePlugin class to define plugin behavior and configuration for the Sofa Ark framework.
.../SofaArkGradlePluginExtension.java Added an abstract class for configuring the Sofa Ark Gradle plugin, encapsulating various properties and settings.
.../ArkJar.java Added ArkJar class for creating bootable JAR files with specific configurations and classpath management.
.../BootArchive.java Introduced BootArchive interface for defining properties related to bootable archives in Gradle.
.../DefaultTimeZoneOffset.java Added DefaultTimeZoneOffset class for handling time offsets based on the system's default time zone.
.../JarTypeFileSpec.java Introduced JarTypeFileSpec class for evaluating JAR files based on their manifest attributes.
.../LoaderZipEntries.java Added LoaderZipEntries class for copying entries from the spring-boot-loader.jar.
.../MainClassFinder.java Introduced MainClassFinder class for locating Java classes with a public static main method.
.../ResolveMainClassName.java Added ResolveMainClassName class for resolving and storing the name of the main class for Java applications.
.../StringUtils.java Introduced StringUtils class for utility methods related to string manipulation and collections.
.../ZipCompression.java Added ZipCompression enum for defining compression types for JAR entries.
.../README.md New documentation added for the sofa-ark-gradle-plugin module, outlining configuration and usage.

Suggested labels

size/XL

Suggested reviewers

  • gaosaroma

Poem

🐰 In the garden of code, where the rabbits play,
New classes and features hop in every day.
With archives and jars, they dance in delight,
Packaging dreams, making builds feel just right.
So here's to the changes, both big and small,
In the world of Sofa Ark, we celebrate all! 🎉


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Outside diff range, codebase verification and nitpick comments (3)
sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ResolveMainClassName.java (1)

49-203: Well-structured Gradle task class with clear responsibilities.

The ResolveMainClassName class is well-implemented with clear methods for setting and getting properties, and for resolving the main class name. The use of Gradle's DefaultTask and properties is appropriate, and the task-specific annotations are correctly applied.

  • The method resolveAndStoreMainClassName effectively handles file operations which are crucial for the task's functionality.
  • The use of @DisableCachingByDefault is justified as the task's output is not worth caching due to its dynamic nature based on the project's state.

Consider adding error handling for file operations and classpath manipulations to enhance robustness.

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkJar.java (1)

37-254: Well-structured Gradle task class for creating bootable JAR files.

The ArkJar class is well-implemented with clear methods for configuring the JAR task and handling file operations. The use of Gradle's Jar class and the BootArchive interface is appropriate, and the task-specific methods are correctly applied.

  • The methods copy and createCopyAction effectively handle the JAR creation process, which is crucial for the task's functionality.
  • The use of properties like mainClass and classpath is well thought out, allowing for dynamic configuration based on the project's state.

Consider adding more detailed error handling for file operations and JAR creation to enhance robustness.

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkPluginAction.java (1)

47-221: Comprehensive Gradle task configuration class with robust functionality.

The ArkPluginAction class is well-implemented with methods that comprehensively configure Gradle tasks and settings for the SofaArk framework. The use of the Action<Project> interface is appropriate, and the methods are well-organized and documented.

  • The methods cover a broad range of configurations, from JAR task setup to compiler argument adjustments, which are crucial for the framework's functionality.
  • The UTF-8 encoding setup and the handling of compiler arguments are particularly noteworthy for ensuring compatibility and performance.

Consider adding more detailed logging or debugging information to help trace the configuration steps during project builds.

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 9958c81 and f136a0f.

Files selected for processing (13)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkArchiveSupport.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkBizCopyAction.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkCopyAction.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkJar.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkPluginAction.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/BootArchive.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/DefaultTimeZoneOffset.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/JarTypeFileSpec.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/LoaderZipEntries.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ResolveMainClassName.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePlugin.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePluginExtension.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ZipCompression.java (1 hunks)
Files skipped from review due to trivial changes (1)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ZipCompression.java
Additional comments not posted (12)
sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/DefaultTimeZoneOffset.java (1)

23-51: Well-implemented utility class for time zone offset handling.

The DefaultTimeZoneOffset class is well-designed, using a singleton pattern for easy reuse across the project. The methods are clear and effectively documented, providing robust functionality for time manipulation relative to the default time zone.

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/BootArchive.java (4)

32-38: Well-implemented method for retrieving the main class.

The use of @Input and Property<String> is appropriate and follows Gradle best practices for task inputs.


40-45: Flexible method for specifying unpack requirements.

The use of varargs for specifying Ant-style patterns is flexible and user-friendly.


47-52: Enhanced flexibility with spec-based unpacking.

Allowing the use of Spec<FileTreeElement> provides advanced users more control over unpacking conditions.


55-61: Properly annotated method for classpath retrieval.

The use of @Optional and @Classpath annotations is correct and aligns with Gradle's requirements for task classpaths.

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkArchiveSupport.java (3)

54-66: Well-implemented static initialization block.

The static block correctly initializes the DEFAULT_LAUNCHER_CLASSES with an immutable set, which is a good practice for thread safety and to prevent accidental modifications.


86-96: Constructor initializes class fields effectively.

The constructor of ArkArchiveSupport is well-structured and initializes multiple class fields. It also sets up the manifest and parses Git information, which are crucial for the functionality of this class.

Consider verifying the robustness of the JGitParser.parse(gitDic) method in handling different edge cases or potential exceptions.


99-113: Effective use of Java Map methods in manifest configuration.

The method configureManifest uses putIfAbsent and computeIfAbsent effectively to ensure that manifest entries are not inadvertently overwritten. This approach maintains the integrity of existing manifest data while allowing new data to be added conditionally.

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkCopyAction.java (4)

115-124: Review of execute Method

The execute method in ArkCopyAction class handles the core functionality of copying actions. Here are some observations:

  1. Error Handling: The method catches IOException and wraps it in a GradleException, which is a good practice as it provides a clear message about the failure context.
  2. WorkResult Usage: The method returns a WorkResult object, which is appropriate for tasks in Gradle that can either succeed or fail.

This method is implemented correctly and follows good error handling practices.


126-149: Review of writeArchive Methods

There are overloaded writeArchive methods designed to handle the writing of the archive with detailed error handling and resource management:

  1. Resource Management: The use of try-with-resources or finally blocks to ensure streams are closed is commendable.
  2. Separation of Concerns: The method that accepts an OutputStream as a parameter allows for better testing and reusability by decoupling the file writing logic from stream management.

These methods are well-implemented with proper resource management and error handling.


151-165: Review of closeQuietly Method

The closeQuietly method is used to close streams without throwing exceptions. This is a utility method often used to simplify stream handling in finally blocks. However, consider the following:

  1. Silent Failure: The method catches and ignores IOException. While this is typical for a closeQuietly method, it's essential to ensure that this silent failure is acceptable in all use cases where closeQuietly is used.

This method is generally well-implemented for its intended use, but ensure that ignoring exceptions is acceptable in all cases where this method is used.


183-209: Review of InputStreamEntryWriter Class

The InputStreamEntryWriter class is an implementation of the EntryWriter interface, designed to write data from an InputStream to an OutputStream. Observations:

  1. Resource Management: The class takes care of closing the InputStream if the close flag is true, which is good practice.
  2. Buffer Size: The buffer size is set to 32KB, which is a reasonable default for file I/O operations.

This class is correctly implemented and follows good practices for I/O operations.

Comment on lines +26 to +44
class JarTypeFileSpec implements Spec<File> {

private static final Set<String> EXCLUDED_JAR_TYPES = Collections.singleton("dependencies-starter");

@Override
public boolean isSatisfiedBy(File file) {
try (JarFile jar = new JarFile(file)) {
String jarType = jar.getManifest().getMainAttributes().getValue("Spring-Boot-Jar-Type");
if (jarType != null && EXCLUDED_JAR_TYPES.contains(jarType)) {
return false;
}
}
catch (Exception ex) {
// Continue
}
return true;
}

}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall implementation looks good, but consider enhancing exception handling.

The class JarTypeFileSpec effectively filters files based on the jar type. However, the exception handling in the isSatisfiedBy method is minimal and could potentially suppress important errors that should be addressed or logged.

Consider logging the exception or rethrowing it as a specific type that could be handled upstream.

Comment on lines 25 to 60
public class SofaArkGradlePlugin implements Plugin<Project> {

public static final String ARK_VERSION = "2.2.14";
public static final String ARK_BIZ_CONFIGURATION_NAME = "bizJar";
public static final String ARK_CONFIGURATION_NAME = "arkJar";
public static final String ARK_BIZ_TASK_NAME = "bizJar";
public static final String ARK_TASK_NAME = "arkJar";
public static final String DEVELOPMENT_ONLY_CONFIGURATION_NAME = "developmentOnly";
public static final String PRODUCTION_RUNTIME_CLASSPATH_CONFIGURATION_NAME = "productionRuntimeClasspath";

@Override
public void apply(Project project) {
verifyGradleVersion();
createAndSetExtension(project);
registerPluginActions(project);
}

private void verifyGradleVersion() {
GradleVersion currentVersion = GradleVersion.current();
if (currentVersion.compareTo(GradleVersion.version("6.8")) < 0) {
throw new GradleException("Spring Boot plugin requires Gradle 6.8.+ "
+ "The current version is " + currentVersion);
}
}

private void createAndSetExtension(Project project) {
SofaArkGradlePluginExtension arkExtension = project.getExtensions().create("arkConfig", SofaArkGradlePluginExtension.class, project);

}

private void registerPluginActions(Project project) {
ArkPluginAction arkAction = new ArkPluginAction();
arkAction.execute(project);
}

}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid implementation with a suggestion for enhanced error handling.

The SofaArkGradlePlugin class is well-structured and effectively sets up the necessary configurations and actions for the SofaArk framework within a Gradle environment. The enforcement of a minimum Gradle version is crucial and well-handled.

Consider enhancing the error message in the verifyGradleVersion method to provide more detailed guidance on how to resolve the version issue, possibly including a link to documentation or support resources.

Comment on lines +70 to +83
/**
* Sets the classpath to include in the archive. The given {@code classpath} is
* evaluated as per {@link Project#files(Object...)}.
* @param classpath the classpath
* @since 2.0.7
*/
void setClasspath(Object classpath);

/**
* Sets the classpath to include in the archive.
* @param classpath the classpath
* @since 2.0.7
*/
void setClasspath(FileCollection classpath);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarify usage of overloaded setClasspath methods.

While the flexibility of overloading the setClasspath method is beneficial, it could lead to confusion. Consider enhancing the documentation or providing usage examples to clarify when to use each variant.

Comment on lines 116 to 137
private void buildArkManifest(){
this.arkManifest.getMainAttributes().putValue("Manifest-Version", "1.0");
this.arkManifest.getMainAttributes().putValue("Main-Class", this.loaderMainClass);
this.arkManifest.getMainAttributes().putValue("Start-Class", this.loaderMainClass);
this.arkManifest.getMainAttributes().putValue("Sofa-Ark-Version",this.arkVersion);
this.arkManifest.getMainAttributes().putValue("Ark-Container-Root","SOFA-ARK/container/");
this.arkManifest.getMainAttributes().putValue("build-time",
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(new Date()));

if (gitInfo != null) {
this.arkManifest.getMainAttributes().putValue("remote-origin-url", gitInfo.getRepository());
this.arkManifest.getMainAttributes().putValue("commit-branch", gitInfo.getBranchName());
this.arkManifest.getMainAttributes().putValue("commit-id", gitInfo.getLastCommitId());
this.arkManifest.getMainAttributes().putValue("commit-user-name", gitInfo.getLastCommitUser());
this.arkManifest.getMainAttributes()
.putValue("commit-user-email", gitInfo.getLastCommitEmail());
this.arkManifest.getMainAttributes().putValue("COMMIT_TIME", gitInfo.getLastCommitDateTime());
this.arkManifest.getMainAttributes().putValue("COMMIT_TIMESTAMP",
String.valueOf(gitInfo.getLastCommitTime()));
this.arkManifest.getMainAttributes().putValue("build-user", gitInfo.getBuildUser());
this.arkManifest.getMainAttributes().putValue("build-email", gitInfo.getBuildEmail());
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comprehensive manifest building with a suggestion for date handling.

The method buildArkManifest effectively includes detailed build and version information in the manifest. However, consider specifying a locale or timezone in SimpleDateFormat to avoid potential inconsistencies in time representation across different environments.

Consider using SimpleDateFormat with a specific timezone setting to ensure consistent date formatting:

new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US).setTimeZone(TimeZone.getTimeZone("UTC"));

Comment on lines 181 to 209
public CopyAction createCopyAction(Jar jar) throws IOException {
return createCopyAction(jar, null);
}

public CopyAction createCopyAction(Jar jar, String layerToolsLocation) throws IOException {
File bizOutput = getTargetFile(jar, this.arkExtension.getBizClassifier().get());
File arkOutput = getTargetFile(jar, this.arkExtension.getArkClassifier().get());

System.out.println("bizOutput:"+bizOutput.getAbsolutePath());
System.out.println("arkOutput:"+arkOutput.getAbsolutePath());

Manifest manifest = jar.getManifest();
boolean preserveFileTimestamps = jar.isPreserveFileTimestamps();
Integer dirMode = getDirMode(jar);
Integer fileMode = getFileMode(jar);
boolean includeDefaultLoader = isUsingDefaultLoader(jar);
Spec<FileTreeElement> requiresUnpack = this.requiresUnpack.getAsSpec();
Spec<FileTreeElement> exclusions = this.exclusions.getAsExcludeSpec();
Spec<FileCopyDetails> librarySpec = this.librarySpec;
Function<FileCopyDetails, ZipCompression> compressionResolver = this.compressionResolver;
String encoding = jar.getMetadataCharset();

CopyAction action = new ArkBizCopyAction(bizOutput,arkOutput, manifest, preserveFileTimestamps, dirMode, fileMode,
includeDefaultLoader, requiresUnpack, exclusions, librarySpec,
compressionResolver, encoding, this.arkManifest);


return jar.isReproducibleFileOrder() ? new ReproducibleOrderingCopyAction(action) : action;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complex method handling file copy actions effectively.

The createCopyAction method is well-implemented, handling various configurations and conditions to set up the appropriate CopyAction. Due to its complexity, consider adding more inline comments to explain the logic behind each configuration step, especially around the conditions that affect the choice of CopyAction.

Add more inline comments to improve readability and maintainability of the method, especially explaining the conditions and their impact on the behavior of the method.

Comment on lines 38 to 128
class LoaderZipEntries {

private final Long entryTime;

private final int dirMode;

private final int fileMode;

LoaderZipEntries(Long entryTime, int dirMode, int fileMode) {
this.entryTime = entryTime;
this.dirMode = dirMode;
this.fileMode = fileMode;
}

WrittenEntries writeTo(ZipArchiveOutputStream out) throws IOException {
WrittenEntries written = new WrittenEntries();
try (ZipInputStream loaderJar = new ZipInputStream(
getClass().getResourceAsStream("/META-INF/loader/spring-boot-loader.jar"))) {
ZipEntry entry = loaderJar.getNextEntry();
while (entry != null) {
if (entry.isDirectory() && !entry.getName().equals("META-INF/")) {
writeDirectory(new ZipArchiveEntry(entry), out);
written.addDirectory(entry);
}
else if (entry.getName().endsWith(".class")) {
writeClass(new ZipArchiveEntry(entry), loaderJar, out);
written.addFile(entry);
}
entry = loaderJar.getNextEntry();
}
}
return written;
}

private void writeDirectory(ZipArchiveEntry entry, ZipArchiveOutputStream out) throws IOException {
prepareEntry(entry, this.dirMode);
out.putArchiveEntry(entry);
out.closeArchiveEntry();
}

private void writeClass(ZipArchiveEntry entry, ZipInputStream in, ZipArchiveOutputStream out) throws IOException {
prepareEntry(entry, this.fileMode);
out.putArchiveEntry(entry);
copy(in, out);
out.closeArchiveEntry();
}

private void prepareEntry(ZipArchiveEntry entry, int unixMode) {
if (this.entryTime != null) {
entry.setTime(DefaultTimeZoneOffset.INSTANCE.removeFrom(this.entryTime));
}
entry.setUnixMode(unixMode);
}

private void copy(InputStream in, OutputStream out) throws IOException {
System.out.println("55555555555555555555555555555");
StreamUtils.copy(in, out);
}

/**
* Tracks entries that have been written.
*/
static class WrittenEntries {

private final Set<String> directories = new LinkedHashSet<>();

private final Set<String> files = new LinkedHashSet<>();

private void addDirectory(ZipEntry entry) {
this.directories.add(entry.getName());
}

private void addFile(ZipEntry entry) {
this.files.add(entry.getName());
}

boolean isWrittenDirectory(FileTreeElement element) {
String path = element.getRelativePath().getPathString();
if (element.isDirectory() && !path.endsWith(("/"))) {
path += "/";
}
return this.directories.contains(path);
}

Set<String> getFiles() {
return this.files;
}

}

}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review of LoaderZipEntries class

The LoaderZipEntries class is generally well-implemented and uses Java's try-with-resources effectively. However, it contains debug print statements (e.g., line 93) that should be removed for production code. Additionally, the exception handling could be improved to provide more specific error messages or handle specific cases.

Remove debug print statements:

- System.out.println("55555555555555555555555555555");

Improve exception handling by adding specific catch blocks or error messages where applicable.

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
class LoaderZipEntries {
private final Long entryTime;
private final int dirMode;
private final int fileMode;
LoaderZipEntries(Long entryTime, int dirMode, int fileMode) {
this.entryTime = entryTime;
this.dirMode = dirMode;
this.fileMode = fileMode;
}
WrittenEntries writeTo(ZipArchiveOutputStream out) throws IOException {
WrittenEntries written = new WrittenEntries();
try (ZipInputStream loaderJar = new ZipInputStream(
getClass().getResourceAsStream("/META-INF/loader/spring-boot-loader.jar"))) {
ZipEntry entry = loaderJar.getNextEntry();
while (entry != null) {
if (entry.isDirectory() && !entry.getName().equals("META-INF/")) {
writeDirectory(new ZipArchiveEntry(entry), out);
written.addDirectory(entry);
}
else if (entry.getName().endsWith(".class")) {
writeClass(new ZipArchiveEntry(entry), loaderJar, out);
written.addFile(entry);
}
entry = loaderJar.getNextEntry();
}
}
return written;
}
private void writeDirectory(ZipArchiveEntry entry, ZipArchiveOutputStream out) throws IOException {
prepareEntry(entry, this.dirMode);
out.putArchiveEntry(entry);
out.closeArchiveEntry();
}
private void writeClass(ZipArchiveEntry entry, ZipInputStream in, ZipArchiveOutputStream out) throws IOException {
prepareEntry(entry, this.fileMode);
out.putArchiveEntry(entry);
copy(in, out);
out.closeArchiveEntry();
}
private void prepareEntry(ZipArchiveEntry entry, int unixMode) {
if (this.entryTime != null) {
entry.setTime(DefaultTimeZoneOffset.INSTANCE.removeFrom(this.entryTime));
}
entry.setUnixMode(unixMode);
}
private void copy(InputStream in, OutputStream out) throws IOException {
System.out.println("55555555555555555555555555555");
StreamUtils.copy(in, out);
}
/**
* Tracks entries that have been written.
*/
static class WrittenEntries {
private final Set<String> directories = new LinkedHashSet<>();
private final Set<String> files = new LinkedHashSet<>();
private void addDirectory(ZipEntry entry) {
this.directories.add(entry.getName());
}
private void addFile(ZipEntry entry) {
this.files.add(entry.getName());
}
boolean isWrittenDirectory(FileTreeElement element) {
String path = element.getRelativePath().getPathString();
if (element.isDirectory() && !path.endsWith(("/"))) {
path += "/";
}
return this.directories.contains(path);
}
Set<String> getFiles() {
return this.files;
}
}
}
class LoaderZipEntries {
private final Long entryTime;
private final int dirMode;
private final int fileMode;
LoaderZipEntries(Long entryTime, int dirMode, int fileMode) {
this.entryTime = entryTime;
this.dirMode = dirMode;
this.fileMode = fileMode;
}
WrittenEntries writeTo(ZipArchiveOutputStream out) throws IOException {
WrittenEntries written = new WrittenEntries();
try (ZipInputStream loaderJar = new ZipInputStream(
getClass().getResourceAsStream("/META-INF/loader/spring-boot-loader.jar"))) {
ZipEntry entry = loaderJar.getNextEntry();
while (entry != null) {
if (entry.isDirectory() && !entry.getName().equals("META-INF/")) {
writeDirectory(new ZipArchiveEntry(entry), out);
written.addDirectory(entry);
}
else if (entry.getName().endsWith(".class")) {
writeClass(new ZipArchiveEntry(entry), loaderJar, out);
written.addFile(entry);
}
entry = loaderJar.getNextEntry();
}
}
return written;
}
private void writeDirectory(ZipArchiveEntry entry, ZipArchiveOutputStream out) throws IOException {
prepareEntry(entry, this.dirMode);
out.putArchiveEntry(entry);
out.closeArchiveEntry();
}
private void writeClass(ZipArchiveEntry entry, ZipInputStream in, ZipArchiveOutputStream out) throws IOException {
prepareEntry(entry, this.fileMode);
out.putArchiveEntry(entry);
copy(in, out);
out.closeArchiveEntry();
}
private void prepareEntry(ZipArchiveEntry entry, int unixMode) {
if (this.entryTime != null) {
entry.setTime(DefaultTimeZoneOffset.INSTANCE.removeFrom(this.entryTime));
}
entry.setUnixMode(unixMode);
}
private void copy(InputStream in, OutputStream out) throws IOException {
StreamUtils.copy(in, out);
}
/**
* Tracks entries that have been written.
*/
static class WrittenEntries {
private final Set<String> directories = new LinkedHashSet<>();
private final Set<String> files = new LinkedHashSet<>();
private void addDirectory(ZipEntry entry) {
this.directories.add(entry.getName());
}
private void addFile(ZipEntry entry) {
this.files.add(entry.getName());
}
boolean isWrittenDirectory(FileTreeElement element) {
String path = element.getRelativePath().getPathString();
if (element.isDirectory() && !path.endsWith(("/"))) {
path += "/";
}
return this.directories.contains(path);
}
Set<String> getFiles() {
return this.files;
}
}
}

Comment on lines 57 to 545
this.out.write(data, 0, data.length);
this.out.closeArchiveEntry();
}

private void prepareStoredEntry(FileCopyDetails details, ZipArchiveEntry archiveEntry) throws IOException {
prepareStoredEntry(details.open(), archiveEntry);
if (ArkBizCopyAction.this.requiresUnpack.isSatisfiedBy(details)) {
archiveEntry.setComment("UNPACK:" + FileUtils.sha1Hash(details.getFile()));
}
}

private void prepareStoredEntry(InputStream input, ZipArchiveEntry archiveEntry) throws IOException {
new CrcAndSize(input).setUpStoredEntry(archiveEntry);
}

private Long getTime() {
return getTime(null);
}

private Long getTime(FileCopyDetails details) {
if (!ArkBizCopyAction.this.preserveFileTimestamps) {
return CONSTANT_TIME_FOR_ZIP_ENTRIES;
}
if (details != null) {
return details.getLastModified();
}
return null;
}

private int getDirMode() {
return (ArkBizCopyAction.this.dirMode != null) ? ArkBizCopyAction.this.dirMode
: UnixStat.DIR_FLAG | UnixStat.DEFAULT_DIR_PERM;
}

private int getFileMode() {
return (ArkBizCopyAction.this.fileMode != null) ? ArkBizCopyAction.this.fileMode
: UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM;
}

private int getFileMode(FileCopyDetails details) {
return (ArkBizCopyAction.this.fileMode != null) ? ArkBizCopyAction.this.fileMode
: UnixStat.FILE_FLAG | getPermissions(details);
}

private int getPermissions(FileCopyDetails details) {
if (GradleVersion.current().compareTo(GradleVersion.version("8.3")) >= 0) {
try {
Object permissions = details.getClass().getMethod("getPermissions").invoke(details);
return (int) permissions.getClass().getMethod("toUnixNumeric").invoke(permissions);
}
catch (Exception ex) {
throw new GradleException("Failed to get permissions", ex);
}
}
return details.getMode();
}


}


/**
* Data holder for CRC and Size.
*/
private static class CrcAndSize {

private static final int BUFFER_SIZE = 32 * 1024;

private final CRC32 crc = new CRC32();

private long size;

CrcAndSize(InputStream inputStream) throws IOException {
try {
load(inputStream);
}
finally {
inputStream.close();
}
}

private void load(InputStream inputStream) throws IOException {
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
this.crc.update(buffer, 0, bytesRead);
this.size += bytesRead;
}
}

void setUpStoredEntry(ZipArchiveEntry entry) {
entry.setSize(this.size);
entry.setCompressedSize(this.size);
entry.setCrc(this.crc.getValue());
entry.setMethod(ZipEntry.STORED);
}

}

}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review of ArkBizCopyAction class

The ArkBizCopyAction class is complex and handles many aspects of file copying and ZIP handling effectively using Java's try-with-resources and functional interfaces. However, it contains debug print statements (e.g., lines 126, 148, 151) that should be removed for production code. The class could also benefit from additional comments or refactoring for better readability and maintainability.

Remove debug print statements:

- System.out.println("``````````````````````````````````````````````````````````````````````````````````");
- System.out.println("111111111111111111111111111111111");
- System.out.println("2222222222222222222222222222");

Add more comments or refactor for clarity, especially around complex logic sections.

Committable suggestion was skipped due to low confidence.

Comment on lines 60 to 113
public class ArkCopyAction implements CopyAction {

static final long CONSTANT_TIME_FOR_ZIP_ENTRIES = OffsetDateTime.of(1980, 2, 1, 0, 0, 0, 0, ZoneOffset.UTC)
.toInstant()
.toEpochMilli();

private final File output;

private final Manifest manifest;

private final boolean preserveFileTimestamps;

private final Integer dirMode;

private final Integer fileMode;

private final boolean includeDefaultLoader;

private final Spec<FileTreeElement> requiresUnpack;

private final Spec<FileTreeElement> exclusions;

private final Spec<FileCopyDetails> librarySpec;

private final Function<FileCopyDetails, ZipCompression> compressionResolver;

private final String encoding;

private final JarOutputStream jarOutput;

private final File arkOutput;



ArkCopyAction(File output,File arkOutput, Manifest manifest, boolean preserveFileTimestamps, Integer dirMode, Integer fileMode,
boolean includeDefaultLoader, Spec<FileTreeElement> requiresUnpack,
Spec<FileTreeElement> exclusions, Spec<FileCopyDetails> librarySpec,
Function<FileCopyDetails, ZipCompression> compressionResolver, String encoding
) throws IOException {
this.output = output;
this.arkOutput = arkOutput;
this.manifest = manifest;
this.preserveFileTimestamps = preserveFileTimestamps;
this.dirMode = dirMode;
this.fileMode = fileMode;
this.includeDefaultLoader = includeDefaultLoader;
this.requiresUnpack = requiresUnpack;
this.exclusions = exclusions;
this.librarySpec = librarySpec;
this.compressionResolver = compressionResolver;
this.encoding = encoding;
FileOutputStream fileOutputStream = new FileOutputStream(this.output);
this.jarOutput = new JarOutputStream(fileOutputStream);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review of ArkCopyAction Constructor

The constructor of ArkCopyAction is well-structured and initializes several fields based on the parameters it receives. However, there are a few points to consider:

  1. Exception Handling: The constructor throws IOException, which is appropriate given that it deals with file operations. Ensure that callers of this constructor handle this exception properly.
  2. Parameter Validation: There is no explicit validation of the parameters (e.g., checking if any are null). Adding null checks could prevent potential NullPointerExceptions during runtime.
  3. Resource Management: The constructor initializes JarOutputStream on FileOutputStream. It's crucial to ensure that these streams are closed properly in case of exceptions to avoid resource leaks.

Consider adding parameter validation and enhancing exception handling.

Comment on lines 217 to 261
/**
* Internal process used to copy {@link FileCopyDetails file details} to the zip file.
*/
private class Processor {

private final ZipArchiveOutputStream out;

private final JarArchiveOutputStream jarOutput;

private File arkFile;


private LoaderZipEntries.WrittenEntries writtenLoaderEntries;

private final Set<String> writtenDirectories = new LinkedHashSet<>();

private final Set<String> writtenLibraries = new LinkedHashSet<>();


Processor(ZipArchiveOutputStream out, JarArchiveOutputStream outputStream) throws IOException {
this.out = out;
jarOutput = outputStream;
}

void process(FileCopyDetails details) {
if(details.getName().contains("sofa-ark-all")){
this.arkFile = details.getFile();
return;
}
if (skipProcessing(details)) {
return;
}
try {
//writeLoaderEntriesIfNecessary(details);
if (details.isDirectory()) {
processDirectory(details);
}
else {
processFile(details);
}
}
catch (IOException ex) {
throw new GradleException("Failed to add " + details + " to " + ArkCopyAction.this.output, ex);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review of Processor Class

The Processor class is a crucial component within ArkCopyAction, handling the actual file and directory processing for the archive. Key points:

  1. Complexity: The class is quite complex and handles multiple responsibilities, including file and directory processing, and special cases like handling sofa-ark-all files.
  2. Error Handling: Proper exception handling is in place, with specific errors wrapped in GradleException providing context about what failed.

Consider refactoring to reduce complexity and improve maintainability, possibly by breaking down responsibilities into smaller, more focused methods or classes.

Comment on lines 325 to 432
System.out.println("||"+entry.getName());
if (entry.getName().contains("sofa-ark-archive")
|| entry.getName().contains("sofa-ark-spi")
|| entry.getName().contains("sofa-ark-common")) {

JarInputStream inputStream = new JarInputStream(new BufferedInputStream(
jarFileSource.getInputStream(entry)));
writeLoaderClasses(inputStream, jarFileSource);
}
}
} catch (NullPointerException exception){
throw new RuntimeException("No sofa-ark-all file find, please configure it");
}

}

private void writeLoaderClasses(JarInputStream jarInputStream, JarFile jarFileSource) throws IOException {
JarEntry entry;
while ((entry = jarInputStream.getNextJarEntry()) != null) {
if (entry.getName().endsWith(".class")
&& (entry.getName().contains("com/alipay/sofa/ark/spi/archive")
|| entry.getName().contains("com/alipay/sofa/ark/loader")
|| entry.getName().contains("com/alipay/sofa/ark/bootstrap")
|| entry.getName().contains("com/alipay/sofa/ark/common/util/StringUtils")
|| entry.getName().contains("com/alipay/sofa/ark/common/util/AssertUtils") || entry
.getName().contains("com/alipay/sofa/ark/spi/constant"))) {


// byte[] buffer = new byte[BUFFER_SIZE];
// int bytesRead;
// while ((bytesRead = this.inputStream.read(buffer)) != -1) {
// outputStream.write(buffer, 0, bytesRead);
// }

ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(entry.getName());
prepareEntry(zipArchiveEntry, entry.getName(), getTime(), getFileMode());
this.out.putArchiveEntry(zipArchiveEntry);

byte[] bytes = new byte[1024];
int length;
while ((length = jarInputStream.read(bytes)) >= 0) {
this.out.write(bytes, 0, length);
}
// this.out.write(entry.);
this.out.closeArchiveEntry();


}
}
jarInputStream.close();
}

private void writeEntry(JarEntry entry, EntryWriter entryWriter, JarFile jarFileSource) throws IOException {
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>"+entry.getName());

JarArchiveEntry newEntry = new JarArchiveEntry(entry.getName());
newEntry.setSize(entry.getSize());
newEntry.setTime(entry.getTime());

jarOutput.putArchiveEntry(newEntry);

//
try (InputStream is = jarFileSource.getInputStream(entry)) {
IOUtils.copy(is, jarOutput);
}

jarOutput.closeArchiveEntry();


// String parent = entry.getName();
// if (parent.endsWith("/")) {
// parent = parent.substring(0, parent.length() - 1);
// }
// if (parent.lastIndexOf("/") != -1) {
// parent = parent.substring(0, parent.lastIndexOf("/") + 1);
// if (parent.length() > 0) {
// writeEntry(new JarEntry(parent), null);
// }
// }
//
//
// this.jarOutput.putNextEntry(entry);
// if (entryWriter != null) {
// entryWriter.write(this.jarOutput);
// }
// this.jarOutput.closeEntry();

}



private void writeArkBizMark() throws IOException {
String str = "a mark file included in sofa-ark module.";
String name = "com/alipay/sofa/ark/biz/mark";
ZipArchiveEntry entry = new ZipArchiveEntry(name);
prepareEntry(entry, name, getTime(), getFileMode());
this.out.putArchiveEntry(entry);
this.out.write(str.getBytes());
this.out.closeArchiveEntry();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review of writeArkBizMark Method

The writeArkBizMark method adds a specific entry to the archive, which seems to be a marker for the sofa-ark module. Observations:

  1. Hardcoded Values: The method contains hardcoded strings for both the entry name and the content. These could be extracted as constants or configurable properties to enhance flexibility and maintainability.
  2. Single Responsibility: The method is focused and handles only one specific task, which is good for maintainability.

Consider extracting hardcoded values to constants or configuration settings.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between f136a0f and 26b4555.

Files selected for processing (5)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkArchiveSupport.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkBizCopyAction.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkCopyAction.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkJar.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/LoaderZipEntries.java (1 hunks)
Files skipped from review due to trivial changes (1)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkJar.java
Files skipped from review as they are similar to previous changes (4)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkArchiveSupport.java
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkBizCopyAction.java
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkCopyAction.java
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/LoaderZipEntries.java

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 31

🧹 Outside diff range and nitpick comments (14)
sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/Nullable.java (1)

28-34: LGTM: Annotation declaration and meta-annotations are well-defined.

The Nullable annotation is correctly declared with appropriate meta-annotations. It's designed to be used on methods, parameters, and fields, with runtime retention for reflection access. The use of @Nonnull(when = When.MAYBE) effectively indicates that the annotated element might be non-null under certain conditions.

Consider adding a brief Javadoc comment to explain the purpose and usage of this annotation. For example:

/**
 * Indicates that the annotated element can be null under some circumstances.
 * This annotation can be used on methods, parameters, and fields to provide
 * nullability information for static analysis tools and to improve code readability.
 */
@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
// ... (rest of the annotations)
public @interface Nullable {
}
sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePlugin.java (1)

53-54: Remove unused variable or utilize the extension instance.

The variable arkExtension is assigned but not used afterward. If you don't need to reference the extension instance in your plugin code, you can omit the variable assignment.

Apply this diff to simplify the code:

- SofaArkGradlePluginExtension arkExtension =  project.getExtensions().create("arkConfig", SofaArkGradlePluginExtension.class, project);
+ project.getExtensions().create("arkConfig", SofaArkGradlePluginExtension.class, project);
sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ResolveMainClassName.java (3)

49-50: Re-evaluate the decision to disable task caching

The @DisableCachingByDefault(because = "Not worth caching") annotation disables build caching for the ResolveMainClassName task. While this might be appropriate for tasks with negligible execution time, enabling caching can improve build performance in larger projects or when the task's outputs are reused.

Consider whether the benefits of caching outweigh the overhead in your specific use case.


176-182: Ensure JavaApplication plugin is applied before accessing its properties

In the getJavaApplicationMainClass method, you attempt to obtain the JavaApplication extension from the project. If the Java Application plugin is not applied, this method will return null.

To prevent unexpected null values, consider checking if the plugin is applied:

if (!project.getPlugins().hasPlugin("application")) {
    return null;
}

Alternatively, document that the Java Application plugin must be applied for this functionality.


52-52: Clarify the constant's purpose with an explanatory comment

The constant SPRING_BOOT_APPLICATION_CLASS_NAME holds the class name for Spring Boot's @SpringBootApplication. Adding a comment explaining its use can improve code readability.

For example:

// Class name of Spring Boot's @SpringBootApplication to assist in main class detection
private static final String SPRING_BOOT_APPLICATION_CLASS_NAME = "org.springframework.boot.autoconfigure.SpringBootApplication";
sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkJar.java (1)

218-230: Simplify the fromCallTo and callTo methods

The helper methods fromCallTo and callTo add extra layers of abstraction, which might reduce code readability. Consider replacing them with lambda expressions for clarity.

Apply this diff to simplify the code:

private void configureBootInfSpec(CopySpec bootInfSpec) {
-    bootInfSpec.into("", fromCallTo(this::classpathDirectories));
+    bootInfSpec.into("", spec -> spec.from(this.classpathDirectories()));

-    bootInfSpec.into("lib", fromCallTo(this::classpathFiles)).eachFile(this.support::excludeNonZipFiles);
+    bootInfSpec.into("lib", spec -> spec.from(this.classpathFiles())).eachFile(this.support::excludeNonZipFiles);
}

- private static <T> Action<CopySpec> fromCallTo(Callable<T> callable) {
-     return (spec) -> spec.from(callTo(callable));
- }

- private static <T> Callable<T> callTo(Callable<T> callable) {
-     return callable;
- }

This change reduces indirection and may improve readability.

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkArchiveSupport.java (3)

220-225: Rename isTrue method to isNonEmptyString for clarity

The isTrue method at lines 220-225 checks if an object is a non-empty String. The method name isTrue might be misleading, as it suggests a general truth value check. Consider renaming the method to isNonEmptyString to clearly convey its purpose.

Apply this diff to rename the method:

-    private Boolean isTrue(Object object){
+    private Boolean isNonEmptyString(Object object){
         if (object instanceof String) {
             return !((String) object).isEmpty();
         }
         return false;
     }

227-233: Enhance the maybe method for better readability

The maybe method concatenates strings with a hyphen, which can be unclear. To improve readability, consider renaming the method to appendWithHyphenIfNotEmpty and adding documentation to explain its behavior.

Apply this diff to rename the method and add documentation:

+    /**
+     * Appends the value to the prefix with a hyphen if the value is not empty.
+     *
+     * @param prefix the current string
+     * @param value  the value to append
+     * @return the combined string
+     */
-    private String maybe(String prefix, String value) {
+    private String appendWithHyphenIfNotEmpty(String prefix, String value) {
         if (isNonEmptyString(value)) {
             return isNonEmptyString(prefix) ? "-".concat(value) : value;
         } else {
             return "";
         }
     }

246-256: Handle exceptions more specifically in getMode method

In the getMode method, a general Exception is caught and rethrown as a GradleException. Catching broad exceptions can obscure the root cause of issues. Consider catching more specific exceptions, such as NoSuchMethodException and IllegalAccessException, to improve error handling and debugging.

Apply this diff to catch specific exceptions:

             try {
                 Object filePermissions = ((Property<Object>) copySpec.getClass().getMethod(methodName).invoke(copySpec))
                     .getOrNull();
                 return (filePermissions != null)
                     ? (int) filePermissions.getClass().getMethod("toUnixNumeric").invoke(filePermissions) : null;
             }
-            catch (Exception ex) {
+            catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
                 throw new GradleException("Failed to get permissions", ex);
             }
sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/MainClassFinder.java (3)

360-361: Use LinkedHashSet to maintain annotation order

Currently, annotations are stored in a HashSet, which does not preserve the order of insertion. If the order of annotations is significant, consider using a LinkedHashSet to maintain the order.

Update the constructor in MainClass as follows:

-        this.annotationNames = Collections.unmodifiableSet(new HashSet<>(annotationNames));
+        this.annotationNames = Collections.unmodifiableSet(new LinkedHashSet<>(annotationNames));

251-261: Log exceptions in createClassDescriptor for better diagnostics

In the createClassDescriptor method, if an IOException occurs, it is caught and ignored, returning null. This can make debugging difficult if classes fail to load.

Consider logging the exception using a proper logging framework:

     catch (IOException ex) {
+        // Log the exception using the appropriate logging framework
+        logger.warn("Failed to read class descriptor", ex);
         return null;
     }

Ensure that a logger (e.g., private static final Logger logger = LoggerFactory.getLogger(MainClassFinder.class);) is available in the class.


289-289: Update ASM API version to support newer Java class files

The code uses Opcodes.ASM7, which supports class files up to Java 11. To ensure compatibility with newer Java versions (Java 12 and above), consider updating to Opcodes.ASM9 or the latest ASM version.

Modify the super constructor to use the latest ASM API version:

-        super(Opcodes.ASM7);
+        super(Opcodes.ASM9);

Ensure that the ASM library dependency is updated to the corresponding version that supports Opcodes.ASM9.

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkBizCopyAction.java (1)

263-265: Remove or implement the empty writeConfig method

The writeConfig method is currently empty, which may lead to confusion.

If this method is intended for future use, consider adding a // TODO comment to indicate that implementation is pending:

// TODO: Implement the writeConfig method

Otherwise, if it's unnecessary, consider removing it to keep the code clean.

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/LoaderZipEntries.java (1)

117-117: Simplify condition by removing unnecessary parentheses

In the condition !path.endsWith(("/")), the parentheses around the string "/" are unnecessary. Removing them enhances code readability.

Apply this diff:

-    if (element.isDirectory() && !path.endsWith(("/"))) {
+    if (element.isDirectory() && !path.endsWith("/")) {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 26b4555 and 9d1b0b1.

📒 Files selected for processing (13)
  • sofa-ark-parent/support/ark-gradle-plugin/build.gradle (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/settings.gradle (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkArchiveSupport.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkBizCopyAction.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkJar.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkPluginAction.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/LoaderZipEntries.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/MainClassFinder.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/Nullable.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ResolveMainClassName.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePlugin.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePluginExtension.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/StringUtils.java (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • sofa-ark-parent/support/ark-gradle-plugin/build.gradle
  • sofa-ark-parent/support/ark-gradle-plugin/settings.gradle
🧰 Additional context used
🔇 Additional comments (14)
sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/Nullable.java (3)

1-17: LGTM: License header and package declaration are correct.

The Apache License 2.0 header is properly included, and the package declaration follows Java naming conventions.


19-26: LGTM: Import statements are appropriate and well-organized.

All imported classes are used in the annotation declaration, and there are no unused or redundant imports. The use of javax.annotation classes indicates good integration with existing annotation processing tools.


1-34: Overall assessment: Well-implemented Nullable annotation.

This new Nullable annotation is a valuable addition to the SofaArk framework. It follows Java annotation best practices and integrates well with existing annotation processing tools. The annotation will be useful for static analysis and improving code readability by providing explicit nullability information.

The implementation is clean, concise, and correctly structured. With the suggested minor documentation improvement, this file will be in excellent shape.

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/StringUtils.java (3)

3-7: LGTM: Import statements are appropriate and concise.

The import statements are well-organized and include only the necessary classes for the functionality provided in this utility class.


23-25: LGTM: Concise and well-implemented convenience method.

The collectionToCommaDelimitedString method is a clear and useful wrapper for the more general collectionToDelimitedString method. The use of the @Nullable annotation is a good practice for null safety.


27-29: LGTM: Well-designed overloaded method with sensible defaults.

This overload of collectionToDelimitedString provides a good balance between flexibility and convenience, allowing users to specify a delimiter while using sensible defaults for prefix and suffix.

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePluginExtension.java (2)

29-39: Consider making constants static

The constants defined between lines 29-39 should be made static since they do not depend on instance state. This change would improve memory efficiency for the class.


58-90: Code implementation is correct and follows best practices

The methods and properties are well-defined, adhering to Gradle plugin development conventions. The use of abstract methods for property definitions allows for flexible configuration in subclasses.

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ResolveMainClassName.java (1)

126-138: Handle the case when no main class is found

In the resolveMainClassName method, if no main class is found, the method returns an empty string (""). Depending on how this value is used later, it might be more appropriate to return null or throw an exception to explicitly indicate that the main class could not be resolved.

Consider updating the method to reflect this:

  • Return null if no main class is found.
  • Or, throw an exception with a clear message.
sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkPluginAction.java (4)

157-177: Clarify Configuration Attributes for Development and Runtime Classpaths

The method configureDevelopmentOnlyConfiguration sets up configurations for development-only and production runtime classpaths. Ensure that the attributes are set correctly to avoid unexpected behavior during dependency resolution.

Please double-check the configuration attributes and their implications on dependency resolution.


125-132: Ensuring UTF-8 Encoding in Compilation

The configureUtf8Encoding method sets the encoding to UTF-8 if not already specified. This is good practice to ensure consistent character encoding.


134-141: Adding Compiler Argument for Method Parameter Names

The configureParametersCompilerArg method adds the -parameters compiler argument to retain parameter names at runtime. This is beneficial for frameworks that rely on reflection.


97-98: ⚠️ Potential issue

Missing Import for ResolveMainClassName

The class ResolveMainClassName is used but not imported. This will cause a compilation issue.

Add the appropriate import statement for ResolveMainClassName:

+ import org.springframework.boot.gradle.tasks.application.ResolveMainClassName;

Ensure that ResolveMainClassName is available in your project's dependencies or replace it with the correct class.

Likely invalid or redundant comment.

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkBizCopyAction.java (1)

247-250: Ensure proper closure of FileInputStream in writeBizJar

While try-with-resources is used, confirm that the FileInputStream is correctly closed to prevent resource leaks.

Run the following script to check for any unclosed streams:

If the script returns any matches, ensure those streams are properly managed.

Comment on lines +56 to +60
static class CollectionUtils {
public static boolean isEmpty(@Nullable Collection<?> collection) {
return (collection == null || collection.isEmpty());
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider moving isEmpty method to the main class.

The isEmpty method is well-implemented and uses the @Nullable annotation correctly. However, nesting it within a static CollectionUtils class might be unnecessary and could lead to confusion.

Consider moving the isEmpty method directly into the StringUtils class:

-    static class CollectionUtils {
-        public static boolean isEmpty(@Nullable Collection<?> collection) {
-            return (collection == null || collection.isEmpty());
-        }
-    }
+    public static boolean isEmpty(@Nullable Collection<?> collection) {
+        return (collection == null || collection.isEmpty());
+    }

This change would simplify the class structure and make the method more directly accessible. If you prefer to keep collection-related utilities separate, consider creating a standalone CollectionUtils class instead of nesting it within StringUtils.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
static class CollectionUtils {
public static boolean isEmpty(@Nullable Collection<?> collection) {
return (collection == null || collection.isEmpty());
}
}
public static boolean isEmpty(@Nullable Collection<?> collection) {
return (collection == null || collection.isEmpty());
}

Comment on lines +11 to +21
static int copyTo(InputStream in, OutputStream out) throws IOException {
int byteCount = 0;

int bytesRead;
for(byte[] buffer = new byte[4096]; (bytesRead = in.read(buffer)) != -1; byteCount += bytesRead) {
out.write(buffer, 0, bytesRead);
}

out.flush();
return byteCount;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider improving robustness and clarify method visibility.

The copyTo method efficiently copies data between streams. However, consider the following improvements:

  1. Add null checks for input parameters to prevent NullPointerExceptions.
  2. The method is package-private. Is this intentional, or should it be public for wider use?

Here's a suggested improvement:

-    static int copyTo(InputStream in, OutputStream out) throws IOException {
+    public static int copyTo(InputStream in, OutputStream out) throws IOException {
+        if (in == null || out == null) {
+            throw new IllegalArgumentException("Input and output streams cannot be null");
+        }
         int byteCount = 0;
         int bytesRead;
         for(byte[] buffer = new byte[4096]; (bytesRead = in.read(buffer)) != -1; byteCount += bytesRead) {
             out.write(buffer, 0, bytesRead);
         }
         out.flush();
         return byteCount;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
static int copyTo(InputStream in, OutputStream out) throws IOException {
int byteCount = 0;
int bytesRead;
for(byte[] buffer = new byte[4096]; (bytesRead = in.read(buffer)) != -1; byteCount += bytesRead) {
out.write(buffer, 0, bytesRead);
}
out.flush();
return byteCount;
}
public static int copyTo(InputStream in, OutputStream out) throws IOException {
if (in == null || out == null) {
throw new IllegalArgumentException("Input and output streams cannot be null");
}
int byteCount = 0;
int bytesRead;
for(byte[] buffer = new byte[4096]; (bytesRead = in.read(buffer)) != -1; byteCount += bytesRead) {
out.write(buffer, 0, bytesRead);
}
out.flush();
return byteCount;
}

Comment on lines +31 to +52
public static String collectionToDelimitedString(
@Nullable Collection<?> coll, String delim, String prefix, String suffix) {

if (CollectionUtils.isEmpty(coll)) {
return "";
}

int totalLength = coll.size() * (prefix.length() + suffix.length()) + (coll.size() - 1) * delim.length();
for (Object element : coll) {
totalLength += String.valueOf(element).length();
}

StringBuilder sb = new StringBuilder(totalLength);
Iterator<?> it = coll.iterator();
while (it.hasNext()) {
sb.append(prefix).append(it.next()).append(suffix);
if (it.hasNext()) {
sb.append(delim);
}
}
return sb.toString();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

LGTM: Efficient and well-implemented string conversion method.

The collectionToDelimitedString method is well-designed and efficiently implemented. It handles edge cases, uses StringBuilder for performance, and delegates null checks to a utility method.

A minor optimization suggestion:

Consider using StringBuilder.append(CharSequence, int, int) to avoid creating substring objects:

         StringBuilder sb = new StringBuilder(totalLength);
         Iterator<?> it = coll.iterator();
         while (it.hasNext()) {
-            sb.append(prefix).append(it.next()).append(suffix);
+            sb.append(prefix);
+            String element = String.valueOf(it.next());
+            sb.append(element, 0, element.length());
+            sb.append(suffix);
             if (it.hasNext()) {
                 sb.append(delim);
             }
         }

This change can potentially reduce object creation and improve performance, especially for large collections.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static String collectionToDelimitedString(
@Nullable Collection<?> coll, String delim, String prefix, String suffix) {
if (CollectionUtils.isEmpty(coll)) {
return "";
}
int totalLength = coll.size() * (prefix.length() + suffix.length()) + (coll.size() - 1) * delim.length();
for (Object element : coll) {
totalLength += String.valueOf(element).length();
}
StringBuilder sb = new StringBuilder(totalLength);
Iterator<?> it = coll.iterator();
while (it.hasNext()) {
sb.append(prefix).append(it.next()).append(suffix);
if (it.hasNext()) {
sb.append(delim);
}
}
return sb.toString();
}
public static String collectionToDelimitedString(
@Nullable Collection<?> coll, String delim, String prefix, String suffix) {
if (CollectionUtils.isEmpty(coll)) {
return "";
}
int totalLength = coll.size() * (prefix.length() + suffix.length()) + (coll.size() - 1) * delim.length();
for (Object element : coll) {
totalLength += String.valueOf(element).length();
}
StringBuilder sb = new StringBuilder(totalLength);
Iterator<?> it = coll.iterator();
while (it.hasNext()) {
sb.append(prefix);
String element = String.valueOf(it.next());
sb.append(element, 0, element.length());
sb.append(suffix);
if (it.hasNext()) {
sb.append(delim);
}
}
return sb.toString();
}

Comment on lines +47 to +48
throw new GradleException("Spring Boot plugin requires Gradle 6.8.+ "
+ "The current version is " + currentVersion);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Correct the exception message to reference the Sofa Ark Gradle plugin.

The exception message currently refers to the "Spring Boot plugin," which is incorrect. It should reference the "Sofa Ark Gradle plugin" to accurately reflect the context.

Apply this diff to fix the error message:

- throw new GradleException("Spring Boot plugin requires Gradle 6.8.+ "
+ throw new GradleException("Sofa Ark Gradle plugin requires Gradle 6.8.+ "
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
throw new GradleException("Spring Boot plugin requires Gradle 6.8.+ "
+ "The current version is " + currentVersion);
throw new GradleException("Sofa Ark Gradle plugin requires Gradle 6.8.+ "
+ "The current version is " + currentVersion);

@TaskAction
void resolveAndStoreMainClassName() throws IOException {
File outputFile = this.outputFile.getAsFile().get();
outputFile.getParentFile().mkdirs();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Check the result of mkdirs() to handle potential directory creation failures

The call to outputFile.getParentFile().mkdirs(); creates the parent directories if they do not exist. However, if directory creation fails due to permission issues or other I/O errors, the subsequent file write operation could throw an exception.

Consider checking the return value of mkdirs() and handling any failures appropriately, possibly by throwing an exception with a clear error message.

Comment on lines +217 to +235
private void writeBootstrapEntry() throws IOException {
try (JarFile jarFileSource = new JarFile(this.arkFile)){
Enumeration<JarEntry> entries = jarFileSource.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry.getName().contains("sofa-ark-archive")
|| entry.getName().contains("sofa-ark-spi")
|| entry.getName().contains("sofa-ark-common")) {

JarInputStream inputStream = new JarInputStream(new BufferedInputStream(
jarFileSource.getInputStream(entry)));
writeLoaderClasses(inputStream, jarFileSource);
}
}
} catch (NullPointerException exception){
throw new RuntimeException("No sofa-ark-all file find, please configure it");
}

}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Refactor writeBootstrapEntry for efficiency and clarity

The logic within writeBootstrapEntry can be optimized for better performance and readability.

Consider the following improvements:

  • Combine conditions or use a collection to manage the entry names to check.
  • Avoid nested streams if possible.
  • Ensure all resources are properly closed.

Here's an example of refactored code:

private void writeBootstrapEntry() throws IOException {
    List<String> entryNames = Arrays.asList(
        "sofa-ark-archive",
        "sofa-ark-spi",
        "sofa-ark-common"
    );

    try (JarFile jarFileSource = new JarFile(this.arkFile)) {
        Enumeration<JarEntry> entries = jarFileSource.entries();
        while (entries.hasMoreElements()) {
            JarEntry entry = entries.nextElement();
            if (entryNames.stream().anyMatch(entry.getName()::contains)) {
                try (JarInputStream inputStream = new JarInputStream(
                        new BufferedInputStream(jarFileSource.getInputStream(entry)))) {
                    writeLoaderClasses(inputStream, jarFileSource);
                }
            }
        }
    } catch (IOException exception) {
        throw new RuntimeException("Error processing ark file", exception);
    }
}

Comment on lines +218 to +235
try (JarFile jarFileSource = new JarFile(this.arkFile)){
Enumeration<JarEntry> entries = jarFileSource.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry.getName().contains("sofa-ark-archive")
|| entry.getName().contains("sofa-ark-spi")
|| entry.getName().contains("sofa-ark-common")) {

JarInputStream inputStream = new JarInputStream(new BufferedInputStream(
jarFileSource.getInputStream(entry)));
writeLoaderClasses(inputStream, jarFileSource);
}
}
} catch (NullPointerException exception){
throw new RuntimeException("No sofa-ark-all file find, please configure it");
}

}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Improve resource handling in writeBootstrapEntry

In the writeBootstrapEntry method, the JarInputStream is opened inside a loop but may not be properly closed for each iteration, which can lead to resource leaks.

Ensure that JarInputStream is closed after processing each entry. Modify the code as follows:

                 if (entry.getName().contains("sofa-ark-archive")
                     || entry.getName().contains("sofa-ark-spi")
                     || entry.getName().contains("sofa-ark-common")) {

-                    JarInputStream inputStream = new JarInputStream(new BufferedInputStream(
+                    try (JarInputStream inputStream = new JarInputStream(new BufferedInputStream(
                         jarFileSource.getInputStream(entry)))) {
                         writeLoaderClasses(inputStream, jarFileSource);
+                    }
                 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try (JarFile jarFileSource = new JarFile(this.arkFile)){
Enumeration<JarEntry> entries = jarFileSource.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry.getName().contains("sofa-ark-archive")
|| entry.getName().contains("sofa-ark-spi")
|| entry.getName().contains("sofa-ark-common")) {
JarInputStream inputStream = new JarInputStream(new BufferedInputStream(
jarFileSource.getInputStream(entry)));
writeLoaderClasses(inputStream, jarFileSource);
}
}
} catch (NullPointerException exception){
throw new RuntimeException("No sofa-ark-all file find, please configure it");
}
}
try (JarFile jarFileSource = new JarFile(this.arkFile)){
Enumeration<JarEntry> entries = jarFileSource.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry.getName().contains("sofa-ark-archive")
|| entry.getName().contains("sofa-ark-spi")
|| entry.getName().contains("sofa-ark-common")) {
try (JarInputStream inputStream = new JarInputStream(new BufferedInputStream(
jarFileSource.getInputStream(entry)))) {
writeLoaderClasses(inputStream, jarFileSource);
}
}
}
} catch (NullPointerException exception){
throw new RuntimeException("No sofa-ark-all file find, please configure it");
}
}

Comment on lines +466 to +475
if (GradleVersion.current().compareTo(GradleVersion.version("8.3")) >= 0) {
try {
Object permissions = details.getClass().getMethod("getPermissions").invoke(details);
return (int) permissions.getClass().getMethod("toUnixNumeric").invoke(permissions);
}
catch (Exception ex) {
throw new GradleException("Failed to get permissions", ex);
}
}
return details.getMode();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Handle potential reflection exceptions and ensure compatibility

The code uses reflection to access getPermissions and toUnixNumeric methods based on the Gradle version. This approach might fail if the methods change in future Gradle releases.

Consider handling possible exceptions more gracefully and providing a fallback:

             try {
                 Object permissions = details.getClass().getMethod("getPermissions").invoke(details);
                 return (int) permissions.getClass().getMethod("toUnixNumeric").invoke(permissions);
             }
             catch (Exception ex) {
-                throw new GradleException("Failed to get permissions", ex);
+                // Fall back to default file permissions if reflection fails
+                return UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM;
             }

Additionally, consider checking method availability to ensure compatibility with future Gradle versions.

Committable suggestion was skipped due to low confidence.

}

private void copy(InputStream in, OutputStream out) throws IOException {
StringUtils.copyTo(in, out);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use appropriate utility for copying streams and ensure necessary imports

The method StringUtils.copyTo(in, out); may not be suitable for copying data between streams. Consider using IOUtils.copy(in, out); from Apache Commons IO for this purpose. Additionally, ensure that the IOUtils class is properly imported.

Apply this diff to update the copy method and import statement:

+import org.apache.commons.io.IOUtils;
...
 private void copy(InputStream in, OutputStream out) throws IOException {
-    StringUtils.copyTo(in, out);
+    IOUtils.copy(in, out);
 }

Committable suggestion was skipped due to low confidence.

Comment on lines +54 to +56
try (ZipInputStream loaderJar = new ZipInputStream(
getClass().getResourceAsStream("/META-INF/loader/spring-boot-loader.jar"))) {
ZipEntry entry = loaderJar.getNextEntry();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle potential NullPointerException when resource is missing

If the resource /META-INF/loader/spring-boot-loader.jar is not found, getResourceAsStream returns null, leading to a NullPointerException when initializing ZipInputStream. It's important to check for null and handle this scenario appropriately to prevent runtime exceptions.

Apply this diff to handle the potential null InputStream:

 try (InputStream resourceStream = getClass().getResourceAsStream("/META-INF/loader/spring-boot-loader.jar")) {
+    if (resourceStream == null) {
+        throw new IOException("Resource /META-INF/loader/spring-boot-loader.jar not found.");
+    }
     try (ZipInputStream loaderJar = new ZipInputStream(resourceStream)) {
         ZipEntry entry = loaderJar.getNextEntry();
         while (entry != null) {
             // existing logic
             entry = loaderJar.getNextEntry();
         }
     }
 }

Committable suggestion was skipped due to low confidence.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

🧹 Outside diff range and nitpick comments (3)
sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePlugin.java (1)

26-26: Add class-level documentation.

Since this is the main entry point for the Gradle plugin, consider adding comprehensive JavaDoc documentation that includes:

  • Purpose and responsibilities of the plugin
  • Configuration options available through arkConfig
  • Example usage in build.gradle
+/**
+ * Gradle plugin for SofaArk framework that enables building Biz and Ark packages.
+ * <p>
+ * Usage in build.gradle:
+ * <pre>
+ * plugins {
+ *     id 'com.alipay.sofa.ark'
+ * }
+ *
+ * arkConfig {
+ *     // configuration options
+ * }
+ * </pre>
+ */
 public class SofaArkGradlePlugin implements Plugin<Project> {
sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePluginExtension.java (1)

Line range hint 27-102: Add Javadoc comments to public methods and the class

Including Javadoc comments for the class and its public methods enhances code readability and provides valuable documentation for users and maintainers. This is especially important for plugin extensions, as other developers may need to understand how to interact with these properties and what each configuration option does.

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkBizCopyAction.java (1)

423-430: Enhance the content of the mark file for clarity

The mark file written in writeArkBizMark() contains a generic message. Providing more specific information can be helpful for future maintenance and understanding the purpose of the file.

Consider including details such as version information or a descriptive message:

private void writeArkBizMark() throws IOException {
-    String info = "a mark file included in sofa-ark module.";
+    String info = "This is a marker file indicating the presence of a SOFA-Ark business module.";
    String name = "com/alipay/sofa/ark/biz/mark";
    ZipArchiveEntry entry = new ZipArchiveEntry(name);
    prepareEntry(entry, name, getTime(), getFileMode());
    this.out.putArchiveEntry(entry);
    byte[] data = info.getBytes(StandardCharsets.UTF_8);
    this.out.write(data, 0, data.length);
    this.out.closeArchiveEntry();
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 9d1b0b1 and 8954fa9.

📒 Files selected for processing (5)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkArchiveSupport.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkBizCopyAction.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkPluginAction.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePlugin.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePluginExtension.java (1 hunks)
🧰 Additional context used
🔇 Additional comments (9)
sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePlugin.java (2)

23-23: Remove unused internal Gradle import.

The import of org.gradle.internal.impldep.org.bouncycastle.pqc.crypto.newhope.NHSecretKeyProcessor.PartyUBuilder appears to be unused and references an internal Gradle class.


44-45: Fix the plugin name in error message.

The error message incorrectly references "Spring Boot plugin" instead of "Sofa Ark Gradle plugin".

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkPluginAction.java (5)

90-90: Inconsistent Task Name Reference

The assemble task depends on "arkJar", but the task is registered with the name SofaArkGradlePlugin.ARK_BIZ_TASK_NAME. This inconsistency can cause a TaskNotFoundException.


176-179: Optimize Configuration Timing

Configuring additional metadata locations after project evaluation may be inefficient.


232-237: Hard-Coded Reference to spring-boot-configuration-processor

The method hasConfigurationProcessorOnClasspath specifically checks for spring-boot-configuration-processor, which might not be appropriate for a Sofa Ark plugin.


242-243: Missing Import for StringUtils

StringUtils.collectionToCommaDelimitedString is used without importing StringUtils, resulting in a compilation error.


107-107: ⚠️ Potential issue

Undefined Class JarTypeFileSpec

JarTypeFileSpec used in .filter(new JarTypeFileSpec()) is not defined or imported, leading to a compilation error.

Ensure JarTypeFileSpec is defined or imported. If it's part of your project, add the import statement:

+ import com.alipay.sofa.ark.plugin.utils.JarTypeFileSpec;

If it doesn't exist, implement it or replace it with an appropriate alternative.

Likely invalid or redundant comment.

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkArchiveSupport.java (2)

191-194: Confirm Accurate Spring Boot Version Retrieval

The determineSpringBootVersion method uses the implementation version of the current class's package, which may not accurately reflect the Spring Boot version if not set.

Consider specifying the Spring Boot version explicitly or verifying that the implementation version is correctly populated during the build process.


266-279: ⚠️ Potential issue

Ensure Compatibility with Gradle Versions Below 8.3

The method getMode uses reflection to access methods that may not exist in Gradle versions below 8.3. This could lead to runtime exceptions in environments using older Gradle versions.

Run the following script to verify compatibility with different Gradle versions:

Ensure that your build script or plugin accounts for environments with older Gradle versions.


public class SofaArkGradlePlugin implements Plugin<Project> {

public static final String ARK_VERSION = "2.2.14";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider externalizing the ARK_VERSION constant.

Hardcoding the version number makes it difficult to maintain and update. Consider moving this to a gradle.properties file or using version catalogs for better version management.

Example using gradle.properties:

-    public static final String ARK_VERSION = "2.2.14";
+    public static final String ARK_VERSION = project.findProperty("sofaArkVersion") ?: "2.2.14";

Committable suggestion was skipped due to low confidence.

Comment on lines +53 to +56
private void registerPluginActions(Project project) {
ArkPluginAction arkAction = new ArkPluginAction();
arkAction.execute(project);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider dependency injection for ArkPluginAction.

The current implementation creates a tight coupling with the ArkPluginAction class. Consider using dependency injection to improve testability and flexibility.

-    private void  registerPluginActions(Project project) {
-        ArkPluginAction arkAction =  new ArkPluginAction();
+    private void registerPluginActions(Project project) {
+        ArkPluginAction arkAction = createPluginAction();
         arkAction.execute(project);
     }
+
+    // Protected for testing
+    protected ArkPluginAction createPluginAction() {
+        return new ArkPluginAction();
+    }

Also, there's an extra whitespace in the method declaration and before the new keyword.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private void registerPluginActions(Project project) {
ArkPluginAction arkAction = new ArkPluginAction();
arkAction.execute(project);
}
private void registerPluginActions(Project project) {
ArkPluginAction arkAction = createPluginAction();
arkAction.execute(project);
}
// Protected for testing
protected ArkPluginAction createPluginAction() {
return new ArkPluginAction();
}

Comment on lines 41 to 62
private final Property<String> mainClass;

public SofaArkGradlePluginExtension(Project project){

this.mainClass = project.getObjects().property(String.class);

getPriority().convention(project.provider(() -> PRIORITY));
getArkClassifier().convention(project.provider(() -> ARK_CLASSIFIER));
getFinalName().convention(project.provider(() -> FINAL_NAME));
getBizName().convention(project.provider(() -> BIZ_NAME));
getBizClassifier().convention(project.provider(() -> BIZ_CLASSIFIER));

getBizVersion().convention(project.provider(() -> ARK_JAR_PLUGIN_VERSION));
getArkVersion().convention(project.provider(() -> ARK_JAR_PLUGIN_VERSION));
getWebContextPath().convention(project.provider(()-> WEB_CONTEXT_PATH));

getOutputDirectory().convention(project.getLayout().getBuildDirectory().dir("libs"));
}

public Property<String> getMainClass() {
return this.mainClass;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider declaring getMainClass() as an abstract method for consistency

The mainClass property is initialized directly in the constructor, and getMainClass() is a concrete method returning this property. In contrast, all other properties are declared as abstract methods and utilize Gradle's lazy property mechanism with conventions set in the constructor. For consistency and to align with Gradle best practices, consider declaring getMainClass() as an abstract method like the others.

Suggested refactor:

Remove the mainClass field and its initialization. Declare getMainClass() as an abstract method and set its convention in the constructor if needed.

-    private final Property<String> mainClass;

     public SofaArkGradlePluginExtension(Project project){

-        this.mainClass = project.getObjects().property(String.class);

         // Set conventions for properties
+        getMainClass().convention(project.provider(() -> /* default value if any */));

         // Existing code...
     }

-    public Property<String> getMainClass() {
-        return this.mainClass;
-    }
+    abstract public Property<String> getMainClass();

Committable suggestion was skipped due to low confidence.

Configuration runtimeClasspath = project.getConfigurations().getByName("runtimeClasspath");
Configuration sofaArkConfig = project.getConfigurations().maybeCreate("sofaArkConfig");
Dependency arkDependency = project.getDependencies().create(SofaArkGradlePlugin.ARK_BOOTSTRAP+arkConfig.getArkVersion().get());
((ModuleDependency) arkDependency).setTransitive(false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure Safe Casting to ModuleDependency

Casting arkDependency to ModuleDependency without verification may lead to a ClassCastException if the dependency is not of the expected type.

Before casting, verify that arkDependency is an instance of ModuleDependency:

if (arkDependency instanceof ModuleDependency) {
    ((ModuleDependency) arkDependency).setTransitive(false);
} else {
    // Handle the scenario where arkDependency is not a ModuleDependency
}

This ensures type safety and prevents potential runtime exceptions.

Comment on lines 129 to 136
for (String exclude : arkConfig.getExcludes().get()) {
String[] parts = exclude.split(":");
if (parts.length == 2) {
Map<String, String> excludeMap = new HashMap<>();
excludeMap.put("group", parts[0]);
excludeMap.put("module", parts[1]);
configuration.exclude(excludeMap);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle Invalid Exclusion Patterns

In applyExclusions, exclusions with an unexpected format are silently ignored, which may lead to configuration issues.

Add logging to notify about invalid exclusion patterns:

else {
    project.getLogger().warn("Invalid exclusion pattern: {}", exclude);
}

This aids in debugging and ensures all intended exclusions are applied.

Comment on lines +176 to +182
private void closeQuietly(OutputStream outputStream) {
try {
outputStream.close();
}
catch (IOException ex) {
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid empty catch blocks; handle exceptions appropriately

The catch block in closeQuietly is empty, which can suppress exceptions silently and make debugging difficult. It's advisable to at least log the exception or document why it's safe to ignore it.

Consider handling the exception as follows:

private void closeQuietly(OutputStream outputStream) {
    try {
        outputStream.close();
    }
-   catch (IOException ex) {
-   }
+   catch (IOException ex) {
+       // Optionally log the exception
+       // logger.warn("Failed to close OutputStream", ex);
+       // If logging is not desired, add a comment explaining why
+       // Ignoring IOException on close as it's not critical
+   }
}

If you choose to ignore the exception intentionally, adding a comment clarifies the reasoning.

Committable suggestion was skipped due to low confidence.

Comment on lines +202 to +205
Processor1(ZipArchiveOutputStream out) throws IOException {
this.out = out;
this.arkFile = conFile.get(0).getAbsolutePath();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add null or empty check for conFile to prevent IndexOutOfBoundsException

In the constructor of Processor1, the code assumes that conFile is not null or empty when accessing conFile.get(0). If conFile is empty, this will result in an IndexOutOfBoundsException.

Consider adding a check to ensure conFile is not null or empty before accessing it:

Processor1(ZipArchiveOutputStream out) throws IOException {
    this.out = out;
-   this.arkFile = conFile.get(0).getAbsolutePath();
+   if (conFile != null && !conFile.isEmpty()) {
+       this.arkFile = conFile.get(0).getAbsolutePath();
+   } else {
+       throw new IllegalArgumentException("Container file list is null or empty. Please configure it properly.");
+   }
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Processor1(ZipArchiveOutputStream out) throws IOException {
this.out = out;
this.arkFile = conFile.get(0).getAbsolutePath();
}
Processor1(ZipArchiveOutputStream out) throws IOException {
this.out = out;
if (conFile != null && !conFile.isEmpty()) {
this.arkFile = conFile.get(0).getAbsolutePath();
} else {
throw new IllegalArgumentException("Container file list is null or empty. Please configure it properly.");
}
}

Comment on lines +259 to +268
void writeFiles(List<File> files, String path){
for(File file : files){
try( FileInputStream fileInputStream = new FileInputStream(file)) {
ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(path + file.getName());
writeEntry(fileInputStream, zipArchiveEntry);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Check for null or empty file lists in writeFiles method

The method writeFiles assumes that the files list is not null or empty. If files is null, it will cause a NullPointerException. If it's empty, the method does nothing, which might not be the intended behavior.

Add a null and empty check for the files list:

void writeFiles(List<File> files, String path){
+   if (files == null || files.isEmpty()) {
+       throw new IllegalArgumentException("File list is null or empty. Cannot write files.");
+   }
    for(File file : files){
        try(FileInputStream fileInputStream = new FileInputStream(file)) {
            ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(path + file.getName());
            writeEntry(fileInputStream, zipArchiveEntry);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void writeFiles(List<File> files, String path){
for(File file : files){
try( FileInputStream fileInputStream = new FileInputStream(file)) {
ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(path + file.getName());
writeEntry(fileInputStream, zipArchiveEntry);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
void writeFiles(List<File> files, String path){
if (files == null || files.isEmpty()) {
throw new IllegalArgumentException("File list is null or empty. Cannot write files.");
}
for(File file : files){
try( FileInputStream fileInputStream = new FileInputStream(file)) {
ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(path + file.getName());
writeEntry(fileInputStream, zipArchiveEntry);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}


private void writeClass(ZipArchiveEntry entry, ZipInputStream in, ZipArchiveOutputStream out) throws IOException {
out.putArchiveEntry(entry);
StringUtils.copyTo(in, out);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Import StringUtils or replace with appropriate utility method

In line 289, StringUtils.copyTo(in, out); is used, but there is no import statement for StringUtils. This may lead to a compilation error if StringUtils is not defined or imported correctly.

If StringUtils is a custom utility class, ensure it is imported:

+ import com.alipay.sofa.ark.common.util.StringUtils;

Alternatively, if you intended to use a standard utility, consider replacing it with IOUtils.copy() from Apache Commons IO:

- StringUtils.copyTo(in, out);
+ IOUtils.copy(in, out);

And add the necessary import:

+ import org.apache.commons.io.IOUtils;

Committable suggestion was skipped due to low confidence.

Comment on lines +299 to +324
private void writeLoaderClasses(JarInputStream jarInputStream, JarFile jarFileSource) throws IOException {
JarEntry entry;
while ((entry = jarInputStream.getNextJarEntry()) != null) {
if (entry.getName().endsWith(".class")
&& (entry.getName().contains("com/alipay/sofa/ark/spi/archive")
|| entry.getName().contains("com/alipay/sofa/ark/loader")
|| entry.getName().contains("com/alipay/sofa/ark/bootstrap")
|| entry.getName().contains("com/alipay/sofa/ark/common/util/StringUtils")
|| entry.getName().contains("com/alipay/sofa/ark/common/util/AssertUtils") || entry
.getName().contains("com/alipay/sofa/ark/spi/constant"))) {

ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(entry.getName());
this.out.putArchiveEntry(zipArchiveEntry);

byte[] bytes = new byte[1024];
int length;
while ((length = jarInputStream.read(bytes)) >= 0) {
this.out.write(bytes, 0, length);
}
this.out.closeArchiveEntry();


}
}
jarInputStream.close();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Close JarInputStream properly to prevent resource leaks

In the writeLoaderClasses method, the JarInputStream is closed inside the method. However, since it's passed as a parameter, it's generally the caller's responsibility to close it.

Avoid closing JarInputStream inside the method or document that the method takes ownership of the stream.

Alternatively, remove the explicit close statement if the stream is managed elsewhere:

private void writeLoaderClasses(JarInputStream jarInputStream, JarFile jarFileSource) throws IOException {
    // processing entries
    }
-   jarInputStream.close();
}

Ensure that JarInputStream is properly closed in the calling method using try-with-resources.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private void writeLoaderClasses(JarInputStream jarInputStream, JarFile jarFileSource) throws IOException {
JarEntry entry;
while ((entry = jarInputStream.getNextJarEntry()) != null) {
if (entry.getName().endsWith(".class")
&& (entry.getName().contains("com/alipay/sofa/ark/spi/archive")
|| entry.getName().contains("com/alipay/sofa/ark/loader")
|| entry.getName().contains("com/alipay/sofa/ark/bootstrap")
|| entry.getName().contains("com/alipay/sofa/ark/common/util/StringUtils")
|| entry.getName().contains("com/alipay/sofa/ark/common/util/AssertUtils") || entry
.getName().contains("com/alipay/sofa/ark/spi/constant"))) {
ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(entry.getName());
this.out.putArchiveEntry(zipArchiveEntry);
byte[] bytes = new byte[1024];
int length;
while ((length = jarInputStream.read(bytes)) >= 0) {
this.out.write(bytes, 0, length);
}
this.out.closeArchiveEntry();
}
}
jarInputStream.close();
}
private void writeLoaderClasses(JarInputStream jarInputStream, JarFile jarFileSource) throws IOException {
JarEntry entry;
while ((entry = jarInputStream.getNextJarEntry()) != null) {
if (entry.getName().endsWith(".class")
&& (entry.getName().contains("com/alipay/sofa/ark/spi/archive")
|| entry.getName().contains("com/alipay/sofa/ark/loader")
|| entry.getName().contains("com/alipay/sofa/ark/bootstrap")
|| entry.getName().contains("com/alipay/sofa/ark/common/util/StringUtils")
|| entry.getName().contains("com/alipay/sofa/ark/common/util/AssertUtils") || entry
.getName().contains("com/alipay/sofa/ark/spi/constant"))) {
ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(entry.getName());
this.out.putArchiveEntry(zipArchiveEntry);
byte[] bytes = new byte[1024];
int length;
while ((length = jarInputStream.read(bytes)) >= 0) {
this.out.write(bytes, 0, length);
}
this.out.closeArchiveEntry();
}
}
}

Copy link

codecov bot commented Oct 22, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 78.36%. Comparing base (0a41e92) to head (5dbb578).
Report is 1 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff              @@
##             master    #1005      +/-   ##
============================================
+ Coverage     78.21%   78.36%   +0.14%     
  Complexity      874      874              
============================================
  Files           171      171              
  Lines          7052     7052              
  Branches       1036     1036              
============================================
+ Hits           5516     5526      +10     
+ Misses         1005      994      -11     
- Partials        531      532       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

🧹 Outside diff range and nitpick comments (8)
sofa-ark-parent/support/ark-gradle-plugin/README.md (2)

1-11: Consider providing English documentation for better accessibility.

The documentation is currently only in Chinese. Consider providing an English version or making it bilingual to improve accessibility for the international community.


1-11: Add missing essential documentation sections.

Consider adding these important sections to complete the documentation:

  1. Project Status/Roadmap
  2. Contributing Guidelines
  3. License Information
  4. Changelog/Release Notes
  5. Support and Issue Reporting

This documentation serves as the entry point for developers. A well-structured README is crucial for adoption and maintenance of the Gradle plugin.

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/JarTypeFileSpec.java (1)

25-28: Add class-level documentation.

Add Javadoc to explain the purpose of this class and its role in the Gradle plugin. This will help maintainers understand its significance in filtering Spring Boot dependency starters.

+/**
+ * Gradle file specification that filters JAR files based on their manifest attributes.
+ * Specifically excludes JARs with certain Spring Boot jar types defined in {@link #EXCLUDED_JAR_TYPES}.
+ */
 class JarTypeFileSpec implements Spec<File> {
sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/StringUtils.java (1)

72-76: Consider architectural implications before refactoring CollectionUtils.

While moving isEmpty to the main class would simplify the structure, consider these architectural points first:

  1. The method might be used by other plugin components that expect collection-related utilities to be grouped.
  2. If more collection utilities are planned for the plugin, a separate CollectionUtils class might be justified.

Consider creating a utility package (e.g., com.alipay.sofa.ark.plugin.util) to organize utility classes if more are planned. This would provide better separation of concerns and maintainability as the plugin grows.

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePlugin.java (1)

25-25: Add class-level JavaDoc for the plugin class

Consider adding JavaDoc comments to the SofaArkGradlePlugin class to improve code readability and maintainability. Providing documentation helps other developers understand the purpose and usage of the plugin.

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkJar.java (2)

36-36: Add class-level JavaDoc for better documentation

Adding JavaDoc comments to the ArkJar class will improve code readability and maintainability by providing clear documentation of its purpose and usage.


217-229: Simplify utility methods by inlining callTo

The callTo method simply returns the provided Callable. You can simplify the code by inlining this method within fromCallTo.

Apply this diff to simplify the methods:

- private static <T> Callable<T> callTo(Callable<T> callable) {
-     return callable;
- }

private static <T> Action<CopySpec> fromCallTo(Callable<T> callable) {
-     return (spec) -> spec.from(callTo(callable));
+     return (spec) -> spec.from(callable);
}
sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/MainClassFinder.java (1)

331-340: Annotate MainClassCallback with @FunctionalInterface

Since MainClassCallback is an interface with a single abstract method doWith, it can be annotated with @FunctionalInterface. This annotation improves code readability and indicates that the interface is intended to be implemented by a lambda expression or method reference.

Apply this diff to annotate the interface:

+@FunctionalInterface
 interface MainClassCallback<T> {

     /**
      * Handle the specified main class.
      * @param mainClass the main class
      * @return a non-null value if processing should end or {@code null} to continue
      */
     T doWith(MainClass mainClass);

 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 8954fa9 and a24a07f.

📒 Files selected for processing (15)
  • sofa-ark-parent/support/ark-gradle-plugin/README.md (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkArchiveSupport.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkBizCopyAction.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkJar.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkPluginAction.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/BootArchive.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/DefaultTimeZoneOffset.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/JarTypeFileSpec.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/LoaderZipEntries.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/MainClassFinder.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ResolveMainClassName.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePlugin.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePluginExtension.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/StringUtils.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ZipCompression.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkArchiveSupport.java
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkBizCopyAction.java
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePluginExtension.java
🧰 Additional context used
🔇 Additional comments (13)
sofa-ark-parent/support/ark-gradle-plugin/README.md (1)

6-9: ⚠️ Potential issue

Expand the usage section with detailed instructions.

The usage section needs significant enhancement. Consider adding:

  1. Prerequisites and system requirements
  2. Step-by-step installation instructions
  3. Detailed usage examples with code snippets
  4. Clear explanation of the "pending application" status for remote repository integration
  5. Version compatibility information
  6. Migration guide from the Maven plugin

Let's check if there are any example usages in the codebase:

#!/bin/bash
# Look for example configurations or test cases that could be used as documentation examples
fd -e gradle -e groovy --exec rg -l 'arkConfig'
sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ZipCompression.java (1)

21-33: Clean and well-structured enum implementation!

The enum effectively maps to Java's ZipEntry compression methods with clear documentation.

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/JarTypeFileSpec.java (1)

25-43: Verify integration with Spring Boot jar types.

The class assumes specific Spring Boot manifest attributes. Let's verify this assumption against the Spring Boot documentation and codebase.

✅ Verification successful

Let me gather more information about this manifest attribute usage and related Spring Boot dependencies.

This will help us understand:

  1. The context of "dependencies-starter" usage
  2. Other Spring Boot manifest attributes being used
  3. Similar manifest attribute checks
  4. Spring Boot related configuration files

Let me check the test file that mentions "dependencies-starter" to understand its usage context and look for any related Spring Boot configurations.


Let me check the Spring Boot integration modules to understand how this manifest attribute is used in the context of the project.


Based on the investigation so far, I can now provide a final response about the Spring Boot jar type verification concern.

The Spring-Boot-Jar-Type manifest attribute appears to be project-specific and not a standard Spring Boot attribute

The investigation reveals that:

  1. The Spring-Boot-Jar-Type manifest attribute is only used in JarTypeFileSpec.java to exclude jars with type "dependencies-starter"
  2. This is not a standard Spring Boot manifest attribute, as it's not found in Spring Boot's documentation or codebase
  3. The attribute appears to be specific to SOFA Ark's build tooling for managing dependencies

The code is correctly implementing a project-specific jar filtering mechanism and doesn't make incorrect assumptions about Spring Boot's manifest attributes.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for Spring-Boot-Jar-Type usage in dependencies
# Expected: Find documentation or usage of this manifest attribute

# Look for any Spring Boot related configuration or documentation files
fd -e md -e properties -e yaml -e xml . | xargs rg -l "Spring-Boot-Jar-Type"

# Look for similar manifest attribute usage in Java files
rg -t java "getMainAttributes\(\)\.getValue\(.*Boot.*\)" 

Length of output: 331


Script:

#!/bin/bash
# Search for dependencies-starter related configurations and code
rg -g '!*.class' -l "dependencies-starter"

# Look for Spring Boot manifest attribute definitions or configurations
rg -g '!*.class' "Boot.*Type" -A 2 -B 2

# Check if there are any other manifest attribute checks in the codebase
ast-grep --pattern 'getManifest().getMainAttributes().getValue($STR)'

# Look for any Spring Boot plugin or configuration files
fd "spring-boot" -t f

Length of output: 1176


Script:

#!/bin/bash
# Check the test file content
cat "sofa-ark-parent/support/ark-maven-plugin/src/test/java/com/alipay/sofa/ark/boot/mojo/ModuleSlimStrategyTest.java"

# Look for any Spring Boot related configurations in build files
fd -e gradle -e xml . | xargs rg -l "spring-boot"

# Search for any documentation about jar types or manifest attributes
fd "README|CONTRIBUTING" -e md | xargs rg -l -i "manifest|jar.*type"

Length of output: 20992


Script:

#!/bin/bash
# Check Spring Boot integration related files
cat "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/pom.xml"

# Look for any manifest-related code in Spring Boot integration
rg -g '!*.class' -t java "manifest|jar.*type" -A 2 -B 2 "sofa-ark-parent/support/ark-springboot-integration/"

# Check if there are any configuration properties related to jar types
fd -e properties -e yaml . "sofa-ark-parent/support/ark-springboot-integration/" | xargs cat

Length of output: 6414

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/DefaultTimeZoneOffset.java (1)

46-48: 🛠️ Refactor suggestion

Verify timestamp handling for edge cases

The current implementation might have issues with:

  1. Extreme timestamps that could cause long overflow
  2. Daylight savings time transitions
  3. Dates before 1970 (negative timestamps)

Let's verify the usage context to understand the potential impact:

Consider adding bounds checking and handling edge cases:

 long removeFrom(long time) {
+    // Handle potential overflow
+    long offset = this.defaultTimeZone.getOffset(time);
+    if (time > 0 && offset > 0 && time > Long.MAX_VALUE - offset) {
+        throw new ArithmeticException("Timestamp too large for offset adjustment");
+    }
+    if (time < 0 && offset < 0 && time < Long.MIN_VALUE - offset) {
+        throw new ArithmeticException("Timestamp too small for offset adjustment");
+    }
     return time - this.defaultTimeZone.getOffset(time);
 }
sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/StringUtils.java (2)

39-68: Verify delimiter usage patterns in the plugin.

The string conversion methods are well-implemented. Let's verify their usage patterns in the plugin context to ensure consistent delimiter handling.

✅ Verification successful

Let me generate additional scripts to check for actual usage of these string conversion methods in the codebase.


Based on the search results, I can now provide a final response as we have sufficient information about the usage patterns of these string conversion methods.

String conversion methods are used consistently and appropriately

The implementation is not only well-designed but also consistently used across the codebase. The search results show that:

  • The comma-delimited version is used in specific contexts where comma separation is the standard format:
    • Spring Boot configuration processor metadata locations
    • Tomcat TLD skip patterns configuration
  • No alternative or inconsistent collection-to-string conversion patterns were found (no competing stream().collect(Collectors.joining()) or StringBuilder patterns)
  • The delimiter usage is standardized and matches the expected use cases in the Gradle plugin context
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent delimiter usage across the plugin

echo "Checking delimiter patterns in method calls..."
rg "collectionToDelimitedString\(.*\)" --type java -A 2

echo "Checking for potential inconsistent delimiter usage..."
rg "String.*delim.*=.*[\"'].*[\"']" --type java

Length of output: 1266


Script:

#!/bin/bash
# Search for usages of collectionToCommaDelimitedString and any string concatenation with collections
echo "Checking usages of collection string conversion methods..."
rg "collectionToCommaDelimitedString|collectionToDelimitedString" --type java -B 2 -A 2

echo "Checking for collection joining patterns..."
ast-grep --pattern 'String $_ = $collection.stream().collect(Collectors.joining($_))'

echo "Checking for StringBuilder operations with collections..."
ast-grep --pattern '$_.append($collection.stream())'

Length of output: 5379


27-37: Verify stream handling in the Gradle plugin context.

While the implementation is correct, let's ensure it's used safely in the plugin context.

✅ Verification successful

Let me gather more context about the stream handling in the calling classes to ensure proper resource management.

This will help us verify if the streams are properly managed in the calling code.


Based on the analysis of the shell script results, I can now provide a final response regarding the stream handling in the copyTo method:

Stream handling is properly managed by calling code

The copyTo method is used in a safe manner throughout the codebase:

  • In ArkBizCopyAction, the streams are managed with try-with-resources blocks:

    try (FileInputStream fileInputStream = new FileInputStream(file)) {
      writeEntry(fileInputStream, zipArchiveEntry);
    }
  • In LoaderZipEntries, the streams are also properly closed:

    try (ZipInputStream loaderJar = new ZipInputStream(...)) {
      // copyTo usage
    }

The method is correctly implemented as an internal utility method that expects the caller to handle stream lifecycle, which is consistently done throughout the codebase using try-with-resources blocks.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for proper stream handling patterns
# Look for try-with-resources usage with this method

# Search for copyTo method usage
echo "Searching for copyTo method usage patterns..."
rg -A 10 "copyTo\(.*\)" --type java

# Look for potential resource leaks
echo "Checking for proper stream handling..."
ast-grep --pattern 'try ($_) {
  $$$
  copyTo($_, $_)
  $$$
}'

Length of output: 6144


Script:

#!/bin/bash
# Check the implementation of classes that use copyTo
echo "Checking ArkBizCopyAction implementation..."
rg -A 20 "class ArkBizCopyAction" --type java

echo "\nChecking LoaderZipEntries implementation..."
rg -A 20 "class LoaderZipEntries" --type java

# Look for try-with-resources or stream closing patterns
echo "\nChecking stream handling patterns..."
ast-grep --pattern 'try ($$$) {
  $$$
}'

Length of output: 67900

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePlugin.java (1)

34-38: Initialization logic looks good

The apply method correctly verifies the Gradle version, sets up the extension, and registers plugin actions. The sequence ensures that the plugin is properly configured.

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/BootArchive.java (1)

29-84: Interface definition is clear and well-structured

The BootArchive interface is well-designed, with appropriately defined methods and annotations that align with Gradle's task configuration practices.

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ResolveMainClassName.java (4)

119-119: Check the result of mkdirs() to handle potential directory creation failures

The call to outputFile.getParentFile().mkdirs(); may fail due to I/O errors or permission issues, which can cause subsequent file operations to throw exceptions.


143-144: Log exceptions when finding the main class for better debugging

In the findMainClass method, when an IOException occurs, it is caught and the method returns null silently, which can make debugging difficult.


165-168: Prevent possible NullPointerException when accessing springBootExtension

The springBootExtension may be null if the SofaArkGradlePluginExtension is not applied to the project. Accessing it without checking can cause a NullPointerException.


196-196: Include the caught exception as the cause in the RuntimeException

When catching the IOException, the re-thrown RuntimeException does not include the original exception as its cause, which can hinder debugging.

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkPluginAction.java (1)

49-66: Well-structured implementation of the execute method

The execute method effectively sets up the project by invoking configuration methods, ensuring that tasks and settings are properly initialized.

Comment on lines +3 to +4
# 配置
`sofa-ark-gradle-plugin` 使用 arkConfig 来进行配置。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Enhance the configuration section with detailed examples.

The configuration section is too brief. Consider adding:

  • Complete list of available configuration options in arkConfig
  • Example configurations for common use cases
  • Default values for each configuration option

Would you like me to help create a comprehensive configuration documentation section?

Comment on lines +10 to +11
参考`sofa-ark-plugin-gradle-plugin`的本地发布和引入。
使用Gradle刷新后,如果一切正常,会在IDEA右侧Gradle任务列表中出现arkJar,具体如下: Tasks > build > arkJar,点击arkJar执行,会在指定的outputDirectory中输出ark包和biz包。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

⚠️ Potential issue

Based on the gathered information, I can now provide a comprehensive response about the build instructions.

Enhance the build instructions with more details and configuration options.

The current documentation should be expanded to include:

  1. Configuration options through arkConfig:

    • arkVersion: Version of SOFAArk to use
    • bizName: Name of the business module
    • bizClassifier: Classifier for the biz package (defaults to "ark-biz")
    • arkClassifier: Classifier for the ark package (defaults to "ark-executable")
    • excludes: Dependencies to exclude
    • excludeGroupIds: Group IDs to exclude
    • excludeArtifactIds: Artifact IDs to exclude
  2. Task outputs:

    • The arkJar task generates two artifacts:
      • An executable ark package (with classifier "ark-executable")
      • A business module package (with classifier "ark-biz")
    • Output files are placed in the project's build directory
  3. Task dependencies:

    • The task is added to the build group
    • It depends on the project's classes and resources being compiled
  4. Example configuration:

arkConfig {
    arkVersion = "2.2.14"
    bizName = "example-biz"
    excludes = ["group:artifact"]
}
🔗 Analysis chain

Enhance the build instructions with more details.

The build instructions should include:

  1. Expected output artifacts and their locations
  2. Description of the arkJar task and its dependencies
  3. Available configuration options for output directory
  4. Troubleshooting guide for common issues

Let's check the actual implementation to ensure documentation accuracy:


Let me try a different approach to find the Gradle task and configuration details.


Let me check the README files to understand the documented behavior and configuration options.


Let me try one final search to find the configuration options and task details.


Let me check the ArkJar task implementation and configuration options.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Look for the arkJar task implementation
ast-grep --pattern 'class $_ implements Task {
  $$$
  arkJar($$$) {
    $$$
  }
  $$$
}'

Length of output: 47


Script:

#!/bin/bash
# Search for arkJar task definition in Gradle files
rg -l "task.*arkJar" --type groovy --type gradle

# Search for configuration and task details
rg "arkJar|outputDirectory|ark" --type groovy --type gradle -A 5

# Look for any documentation about the task
fd "README" -e md -x rg -l "arkJar|ark.*plugin"

Length of output: 875


Script:

#!/bin/bash
# Check content of relevant README files
echo "=== Main README ==="
cat README.md | rg -A 5 "arkJar|gradle.*plugin"

echo -e "\n=== Gradle Plugin README ==="
cat sofa-ark-parent/support/ark-gradle-plugin/README.md | rg -A 10 "arkJar|gradle.*plugin|配置项"

# Look for any test files that might document the behavior
rg -l "arkJar" --type-add 'test:*{Test,Spec}.*' --type test

Length of output: 677


Script:

#!/bin/bash
# Search for arkConfig configuration options in source files
rg "arkConfig" --type groovy --type java -B 2 -A 5

# Look for any source files that define the task
fd -e java -e groovy --exec rg -l "class.*ArkJar|task.*arkJar"

# Check for any examples in test resources
fd -e gradle -t f -x rg -l "arkConfig"

Length of output: 8871


Script:

#!/bin/bash
# Check the ArkJar task implementation
cat sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkJar.java

# Check the extension class for configuration options
rg -A 10 "class SofaArkGradlePluginExtension" --type java

# Look for any example configurations in test resources
fd -t f -e gradle -x rg -A 5 "arkConfig \{" 

Length of output: 9987

Comment on lines +21 to +33
public enum ZipCompression {

/**
* The entry should be {@link ZipEntry#STORED} in the archive.
*/
STORED,

/**
* The entry should be {@link ZipEntry#DEFLATED} in the archive.
*/
DEFLATED

}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding utility method for ZipEntry integration.

To improve usability and encapsulation, consider adding a method to convert the enum to the corresponding ZipEntry compression value.

Here's a suggested implementation:

 public enum ZipCompression {
     /**
      * The entry should be {@link ZipEntry#STORED} in the archive.
      */
     STORED,
 
     /**
      * The entry should be {@link ZipEntry#DEFLATED} in the archive.
      */
     DEFLATED;
+
+    /**
+     * Returns the corresponding ZipEntry compression method value.
+     * @return the compression method value as defined in {@link ZipEntry}
+     */
+    public int getZipEntryMethod() {
+        return this == STORED ? ZipEntry.STORED : ZipEntry.DEFLATED;
+    }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public enum ZipCompression {
/**
* The entry should be {@link ZipEntry#STORED} in the archive.
*/
STORED,
/**
* The entry should be {@link ZipEntry#DEFLATED} in the archive.
*/
DEFLATED
}
public enum ZipCompression {
/**
* The entry should be {@link ZipEntry#STORED} in the archive.
*/
STORED,
/**
* The entry should be {@link ZipEntry#DEFLATED} in the archive.
*/
DEFLATED;
/**
* Returns the corresponding ZipEntry compression method value.
* @return the compression method value as defined in {@link ZipEntry}
*/
public int getZipEntryMethod() {
return this == STORED ? ZipEntry.STORED : ZipEntry.DEFLATED;
}
}


class JarTypeFileSpec implements Spec<File> {

private static final Set<String> EXCLUDED_JAR_TYPES = Collections.singleton("dependencies-starter");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider making EXCLUDED_JAR_TYPES more flexible.

The hardcoded singleton set limits extensibility. Consider making it configurable through the Gradle plugin extension to allow users to customize excluded jar types.

-private static final Set<String> EXCLUDED_JAR_TYPES = Collections.singleton("dependencies-starter");
+private final Set<String> excludedJarTypes;
+
+public JarTypeFileSpec(Set<String> excludedJarTypes) {
+    this.excludedJarTypes = excludedJarTypes != null ? 
+        Collections.unmodifiableSet(new HashSet<>(excludedJarTypes)) :
+        Collections.singleton("dependencies-starter");
+}

Committable suggestion was skipped due to low confidence.

Comment on lines +29 to +41
@Override
public boolean isSatisfiedBy(File file) {
try (JarFile jar = new JarFile(file)) {
String jarType = jar.getManifest().getMainAttributes().getValue("Spring-Boot-Jar-Type");
if (jarType != null && EXCLUDED_JAR_TYPES.contains(jarType)) {
return false;
}
}
catch (Exception ex) {
// Continue
}
return true;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Enhance robustness of manifest attribute checking.

The current implementation has several potential issues:

  1. No null check for manifest
  2. No validation of file existence or JAR format
  3. Magic string for manifest attribute key

Consider this improved implementation:

+private static final String SPRING_BOOT_JAR_TYPE = "Spring-Boot-Jar-Type";

 @Override
 public boolean isSatisfiedBy(File file) {
+    if (file == null || !file.exists() || !file.isFile()) {
+        return false;
+    }
     try (JarFile jar = new JarFile(file)) {
-        String jarType = jar.getManifest().getMainAttributes().getValue("Spring-Boot-Jar-Type");
+        var manifest = jar.getManifest();
+        if (manifest == null) {
+            return true;
+        }
+        String jarType = manifest.getMainAttributes().getValue(SPRING_BOOT_JAR_TYPE);
         if (jarType != null && EXCLUDED_JAR_TYPES.contains(jarType)) {
             return false;
         }
     }
     catch (Exception ex) {
-        // Continue
+        // Invalid JAR file or IO error, exclude it
+        return false;
     }
     return true;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Override
public boolean isSatisfiedBy(File file) {
try (JarFile jar = new JarFile(file)) {
String jarType = jar.getManifest().getMainAttributes().getValue("Spring-Boot-Jar-Type");
if (jarType != null && EXCLUDED_JAR_TYPES.contains(jarType)) {
return false;
}
}
catch (Exception ex) {
// Continue
}
return true;
}
private static final String SPRING_BOOT_JAR_TYPE = "Spring-Boot-Jar-Type";
@Override
public boolean isSatisfiedBy(File file) {
if (file == null || !file.exists() || !file.isFile()) {
return false;
}
try (JarFile jar = new JarFile(file)) {
var manifest = jar.getManifest();
if (manifest == null) {
return true;
}
String jarType = manifest.getMainAttributes().getValue(SPRING_BOOT_JAR_TYPE);
if (jarType != null && EXCLUDED_JAR_TYPES.contains(jarType)) {
return false;
}
}
catch (Exception ex) {
// Invalid JAR file or IO error, exclude it
return false;
}
return true;
}

Comment on lines +31 to +37
/**
* Returns the fully-qualified name of the application's main class.
* @return the fully-qualified name of the application's main class
* @since 2.4.0
*/
@Input
Property<String> getMainClass();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Verify the accuracy of the @since tags in Javadoc comments

The methods getMainClass() and setClasspath() have @since tags with versions (2.4.0 and 2.0.7) that may not reflect the actual versioning of this new interface. Please ensure that the @since annotations accurately represent when these methods were introduced.

Also applies to: 69-82

Comment on lines +32 to +37
* Internal utility used to copy entries from the {@code spring-boot-loader.jar}.
*
* @author Andy Wilkinson
* @author Phillip Webb
* @author Scott Frederick
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Verify license compliance and proper attribution.

The class documentation and the authors listed (Andy Wilkinson, Phillip Webb, Scott Frederick) suggest that this code may have been copied from the Spring Boot project. Please ensure that the code is used in compliance with its original license and that proper attribution is provided according to the license terms.


private void prepareEntry(ZipArchiveEntry entry, int unixMode) {
if (this.entryTime != null) {
entry.setTime(DefaultTimeZoneOffset.INSTANCE.removeFrom(this.entryTime));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

DefaultTimeZoneOffset is undefined or missing an import.

The class DefaultTimeZoneOffset used in entry.setTime(DefaultTimeZoneOffset.INSTANCE.removeFrom(this.entryTime)); is neither defined in this file nor imported from any library. This will lead to a compilation error. Ensure that DefaultTimeZoneOffset is properly defined or the appropriate import statement is added.

Apply this diff to address the issue if DefaultTimeZoneOffset is part of the project or a library:

+import org.springframework.boot.loader.tools.DefaultTimeZoneOffset;

If DefaultTimeZoneOffset is not available, consider handling the time adjustment differently or implementing the necessary functionality.

Committable suggestion was skipped due to low confidence.

Configuration developmentOnly = project.getConfigurations()
.create(SofaArkGradlePlugin.DEVELOPMENT_ONLY_CONFIGURATION_NAME);
developmentOnly
.setDescription("Configuration for development-only dependencies such as Spring Boot's DevTools.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid hard-coded references to Spring Boot components in configuration descriptions

The description for the developmentOnly configuration mentions "Spring Boot's DevTools". Since this plugin is for Sofa Ark, referencing Spring Boot-specific components may not be appropriate.

Consider updating the description to be more general:

- .setDescription("Configuration for development-only dependencies such as Spring Boot's DevTools.");
+ .setDescription("Configuration for development-only dependencies.");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.setDescription("Configuration for development-only dependencies such as Spring Boot's DevTools.");
.setDescription("Configuration for development-only dependencies.");

Comment on lines +251 to +261
private static ClassDescriptor createClassDescriptor(InputStream inputStream) {
try {
ClassReader classReader = new ClassReader(inputStream);
ClassDescriptor classDescriptor = new ClassDescriptor();
classReader.accept(classDescriptor, ClassReader.SKIP_CODE);
return classDescriptor;
}
catch (IOException ex) {
return null;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle potential non-IOException exceptions in createClassDescriptor

The createClassDescriptor method currently catches only IOException, but methods like ClassReader's constructor and accept can throw other exceptions such as IllegalArgumentException or ArrayIndexOutOfBoundsException when processing malformed or corrupted class files. To prevent the application from crashing due to unhandled exceptions, consider catching Exception instead.

Apply this diff to handle all exceptions:

     private static ClassDescriptor createClassDescriptor(InputStream inputStream) {
         try {
             ClassReader classReader = new ClassReader(inputStream);
             ClassDescriptor classDescriptor = new ClassDescriptor();
             classReader.accept(classDescriptor, ClassReader.SKIP_CODE);
             return classDescriptor;
-        }
-        catch (IOException ex) {
+        } catch (Exception ex) {
             return null;
         }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private static ClassDescriptor createClassDescriptor(InputStream inputStream) {
try {
ClassReader classReader = new ClassReader(inputStream);
ClassDescriptor classDescriptor = new ClassDescriptor();
classReader.accept(classDescriptor, ClassReader.SKIP_CODE);
return classDescriptor;
}
catch (IOException ex) {
return null;
}
}
private static ClassDescriptor createClassDescriptor(InputStream inputStream) {
try {
ClassReader classReader = new ClassReader(inputStream);
ClassDescriptor classDescriptor = new ClassDescriptor();
classReader.accept(classDescriptor, ClassReader.SKIP_CODE);
return classDescriptor;
} catch (Exception ex) {
return null;
}
}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (2)
sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkArchiveSupport.java (2)

198-219: Unused parameter layerToolsLocation in createCopyAction method

The parameter layerToolsLocation in the createCopyAction(Jar jar, String layerToolsLocation) method is not used within the method implementation. Consider removing it if it's unnecessary or implement its usage if it's intended for future functionality.


222-238: Use Gradle logging instead of System.out.println

Using System.out.println for logging is not recommended in Gradle plugins. It's better to use Gradle's logging API to allow users to control the logging level and integrate with Gradle's logging system.

Apply this diff to replace System.out.println with jar.getProject().getLogger().lifecycle:

if (!outputDir.exists()) {
    boolean created = outputDir.mkdirs();
    if (!created) {
        throw new GradleException("Failed to create output directory: " + outputDir.getAbsolutePath());
    }
-    System.out.println("Created output directory: " + outputDir.getAbsolutePath());
+    jar.getProject().getLogger().lifecycle("Created output directory: " + outputDir.getAbsolutePath());
}
...
- System.out.println("Target file will be created at: " + targetFile.getAbsolutePath());
+ jar.getProject().getLogger().lifecycle("Target file will be created at: " + targetFile.getAbsolutePath());
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between a24a07f and a861a10.

📒 Files selected for processing (3)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkArchiveSupport.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePlugin.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePluginExtension.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePluginExtension.java
🔇 Additional comments (8)
sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePlugin.java (3)

33-38: LGTM! Well-structured plugin implementation.

The apply method follows a clear and logical sequence of operations:

  1. Version verification
  2. Extension creation
  3. Plugin action registration

52-55: 🛠️ Refactor suggestion

Clean up formatting and improve testability.

  1. Remove extra spaces:
-    private void  registerPluginActions(Project project) {
-        ArkPluginAction arkAction =  new ArkPluginAction();
+    private void registerPluginActions(Project project) {
+        ArkPluginAction arkAction = new ArkPluginAction();
  1. Consider improving testability by introducing a factory method:
+    // Protected for testing
+    protected ArkPluginAction createPluginAction() {
+        return new ArkPluginAction();
+    }

     private void registerPluginActions(Project project) {
-        ArkPluginAction arkAction = new ArkPluginAction();
+        ArkPluginAction arkAction = createPluginAction();
         arkAction.execute(project);
     }

Likely invalid or redundant comment.


40-46: ⚠️ Potential issue

Fix incorrect error message in version verification.

The error message incorrectly references the "Spring Boot plugin" instead of "Sofa Ark Gradle plugin".

Apply this fix:

-            throw new GradleException("Spring Boot plugin requires Gradle 6.8.+ "
+            throw new GradleException("Sofa Ark Gradle plugin requires Gradle 6.8.+ "
                 + "The current version is " + currentVersion);

Likely invalid or redundant comment.

sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkArchiveSupport.java (5)

57-107: Constructor and initialization are properly implemented

The ArkArchiveSupport constructor correctly initializes all required fields, including handling the Git information and building the initial Ark manifest.


110-125: configureBizManifest method correctly sets manifest attributes

The method accurately populates the manifest with essential attributes for the Biz application, ensuring proper configuration.


127-149: Ensure consistency in adding Git information to manifest

While the buildArkManifest method effectively adds necessary attributes, consider refactoring to eliminate duplicate code when adding Git information to the manifest, as previously suggested.


252-257: Consider renaming isTrue to isNonEmptyString for clarity

The method isTrue(Object object) checks if an object is a non-empty string. Renaming it to isNonEmptyString(Object object) would enhance readability and better reflect its functionality.


337-355: Handle exceptions appropriately in isSofaArk method

The catch block for the IOException is currently empty, which can make debugging difficult if an exception occurs. Swallowing exceptions may hide underlying issues.

import org.gradle.api.GradleException;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.internal.impldep.org.bouncycastle.pqc.crypto.newhope.NHSecretKeyProcessor.PartyUBuilder;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove unnecessary internal Gradle dependency.

The import of org.gradle.internal.impldep.org.bouncycastle.pqc.crypto.newhope.NHSecretKeyProcessor.PartyUBuilder appears to be unused and references an internal Gradle class. Internal Gradle APIs (org.gradle.internal.*) are not part of the public API contract and may change without notice.

Remove this import line as it's not being used in the implementation.

Comment on lines +27 to +31
public static final String ARK_VERSION = "2.2.14";
public static final String ARK_BIZ_TASK_NAME = "arkJar";
public static final String DEVELOPMENT_ONLY_CONFIGURATION_NAME = "developmentOnly";
public static final String PRODUCTION_RUNTIME_CLASSPATH_CONFIGURATION_NAME = "productionRuntimeClasspath";
public static final String ARK_BOOTSTRAP = "com.alipay.sofa:sofa-ark-all:";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve constants management and encapsulation.

  1. The ARK_VERSION should be externalized to support easier version management:

    • Consider using Gradle's version catalog feature
    • Or move it to gradle.properties
    • Or use a version management plugin
  2. Consider reducing the visibility of these constants to package-private since they appear to be used only internally:

-    public static final String ARK_VERSION = "2.2.14";
-    public static final String ARK_BIZ_TASK_NAME = "arkJar";
-    public static final String DEVELOPMENT_ONLY_CONFIGURATION_NAME = "developmentOnly";
-    public static final String PRODUCTION_RUNTIME_CLASSPATH_CONFIGURATION_NAME = "productionRuntimeClasspath";
-    public static final String ARK_BOOTSTRAP = "com.alipay.sofa:sofa-ark-all:";
+    static final String ARK_VERSION = "2.2.14";
+    static final String ARK_BIZ_TASK_NAME = "arkJar";
+    static final String DEVELOPMENT_ONLY_CONFIGURATION_NAME = "developmentOnly";
+    static final String PRODUCTION_RUNTIME_CLASSPATH_CONFIGURATION_NAME = "productionRuntimeClasspath";
+    static final String ARK_BOOTSTRAP = "com.alipay.sofa:sofa-ark-all:";

Committable suggestion was skipped due to low confidence.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between a861a10 and 61ede86.

📒 Files selected for processing (2)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkArchiveSupport.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePluginExtension.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePluginExtension.java

if (!created) {
throw new GradleException("Failed to create output directory: " + outputDir.getAbsolutePath());
}
System.out.println("Created output directory: " + outputDir.getAbsolutePath());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use a logging framework instead of System.out.println

Using System.out.println for logging is not recommended in production code. Consider using a logging framework like SLF4J or Log4j for better control over logging levels and outputs, which aligns with best practices and enhances maintainability.

Apply this diff to replace System.out.println with a logging framework:

+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;

 public class ArkArchiveSupport {
+    private static final Logger logger = LoggerFactory.getLogger(ArkArchiveSupport.class);
     ...

     if (!outputDir.exists()) {
         boolean created = outputDir.mkdirs();
         if (!created) {
             throw new GradleException("Failed to create output directory: " + outputDir.getAbsolutePath());
         }
-        System.out.println("Created output directory: " + outputDir.getAbsolutePath());
+        logger.info("Created output directory: {}", outputDir.getAbsolutePath());
     }
     File targetFile = new File(outputDir, getArkBizName(jar, classifier));
-    System.out.println("Target file will be created at: " + targetFile.getAbsolutePath());
+    logger.info("Target file will be created at: {}", targetFile.getAbsolutePath());

Also applies to: 226-226

Copy link
Collaborator

@gaosaroma gaosaroma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ark-gradle-plugin 的能力如下:

  • 支持打包:ark-biz.jar 和 ark-executable.jar
  • 支持参数配置:priority, finalName, bizName, bizVersion, mainClass, outputDirectory, arkClassifier, excludes, excludeArtifactIds, excludeGroupIds, denyImportPackages, denyImportClasses, denyImportResources, injectPluginDependencies, injectPluginExportPackages, webContextPath 字段
  • 不支持参数配置:declaredMode, packExcludesUrl, packExcludesConfig, baseDir, mainClass, baseDependencyParentIdentity, skip, includes, includeGroupIds, includeArtifactIds ( 及不常用字段:requiresUnpack, packageProvided, skipArkExecutable, keepArkBizJar, disableGitInfo, gitDirectory)

整体上,具备 ark-biz.jar 打包能力

后续 TODO:

  1. 支持 declaredMode 等其它必要参数
  2. 复用 sofa-ark 自身的 JarWriter、ModuleSlimStrategy 等代码做代码打包、模块瘦身等
  3. 一些方法的兼容性和重命名(详见代码中的 TODO 部分)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (2)
sofa-ark-parent/support/ark-gradle-plugin/build.gradle (2)

15-30: Enhance publishing configuration for production readiness

Consider the following improvements:

  1. Add artifact signing for security
  2. Configure distribution repositories beyond mavenLocal()
  3. Add POM metadata (description, developers, SCM info)

Example configuration:

signing {
    sign publishing.publications.maven
}

publishing {
    publications {
        maven(MavenPublication) {
            pom {
                name = 'SofaArk Gradle Plugin'
                description = 'Gradle plugin for building SofaArk Biz and Ark packages'
                url = 'https://github.com/sofastack/sofa-ark'
                licenses {
                    license {
                        name = 'The Apache License, Version 2.0'
                        url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                    }
                }
            }
        }
    }
}

53-55: Enhance test configuration

The test setup needs additional configuration:

  1. Add test dependencies (JUnit, Mockito, etc.)
  2. Configure test logging
  3. Add test sets for integration tests

Add these test dependencies:

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
    testImplementation 'org.mockito:mockito-core:5.3.1'
    testImplementation gradleTestKit()
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 61ede86 and 5dbb578.

📒 Files selected for processing (5)
  • sofa-ark-parent/support/ark-gradle-plugin/build.gradle (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkArchiveSupport.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkPluginAction.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ResolveMainClassName.java (1 hunks)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePluginExtension.java (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ResolveMainClassName.java
🚧 Files skipped from review as they are similar to previous changes (3)
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkArchiveSupport.java
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkPluginAction.java
  • sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePluginExtension.java
🔇 Additional comments (3)
sofa-ark-parent/support/ark-gradle-plugin/build.gradle (3)

1-5: LGTM: Plugin configuration is appropriate

The selected plugins provide the necessary functionality for developing and publishing a Gradle plugin.


47-51: Improve dependency version management

Consider the following recommendations:

  1. Use version catalogs or a central version management system
  2. Verify compatibility between ASM 9.4 and SofaArk core
  3. Add constraints to prevent version conflicts
#!/bin/bash
# Check for version conflicts with SofaArk core
rg "org\.ow2\.asm:asm:" 
rg "commons-compress:"

32-39: Revise plugin naming for clarity

The plugin name 'DependenciesPlugin' doesn't accurately reflect its purpose of building Ark packages. Consider renaming to 'SofaArkPlugin' or 'ArkBuildPlugin'.

#!/bin/bash
# Verify the implementation class exists and is properly implemented
ast-grep --pattern 'class SofaArkGradlePlugin implements Plugin<Project> {
  $$$
}'

Comment on lines +7 to +13
ext {
arkGradlePluginId = 'sofa-ark-gradle-plugin'
}

group = 'com.alipay.sofa'
version = '1.0.0'
sourceCompatibility = '1.8'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Version and Java compatibility settings should align with parent POM

The hardcoded version '1.0.0' and Java 1.8 compatibility in the Gradle plugin should align with the parent project configuration:

  • The parent POM defines java.version=1.8, so the current Java compatibility is correct
  • Version should be synchronized with sofa.ark.version.old (2.2.14) from the parent POM
  • Update version = '1.0.0' to match the parent project version
  • Keep sourceCompatibility = '1.8' as it aligns with the project's Java requirements
🔗 Analysis chain

Consider using dynamic version management

The version is currently hardcoded to '1.0.0'. Consider:

  1. Using project-wide version management
  2. Aligning the version with SofaArk's versioning scheme

Review Java compatibility requirements

Java 1.8 compatibility is set. Consider upgrading to a more recent Java version if there are no specific compatibility requirements with older systems.


Let's search for Java version requirements and version management patterns in build configuration files to better understand the context.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check SofaArk's Java compatibility requirements
rg -l "sourceCompatibility|targetCompatibility" | xargs cat

Length of output: 85


Script:

#!/bin/bash
# Check Java version requirements across the project
rg -i "java|jdk|sourcecompat|targetcompat" "*.gradle" "*.properties" "pom.xml" -A 2 -B 2

# Check if there's a parent build file or properties file that might define versions
fd -e gradle -e properties "^(gradle|sofa)" --type f

Length of output: 2816

@gaosaroma gaosaroma merged commit 30663b3 into sofastack:master Oct 30, 2024
7 checks passed
lvjing2 pushed a commit that referenced this pull request Nov 4, 2024
* feat: initial implementation of ark gradle plugin

(cherry picked from commit 30663b3)
lvjing2 added a commit that referenced this pull request Nov 4, 2024
* add check for auto slim config in sofa-ark-maven-plugin (#1009)

(cherry picked from commit 74f7ede)

* support blank in properties for slim config  @lvjing2 (#1010)

* add check for auto slim config in sofa-ark-maven-plugin

* support blank in properties for slim config

(cherry picked from commit 88fb8e2)

* enable handle AfterBizStopEvent

(cherry picked from commit f957314)

* add clean up event handlers

(cherry picked from commit 3b8c05e)

* fix test

(cherry picked from commit 5d66962)

* simply unregister event handlers (#1014)

(cherry picked from commit 6eef785)

* enable not clean biz instance when stop biz failed  (#1013)

* simply unregister event handlers

* try to fix stop failed

* not remove biz install when biz stop failed

* add test

* rebase master

(cherry picked from commit a7bc1ca)

* add commit for biz state (#1015)

* add comments for UNRESOLVED

* add commit for biz state

(cherry picked from commit 9ce12e6)

* feat: support multiple version module as activated (#1018)

* feat: support multiple version module as activated

(cherry picked from commit 7c40ec7)

* feat:baseDependencyParentIdentity_without_version (#1019)

(cherry picked from commit f3b88fc)

* gradle implementation for the Ark plugin (#1012)

* gradle implementation for the Ark plugin

---------

Co-authored-by: pmupkin <[email protected]>
Co-authored-by: gaosaroma <[email protected]>
(cherry picked from commit 0a41e92)

* feat: initial implementation of gradle plugin #73 (#1005)

* feat: initial implementation of ark gradle plugin

(cherry picked from commit 30663b3)

* init log space before log reinit (#1020)

(cherry picked from commit 53fa805)

* feat: 模块瘦身检查 && 允许不自动排除和基座相同的依赖 (#1017)

* 优化显示

* fix as bizStateRecords

* 更新显示

* feat: add 'build failed when exclude different base dependency'; allow not to exclude same dependency by base-starter automatically

* build failed when exclude dependencies not in base

* rm all gradle

* fix merge

* fix ut

---------

Co-authored-by: leo james <[email protected]>
(cherry picked from commit a99756a)

---------

Co-authored-by: Lipeng <[email protected]>
Co-authored-by: pmupkin <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants