2024-09-17
有些梦想虽然遥不可及,但不是不可能实现。只要我足够的强。 --- 《秦时明月》 · 小学生
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
-
Open PowerShell
- Press
Win + X
, and select Windows PowerShell (Admin) or Windows Terminal (Admin).
- Press
-
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" -
Script Explanation
- Retrieve Subdirectories:
Get-ChildItem -Path $sharedPath -Directory
retrieves all first-level subdirectories underD:\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
.
- Retrieve Subdirectories:
-
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
-
Open Command Prompt
- Press
Win + R
, typecmd
, and pressEnter
.
- Press
-
Run the following command
cd /d D:\Sharing_Data
for /D %G in (*) do icacls "%G" >> D:\Sharing_Data_Permissions.txtNote:
- 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
- If you are writing this command in a batch file (.bat), replace
-
Command Explanation
for /D %G in (*)
: Loops through all first-level subdirectories underD:\Sharing_Data
.icacls "%G"
: Retrieves the permission information for each subdirectory.>> D:\Sharing_Data_Permissions.txt
: Appends the permission info toD:\Sharing_Data_Permissions.txt
.
-
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:
- Gradle Plugin Development Documentation
- gradle-jooq-plugin Documentation
- JOOQ Official Documentation
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:
- Testing Environment: Set up dependencies using Gradle TestKit and JUnit.
- Test Classes: Create isolated tests for validating plugin behavior.
- Test Execution: Use
./gradlew test
to run tests and verify results. - 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
andassertTrue
.
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
andmavenCentral
. - 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:
- Kotlin DSL configuration:
- Use
build.gradle.kts
for project configuration. - Adjust plugin application and dependency management to follow Kotlin syntax.
- Use
- Unit testing:
- Write tests using Gradle TestKit and JUnit 5.
- Write test classes in Kotlin, using
@TempDir
to create temporary directories.
- Continuous Integration:
- Configure GitHub Actions to automatically build and test the plugin.
- 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:
- Gradle Plugin Development Documentation
- gradle-jooq-plugin Documentation
- JOOQ Official Documentation
- Kotlin Gradle DSL Documentation
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-intest
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, version5.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 newfunctionalTest
suite, inheriting fromJvmTestSuite
, 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 thefunctionalTest
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 thefunctionalTest
task runs after the built-intest
task. This guarantees that functional tests will only run after the unit tests have been executed.
Summary
This code achieves the following:
- Configures the built-in unit test suite to use the JUnit Jupiter test framework.
- Defines a new functional test suite
functionalTest
, and sets up its dependencies to ensure it relies on the project's production code. - 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
andfunctionalTest
) 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.
- Used for declaring multiple test suites (e.g.,
-
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 defaulttest
task.
- This is the traditional Gradle configuration method, where
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.
- Both methods can introduce JUnit as a testing framework and run test classes like
-
Differences:
- Using the
testing
block allows you to define multiple test suites (e.g.,test
andfunctionalTest
) 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 defaulttest
task where all tests are run together.
- Using the
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.