Skip to main content

2024-09-17

One-liner

有些梦想虽然遥不可及,但不是不可能实现。只要我足够的强。 --- 《秦时明月》 · 小学生


English

once again : 再次

It's again possible : 再次可行

PowerShell: Export Directory Permission List

To export the user permission list for the first-level subdirectories in D:\Sharing_Data, you can use PowerShell or the Command Prompt with the icacls tool. Below are the detailed steps for both methods:


Method 1: Using PowerShell

  1. Open PowerShell

    • Press Win + X, and select Windows PowerShell (Admin) or Windows Terminal (Admin).
  2. Run the following script

    # Set the shared directory path
    $sharedPath = "D:\Sharing_Data"

    # Get first-level subdirectories
    $subDirectories = Get-ChildItem -Path $sharedPath -Directory

    # Create an empty array to store permission info
    $permissionsList = @()

    foreach ($dir in $subDirectories) {
    # Get the current subdirectory's ACL (Access Control List)
    $acl = Get-Acl -Path $dir.FullName

    foreach ($access in $acl.Access) {
    # Create a custom object to store info
    $permission = [PSCustomObject]@{
    Directory = $dir.Name
    Identity = $access.IdentityReference
    FileSystemRights = $access.FileSystemRights
    AccessControlType = $access.AccessControlType
    }
    # Add to the array
    $permissionsList += $permission
    }
    }

    # Export to CSV file
    $permissionsList | Export-Csv -Path "D:\Sharing_Data_Permissions.csv" -NoTypeInformation -Encoding UTF8

    Write-Output "Permission list has been successfully exported to D:\Sharing_Data_Permissions.csv"
  3. Script Explanation

    • Retrieve Subdirectories: Get-ChildItem -Path $sharedPath -Directory retrieves all first-level subdirectories under D:\Sharing_Data.
    • Retrieve Permissions: Get-Acl retrieves the permission info for each subdirectory.
    • Organize Data: Directory names, users, and permission types are organized into a table format.
    • Export to CSV: The organized permission information is exported to D:\Sharing_Data_Permissions.csv.
  4. View the Exported Permission File

    You can open the D:\Sharing_Data_Permissions.csv file using Excel or any CSV-compatible application for easy viewing and analysis.


Method 2: Using icacls in Command Prompt

  1. Open Command Prompt

    • Press Win + R, type cmd, and press Enter.
  2. Run the following command

    cd /d D:\Sharing_Data
    for /D %G in (*) do icacls "%G" >> D:\Sharing_Data_Permissions.txt

    Note:

    • If you are writing this command in a batch file (.bat), replace %G with %%G:
      for /D %%G in (*) do icacls "%%G" >> D:\Sharing_Data_Permissions.txt
  3. Command Explanation

    • for /D %G in (*): Loops through all first-level subdirectories under D:\Sharing_Data.
    • icacls "%G": Retrieves the permission information for each subdirectory.
    • >> D:\Sharing_Data_Permissions.txt: Appends the permission info to D:\Sharing_Data_Permissions.txt.
  4. View the Exported Permission File

    Open the D:\Sharing_Data_Permissions.txt file to view the permission list for each subdirectory.


Which Method to Choose?

  • The PowerShell method is more flexible and powerful, suitable for scenarios where further processing or analysis of permission data is required.
  • The icacls method is quick and simple, ideal for rapidly exporting permission information to a text file.

Choose the method that best fits your needs. If you need detailed or formatted permission data, the PowerShell method is recommended.


Additional Tips

  • Administrator Privileges: Ensure you run PowerShell or Command Prompt as an administrator to avoid permission issues when reading certain directories.
  • Encoding Issues: Use UTF-8 encoding when exporting to a CSV file to prevent Chinese characters from displaying as garbled text.
  • Permission Description:
    • IdentityReference: Represents the user or user group.
    • FileSystemRights: Specifies the type of permission, such as read, write, modify, etc.
    • AccessControlType: Specifies whether the permission is allowed (Allow) or denied (Deny).

Developing a Gradle Plugin

To create a custom Gradle plugin called codegen-jooq-gradle-plugin with plugin ID org.moonlit.jooq, which adds default configurations to the gradle-jooq-plugin, you can follow the steps below. The process includes setting up the plugin project, defining the plugin logic, adding default configurations, and packaging and publishing the plugin.

Below are the detailed steps with code examples:


1. Create the Plugin Project

Start by creating a new Gradle project to develop your plugin.

mkdir codegen-jooq-gradle-plugin
cd codegen-jooq-gradle-plugin
gradle init --type java-gradle-plugin

This will generate a basic structure for your Gradle plugin project.


2. Configure build.gradle

Next, edit the build.gradle file to add the necessary dependencies and plugin information.

plugins {
id 'java-gradle-plugin'
id 'maven-publish'
id 'java'
}

group = 'org.moonlit'
version = '1.0.0'

repositories {
mavenCentral()
}

dependencies {
implementation 'org.jooq:jooq:3.18.4' // Adjust version as needed
implementation 'nu.studer:gradle-jooq-plugin:5.2.1' // Use the appropriate version of gradle-jooq-plugin
}

gradlePlugin {
plugins {
codegenJooq {
id = 'org.moonlit.jooq'
implementationClass = 'org.moonlit.jooq.CodegenJooqPlugin'
displayName = 'Codegen JOOQ Gradle Plugin'
description = 'A custom Gradle plugin that extends gradle-jooq-plugin with default configurations.'
}
}
}

publishing {
publications {
pluginMaven(MavenPublication) {
from components.java
groupId = 'org.moonlit'
artifactId = 'codegen-jooq-gradle-plugin'
version = '1.0.0'
}
}
repositories {
mavenLocal()
// Add remote repository configuration if needed
// maven {
// url = uri("https://your.repository.url")
// credentials {
// username = project.findProperty("repoUser") ?: ""
// password = project.findProperty("repoPassword") ?: ""
// }
// }
}
}

Explanation:

  • The gradlePlugin block defines the plugin ID and implementation class.
  • The publishing block configures Maven publishing to allow distribution of the plugin locally or to a remote repository.

3. Implement the Plugin Logic

Now, create the plugin implementation file. In the directory src/main/java/org/moonlit/jooq/, create CodegenJooqPlugin.java.

package org.moonlit.jooq;

import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.jooq.gradle.JooqGenerate;
import nu.studer.gradle.jooq.JooqPluginExtension;

public class CodegenJooqPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
// Apply the original gradle-jooq-plugin
project.getPluginManager().apply("nu.studer.jooq");

// Get the Jooq plugin extension
JooqPluginExtension jooqExtension = project.getExtensions().getByType(JooqPluginExtension.class);

// Set default configurations
jooqExtension.getVersion().convention("3.18.4"); // Default JOOQ version

jooqExtension.generateAll(generate -> {
generate.getInputSchema().convention("public");
generate.getOutputDirectory().convention(new File(project.getBuildDir(), "generated-jooq"));
generate.getGenerator().getName().convention("org.jooq.codegen.DefaultGenerator");
generate.getGenerator().getStrategy().getName().convention("org.jooq.codegen.DefaultGeneratorStrategy");
});

// More default configurations can be added, such as database connection settings
}
}

Explanation:

  • This code applies the gradle-jooq-plugin and adds default configurations for schema, output directory, and generator strategy.

4. Configure Plugin Metadata

Ensure that the plugin metadata file exists at resources/META-INF/gradle-plugins/org.moonlit.jooq.properties with the following content:

implementation-class=org.moonlit.jooq.CodegenJooqPlugin

This informs Gradle about the plugin’s implementation class.


5. Package and Publish the Plugin

Local Publishing

To publish the plugin to your local Maven repository for testing:

./gradlew publishToMavenLocal

Remote Publishing

If you want to publish the plugin to Maven Central or another remote repository, ensure that the publishing block in build.gradle is correctly configured with credentials and repository URLs.


6. Use the Plugin

To use the plugin in another project, follow these steps:

1. Add Plugin Repository to settings.gradle

If the plugin has been published to Maven Central or another repository, add the repository in settings.gradle:

pluginManagement {
repositories {
gradlePluginPortal()
mavenLocal()
mavenCentral()
}
}

2. Apply the Plugin in build.gradle

plugins {
id 'org.moonlit.jooq' version '1.0.0'
}

// Minimal configuration needed as default values are pre-configured
jooq {
version = '3.18.4' // Optional, only if you want to override the default version
configurations {
main {
jdbc {
driver = 'org.postgresql.Driver'
url = 'jdbc:postgresql://localhost:5432/mydb'
user = 'username'
password = 'password'
}
generator {
database {
inputSchema = 'public'
}
target {
packageName = 'com.example.jooq'
directory = "${project.buildDir}/generated-jooq"
}
}
}
}
}

3. Generate JOOQ Classes

Run the following command to generate the JOOQ classes:

./gradlew generateJooq

7. Increase Flexibility with Custom Extension

If you want the plugin to be more flexible and allow users to override default configurations, you can modify the plugin as follows:

package org.moonlit.jooq;

import org.gradle.api.Plugin;
import org.gradle.api.Project;
import nu.studer.gradle.jooq.JooqPluginExtension;

public class CodegenJooqPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
project.getPluginManager().apply("nu.studer.jooq");

JooqPluginExtension jooqExtension = project.getExtensions().getByType(JooqPluginExtension.class);

jooqExtension.getVersion().convention("3.18.4");

jooqExtension.configurations().all(configuration -> {
configuration.generate(generate -> {
generate.getInputSchema().convention("public");
generate.getOutputDirectory().convention(new File(project.getBuildDir(), "generated-jooq"));
generate.getGenerator().getName().convention("org.jooq.codegen.DefaultGenerator");
});
});
}
}

Explanation:

  • Users can override the default configurations as needed in their build.gradle file.

8. Version Control and Continuous Integration

To ensure plugin versioning and CI/CD processes:

  • Use Git for version control.
  • Set up a CI/CD pipeline (e.g., GitHub Actions, GitLab CI) to automate testing and publishing.
  • Follow Semantic Versioning (SemVer) to manage plugin versions.

9. Example Project

To help users understand how to use the plugin, create an example project with instructions in the README.md.

Example Project Structure

example-project/
├── build.gradle
├── settings.gradle
└── src/
└── main/
└── java/

Example build.gradle

plugins {
id 'org.moonlit.jooq' version '1.0.0'
}

repositories {
mavenLocal()
mavenCentral()
}

jooq {
configurations {
main {
jdbc {
driver = 'org.postgresql.Driver'
url = 'jdbc:postgresql://localhost:5432/mydb'
user = 'username'
password = 'password'
}
generator {
database {
inputSchema = 'public'
}
target {
packageName = 'com.example.jooq'
}
}
}
}
}

Summary

By following these steps, you can develop a custom Gradle plugin codegen-jooq-gradle-plugin that extends gradle-jooq-plugin with default configurations. Make sure to thoroughly test the plugin during development and provide clear documentation for users to enhance the usability of your plugin.

For more information, you can refer to:

Testing a Gradle Plugin Project

When developing the codegen-jooq-gradle-plugin, it's important to test the plugin to ensure it works as expected in different configurations. This guide covers how to configure a testing environment, create test classes, and execute them using Gradle TestKit.

1. Configure the Testing Environment

Start by ensuring that your build.gradle file contains the necessary dependencies and configuration for testing.

Modify build.gradle

plugins {
id 'java-gradle-plugin'
id 'maven-publish'
id 'java'
}

group = 'org.moonlit'
version = '1.0.0'

repositories {
mavenCentral()
}

dependencies {
implementation 'org.jooq:jooq:3.18.4'
implementation 'nu.studer:gradle-jooq-plugin:5.2.1'

// Testing dependencies
testImplementation gradleTestKit() // For testing the Gradle plugin
testImplementation 'junit:junit:4.13.2' // Using JUnit 4 for testing
// If you want to use JUnit 5:
// testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1'
}

gradlePlugin {
plugins {
codegenJooq {
id = 'org.moonlit.jooq'
implementationClass = 'org.moonlit.jooq.CodegenJooqPlugin'
displayName = 'Codegen JOOQ Gradle Plugin'
description = 'A custom Gradle plugin that extends gradle-jooq-plugin with default configurations.'
}
}
}

publishing {
publications {
pluginMaven(MavenPublication) {
from components.java
groupId = 'org.moonlit'
artifactId = 'codegen-jooq-gradle-plugin'
version = '1.0.0'
}
}
repositories {
mavenLocal()
}
}

// Use JUnit 4 for testing
test {
useJUnit()
// If using JUnit 5:
// useJUnitPlatform()
}

Explanation:

  • The testImplementation gradleTestKit() dependency enables you to use Gradle TestKit for plugin testing.
  • You can choose between JUnit 4 or JUnit 5 for writing your tests by configuring the corresponding dependencies and test {} block.

2. Create a Test Class

Next, you will create a test class that uses Gradle TestKit to validate your plugin's behavior.

Directory Structure

Ensure your project structure looks like this:

codegen-jooq-gradle-plugin/
├── build.gradle
├── settings.gradle
├── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── moonlit/
│ │ └── jooq/
│ │ └── CodegenJooqPlugin.java
│ └── test/
│ └── java/
│ └── org/
│ └── moonlit/
│ └── jooq/
│ └── CodegenJooqPluginTest.java

Writing the Test Class

Create CodegenJooqPluginTest.java under src/test/java/org/moonlit/jooq/.

package org.moonlit.jooq;

import org.gradle.testkit.runner.GradleRunner;
import org.gradle.testkit.runner.BuildResult;
import org.gradle.testkit.runner.TaskOutcome;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;

import static org.junit.Assert.*;

public class CodegenJooqPluginTest {

@Rule
public final TemporaryFolder testProjectDir = new TemporaryFolder(); // To isolate test environments

private File buildFile;

@Test
public void testPluginAppliesSuccessfully() throws IOException {
// Create a temporary project directory
File projectDir = testProjectDir.getRoot();

// Create a build.gradle file
buildFile = testProjectDir.newFile("build.gradle");
String buildFileContent = "plugins {\n" +
" id 'org.moonlit.jooq'\n" +
"}\n" +
"\n" +
"repositories {\n" +
" mavenCentral()\n" +
"}\n" +
"\n" +
"jooq {\n" +
" configurations {\n" +
" main {\n" +
" jdbc {\n" +
" driver = 'org.h2.Driver'\n" +
" url = 'jdbc:h2:mem:testdb'\n" +
" user = 'sa'\n" +
" password = ''\n" +
" }\n" +
" generator {\n" +
" name = 'org.jooq.codegen.DefaultGenerator'\n" +
" strategy {\n" +
" name = 'org.jooq.codegen.DefaultGeneratorStrategy'\n" +
" }\n" +
" database {\n" +
" name = 'org.jooq.meta.h2.H2Database'\n" +
" inputSchema = 'PUBLIC'\n" +
" }\n" +
" generate {\n" +
" daos = true\n" +
" records = true\n" +
" immutablePojos = true\n" +
" }\n" +
" target {\n" +
" packageName = 'com.example.jooq'\n" +
" directory = '${project.buildDir}/generated-jooq'\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}\n";

Files.write(buildFile.toPath(), buildFileContent.getBytes());

// Run the Gradle task
BuildResult result = GradleRunner.create()
.withProjectDir(projectDir)
.withPluginClasspath()
.withArguments("generateJooq")
.build();

// Assert the task succeeded
assertEquals(TaskOutcome.SUCCESS, result.task(":generateJooq").getOutcome());
}

@Test
public void testPluginConfiguration() throws IOException {
// Create another temporary project directory
File projectDir = testProjectDir.getRoot();

// Create a build.gradle file with custom configurations
buildFile = testProjectDir.newFile("build.gradle");
String buildFileContent = "plugins {\n" +
" id 'org.moonlit.jooq'\n" +
"}\n" +
"\n" +
"repositories {\n" +
" mavenCentral()\n" +
"}\n" +
"\n" +
"jooq {\n" +
" configurations {\n" +
" main {\n" +
" jdbc {\n" +
" driver = 'org.h2.Driver'\n" +
" url = 'jdbc:h2:mem:testdb'\n" +
" user = 'sa'\n" +
" password = ''\n" +
" }\n" +
" generator {\n" +
" target {\n" +
" packageName = 'com.custom.jooq'\n" +
" directory = '${project.buildDir}/custom-generated-jooq'\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}\n";

Files.write(buildFile.toPath(), buildFileContent.getBytes());

// Run the Gradle task
BuildResult result = GradleRunner.create()
.withProjectDir(projectDir)
.withPluginClasspath()
.withArguments("generateJooq")
.build();

// Assert the task succeeded
assertEquals(TaskOutcome.SUCCESS, result.task(":generateJooq").getOutcome());

// Verify the output directory exists
File generatedDir = new File(projectDir, "build/custom-generated-jooq");
assertTrue(generatedDir.exists());
}
}

Explanation:

  • TemporaryFolder ensures that each test has its own isolated directory for creating temporary projects.
  • testPluginAppliesSuccessfully(): This test verifies that the plugin can be applied successfully and runs the generateJooq task.
  • testPluginConfiguration(): This test checks if the plugin works with custom configurations and verifies the output.

3. Run the Tests

To execute the tests, run the following command from your project root:

./gradlew test

Gradle will run the tests and generate a report in build/reports/tests/test/index.html.


4. Use JUnit 5 (Optional)

If you prefer JUnit 5 over JUnit 4, you can update your configuration as follows:

Modify build.gradle

dependencies {
implementation 'org.jooq:jooq:3.18.4'
implementation 'nu.studer:gradle-jooq-plugin:5.2.1'

// JUnit 5 dependency
testImplementation gradleTestKit()
testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1'
}

test {
useJUnitPlatform() // Enable JUnit 5 platform
}

Example JUnit 5 Test Class

package org.moonlit.jooq;

import org.gradle

.testkit.runner.GradleRunner;
import org.gradle.testkit.runner.BuildResult;
import org.gradle.testkit.runner.TaskOutcome;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;

import static org.junit.jupiter.api.Assertions.*;

public class CodegenJooqPluginJUnit5Test {

@TempDir
File testProjectDir;

private File buildFile;

@Test
public void testPluginAppliesSuccessfully() throws IOException {
buildFile = new File(testProjectDir, "build.gradle");
String buildFileContent = "plugins {\n" +
" id 'org.moonlit.jooq'\n" +
"}\n" +
"\n" +
"repositories {\n" +
" mavenCentral()\n" +
"}\n" +
"\n" +
"jooq {\n" +
" configurations {\n" +
" main {\n" +
" jdbc {\n" +
" driver = 'org.h2.Driver'\n" +
" url = 'jdbc:h2:mem:testdb'\n" +
" user = 'sa'\n" +
" password = ''\n" +
" }\n" +
" generator {\n" +
" name = 'org.jooq.codegen.DefaultGenerator'\n" +
" strategy {\n" +
" name = 'org.jooq.codegen.DefaultGeneratorStrategy'\n" +
" }\n" +
" database {\n" +
" name = 'org.jooq.meta.h2.H2Database'\n" +
" inputSchema = 'PUBLIC'\n" +
" }\n" +
" generate {\n" +
" daos = true\n" +
" records = true\n" +
" immutablePojos = true\n" +
" }\n" +
" target {\n" +
" packageName = 'com.example.jooq'\n" +
" directory = '${project.buildDir}/generated-jooq'\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}\n";

Files.write(buildFile.toPath(), buildFileContent.getBytes());

// Run the Gradle task
BuildResult result = GradleRunner.create()
.withProjectDir(testProjectDir)
.withPluginClasspath()
.withArguments("generateJooq")
.build();

assertEquals(TaskOutcome.SUCCESS, result.task(":generateJooq").getOutcome());
}
}

5. Expand Tests

You can add additional tests to cover more scenarios, such as:

  • Default Configuration Tests: Verify that default values are applied correctly when no custom configurations are provided.
  • Custom Configuration Tests: Validate that the plugin correctly applies user-provided configurations.
  • Error Handling Tests: Ensure the plugin provides helpful error messages when misconfigured.

Example Test for Default Configuration

@Test
public void testDefaultConfiguration() throws IOException {
buildFile = testProjectDir.newFile("build.gradle");
String buildFileContent = "plugins {\n" +
" id 'org.moonlit.jooq'\n" +
"}\n" +
"\n" +
"repositories {\n" +
" mavenCentral()\n" +
"}\n";

Files.write(buildFile.toPath(), buildFileContent.getBytes());

BuildResult result = GradleRunner.create()
.withProjectDir(testProjectDir.getRoot())
.withPluginClasspath()
.withArguments("generateJooq")
.build();

assertEquals(TaskOutcome.SUCCESS, result.task(":generateJooq").getOutcome());

File generatedDir = new File(testProjectDir.getRoot(), "build/generated-jooq");
assertTrue(generatedDir.exists());
}

6. Continuous Integration (CI)

To ensure your plugin works across different environments and code changes, set up a CI pipeline using tools like GitHub Actions. Here's an example GitHub Actions configuration:

GitHub Actions Workflow

Create a .github/workflows/gradle.yml file:

name: Gradle CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'

- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Build with Gradle
run: ./gradlew build

Summary

By following these steps, you can effectively test your Gradle plugin and ensure it works correctly in various scenarios. Key points include:

  1. Testing Environment: Set up dependencies using Gradle TestKit and JUnit.
  2. Test Classes: Create isolated tests for validating plugin behavior.
  3. Test Execution: Use ./gradlew test to run tests and verify results.
  4. CI Integration: Automate testing using GitHub Actions or another CI tool.

This setup will help you maintain a high-quality Gradle plugin.

Replacing Groovy with Kotlin

Alright, let's convert the previous Groovy-based build.gradle example into a Kotlin-based build.gradle.kts and adjust the testing section to use Kotlin and JUnit 5. The following steps cover the plugin project configuration, plugin implementation, unit testing, and CI setup, all using Kotlin DSL.

1. Configuring build.gradle.kts

First, convert the build.gradle file to build.gradle.kts, and adjust the syntax to meet Kotlin DSL requirements.

Modifying build.gradle.kts

plugins {
`java-gradle-plugin`
`maven-publish`
`java`
}

group = "org.moonlit"
version = "1.0.0"

repositories {
mavenCentral()
}

dependencies {
implementation("org.jooq:jooq:3.18.4") // Adjust the version as needed
implementation("nu.studer:gradle-jooq-plugin:5.2.1") // Use the correct version of the gradle-jooq-plugin

// Test dependencies
testImplementation(gradleTestKit())
testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") // Using JUnit 5
}

gradlePlugin {
plugins {
create("codegenJooq") {
id = "org.moonlit.jooq"
implementationClass = "org.moonlit.jooq.CodegenJooqPlugin"
displayName = "Codegen JOOQ Gradle Plugin"
description = "A custom Gradle plugin that extends gradle-jooq-plugin with default configurations."
}
}
}

publishing {
publications {
create<MavenPublication>("pluginMaven") {
from(components["java"])
groupId = "org.moonlit"
artifactId = "codegen-jooq-gradle-plugin"
version = "1.0.0"
}
}
repositories {
mavenLocal()
// Configure remote repositories (if needed)
/*
maven {
url = uri("https://your.repository.url")
credentials {
username = findProperty("repoUser") as String? ?: ""
password = findProperty("repoPassword") as String? ?: ""
}
}
*/
}
}

tasks.test {
useJUnitPlatform()
}

Explanation:

  • Plugin application: Use the Kotlin DSL plugin syntax, wrapping the plugin names with backticks.
  • Dependency management: Leverage Kotlin’s string template syntax.
  • Plugin publishing: Use create<MavenPublication> to configure Maven publishing.
  • Test configuration: Configure test tasks to use the JUnit 5 platform.

2. Create Plugin Implementation Class

The plugin implementation class remains the same, as it's written in Java. If you prefer to write the plugin implementation in Kotlin, that is also possible, but here we'll keep it in Java.

Create the CodegenJooqPlugin.java file under src/main/java/org/moonlit/jooq/ (if not already created).

package org.moonlit.jooq;

import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Action;
import nu.studer.gradle.jooq.JooqPluginExtension;
import nu.studer.gradle.jooq.JooqGenerate;

import java.io.File;

public class CodegenJooqPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
// Apply the original gradle-jooq-plugin
project.getPluginManager().apply("nu.studer.jooq");

// Get Jooq plugin extension
JooqPluginExtension jooqExtension = project.getExtensions().getByType(JooqPluginExtension.class);

// Set default configuration
jooqExtension.getVersion().convention("3.18.4"); // Set JOOQ version

// Configure generator
jooqExtension.getConfigurations().all(configuration -> {
configuration.getGenerate().convention(new Action<JooqGenerate>() {
@Override
public void execute(JooqGenerate generate) {
generate.getInputSchema().convention("public");
generate.getOutputDirectory().convention(new File(project.getBuildDir(), "generated-jooq"));
// Add more default configurations
generate.getGenerator().getName().convention("org.jooq.codegen.DefaultGenerator");
// Example: Set target package name
generate.getGenerator().getStrategy().getName().convention("org.jooq.codegen.DefaultGeneratorStrategy");
}
});
});

// Add more default configurations like database connection, generation strategy, etc.
}
}

3. Add Unit Tests

Use Gradle TestKit and JUnit 5 for plugin unit testing. Below is how to write a Kotlin-based test class.

Configure the Test Environment

Ensure that the necessary test dependencies and configurations are included in your build.gradle.kts file, as shown above.

Create the Test Class

Create the test class CodegenJooqPluginTest.kt under src/test/kotlin/org/moonlit/jooq/.

Create the Directory Structure

Ensure the project structure is as follows:

codegen-jooq-gradle-plugin/
├── build.gradle.kts
├── settings.gradle.kts
├── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── moonlit/
│ │ └── jooq/
│ │ └── CodegenJooqPlugin.java
│ └── test/
│ └── kotlin/
│ └── org/
│ └── moonlit/
│ └── jooq/
│ └── CodegenJooqPluginTest.kt
Write the Test Class
package org.moonlit.jooq

import org.gradle.testkit.runner.GradleRunner
import org.gradle.testkit.runner.BuildResult
import org.gradle.testkit.runner.TaskOutcome
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import java.io.File
import java.nio.file.Files

class CodegenJooqPluginTest {

@TempDir
lateinit var testProjectDir: File

private lateinit var buildFile: File

@Test
fun `test plugin applies successfully`() {
// Create build.gradle.kts file
buildFile = File(testProjectDir, "build.gradle.kts")
val buildFileContent = """
plugins {
id("org.moonlit.jooq") version "1.0.0"
}

repositories {
mavenCentral()
}

jooq {
configurations {
main {
jdbc {
driver = "org.h2.Driver"
url = "jdbc:h2:mem:testdb"
user = "sa"
password = ""
}
generator {
name = "org.jooq.codegen.DefaultGenerator"
strategy {
name = "org.jooq.codegen.DefaultGeneratorStrategy"
}
database {
name = "org.jooq.meta.h2.H2Database"
inputSchema = "PUBLIC"
}
generate {
daos = true
records = true
immutablePojos = true
}
target {
packageName = "com.example.jooq"
directory = "${'$'}{project.buildDir}/generated-jooq"
}
}
}
}
}
""".trimIndent()

Files.write(buildFile.toPath(), buildFileContent.toByteArray())

// Run Gradle task
val result: BuildResult = GradleRunner.create()
.withProjectDir(testProjectDir)
.withPluginClasspath()
.withArguments("generateJooq")
.build()

// Assert task success
assertEquals(TaskOutcome.SUCCESS, result.task(":generateJooq")?.outcome)
}

@Test
fun `test plugin configuration`() {
// Create build.gradle.kts file
buildFile = File(testProjectDir, "build.gradle.kts")
val buildFileContent = """
plugins {
id("org.moonlit.jooq") version "1.0.0"
}

repositories {
mavenCentral()
}

jooq {
configurations {
main {
jdbc {
driver = "org.h2.Driver"
url = "jdbc:h2:mem:testdb"
user = "sa"
password = ""
}
generator {
target {
packageName = "com.custom.jooq"
directory = "${'$'}{project.buildDir}/custom-generated-jooq"
}
}
}
}
}
""".trimIndent()

Files.write(buildFile.toPath(), buildFileContent.toByteArray())

// Run Gradle task
val result: BuildResult = GradleRunner.create()
.withProjectDir(testProjectDir)
.withPluginClasspath()
.withArguments("generateJooq")
.build()

// Assert task success
assertEquals(TaskOutcome.SUCCESS, result.task(":generateJooq")?.outcome)

// Verify the generated directory exists
val generatedDir = File(testProjectDir, "build/custom-generated-jooq")
assertTrue(generatedDir.exists())
}

@Test
fun `test default configuration`() {
// Create build.gradle.kts file without any extra configuration
buildFile = File(testProjectDir, "build.gradle.kts")
val buildFileContent = """
plugins {
id("org.moonlit.jooq") version "1.0.0"
}

repositories {
mavenCentral()
}
""".trimIndent()

Files.write(build

File.toPath(), buildFileContent.toByteArray())

// Run Gradle task
val result: BuildResult = GradleRunner.create()
.withProjectDir(testProjectDir)
.withPluginClasspath()
.withArguments("generateJooq")
.build()

// Assert task success
assertEquals(TaskOutcome.SUCCESS, result.task(":generateJooq")?.outcome)

// Verify the default generated directory
val generatedDir = File(testProjectDir, "build/generated-jooq")
assertTrue(generatedDir.exists())
}
}

Explanation:

  • @TempDir: Using JUnit 5's @TempDir annotation to create temporary directories, replacing JUnit 4's @Rule.
  • Test methods: Use Kotlin test method names, wrapped in backticks for more descriptive names.
  • String templates: Use Kotlin string template ${'$'}{} to embed variables.
  • Assertions: Use JUnit 5 assertion methods like assertEquals and assertTrue.

4. Using JUnit 5

The above test class is already configured to use JUnit 5. If you want further customization or extensions, you can adjust the test configuration in build.gradle.kts.

Example build.gradle.kts Test Configuration

tasks.test {
useJUnitPlatform()
}

5. Continuous Integration (CI)

Configure GitHub Actions for continuous integration with Kotlin DSL Gradle projects. Create a .github/workflows/gradle.yml file.

Creating GitHub Actions Configuration

name: Gradle CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'adopt'

- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Build with Gradle
run: ./gradlew build

Explanation:

  • Trigger conditions: Builds are triggered on pushes and pull requests to the main branch.
  • Job steps:
    • Check out the code.
    • Set up the JDK environment (in this case, JDK 11, but adjust as needed).
    • Grant execute permissions for gradlew.
    • Run ./gradlew build, which runs all tests and builds the project.

6. Example Project

To help users better understand and use your plugin, create an example project based on Kotlin DSL and include usage instructions in the README.md.

Example Project Structure

example-project/
├── build.gradle.kts
├── settings.gradle.kts
└── src/
└── main/
└── kotlin/
└── com/
└── example/
└── App.kt

Example build.gradle.kts

plugins {
id("org.moonlit.jooq") version "1.0.0"
}

repositories {
mavenLocal()
mavenCentral()
}

jooq {
configurations {
main {
jdbc {
driver = "org.postgresql.Driver"
url = "jdbc:postgresql://localhost:5432/mydb"
user = "username"
password = "password"
}
generator {
database {
name = "org.jooq.meta.postgres.PostgresDatabase"
inputSchema = "public"
}
target {
packageName = "com.example.jooq"
directory = "${"$"}{project.buildDir}/generated-jooq"
}
}
}
}
}

Explanation:

  • Plugin application: Apply the custom plugin org.moonlit.jooq.
  • Repository configuration: Add both mavenLocal and mavenCentral.
  • JOOQ configuration: Configure the JDBC connection and generator settings with default configurations while allowing overrides.

7. Conclusion

By following the steps above, you've successfully converted a Groovy-based build.gradle to a Kotlin-based build.gradle.kts, and adjusted unit tests to use Kotlin and JUnit 5. Key takeaways:

  1. Kotlin DSL configuration:
    • Use build.gradle.kts for project configuration.
    • Adjust plugin application and dependency management to follow Kotlin syntax.
  2. Unit testing:
    • Write tests using Gradle TestKit and JUnit 5.
    • Write test classes in Kotlin, using @TempDir to create temporary directories.
  3. Continuous Integration:
    • Configure GitHub Actions to automatically build and test the plugin.
  4. Example project:
    • Create an example project to demonstrate how to apply and configure the plugin using Kotlin DSL.

Make sure to continuously write and maintain test cases during development, as it greatly improves the stability and reliability of your plugin. If you encounter any issues during development, consider consulting the following resources:

This code defines a section for configuring test suites using the Gradle Kotlin DSL. It uses Gradle's testing block and defines two test suites: one is the built-in test suite, and the other is a custom functionalTest suite. Here's a detailed explanation:

1. testing block

The testing block in Gradle is a DSL (domain-specific language) used for configuring test suites. It allows you to define and configure different test suites (such as unit tests, functional tests, etc.).

2. suites block

The suites block is a container for defining multiple test suites. Each test suite can have its own configuration and dependencies.

3. Built-in test suite

  • val test by getting(JvmTestSuite::class): This retrieves the built-in test suite, which is by default used for executing JVM (Java Virtual Machine) related unit tests.
  • useJUnitJupiter("5.10.3"): This specifies that the suite should use the JUnit Jupiter test framework, version 5.10.3. JUnit Jupiter is part of JUnit 5, providing modern testing features.

4. Creating a new functionalTest suite

  • val functionalTest by registering(JvmTestSuite::class): This defines a new functionalTest suite, inheriting from JvmTestSuite, indicating that it's a test suite for running JVM tests.

  • Dependency configuration:

    dependencies {
    implementation(project())
    }

    This code specifies the dependencies for the functionalTest suite. implementation(project()) means that the functionalTest suite depends on the production code of the project. This ensures that the functional tests use the project's code when running.

  • Execution order configuration:

    targets {
    all {
    testTask.configure { shouldRunAfter(test) }
    }
    }

    This configures the execution order of the functionalTest suite:

    • testTask.configure { shouldRunAfter(test) }: This ensures that the functionalTest task runs after the built-in test task. This guarantees that functional tests will only run after the unit tests have been executed.

Summary

This code achieves the following:

  1. Configures the built-in unit test suite to use the JUnit Jupiter test framework.
  2. Defines a new functional test suite functionalTest, and sets up its dependencies to ensure it relies on the project's production code.
  3. Ensures that functional tests run after unit tests, using shouldRunAfter(test) to control the execution order.

In your code, you used the dependencies block to define project dependencies, especially the testImplementation part. This code has some differences from the testing block you mentioned earlier, mainly in how tests are organized and configured. Let me first explain the purpose of the code you shared and then compare whether the two approaches can achieve the same result.

1. Purpose of your code:

dependencies {
implementation("org.jooq:jooq:3.18.4")
implementation("nu.studer:gradle-jooq-plugin:5.2.1")

testImplementation(gradleTestKit())
testImplementation("org.junit.jupiter:junit-jupiter:5.8.1")
}
  • implementation:

    • "org.jooq:jooq:3.18.4": Adds the JOOQ library (version 3.18.4) to handle database access in the project.
    • "nu.studer:gradle-jooq-plugin:5.2.1": Adds the Gradle JOOQ plugin to help generate code based on the database schema.
  • testImplementation:

    • testImplementation(gradleTestKit()): Adds Gradle TestKit, a library used to write tests that verify the behavior of Gradle plugins or build scripts. This is typically used for testing Gradle plugin development.
    • "org.junit.jupiter:junit-jupiter:5.8.1": Adds the JUnit Jupiter (JUnit 5) test framework, version 5.8.1, for writing and running unit or functional tests.

2. The role of the CodegenJooqPluginTest.kt file:

In the test class CodegenJooqPluginTest.kt, created under src/test/kotlin/org/moonlit/jooq/, you would typically write tests using the JUnit framework. The tests could verify the behavior of your JOOQ code generation plugin or other code-related logic, using either Gradle TestKit or JUnit.

You might be using Gradle TestKit in this class to test how your Gradle build script behaves or using JUnit to test JOOQ-related logic.

3. Comparison with earlier code (testing block vs dependencies block):

  • testing block:

    • Used for declaring multiple test suites (e.g., test and functionalTest) and defining different dependencies and execution order for each suite.
    • This method was introduced in Gradle 6.7 and is suitable when you need fine-grained control over multiple test phases or tasks, such as ensuring functional tests run after unit tests.
  • Your current dependencies block:

    • This is the traditional Gradle configuration method, where testImplementation directly provides dependencies for all test classes. This approach works well if you only need a simple testing structure, such as unit tests.
    • It doesn’t offer the fine-grained control over multiple test suites or execution order that the testing block provides. All tests will run under the default test task.

4. Comparison of effects:

  • Similarities:

    • Both methods can introduce JUnit as a testing framework and run test classes like CodegenJooqPluginTest.kt.
    • If you only need a simple unit testing environment, your dependencies block is sufficient for testing the JOOQ-generated code or validating the integration with the Gradle plugin.
  • Differences:

    • Using the testing block allows you to define multiple test suites (e.g., test and functionalTest) and control their execution order. This is helpful for more complex projects where you need to run different types of tests (e.g., unit tests, functional tests) separately.
    • Your current dependencies approach is simpler, but if you need functional or integration tests in the future, it may not be flexible enough, as it only creates one default test task where all tests are run together.

5. Recommendations for use:

  • If you only need simple unit testing or are testing the behavior of your Gradle plugin, the current dependencies configuration is sufficient.
  • If you want to split your tests into different phases or need more types of tests (e.g., functional tests, integration tests), using the testing block to configure different suites is a better fit.

Conclusion:

Your current dependencies block configuration works well for single test suite scenarios and is suitable for simpler testing needs. However, if you need to handle multiple test suites and different test types with execution order control, the testing DSL configuration provides more advanced options.